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

« back to all changes in this revision

Viewing changes to ivle/dispatch/__init__.py

  • Committer: drtomc
  • Date: 2007-12-04 00:18:29 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:1
AnĀ initialĀ checkin.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# IVLE - Informatics Virtual Learning Environment
2
 
# Copyright (C) 2007-2009 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
 
# Author: Matt Giuca, Will Grant
19
 
 
20
 
"""
21
 
This is a mod_python handler program. The correct way to call it is to have
22
 
Apache send all requests to be handled by the module 'dispatch'.
23
 
 
24
 
Top-level handler. Handles all requests to all pages in IVLE.
25
 
Handles authentication (not authorization).
26
 
Then passes the request along to the appropriate ivle app.
27
 
"""
28
 
 
29
 
import sys
30
 
import os
31
 
import os.path
32
 
import urllib
33
 
import cgi
34
 
import traceback
35
 
import logging
36
 
import socket
37
 
import time
38
 
 
39
 
import mod_python
40
 
import routes
41
 
 
42
 
from ivle import util
43
 
import ivle.conf
44
 
import ivle.conf.apps
45
 
from ivle.dispatch.request import Request
46
 
from ivle.dispatch import login
47
 
import apps
48
 
import html
49
 
import plugins.console # XXX: Relies on www/ being in the Python path.
50
 
 
51
 
# XXX List of plugins, which will eventually be read in from conf
52
 
plugins_HACK = [
53
 
    'ivle.webapp.core#Plugin',
54
 
    'ivle.webapp.admin.user#Plugin',
55
 
    'ivle.webapp.tutorial#Plugin',
56
 
    'ivle.webapp.admin.subject#Plugin',
57
 
    'ivle.webapp.filesystem.browser#Plugin',
58
 
    'ivle.webapp.filesystem.diff#Plugin',
59
 
    'ivle.webapp.filesystem.svnlog#Plugin',
60
 
    'ivle.webapp.groups#Plugin',
61
 
    'ivle.webapp.console#Plugin',
62
 
    'ivle.webapp.security#Plugin',
63
 
    'ivle.webapp.media#Plugin',
64
 
]
65
 
 
66
 
def generate_route_mapper(plugins):
67
 
    """
68
 
    Build a Mapper object for doing URL matching using 'routes', based on the
69
 
    given plugin registry.
70
 
    """
71
 
    m = routes.Mapper(explicit=True)
72
 
    for name in plugins:
73
 
        # Establish a URL pattern for each element of plugin.urls
74
 
        if not hasattr(plugins[name], 'urls'):
75
 
            continue
76
 
        for url in plugins[name].urls:
77
 
            routex = url[0]
78
 
            view_class = url[1]
79
 
            kwargs_dict = url[2] if len(url) >= 3 else {}
80
 
            m.connect(routex, view=view_class, **kwargs_dict)
81
 
    return m
82
 
 
83
 
def get_plugin(pluginstr):
84
 
    plugin_path, classname = pluginstr.split('#')
85
 
    # Load the plugin module from somewhere in the Python path
86
 
    # (Note that plugin_path is a fully-qualified Python module name).
87
 
    return (plugin_path,
88
 
            getattr(__import__(plugin_path, fromlist=[classname]), classname))
89
 
 
90
 
def handler(req):
91
 
    """Handles a request which may be to anywhere in the site except media.
92
 
    Intended to be called by mod_python, as a handler.
93
 
 
94
 
    req: An Apache request object.
95
 
    """
96
 
    # Make the request object into an IVLE request which can be passed to apps
97
 
    apachereq = req
98
 
    try:
99
 
        req = Request(req, html.write_html_head)
100
 
    except Exception:
101
 
        # Pass the apachereq to error reporter, since ivle req isn't created
102
 
        # yet.
103
 
        handle_unknown_exception(apachereq, *sys.exc_info())
104
 
        # Tell Apache not to generate its own errors as well
105
 
        return mod_python.apache.OK
106
 
 
107
 
    # Run the main handler, and catch all exceptions
108
 
    try:
109
 
        return handler_(req, apachereq)
110
 
    except mod_python.apache.SERVER_RETURN:
111
 
        # An apache error. We discourage these, but they might still happen.
112
 
        # Just raise up.
113
 
        raise
114
 
    except Exception:
