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

« back to all changes in this revision

Viewing changes to ivle/dispatch/request.py

  • Committer: William Grant
  • Date: 2009-12-08 10:05:52 UTC
  • Revision ID: grantw@unimelb.edu.au-20091208100552-51qd6m3pl3h2owkk
Improve the diff/log breadcrumbs.

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
 
19
 
 
20
"""
 
21
IVLE Request Object
 
22
 
 
23
Builds an IVLE request object from a mod_python request object.
 
24
See design notes/apps/dispatch.txt for a full specification of this request
 
25
object.
 
26
"""
 
27
 
 
28
try:
 
29
    import mod_python.Session
 
30
    import mod_python.Cookie
 
31
    import mod_python.util
 
32
except ImportError:
 
33
    # This needs to be importable from outside Apache.
 
34
    pass
 
35
 
 
36
import os.path
 
37
 
 
38
import ivle.util
 
39
import ivle.database
 
40
from ivle.webapp.base.plugins import CookiePlugin
 
41
 
 
42
class Request:
 
43
    """An IVLE request object. This is presented to the IVLE apps as a way of
 
44
    interacting with the web server and the dispatcher.
 
45
 
 
46
    Request object attributes:
 
47
        method (read)
 
48
            String. The request method (eg. 'GET', 'POST', etc)
 
49
        uri (read)
 
50
            String. The path portion of the URI.
 
51
        app (read)
 
52
            String. Name of the application specified in the URL, or None.
 
53
        path (read)
 
54
            String. The path specified in the URL *not including* the
 
55
            application or the IVLE location prefix. eg. a URL of
 
56
            "/ivle/files/joe/myfiles" has a path of "joe/myfiles".
 
57
        user (read)
 
58
            User object. Details of the user who is currently logged in, or
 
59
            None.
 
60
        store (read)
 
61
            storm.store.Store instance. Holds a database transaction open,
 
62
            which is available for the entire lifetime of the request.
 
63
        hostname (read)
 
64
            String. Hostname the server is running on.
 
65
        headers_in (read)
 
66
            Table object representing headers sent by the client.
 
67
        headers_out (read, can be written to)
 
68
            Table object representing headers to be sent to the client.
 
69
        publicmode (read)
 
70
            Bool. True if the request came for the "public host" as
 
71
            configured in conf.py. Note that public mode requests do not
 
72
            have an app (app is set to None).
 
73
 
 
74
        status (write)
 
75
            Int. Response status number. Use one of the status codes defined
 
76
            in class Request.
 
77
        content_type (write)
 
78
            String. The Content-Type (mime type) header value.
 
79
        location (write)
 
80
            String. Response "Location" header value. Used with HTTP redirect
 
81
            responses.
 
82
    """
 
83
 
 
84
    # Special code for an OK response.
 
85
    # Do not use HTTP_OK; for some reason Apache produces an "OK" error
 
86
    # message if you do that.
 
87
    OK  = 0
 
88
 
 
89
    # HTTP status codes
 
90
 
 
91
    HTTP_OK                           = 200
 
92
    HTTP_MOVED_TEMPORARILY            = 302
 
93
    HTTP_FORBIDDEN                    = 403
 
94
    HTTP_NOT_FOUND                    = 404
 
95
    HTTP_INTERNAL_SERVER_ERROR        = 500
 
96
 
 
97
    def __init__(self, req, config):
 
98
        """Create an IVLE request from a mod_python one.
 
99
 
 
100
        @param req: A mod_python request.
 
101
        @param config: An IVLE configuration.
 
102
        """
 
103
 
 
104
        # Methods are mostly wrappers around the Apache request object
 
105
        self.apache_req = req
 
106
        self.config = config
 
107
        self.headers_written = False
 
108
 
 
109
        # Determine if the browser used the public host name to make the
 
110
        # request (in which case we are in "public mode")
 
111
        if req.hostname == config['urls']['public_host']:
 
112
            self.publicmode = True
 
113
        else:
 
114
            self.publicmode = False
 
115
 
 
116
        # Inherit values for the input members
 
117
        self.method = req.method
 
118
        self.uri = req.uri
 
119
        # Split the given path into the app (top-level dir) and sub-path
 
120
        # (after first stripping away the root directory)
 
121
        (self.app, self.path) = (ivle.util.split_path(req.uri))
 
122
        self.user = None
 
123
        self.hostname = req.hostname
 
124
        self.headers_in = req.headers_in
 
125
        self.headers_out = req.headers_out
 
126
 
 
127
        # Open a database connection and transaction, keep it around for users
 
128
        # of the Request object to use
 
129
        self.store = ivle.database.get_store(config)
 
130
 
 
131
        # Default values for the output members
 
132
        self.status = Request.HTTP_OK
 
133
        self.content_type = None        # Use Apache's default
 
134
        self.location = None
 
135
        # In some cases we don't want the template JS (such as the username
 
136
        # and public FQDN) in the output HTML. In that case, set this to 0.
 
137
        self.write_javascript_settings = True
 
138
        self.got_common_vars = False
 
139
 
 
140
    def __del__(self):
 
141
        """Cleanup."""
 
142
        self.store.close()
 
143
 
 
144
    def __writeheaders(self):
 
145
        """Writes out the HTTP and HTML headers before any real data is
 
146
        written."""
 
147
        self.headers_written = True
 
148
 
 
149
        # Prepare the HTTP and HTML headers before the first write is made
 
