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

« back to all changes in this revision

Viewing changes to www/dispatch/__init__.py

  • Committer: mattgiuca
  • Date: 2008-07-21 05:25:48 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:920
userdb: Added to table "offering" fields max_students_per_group and
    max_groups_per_student.

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
    req.user = login.get_user_details(req)
 
90
 
 
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 conf.apps.app_url:
 
93
        # Maybe it is a special app!
 
94
        if req.app == 'logout':
 
95
            logout(req)
 
96
        else:
 
97
            req.throw_error(Request.HTTP_NOT_FOUND,
 
98
                "There is no application called %s." % repr(req.app))
 
99
 
 
100
    # Special handling for public mode - just call public app and get out
 
101
    # NOTE: This will not behave correctly if the public app uses
 
102
    # write_html_head_foot, but "serve" does not.
 
103
    if req.publicmode:
 
104
        app = conf.apps.app_url[conf.apps.public_app]
 
105
        apps.call_app(app.dir, req)
 
106
        return req.OK
 
107
 
 
108
    # app is the App object for the chosen app
 
109
    if req.app is None:
 
110
        app = conf.apps.app_url[conf.apps.default_app]
 
111
    else:
 
112
        app = conf.apps.app_url[req.app]
 
113
 
 
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.
 
117
    if app.requireauth:
 
118
        req.user = login.login(req)
 
119
        logged_in = req.user is not None
 
120
    else:
 
121
        req.user = login.get_user_details(req)
 
122
        logged_in = True
 
123
 
 
124
    if logged_in:
 
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
        
 
131
        # If user did not specify an app, HTTP redirect to default app and
 
132
        # exit.
 
133
        if req.app is None:
 
134
            req.throw_redirect(util.make_path(conf.apps.default_app))
 
135
 
 
136
        # Set the default title to the app's tab name, if any. Otherwise URL
 
137
        # name.
 
138
        if app.name is not None:
 
139
            req.title = app.name
 
140
        else:
 
141
            req.title = req.app
 
142
 
 
143
        # Call the specified app with the request object
 
144
        apps.call_app(app.dir, req)
 
145
 
 
146
    # if not logged in, login.login will have written the login box.
 
147
    # Just clean up and exit.
 
148
 
 
149
    # MAKE SURE we write the HTTP (and possibly HTML) header. This
 
150
    # wouldn't happen if nothing else ever got written, so we have to make
 
151
    # sure.
 
152
    req.ensure_headers_written()
 
153
 
 
154
    # When done, write out the HTML footer if the app has requested it
 
155
    if req.write_html_head_foot:
 
156
        # Show the console if required
 
157
        if logged_in and app.useconsole:
 
158
            plugins.console.present(req, windowpane=True)
 
159
        html.write_html_foot(req)
 
160
 
 
161
    # Note: Apache will not write custom HTML error messages here.
 
162
    # Use req.throw_error to do that.
 
163
    return req.OK
 
164
 
 
165
def logout(req):
 
166
    """Log out the current user (if any) by destroying the session state.
 
167
    Then redirect to the top-level IVLE page."""
 
168
    session = req.get_session()
 
169
    session.invalidate()
 
170
    session.delete()
 
171
    req.add_cookie(forumutil.invalidated_forum_cookie())
 
172
    req.throw_redirect(util.make_path(''))
 
173
 
 
174
def handle_unknown_exception(req, exc_type, exc_value, exc_traceback):
 
175
    """
 
176
    Given an exception that has just been thrown from IVLE, print its details
 
177
    to the request.
 
178
    This is a full handler. It assumes nothing has been written, and writes a
 
179
    complete HTML page.
 
180
    req: May be EITHER an IVLE req or an Apache req.
 
181
    IVLE reqs may have the HTML head/foot written (on a 400 error), but
 
182
    the handler code may pass an apache req if an exception occurs before
 
183
    the IVLE request is created.
 
184
    """
 
185
    req.content_type = "text/html"
 
186
    logfile = os.path.join(conf.conf.log_path, 'ivle_error.log')
 
187
    logfail = False
 
188
    # For some reason, some versions of mod_python have "_server" instead of
 
189
    # "main_server". So we check for both.
 
190
    try:
 
191
        admin_email = apache.main_server.server_admin
 
192
    except AttributeError:
 
193
        try:
 
194
            admin_email = apache._server.server_admin
 
195
        except AttributeError:
 
196
            admin_email = ""
 
197
    try:
 
198
        httpcode = exc_value.httpcode
 
199
        req.status = httpcode
 
200
    except AttributeError:
 
201
        httpcode = None
 
202
        req.status = apache.HTTP_INTERNAL_SERVER_ERROR
 
203
    try:
 
204
        login = req.user.login
 
205
    except AttributeError:
 
206
        login = None
 
207
 
 
208
    # Log File
 
209
    try:
 
210
        logging.basicConfig(level=logging.INFO,
 
211
            format='%(asctime)s %(levelname)s: ' +
 
212
                '(HTTP: ' + str(req.status) +
 
213
                ', Ref: ' + str(login) + '@' +
 
214
                str(socket.gethostname()) + str(req.uri) +
 
215
                ') %(message)s',
 
216
            filename=logfile,
 
217
            filemode='a')
 