115
 
        handle_unknown_exception(req, *sys.exc_info())
116
 
        # Tell Apache not to generate its own errors as well
117
 
        return mod_python.apache.OK
118
 
 
119
 
def handler_(req, apachereq):
120
 
    """
121
 
    Nested handler function. May raise exceptions. The top-level handler is
122
 
    just used to catch exceptions.
123
 
    Takes both an IVLE request and an Apache req.
124
 
    """
125
 
    # Hack? Try and get the user login early just in case we throw an error
126
 
    # (most likely 404) to stop us seeing not logged in even when we are.
127
 
    if not req.publicmode:
128
 
        req.user = login.get_user_details(req)
129
 
 
130
 
    ### BEGIN New plugins framework ###
131
 
    # XXX This should be done ONCE per Python process, not per request.
132
 
    # (Wait till WSGI)
133
 
    # XXX No authentication is done here
134
 
    req.plugins = dict([get_plugin(pluginstr) for pluginstr in plugins_HACK])
135
 
    req.reverse_plugins = dict([(v, k) for (k, v) in req.plugins.items()])
136
 
    req.mapper = generate_route_mapper(req.plugins)
137
 
 
138
 
    matchdict = req.mapper.match(req.uri)
139
 
    if matchdict is not None:
140
 
        viewcls = matchdict['view']
141
 
        # Get the remaining arguments, less 'view', 'action' and 'controller'
142
 
        # (The latter two seem to be built-in, and we don't want them).
143
 
        kwargs = matchdict.copy()
144
 
        del kwargs['view']
145
 
        # Instantiate the view, which should be a BaseView class
146
 
        view = viewcls(req, **kwargs)
147
 
        # Render the output
148
 
        view.render(req)
149
 
        req.store.commit()
150
 
        return req.OK
151
 
    ### END New plugins framework ###
152
 
 
153
 
    # Check req.app to see if it is valid. 404 if not.
154
 
    if req.app is not None and req.app not in ivle.conf.apps.app_url:
155
 
        req.throw_error(Request.HTTP_NOT_FOUND,
156
 
            "There is no application called %s." % repr(req.app))
157
 
 
158
 
    # Special handling for public mode - only allow the public app, call it
159
 
    # and get out.
160
 
    # NOTE: This will not behave correctly if the public app uses
161
 
    # write_html_head_foot, but "serve" does not.
162
 
    if req.publicmode:
163
 
        if req.app != ivle.conf.apps.public_app:
164
 
            req.throw_error(Request.HTTP_FORBIDDEN,
165
 
                "This application is not available on the public site.")
166
 
        app = ivle.conf.apps.app_url[ivle.conf.apps.public_app]
167
 
        apps.call_app(app.dir, req)
168
 
        return req.OK
169
 
 
170
 
    # app is the App object for the chosen app
171
 
    if req.app is None:
172
 
        app = ivle.conf.apps.app_url[ivle.conf.apps.default_app]
173
 
    else:
174
 
        app = ivle.conf.apps.app_url[req.app]
175
 
 
176
 
    # Check if app requires auth. If so, perform authentication and login.
177
 
    # This will either return a User object, None, or perform a redirect
178
 
    # which we will not catch here.
179
 
    if app.requireauth:
180
 
        req.user = login.login(req)
181
 
        logged_in = req.user is not None
182
 
    else:
183
 
        req.user = login.get_user_details(req)
184
 
        logged_in = True
185
 
 
186
 
    if logged_in:
187
 
        # Keep the user's session alive by writing to the session object.
188
 
        # req.get_session().save()
189
 
        # Well, it's a fine idea, but it creates considerable grief in the
190
 
        # concurrent update department, so instead, we'll just make the
191
 
        # sessions not time out.
192
 
        req.get_session().unlock()
193
 
 
194
 
        # If user did not specify an app, HTTP redirect to default app and
195
 
        # exit.
196
 
        if req.app is None:
197
 
            req.throw_redirect(util.make_path(ivle.conf.apps.default_app))
198
 
 
199
 
        # Set the default title to the app's tab name, if any. Otherwise URL
200
 
        # name.
201
 
        if app.name is not None:
202
 
            req.title = app.name
203
 
        else:
204
 
            req.title = req.app
205
 
 
206
 
        # Call the specified app with the request object
