5c93dde5814fddc96330bbdea7aa3f9ff374aedf
angie
  Wed May 25 15:41:05 2016 -0700
In order to support userNames with leading/trailing spaces, CGI-encode the userName store in the cookie, and CGI-decode before comparing it to gbMembers.userName.  refs #17396 note-15, note-17

diff --git src/hg/lib/wikiLink.c src/hg/lib/wikiLink.c
index c05e473..d22d2d4 100644
--- src/hg/lib/wikiLink.c
+++ src/hg/lib/wikiLink.c
@@ -225,31 +225,31 @@
 if (isNotEmpty(value))
     // Set the cookie to value
     safef(cookieString, sizeof(cookieString), "%s=%s;%s path=/; expires="NO_EXPIRE_COOKIE_DATE,
           name, value, domain);
 else
     // Invalidate the cookie
     safef(cookieString, sizeof(cookieString), "%s=;%s path=/; expires="EXPIRED_COOKIE_DATE,
           name, domain);
 return slNameNew(cookieString);
 }
 
 static struct slName *loginUserNameCookieString(char *userName)
 /* Return a cookie string that sets userName cookie to userName if non-empty and
  * deletes/invalidates the cookie if empty/NULL. */
 {
-return newCookieString(loginUserNameCookie(), userName);
+return newCookieString(loginUserNameCookie(), cgiEncodeFull(userName));
 }
 
 static struct slName *loginIdKeyCookieString(uint idx, char *key)
 /* Return a cookie string that sets ID cookie to idKey if idKey is non-empty and
  * deletes/invalidates the cookie if empty/NULL. */
 {
 char newVal[1024];
 if (isNotEmpty(key))
     safef(newVal, sizeof(newVal), "%u_%s", idx, key);
 else
     safef(newVal, sizeof(newVal), "%u", idx);
 return newCookieString(loginIdKeyCookie(), idx ? newVal : NULL);
 }
 
 static char *makeNewKey()
@@ -336,58 +336,71 @@
 static void remoteHostBypass()
 /* If a remote host was used for login, redirecting back to this server, then this server's
  * central database does not have the correct login token -- so we can't validate cookies.
  * Fall back on the old method of just accepting whatever cookies we get.  Unfortunately
  * we'll have to take a guess at what those cookies are, although hg.conf can override. */
 {
 char *wikiHost = cfgOption(CFG_WIKI_HOST);
 if (isEmpty(wikiHost) || sameString(wikiHost, "HTTPHOST") || sameString(wikiHost, hHttpHost()))
     return;
 alreadyAuthenticated = TRUE;
 cookieStrings = NULL;
 char *cookiePrefix = getRemoteCookiePrefix(wikiHost);
 char *userName = getRemoteCookieVal(CFG_LOGIN_USER_NAME_COOKIE, cookiePrefix, "hgLoginUserName");
 char *idKey = getRemoteCookieVal(CFG_LOGIN_IDKEY_COOKIE, cookiePrefix, "hgLoginToken");
 authenticated = (isNotEmpty(userName) && isNotEmpty(idKey));
-remoteUserName = userName;
+if (isNotEmpty(userName))
+    {
+    remoteUserName = cloneString(userName);
+    cgiDecodeFull(remoteUserName, remoteUserName, strlen(remoteUserName));
+    }
 // BEGIN TODO: remove in July 2016
 if (! authenticated && wikiLinkEnabled())
     {
     // Accept old wiki cookies for now
     char *wikiUserName = findCookieData(wikiLinkUserNameCookie());
     char *wikiLoggedIn = findCookieData(wikiLinkLoggedInCookie());
     if (isNotEmpty(wikiLoggedIn) && isNotEmpty(wikiUserName))
         {
         authenticated = TRUE;
         remoteUserName = wikiUserName;
         }
     }
 // END TODO: remove in July 2016
 }
 
+static char *getLoginUserName()
+/* Get the (CGI-decoded) value of the login userName cookie. */
+{
+char *userName = cloneString(findCookieData(loginUserNameCookie()));
+if (isNotEmpty(userName))
+    cgiDecodeFull(userName, userName, strlen(userName));
+return userName;
+}
+
 struct slName *loginValidateCookies()
 /* Return possibly empty list of cookie strings for the caller to set.
  * If login cookies are obsolete but (formerly) valid, the results sets updated cookies.
  * If login cookies are present but invalid, the result deletes/expires the cookies.
  * Otherwise returns NULL (no change to cookies). */
 {
 remoteHostBypass();
 if (alreadyAuthenticated)
     return cookieStrings;
 alreadyAuthenticated = TRUE;
 authenticated = FALSE;
-char *userName = findCookieData(loginUserNameCookie());
+char *userName = getLoginUserName();
 
 // BEGIN TODO: remove in July 2016
 // If we're using values from old wiki cookies, replace the cookies.
 boolean replaceOldCookies = FALSE;
 if (isEmpty(userName) && wikiLinkEnabled())
     {
     userName = findCookieData(wikiLinkUserNameCookie());
     if (isNotEmpty(userName))
         replaceOldCookies = TRUE;
     }
 boolean deleteCookies = FALSE;
 // END TODO: remove in July 2016
 
 char *cookieKey = NULL;
 uint cookieIdx = getCookieIdKey(&cookieKey, &replaceOldCookies);
@@ -455,31 +468,31 @@
 {
 char *wikiHost = cfgOption(CFG_WIKI_HOST);
 if (isEmpty(wikiHost) || sameString(wikiHost, "HTTPHOST"))
     wikiHost = hHttpHost();
 return cloneString(wikiHost);
 }
 
 char *wikiLinkUserName()
 /* Return the user name specified in cookies from the browser, or NULL if 
  * the user doesn't appear to be logged in. */
 {
 if (loginSystemEnabled())
     {
     if (! alreadyAuthenticated)
         errAbort("wikiLinkUserName: loginValidateCookies must be called first.");
-    char *userName = findCookieData(loginUserNameCookie());
+    char *userName = getLoginUserName();
     if (isEmpty(userName) && wikiLinkEnabled())                   // TODO: remove in July 2016
         userName = findCookieData(wikiLinkUserNameCookie());      // TODO: remove in July 2016
     if (isEmpty(userName) && isNotEmpty(remoteUserName))
         userName = remoteUserName;
     if (authenticated)
         return cloneString(userName);
     }
 else if (wikiLinkEnabled())
     {
     char *wikiUserName = findCookieData(wikiLinkUserNameCookie());
     char *wikiLoggedIn = findCookieData(wikiLinkLoggedInCookie());
     if (isNotEmpty(wikiLoggedIn) && isNotEmpty(wikiUserName))
         return cloneString(wikiUserName);
     }
 else