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

mail settings
Submitter Angel Ezquerra
Date Feb. 17, 2013, 11:14 p.m.
Message ID <45216cdf25a89a639290.1361142841@Angel-PC.localdomain>
Download mbox | patch
Permalink /patch/1029/
State Superseded
Commit bb38f4f78104f16ba3538df3b8cb3465755e5f57
Headers show


Angel Ezquerra - Feb. 17, 2013, 11:14 p.m.
# HG changeset patch
# User Angel Ezquerra <>
# Date 1360493525 -3600
# Node ID 45216cdf25a89a6392901a10fde16b7f96307cc5
# Parent  2f7b559540108a021cba31c0c7affa011ef119cc
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:

This will download all files on the mercurial/templates directory as a zip file.
It is not possible to specify file patterns. Those will be rejected with a 403
reply fromthe server.

Note that this is a first step to add support for downloading directories from
the web interface. Currently the only way to use this feature is by manually
constructing the URL that you want to download. We will have to modify the
archiveentry map entry on the different templates so that it adds the current
folder path to the archive links.

This revision also adds a two tests for this feature to test-archive.t. The
first tests the selective archive feature and the second tests that the server
rejects patterns.


diff --git a/mercurial/hgweb/ b/mercurial/hgweb/
--- a/mercurial/hgweb/
+++ b/mercurial/hgweb/
@@ -816,6 +816,17 @@ 
     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)
+    defaultpat = 'path'
+    if file:
+        pats = [req.form['file'][0]]
+        if ':' in pats[0]:
+            msg = 'Archive pattern not allowed: %s' % pats[0]
+            raise ErrorResponse(HTTP_FORBIDDEN, msg)
     mimetype, artype, extension, encoding = web.archive_specs[type_]
     headers = [
         ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
@@ -825,9 +836,9 @@ 
     req.respond(HTTP_OK, mimetype)
-    ctx = webutil.changectx(web.repo, req)
+    matchfn = scmutil.match(ctx, pats, default=defaultpat)
     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
@@ -100,6 +100,13 @@ 
       testing: test-archive-2c0277f05ed4/baz/bletch   OK
       testing: test-archive-2c0277f05ed4/foo   OK
   No errors detected in compressed data of
+  $ python "$TIP" gz baz | gunzip | tar tf - 2>/dev/null
+  test-archive-2c0277f05ed4/baz/bletch
+test that we reject unsafe patterns
+  $ python "$TIP" gz relre:baz
+  HTTP Error 403: Archive pattern not allowed: relre:baz