2
# Copyright (C) 2007-2008 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
18
# Module: File Service / Action
22
# Handles actions requested by the client as part of the 2-stage process of
23
# fileservice (the second part being the return listing).
27
# The most important argument is "action". This determines which action is
28
# taken. Note that action, and all other arguments, are ignored unless the
29
# request is a POST request. The other arguments depend upon the action.
30
# Note that paths are often specified as arguments. Paths that begin with a
31
# slash are taken relative to the user's home directory (the top-level
32
# directory seen when fileservice has no arguments or path). Paths without a
33
# slash are taken relative to the specified path.
35
# action=remove: Delete a file(s) or directory(s) (recursively).
36
# path: The path to the file or directory to delete. Can be specified
39
# action=move: Move or rename a file or directory.
40
# from: The path to the file or directory to be renamed.
41
# to: The path of the target filename. Error if the file already
44
# action=putfile: Upload a file to the student workspace.
45
# path: The path to the file to be written. If it exists, will
46
# overwrite. Error if the target file is a directory.
47
# data: Bytes to be written to the file verbatim. May either be
48
# a string variable or a file upload.
50
# Clipboard-based actions. Cut/copy/paste work in the same way as modern
51
# file browsers, by keeping a server-side clipboard of files that have been
52
# cut and copied. The clipboard is stored in the session data, so it persists
53
# across navigation, tabs and browser windows, but not across browser
56
# action=copy: Write file(s) to the session-based clipboard. Overrides any
57
# existing clipboard data. Does not actually copy the file.
58
# The files are physically copied when the clipboard is pasted.
59
# path: The path to the file or directory to copy. Can be specified
62
# action=cut: Write file(s) to the session-based clipboard. Overrides any
63
# existing clipboard data. Does not actually move the file.
64
# The files are physically moved when the clipboard is pasted.
65
# path: The path to the file or directory to cut. Can be specified
68
# action=paste: Copy or move the files stored in the clipboard. Clears the
69
# clipboard. The files are copied or moved to a specified dir.
70
# path: The path to the DIRECTORY to paste the files to. Must not
74
# action=svnadd: Add an existing file(s) to version control.
75
# path: The path to the file to be added. Can be specified multiple
78
# action=svnrevert: Revert a file(s) to its state as of the current revision
80
# path: The path to the file to be reverted. Can be specified multiple
83
# action=svnupdate: Bring a file up to date with the head revision.
84
# path: The path to the file to be updated. Can be specified multiple
87
# action=svncommit: Commit a file(s) or directory(s) to the repository.
88
# path: The path to the file or directory to be committed. Can be
89
# specified multiple times. Directories are committed
91
# logmsg: Text of the log message. Optional. There is a default log
92
# message if unspecified.
94
# TODO: Implement the following actions:
95
# move, copy, cut, paste, svnadd, svnrevert, svnupdate, svncommit
105
from common import (util, studpath)
106
import conf.mimetypes
108
DEFAULT_LOGMESSAGE = "No log message supplied."
110
# Make a Subversion client object
111
svnclient = pysvn.Client()
114
# application/json is the "best" content type but is not good for
115
# debugging because Firefox just tries to download it
116
mime_dirlisting = "text/html"
117
#mime_dirlisting = "application/json"
119
class ActionError(Exception):
120
"""Represents an error processing an action. This can be
121
raised by any of the action functions, and will be caught
122
by the top-level handler, put into the HTTP response field,
125
Important Security Consideration: The message passed to this
126
exception will be relayed to the client.
130
def handle_action(req, svnclient, action, fields):
131
"""Perform the "action" part of the response.
132
This function should only be called if the response is a POST.
133
This performs the action's side-effect on the server. If unsuccessful,
134
writes the X-IVLE-Action-Error header to the request object. Otherwise,
135
does not touch the request object. Does NOT write any bytes in response.
137
May throw an ActionError. The caller should put this string into the
138
X-IVLE-Action-Error header, and then continue normally.
140
action: String, the action requested. Not sanitised.
141
fields: FieldStorage object containing all arguments passed.
143
global actions_table # Table of function objects
145
action = actions_table[action]
147
# Default, just send an error but then continue
148
raise ActionError("Unknown action")
151
def actionpath_to_local(req, path):
152
"""Determines the local path upon which an action is intended to act.
153
Note that fileservice actions accept two paths: the request path,
154
and the "path" argument given to the action.
155
According to the rules, if the "path" argument begins with a '/' it is
156
relative to the user's home; if it does not, it is relative to the
159
This resolves the path, given the request and path argument.
161
May raise an ActionError("Invalid path"). The caller is expected to
162
let this fall through to the top-level handler, where it will be
163
put into the HTTP response field. Never returns None.
169
elif len(path) > 0 and path[0] == os.sep:
170
# Relative to student home
173
# Relative to req.path
174
path = os.path.join(req.path, path)
176
_, r = studpath.url_to_local(path)
178
raise ActionError("Invalid path")
183
def action_remove(req, fields):
184
# TODO: Do an SVN rm if the file is versioned.
185
# TODO: Disallow removal of student's home directory
186
"""Removes a list of files or directories.
188
Reads fields: 'path' (multiple)
190
paths = fields.getlist('path')
193
path = actionpath_to_local(req, path)
195
if os.path.isdir(path):
205
raise ActionError("Could not delete the file specified")
208
"Could not delete one or more of the files specified")
210
def action_putfile(req, fields):
211
"""Writes data to a file, overwriting it if it exists and creating it if
214
Reads fields: 'path', 'data' (file upload)
216
path = fields.getfirst('path')
217
data = fields.getfirst('data')
218
if path is None: raise ActionError("No path specified")
219
if data is None: raise ActionError("No data specified")
220
path = actionpath_to_local(req, path)
223
# Copy the contents of file object 'data' to the path 'path'
225
dest = open(path, 'wb')
226
shutil.copyfileobj(data, dest)
228
raise ActionError("Could not write to target file")
230
# Table of all action functions #
233
"remove" : action_remove,
234
"putfile" : action_putfile,