~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to www/apps/fileservice/action.py

  • Committer: mattgiuca
  • Date: 2008-01-10 04:40:27 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:159
fileservice: Split growing module into three modules: top-level,
    listing and action. Listing and action are the well-separated two phases
    of the fileservice response.
    listing: Added a much more detailed report on the output format at the
    top.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# IVLE
 
2
# Copyright (C) 2007-2008 The University of Melbourne
 
3
#
 
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.
 
8
#
 
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.
 
13
#
 
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
 
17
 
 
18
# Module: File Service / Action
 
19
# Author: Matt Giuca
 
20
# Date: 10/1/2008
 
21
 
 
22
# Handles actions requested by the client as part of the 2-stage process of
 
23
# fileservice (the second part being the return listing).
 
24
 
 
25
### Actions ###
 
26
 
 
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.
 
34
 
 
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
 
37
#               multiple times.
 
38
#
 
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
 
42
#               exists.
 
43
#
 
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.
 
49
#
 
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
 
54
# sessions.
 
55
 
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
 
60
#               multiple times.
 
61
 
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
 
66
#               multiple times.
 
67
 
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
 
71
#               be a file.
 
72
#
 
73
# Subversion actions.
 
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
 
76
#               times.
 
77
#
 
78
# action=svnrevert: Revert a file(s) to its state as of the current revision
 
79
#               / undo local edits.
 
80
#       path:   The path to the file to be reverted. Can be specified multiple
 
81
#               times.
 
82
#
 
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
 
85
#               times.
 
86
#
 
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
 
90
#               recursively.
 
91
#       logmsg: Text of the log message. Optional. There is a default log
 
92
#               message if unspecified.
 
93
 
94
# TODO: Implement the following actions:
 
95
#   move, copy, cut, paste, svnadd, svnrevert, svnupdate, svncommit
 
96
 
 
97
import os
 
98
import shutil
 
99
import stat
 
100
import time
 
101
import mimetypes
 
102
 
 
103
import pysvn
 
104
 
 
105
from common import (util, studpath)
 
106
import conf.mimetypes
 
107
 
 
108
DEFAULT_LOGMESSAGE = "No log message supplied."
 
109
 
 
110
# Make a Subversion client object
 
111
svnclient = pysvn.Client()
 
112
 
 
113
# Mime types
 
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"
 
118
 
 
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,
 
123
    and continue.
 
124
 
 
125
    Important Security Consideration: The message passed to this
 
126
    exception will be relayed to the client.
 
127
    """
 
128
    pass
 
129
 
 
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.
 
136
 
 
137
    May throw an ActionError. The caller should put this string into the
 
138
    X-IVLE-Action-Error header, and then continue normally.
 
139
 
 
140
    action: String, the action requested. Not sanitised.
 
141
    fields: FieldStorage object containing all arguments passed.
 
142
    """
 
143
    global actions_table        # Table of function objects
 
144
    try:
 
145
        action = actions_table[action]
 
146
    except KeyError:
 
147
        # Default, just send an error but then continue
 
148
        raise ActionError("Unknown action")
 
149
    action(req, fields)
 
150
 
 
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
 
157
    supplied path.
 
158
 
 
159
    This resolves the path, given the request and path argument.
 
160
 
 
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.
 
164
 
 
165
    Does not mutate req.
 
166
    """
 
167
    if path is None:
 
168
        path = req.path
 
169
    elif len(path) > 0 and path[0] == os.sep:
 
170
        # Relative to student home
 
171
        path = path[1:]
 
172
    else:
 
173
        # Relative to req.path
 
174
        path = os.path.join(req.path, path)
 
175
 
 
176
    _, r = studpath.url_to_local(path)
 
177
    if r is None:
 
178
        raise ActionError("Invalid path")
 
179
    return r
 
180
 
 
181
### ACTIONS ###
 
182
 
 
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.
 
187
 
 
188
    Reads fields: 'path' (multiple)
 
189
    """
 
190
    paths = fields.getlist('path')
 
191
    goterror = False
 
192
    for path in paths:
 
193
        path = actionpath_to_local(req, path)
 
194
        try:
 
195
            if os.path.isdir(path):
 
196
                shutil.rmtree(path)
 
197
            else:
 
198
                os.remove(path)
 
199
        except OSError:
 
200
            goterror = True
 
201
        except shutil.Error:
 
202
            goterror = True
 
203
    if goterror:
 
204
        if len(paths) == 1:
 
205
            raise ActionError("Could not delete the file specified")
 
206
        else:
 
207
            raise ActionError(
 
208
                "Could not delete one or more of the files specified")
 
209
 
 
210
def action_putfile(req, fields):
 
211
    """Writes data to a file, overwriting it if it exists and creating it if
 
212
    it doesn't.
 
213
 
 
214
    Reads fields: 'path', 'data' (file upload)
 
215
    """
 
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)
 
221
    data = data.file
 
222
 
 
223
    # Copy the contents of file object 'data' to the path 'path'
 
224
    try:
 
225
        dest = open(path, 'wb')
 
226
        shutil.copyfileobj(data, dest)
 
227
    except OSError:
 
228
        raise ActionError("Could not write to target file")
 
229
 
 
230
# Table of all action functions #
 
231
 
 
232
actions_table = {
 
233
    "remove" : action_remove,
 
234
    "putfile" : action_putfile,
 
235
}