1 #!/usr/bin/env python 2 3 """ 4 Resources for serving static content. 5 6 Copyright (C) 2004, 2005 Paul Boddie <paul@boddie.org.uk> 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Lesser General Public 10 License as published by the Free Software Foundation; either 11 version 2.1 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 """ 22 23 from WebStack.Generic import ContentType, EndOfResponse 24 import os 25 26 class DirectoryResource: 27 28 "A resource serving the contents of a filesystem directory." 29 30 def __init__(self, directory, media_types=None, unrecognised_media_type="application/data"): 31 32 """ 33 Initialise the resource to serve files from the given 'directory'. 34 35 The optional 'media_types' dictionary can be used to map filename 36 extensions to media types, where extensions consist of the part of a 37 name after a "." character (such as "txt", "html"), and where media 38 types are the usual content descriptions (such as "text/plain" and 39 "text/html"). 40 41 If 'media_types' contains a mapping from None to a media type, then 42 this mapping is used when no extension is present on a requested 43 resource name. 44 45 Where no media type can be found for a resource, a predefined media 46 type is set which can be overridden by specifying a value for the 47 optional 'unrecognised_media_type' parameter. 48 """ 49 50 self.directory = directory 51 self.media_types = media_types or {} 52 self.unrecognised_media_type = unrecognised_media_type 53 54 def respond(self, trans): 55 56 "Respond to the given transaction, 'trans', by serving a file." 57 58 parts = trans.get_virtual_path_info().split("/") 59 filename = parts[1] 60 out = trans.get_response_stream() 61 62 # Test for the file's existence. 63 64 pathname = os.path.abspath(os.path.join(self.directory, filename)) 65 if not (pathname.startswith(os.path.join(self.directory, "/")) and os.path.exists(pathname) and os.path.isfile(pathname)): 66 trans.set_response_code(404) 67 trans.set_content_type(ContentType("text/plain")) 68 out.write("Resource '%s' not found." % filename) 69 raise EndOfResponse 70 71 # Get the extension. 72 73 extension_parts = filename.split(".") 74 75 if len(extension_parts) > 1: 76 extension = extension_parts[-1] 77 media_type = self.media_types.get(extension) 78 else: 79 media_type = self.media_types.get(None) 80 81 # Set the content type. 82 83 if media_type is not None: 84 trans.set_content_type(ContentType(media_type)) 85 else: 86 trans.set_content_type(ContentType(self.unrecognised_media_type)) 87 88 # Write the file to the client. 89 90 f = open(os.path.join(self.directory, filename), "rb") 91 out.write(f.read()) 92 f.close() 93 94 # vim: tabstop=4 expandtab shiftwidth=4