112
133
stdin=f, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
136
# Restore the environment
137
for k in os.environ.keys():
139
for (k,v) in old_env.items():
115
142
# process_cgi_line: Reads a single line of CGI output and processes it.
116
143
# Prints to req, and also does fancy HTML warnings if Content-Type
119
cgiflags.got_cgi_header = False
120
cgiflags.started_cgi_body = False
121
cgiflags.wrote_html_warning = False
122
def process_cgi_line(line):
123
# FIXME? Issue with binary files (processing per-line?)
124
if cgiflags.started_cgi_body:
125
# FIXME: HTML escape text if wrote_html_warning
145
cgiflags = CGIFlags()
147
# Read from the process's stdout into req
148
data = pid.stdout.read(CGI_BLOCK_SIZE)
150
process_cgi_output(req, data, cgiflags)
151
data = pid.stdout.read(CGI_BLOCK_SIZE)
153
# If we haven't processed headers yet, now is a good time
154
if not cgiflags.started_cgi_body:
155
process_cgi_output(req, '\n', cgiflags)
157
# If we wrote an HTML warning header, write the footer
158
if cgiflags.wrote_html_warning:
164
def process_cgi_output(req, data, cgiflags):
165
"""Processes a chunk of CGI output. data is a string of arbitrary length;
166
some arbitrary chunk of output written by the CGI script."""
167
if cgiflags.started_cgi_body:
168
if cgiflags.wrote_html_warning:
169
# HTML escape text if wrote_html_warning
170
req.write(cgi.escape(data))
129
if line.strip() == "" and cgiflags.got_cgi_header:
130
cgiflags.started_cgi_body = True
131
elif line.startswith("Content-Type:"):
132
req.content_type = line[13:].strip()
133
cgiflags.got_cgi_header = True
134
elif line.startswith("Location:"):
136
cgiflags.got_cgi_header = True
137
elif line.startswith("Status:"):
139
cgiflags.got_cgi_header = True
140
elif cgiflags.got_cgi_header:
143
req.write("Invalid header")
174
# Break data into lines of CGI header data.
175
linebuf = cgiflags.linebuf + data
176
# First see if we can split all header data
177
split = linebuf.split('\r\n\r\n', 1)
179
# Allow UNIX newlines instead
180
split = linebuf.split('\n\n', 1)
182
# Haven't seen all headers yet. Buffer and come back later.
183
cgiflags.linebuf = linebuf
188
cgiflags.linebuf = ""
189
cgiflags.started_cgi_body = True
190
# Process all the header lines
191
split = headers.split('\r\n', 1)
193
split = headers.split('\n', 1)
195
process_cgi_header_line(req, split[0], cgiflags)
196
if len(split) == 1: break
198
if cgiflags.wrote_html_warning:
199
# We're done with headers. Treat the rest as data.
200
data = headers + '\n' + data
202
split = headers.split('\r\n', 1)
204
split = headers.split('\n', 1)
206
# Check to make sure the required headers were written
207
if cgiflags.wrote_html_warning:
208
# We already reported an error, that's enough
210
elif "Content-Type" in cgiflags.headers:
212
elif "Location" in cgiflags.headers:
213
if ("Status" in cgiflags.headers and req.status >= 300
214
and req.status < 400):
146
# Assume the user is not printing headers and give a warning
148
# User program did not print header.
149
# Make a fancy HTML warning for them.
150
req.content_type = "text/html"
151
req.write("""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
217
message = """You did not write a valid status code for
218
the given location. To make a redirect, you may wish to try:</p>
219
<pre style="margin-left: 1em">Status: 302 Found
220
Location: <redirect address></pre>"""
221
write_html_warning(req, message)
222
cgiflags.wrote_html_warning = True
224
message = """You did not print a Content-Type header.
225
CGI requires that you print a "Content-Type". You may wish to try:</p>
226
<pre style="margin-left: 1em">Content-Type: text/html</pre>"""
227
write_html_warning(req, message)
228
cgiflags.wrote_html_warning = True
230
# Call myself to flush out the extra bit of data we read
231
process_cgi_output(req, data, cgiflags)
233
def process_cgi_header_line(req, line, cgiflags):
234
"""Process a line of CGI header data. line is a string representing a
235
complete line of text, stripped and without the newline.
238
name, value = line.split(':', 1)
240
# No colon. The user did not write valid headers.
241
if len(cgiflags.headers) == 0:
242
# First line was not a header line. We can assume this is not
244
message = """You did not print a CGI header.
245
CGI requires that you print a "Content-Type". You may wish to try:</p>
246
<pre style="margin-left: 1em">Content-Type: text/html</pre>"""
248
# They printed some header at least, but there was an invalid
250
message = """You printed an invalid CGI header. You need to leave
251
a blank line after the headers, before writing the page contents."""
252
write_html_warning(req, message)
253
cgiflags.wrote_html_warning = True
254
# Handle the rest of this line as normal data
255
process_cgi_output(req, line + '\n', cgiflags)
259
value = value.strip()
260
if name == "Content-Type":
261
req.content_type = value
262
elif name == "Location":
264
elif name == "Status":
265
# Must be an integer, followed by a space, and then the status line
266
# which we ignore (seems like Apache has no way to send a custom
269
req.status = int(value.split(' ', 1)[0])
271
message = """The "Status" CGI header was invalid. You need to
272
print a number followed by a message, such as "302 Found"."""
273
write_html_warning(req, message)
274
cgiflags.wrote_html_warning = True
275
# Handle the rest of this line as normal data
276
process_cgi_output(req, line + '\n', cgiflags)
278
# Generic HTTP header
279
# FIXME: Security risk letting users write arbitrary headers?
280
req.headers_out[name] = value
281
cgiflags.headers[name] = value
283
def write_html_warning(req, text):
284
"""Prints an HTML warning about invalid CGI interaction on the part of the
285
user. text may contain HTML markup."""
286
req.content_type = "text/html"
287
req.write("""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
152
288
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
153
289
<html xmlns="http://www.w3.org/1999/xhtml">
155
291
<meta http-equiv="Content-Type"
156
292
content="text/html; charset=utf-8" />
158
<body style="margin: 0; padding: 0; font-family: sans;">
294
<body style="margin: 0; padding: 0; font-family: sans-serif;">
159
295
<div style="background-color: #faa; border-bottom: 1px solid black;
161
<p><strong>Warning</strong>: You did not print a "Content-Type" header.
162
CGI requires you to print some content type. You may wish to try:</p>
163
<pre style="margin-left: 1em">Content-Type: text/html</pre>
297
<p><strong>Warning</strong>: %s
165
299
<div style="margin: 8px;">
168
cgiflags.got_cgi_header = True
169
cgiflags.wrote_html_warning = True
170
cgiflags.started_cgi_body = True
173
# Read from the process's stdout into req
174
for line in pid.stdout:
175
process_cgi_line(line)
177
# If we wrote an HTML warning header, write the footer
178
if cgiflags.wrote_html_warning:
184
# TODO: Replace mytest with cgi trampoline handler script
185
303
location_cgi_python = os.path.join(conf.ivle_install_dir,
186
304
"bin/trampoline")