1d95bea96bc2a652fe77b22c0639eaa888fe858f
max
  Fri Oct 24 05:34:54 2025 -0700
adding CGI var logging option for hg.conf, refs #36533

diff --git src/lib/cheapcgi.c src/lib/cheapcgi.c
index 1e0cccf6ba5..07788267d9e 100644
--- src/lib/cheapcgi.c
+++ src/lib/cheapcgi.c
@@ -1,29 +1,30 @@
 /* Routines for getting variables passed in from web page
  * forms via CGI.
  *
  * This file is copyright 2002 Jim Kent, but license is hereby
  * granted for all use - public, private or commercial. */
 
 #include "common.h"
 #include "hash.h"
 #include "cheapcgi.h"
 #include "portable.h"
 #include "linefile.h"
 #include "errAbort.h"
 #include "filePath.h"
 #include "htmshell.h"
+#include "dystring.h"
 #ifndef GBROWSE
 #include "mime.h"
 #endif /* GBROWSE */
 #include <signal.h>
 
 //============ javascript inline-separation routines ===============
 
 // One of the main services that CSP (Content Security Policy) provides
 // is protecting from reflected and stored XSS attacks by disabling all inline javacript,
 // both in script tags, and in inline event handlers.  The separated javascript 
 // can be either added back to the end of the html page with a nonce or sha hashid,
 // or it can be saved to a temp file in trash and then included as a non-inline, off-page .js.
 
 struct dyString *jsInlineLines = NULL;
 
@@ -227,30 +228,33 @@
 
 
 //============ END of javascript inline-separation routines ===============
 
 
 /* These three variables hold the parsed version of cgi variables. */
 static char *inputString = NULL;
 static unsigned long inputSize;
 static struct hash *inputHash = NULL;
 static struct cgiVar *inputList = NULL;
 
 static boolean haveCookiesHash = FALSE;
 static struct hash *cookieHash = NULL;
 static struct cgiVar *cookieList = NULL;
 
+// maximum length of CGI variables to dump to stderr, 0 = switch off
+static int logCgiVarMaxLen = 0;
+
 /* should cheapcgi use temp files to store uploaded files */
 static boolean doUseTempFile = FALSE;
 
 void dumpCookieList()
 /* Print out the cookie list. */
 {
 struct cgiVar *v;
 for (v=cookieList; v != NULL; v = v->next)
     printf("%s=%s (%d)\n", v->name, v->val, v->saved);
 }
 
 void useTempFile()
 /* tell cheapcgi to use temp files */
 {
 doUseTempFile = TRUE;
@@ -892,71 +896,95 @@
     {
     end = val + strlen(val);
     *pInput = NULL;
     }
 else
     {
     *pInput = end+1;
     *end = 0;
     }
 *retVar = var;
 *retVal = val;
 cgiDecode(val,val,end-val);
 return TRUE;
 }
 
+void cgiSetMaxLogLen(int l)
+/* set the size of variable values that are dumped to stderr. Default is 0, which means no logging */
+{
+logCgiVarMaxLen = l;
+}
+
 void cgiParseInputAbort(char *input, struct hash **retHash,
         struct cgiVar **retList)
 /* Parse cgi-style input into a hash table and list.  This will alter
  * the input data.  The hash table will contain references back
  * into input, so please don't free input until you're done with
  * the hash. Prints message aborts if there's an error.
  * To clean up - slFreeList, hashFree, and only then free input. */
 {
 char *namePt, *dataPt, *nextNamePt;
 struct hash *hash = *retHash;
 struct cgiVar *list = *retList, *el;
 
 if (!hash)
   hash = newHash(6);
 slReverse(&list);
 
+struct dyString *logMsg = NULL;
+if (logCgiVarMaxLen > 0)
+    logMsg = dyStringNew(1024);	
+
 namePt = input;
 while (namePt != NULL && namePt[0] != 0)
     {
     dataPt = strchr(namePt, '=');
     if (dataPt == NULL)
 	{
 	errAbort("Mangled CGI input string %s", namePt);
 	}
     *dataPt++ = 0;
     nextNamePt = strchr(dataPt, '&');
     if (nextNamePt == NULL)
 	nextNamePt = strchr(dataPt, ';');	/* Accomodate DAS. */
     if (nextNamePt != NULL)
          *nextNamePt++ = 0;
+
+    if (logMsg && dataPt && strlen(dataPt) < logCgiVarMaxLen)
+        dyStringPrintf(logMsg, "%s=%s ", namePt, dataPt); // if dataPt is empty string, still print it, could be important
+
     cgiDecode(namePt,namePt,strlen(namePt));	/* for unusual ct names */
     cgiDecode(dataPt,dataPt,strlen(dataPt));
     AllocVar(el);
     el->val = dataPt;
     slAddHead(&list, el);
     hashAddSaveName(hash, namePt, el, &el->name);
     namePt = nextNamePt;
+
+    }
+
+if (logMsg)
+    {
+    char *logStr = dyStringCannibalize(&logMsg);
+    fprintf(stderr, "CGIVARS %s\n", logStr);
+    freez(&logStr);
     }
+
 slReverse(&list);
 *retList = list;
 *retHash = hash;
+
 }
 
 static jmp_buf cgiParseRecover;
 
 static void cgiParseAbort()
 /* Abort cgi parsing. */
 {
 longjmp(cgiParseRecover, -1);
 }
 
 boolean cgiParseInput(char *input, struct hash **retHash,
         struct cgiVar **retList)
 /* Parse cgi-style input into a hash table and list.  This will alter
  * the input data.  The hash table will contain references back
  * into input, so please don't free input until you're done with