207
 
        apps.call_app(app.dir, req)
208
 
 
209
 
    # if not logged in, login.login will have written the login box.
210
 
    # Just clean up and exit.
211
 
 
212
 
    # MAKE SURE we write the HTTP (and possibly HTML) header. This
213
 
    # wouldn't happen if nothing else ever got written, so we have to make
214
 
    # sure.
215
 
    req.ensure_headers_written()
216
 
 
217
 
    # When done, write out the HTML footer if the app has requested it
218
 
    if req.write_html_head_foot:
219
 
        # Show the console if required
220
 
        if logged_in and app.useconsole:
221
 
            plugins.console.present(req, windowpane=True)
222
 
        html.write_html_foot(req)
223
 
 
224
 
    # Note: Apache will not write custom HTML error messages here.
225
 
    # Use req.throw_error to do that.
226
 
    return req.OK
227
 
 
228
 
def handle_unknown_exception(req, exc_type, exc_value, exc_traceback):
229
 
    """
230
 
    Given an exception that has just been thrown from IVLE, print its details
231
 
    to the request.
232
 
    This is a full handler. It assumes nothing has been written, and writes a
233
 
    complete HTML page.
234
 
    req: May be EITHER an IVLE req or an Apache req.
235
 
    IVLE reqs may have the HTML head/foot written (on a 400 error), but
236
 
    the handler code may pass an apache req if an exception occurs before
237
 
    the IVLE request is created.
238
 
    """
239
 
    req.content_type = "text/html"
240
 
    logfile = os.path.join(ivle.conf.log_path, 'ivle_error.log')
241
 
    logfail = False
242
 
    # For some reason, some versions of mod_python have "_server" instead of
243
 
    # "main_server". So we check for both.
244
 
    try:
245
 
        admin_email = mod_python.apache.main_server.server_admin
246
 
    except AttributeError:
247
 
        try:
248
 
            admin_email = mod_python.apache._server.server_admin
249
 
        except AttributeError:
250
 
            admin_email = ""
251
 
    try:
252
 
        httpcode = exc_value.httpcode
253
 
        req.status = httpcode
254
 
    except AttributeError:
255
 
        httpcode = None
256
 
        req.status = mod_python.apache.HTTP_INTERNAL_SERVER_ERROR
257
 
    try:
258
 
        publicmode = req.publicmode
259
 
    except AttributeError:
260
 
        publicmode = True
261
 
    try:
262
 
        login = req.user.login
263
 
    except AttributeError:
264
 
        login = None
265
 
    try:
266
 
        role = req.user.role
267
 
    except AttributeError:
268
 
        role = None
269
 
 
270
 
    # Log File
271
 
    try:
272
 
        for h in logging.getLogger().handlers:
273
 
            logging.getLogger().removeHandler(h)
274
 
        logging.basicConfig(level=logging.INFO,
275
 
            format='%(asctime)s %(levelname)s: ' +
276
 
                '(HTTP: ' + str(req.status) +
277
 
                ', Ref: ' + str(login) + '@' +
278
 
                str(socket.gethostname()) + str(req.uri) +
279
 
                ') %(message)s',
280
 
            filename=logfile,
281
 
            filemode='a')
282
 
    except IOError:
283
 
        logfail = True
284
 
    logging.debug('Logging Unhandled Exception')
285
 
 
286
 
    # We handle 3 types of error.
287
 
    # IVLEErrors with 4xx response codes (client error).
288
 
    # IVLEErrors with 5xx response codes (handled server error).
289
 
    # Other exceptions (unhandled server error).
290
 
    # IVLEErrors should not have other response codes than 4xx or 5xx
291
 
    # (eg. throw_redirect should have been used for 3xx codes).
292
 
    # Therefore, that is treated as an unhandled error.
293
 
 
294
 
    if (exc_type == util.IVLEError and httpcode >= 400
295
 
        and httpcode <= 499):
296
 
        # IVLEErrors with 4xx response codes are client errors.
297
 
        # Therefore, these have a "nice" response (we even coat it in the IVLE
298
 
        # HTML wrappers).
299
 
        
300
 
        req.write_html_head_foot = True
301
 
        req.write_javascript_settings = False
302
 
        req.write('<div id="ivle_padding">\n')
303
 
        try:
304
 
            codename, msg = req.get_http_codename(httpcode)
