1 import mimetools
2 import mimetypes
3 mimetypes.init()
4 mimetypes.types_map['.dwg']='image/x-dwg'
5 mimetypes.types_map['.ico']='image/x-icon'
6
7 import os
8 import re
9 import stat
10 import time
11 import urllib
12
13 import cherrypy
14 from cherrypy.lib import cptools, http
15
16
17 -def serve_file(path, content_type=None, disposition=None, name=None):
18 """Set status, headers, and body in order to serve the given file.
19
20 The Content-Type header will be set to the content_type arg, if provided.
21 If not provided, the Content-Type will be guessed by its extension.
22
23 If disposition is not None, the Content-Disposition header will be set
24 to "<disposition>; filename=<name>". If name is None, it will be set
25 to the basename of path. If disposition is None, no Content-Disposition
26 header will be written.
27 """
28
29 response = cherrypy.response
30
31
32
33
34
35
36 if not os.path.isabs(path):
37 raise ValueError("'%s' is not an absolute path." % path)
38
39 try:
40 st = os.stat(path)
41 except OSError:
42 raise cherrypy.NotFound()
43
44
45 if stat.S_ISDIR(st.st_mode):
46
47 raise cherrypy.NotFound()
48
49
50
51 response.headers['Last-Modified'] = http.HTTPDate(st.st_mtime)
52 cptools.validate_since()
53
54 if content_type is None:
55
56 ext = ""
57 i = path.rfind('.')
58 if i != -1:
59 ext = path[i:].lower()
60 content_type = mimetypes.types_map.get(ext, "text/plain")
61 response.headers['Content-Type'] = content_type
62
63 if disposition is not None:
64 if name is None:
65 name = os.path.basename(path)
66 cd = '%s; filename="%s"' % (disposition, name)
67 response.headers["Content-Disposition"] = cd
68
69
70
71 c_len = st.st_size
72 bodyfile = open(path, 'rb')
73
74
75 if cherrypy.request.protocol >= (1, 1):
76 response.headers["Accept-Ranges"] = "bytes"
77 r = http.get_ranges(cherrypy.request.headers.get('Range'), c_len)
78 if r == []:
79 response.headers['Content-Range'] = "bytes */%s" % c_len
80 message = "Invalid Range (first-byte-pos greater than Content-Length)"
81 raise cherrypy.HTTPError(416, message)
82 if r:
83 if len(r) == 1:
84
85 start, stop = r[0]
86 r_len = stop - start
87 response.status = "206 Partial Content"
88 response.headers['Content-Range'] = ("bytes %s-%s/%s" %
89 (start, stop - 1, c_len))
90 response.headers['Content-Length'] = r_len
91 bodyfile.seek(start)
92 response.body = bodyfile.read(r_len)
93 else:
94
95 response.status = "206 Partial Content"
96 boundary = mimetools.choose_boundary()
97 ct = "multipart/byteranges; boundary=%s" % boundary
98 response.headers['Content-Type'] = ct
99 if response.headers.has_key("Content-Length"):
100
101 del response.headers["Content-Length"]
102
103 def file_ranges():
104
105 yield "\r\n"
106
107 for start, stop in r:
108 yield "--" + boundary
109 yield "\r\nContent-type: %s" % content_type
110 yield ("\r\nContent-range: bytes %s-%s/%s\r\n\r\n"
111 % (start, stop - 1, c_len))
112 bodyfile.seek(start)
113 yield bodyfile.read(stop - start)
114 yield "\r\n"
115
116 yield "--" + boundary + "--"
117
118
119 yield "\r\n"
120 response.body = file_ranges()
121 else:
122 response.headers['Content-Length'] = c_len
123 response.body = bodyfile
124 else:
125 response.headers['Content-Length'] = c_len
126 response.body = bodyfile
127 return response.body
128
130 """Serve 'path' as an application/x-download attachment."""
131
132 return serve_file(path, "application/x-download", "attachment", name)
133
134
136 try:
137
138
139 content_type = None
140 if content_types:
141 r, ext = os.path.splitext(filename)
142 content_type = content_types.get(ext[1:], None)
143 serve_file(filename, content_type=content_type)
144 return True
145 except cherrypy.NotFound:
146
147
148 return False
149
150 -def staticdir(section, dir, root="", match="", content_types=None, index=""):
151 """Serve a static resource from the given (root +) dir."""
152 if match and not re.search(match, cherrypy.request.path_info):
153 return False
154
155
156 if not os.path.isabs(dir):
157 if not root:
158 msg = "Static dir requires an absolute dir (or root)."
159 raise ValueError(msg)
160 dir = os.path.join(root, dir)
161
162
163
164 if section == 'global':
165 section = "/"
166 section = section.rstrip(r"\/")
167 branch = cherrypy.request.path_info[len(section) + 1:]
168 branch = urllib.unquote(branch.lstrip(r"\/"))
169
170
171 filename = os.path.join(dir, branch)
172
173
174
175
176 if not os.path.normpath(filename).startswith(os.path.normpath(dir)):
177 raise cherrypy.HTTPError(403)
178
179 handled = _attempt(filename, content_types)
180 if not handled:
181
182 if index and filename[-1] in (r"\/"):
183 handled = _attempt(os.path.join(filename, index), content_types)
184 return handled
185
186 -def staticfile(filename, root=None, match="", content_types=None):
187 """Serve a static resource from the given (root +) filename."""
188 if match and not re.search(match, cherrypy.request.path_info):
189 return False
190
191
192 if not os.path.isabs(filename):
193 if not root:
194 msg = "Static tool requires an absolute filename (got '%s')." % filename
195 raise ValueError(msg)
196 filename = os.path.join(root, filename)
197
198 return _attempt(filename, content_types)
199