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.
30
from mod_python import apache
39
from request import Request
43
from common import (util, forumutil)
48
"""Handles a request which may be to anywhere in the site except media.
49
Intended to be called by mod_python, as a handler.
51
req: An Apache request object.
53
# Make the request object into an IVLE request which can be passed to apps
56
req = Request(req, html.write_html_head)
58
# Pass the apachereq to error reporter, since ivle req isn't created
60
handle_unknown_exception(apachereq, *sys.exc_info())
61
# Tell Apache not to generate its own errors as well
64
# Run the main handler, and catch all exceptions
66
return handler_(req, apachereq)
67
except mod_python.apache.SERVER_RETURN:
68
# An apache error. We discourage these, but they might still happen.
72
handle_unknown_exception(req, *sys.exc_info())
73
# Tell Apache not to generate its own errors as well
76
def handler_(req, apachereq):
78
Nested handler function. May raise exceptions. The top-level handler is
79
just used to catch exceptions.
80
Takes both an IVLE request and an Apache req.
82
# Check req.app to see if it is valid. 404 if not.
83
if req.app is not None and req.app not in conf.apps.app_url:
84
# Maybe it is a special app!
85
if req.app == 'logout':
88
req.throw_error(Request.HTTP_NOT_FOUND,
89
"There is no application called %s." % repr(req.app))
91
# Special handling for public mode - just call public app and get out
92
# NOTE: This will not behave correctly if the public app uses
93
# write_html_head_foot, but "serve" does not.
95
app = conf.apps.app_url[conf.apps.public_app]
96
apps.call_app(app.dir, req)
99
# app is the App object for the chosen app
101
app = conf.apps.app_url[conf.apps.default_app]
103
app = conf.apps.app_url[req.app]
105
# Check if app requires auth. If so, perform authentication and login.
106
# This will either return a User object, None, or perform a redirect
107
# which we will not catch here.
109
req.user = login.login(req)
110
logged_in = req.user is not None
112
req.user = login.get_user_details(req)
116
# Keep the user's session alive by writing to the session object.
117
# req.get_session().save()
118
# Well, it's a fine idea, but it creates considerable grief in the
119
# concurrent update department, so instead, we'll just make the
120
# sessions not time out.
122
# If user did not specify an app, HTTP redirect to default app and
125
req.throw_redirect(util.make_path(conf.apps.default_app))
127
# Set the default title to the app's tab name, if any. Otherwise URL
129
if app.name is not None:
134
# Call the specified app with the request object
135
apps.call_app(app.dir, req)
137
# if not logged in, login.login will have written the login box.
138
# Just clean up and exit.
140
# MAKE SURE we write the HTTP (and possibly HTML) header. This
141
# wouldn't happen if nothing else ever got written, so we have to make
143
req.ensure_headers_written()
145
# When done, write out the HTML footer if the app has requested it
146
if req.write_html_head_foot:
147
html.write_html_foot(req)
149
# Note: Apache will not write custom HTML error messages here.
150
# Use req.throw_error to do that.
154
"""Log out the current user (if any) by destroying the session state.
155
Then redirect to the top-level IVLE page."""
156
session = req.get_session()
159
req.add_cookie(forumutil.invalidated_forum_cookie())
160
req.throw_redirect(util.make_path(''))
162
def handle_unknown_exception(req, exc_type, exc_value, exc_traceback):
164
Given an exception that has just been thrown from IVLE, print its details
166
This is a full handler. It assumes nothing has been written, and writes a
168
req: May be EITHER an IVLE req or an Apache req.
169
IVLE reqs may have the HTML head/foot written (on a 400 error), but
170
the handler code may pass an apache req if an exception occurs before
171
the IVLE request is created.
173
req.content_type = "text/html"
174
# For some reason, some versions of mod_python have "_server" instead of
175
# "main_server". So we check for both.
177
admin_email = apache.main_server.server_admin
178
except AttributeError:
180
admin_email = apache._server.server_admin
181
except AttributeError:
184
httpcode = exc_value.httpcode
185
req.status = httpcode
186
except AttributeError:
188
req.status = apache.HTTP_INTERNAL_SERVER_ERROR
189
# We handle 3 types of error.
190
# IVLEErrors with 4xx response codes (client error).
191
# IVLEErrors with 5xx response codes (handled server error).
192
# Other exceptions (unhandled server error).
193
# IVLEErrors should not have other response codes than 4xx or 5xx
194
# (eg. throw_redirect should have been used for 3xx codes).
195
# Therefore, that is treated as an unhandled error.
197
if (exc_type == util.IVLEError and httpcode >= 400
198
and httpcode <= 499):
199
# IVLEErrors with 4xx response codes are client errors.
200
# Therefore, these have a "nice" response (we even coat it in the IVLE
202
req.write_html_head_foot = True
203
req.write('<div id="ivle_padding">\n')
205
codename, msg = req.get_http_codename(httpcode)
206
except AttributeError:
207
codename, msg = None, None
208
# Override the default message with the supplied one,
210
if exc_value.message is not None:
211
msg = exc_value.message
212
if codename is not None:
213
req.write("<h1>Error: %s</h1>\n" % cgi.escape(codename))
215
req.write("<h1>Error</h1>\n")
217
req.write("<p>%s</p>\n" % cgi.escape(msg))
219
req.write("<p>An unknown error occured.</p>\n")
220
req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
221
req.write('</div>\n')
223
# A "bad" error message. We shouldn't get here unless IVLE
224
# misbehaves (which is currently very easy, if things aren't set up
226
# Write the traceback.
227
# If this is a non-4xx IVLEError, get the message and httpcode and
228
# make the error message a bit nicer (but still include the
231
codename, msg = req.get_http_codename(httpcode)
232
except AttributeError:
233
codename, msg = None, None
234
# Override the default message with the supplied one,
236
if hasattr(exc_value, 'message') and exc_value.message is not None:
237
msg = exc_value.message
238
# Prepend the exception type
239
if exc_type != util.IVLEError:
240
msg = exc_type.__name__ + ": " + msg
243
<head><title>IVLE Internal Server Error</title></head>
245
<h1>IVLE Internal Server Error""")
246
if (codename is not None
247
and httpcode != apache.HTTP_INTERNAL_SERVER_ERROR):
248
req.write(": %s" % cgi.escape(codename))
250
<p>An error has occured which is the fault of the IVLE developers or
254
req.write("<p>%s</p>\n" % cgi.escape(msg))
255
if httpcode is not None:
256
req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
258
<p>Please report this to <a href="mailto:%s">%s</a> (the system
259
administrator). Include the following information:</p>
260
""" % (cgi.escape(admin_email), cgi.escape(admin_email)))
262
tb_print = cStringIO.StringIO()
263
traceback.print_exception(exc_type, exc_value, exc_traceback,
266
req.write(cgi.escape(tb_print.getvalue()))
267
req.write("</pre>\n</body>\n")