/[Apache-SVN]/httpd/mod_python/trunk/lib/python/mod_python/util.py
ViewVC logotype

Contents of /httpd/mod_python/trunk/lib/python/mod_python/util.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 103562 - (show annotations) (download) (as text)
Fri Apr 30 19:26:36 2004 UTC (19 years, 5 months ago) by grisha
File MIME type: text/x-python
File size: 12646 byte(s)
This patch fixes the problem by checking if the file has a "file"
attribute itself, and checking the type of that.
There may be a cleaner way to do it.

PR:
Obtained from:
Submitted by:	David Fraser
Reviewed by:

1 #
2 # Copyright 2004 Apache Software Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you
5 # may not use this file except in compliance with the License. You
6 # may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # implied. See the License for the specific language governing
14 # permissions and limitations under the License.
15 #
16 # Originally developed by Gregory Trubetskoy.
17 #
18 # $Id: util.py,v 1.22 2004/04/30 19:26:36 grisha Exp $
19
20 import _apache
21 import apache
22 import cStringIO
23 import tempfile
24
25 from types import *
26 from exceptions import *
27
28 parse_qs = _apache.parse_qs
29 parse_qsl = _apache.parse_qsl
30
31 """ The classes below are a (almost) a drop-in replacement for the
32 standard cgi.py FieldStorage class. They should have pretty much the
33 same functionality.
34
35 These classes differ in that unlike cgi.FieldStorage, they are not
36 recursive. The class FieldStorage contains a list of instances of
37 Field class. Field class is incapable of storing anything in it.
38
39 These objects should be considerably faster than the ones in cgi.py
40 because they do not expect CGI environment, and are
41 optimized specifically for Apache and mod_python.
42 """
43
44 class Field:
45
46 filename = None
47 headers = {}
48
49 def __init__(self, name, file, ctype, type_options,
50 disp, disp_options):
51 self.name = name
52 self.file = file
53 self.type = ctype
54 self.type_options = type_options
55 self.disposition = disp
56 self.disposition_options = disp_options
57
58 def __repr__(self):
59 """Return printable representation."""
60 return "Field(%s, %s)" % (`self.name`, `self.value`)
61
62 def __getattr__(self, name):
63 if name != 'value':
64 raise AttributeError, name
65 if self.file:
66 self.file.seek(0)
67 value = self.file.read()
68 self.file.seek(0)
69 else:
70 value = None
71 return value
72
73 def __del__(self):
74 self.file.close()
75
76 class StringField(str):
77 """ This class is basically a string with
78 a value attribute for compatibility with std lib cgi.py
79 """
80
81 def __init__(self, str=""):
82 str.__init__(self, str)
83 self.value = self.__str__()
84
85 class FieldStorage:
86
87 def __init__(self, req, keep_blank_values=0, strict_parsing=0):
88
89 self.list = []
90
91 # always process GET-style parameters
92 if req.args:
93 pairs = parse_qsl(req.args, keep_blank_values)
94 for pair in pairs:
95 file = cStringIO.StringIO(pair[1])
96 self.list.append(Field(pair[0], file, "text/plain", {},
97 None, {}))
98
99 if req.method == "POST":
100
101 try:
102 clen = int(req.headers_in["content-length"])
103 except (KeyError, ValueError):
104 # absent content-length is not acceptable
105 raise apache.SERVER_RETURN, apache.HTTP_LENGTH_REQUIRED
106
107 if not req.headers_in.has_key("content-type"):
108 ctype = "application/x-www-form-urlencoded"
109 else:
110 ctype = req.headers_in["content-type"]
111
112 if ctype == "application/x-www-form-urlencoded":
113
114 pairs = parse_qsl(req.read(clen), keep_blank_values)
115 for pair in pairs:
116 file = cStringIO.StringIO(pair[1])
117 self.list.append(Field(pair[0], file, "text/plain",
118 {}, None, {}))
119
120 elif ctype[:10] == "multipart/":
121
122 # figure out boundary
123 try:
124 i = ctype.lower().rindex("boundary=")
125 boundary = ctype[i+9:]
126 if len(boundary) >= 2 and boundary[0] == boundary[-1] == '"':
127 boundary = boundary[1:-1]
128 boundary = "--" + boundary
129 except ValueError:
130 raise apache.SERVER_RETURN, apache.HTTP_BAD_REQUEST
131
132 #read until boundary
133 line = req.readline()
134 sline = line.strip()
135 while line and sline != boundary:
136 line = req.readline()
137 sline = line.strip()
138
139 while 1:
140
141 ## parse headers
142
143 ctype, type_options = "text/plain", {}
144 disp, disp_options = None, {}
145 headers = apache.make_table()
146
147 line = req.readline()
148 sline = line.strip()
149 if not line or sline == (boundary + "--"):
150 break
151
152 while line and line not in ["\n", "\r\n"]:
153 h, v = line.split(":", 1)
154 headers.add(h, v)
155 h = h.lower()
156 if h == "content-disposition":
157 disp, disp_options = parse_header(v)
158 elif h == "content-type":
159 ctype, type_options = parse_header(v)
160 line = req.readline()
161 sline = line.strip()
162
163 if disp_options.has_key("name"):
164 name = disp_options["name"]
165 else:
166 name = None
167
168 # is this a file?
169 if disp_options.has_key("filename"):
170 file = self.make_file()
171 else:
172 file = cStringIO.StringIO()
173
174 # read it in
175 self.read_to_boundary(req, boundary, file)
176 file.seek(0)
177
178 # make a Field
179 field = Field(name, file, ctype, type_options,
180 disp, disp_options)
181 field.headers = headers
182 if disp_options.has_key("filename"):
183 field.filename = disp_options["filename"]
184
185 self.list.append(field)
186
187 else:
188 # we don't understand this content-type
189 raise apache.SERVER_RETURN, apache.HTTP_NOT_IMPLEMENTED
190
191
192 def make_file(self):
193 return tempfile.TemporaryFile("w+b")
194
195 def skip_to_boundary(self, req, boundary):
196 line = req.readline()
197 sline = line.strip()
198 last_bound = boundary + "--"
199 while line and sline != boundary and sline != last_bound:
200 line = req.readline()
201 sline = line.strip()
202
203 def read_to_boundary(self, req, boundary, file):
204 delim = ""
205 line = req.readline()
206 sline = line.strip()
207 last_bound = boundary + "--"
208 while line and sline != boundary and sline != last_bound:
209 odelim = delim
210 if line[-2:] == "\r\n":
211 delim = "\r\n"
212 line = line[:-2]
213 elif line[-1:] == "\n":
214 delim = "\n"
215 line = line[:-1]
216 file.write(odelim + line)
217 line = req.readline()
218 sline = line.strip()
219
220 def __getitem__(self, key):
221 """Dictionary style indexing."""
222 if self.list is None:
223 raise TypeError, "not indexable"
224 found = []
225 for item in self.list:
226 if item.name == key:
227 if isinstance(item.file, FileType) or \
228 isinstance(getattr(item.file, 'file', None), FileType):
229 found.append(item)
230 else:
231 found.append(StringField(item.value))
232 if not found:
233 raise KeyError, key
234 if len(found) == 1:
235 return found[0]
236 else:
237 return found
238
239 def get(self, key, default):
240 try:
241 return self.__getitem__(key)
242 except KeyError:
243 return default
244
245 def keys(self):
246 """Dictionary style keys() method."""
247 if self.list is None:
248 raise TypeError, "not indexable"
249 keys = []
250 for item in self.list:
251 if item.name not in keys: keys.append(item.name)
252 return keys
253
254 def has_key(self, key):
255 """Dictionary style has_key() method."""
256 if self.list is None:
257 raise TypeError, "not indexable"
258 for item in self.list:
259 if item.name == key: return 1
260 return 0
261
262 __contains__ = has_key
263
264 def __len__(self):
265 """Dictionary style len(x) support."""
266 return len(self.keys())
267
268 def getfirst(self, key, default=None):
269 """ return the first value received """
270 for item in self.list:
271 if item.name == key:
272 if isinstance(item.file, FileType) or \
273 isinstance(getattr(item.file, 'file', None), FileType):
274 return item
275 else:
276 return StringField(item.value)
277 return default
278
279 def getlist(self, key):
280 """ return a list of received values """
281 if self.list is None:
282 raise TypeError, "not indexable"
283 found = []
284 for item in self.list:
285 if item.name == key:
286 if isinstance(item.file, FileType) or \
287 isinstance(getattr(item.file, 'file', None), FileType):
288 found.append(item)
289 else:
290 found.append(StringField(item.value))
291 return found
292
293 def parse_header(line):
294 """Parse a Content-type like header.
295
296 Return the main content-type and a dictionary of options.
297
298 """
299
300 plist = map(lambda a: a.strip(), line.split(';'))
301 key = plist[0].lower()
302 del plist[0]
303 pdict = {}
304 for p in plist:
305 i = p.find('=')
306 if i >= 0:
307 name = p[:i].strip().lower()
308 value = p[i+1:].strip()
309 if len(value) >= 2 and value[0] == value[-1] == '"':
310 value = value[1:-1]
311 pdict[name] = value
312 return key, pdict
313
314 def apply_fs_data(object, fs, **args):
315 """
316 Apply FieldStorage data to an object - the object must be
317 callable. Examine the args, and match then with fs data,
318 then call the object, return the result.
319 """
320
321 # add form data to args
322 for field in fs.list:
323 if field.filename:
324 val = field
325 else:
326 val = field.value
327 args.setdefault(field.name, []).append(val)
328
329 # replace lists with single values
330 for arg in args:
331 if ((type(args[arg]) is ListType) and
332 (len(args[arg]) == 1)):
333 args[arg] = args[arg][0]
334
335 # we need to weed out unexpected keyword arguments
336 # and for that we need to get a list of them. There
337 # are a few options for callable objects here:
338
339 if type(object) is InstanceType:
340 # instances are callable when they have __call__()
341 object = object.__call__
342
343 expected = []
344 if hasattr(object, "func_code"):
345 # function
346 fc = object.func_code
347 expected = fc.co_varnames[0:fc.co_argcount]
348 elif hasattr(object, 'im_func'):
349 # method
350 fc = object.im_func.func_code
351 expected = fc.co_varnames[1:fc.co_argcount]
352 elif type(object) is ClassType:
353 # class
354 fc = object.__init__.im_func.func_code
355 expected = fc.co_varnames[1:fc.co_argcount]
356
357 # remove unexpected args unless co_flags & 0x08,
358 # meaning function accepts **kw syntax
359 if not (fc.co_flags & 0x08):
360 for name in args.keys():
361 if name not in expected:
362 del args[name]
363
364 return object(**args)
365
366 def redirect(req, location, permanent=0, text=None):
367 """
368 A convenience function to provide redirection
369 """
370
371 if req.sent_bodyct:
372 raise IOError, "Cannot redirect after headers have already been sent."
373
374 req.err_headers_out["Location"] = location
375 if permanent:
376 req.status = apache.HTTP_MOVED_PERMANENTLY
377 else:
378 req.status = apache.HTTP_MOVED_TEMPORARILY
379
380 if text is None:
381 req.write('<p>The document has moved'
382 ' <a href="%s">here</a></p>\n'
383 % location)
384 else:
385 req.write(text)
386
387 raise apache.SERVER_RETURN, apache.OK
388

Properties

Name Value
cvs2svn:cvs-rev 1.22
svn:eol-style native
svn:keywords Author Date Id Revision

infrastructure at apache.org
ViewVC Help
Powered by ViewVC 1.1.26