~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to www/dispatch/__init__.py

  • Committer: Matt Giuca
  • Date: 2009-03-24 06:50:39 UTC
  • mto: This revision was merged to the branch mainline in revision 1322.
  • Revision ID: matt.giuca@gmail.com-20090324065039-5c6xkjeb8x2f5d01
doc/conf.py: Renamed project from "ivle" to "IVLE". (Turns out this is a
    friendly name).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# IVLE
2
 
# Copyright (C) 2007-2008 The University of Melbourne
3
 
#
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.
8
 
#
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.
13
 
#
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
17
 
 
18
 
# Module: dispatch
19
 
# Author: Matt Giuca
20
 
# Date: 11/12/2007
21
 
 
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'.
24
 
 
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.
28
 
 
29
 
import mod_python
30
 
from mod_python import apache
31
 
 
32
 
import sys
33
 
import os
34
 
import os.path
35
 
import urllib
36
 
 
37
 
import conf
38
 
import conf.apps
39
 
import conf.conf
40
 
import apps
41
 
 
42
 
from request import Request
43
 
import html
44
 
import cgi
45
 
import login
46
 
from common import (util, forumutil)
47
 
import traceback
48
 
import plugins.console
49
 
import logging
50
 
import socket
51
 
 
52
 
def handler(req):
53
 
    """Handles a request which may be to anywhere in the site except media.
54
 
    Intended to be called by mod_python, as a handler.
55
 
 
56
 
    req: An Apache request object.
57
 
    """
58
 
    # Make the request object into an IVLE request which can be passed to apps
59
 
    apachereq = req
60
 
    try:
61
 
        req = Request(req, html.write_html_head)
62
 
    except Exception:
63
 
        # Pass the apachereq to error reporter, since ivle req isn't created
64
 
        # yet.
65
 
        handle_unknown_exception(apachereq, *sys.exc_info())
66
 
        # Tell Apache not to generate its own errors as well
67
 
        return apache.OK
68
 
 
69
 
    # Run the main handler, and catch all exceptions
70
 
    try:
71
 
        return handler_(req, apachereq)
72
 
    except mod_python.apache.SERVER_RETURN:
73
 
        # An apache error. We discourage these, but they might still happen.
74
 
        # Just raise up.
75
 
        raise
76
 
    except Exception:
77
 
        handle_unknown_exception(req, *sys.exc_info())
78
 
        # Tell Apache not to generate its own errors as well
79
 
        return apache.OK
80
 
 
81
 
def handler_(req, apachereq):
82
 
    """
83
 
    Nested handler function. May raise exceptions. The top-level handler is
84
 
    just used to catch exceptions.
85
 
    Takes both an IVLE request and an Apache req.
86
 
    """
87
 
    # Hack? Try and get the user login early just in case we throw an error
88
 
    # (most likely 404) to stop us seeing not logged in even when we are.
89
 
    if not req.publicmode:
90
 
        req.user = login.get_user_details(req)
91
 
 
92
 
    # Check req.app to see if it is valid. 404 if not.
93
 
    if req.app is not None and req.app not in conf.apps.app_url:
94
 
        # Maybe it is a special app!
95
 
        if req.app == 'logout':
96
 
            logout(req)
97
 
        else:
98
 
            req.throw_error(Request.HTTP_NOT_FOUND,
99
 
                "There is no application called %s." % repr(req.app))
100
 
 
101
 
    # Special handling for public mode - only allow the public app, call it
102
 
    # and get out.
103
 
    # NOTE: This will not behave correctly if the public app uses
104
 
    # write_html_head_foot, but "serve" does not.
105
 
    if req.publicmode:
106
 
        if req.app != conf.apps.public_app:
107
 
            req.throw_error(Request.HTTP_FORBIDDEN,
108
 
                "This application is not available on the public site.")
109
 
        app = conf.apps.app_url[conf.apps.public_app]
110
 
        apps.call_app(app.dir, req)
111
 
        return req.OK
112
 
 
113
 
    # app is the App object for the chosen app
114
 
    if req.app is None:
115
 
        app = conf.apps.app_url[conf.apps.default_app]
116
 
    else:
117
 
        app = conf.apps.app_url[req.app]
118
 
 
119
 
    # Check if app requires auth. If so, perform authentication and login.
120
 
    # This will either return a User object, None, or perform a redirect
121
 
    # which we will not catch here.
122
 
    if app.requireauth:
123
 
        req.user = login.login(req)
124
 
        logged_in = req.user is not None
