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

« back to all changes in this revision

Viewing changes to ivle/dispatch/__init__.py

Update some docs, and remove other obsolete ones.

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
from ivle.dispatch.request import Request
 
45
import ivle.webapp.security
 
46
from ivle.webapp.base.plugins import ViewPlugin, PublicViewPlugin
 
47
from ivle.webapp.errors import HTTPError, Unauthorized
 
48
 
 
49
def generate_route_mapper(view_plugins, attr):
 
50
    """
 
51
    Build a Mapper object for doing URL matching using 'routes', based on the
 
52
    given plugin registry.
 
53
    """
 
54
    m = routes.Mapper(explicit=True)
 
55
    for plugin in view_plugins:
 
56
        # Establish a URL pattern for each element of plugin.urls
 
57
        assert hasattr(plugin, 'urls'), "%r does not have any urls" % plugin 
 
58
        for url in getattr(plugin, attr):
 
59
            routex = url[0]
 
60
            view_class = url[1]
 
61
            kwargs_dict = url[2] if len(url) >= 3 else {}
 
62
            m.connect(routex, view=view_class, **kwargs_dict)
 
63
    return m
 
64
 
 
65
def handler(req):
 
66
    """Handles a request which may be to anywhere in the site except media.
 
67
    Intended to be called by mod_python, as a handler.
 
68
 
 
69
    req: An Apache request object.
 
70
    """
 
71
    # Make the request object into an IVLE request which can be passed to apps
 
72
    apachereq = req
 
73
    try:
 
74
        req = Request(req)
 
75
    except Exception:
 
76
        # Pass the apachereq to error reporter, since ivle req isn't created
 
77
        # yet.
 
78
        handle_unknown_exception(apachereq, *sys.exc_info())
 
79
        # Tell Apache not to generate its own errors as well
 
80
        return mod_python.apache.OK
 
81
 
 
82
    # Run the main handler, and catch all exceptions
 
83
    try:
 
84
        return handler_(req, apachereq)
 
85
    except mod_python.apache.SERVER_RETURN:
 
86
        # An apache error. We discourage these, but they might still happen.
 
87
        # Just raise up.
 
88
        raise
 
89
    except Exception:
 
90
        handle_unknown_exception(req, *sys.exc_info())
 
91
        # Tell Apache not to generate its own errors as well
 
92
        return mod_python.apache.OK
 
93
 
 
94
def handler_(req, apachereq):
 
95
    """
 
96
    Nested handler function. May raise exceptions. The top-level handler is
 
97
    just used to catch exceptions.
 
98
    Takes both an IVLE request and an Apache req.
 
99
    """
 
100
    # Hack? Try and get the user login early just in case we throw an error
 
101
    # (most likely 404) to stop us seeing not logged in even when we are.
 
102
    if not req.publicmode:
 
103
        user = ivle.webapp.security.get_user_details(req)
 
104
 
 
105
        # Don't set the user if it is disabled or hasn't accepted the ToS.
 
106
        if user and user.valid:
 
107
            req.user = user
 
108
 
 
109
    conf = ivle.config.Config()
 
110
    req.config = conf
 
111
 
 
112
    if req.publicmode:
 
113
        req.mapper = generate_route_mapper(conf.plugin_index[PublicViewPlugin],
 
114
                                           'public_urls')
 
115
    else:
 
116
        req.mapper = generate_route_mapper(conf.plugin_index[ViewPlugin],
 
117
                                           'urls')
 
118
 
 
119
    matchdict = req.mapper.match(req.uri)
 
120
    if matchdict is not None:
 
121
        viewcls = matchdict['view']
 
122
        # Get the remaining arguments, less 'view', 'action' and 'controller'
 
123
        # (The latter two seem to be built-in, and we don't want them).
 
124
        kwargs = matchdict.copy()
 
125
        del kwargs['view']
 
126
        try:
 
127
            # Instantiate the view, which should be a BaseView class
 
128
            view = viewcls(req, **kwargs)
 
129
 
 
130
            # Check that the request (mainly the user) is permitted to access
 
131
            # the view.
 
132
            if not view.authorize(req):
 
133
                raise Unauthorized()
 
134
            # Render the output
 
135
            view.render(req)
 
136
        except HTTPError, e:
 
137
            # A view explicitly raised an HTTP error. Respect it.
 
138
            req.status = e.code
 
139
 
 
140
            # Try to find a custom error view.
 
141
            if hasattr(viewcls, 'get_error_view'):
 
142
                errviewcls = viewcls.get_error_view(e)
 
143
            else:
 
144
                errviewcls = None
 
145
 
 
146
            if errviewcls:
 
147
                errview = errviewcls(req, e)
 
148
                errview.render(req)
 
149
                return req.OK
 
150
            elif e.message:
 
151
                req.write(e.message)
 
152
                return req.OK
 
153
            else:
 
154
                return e.code
 
155
        except Exception, e:
 
156
            # A non-HTTPError appeared. We have an unknown exception. Panic.
 
157
            handle_unknown_exception(req, *sys.exc_info())
 
158
            return req.OK
 
159
        else:
 
160
            req.store.commit()
 
161
            return req.OK
 
162
    else:
 
