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

« back to all changes in this revision

Viewing changes to ivle/fileservice_lib/action.py

  • Committer: William Grant
  • Date: 2010-02-11 08:26:17 UTC
  • Revision ID: grantw@unimelb.edu.au-20100211082617-s7m4lxpurlur3879
Make the error message for disabled accounts a little more descriptive.

Show diffs side-by-side

added added

removed removed

Lines of Context:
91
91
# action=svnupdate: Bring a file up to date with the head revision.
92
92
#       path:   The path to the file to be updated. Only one file may be
93
93
#               specified.
 
94
#       revision: The revision number to update to. If not provided this
 
95
#               defaults to HEAD.
94
96
#
95
97
# action=svnpublish: Set the "published" flag on a file to True.
96
98
#       path:   The path to the file to be published. Can be specified
119
121
#       path:   The path to the directory to be checked (under the IVLE
120
122
#               repository base).
121
123
#
 
124
# action=svncleanup: Recursively clean up the working copy, removing locks,
 
125
#   resuming unfinished operations, etc.
 
126
#       path:   The path to the directory to be cleaned
 
127
#
122
128
# TODO: Implement the following actions:
123
129
#   svnupdate (done?)
124
130
# TODO: Implement ZIP unpacking in putfiles (done?).
131
137
import os
132
138
import cStringIO
133
139
import shutil
 
140
import urllib
134
141
 
135
142
import pysvn
136
143
 
137
144
from ivle import (util, studpath, zip)
 
145
from ivle.fileservice_lib.exceptions import WillNotOverwrite
138
146
import ivle.conf
139
 
 
140
 
def get_login(_realm, existing_login, _may_save):
141
 
    """Callback function used by pysvn for authentication.
142
 
    realm, existing_login, _may_save: The 3 arguments passed by pysvn to
143
 
        callback_get_login.
144
 
        The following has been determined empirically, not from docs:
145
 
        existing_login will be the name of the user who owns the process on
146
 
        the first attempt, "" on subsequent attempts. We use this fact.
147
 
    """
148
 
    # Only provide credentials on the _first_ attempt.
149
 
    # If we're being asked again, then it means the credentials failed for
150
 
    # some reason and we should just fail. (This is not desirable, but it's
151
 
    # better than being asked an infinite number of times).
152
 
    return (existing_login != "", ivle.conf.login, ivle.conf.svn_pass, True)
153
 
 
154
 
# Make a Subversion client object
155
 
svnclient = pysvn.Client()
156
 
svnclient.callback_get_login = get_login
 
147
import ivle.svn
 
148
 
 
149
# Make a Subversion client object (which will log in with this user's
 
150
# credentials, upon request)
 
151
svnclient = ivle.svn.create_auth_svn_client(username=ivle.conf.login,
 
152
                                            password=ivle.conf.svn_pass)
157
153
svnclient.exception_style = 0               # Simple (string) exceptions
158
154
 
159
155
DEFAULT_LOGMESSAGE = "No log message supplied."
194
190
    except KeyError:
195
191
        # Default, just send an error but then continue
196
192
        raise ActionError("Unknown action")
197
 
    action(req, fields)
 
193
    return action(req, fields)
198
194
 
199
195
def actionpath_to_urlpath(req, path):
200
196
    """Determines the URL path (relative to the student home) upon which the
225
221
 
226
222
    Does not mutate req.
227
223
    """
228
 
    (_, _, r) = studpath.url_to_jailpaths(actionpath_to_urlpath(req, path))
 
224
    r = studpath.to_home_path(actionpath_to_urlpath(req, path))
229
225
    if r is None:
230
226
        raise ActionError("Invalid path")
231
227
    return r
263
259
    except shutil.Error:
264
260
        raise ActionError("Could not move the file specified")
265
261
 
 
262
def svn_movefile(req, frompath, topath, copy=False):
 
263
    """Performs an svn move, resolving filenames, checking for any errors,
 
264
    and throwing ActionErrors if necessary. Can also be used to do a copy
 
265
    operation instead.
 
266
 
 
267
    frompath and topath are straight paths from the client. Will be checked.
 
268
    """
 
269
    if frompath is None or topath is None:
 
270
        raise ActionError("Required field missing")
 
271
    frompath = actionpath_to_local(req, frompath)
 
272
    topath = actionpath_to_local(req, topath)
 
273
    if not os.path.exists(frompath):
 
274
        raise ActionError("The source file does not exist")
 
275
    if os.path.exists(topath):
 
276
        if frompath == topath:
 
277
            raise ActionError("Source and destination are the same")
 
278
        raise ActionError("A file already exists with that name")
 
