/* 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" #ifndef GBROWSE #include "mime.h" #endif /* GBROWSE */ #include /* 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; /* 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; } boolean cgiIsOnWeb() /* Return TRUE if looks like we're being run as a CGI. */ { return getenv("REQUEST_METHOD") != NULL; } char *cgiRequestMethod() /* Return CGI REQUEST_METHOD (such as 'GET/POST/PUT/DELETE/HEAD') */ { return getenv("REQUEST_METHOD"); } char *cgiRequestUri() /* Return CGI REQUEST_URI */ { return getenv("REQUEST_URI"); } char *cgiRequestContentLength() /* Return HTTP REQUEST CONTENT_LENGTH if available*/ { return getenv("CONTENT_LENGTH"); } char *cgiScriptName() /* Return name of script so libs can do context-sensitive stuff. */ { return getenv("SCRIPT_NAME"); } char *cgiServerName() /* Return name of server, better to use cgiServerNamePort() for actual URL construction */ { return getenv("SERVER_NAME"); } char *cgiServerPort() /* Return port number of server, default 80 if not found */ { char *port = getenv("SERVER_PORT"); if (port) return port; else return "80"; } char *cgiServerNamePort() /* Return name of server with port if different than 80 */ { char *port = cgiServerPort(); char *namePort = cgiServerName(); struct dyString *result = newDyString(256); if (namePort) { dyStringPrintf(result,"%s",namePort); if (differentString(port, "80")) dyStringPrintf(result,":%s",port); return dyStringCannibalize(&result); } else return NULL; } char *cgiRemoteAddr() /* Return IP address of client (or "unknown"). */ { static char *dunno = "unknown"; char *remoteAddr = getenv("REMOTE_ADDR"); if (remoteAddr == NULL) remoteAddr = dunno; return remoteAddr; } char *cgiUserAgent() /* Return remote user agent (HTTP_USER_AGENT) or NULL if remote user agent is not known */ { return getenv("HTTP_USER_AGENT"); } enum browserType cgiClientBrowser(char **browserQualifier, enum osType *clientOs, char **clientOsQualifier) /* Return client browser type determined from (HTTP_USER_AGENT) Optionally requuest the additional info about the client */ { // WARNING: The specifics of the HTTP_USER_AGENT vary widely. // This has only been tested on a few cases. static enum browserType clientBrowser = btUnknown; static enum browserType clientOsType = osUnknown; static char *clientBrowserExtra = NULL; static char *clientOsExtra = NULL; if (clientBrowser == btUnknown) { char *userAgent = cgiUserAgent(); if (userAgent != NULL) { //warn(userAgent); // Use this to investigate other cases char *ptr=NULL; // Determine the browser if ((ptr = stringIn("Opera",userAgent)) != NULL) // Must be before IE { clientBrowser = btOpera; } else if ((ptr = stringIn("MSIE ",userAgent)) != NULL) { clientBrowser = btIE; ptr += strlen("MSIE "); clientBrowserExtra = cloneFirstWordByDelimiter(ptr,';'); } else if ((ptr = stringIn("Firefox",userAgent)) != NULL) { clientBrowser = btFF; ptr += strlen("(Firefox/"); clientBrowserExtra = cloneFirstWordByDelimiter(ptr,' '); } else if ((ptr = stringIn("Chrome",userAgent)) != NULL) // Must be before Safari { clientBrowser = btChrome; ptr += strlen("Chrome/"); clientBrowserExtra = cloneFirstWordByDelimiter(ptr,' '); } else if ((ptr = stringIn("Safari",userAgent)) != NULL) { clientBrowser = btSafari; ptr += strlen("Safari/"); clientBrowserExtra = cloneFirstWordByDelimiter(ptr,' '); } else { clientBrowser = btOther; } // Determine the OS if ((ptr = stringIn("Windows",userAgent)) != NULL) { clientOsType = osWindows; ptr += strlen("Windows "); clientOsExtra = cloneFirstWordByDelimiter(ptr,';'); } else if ((ptr = stringIn("Linux",userAgent)) != NULL) { clientOsType = osLinux; ptr += strlen("Linux "); clientOsExtra = cloneFirstWordByDelimiter(ptr,';'); } else if ((ptr = stringIn("Mac ",userAgent)) != NULL) { clientOsType = osMac; ptr += strlen("Mac "); clientOsExtra = cloneFirstWordByDelimiter(ptr,';'); } else { clientOsType = osOther; } } } if (browserQualifier != NULL) { if (clientBrowserExtra != NULL) *browserQualifier = cloneString(clientBrowserExtra); else *browserQualifier = NULL; } if (clientOs != NULL) *clientOs = clientOsType; if (clientOsQualifier != NULL) { if (clientOsExtra != NULL) *clientOsQualifier = cloneString(clientOsExtra); else *clientOsQualifier = NULL; } return clientBrowser; } char *_cgiRawInput() /* For debugging get the unprocessed input. */ { return inputString; } static void getQueryInputExt(boolean abortOnErr) /* Get query string from environment if they've used GET method. */ { inputString = getenv("QUERY_STRING"); if (inputString == NULL) { if (abortOnErr) errAbort("No QUERY_STRING in environment."); inputString = cloneString(""); return; } inputString = cloneString(inputString); } static void getQueryInput() /* Get query string from environment if they've used GET method. */ { getQueryInputExt(TRUE); } static void getPostInput() /* Get input from file if they've used POST method. * Grab any GET QUERY_STRING input first. */ { char *s; long i; int r; getQueryInputExt(FALSE); int getSize = strlen(inputString); s = getenv("CONTENT_LENGTH"); if (s == NULL) errAbort("No CONTENT_LENGTH in environment."); if (sscanf(s, "%lu", &inputSize) != 1) errAbort("CONTENT_LENGTH isn't a number."); s = getenv("CONTENT_TYPE"); if (s != NULL && startsWith("multipart/form-data", s)) { /* use MIME parse on input stream instead, can handle large uploads */ /* inputString must not be NULL so it knows it was set */ return; } int len = getSize + inputSize; if (getSize > 0) ++len; char *temp = needMem((size_t)len+1); for (i=0; i 0) temp[i++] = '&'; strncpy(temp+i, inputString, getSize); temp[len] = 0; freeMem(inputString); inputString = temp; } #define memmem(hay, haySize, needle, needleSize) \ memMatch(needle, needleSize, hay, haySize) #ifndef GBROWSE static void cgiParseMultipart(struct hash **retHash, struct cgiVar **retList) /* process a multipart form */ { char h[1024]; /* hold mime header line */ char *s = NULL, *ct = NULL; struct dyString *dy = newDyString(256); struct mimeBuf *mb = NULL; struct mimePart *mp = NULL; char **env = NULL; struct hash *hash = newHash(6); struct cgiVar *list = NULL, *el; extern char **environ; //debug //fprintf(stderr,"GALT: top of cgiParseMultipart()\n"); //fflush(stderr); /* find the CONTENT_ environment strings, use to make Alternate Header string for MIME */ for(env=environ; *env; env++) if (startsWith("CONTENT_",*env)) { //debug //fprintf(stderr,"%s\n",*env); //debug safef(h,sizeof(h),"%s",*env); s = strchr(h,'_'); /* change env syntax to MIME style header, from _= to -: */ if (!s) errAbort("expecting '_' parsing env var %s for MIME alt header", *env); *s = '-'; s = strchr(h,'='); if (!s) errAbort("expecting '=' parsing env var %s for MIME alt header", *env); *s = ':'; dyStringPrintf(dy,"%s\r\n",h); } dyStringAppend(dy,"\r\n"); /* blank line at end means end of headers */ //debug //fprintf(stderr,"Alternate Header Text:\n%s",dy->string); //fflush(stderr); mb = initMimeBuf(STDIN_FILENO); //debug //fprintf(stderr,"got past initMimeBuf(STDIN_FILENO)\n"); //fflush(stderr); mp = parseMultiParts(mb, cloneString(dy->string)); /* The Alternate Header will get freed */ freeDyString(&dy); if(!mp->multi) /* expecting multipart child parts */ errAbort("Malformatted multipart-form."); //debug //fprintf(stderr,"GALT: Wow got past parse of MIME!\n"); //fflush(stderr); ct = hashFindVal(mp->hdr,"content-type"); //debug //fprintf(stderr,"GALT: main content-type: %s\n",ct); //fflush(stderr); if (!startsWith("multipart/form-data",ct)) errAbort("main content-type expected starts with [multipart/form-data], found [%s]",ct); for(mp=mp->multi;mp;mp=mp->next) { char *cd = NULL, *cdMain = NULL, *cdName = NULL, *cdFileName = NULL, *ct = NULL; cd = hashFindVal(mp->hdr,"content-disposition"); ct = hashFindVal(mp->hdr,"content-type"); //debug //fprintf(stderr,"GALT: content-disposition: %s\n",cd); //fprintf(stderr,"GALT: content-type: %s\n",ct); //fflush(stderr); cdMain=getMimeHeaderMainVal(cd); cdName=getMimeHeaderFieldVal(cd,"name"); cdFileName=getMimeHeaderFieldVal(cd,"filename"); //debug //fprintf(stderr,"cgiParseMultipart: main:[%s], name:[%s], filename:[%s]\n",cdMain,cdName,cdFileName); //fflush(stderr); if (!sameString(cdMain,"form-data")) errAbort("main content-type expected [form-data], found [%s]",cdMain); //debug //fprintf(stderr,"GALT: mp->size[%llu], mp->binary=[%d], mp->fileName=[%s], mp=>data:[%s]\n", //(unsigned long long) mp->size, mp->binary, mp->fileName, //mp->binary && mp->data ? "" : mp->data); //fflush(stderr); /* filename if there is one */ /* Internet Explorer on Windows is sending full path names, strip * directory name from those. Using \ and / and : as potential * path separator characters, e.g.: * C:\Documents and Settings\tmp\file.txt.gz */ if (cdFileName) { char *lastPathSep = strrchr(cdFileName, (int) '\\'); if (!lastPathSep) lastPathSep = strrchr(cdFileName, (int) '/'); if (!lastPathSep) lastPathSep = strrchr(cdFileName, (int) ':'); char varNameFilename[256]; safef(varNameFilename, sizeof(varNameFilename), "%s__filename", cdName); AllocVar(el); if (lastPathSep) el->val = cloneString(lastPathSep+1); else el->val = cloneString(cdFileName); slAddHead(&list, el); hashAddSaveName(hash, varNameFilename, el, &el->name); } if (mp->data) { if (mp->binary) { char varNameBinary[256]; char addrSizeBuf[40]; safef(varNameBinary,sizeof(varNameBinary),"%s__binary",cdName); safef(addrSizeBuf,sizeof(addrSizeBuf),"%lu %llu", (unsigned long)mp->data, (unsigned long long)mp->size); AllocVar(el); el->val = cloneString(addrSizeBuf); slAddHead(&list, el); hashAddSaveName(hash, varNameBinary, el, &el->name); } else /* normal variable, not too big, does not contain zeros */ { AllocVar(el); el->val = mp->data; slAddHead(&list, el); hashAddSaveName(hash, cdName, el, &el->name); } } else if (mp->fileName) { char varNameData[256]; safef(varNameData, sizeof(varNameData), "%s__data", cdName); AllocVar(el); el->val = mp->fileName; slAddHead(&list, el); hashAddSaveName(hash, varNameData, el, &el->name); //debug //fprintf(stderr,"GALT special: saved varNameData:[%s], mp=>fileName:[%s]\n",el->name,el->val); //fflush(stderr); } else if (mp->multi) { warn("unexpected nested MIME structures"); } else { errAbort("mp-> type not data,fileName, or multi - unexpected MIME structure"); } freez(&cdMain); freez(&cdName); freez(&cdFileName); } slReverse(&list); *retList = list; *retHash = hash; } #endif /* GBROWSE */ static void parseCookies(struct hash **retHash, struct cgiVar **retList) /* parses any cookies and puts them into the given hash and list */ { char* str; char *namePt, *dataPt, *nextNamePt; struct hash *hash; struct cgiVar *list = NULL, *el; /* don't build the hash table again */ if(haveCookiesHash == TRUE) return; str = cloneString(getenv("HTTP_COOKIE")); if(str == NULL) /* don't have a cookie */ return; hash = newHash(6); namePt = str; while (isNotEmpty(namePt)) { dataPt = strchr(namePt, '='); if (dataPt == NULL) errAbort("Mangled Cookie input string: no = in '%s' (offset %d in complete cookie string: '%s')", namePt, (int)(namePt - str), getenv("HTTP_COOKIE")); *dataPt++ = 0; nextNamePt = strchr(dataPt, ';'); if (nextNamePt != NULL) { *nextNamePt++ = 0; if (*nextNamePt == ' ') nextNamePt++; } cgiDecode(dataPt,dataPt,strlen(dataPt)); AllocVar(el); el->val = dataPt; slAddHead(&list, el); hashAddSaveName(hash, namePt, el, &el->name); namePt = nextNamePt; } haveCookiesHash = TRUE; slReverse(&list); *retList = list; *retHash = hash; } char *findCookieData(char *varName) /* Get the string associated with varName from the cookie string. */ { struct hashEl *hel; char *firstResult; /* make sure that the cookie hash table has been created */ parseCookies(&cookieHash, &cookieList); if (cookieHash == NULL) return NULL; /* Watch out for multiple cookies with the same name (hel is a list) -- * warn if we find them. */ hel = hashLookup(cookieHash, varName); if (hel == NULL) return NULL; else firstResult = ((struct cgiVar *)hel->val)->val; hel = hel->next; while (hel != NULL) { char *val = ((struct cgiVar *)(hel->val))->val; if (sameString(varName, hel->name) && !sameString(firstResult, val)) { /* This is too early to call warn -- it will mess up html output. */ fprintf(stderr, "findCookieData: Duplicate cookie value from IP=%s: " "%s has both %s and %s\n", cgiRemoteAddr(), varName, firstResult, val); } hel = hel->next; } return firstResult; } static char *cgiInputSource(char *s) /* For NULL sources make a guess as to real source. */ { char *qs; if (s != NULL) return s; qs = getenv("QUERY_STRING"); if (qs == NULL) return "POST"; char *cl = getenv("CONTENT_LENGTH"); if (cl != NULL && atoi(cl) > 0) return "POST"; return "QUERY"; } static void _cgiFindInput(char *method) /* Get raw CGI input into inputString. Method can be "POST", "QUERY", "GET" or NULL * for unknown. */ { if (inputString == NULL) { method = cgiInputSource(method); if (sameWord(method, "POST")) getPostInput(); else if (sameWord(method, "QUERY") || sameWord(method, "GET")) getQueryInput(); else errAbort("Unknown form method"); } } static 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.*/ { char *namePt, *dataPt, *nextNamePt; struct hash *hash = *retHash; struct cgiVar *list = *retList, *el; if (!hash) hash = newHash(6); slReverse(&list); 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; 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; } 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 * the hash. Prints message and returns FALSE if there's an error.*/ { boolean ok = TRUE; int status = setjmp(cgiParseRecover); if (status == 0) /* Always true except after long jump. */ { pushAbortHandler(cgiParseAbort); cgiParseInputAbort(input, retHash, retList); } else /* They long jumped here because of an error. */ { ok = FALSE; } popAbortHandler(); return ok; } static boolean dumpStackOnSignal = FALSE; // should a stack dump be generated? static void catchSignal(int sigNum) /* handler for various terminal signals for logging purposes */ { char *sig = NULL; switch (sigNum) { case SIGTERM: // after timing out for not producing any stdout or stderr output for too long, // apache gives you 3 seconds to clean up and exit or it then sends SIGKILL. sig = "SIGTERM"; break; case SIGHUP: // apache sends this when it is doing a graceful restart or a log rotate. sig = "SIGHUP"; break; case SIGABRT: sig = "SIGABRT"; break; case SIGSEGV: sig = "SIGSEGV"; break; case SIGFPE: sig = "SIGFPE"; break; case SIGBUS: sig = "SIGBUS"; break; } logCgiToStderr(); // apache closes STDERR on the CGI before it sends the SIGTERM and SIGKILL signals // which means that stderr output after that point will not be visible in error_log. fprintf(stderr, "Received signal %s\n", sig); if (dumpStackOnSignal) dumpStack("Stack for signal %s\n", sig); if (sigNum == SIGTERM || sigNum == SIGHUP) exit(1); // so that atexit cleanup get called raise(SIGKILL); } void initSigHandlers(boolean dumpStack) /* set handler for various terminal signals for logging purposes. * if dumpStack is TRUE, attempt to dump the stack. */ { if (cgiIsOnWeb()) { // SIGKILL is not trappable or ignorable signal(SIGTERM, catchSignal); signal(SIGHUP, catchSignal); signal(SIGABRT, catchSignal); signal(SIGSEGV, catchSignal); signal(SIGFPE, catchSignal); signal(SIGBUS, catchSignal); dumpStackOnSignal = dumpStack; } } static void initCgiInput() /* Initialize CGI input stuff. After this CGI vars are * stored in an internal hash/list regardless of how they * were passed to the program. */ { char* s; if (inputString != NULL) return; _cgiFindInput(NULL); #ifndef GBROWSE /* check to see if the input is a multipart form */ s = getenv("CONTENT_TYPE"); if (s != NULL && startsWith("multipart/form-data", s)) { cgiParseMultipart(&inputHash, &inputList); } #endif /* GBROWSE */ cgiParseInputAbort(inputString, &inputHash, &inputList); /* now parse the cookies */ parseCookies(&cookieHash, &cookieList); /* Set enviroment variables CGIs to enable sql tracing and/or profiling */ s = cgiOptionalString("JKSQL_TRACE"); if (s != NULL) envUpdate("JKSQL_TRACE", s); s = cgiOptionalString("JKSQL_PROF"); if (s != NULL) envUpdate("JKSQL_PROF", s); } struct cgiVar *cgiVarList() /* return the list of cgiVar's */ { initCgiInput(); return inputList; } static char *findVarData(char *varName) /* Get the string associated with varName from the query string. */ { struct cgiVar *var; initCgiInput(); if ((var = hashFindVal(inputHash, varName)) == NULL) return NULL; return var->val; } void cgiBadVar(char *varName) /* Complain about a variable that's not there. */ { if (varName == NULL) varName = ""; errAbort("Sorry, didn't find CGI input variable '%s'", varName); } static char *mustFindVarData(char *varName) /* Find variable and associated data or die trying. */ { char *res = findVarData(varName); if (res == NULL) cgiBadVar(varName); return res; } char *javaScriptLiteralEncode(char *inString) /* Use backslash escaping on newline * and quote chars, backslash and others. * Intended that the encoded string will be * put between quotes at a higher level and * then interpreted by Javascript. */ { char c; int outSize = 0; char *outString, *out, *in; if (inString == NULL) return(cloneString("")); /* Count up how long it will be */ in = inString; while ((c = *in++) != 0) { if (c == '\'' || c == '\"' || c == '&' || c == '\\' || c == '\n' || c == '\r' || c == '\t' || c == '\b' || c == '\f' ) outSize += 2; else outSize += 1; } outString = needMem(outSize+1); /* Encode string */ in = inString; out = outString; while ((c = *in++) != 0) { if (c == '\'' || c == '\"' || c == '&' || c == '\\' || c == '\n' || c == '\r' || c == '\t' || c == '\b' || c == '\f' ) *out++ = '\\'; *out++ = c; } *out++ = 0; return outString; } void cgiDecode(char *in, char *out, int inLength) /* Decode from cgi pluses-for-spaces format to normal. * Out will be a little shorter than in typically, and * can be the same buffer. */ { char c; int i; for (i=0; inext) { if (sameString(hel->name, varName)) { struct cgiVar *var = hel->val; string = newSlName(var->val); slAddHead(&stringList, string); } } return stringList; } int cgiInt(char *varName) /* Return int value of cgi variable. */ { char *data; char c; data = mustFindVarData(varName); data = skipLeadingSpaces(data); c = data[0]; if (!(isdigit(c) || (c == '-' && isdigit(data[1])))) errAbort("Expecting number in %s, got \"%s\"\n", varName, data); return atoi(data); } int cgiIntExp(char *varName) /* Evaluate an integer expression in varName and * return value. */ { return intExp(cgiString(varName)); } int cgiOptionalInt(char *varName, int defaultVal) /* This returns integer value of varName if it exists in cgi environment * and it's not just the empty string otherwise it returns defaultVal. */ { char *s = cgiOptionalString(varName); s = skipLeadingSpaces(s); if (isEmpty(s)) return defaultVal; return cgiInt(varName); } double cgiDouble(char *varName) /* Returns double value. */ { char *data; double x; data = mustFindVarData(varName); if (sscanf(data, "%lf", &x)<1) errAbort("Expecting real number in %s, got \"%s\"\n", varName, data); return x; } double cgiOptionalDouble(char *varName, double defaultVal) /* Returns double value. */ { if (!cgiVarExists(varName)) return defaultVal; return cgiDouble(varName); } boolean cgiVarExists(char *varName) /* Returns TRUE if the variable was passed in. */ { initCgiInput(); return hashLookup(inputHash, varName) != NULL; } boolean cgiBoolean(char *varName) { return cgiVarExists(varName); } int cgiOneChoice(char *varName, struct cgiChoice *choices, int choiceSize) /* Returns value associated with string variable in choice table. */ { char *key = cgiString(varName); int i; int val = -1; for (i=0; i"); } void cgiMakeClearButton(char *form, char *field) /* Make button to clear a text field. */ { char javascript[1024]; safef(javascript, sizeof(javascript), "document.%s.%s.value = ''; document.%s.submit();", form, field, form); cgiMakeOnClickButton(javascript, " Clear "); } void cgiMakeButtonWithMsg(char *name, char *value, char *msg) /* Make 'submit' type button. Display msg on mouseover, if present*/ { printf("", name, value, (msg ? " TITLE=\"" : ""), (msg ? msg : ""), (msg ? "\"" : "" )); } void cgiMakeButtonWithOnClick(char *name, char *value, char *msg, char *onClick) /* Make 'submit' type button, with onclick javascript */ { printf("", name, value, onClick, (msg ? " TITLE=\"" : ""), (msg ? msg : ""), (msg ? "\"" : "" )); } void cgiMakeButton(char *name, char *value) /* Make 'submit' type button */ { cgiMakeButtonWithMsg(name, value, NULL); } void cgiMakeOnClickButton(char *command, char *value) /* Make 'push' type button with client side onClick (java)script. */ { printf("", value, command); } void cgiMakeOnClickSubmitButton(char *command, char *name, char *value) /* Make submit button with both variable name and value with client side * onClick (java)script. */ { printf("", name, value, command); } void cgiMakeOptionalButton(char *name, char *value, boolean disabled) /* Make 'submit' type button that can be disabled. */ { printf(""); } void cgiMakeFileEntry(char *name) /* Make file entry box/browser */ { printf("", name); } void cgiSimpleTableStart() /* start HTML table -- no customization. Leaves room * for a fancier implementation */ { printf("\n"); } void cgiTableEnd() /* end HTML table */ { printf("
\n"); } void cgiSimpleTableRowStart() /* Start table row */ { printf("\n"); } void cgiTableRowEnd() /* End table row */ { printf("\n"); } void cgiSimpleTableFieldStart() /* Start table field */ { printf(""); } void cgiTableFieldStartAlignRight() /* Start table field and align right*/ { printf(""); } void cgiTableFieldEnd() /* End table field */ { printf("\n"); } void cgiTableField(char *text) /* Make table field entry */ { printf(" %s \n", text); } void cgiTableFieldWithMsg(char *text, char *msg) /* Make table field entry with mouseover */ { printf(" %s \n", (msg ? " TITLE=\"" : ""), (msg ? msg : ""), (msg ? "\"" : "" ), text); } void cgiParagraph(char *text) /* Make text paragraph */ { printf("

%s\n", text); } void cgiMakeRadioButton(char *name, char *value, boolean checked) /* Make radio type button. A group of radio buttons should have the * same name but different values. The default selection should be * sent with checked on. */ { printf("", name, value, (checked ? "CHECKED" : "")); } void cgiMakeOnClickRadioButton(char *name, char *value, boolean checked, char *command) /* Make radio type button with onClick command. * A group of radio buttons should have the * same name but different values. The default selection should be * sent with checked on. */ { printf("", name, value, command, (checked ? "CHECKED" : "")); } char *cgiBooleanShadowPrefix() /* Prefix for shadow variable set with boolean variables. */ { return "boolshad."; } #define BOOLSHAD_EXTRA "class='cbShadow'" boolean cgiBooleanDefined(char *name) /* Return TRUE if boolean variable is defined (by * checking for shadow. */ { char buf[256]; safef(buf, sizeof(buf), "%s%s", cgiBooleanShadowPrefix(), name); return cgiVarExists(buf); } static void cgiMakeCheckBox2Bool(char *name, boolean checked, boolean enabled, char *id, char *moreHtml) /* Make check box - designed to be called by the variously overloaded * cgiMakeCheckBox functions, and should NOT be called directly. * moreHtml: optional additional html like javascript call or mouseover msg (may be NULL) * id: button id (may be NULL) * Also make a shadow hidden variable and support 2 boolean states: * checked/unchecked and enabled/disabled. */ { char buf[256], idBuf[256]; if(id) safef(idBuf, sizeof(idBuf), " id=\"%s\"", id); else idBuf[0] = 0; printf("", name, idBuf, (moreHtml ? moreHtml : ""), (checked ? " CHECKED" : ""), (enabled ? "" : " DISABLED")); safef(buf, sizeof(buf), "%s%s", cgiBooleanShadowPrefix(), name); cgiMakeHiddenVarWithExtra(buf, ( enabled ? "0" : (checked ? "-1" : "-2")),BOOLSHAD_EXTRA); } void cgiMakeCheckBoxUtil(char *name, boolean checked, char *msg, char *id) /* Make check box - can be called directly, though it was originally meant * as the common code for all lower level checkbox routines. * However, it's util functionality has been taken over by * cgiMakeCheckBoxWithIdAndOptionalHtml() */ { char buf[256]; if (msg) safef(buf, sizeof(buf), "TITLE=\"%s\"", msg); else buf[0] = 0; cgiMakeCheckBox2Bool(name, checked, TRUE, id, buf); } void cgiMakeCheckBoxWithMsg(char *name, boolean checked, char *msg) { char buf[512]; safef(buf, sizeof(buf), "title='%s'", msg); cgiMakeCheckBox2Bool(name, checked, TRUE, NULL, buf); } void cgiMakeCheckBoxWithId(char *name, boolean checked, char *id) /* Make check box, which includes an ID. */ { cgiMakeCheckBox2Bool(name, checked, TRUE, id, NULL); } void cgiMakeCheckBox(char *name, boolean checked) /* Make check box. */ { cgiMakeCheckBox2Bool(name, checked, TRUE, NULL, NULL); } void cgiMakeCheckBoxJS(char *name, boolean checked, char *javascript) /* Make check box with javascript. */ { cgiMakeCheckBox2Bool(name,checked,TRUE,NULL,javascript); } void cgiMakeCheckBoxIdAndJS(char *name, boolean checked, char *id, char *javascript) /* Make check box with ID and javascript. */ { cgiMakeCheckBox2Bool(name,checked,TRUE,id,javascript); } void cgiMakeCheckBoxFourWay(char *name, boolean checked, boolean enabled, char *id, char *classes, char *moreHtml) /* Make check box - with fourWay functionality (checked/unchecked by enabled/disabled) * Also makes a shadow hidden variable that supports the 2 boolean states. */ { char shadName[256]; printf(""); // The hidden var needs to hold the 4way state safef(shadName, sizeof(shadName), "%s%s", cgiBooleanShadowPrefix(), name); cgiMakeHiddenVarWithExtra(shadName, ( enabled ? "0" : (checked ? "-1" : "-2")),BOOLSHAD_EXTRA); } void cgiMakeHiddenBoolean(char *name, boolean on) /* Make hidden boolean variable. Also make a shadow hidden variable so we * can distinguish between variable not present and * variable set to false. */ { char buf[256]; cgiMakeHiddenVar(name, on ? "on" : "off"); safef(buf, sizeof(buf), "%s%s", cgiBooleanShadowPrefix(), name); cgiMakeHiddenVarWithExtra(buf, "1",BOOLSHAD_EXTRA); } void cgiMakeTextArea(char *varName, char *initialVal, int rowCount, int columnCount) /* Make a text area with area rowCount X columnCount and with text: intialVal */ { cgiMakeTextAreaDisableable(varName, initialVal, rowCount, columnCount, FALSE); } void cgiMakeTextAreaDisableable(char *varName, char *initialVal, int rowCount, int columnCount, boolean disabled) /* Make a text area that can be disabled. The rea has rowCount X * columnCount and with text: intialVal */ { printf("", varName, rowCount, columnCount, disabled ? "DISABLED" : "", (initialVal != NULL ? initialVal : "")); } void cgiMakeOnKeypressTextVar(char *varName, char *initialVal, int charSize, char *script) /* Make a text control filled with initial value, with a (java)script * to execute every time a key is pressed. If charSize is zero it's * calculated from initialVal size. */ { if (initialVal == NULL) initialVal = ""; if (charSize == 0) charSize = strlen(initialVal); if (charSize == 0) charSize = 8; printf("\n"); } void cgiMakeTextVar(char *varName, char *initialVal, int charSize) /* Make a text control filled with initial value. If charSize * is zero it's calculated from initialVal size. */ { cgiMakeOnKeypressTextVar(varName, initialVal, charSize, NULL); } void cgiMakeTextVarWithExtraHtml(char *varName, char *initialVal, int width, char *extra) /* Make a text control filled with initial value. */ { if (initialVal == NULL) initialVal = ""; if (width==0) width=strlen(initialVal)*10; if (width==0) width = 100; printf("\n"); } void cgiMakeIntVar(char *varName, int initialVal, int maxDigits) /* Make a text control filled with initial value. */ { if (maxDigits == 0) maxDigits = 4; printf("", varName, maxDigits, initialVal); } void cgiMakeIntVarInRange(char *varName, int initialVal, char *title, int width, char *min, char *max) /* Make a integer control filled with initial value. If min and/or max are non-NULL will enforce range Requires utils.js jQuery.js and inputBox class */ { if (width==0) { if (max) width=strlen(max)*10; else { int sz=initialVal+1000; if (min) sz=atoi(min) + 1000; width = 10; while (sz/=10) width+=10; } } if (width < 65) width = 65; printf("\n"); } void cgiMakeIntVarWithLimits(char *varName, int initialVal, char *title, int width, int min, int max) { char minLimit[20]; char maxLimit[20]; char *minStr=NULL; char *maxStr=NULL; if (min != NO_VALUE) { safef(minLimit,sizeof(minLimit),"%d",min); minStr = minLimit; } if (max != NO_VALUE) { safef(maxLimit,sizeof(maxLimit),"%d",max); maxStr = maxLimit; } cgiMakeIntVarInRange(varName,initialVal,title,width,minStr,maxStr); } void cgiMakeIntVarWithMin(char *varName, int initialVal, char *title, int width, int min) { char minLimit[20]; char *minStr=NULL; if (min != NO_VALUE) { safef(minLimit,sizeof(minLimit),"%d",min); minStr = minLimit; } cgiMakeIntVarInRange(varName,initialVal,title,width,minStr,NULL); } void cgiMakeIntVarWithMax(char *varName, int initialVal, char *title, int width, int max) { char maxLimit[20]; char *maxStr=NULL; if (max != NO_VALUE) { safef(maxLimit,sizeof(maxLimit),"%d",max); maxStr = maxLimit; } cgiMakeIntVarInRange(varName,initialVal,title,width,NULL,maxStr); } void cgiMakeDoubleVar(char *varName, double initialVal, int maxDigits) /* Make a text control filled with initial floating-point value. */ { if (maxDigits == 0) maxDigits = 4; printf("", varName, maxDigits, initialVal); } void cgiMakeDoubleVarInRange(char *varName, double initialVal, char *title, int width, char *min, char *max) /* Make a floating point control filled with initial value. If min and/or max are non-NULL will enforce range Requires utils.js jQuery.js and inputBox class */ { if (width==0) { if (max) width=strlen(max)*10; } if (width < 65) width = 65; printf("\n"); } void cgiMakeDoubleVarWithLimits(char *varName, double initialVal, char *title, int width, double min, double max) { char minLimit[20]; char maxLimit[20]; char *minStr=NULL; char *maxStr=NULL; if ((int)min != NO_VALUE) { safef(minLimit,sizeof(minLimit),"%g",min); minStr = minLimit; } if ((int)max != NO_VALUE) { safef(maxLimit,sizeof(maxLimit),"%g",max); maxStr = maxLimit; } cgiMakeDoubleVarInRange(varName,initialVal,title,width,minStr,maxStr); } void cgiMakeDoubleVarWithMin(char *varName, double initialVal, char *title, int width, double min) { char minLimit[20]; char *minStr=NULL; if ((int)min != NO_VALUE) { safef(minLimit,sizeof(minLimit),"%g",min); minStr = minLimit; } cgiMakeDoubleVarInRange(varName,initialVal,title,width,minStr,NULL); } void cgiMakeDoubleVarWithMax(char *varName, double initialVal, char *title, int width, double max) { char maxLimit[20]; char *maxStr=NULL; if ((int)max != NO_VALUE) { safef(maxLimit,sizeof(maxLimit),"%g",max); maxStr = maxLimit; } cgiMakeDoubleVarInRange(varName,initialVal,title,width,NULL,maxStr); } void cgiMakeDropListClassWithStyleAndJavascript(char *name, char *menu[], int menuSize, char *checked, char *class, char *style,char *javascript) /* Make a drop-down list with names, text class, style and javascript. */ { int i; char *selString; if (checked == NULL) checked = menu[0]; printf("\n"); for (i=0; i%s\n", selString, menu[i]); } printf("\n"); } void cgiMakeDropListClassWithStyle(char *name, char *menu[], int menuSize, char *checked, char *class, char *style) /* Make a drop-down list with names, text class and style. */ { cgiMakeDropListClassWithStyleAndJavascript(name,menu,menuSize,checked,class,style,""); } void cgiMakeDropListClass(char *name, char *menu[], int menuSize, char *checked, char *class) /* Make a drop-down list with names. */ { cgiMakeDropListClassWithStyle(name, menu, menuSize, checked, class, NULL); } void cgiMakeDropList(char *name, char *menu[], int menuSize, char *checked) /* Make a drop-down list with names. * uses style "normalText" */ { cgiMakeDropListClass(name, menu, menuSize, checked, "normalText"); } char *cgiMultListShadowPrefix() /* Prefix for shadow variable set with multi-select inputs. */ { return "multishad."; } void cgiMakeMultList(char *name, char *menu[], int menuSize, struct slName *checked, int length) /* Make a list of names with window height equalt to length, * which can have multiple selections. Same as drop-down list * except "multiple" is added to select tag. */ { int i; char *selString; if (checked == NULL) checked = slNameNew(menu[0]); printf("\n"); char buf[512]; safef(buf, sizeof(buf), "%s%s", cgiMultListShadowPrefix(), name); cgiMakeHiddenVar(buf, "1"); } void cgiMakeCheckboxGroupWithVals(char *name, char *menu[], char *values[], int menuSize, struct slName *checked, int tableColumns) /* Make a table of checkboxes that have the same variable name but different * values (same behavior as a multi-select input), with nice labels in menu[]. */ { int i; if (values == NULL) values = menu; if (menu == NULL) menu = values; puts(""); for (i = 0; i < menuSize; i++) { if (i > 0 && (i % tableColumns) == 0) printf(""); printf("" "\n", name, values[i], (slNameInList(checked, values[i]) ? "CHECKED" : ""), menu[i]); } if ((i % tableColumns) != 0) while ((i++ % tableColumns) != 0) printf(""); puts("
%s
"); char buf[512]; safef(buf, sizeof(buf), "%s%s", cgiMultListShadowPrefix(), name); cgiMakeHiddenVar(buf, "0"); } void cgiMakeCheckboxGroup(char *name, char *menu[], int menuSize, struct slName *checked, int tableColumns) /* Make a table of checkboxes that have the same variable name but different * values (same behavior as a multi-select input). */ { cgiMakeCheckboxGroupWithVals(name, menu, NULL, menuSize, checked, tableColumns); } void cgiMakeDropListFull(char *name, char *menu[], char *values[], int menuSize, char *checked, char *extraAttribs) /* Make a drop-down list with names and values. */ { int i; char *selString; if (checked == NULL) checked = menu[0]; if (NULL != extraAttribs) { printf("\n", name); } for (i=0; i%s\n", selString, values[i], menu[i]); } printf("\n"); } char *cgiMakeSelectDropList(boolean multiple, char *name, struct slPair *valsAndLabels, char *selected, char *anyAll,char *extraClasses, char *extraHtml) // Returns allocated string of HTML defining a drop-down select // (if multiple, REQUIRES ui-dropdownchecklist.js) // valsAndLabels: val (pair->name) must be filled in but label (pair->val) may be NULL. // selected: if not NULL is a val found in the valsAndLabels (multiple then comma delimited list). // If null and anyAll not NULL, that will be selected // anyAll: if not NULL is the string for an initial option. It can contain val and label, // delimited by a comma // extraHtml: if not NULL contains id, javascript calls and style. // It does NOT contain class definitions { struct dyString *output = dyStringNew(1024); boolean checked = FALSE; dyStringPrintf(output,"\n"); return dyStringCannibalize(&output); } void cgiMakeDropListWithVals(char *name, char *menu[], char *values[], int menuSize, char *checked) /* Make a drop-down list with names and values. In this case checked * corresponds to a value, not a menu. */ { int i; char *selString; if (checked == NULL) checked = values[0]; printf("\n"); } void cgiDropDownWithTextValsAndExtra(char *name, char *text[], char *values[], int count, char *selected, char *extra) /* Make a drop-down list with both text and values. */ { int i; char *selString; assert(values != NULL && text != NULL); if (selected == NULL) selected = values[0]; printf("\n"); for (i=0; i%s\n", selString, values[i], text[i]); } printf("\n"); } void cgiMakeHiddenVarWithExtra(char *varName, char *string,char *extra) /* Store string in hidden input for next time around. */ { printf("\n",extra); else puts(">"); } void cgiContinueHiddenVar(char *varName) /* Write CGI var back to hidden input for next time around. */ { if (cgiVarExists(varName)) cgiMakeHiddenVar(varName, cgiString(varName)); } void cgiVarExclude(char *varName) /* If varName exists, remove it. */ { if (cgiVarExists(varName)) { struct cgiVar *cv = hashRemove(inputHash, varName); slRemoveEl(&inputList, cv); } } void cgiVarExcludeExcept(char **varNames) /* Exclude all variables except for those in NULL * terminated array varNames. varNames may be NULL * in which case nothing is excluded. */ { struct hashEl *list, *el; struct hash *exclude = newHash(8); char *s; /* Build up hash of things to exclude */ if (varNames != NULL) { while ((s = *varNames++) != NULL) hashAdd(exclude, s, NULL); } /* Step through variable list and remove them if not * excluded. */ initCgiInput(); list = hashElListHash(inputHash); for (el = list; el != NULL; el = el->next) { if (!hashLookup(exclude, el->name)) cgiVarExclude(el->name); } hashElFreeList(&list); freeHash(&exclude); } void cgiVarSet(char *varName, char *val) /* Set a cgi variable to a particular value. */ { struct cgiVar *var; initCgiInput(); AllocVar(var); var->val = cloneString(val); hashAddSaveName(inputHash, varName, var, &var->name); } struct dyString *cgiUrlString() /* Get URL-formatted that expresses current CGI variable state. */ { struct dyString *dy = newDyString(0); struct cgiVar *cv; char *e; for (cv = inputList; cv != NULL; cv = cv->next) { if (cv != inputList) dyStringAppend(dy, "&"); e = cgiEncode(cv->val); dyStringPrintf(dy, "%s=", cv->name); dyStringAppend(dy, e); freez(&e); } return dy; } void cgiContinueAllVars() /* Write back all CGI vars as hidden input for next time around. */ { struct cgiVar *cv; for (cv = inputList; cv != NULL; cv = cv->next) cgiMakeHiddenVar(cv->name, cv->val); } boolean cgiFromCommandLine(int *pArgc, char *argv[], boolean preferWeb) /* Use the command line to set up things as if we were a CGI program. * User types in command line (assuming your program called cgiScript) * like: * cgiScript nonCgiArg1 var1=value1 var2=value2 var3=value3 nonCgiArg2 * or like * cgiScript nonCgiArg1 var1=value1&var2=value2&var3=value3 nonCgiArg2 * or even like * cgiScript nonCgiArg1 -x -y=bogus z=really * (The non-cgi arguments can occur anywhere. The cgi arguments (all containing * the character '=' or starting with '-') are erased from argc/argv. Normally * you call this cgiSpoof(&argc, argv); */ { int argc = *pArgc; int i; int argcLeft = argc; char *name; static char queryString[65536]; char *q = queryString; boolean needAnd = TRUE; boolean gotAny = FALSE; boolean startDash; boolean gotEq; static char hostLine[512]; if (preferWeb && cgiIsOnWeb()) return TRUE; /* No spoofing required! */ q += safef(q, queryString + sizeof(queryString) - q, "%s", "QUERY_STRING=cgiSpoof=on"); for (i=0; i= maxArgc) { ExpandArray(argv, maxArgc, 2*maxArgc); maxArgc *= 2; } /* Fill in another argument to our psuedo arguments. */ argv[argc++] = cloneString(line); } spoof = cgiSpoof(&argc, argv); /* Cleanup. */ lineFileClose(&lf); for(i=0; i 3) ascTime[len-2] = '\0'; if (!ip) ip = "unknown"; if (!hgsid) hgsid = "unknown"; if (!requestUri) requestUri = "unknown"; fprintf(stderr, "[%s] [%s] [client %s] [hgsid=%.24s] [%.1024s] ", ascTime, cgiFileName, ip, hgsid, requestUri); } void cgiResetState() /* This is for reloading CGI settings multiple times in the same program * execution. No effect if state has not yet been initialized. */ { freez(&inputString); inputString = NULL; if (inputHash != NULL) hashFree(&inputHash); inputHash = NULL; inputList = NULL; haveCookiesHash = FALSE; if (cookieHash != NULL) hashFree(&cookieHash); cookieHash = NULL; cookieList = NULL; } void cgiDown(float lines) // Drop down a certain number of lines (may be fractional) { printf("

\n",lines); } char *commonCssStyles() /* Returns a string of common CSS styles */ { // Contents currently is OBSOLETE as these have been moved to HGStyle.css // TODO: remove all traces (from web.c, hgTracks, hgTables) as this funtion does nothing. return ""; }