125
 
    else:
126
 
        req.user = login.get_user_details(req)
127
 
        logged_in = True
128
 
 
129
 
    if logged_in:
130
 
        # Keep the user's session alive by writing to the session object.
131
 
        # req.get_session().save()
132
 
        # Well, it's a fine idea, but it creates considerable grief in the
133
 
        # concurrent update department, so instead, we'll just make the
134
 
        # sessions not time out.
135
 
        
136
 
        # If user did not specify an app, HTTP redirect to default app and
137
 
        # exit.
138
 
        if req.app is None:
139
 
            req.throw_redirect(util.make_path(conf.apps.default_app))
140
 
 
141
 
        # Set the default title to the app's tab name, if any. Otherwise URL
142
 
        # name.
143
 
        if app.name is not None:
144
 
            req.title = app.name
145
 
        else:
146
 
            req.title = req.app
147
 
 
148
 
        # Call the specified app with the request object
149
 
        apps.call_app(app.dir, req)
150
 
 
151
 
    # if not logged in, login.login will have written the login box.
152
 
    # Just clean up and exit.
153
 
 
154
 
    # MAKE SURE we write the HTTP (and possibly HTML) header. This
155
 
    # wouldn't happen if nothing else ever got written, so we have to make
156
 
    # sure.
157
 
    req.ensure_headers_written()
158
 
 
159
 
    # When done, write out the HTML footer if the app has requested it
160
 
    if req.write_html_head_foot:
161
 
        # Show the console if required
162
 
        if logged_in and app.useconsole:
163
 
            plugins.console.present(req, windowpane=True)
164
 
        html.write_html_foot(req)
165
 
 
166
 
    # Note: Apache will not write custom HTML error messages here.
167
 
    # Use req.throw_error to do that.
168
 
    return req.OK
169
 
 
170
 
def logout(req):
171
 
    """Log out the current user (if any) by destroying the session state.
172
 
    Then redirect to the top-level IVLE page."""
173
 
    session = req.get_session()
174
 
    session.invalidate()
175
 
    session.delete()
176
 
    req.add_cookie(forumutil.invalidated_forum_cookie())
177
 
    req.throw_redirect(util.make_path(''))
178
 
 
179
 
def handle_unknown_exception(req, exc_type, exc_value, exc_traceback):
180
 
    """
181
 
    Given an exception that has just been thrown from IVLE, print its details
182
 
    to the request.
183
 
    This is a full handler. It assumes nothing has been written, and writes a
184
 
    complete HTML page.
185
 
    req: May be EITHER an IVLE req or an Apache req.
186
 
    IVLE reqs may have the HTML head/foot written (on a 400 error), but
187
 
    the handler code may pass an apache req if an exception occurs before
188
 
    the IVLE request is created.
189
 
    """
190
 
    req.content_type = "text/html"
191
 
    logfile = os.path.join(conf.conf.log_path, 'ivle_error.log')
192
 
    logfail = False
193
 
    # For some reason, some versions of mod_python have "_server" instead of
194
 
    # "main_server". So we check for both.
195
 
    try:
196
 
        admin_email = apache.main_server.server_admin
197
 
    except AttributeError:
198
 
        try:
199
 
            admin_email = apache._server.server_admin
200
 
        except AttributeError:
201
 
            admin_email = ""
202
 
    try:
203
 
        httpcode = exc_value.httpcode
204
 
        req.status = httpcode
205
 
    except AttributeError:
206
 
        httpcode = None
207
 
        req.status = apache.HTTP_INTERNAL_SERVER_ERROR
208
 
    try:
209
 
        login = req.user.login
210
 
    except AttributeError:
211
 
        login = None
212
 
 
213
 
    # Log File
214
 
    try:
215
 
        logging.basicConfig(level=logging.INFO,
216
 
            format='%(asctime)s %(levelname)s: ' +
217
 
                '(HTTP: ' + str(req.status) +
218
 
                ', Ref: ' + str(login) + '@' +
219
 
                str(socket.gethostname()) + str(req.uri) +
220
 
                ') %(message)s',
221
 
            filename=logfile,
222
 
            filemode='a')
223
 
    except IOError:
224
 
        logfail = True
225
 
    logging.debug('Logging Unhandled Exception')
226
 
 
227
 
    # We handle 3 types of error.
228
 
    # IVLEErrors with 4xx response codes (client error).
229
 
    # IVLEErrors with 5xx response codes (handled server error).
230
 
    # Other exceptions (unhandled server error).