279
 
 
280
    try:
 
281
        if copy:
 
282
            svnclient.copy(frompath, topath)
 
283
        else:
 
284
            svnclient.move(frompath, topath)
 
285
    except OSError:
 
286
        raise ActionError("Could not move the file specified")
 
287
    except pysvn.ClientError:
 
288
        raise ActionError("Could not move the file specified")  
 
289
 
 
290
 
266
291
### ACTIONS ###
267
292
 
268
293
def action_delete(req, fields):
370
395
 
371
396
    Reads fields: 'path', 'data' (file upload, multiple), 'unpack'
372
397
    """
373
 
 
374
398
    # Important: Data is "None" if the file submitted is empty.
375
399
    path = fields.getfirst('path')
376
400
    data = fields['data']
389
413
    for datum in data:
390
414
        # Each of the uploaded files
391
415
        filepath = os.path.join(path, datum.filename)
 
416
        filepath_local = studpath.to_home_path(filepath)
 
417
        if os.path.isdir(filepath_local):
 
418
            raise ActionError("A directory already exists "
 
419
                    + "with that name")
 
420
        else:
 
421
            if os.path.exists(filepath_local):
 
422
                raise ActionError("A file already exists with that name")
392
423
        filedata = datum.file
393
424
 
394
425
        if unpack and datum.filename.lower().endswith(".zip"):
398
429
            # filename)
399
430
            try:
400
431
                # First get the entire path (within jail)
401
 
                _, _, abspath = studpath.url_to_jailpaths(path)
 
432
                abspath = studpath.to_home_path(path)
402
433
                abspath = os.path.join(os.sep, abspath)
403
434
                zip.unzip(abspath, filedata)
404
435
            except (OSError, IOError):
405
436
                goterror = True
 
437
            except WillNotOverwrite, e:
 
438
                raise ActionError("File '" + e.filename + "' already exists.")
406
439
        else:
407
440
            # Not a zip file
408
 
            (_, _, filepath_local) = studpath.url_to_jailpaths(filepath)
 
441
            filepath_local = studpath.to_home_path(filepath)
409
442
            if filepath_local is None:
410
443
                raise ActionError("Invalid path")
411
444
 
444
477
    files = fields.getlist('file')
445
478
    if dst is None or src is None or mode is None:
446
479
        raise ActionError("Required field missing")
447
 
    if mode == "copy":
448
 
        copy = True
449
 
    elif mode == "move":
450
 
        copy = False
451
 
    else:
452
 
        raise ActionError("Invalid mode (must be 'copy' or 'move')")
 
480
 
453
481
    dst_local = actionpath_to_local(req, dst)
454
482
    if not os.path.isdir(dst_local):
455
483
        raise ActionError("dst is not a directory")
464
492
        # The destination is found by taking just the basename of the file
465
493
        topath = os.path.join(dst, os.path.basename(file))
466
494
        try:
467
 
            movefile(req, frompath, topath, copy)
 
495
            if mode == "copy":
 
496
                movefile(req, frompath, topath, True)
 
497
            elif mode == "move":
 
498
                movefile(req, frompath, topath, False)
 
499
            elif mode == "svncopy":
 
500
                svn_movefile(req, frompath, topath, True)
 
501
            elif mode == "svnmove":
 
502
                svn_movefile(req, frompath, topath, False)
 
503
            else:
 
504
                raise ActionError("Invalid mode (must be '(svn)copy' or '(svn)move')")
468
505
        except ActionError, message:
469
506
            # Store the error for later; we want to copy as many as possible
470
507
            if errormsg is None:
488
525
    Reads fields: 'path'
489
526
    """
490
527
    paths = fields.getlist('path')
491
 
    user = studpath.url_to_local(req.path)[0]
 
528
    user = util.split_path(req.path)[0]
492
529
    homedir = "/home/%s" % user
493
530
    if len(paths):
494
531
        paths = map(lambda path: actionpath_to_local(req, path), paths)
495
532
    else:
496
 
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
533
        paths = [studpath.to_home_path(req.path)]
497
534
 
498
535
    # Set all the dirs in home dir world browsable (o+r,o+x)
499
536
    #FIXME: Should really only do those in the direct path not all of the 
523
560
    if len(paths):
524
561
        paths = map(lambda path: actionpath_to_local(req, path), paths)
525
562
    else:
526
 
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
563
        paths = [studpath.to_home_path(req.path)]
527
564
 
528
565
    try:
529
566
        for path in paths:
566
603
def action_svnupdate(req, fields):
567
604
    """Performs a "svn update" to each file specified.
568
605
 
569
 
    Reads fields: 'path'
 
606
    Reads fields: 'path' and 'revision'
570
607
    """
