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
(self.app, self.path) = (ivle.util.split_path(req.uri))
123
self.hostname = req.hostname
124
self.headers_in = req.headers_in
125
self.headers_out = req.headers_out
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)
131
# Default values for the output members
132
self.status = Request.HTTP_OK
133
self.content_type = None # Use Apache's default
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
144
def __writeheaders(self):
145
"""Writes out the HTTP and HTML headers before any real data is
147
self.headers_written = True
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
156
def ensure_headers_written(self):
157
"""Writes out the HTTP and HTML headers if they haven't already been
159
if not self.headers_written:
160
self.__writeheaders()
162
def write(self, string, flush=1):
163
"""Writes string directly to the client, then flushes the buffer,
164
unless flush is 0."""
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)
173
# 8-bit clean strings just get written directly.
174
# This includes binary strings.
175
self.apache_req.write(string, flush)
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)
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(''))
195
"""Flushes the output buffer."""
196
self.apache_req.flush()
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)
204
def read(self, len=None):
205
"""Reads at most len bytes directly from the client. (See mod_python
208
return self.apache_req.read()
210
return self.apache_req.read(len)
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.
217
httpcode: An HTTP response status code. Pass a constant from the
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"))
224
def add_cookie(self, cookie, value=None, **attributes):
225
"""Inserts a cookie into this request object's headers."""
227
mod_python.Cookie.add_cookie(self.apache_req, cookie)
229
mod_python.Cookie.add_cookie(self.apache_req, cookie, value, **attributes)
231
def make_path(self, path):
232
"""Prepend the IVLE URL prefix to the given path.
234
This is used when generating URLs to send to the client.
236
This method is DEPRECATED. We no longer support use of a prefix.
238
return os.path.join(self.config['urls']['root'], path)
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.
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!
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)
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)
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