231
 
    # IVLEErrors should not have other response codes than 4xx or 5xx
232
 
    # (eg. throw_redirect should have been used for 3xx codes).
233
 
    # Therefore, that is treated as an unhandled error.
234
 
 
235
 
    if (exc_type == util.IVLEError and httpcode >= 400
236
 
        and httpcode <= 499):
237
 
        # IVLEErrors with 4xx response codes are client errors.
238
 
        # Therefore, these have a "nice" response (we even coat it in the IVLE
239
 
        # HTML wrappers).
240
 
        
241
 
        req.write_html_head_foot = True
242
 
        req.write('<div id="ivle_padding">\n')
243
 
        try:
244
 
            codename, msg = req.get_http_codename(httpcode)
245
 
        except AttributeError:
246
 
            codename, msg = None, None
247
 
        # Override the default message with the supplied one,
248
 
        # if available.
249
 
        if exc_value.message is not None:
250
 
            msg = exc_value.message
251
 
        if codename is not None:
252
 
            req.write("<h1>Error: %s</h1>\n" % cgi.escape(codename))
253
 
        else:
254
 
            req.write("<h1>Error</h1>\n")
255
 
        if msg is not None:
256
 
            req.write("<p>%s</p>\n" % cgi.escape(msg))
257
 
        else:
258
 
            req.write("<p>An unknown error occured.</p>\n")
259
 
        
260
 
        # Logging
261
 
        logging.info(str(msg))
262
 
        
263
 
        req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
264
 
        if logfail:
265
 
            req.write("<p>Warning: Could not open Error Log: '%s'</p>\n"
266
 
                %cgi.escape(logfile))
267
 
        req.write('</div>\n')
268
 
    else:
269
 
        # A "bad" error message. We shouldn't get here unless IVLE
270
 
        # misbehaves (which is currently very easy, if things aren't set up
271
 
        # correctly).
272
 
        # Write the traceback.
273
 
        # If this is a non-4xx IVLEError, get the message and httpcode and
274
 
        # make the error message a bit nicer (but still include the
275
 
        # traceback).
276
 
        # We also need to special-case IVLEJailError, as we can get another
277
 
        # almost-exception out of it.
278
 
 
279
 
        codename, msg = None, None
280
 
 
281
 
        if exc_type is util.IVLEJailError:
282
 
            msg = exc_value.type_str + ": " + exc_value.message
283
 
            tb = 'Exception information extracted from IVLEJailError:\n'
284
 
            tb += urllib.unquote(exc_value.info)
285
 
        else:
286
 
            try:
287
 
                codename, msg = req.get_http_codename(httpcode)
288
 
            except AttributeError:
289
 
                pass
290
 
            # Override the default message with the supplied one,
291
 
            # if available.
292
 
            if hasattr(exc_value, 'message') and exc_value.message is not None:
293
 
                msg = exc_value.message
294
 
                # Prepend the exception type
295
 
                if exc_type != util.IVLEError:
296
 
                    msg = exc_type.__name__ + ": " + msg
297
 
 
298
 
            tb = ''.join(traceback.format_exception(exc_type, exc_value,
299
 
                                                    exc_traceback))
300
 
 
301
 
        # Logging
302
 
        logging.error('%s\n%s'%(str(msg), tb))
303
 
 
304
 
        req.write("""<html>
305
 
<head><title>IVLE Internal Server Error</title></head>
306
 
<body>
307
 
<h1>IVLE Internal Server Error""")
308
 
        if (codename is not None
309
 
            and httpcode != apache.HTTP_INTERNAL_SERVER_ERROR):
310
 
            req.write(": %s" % cgi.escape(codename))
311
 
        req.write("""</h1>
312
 
<p>An error has occured which is the fault of the IVLE developers or
313
 
administration.</p>
314
 
""")
315
 
        if msg is not None:
316
 
            req.write("<p>%s</p>\n" % cgi.escape(msg))
317
 
        if httpcode is not None:
318
 
            req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
319
 
        req.write("""
320
 
<p>Please report this to <a href="mailto:%s">%s</a> (the system
321
 
administrator). Include the following information:</p>
322
 
""" % (cgi.escape(admin_email), cgi.escape(admin_email)))
323
 
 
324
 
        req.write("<pre>\n%s\n</pre>\n"%cgi.escape(tb))
325
 
        if logfail:
326
 
            req.write("<p>Warning: Could not open Error Log: '%s'</p>\n"
327
 
                %cgi.escape(logfile))
328
 
        req.write("</body>")