571
608
    path = fields.getfirst('path')
 
609
    revision = fields.getfirst('revision')
572
610
    if path is None:
573
611
        raise ActionError("Required field missing")
 
612
    if revision is None:
 
613
        revision = pysvn.Revision( pysvn.opt_revision_kind.head )
 
614
    else:
 
615
        try:
 
616
            revision = pysvn.Revision(pysvn.opt_revision_kind.number,
 
617
                    int(revision))
 
618
        except ValueError, e:
 
619
            raise ActionError("Bad revision number: '%s'"%revision,)
574
620
    path = actionpath_to_local(req, path)
575
621
 
576
622
    try:
577
 
        svnclient.update(path, recurse=True)
 
623
        svnclient.update(path, recurse=True, revision=revision)
578
624
    except pysvn.ClientError, e:
579
625
        raise ActionError(str(e))
580
626
 
620
666
    if len(paths):
621
667
        paths = map(lambda path: actionpath_to_local(req, path), paths)
622
668
    else:
623
 
        paths = [studpath.url_to_jailpaths(req.path)[2]]
 
669
        paths = [studpath.to_home_path(req.path)]
624
670
 
625
671
    try:
626
672
        for path in paths:
669
715
    paths = fields.getlist('path')
670
716
    if len(paths) != 2:
671
717
        raise ActionError("usage: svncheckout url local-path")
672
 
    url = ivle.conf.svn_addr + "/" + paths[0]
 
718
    url = ivle.conf.svn_addr + "/" + urllib.quote(paths[0])
673
719
    local_path = actionpath_to_local(req, str(paths[1]))
674
720
    try:
675
 
        svnclient.callback_get_login = get_login
676
721
        svnclient.checkout(url, local_path, recurse=True)
677
722
    except pysvn.ClientError, e:
678
723
        raise ActionError(str(e))
684
729
    """
685
730
    path = fields.getfirst('path')
686
731
    logmsg = fields.getfirst('logmsg')
687
 
    url = ivle.conf.svn_addr + "/" + path
 
732
    url = ivle.conf.svn_addr + "/" + urllib.quote(path)
688
733
    try:
689
 
        svnclient.callback_get_login = get_login
690
734
        svnclient.mkdir(url, log_message=logmsg)
691
735
    except pysvn.ClientError, e:
692
736
        raise ActionError(str(e))
694
738
def action_svnrepostat(req, fields):
695
739
    """Discovers whether a path exists in a repo under the IVLE SVN root.
696
740
 
 
741
    If it does exist, returns a dict containing its metadata.
 
742
 
697
743
    Reads fields: 'path'
698
744
    """
699
745
    path = fields.getfirst('path')
700
 
    url = ivle.conf.svn_addr + "/" + path
701
 
    svnclient.exception_style = 1 
 
746
    url = ivle.conf.svn_addr + "/" + urllib.quote(path)
 
747
    svnclient.exception_style = 1
702
748
 
703
749
    try:
704
 
        svnclient.callback_get_login = get_login
705
 
        svnclient.info2(url)
 
750
        info = svnclient.info2(url,
 
751
            revision=pysvn.Revision(pysvn.opt_revision_kind.head))[0][1]
 
752
        return {'svnrevision': info['rev'].number
 
753
                  if info['rev'] and
 
754
                     info['rev'].kind == pysvn.opt_revision_kind.number
 
755
                  else None}
706
756
    except pysvn.ClientError, e:
707
757
        # Error code 170000 means ENOENT in this revision.
708
758
        if e[1][0][1] == 170000:
710
760
        else:
711
761
            raise ActionError(str(e[0]))
712
762
 
 
763
 
 
764
def action_svncleanup(req, fields):
 
765
    """Recursively clean up the working copy, removing locks, resuming 
 
766
    unfinished operations, etc.
 
767
        path:   The path to be cleaned"""
 
768
 
 
769
    path = fields.getfirst('path')
 
770
    if path is None:
 
771
        raise ActionError("Required field missing")
 
772
    path = actionpath_to_local(req, path)
 
773
 
 
774
    try:
 
775
        svnclient.cleanup(path)
 
776
    except pysvn.ClientError, e:
 
777
        raise ActionError(str(e))
 
778
 
 
779
 
713
780
# Table of all action functions #
714
781
# Each function has the interface f(req, fields).
715
782
 
734
801
    "svncheckout" : action_svncheckout,
735
802
    "svnrepomkdir" : action_svnrepomkdir,
736
803
    "svnrepostat" : action_svnrepostat,
 
804
    "svncleanup" : action_svncleanup,
737
805
}