218
    except IOError:
 
219
        logfail = True
 
220
    logging.debug('Logging Unhandled Exception')
 
221
 
 
222
    # We handle 3 types of error.
 
223
    # IVLEErrors with 4xx response codes (client error).
 
224
    # IVLEErrors with 5xx response codes (handled server error).
 
225
    # Other exceptions (unhandled server error).
 
226
    # IVLEErrors should not have other response codes than 4xx or 5xx
 
227
    # (eg. throw_redirect should have been used for 3xx codes).
 
228
    # Therefore, that is treated as an unhandled error.
 
229
 
 
230
    if (exc_type == util.IVLEError and httpcode >= 400
 
231
        and httpcode <= 499):
 
232
        # IVLEErrors with 4xx response codes are client errors.
 
233
        # Therefore, these have a "nice" response (we even coat it in the IVLE
 
234
        # HTML wrappers).
 
235
        
 
236
        req.write_html_head_foot = True
 
237
        req.write('<div id="ivle_padding">\n')
 
238
        try:
 
239
            codename, msg = req.get_http_codename(httpcode)
 
240
        except AttributeError:
 
241
            codename, msg = None, None
 
242
        # Override the default message with the supplied one,
 
243
        # if available.
 
244
        if exc_value.message is not None:
 
245
            msg = exc_value.message
 
246
        if codename is not None:
 
247
            req.write("<h1>Error: %s</h1>\n" % cgi.escape(codename))
 
248
        else:
 
249
            req.write("<h1>Error</h1>\n")
 
250
        if msg is not None:
 
251
            req.write("<p>%s</p>\n" % cgi.escape(msg))
 
252
        else:
 
253
            req.write("<p>An unknown error occured.</p>\n")
 
254
        
 
255
        # Logging
 
256
        logging.info(str(msg))
 
257
        
 
258
        req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
 
259
        if logfail:
 
260
            req.write("<p>Warning: Could not open Error Log: '%s'</p>\n"
 
261
                %cgi.escape(logfile))
 
262
        req.write('</div>\n')
 
263
    else:
 
264
        # A "bad" error message. We shouldn't get here unless IVLE
 
265
        # misbehaves (which is currently very easy, if things aren't set up
 
266
        # correctly).
 
267
        # Write the traceback.
 
268
        # If this is a non-4xx IVLEError, get the message and httpcode and
 
269
        # make the error message a bit nicer (but still include the
 
270
        # traceback).
 
271
        # We also need to special-case IVLEJailError, as we can get another
 
272
        # almost-exception out of it.
 
273
 
 
274
        codename, msg = None, None
 
275
 
 
276
        if exc_type is util.IVLEJailError:
 
277
            msg = exc_value.type_str + ": " + exc_value.message
 
278
            tb = 'Exception information extracted from IVLEJailError:\n'
 
279
            tb += urllib.unquote(exc_value.info)
 
280
        else:
 
281
            try:
 
282
                codename, msg = req.get_http_codename(httpcode)
 
283
            except AttributeError:
 
284
                pass
 
285
            # Override the default message with the supplied one,
 
286
            # if available.
 
287
            if hasattr(exc_value, 'message') and exc_value.message is not None:
 
288
                msg = exc_value.message
 
289
                # Prepend the exception type
 
290
                if exc_type != util.IVLEError:
 
291
                    msg = exc_type.__name__ + ": " + msg
 
292
 
 
293
            tb = ''.join(traceback.format_exception(exc_type, exc_value,
 
294
                                                    exc_traceback))
 
295
 
 
296
        # Logging
 
297
        logging.error('%s\n%s'%(str(msg), tb))
 
298
 
 
299
        req.write("""<html>
 
300
<head><title>IVLE Internal Server Error</title></head>
 
301
<body>
 
302
<h1>IVLE Internal Server Error""")
 
303
        if (codename is not None
 
304
            and httpcode != apache.HTTP_INTERNAL_SERVER_ERROR):
 
305
            req.write(": %s" % cgi.escape(codename))
 
306
        req.write("""</h1>
 
307
<p>An error has occured which is the fault of the IVLE developers or
 
308
administration.</p>
 
309
""")
 
310
        if msg is not None:
 
311
            req.write("<p>%s</p>\n" % cgi.escape(msg))
 
312
        if httpcode is not None:
 
313
            req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
 
314
        req.write("""
 
315
<p>Please report this to <a href="mailto:%s">%s</a> (the system
 
316
administrator). Include the following information:</p>
 
317
""" % (cgi.escape(admin_email), cgi.escape(admin_email)))
 
318
 
 
319
        req.write("<pre>\n%s\n</pre>\n"%cgi.escape(tb))
 
320
        if logfail:
 
321
            req.write("<p>Warning: Could not open Error Log: '%s'</p>\n"
 
322
                %cgi.escape(logfile))
 
323
        req.write("</body>")