150
        if self.content_type != None:
 
151
            self.apache_req.content_type = self.content_type
 
152
        self.apache_req.status = self.status
 
153
        if self.location != None:
 
154
            self.apache_req.headers_out['Location'] = self.location
 
155
 
 
156
    def ensure_headers_written(self):
 
157
        """Writes out the HTTP and HTML headers if they haven't already been
 
158
        written."""
 
159
        if not self.headers_written:
 
160
            self.__writeheaders()
 
161
 
 
162
    def write(self, string, flush=1):
 
163
        """Writes string directly to the client, then flushes the buffer,
 
164
        unless flush is 0."""
 
165
 
 
166
        if not self.headers_written:
 
167
            self.__writeheaders()
 
168
        if isinstance(string, unicode):
 
169
            # Encode unicode strings as UTF-8
 
170
            # (Otherwise cannot handle being written to a bytestream)
 
171
            self.apache_req.write(string.encode('utf8'), flush)
 
172
        else:
 
173
            # 8-bit clean strings just get written directly.
 
174
            # This includes binary strings.
 
175
            self.apache_req.write(string, flush)
 
176
 
 
177
    def logout(self):
 
178
        """Log out the current user by destroying the session state.
 
179
        Then redirect to the top-level IVLE page."""
 
180
        if hasattr(self, 'session'):
 
181
            self.session.invalidate()
 
182
            self.session.delete()
 
183
            # Invalidates all IVLE cookies
 
184
            all_cookies = mod_python.Cookie.get_cookies(self)
 
185
 
 
186
            # Create cookies for plugins that might request them.
 
187
            for plugin in self.config.plugin_index[CookiePlugin]:
 
188
                for cookie in plugin.cookies:
 
189
                    self.add_cookie(mod_python.Cookie.Cookie(cookie, '',
 
190
                                                    expires=1, path='/'))
 
191
        self.throw_redirect(self.make_path(''))
 
192
 
 
193
 
 
194
    def flush(self):
 
195
        """Flushes the output buffer."""
 
196
        self.apache_req.flush()
 
197
 
 
198
    def sendfile(self, filename):
 
199
        """Sends the named file directly to the client."""
 
200
        if not self.headers_written:
 
201
            self.__writeheaders()
 
202
        self.apache_req.sendfile(filename)
 
203
 
 
204
    def read(self, len=None):
 
205
        """Reads at most len bytes directly from the client. (See mod_python
 
206
        Request.read)."""
 
207
        if len is None:
 
208
            return self.apache_req.read()
 
209
        else:
 
210
            return self.apache_req.read(len)
 
211
 
 
212
    def throw_redirect(self, location):
 
213
        """Writes out an HTTP redirect to the specified URL. Raises an
 
214
        exception which is caught by the dispatch or web server, so any
 
215
        code following this call will not be executed.
 
216
 
 
217
        httpcode: An HTTP response status code. Pass a constant from the
 
218
        Request class.
 
219
        """
 
220
        # Note: location may be a unicode, but it MUST only have ASCII
 
221
        # characters (non-ascii characters should be URL-encoded).
 
222
        mod_python.util.redirect(self.apache_req, location.encode("ascii"))
 
223
 
 
224
    def add_cookie(self, cookie, value=None, **attributes):
 
225
        """Inserts a cookie into this request object's headers."""
 
226
        if value is None:
 
227
            mod_python.Cookie.add_cookie(self.apache_req, cookie)
 
228
        else:
 
229
            mod_python.Cookie.add_cookie(self.apache_req, cookie, value, **attributes)
 
230
 
 
231
    def make_path(self, path):
 
232
        """Prepend the IVLE URL prefix to the given path.
 
233
 
 
234
        This is used when generating URLs to send to the client.
 
235
 
 
236
        This method is DEPRECATED. We no longer support use of a prefix.
 
237
        """
 
238
        return os.path.join(self.config['urls']['root'], path)
 
239
 
 
240
    def get_session(self):
 
241
        """Returns a mod_python Session object for this request.
 
242
        Note that this is dependent on mod_python and may need to change
 
243
        interface if porting away from mod_python.
 
244
 
 
245
        IMPORTANT: Call unlock() on the session as soon as you are done with
 
246
                   it! If you don't, all other requests will block!
 
247
        """
 
248
        # Cache the session object and set the timeout to 24 hours.
 
249
        if not hasattr(self, 'session'):
 
250
            self.session = mod_python.Session.FileSession(self.apache_req,
 
251
                                               timeout = 60 * 60 * 24)
 
252
        return self.session
 
253
 
 
254
    def get_fieldstorage(self):
 
255
        """Returns a mod_python FieldStorage object for this request.
 
256
        Note that this is dependent on mod_python and may need to change
 
257
        interface if porting away from mod_python."""
 
258
        # Cache the fieldstorage object
 
259
        if not hasattr(self, 'fields'):
 
260
            self.fields = mod_python.util.FieldStorage(self.apache_req)
 
261
        return self.fields
 
262
 
 
263
    def get_cgi_environ(self):
 
264
        """Returns the CGI environment emulation for this request. (Calls
 
265
        add_common_vars). The environment is returned as a mapping
 
266
        compatible with os.environ."""
 
267
        if not self.got_common_vars:
 
268
            self.apache_req.add_common_vars()
 
269
            self.got_common_vars = True
 
270
        return self.apache_req.subprocess_env
 
271