1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
# IVLE
# Copyright (C) 2007-2008 The University of Melbourne
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# App: server
# Author: Tom Conway, Matt Giuca
# Date: 13/12/2007
# Serves content to the user (acting as a web server for students files).
# For most file types we just serve the static file, but
# for python files, we evaluate the python script inside
# our safe execution environment.
from common import (util, studpath, interpret)
import conf
import conf.app.server
import os
import mimetypes
serveservice_path = "/opt/ivle/scripts/serveservice"
# Serve all files as application/octet-stream so the browser presents them as
# a download.
default_mimetype = "application/octet-stream"
zip_mimetype = "application/zip"
def handle(req):
"""Handler for the Server application which serves pages."""
req.write_html_head_foot = False
# Get the username of the student whose work we are browsing, and the path
# on the local machine where the file is stored.
(user, path) = studpath.url_to_local(req.path)
if user is None:
req.throw_error(req.HTTP_NOT_FOUND,
"The path specified is invalid.")
serve_file(req, user, path)
def authorize(req):
"""Given a request, checks whether req.username is allowed to
access req.path. Returns None on authorization success. Raises
HTTP_FORBIDDEN on failure.
"""
if req.publicmode:
# Public mode authorization: any user can access any other user's
# files, BUT the accessed file needs to have its "ivle:published" flag
# turned on in the SVN status.
studpath.authorize_public(req)
else:
# Private mode authorization: standard (only logged in user can access
# their own files, and can access all of them).
studpath.authorize(req)
def serve_file(req, owner, filename, download=False):
"""Serves a file, using one of three possibilities: interpreting the file,
serving it directly, or denying it and returning a 403 Forbidden error.
No return value. Writes to req (possibly throwing a server error exception
using req.throw_error).
req: An IVLE request object.
owner: Username of the user who owns the file being served.
filename: Filename in the local file system.
download: Should the file be viewed in browser or downloaded
"""
# Authorize access. If failure, this throws a HTTP_FORBIDDEN error.
authorize(req)
# First get the mime type of this file
# If download then use "application/octet-stream" type to force the browser
# to download the file.
# (Note that importing common.util has already initialised mime types)
if download:
if os.path.isdir(filename):
type = zip_mimetype
else:
type = default_mimetype
req.headers_out["Content-Disposition"] = "attachment"
else:
(type, _) = mimetypes.guess_type(filename)
if type is None:
type = conf.mimetypes.default_mimetype
# If this type is to be interpreted
if os.path.isdir(filename) and not download:
# 403 Forbidden error for visiting a directory
# (Not giving a directory listing, since this can be seen by
# the world at large. Directory contents are private).
req.throw_error(req.HTTP_FORBIDDEN,
"The path specified is a directory.")
elif type in conf.app.server.interpreters:
interp_name = conf.app.server.interpreters[type]
try:
# Get the interpreter function object
interp_object = interpret.interpreter_objects[interp_name]
(_, jail_dir, path) = studpath.url_to_jailpaths(req.path)
except KeyError:
req.throw_error(req.HTTP_INTERNAL_SERVER_ERROR,
"The interpreter for this file has not been "
"configured correctly.")
interpret.interpret_file(req, owner, jail_dir, path, interp_object)
else:
# Otherwise, use the blacklist/whitelist to see if this file should be
# served or disallowed
if (conf.app.server.blacklist_served_filetypes and \
type in conf.app.server.served_filetypes_blacklist) or \
(conf.app.server.served_filetypes_whitelist and \
type not in conf.app.server.served_filetypes_whitelist):
req.throw_error(req.HTTP_FORBIDDEN,
"Files of this type are not allowed to be served.")
interp_object = interpret.interpreter_objects["cgi-python"]
user_jail_dir = os.path.join(conf.jail_base, owner)
interpret.interpret_file(req, owner, user_jail_dir,
serveservice_path, interp_object)
def serve_file_direct(req, filename, type):
"""Serves a file by directly writing it out to the response.
req: An IVLE request object.
filename: Filename in the local file system.
type: String. Mime type to serve the file with.
"""
if not os.access(filename, os.R_OK):
req.throw_error(req.HTTP_NOT_FOUND,
"The specified file does not exist.")
req.content_type = type
req.sendfile(filename)
|