ce2f414131cc49f0cdd0799a8a0d6a3f94f75e9a
chmalee
  Thu Sep 17 11:57:42 2026 -0700
hubspace: double-encode the user name when building hub URLs

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

diff --git src/hg/lib/userdata.c src/hg/lib/userdata.c
index 4617b662ff6..2256f84954f 100644
--- src/hg/lib/userdata.c
+++ src/hg/lib/userdata.c
@@ -181,32 +181,37 @@
 {
 if (!hubSpaceUrl)
     hubSpaceUrl = cfgOption("hubSpaceUrl");
 return hubSpaceUrl;
 }
 
 char *webDataDir(char *userName)
 /* Return a web accesible path to the userDataDir, this is different from the full path tusd uses */
 {
 char *retUrl = NULL;
 if (userName)
     {
     char *encUserName = cgiEncode(userName);
     char *userPrefix = md5HexForString(encUserName);
     userPrefix[2] = '\0';
+    // the directory on disk is named encUserName, so a user name like "abc-def" is
+    // stored as the literal characters "abc%2Ddef". Encode a second time so apache
+    // looks for that literal '%': the URL component must be "abc%252Ddef".
+    // writeHubStanzasForFile does the same thing to a file name for bigDataUrl
     struct dyString *userDirDy = dyStringNew(0);
-    dyStringPrintf(userDirDy, "%s/%s/%s/", getHubSpaceUrl(), userPrefix, encUserName);
+    dyStringPrintf(userDirDy, "%s/%s/%s/", getHubSpaceUrl(), userPrefix,
+        cgiEncodeFull(encUserName));
     retUrl = dyStringCannibalize(&userDirDy);
     }
 return retUrl;
 }
 
 char *urlForFile(char *userName, char *filePath)
 /* Return a web accessible URL to filePath */
 {
 char *webDataUrl = webDataDir(userName);
 if (webDataUrl)
     {
     return catTwoStrings(webDataUrl, filePath);
     }
 return NULL;
 }