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

« back to all changes in this revision

Viewing changes to www/dispatch/__init__.py

  • Committer: wagrant
  • Date: 2008-07-23 05:48:36 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:940
userservice: Repeat after me: I will not talk to code that runs as root
             for the sole purpose of writing to the database! Bad
             userservice! BAD! Write to the database yourself.

usrmgt-service: Remove {create,update}_user. They were just DB-access
                functions, which userservice now performs itself. We're
                now down to a single function, but additions will come.

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>")