hgweb: teach archive how to download a specific directory or file
authorAngel Ezquerra <angel.ezquerra@gmail.com>
Sun, 10 Feb 2013 11:52:05 +0100
changeset 18771 bb38f4f78104
parent 18770 dcb6a99e82ff
child 18772 0bba1ff2ac7b
hgweb: teach archive how to download a specific directory or file The archive web command now takes into account the "file" request entry, if one is provided. The provided "file" is processed as a "path" corresponding to a directory or file that will be downloaded. With this change hgweb can to process requests such as: http://mercurial.selenic.com/hg/archive/tip.zip/mercurial/templates This will download all files on the mercurial/templates directory as a zip file. It is not possible to specify file patterns ('glob', 'relglob', 'path', 'relpath', 're', 'relre' nor 'set'). The server will reject those with a 403 HTTP error response. Note that this is a first step to add support for downloading directories from the web interface. A following patch will modify the archiveentry map entry on the different templates so that it adds the current folder path to the archive links.
mercurial/hgweb/webcommands.py
tests/test-archive.t
--- a/mercurial/hgweb/webcommands.py	Wed Feb 06 10:06:45 2013 +0100
+++ b/mercurial/hgweb/webcommands.py	Sun Feb 10 11:52:05 2013 +0100
@@ -816,6 +816,19 @@
     if cnode == key or key == 'tip':
         arch_version = short(cnode)
     name = "%s-%s" % (reponame, arch_version)
+
+    ctx = webutil.changectx(web.repo, req)
+    pats = []
+    file = req.form.get('file', None)
+    if file:
+        file = file[0]
+        patandfile = file.split(':')
+        if len(patandfile) > 1 and patandfile[0].lower() in ('glob', 'relglob',
+                'path', 'relpath', 're', 'relre', 'set'):
+            msg = 'Archive pattern not allowed: %s' % file
+            raise ErrorResponse(HTTP_FORBIDDEN, msg)
+        pats = ['path:' + file]
+
     mimetype, artype, extension, encoding = web.archive_specs[type_]
     headers = [
         ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
@@ -825,9 +838,9 @@
     req.headers.extend(headers)
     req.respond(HTTP_OK, mimetype)
 
-    ctx = webutil.changectx(web.repo, req)
+    matchfn = scmutil.match(ctx, pats, default='path')
     archival.archive(web.repo, req, cnode, artype, prefix=name,
-                     matchfn=scmutil.match(ctx, []),
+                     matchfn=matchfn,
                      subrepos=web.configbool("web", "archivesubrepos"))
     return []
 
--- a/tests/test-archive.t	Wed Feb 06 10:06:45 2013 +0100
+++ b/tests/test-archive.t	Sun Feb 10 11:52:05 2013 +0100
@@ -101,6 +101,18 @@
       testing: test-archive-2c0277f05ed4/foo   OK
   No errors detected in compressed data of archive.zip.
 
+test that we can download single directories and files
+
+  $ python getarchive.py "$TIP" gz baz | gunzip | tar tf - 2>/dev/null
+  test-archive-2c0277f05ed4/baz/bletch
+  $ python getarchive.py "$TIP" gz foo | gunzip | tar tf - 2>/dev/null
+  test-archive-2c0277f05ed4/foo
+
+test that we reject unsafe patterns
+
+  $ python getarchive.py "$TIP" gz relre:baz
+  HTTP Error 403: Archive pattern not allowed: relre:baz
+
   $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 
   $ hg archive -t tar test.tar