305
 
        except AttributeError:
306
 
            codename, msg = None, None
307
 
        # Override the default message with the supplied one,
308
 
        # if available.
309
 
        if exc_value.message is not None:
310
 
            msg = exc_value.message
311
 
        if codename is not None:
312
 
            req.write("<h1>Error: %s</h1>\n" % cgi.escape(codename))
313
 
        else:
314
 
            req.write("<h1>Error</h1>\n")
315
 
        if msg is not None:
316
 
            req.write("<p>%s</p>\n" % cgi.escape(msg))
317
 
        else:
318
 
            req.write("<p>An unknown error occured.</p>\n")
319
 
        
320
 
        # Logging
321
 
        logging.info(str(msg))
322
 
        
323
 
        req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
324
 
        if logfail:
325
 
            req.write("<p>Warning: Could not open Error Log: '%s'</p>\n"
326
 
                %cgi.escape(logfile))
327
 
        req.write('</div>\n')
328
 
        html.write_html_foot(req)
329
 
    else:
330
 
        # A "bad" error message. We shouldn't get here unless IVLE
331
 
        # misbehaves (which is currently very easy, if things aren't set up
332
 
        # correctly).
333
 
        # Write the traceback.
334
 
        # If this is a non-4xx IVLEError, get the message and httpcode and
335
 
        # make the error message a bit nicer (but still include the
336
 
        # traceback).
337
 
        # We also need to special-case IVLEJailError, as we can get another
338
 
        # almost-exception out of it.
339
 
 
340
 
        codename, msg = None, None
341
 
 
342
 
        if exc_type is util.IVLEJailError:
343
 
            msg = exc_value.type_str + ": " + exc_value.message
344
 
            tb = 'Exception information extracted from IVLEJailError:\n'
345
 
            tb += urllib.unquote(exc_value.info)
346
 
        else:
347
 
            try:
348
 
                codename, msg = req.get_http_codename(httpcode)
349
 
            except AttributeError:
350
 
                pass
351
 
            # Override the default message with the supplied one,
352
 
            # if available.
353
 
            if hasattr(exc_value, 'message') and exc_value.message is not None:
354
 
                msg = exc_value.message
355
 
                # Prepend the exception type
356
 
                if exc_type != util.IVLEError:
357
 
                    msg = exc_type.__name__ + ": " + msg
358
 
 
359
 
            tb = ''.join(traceback.format_exception(exc_type, exc_value,
360
 
                                                    exc_traceback))
361
 
 
362
 
        # Logging
363
 
        logging.error('%s\n%s'%(str(msg), tb))
364
 
        # Error messages are only displayed is the user is NOT a student,
365
 
        # or if there has been a problem logging the error message
366
 
        show_errors = (not publicmode) and ((login and \
367
 
                            str(role) != "student") or logfail)
368
 
        req.write("""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"                 
369
 
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">                                      
370
 
<html xmlns="http://www.w3.org/1999/xhtml">
371
 
<head><title>IVLE Internal Server Error</title></head>
372
 
<body>
373
 
<h1>IVLE Internal Server Error""")
374
 
        if (show_errors):
375
 
            if (codename is not None
376
 
                        and httpcode != mod_python.apache.HTTP_INTERNAL_SERVER_ERROR):
377
 
                req.write(": %s" % cgi.escape(codename))
378
 
        
379
 
        req.write("""</h1>
380
 
<p>An error has occured which is the fault of the IVLE developers or
381
 
administration. The developers have been notified.</p>
382
 
""")
383
 
        if (show_errors):
384
 
            if msg is not None:
385
 
                req.write("<p>%s</p>\n" % cgi.escape(msg))
386
 
            if httpcode is not None:
387
 
                req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
388
 
            req.write("""
389
 
    <p>Please report this to <a href="mailto:%s">%s</a> (the system
390
 
    administrator). Include the following information:</p>
391
 
    """ % (cgi.escape(admin_email), cgi.escape(admin_email)))
392
 
 
393
 
            req.write("<pre>\n%s\n</pre>\n"%cgi.escape(tb))
394
 
            if logfail:
395
 
                req.write("<p>Warning: Could not open Error Log: '%s'</p>\n"
396
 
                    %cgi.escape(logfile))
397
 
        req.write("</body></html>")