17
17
# along with this program; if not, write to the Free Software
18
18
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
# Author: Thomas Conway, Will Grant
20
# Script: serveservice
21
# Author: Thomas Conway
24
# A CGI script for serving files.
26
from optparse import OptionParser
31
from ivle import (cgirequest, studpath)
30
32
from ivle import zip as zipmod
31
import ivle.conf.app.server
34
def determine_file_type(filename):
35
filetype = mimetypes.guess_type(filename)[0]
37
filetype = ivle.mimetypes.DEFAULT_MIMETYPE
40
def throw_error(message, extra={}):
41
error = {'error': message}
43
print cjson.encode(error)
46
parser = OptionParser()
47
parser.add_option('-d', '--download', dest='download', action='store_true',
48
help='force download, not execution, of the paths')
49
(options, args) = parser.parse_args()
51
# Detect download mode. Download mode zips any multiple selection,
52
# and does not execute CGI scripts.
55
# paths is filled later.
34
req = cgirequest.CGIRequest()
35
req.install_error_handler()
37
# Work out the parts of the URL
38
url = urlparse.urlparse(req.path)
41
filename = studpath.url_to_jailpaths(urlpath)[2]
60
43
default_mimetype = "application/octet-stream"
61
44
zip_mimetype = "application/zip"
67
# If multiple paths have been specified, zip them up.
69
# Mangle the paths - we want the basename of their dirname in front.
71
dir = os.path.dirname(args[0])
73
assert os.path.dirname(path) == dir
74
paths.append(os.path.join(os.path.basename(dir),
75
os.path.basename(path)))
76
dir = os.path.dirname(dir)
51
# If any "path=" variables have been supplied, bring these into a list and
52
# make a zip file instead.
53
fields = req.get_fieldstorage()
54
paths = fields.getlist("path")
57
zipbasepath = filename
79
58
zipfilename = os.path.basename(zipbasepath)
59
#for i in range(0, len(paths)):
60
#paths[i] = paths[i].value
83
if not os.access(filename, os.F_OK):
84
# The given path doesn't exist. CGI lets us backtrack and put the path
85
# elements through which we pass into PATH_INFO, so we try that.
86
while not os.access(filename, os.F_OK):
87
filename, path_info_frag = os.path.split(filename)
89
# We now have a file that exists, but is it something that we're allowed
90
# to execute? If not, we should 404 anyway.
91
if determine_file_type(filename) not in ivle.conf.app.server.interpreters:
92
throw_error('not-found')
94
# If it's a directory, serve as a zip file
63
req.throw_error(req.HTTP_NOT_FOUND,
64
"The path specified is invalid.")
65
elif not os.access(filename, os.R_OK):
66
req.throw_error(req.HTTP_NOT_FOUND,
67
"The specified file (%s) does not exist." % urlpath)
68
# If it's a directory, serve as a zip file
95
69
if os.path.isdir(filename):
97
# Not giving a directory listing - this is visible to everyone.
98
throw_error('is-directory')
100
71
# Zip it from the perspective of its own parent.
101
72
# That way it will be a directory in the top level of the zip
109
80
zipbasepath = splitpath[0]
110
81
paths = [splitpath[1]]
111
zipfilename = filename
113
if not download and \
114
determine_file_type(filename) in ivle.conf.app.server.interpreters:
115
throw_error('is-executable', {'path': filename.decode('utf-8')})
117
if not download and (
118
(ivle.conf.app.server.blacklist_served_filetypes and \
119
determine_file_type(filename) in \
120
ivle.conf.app.server.served_filetypes_blacklist) or \
121
(ivle.conf.app.server.served_filetypes_whitelist and \
122
determine_file_type(filename) not in \
123
ivle.conf.app.server.served_filetypes_whitelist)):
124
throw_error('forbidden')
82
zipfilename = paths[0]
85
req.content_type = zip_mimetype
127
86
# zipfilename is some filename. Strip trailing slash or extension,
129
88
if zipfilename == '':
133
92
elif '.' in zipfilename:
134
93
zipfilename = zipfilename[:zipfilename.rindex('.')]
135
94
zipfilename += ".zip"
136
#req.headers_out["Content-Disposition"] = ("attachment; filename=" +
137
# zipfilename) # TODO
95
req.headers_out["Content-Disposition"] = ("attachment; filename=" +
138
97
zipfile = StringIO.StringIO()
139
98
zipmod.make_zip(zipbasepath, paths, zipfile)
141
print cjson.encode({'type': zip_mimetype,
142
'name': zipfilename.decode('utf-8')})
100
req.write(zipfile.getvalue())
148
print cjson.encode({'type': determine_file_type(filename),
149
'name': os.path.basename(filename).decode('utf-8')})
150
stream = open(filename)
152
next = stream.read(1024)
154
sys.stdout.write(next)
155
next = stream.read(1024)
102
#req.content_type = default_mimetype
103
req.sendfile(filename)