163
        return req.HTTP_NOT_FOUND # TODO: Prettify.
 
164
 
 
165
def handle_unknown_exception(req, exc_type, exc_value, exc_traceback):
 
166
    """
 
167
    Given an exception that has just been thrown from IVLE, print its details
 
168
    to the request.
 
169
    This is a full handler. It assumes nothing has been written, and writes a
 
170
    complete HTML page.
 
171
    req: May be EITHER an IVLE req or an Apache req.
 
172
    The handler code may pass an apache req if an exception occurs before
 
173
    the IVLE request is created.
 
174
    """
 
175
    req.content_type = "text/html"
 
176
    logfile = os.path.join(ivle.conf.log_path, 'ivle_error.log')
 
177
    logfail = False
 
178
    # For some reason, some versions of mod_python have "_server" instead of
 
179
    # "main_server". So we check for both.
 
180
    try:
 
181
        admin_email = mod_python.apache.main_server.server_admin
 
182
    except AttributeError:
 
183
        try:
 
184
            admin_email = mod_python.apache._server.server_admin
 
185
        except AttributeError:
 
186
            admin_email = ""
 
187
    try:
 
188
        httpcode = exc_value.httpcode
 
189
        req.status = httpcode
 
190
    except AttributeError:
 
191
        httpcode = None
 
192
        req.status = mod_python.apache.HTTP_INTERNAL_SERVER_ERROR
 
193
    try:
 
194
        publicmode = req.publicmode
 
195
    except AttributeError:
 
196
        publicmode = True
 
197
    try:
 
198
        login = req.user.login
 
199
    except AttributeError:
 
200
        login = None
 
201
    try:
 
202
        role = req.user.role
 
203
    except AttributeError:
 
204
        role = None
 
205
 
 
206
    # Log File
 
207
    try:
 
208
        for h in logging.getLogger().handlers:
 
209
            logging.getLogger().removeHandler(h)
 
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
    # A "bad" error message. We shouldn't get here unless IVLE
 
223
    # misbehaves (which is currently very easy, if things aren't set up
 
224
    # correctly).
 
225
    # Write the traceback.
 
226
    # If this is a non-4xx IVLEError, get the message and httpcode and
 
227
    # make the error message a bit nicer (but still include the
 
228
    # traceback).
 
229
    # We also need to special-case IVLEJailError, as we can get another
 
230
    # almost-exception out of it.
 
231
 
 
232
    codename, msg = None, None
 
233
 
 
234
    if exc_type is util.IVLEJailError:
 
235
        msg = exc_value.type_str + ": " + exc_value.message
 
236
        tb = 'Exception information extracted from IVLEJailError:\n'
 
237
        tb += urllib.unquote(exc_value.info)
 
238
    else:
 
239
        try:
 
240
            codename, msg = req.get_http_codename(httpcode)
 
241
        except AttributeError:
 
242
            pass
 
243
        # Override the default message with the supplied one,
 
244
        # if available.
 
245
        if hasattr(exc_value, 'message') and exc_value.message is not None:
 
246
            msg = exc_value.message
 
247
            # Prepend the exception type
 
248
            if exc_type != util.IVLEError:
 
249
                msg = exc_type.__name__ + ": " + repr(msg)
 
250
 
 
251
        tb = ''.join(traceback.format_exception(exc_type, exc_value,
 
252
                                                exc_traceback))
 
253
 
 
254
    # Logging
 
255
    logging.error('%s\n%s'%(str(msg), tb))
 
256
    # Error messages are only displayed is the user is NOT a student,
 
257
    # or if there has been a problem logging the error message
 
258
    show_errors = (not publicmode) and ((login and \
 
259
                        str(role) != "student") or logfail)
 
260
    req.write("""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"                 
 
261
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">                                      
 
262
<html xmlns="http://www.w3.org/1999/xhtml">
 
263
<head><title>IVLE Internal Server Error</title></head>
 
264
<body>
 
265
<h1>IVLE Internal Server Error""")
 
266
    if (show_errors):
 
267
        if (codename is not None
 
268
                    and httpcode != mod_python.apache.HTTP_INTERNAL_SERVER_ERROR):
 
269
            req.write(": %s" % cgi.escape(codename))
 
270
 
 
271
    req.write("""</h1>
 
272
<p>An error has occured which is the fault of the IVLE developers or
 
273
administration. The developers have been notified.</p>
 
274
""")
 
275
    if (show_errors):
 
276
        if msg is not None:
 
277
            req.write("<p>%s</p>\n" % cgi.escape(msg))
 
278
        if httpcode is not None:
 
279
            req.write("<p>(HTTP error code %d)</p>\n" % httpcode)
 
280
        req.write("""
 
281
<p>Please report this to <a href="mailto:%s">%s</a> (the system
 
282
administrator). Include the following information:</p>
 
283
""" % (cgi.escape(admin_email), cgi.escape(admin_email)))
 
284
 
 
285
        req.write("<pre>\n%s\n</pre>\n"%cgi.escape(tb))
 
286
        if logfail:
 
287
            req.write("<p>Warning: Could not open Error Log: '%s'</p>\n"
 
288
                %cgi.escape(logfile))
 
289
    req.write("</body></html>")