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", urlencoding=None): 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 The optional 'urlencoding' is used to decode "URL encoded" character 50 values in the request path, and overrides the default encoding wherever 51 possible. 52 """ 53 54 self.directory = directory 55 self.media_types = media_types or {} 56 self.unrecognised_media_type = unrecognised_media_type 57 self.urlencoding = urlencoding 58 59 def respond(self, trans): 60 61 "Respond to the given transaction, 'trans', by serving a file." 62 63 parts = trans.get_virtual_path_info(self.urlencoding).split("/") 64 filename = parts[1] 65 out = trans.get_response_stream() 66 67 # Test for the file's existence. 68 69 pathname = os.path.abspath(os.path.join(self.directory, filename)) 70 if not (pathname.startswith(os.path.join(self.directory, "/")) and os.path.exists(pathname) and os.path.isfile(pathname)): 71 trans.set_response_code(404) 72 trans.set_content_type(ContentType("text/plain")) 73 out.write("Resource '%s' not found." % filename) 74 raise EndOfResponse 75 76 # Get the extension. 77 78 extension_parts = filename.split(".") 79 80 if len(extension_parts) > 1: 81 extension = extension_parts[-1] 82 media_type = self.media_types.get(extension) 83 else: 84 media_type = self.media_types.get(None) 85 86 # Set the content type. 87 88 if media_type is not None: 89 trans.set_content_type(ContentType(media_type)) 90 else: 91 trans.set_content_type(ContentType(self.unrecognised_media_type)) 92 93 # Write the file to the client. 94 95 f = open(os.path.join(self.directory, filename), "rb") 96 out.write(f.read()) 97 f.close() 98 99 # vim: tabstop=4 expandtab shiftwidth=4