1
# IVLE - Informatics Virtual Learning Environment
2
# Copyright (C) 2007-2009 The University of Melbourne
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.
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.
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
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
29
import mod_python.Session
30
import mod_python.Cookie
31
import mod_python.util
33
# This needs to be importable from outside Apache.
40
from ivle.webapp.base.plugins import CookiePlugin
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.
46
Request object attributes:
48
String. The request method (eg. 'GET', 'POST', etc)
50
String. The path portion of the URI.
52
String. Name of the application specified in the URL, or None.
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".
58
User object. Details of the user who is currently logged in, or
61
storm.store.Store instance. Holds a database transaction open,
62
which is available for the entire lifetime of the request.
64
String. Hostname the server is running on.
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.
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).
75
Int. Response status number. Use one of the status codes defined
78
String. The Content-Type (mime type) header value.
80
String. Response "Location" header value. Used with HTTP redirect
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.
92
HTTP_MOVED_TEMPORARILY = 302
95
HTTP_INTERNAL_SERVER_ERROR = 500
97
def __init__(self, req, config):
98
"""Create an IVLE request from a mod_python one.
100
@param req: A mod_python request.
101
@param config: An IVLE configuration.
104
# Methods are mostly wrappers around the Apache request object
105
self.apache_req = req
107
self.headers_written = False
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
114
self.publicmode = False
116
# Inherit values for the input members
117
self.method = req.method
119
# Split the given path into the app (top-level dir) and sub-path
120
# (after first stripping away the root directory)
121
path = self.unmake_path(req.uri)
122
(self.app, self.path) = (ivle.util.split_path(path))
124
self.hostname = req.hostname
125
self.headers_in = req.headers_in
126
self.headers_out = req.headers_out
128
# Open a database connection and transaction, keep it around for users
129
# of the Request object to use
130
self.store = ivle.database.get_store(config)
132
# Default values for the output members
133
self.status = Request.HTTP_OK
134
self.content_type = None # Use Apache's default
136
# In some cases we don't want the template JS (such as the username
137
# and public FQDN) in the output HTML. In that case, set this to 0.
138
self.write_javascript_settings = True
139
self.got_common_vars = False
145
def __writeheaders(self):
146
"""Writes out the HTTP and HTML headers before any real data is
148
self.headers_written = True
150
# Prepare the HTTP and HTML headers before the first write is made
151
if self.content_type != None:
152
self.apache_req.content_type = self.content_type
153
self.apache_req.status = self.status
154
if self.location != None:
155
self.apache_req.headers_out['Location'] = self.location
157
def ensure_headers_written(self):
158
"""Writes out the HTTP and HTML headers if they haven't already been
160
if not self.headers_written:
161
self.__writeheaders()
163
def write(self, string, flush=1):
164
"""Writes string directly to the client, then flushes the buffer,
165
unless flush is 0."""
167
if not self.headers_written:
168
self.__writeheaders()
169
if isinstance(string, unicode):
170
# Encode unicode strings as UTF-8
171
# (Otherwise cannot handle being written to a bytestream)
172
self.apache_req.write(string.encode('utf8'), flush)
174
# 8-bit clean strings just get written directly.
175
# This includes binary strings.
176
self.apache_req.write(string, flush)
179
"""Log out the current user by destroying the session state.
180
Then redirect to the top-level IVLE page."""
181
if hasattr(self, 'session'):
182
self.session.invalidate()
183
self.session.delete()
184
# Invalidates all IVLE cookies
185
all_cookies = mod_python.Cookie.get_cookies(self)
187
# Create cookies for plugins that might request them.
188
for plugin in self.config.plugin_index[CookiePlugin]:
189
for cookie in plugin.cookies:
190
self.add_cookie(mod_python.Cookie.Cookie(cookie, '',
191
expires=1, path='/'))
192
self.throw_redirect(self.make_path(''))
196
"""Flushes the output buffer."""
197
self.apache_req.flush()
199
def sendfile(self, filename):
200
"""Sends the named file directly to the client."""
201
if not self.headers_written:
202
self.__writeheaders()
203
self.apache_req.sendfile(filename)
205
def read(self, len=None):
206
"""Reads at most len bytes directly from the client. (See mod_python
209
return self.apache_req.read()
211
return self.apache_req.read(len)
213
def throw_redirect(self, location):
214
"""Writes out an HTTP redirect to the specified URL. Raises an
215
exception which is caught by the dispatch or web server, so any
216
code following this call will not be executed.
218
httpcode: An HTTP response status code. Pass a constant from the
221
# Note: location may be a unicode, but it MUST only have ASCII
222
# characters (non-ascii characters should be URL-encoded).
223
mod_python.util.redirect(self.apache_req, location.encode("ascii"))
225
def add_cookie(self, cookie, value=None, **attributes):
226
"""Inserts a cookie into this request object's headers."""
228
mod_python.Cookie.add_cookie(self.apache_req, cookie)
230
mod_python.Cookie.add_cookie(self.apache_req, cookie, value, **attributes)
232
def make_path(self, path):
233
"""Prepend the IVLE URL prefix to the given path.
235
This is used when generating URLs to send to the client.
237
This method is DEPRECATED. We no longer support use of a prefix.
239
return os.path.join(self.config['urls']['root'], path)
241
def unmake_path(self, path):
242
"""Strip the IVLE URL prefix from the given path, if present.
244
Also normalises the path.
246
This method is DEPRECATED. We no longer support use of a prefix.
248
path = os.path.normpath(path)
249
root = os.path.normpath(self.config['urls']['root'])
251
if path.startswith(root):
252
path = path[len(root):]
253
# Take out the slash as well
254
if len(path) > 0 and path[0] == os.sep:
259
def get_session(self):
260
"""Returns a mod_python Session object for this request.
261
Note that this is dependent on mod_python and may need to change
262
interface if porting away from mod_python.
264
IMPORTANT: Call unlock() on the session as soon as you are done with
265
it! If you don't, all other requests will block!
267
# Cache the session object and set the timeout to 24 hours.
268
if not hasattr(self, 'session'):
269
self.session = mod_python.Session.FileSession(self.apache_req,
270
timeout = 60 * 60 * 24)
273
def get_fieldstorage(self):
274
"""Returns a mod_python FieldStorage object for this request.
275
Note that this is dependent on mod_python and may need to change
276
interface if porting away from mod_python."""
277
# Cache the fieldstorage object
278
if not hasattr(self, 'fields'):
279
self.fields = mod_python.util.FieldStorage(self.apache_req)
282
def get_cgi_environ(self):
283
"""Returns the CGI environment emulation for this request. (Calls
284
add_common_vars). The environment is returned as a mapping
285
compatible with os.environ."""
286
if not self.got_common_vars:
287
self.apache_req.add_common_vars()
288
self.got_common_vars = True
289
return self.apache_req.subprocess_env
292
def get_http_codename(code):
293
"""Given a HTTP error code int, returns a (name, description)
294
pair, suitable for displaying to the user.
295
May return (None,None) if code is unknown.
296
Only lists common 4xx and 5xx codes (since this is just used
297
to display throw_error error messages).
300
return http_codenames[code]
304
# Human strings for HTTP response codes
306
Request.HTTP_FORBIDDEN:
308
"You are not allowed to view this part of IVLE."),
309
Request.HTTP_NOT_FOUND:
311
"The application or file you requested does not exist."),
312
Request.HTTP_INTERNAL_SERVER_ERROR:
313
("Internal Server Error",
314
"An unknown error occured in IVLE."),