Index: src/login.c
==================================================================
--- src/login.c
+++ src/login.c
@@ -500,27 +500,31 @@
   /* Set the capabilities */
   login_set_capabilities(zCap);
   login_set_anon_nobody_capabilities();
 }
 
+/*
+** Memory of settings
+*/
+static int login_anon_once = 1;
+
 /*
 ** Add the default privileges of users "nobody" and "anonymous" as appropriate
 ** for the user g.zLogin.
 */
 void login_set_anon_nobody_capabilities(void){
-  static int once = 1;
-  if( g.zLogin && once ){
+  if( g.zLogin && login_anon_once ){
     const char *zCap;
     /* All logged-in users inherit privileges from "nobody" */
     zCap = db_text("", "SELECT cap FROM user WHERE login = 'nobody'");
     login_set_capabilities(zCap);
     if( fossil_strcmp(g.zLogin, "nobody")!=0 ){
       /* All logged-in users inherit privileges from "anonymous" */
       zCap = db_text("", "SELECT cap FROM user WHERE login = 'anonymous'");
       login_set_capabilities(zCap);
     }
-    once = 0;
+    login_anon_once = 0;
   }
 }
 
 /*
 ** Set the global capability flags based on a capability string.
@@ -617,18 +621,68 @@
       case 's':  rc = g.okSetup;     break;
       case 't':  rc = g.okTktFmt;    break;
       /* case 'u': READER    */
       /* case 'v': DEVELOPER */
       case 'w':  rc = g.okWrTkt;     break;
-      /* case 'x': */
+      case 'x':  rc = g.okPrivate;   break;
       /* case 'y': */
       case 'z':  rc = g.okZip;       break;
       default:   rc = 0;             break;
     }
   }
   return rc;
 }
+
+/*
+** Change the login to zUser.
+*/
+void login_as_user(const char *zUser){
+  char *zCap = "";   /* New capabilities */
+
+  /* Turn off all capabilities from prior logins */
+  g.okSetup = 0;
+  g.okAdmin = 0;
+  g.okDelete = 0;
+  g.okPassword = 0;
+  g.okQuery = 0;
+  g.okWrite = 0;
+  g.okRead = 0;
+  g.okHistory = 0;
+  g.okClone = 0;
+  g.okRdWiki = 0;
+  g.okNewWiki = 0;
+  g.okApndWiki = 0;
+  g.okWrWiki = 0;
+  g.okRdTkt = 0;
+  g.okNewTkt = 0;
+  g.okApndTkt = 0;
+  g.okWrTkt = 0;
+  g.okAttach = 0;
+  g.okTktFmt = 0;
+  g.okRdAddr = 0;
+  g.okZip = 0;
+  g.okPrivate = 0;
+
+  /* Set the global variables recording the userid and login.  The
+  ** "nobody" user is a special case in that g.zLogin==0.
+  */
+  g.userUid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUser);
+  if( g.userUid==0 ){
+    zUser = 0;
+    g.userUid = db_int(0, "SELECT uid FROM user WHERE login='nobody'");
+  }
+  if( g.userUid ){
+    zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", g.userUid);
+  }
+  if( fossil_strcmp(zUser,"nobody")==0 ) zUser = 0;
+  g.zLogin = fossil_strdup(zUser);
+
+  /* Set the capabilities */
+  login_set_capabilities(zCap);
+  login_anon_once = 1;
+  login_set_anon_nobody_capabilities();
+}
 
 /*
 ** Call this routine when the credential check fails.  It causes
 ** a redirect to the "login" page.
 */

Index: src/main.c
==================================================================
--- src/main.c
+++ src/main.c
@@ -968,19 +968,61 @@
     fossil_redirect_home();
   }else{
     zPath = mprintf("%s", zPathInfo);
   }
 
-  /* Remove the leading "/" at the beginning of the path.
+  /* Make g.zPath point to the first element of the path.  Make
+  ** g.zExtra point to everything past that point.
   */
-  g.zPath = &zPath[1];
-  for(i=1; zPath[i] && zPath[i]!='/'; i++){}
-  if( zPath[i]=='/' ){
-    zPath[i] = 0;
-    g.zExtra = &zPath[i+1];
-  }else{
-    g.zExtra = 0;
+  while(1){
+    char *zAltRepo = 0;
+    g.zPath = &zPath[1];
+    for(i=1; zPath[i] && zPath[i]!='/'; i++){}
+    if( zPath[i]=='/' ){
+      zPath[i] = 0;
+      g.zExtra = &zPath[i+1];
+
+      /* Look for sub-repositories.  A sub-repository is another repository
+      ** that accepts the login credentials of the current repository.  A
+      ** subrepository is identified by a CONFIG table entry "subrepo:NAME"
+      ** where NAME is the first component of the path.  The value of the
+      ** the CONFIG entries is the string "USER:FILENAME" where USER is the
+      ** USER name to log in as in the subrepository and FILENAME is the
+      ** repository filename. 
+      */
+      zAltRepo = db_text(0, "SELECT value FROM config WHERE name='subrepo:%q'",
+                         g.zPath);
+      if( zAltRepo ){
+        int nHost;
+        int jj;
+        char *zUser = zAltRepo;
+        login_check_credentials();
+        for(jj=0; zAltRepo[jj] && zAltRepo[jj]!=':'; jj++){}
+        if( zAltRepo[jj]==':' ){
+          zAltRepo[jj] = 0;
+          zAltRepo += jj+1;
+        }else{
+          zUser = "nobody";
+        }
+        if( zAltRepo[0]!='/' ){
+          zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo);
+          file_simplify_name(zAltRepo, -1);
+        }
+        db_close(1);
+        db_open_repository(zAltRepo);
+        login_as_user(zUser);
+        g.okPassword = 0;
+        zPath += i;
+        nHost = g.zTop - g.zBaseURL;
+        g.zBaseURL = mprintf("%z/%s", g.zBaseURL, g.zPath);
+        g.zTop = g.zBaseURL + nHost;
+        continue;
+      }
+    }else{
+      g.zExtra = 0;
+    }
+    break;
   }
   if( g.zExtra ){
     /* CGI parameters get this treatment elsewhere, but places like getfile
     ** will use g.zExtra directly.
     */