src/hg-ssh
branchdebian
changeset 115 731a72b742db
parent 110 69596fffcf7d
child 117 b6b8a5daf0f4
--- a/src/hg-ssh	Thu May 28 10:43:30 2009 +0100
+++ b/src/hg-ssh	Tue Oct 13 15:30:03 2009 +0100
@@ -33,45 +33,53 @@
 
 from mercurial import dispatch
 
-import sys, os
+import sys, os, os.path
+import base64
 from mercurialserver import ruleset, paths
 
 def fail(message):
-    #logfile.write("Fail: %s\n" % message)
-    sys.stderr.write(message + "\n")
+    sys.stderr.write("mercurial-server: %s\n" % message)
     sys.exit(-1)
 
-def getpath(path):
-    if path.endswith("/"):
-        path = path[:-1]
-    if not ruleset.goodpath(path):
-        fail("Disallowing path: %s" % path)
-    return path
+def checkDots(path):
+    head, tail = os.path.split(path)
+    if tail.startswith("."):
+        fail("paths cannot contain dot file components")
+    if head:
+        checkDots(head)
+
+def checkParents(path):
+    path = os.path.dirname(path)
+    if path == "":
+        return
+    if os.path.exists(path + "/.hg"):
+        fail("Cannot create repo under existing repo")
+    checkParents(path)
 
-def try_cmd(cmd):
-    if cmd.startswith('hg -R ') and cmd.endswith(' serve --stdio'):
-        repo = getpath(cmd[6:-14])
-        ruleset.rules.set(repo=repo)
-        if ruleset.rules.allow("read", branch=None, file=None):
-            dispatch.dispatch(['-R', repo, 'serve', '--stdio'])
-            return
-    elif cmd.startswith('hg init '):
-        repo = getpath(cmd[8:])
-        ruleset.rules.set(repo=repo)
-        if ruleset.rules.allow("init", branch=None, file=None):
-            dispatch.dispatch(['init', repo])
-            return
-    fail("Illegal command %r" % cmd)
-
-#logfile = open("/tmp/hg-ssh.%d.txt" % os.getpid(), "w")
-#logfile.write("Started: %s\n" % sys.argv)
-
-if len(sys.argv) != 2:
-    fail("hg-ssh must have exactly one argument (%s)" 
-        % sys.argv)
+def getrepo(op, repo):
+    # First canonicalise, then check the string, then the rules
+    # and finally the filesystem.
+    repo = repo.rstrip("/")
+    if len(repo) == 0:
+        fail("path to repository seems to be empty")
+    if repo.startswith("/"):
+        fail("absolute paths are not supported")
+    checkDots(path)
+    ruleset.rules.set(repo=repo)
+    if not ruleset.rules.allow(op, branch=None, file=None):
+        fail("access denied")
+    checkParents(repo)
+    return repo
 
 paths.setExePath()
-ruleset.rules.set(user = sys.argv[1])
+
+if len(sys.argv) == 3 and sys.argv[1] == "--base64":
+    ruleset.rules.set(user = base64.b64decode(sys.argv[2]))
+elif len(sys.argv) == 2:
+    ruleset.rules.set(user = sys.argv[1])
+else:
+    fail("hg-ssh wrongly called, is authorized_keys corrupt? (%s)" 
+        % sys.argv)
 
 # Use a different hgrc for remote pulls - this way you can set
 # up access.py for everything at once without affecting local operations
@@ -86,4 +94,22 @@
     if os.path.isfile(f):
         ruleset.rules.readfile(f)
 
-try_cmd(os.environ.get('SSH_ORIGINAL_COMMAND', '?'))
+cmd = os.environ.get('SSH_ORIGINAL_COMMAND', None)
+if cmd is None:
+    fail("direct logins on the hg account prohibited")
+elif cmd.startswith('hg -R ') and cmd.endswith(' serve --stdio'):
+    repo = getrepo("read", cmd[6:-14])
+    if not os.path.isdir(repo + "/.hg"):
+        fail("no such repository %s" % repo)
+    dispatch.dispatch(['-R', repo, 'serve', '--stdio'])
+elif cmd.startswith('hg init '):
+    repo = getrepo("init", cmd[8:])
+    if os.path.exists(repo):
+        fail("%s exists" % repo)
+    d = os.path.dirname(repo)
+    if d != "" and not os.path.isdir(d):
+        os.makedirs(d)
+    dispatch.dispatch(['init', repo])
+else:
+    fail("illegal command %r" % cmd)
+