Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / devtools / v9 / lib / python2.4 / CGIHTTPServer.py
CommitLineData
920dae64
AT
1"""CGI-savvy HTTP Server.
2
3This module builds on SimpleHTTPServer by implementing GET and POST
4requests to cgi-bin scripts.
5
6If the os.fork() function is not present (e.g. on Windows),
7os.popen2() is used as a fallback, with slightly altered semantics; if
8that function is not present either (e.g. on Macintosh), only Python
9scripts are supported, and they are executed by the current process.
10
11In all cases, the implementation is intentionally naive -- all
12requests are executed sychronously.
13
14SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL
15-- it may execute arbitrary Python code or external programs.
16
17"""
18
19
20__version__ = "0.4"
21
22__all__ = ["CGIHTTPRequestHandler"]
23
24import os
25import sys
26import urllib
27import BaseHTTPServer
28import SimpleHTTPServer
29import select
30
31
32class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
33
34 """Complete HTTP server with GET, HEAD and POST commands.
35
36 GET and HEAD also support running CGI scripts.
37
38 The POST command is *only* implemented for CGI scripts.
39
40 """
41
42 # Determine platform specifics
43 have_fork = hasattr(os, 'fork')
44 have_popen2 = hasattr(os, 'popen2')
45 have_popen3 = hasattr(os, 'popen3')
46
47 # Make rfile unbuffered -- we need to read one line and then pass
48 # the rest to a subprocess, so we can't use buffered input.
49 rbufsize = 0
50
51 def do_POST(self):
52 """Serve a POST request.
53
54 This is only implemented for CGI scripts.
55
56 """
57
58 if self.is_cgi():
59 self.run_cgi()
60 else:
61 self.send_error(501, "Can only POST to CGI scripts")
62
63 def send_head(self):
64 """Version of send_head that support CGI scripts"""
65 if self.is_cgi():
66 return self.run_cgi()
67 else:
68 return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
69
70 def is_cgi(self):
71 """Test whether self.path corresponds to a CGI script.
72
73 Return a tuple (dir, rest) if self.path requires running a
74 CGI script, None if not. Note that rest begins with a
75 slash if it is not empty.
76
77 The default implementation tests whether the path
78 begins with one of the strings in the list
79 self.cgi_directories (and the next character is a '/'
80 or the end of the string).
81
82 """
83
84 path = self.path
85
86 for x in self.cgi_directories:
87 i = len(x)
88 if path[:i] == x and (not path[i:] or path[i] == '/'):
89 self.cgi_info = path[:i], path[i+1:]
90 return True
91 return False
92
93 cgi_directories = ['/cgi-bin', '/htbin']
94
95 def is_executable(self, path):
96 """Test whether argument path is an executable file."""
97 return executable(path)
98
99 def is_python(self, path):
100 """Test whether argument path is a Python script."""
101 head, tail = os.path.splitext(path)
102 return tail.lower() in (".py", ".pyw")
103
104 def run_cgi(self):
105 """Execute a CGI script."""
106 dir, rest = self.cgi_info
107 i = rest.rfind('?')
108 if i >= 0:
109 rest, query = rest[:i], rest[i+1:]
110 else:
111 query = ''
112 i = rest.find('/')
113 if i >= 0:
114 script, rest = rest[:i], rest[i:]
115 else:
116 script, rest = rest, ''
117 scriptname = dir + '/' + script
118 scriptfile = self.translate_path(scriptname)
119 if not os.path.exists(scriptfile):
120 self.send_error(404, "No such CGI script (%r)" % scriptname)
121 return
122 if not os.path.isfile(scriptfile):
123 self.send_error(403, "CGI script is not a plain file (%r)" %
124 scriptname)
125 return
126 ispy = self.is_python(scriptname)
127 if not ispy:
128 if not (self.have_fork or self.have_popen2 or self.have_popen3):
129 self.send_error(403, "CGI script is not a Python script (%r)" %
130 scriptname)
131 return
132 if not self.is_executable(scriptfile):
133 self.send_error(403, "CGI script is not executable (%r)" %
134 scriptname)
135 return
136
137 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
138 # XXX Much of the following could be prepared ahead of time!
139 env = {}
140 env['SERVER_SOFTWARE'] = self.version_string()
141 env['SERVER_NAME'] = self.server.server_name
142 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
143 env['SERVER_PROTOCOL'] = self.protocol_version
144 env['SERVER_PORT'] = str(self.server.server_port)
145 env['REQUEST_METHOD'] = self.command
146 uqrest = urllib.unquote(rest)
147 env['PATH_INFO'] = uqrest
148 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
149 env['SCRIPT_NAME'] = scriptname
150 if query:
151 env['QUERY_STRING'] = query
152 host = self.address_string()
153 if host != self.client_address[0]:
154 env['REMOTE_HOST'] = host
155 env['REMOTE_ADDR'] = self.client_address[0]
156 authorization = self.headers.getheader("authorization")
157 if authorization:
158 authorization = authorization.split()
159 if len(authorization) == 2:
160 import base64, binascii
161 env['AUTH_TYPE'] = authorization[0]
162 if authorization[0].lower() == "basic":
163 try:
164 authorization = base64.decodestring(authorization[1])
165 except binascii.Error:
166 pass
167 else:
168 authorization = authorization.split(':')
169 if len(authorization) == 2:
170 env['REMOTE_USER'] = authorization[0]
171 # XXX REMOTE_IDENT
172 if self.headers.typeheader is None:
173 env['CONTENT_TYPE'] = self.headers.type
174 else:
175 env['CONTENT_TYPE'] = self.headers.typeheader
176 length = self.headers.getheader('content-length')
177 if length:
178 env['CONTENT_LENGTH'] = length
179 accept = []
180 for line in self.headers.getallmatchingheaders('accept'):
181 if line[:1] in "\t\n\r ":
182 accept.append(line.strip())
183 else:
184 accept = accept + line[7:].split(',')
185 env['HTTP_ACCEPT'] = ','.join(accept)
186 ua = self.headers.getheader('user-agent')
187 if ua:
188 env['HTTP_USER_AGENT'] = ua
189 co = filter(None, self.headers.getheaders('cookie'))
190 if co:
191 env['HTTP_COOKIE'] = ', '.join(co)
192 # XXX Other HTTP_* headers
193 # Since we're setting the env in the parent, provide empty
194 # values to override previously set values
195 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
196 'HTTP_USER_AGENT', 'HTTP_COOKIE'):
197 env.setdefault(k, "")
198 os.environ.update(env)
199
200 self.send_response(200, "Script output follows")
201
202 decoded_query = query.replace('+', ' ')
203
204 if self.have_fork:
205 # Unix -- fork as we should
206 args = [script]
207 if '=' not in decoded_query:
208 args.append(decoded_query)
209 nobody = nobody_uid()
210 self.wfile.flush() # Always flush before forking
211 pid = os.fork()
212 if pid != 0:
213 # Parent
214 pid, sts = os.waitpid(pid, 0)
215 # throw away additional data [see bug #427345]
216 while select.select([self.rfile], [], [], 0)[0]:
217 if not self.rfile.read(1):
218 break
219 if sts:
220 self.log_error("CGI script exit status %#x", sts)
221 return
222 # Child
223 try:
224 try:
225 os.setuid(nobody)
226 except os.error:
227 pass
228 os.dup2(self.rfile.fileno(), 0)
229 os.dup2(self.wfile.fileno(), 1)
230 os.execve(scriptfile, args, os.environ)
231 except:
232 self.server.handle_error(self.request, self.client_address)
233 os._exit(127)
234
235 elif self.have_popen2 or self.have_popen3:
236 # Windows -- use popen2 or popen3 to create a subprocess
237 import shutil
238 if self.have_popen3:
239 popenx = os.popen3
240 else:
241 popenx = os.popen2
242 cmdline = scriptfile
243 if self.is_python(scriptfile):
244 interp = sys.executable
245 if interp.lower().endswith("w.exe"):
246 # On Windows, use python.exe, not pythonw.exe
247 interp = interp[:-5] + interp[-4:]
248 cmdline = "%s -u %s" % (interp, cmdline)
249 if '=' not in query and '"' not in query:
250 cmdline = '%s "%s"' % (cmdline, query)
251 self.log_message("command: %s", cmdline)
252 try:
253 nbytes = int(length)
254 except (TypeError, ValueError):
255 nbytes = 0
256 files = popenx(cmdline, 'b')
257 fi = files[0]
258 fo = files[1]
259 if self.have_popen3:
260 fe = files[2]
261 if self.command.lower() == "post" and nbytes > 0:
262 data = self.rfile.read(nbytes)
263 fi.write(data)
264 # throw away additional data [see bug #427345]
265 while select.select([self.rfile._sock], [], [], 0)[0]:
266 if not self.rfile._sock.recv(1):
267 break
268 fi.close()
269 shutil.copyfileobj(fo, self.wfile)
270 if self.have_popen3:
271 errors = fe.read()
272 fe.close()
273 if errors:
274 self.log_error('%s', errors)
275 sts = fo.close()
276 if sts:
277 self.log_error("CGI script exit status %#x", sts)
278 else:
279 self.log_message("CGI script exited OK")
280
281 else:
282 # Other O.S. -- execute script in this process
283 save_argv = sys.argv
284 save_stdin = sys.stdin
285 save_stdout = sys.stdout
286 save_stderr = sys.stderr
287 try:
288 save_cwd = os.getcwd()
289 try:
290 sys.argv = [scriptfile]
291 if '=' not in decoded_query:
292 sys.argv.append(decoded_query)
293 sys.stdout = self.wfile
294 sys.stdin = self.rfile
295 execfile(scriptfile, {"__name__": "__main__"})
296 finally:
297 sys.argv = save_argv
298 sys.stdin = save_stdin
299 sys.stdout = save_stdout
300 sys.stderr = save_stderr
301 os.chdir(save_cwd)
302 except SystemExit, sts:
303 self.log_error("CGI script exit status %s", str(sts))
304 else:
305 self.log_message("CGI script exited OK")
306
307
308nobody = None
309
310def nobody_uid():
311 """Internal routine to get nobody's uid"""
312 global nobody
313 if nobody:
314 return nobody
315 try:
316 import pwd
317 except ImportError:
318 return -1
319 try:
320 nobody = pwd.getpwnam('nobody')[2]
321 except KeyError:
322 nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
323 return nobody
324
325
326def executable(path):
327 """Test for executable file."""
328 try:
329 st = os.stat(path)
330 except os.error:
331 return False
332 return st.st_mode & 0111 != 0
333
334
335def test(HandlerClass = CGIHTTPRequestHandler,
336 ServerClass = BaseHTTPServer.HTTPServer):
337 SimpleHTTPServer.test(HandlerClass, ServerClass)
338
339
340if __name__ == '__main__':
341 test()