13f391382e54e6589f383d87069040926c910196
max
  Fri Jun 6 05:57:51 2025 -0700
fixing web.c to output cookie so hgGateway uses cookies, refs #35824

diff --git src/hg/lib/cart.c src/hg/lib/cart.c
index aa6d1b4aeeb..069c527b963 100644
--- src/hg/lib/cart.c
+++ src/hg/lib/cart.c
@@ -1511,32 +1511,31 @@
 
     boolean res = startsWith("{\"success\":true", reply);
     freez(&reply);
     return res;
 }
 
 #define CLOUDFLARESITEKEY "cloudFlareSiteKey"
 
 void printCaptcha() 
 /* print an html page that shows the captcha and on success, reloads the page with the token added as token=x */
 {
     char *cfSiteKey = cfgVal(CLOUDFLARESITEKEY);
     if (!cfSiteKey)
         return;
 
-    puts("Content-Type:text/html");
-    puts("\n");
+    puts("Content-Type:text/html\n"); // puts outputs one newline. Header requires two newlines.
     puts("<html><head>");
     puts("<script>");
     printf("function showWidget() { \n"
        "turnstile.render('#myWidget', {\n"
          "sitekey: '%s',\n"
          "theme: 'light',\n"
          "callback: function (token) {\n"
          "     const parser = new URL(window.location);\n"
          "     parser.searchParams.set('token', token);\n"
          "     window.location = parser.href;\n"
          "   },\n"
        "});\n"
        "}\n", cfSiteKey);
     puts("</script>");
     puts("</head><body>");
@@ -1562,37 +1561,35 @@
 
 // let rtracklayer user agent pass, but allow us to remove this exception in case the bots discover it one day
 if (!cfgOption("blockRtracklayer") && sameOk(cgiUserAgent(), "rtracklayer"))
     return;
 
 // QA can add a user agent after release, in case someone complains that their library is blocked
 char *okUserAgent = cfgOption("okUserAgent");
 if (okUserAgent && sameOk(cgiUserAgent(), okUserAgent))
     return;
 
 // Do not show a captcha if we have a valid cookie 
 // but for debugging, it's nice to be force the captcha to come up
 if (userId && userIdFound && !cgiOptionalString("captcha"))
     return;
 
-// Do not show a captcha if the request is an AJAX request coming from jQuery
-// This is a hack to work around an error in hgGateway and may soon disappear.
+// This is a hack to let all AJAX requests pass without cookies, no needed anymore.
 // It's a hack because the header can be set by any curl script to get around
-// the captcha. Right now, hgGateway is not setting any cookie at all, so the 
-// correct solution would be to change that.
-if (sameOk(getenv("HTTP_X_REQUESTED_WITH"), "XMLHttpRequest"))
-    return;
+// the captcha. 
+//if (sameOk(getenv("HTTP_X_REQUESTED_WITH"), "XMLHttpRequest"))
+    //return;
 
 char *token = cgiOptionalString("token");
 
 if (token && isValidToken(token))
 {
     cartRemove(cart, "token");
     return;
 }
 
 printCaptcha();
 }
 
 void cartRemove(struct cart *cart, char *var);
 
 struct cart *cartNew(char *userId, char *sessionId,
@@ -2700,49 +2697,53 @@
 return cart;
 }
 
 static void addHttpHeaders()
 /* CGIs can initialize the global variable httpHeaders to control their own HTTP
  * headers. This allows, for example, to prevent web browser caching of hgTracks
  * responses, but implicitly allow web browser caching everywhere else */
 {
 struct slPair *h;
 for (h = httpHeaders; h != NULL; h = h->next)
     {
     printf("%s: %s\n", h->name, (char *)h->val);
     }
 }
 
+void cartWriteHeaderAndCont(struct cart* cart, char *cookieName)
+/* write http headers including cookie and content type line */
+{
+addHttpHeaders();
+cartWriteCookie(cart, cookieName);
+puts("Content-Type: text/html\n"); // puts outputs one newline. Headers requires two.
+cartDidContentType = TRUE;
+}
+
 struct cart *cartAndCookieWithHtml(char *cookieName, char **exclude,
                                    struct hash *oldVars, boolean doContentType)
 /* Load cart from cookie and session cgi variable.  Write cookie
  * and optionally content-type part HTTP preamble to web page.  Don't
  * write any HTML though. */
 {
 // Note: early abort works fine but early warn does not
 htmlPushEarlyHandlers();
 struct cart *cart = cartForSession(cookieName, exclude, oldVars);
 popWarnHandler();
 popAbortHandler();
 
 if (doContentType && !cartDidContentType)
-    {
-    addHttpHeaders();
-    cartWriteCookie(cart, cookieName);
-    puts("Content-Type:text/html");
-    cartDidContentType = TRUE;
-    }
+    cartWriteHeaderAndCont(cart, cookieName);
 
 return cart;
 }
 
 struct cart *cartAndCookie(char *cookieName, char **exclude,
                            struct hash *oldVars)
 /* Load cart from cookie and session cgi variable.  Write cookie and
  * content-type part HTTP preamble to web page.  Don't write any HTML though. */
 {
 return cartAndCookieWithHtml(cookieName, exclude, oldVars, TRUE);
 }
 
 struct cart *cartAndCookieNoContent(char *cookieName, char **exclude,
                                     struct hash *oldVars)
 /* Load cart from cookie and session cgi variable. Don't write out