2
# Copyright (C) 2007-2008 The University of Melbourne
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
# This is a mod_python handler program. The correct way to call it is to have
23
# Apache send all requests to be handled by the module 'dispatch'.
25
# Top-level handler. Handles all requests to all pages in IVLE.
26
# Handles authentication (not authorization).
27
# Then passes the request along to the appropriate ivle app.
40
from mod_python import apache, Cookie
48
from request import Request
49
import plugins.console # XXX: Relies on www/ being in the Python path.
52
"""Handles a request which may be to anywhere in the site except media.
53
Intended to be called by mod_python, as a handler.
55
req: An Apache request object.
57
# Make the request object into an IVLE request which can be passed to apps
60
req = Request(req, html.write_html_head)
62
# Pass the apachereq to error reporter, since ivle req isn't created
64
handle_unknown_exception(apachereq, *sys.exc_info())
65
# Tell Apache not to generate its own errors as well
68
# Run the main handler, and catch all exceptions
70
return handler_(req, apachereq)
71
except mod_python.apache.SERVER_RETURN:
72
# An apache error. We discourage these, but they might still happen.
76
handle_unknown_exception(req, *sys.exc_info())
77
# Tell Apache not to generate its own errors as well
80
def handler_(req, apachereq):
82
Nested handler function. May raise exceptions. The top-level handler is
83
just used to catch exceptions.
84
Takes both an IVLE request and an Apache req.
86
# Hack? Try and get the user login early just in case we throw an error
87
# (most likely 404) to stop us seeing not logged in even when we are.
88
if not req.publicmode:
89
req.user = login.get_user_details(req)
91
# Check req.app to see if it is valid. 404 if not.
92
if req.app is not None and req.app not in ivle.conf.apps.app_url:
93
req.throw_error(Request.HTTP_NOT_FOUND,
94
"There is no application called %s." % repr(req.app))
96
# Special handling for public mode - only allow the public app, call it
98
# NOTE: This will not behave correctly if the public app uses
99
# write_html_head_foot, but "serve" does not.
101
if req.app != ivle.conf.apps.public_app:
102
req.throw_error(Request.HTTP_FORBIDDEN,
103
"This application is not available on the public site.")
104
app = ivle.conf.apps.app_url[ivle.conf.apps.public_app]
105
apps.call_app(app.dir, req)
108
# app is the App object for the chosen app
110
app = ivle.conf.apps.app_url[ivle.conf.apps.default_app]
112
app = ivle.conf.apps.app_url[req.app]
114
# Check if app requires auth. If so, perform authentication and login.
115
# This will either return a User object, None, or perform a redirect
116
# which we will not catch here.
118
req.user = login.login(req)
119
logged_in = req.user is not None
121
req.user = login.get_user_details(req)
125
# Keep the user's session alive by writing to the session object.
126
# req.get_session().save()
127
# Well, it's a fine idea, but it creates considerable grief in the
128
# concurrent update department, so instead, we'll just make the
129
# sessions not time out.
130
req.get_session().unlock()
132
# If user did not specify an app, HTTP redirect to default app and
135
req.throw_redirect(util.make_path(ivle.conf.apps.default_app))
137
# Set the default title to the app's tab name, if any. Otherwise URL
139
if app.name is not None:
144
# Call the specified app with the request object
145
apps.call_app(app.dir, req)
147
# if not logged in, login.login will have written the login box.
148
# Just clean up and exit.
150
# MAKE SURE we write the HTTP (and possibly HTML) header. This
151
# wouldn't happen if nothing else ever got written, so we have to make
153
req.ensure_headers_written()
155
# When done, write out the HTML footer if the app has requested it
156
if req.write_html_head_foot:
157
# Show the console if required
158
if logged_in and app.useconsole:
159
plugins.console.present(req, windowpane=True)
160
html.write_html_foot(req)
162
# Note: Apache will not write custom HTML error messages here.
163
# Use req.throw_error to do that.
166
def handle_unknown_exception(req, exc_type, exc_value, exc_traceback):
168
Given an exception that has just been thrown from IVLE, print its details
170
This is a full handler. It assumes nothing has been written, and writes a
172
req: May be EITHER an IVLE req or an Apache req.
173
IVLE reqs may have the HTML head/foot written (on a 400 error), but
174
the handler code may pass an apache req if an exception occurs before
175
the IVLE request is created.
177
req.content_type = "text/html"
178
logfile = os.path.join(ivle.conf.log_path, 'ivle_error.log')
180
# For some reason, some versions of mod_python have "_server" instead of
181
# "main_server". So we check for both.
183
admin_email = apache.main_server.server_admin
184
except AttributeError:
186
admin_email = apache._server.server_admin
187
except AttributeError:
190
httpcode = exc_value.httpcode
191
req.status = httpcode
192
except AttributeError:
194
req.status = apache.HTTP_INTERNAL_SERVER_ERROR
196
publicmode = req.publicmode
197
except AttributeError:
200
login = req.user.login
201
except AttributeError:
205
except AttributeError:
210
for h in logging.getLogger().handlers:
211
logging.getLogger().removeHandler(h)
212
logging.basicConfig(level=logging.INFO,
213
format='%(asctime)s %(levelname)s: ' +
214
'(HTTP: ' + str(req.status) +
215
', Ref: ' + str(login) + '@' +
216
str(socket.gethostname()) + str(req.uri) +
222
logging.debug('Logging Unhandled Exception')
224
# We handle 3 types of error.
225
# IVLEErrors with 4xx response codes (client error).
226
# IVLEErrors with 5xx response codes (handled server error).
227
# Other exceptions (unhandled server error).
228
# IVLEErrors should not have other response codes than 4xx or 5xx
229
# (eg. throw_redirect should have been used for 3xx codes).
230
# Therefore, that is treated as an unhandled error.
232
if (exc_type == util.IVLEError and httpcode >= 400
233
and httpcode <= 499):
234
# IVLEErrors with 4xx response codes are client errors.
235
# Therefore, these have a "nice" response (we even coat it in the IVLE
238
req.write_html_head_foot = True
239
req.write_javascript_settings = False
240
req.write('<div id="ivle_padding">\n')
242
codename, msg = req.get_http_codename(httpcode)
243
except AttributeError:
244
codename, msg = None, None
245
# Override the default message with the supplied one,
247
if exc_value.message is not None:
248
msg = exc_value.message
249
if codename is not None:
250
req.write("<h1>Error: %s</h1>\n" % cgi.escape(codename))
252
req.write("<h1>Error</h1>\n")
254
req.write("<p>%s</p>\n" % cgi.escape(msg))
256
req.write("<p>An unknown error occured.</p>\n")
259
logging.info(str(msg))
261
req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
263
req.write("<p>Warning: Could not open Error Log: '%s'</p>\n"
264
%cgi.escape(logfile))
265
req.write('</div>\n')
266
html.write_html_foot(req)
268
# A "bad" error message. We shouldn't get here unless IVLE
269
# misbehaves (which is currently very easy, if things aren't set up
271
# Write the traceback.
272
# If this is a non-4xx IVLEError, get the message and httpcode and
273
# make the error message a bit nicer (but still include the
275
# We also need to special-case IVLEJailError, as we can get another
276
# almost-exception out of it.
278
codename, msg = None, None
280
if exc_type is util.IVLEJailError:
281
msg = exc_value.type_str + ": " + exc_value.message
282
tb = 'Exception information extracted from IVLEJailError:\n'
283
tb += urllib.unquote(exc_value.info)
286
codename, msg = req.get_http_codename(httpcode)
287
except AttributeError:
289
# Override the default message with the supplied one,
291
if hasattr(exc_value, 'message') and exc_value.message is not None:
292
msg = exc_value.message
293
# Prepend the exception type
294
if exc_type != util.IVLEError:
295
msg = exc_type.__name__ + ": " + msg
297
tb = ''.join(traceback.format_exception(exc_type, exc_value,
301
logging.error('%s\n%s'%(str(msg), tb))
302
# Error messages are only displayed is the user is NOT a student,
303
# or if there has been a problem logging the error message
304
show_errors = (not publicmode) and (not login or \
305
(not str(role) == "student") or logfail)
306
req.write("""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
307
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
308
<html xmlns="http://www.w3.org/1999/xhtml">
309
<head><title>IVLE Internal Server Error</title></head>
311
<h1>IVLE Internal Server Error""")
313
if (codename is not None
314
and httpcode != apache.HTTP_INTERNAL_SERVER_ERROR):
315
req.write(": %s" % cgi.escape(codename))
318
<p>An error has occured which is the fault of the IVLE developers or
319
administration. The developers have been notified.</p>
323
req.write("<p>%s</p>\n" % cgi.escape(msg))
324
if httpcode is not None:
325
req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
327
<p>Please report this to <a href="mailto:%s">%s</a> (the system
328
administrator). Include the following information:</p>
329
""" % (cgi.escape(admin_email), cgi.escape(admin_email)))
331
req.write("<pre>\n%s\n</pre>\n"%cgi.escape(tb))
333
req.write("<p>Warning: Could not open Error Log: '%s'</p>\n"
334
%cgi.escape(logfile))
335
req.write("</body></html>")