Patchwork [2,of,3,V7] hgweb: teach archive how to download a specific directory or file

login
register
mail settings
Submitter Angel Ezquerra
Date March 12, 2013, 11:31 p.m.
Message ID <4f3f3b2eae9c7ff390f7.1363131104@Angel-PC.localdomain>
Download mbox | patch
Permalink /patch/1119/
State Accepted
Commit bb38f4f78104f16ba3538df3b8cb3465755e5f57
Headers show

Comments

Angel Ezquerra - March 12, 2013, 11:31 p.m.
# HG changeset patch
# User Angel Ezquerra <angel.ezquerra@gmail.com>
# Date 1360493525 -3600
#      Sun Feb 10 11:52:05 2013 +0100
# Node ID 4f3f3b2eae9c7ff390f71b07f9310332010bdde3
# Parent  5760b12836327b1366a986cd6335ce8afc372b0b
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.

Patch

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -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 []
 
diff --git a/tests/test-archive.t b/tests/test-archive.t
--- a/tests/test-archive.t
+++ b/tests/test-archive.t
@@ -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