7181c0af889a0eee451b91ff0b23d67f35e0e772 braney Tue Aug 18 08:39:08 2026 -0700 cheapcgi, customTrack, hgSession, hgPhyloPlace: track in-memory uploads in a registry Uploaded file contents are handed to the reading code as a text address and size. Collect that bookkeeping in cheapcgi, which now records each block it makes and hands back a name for it, and have the custom track, session and phyloPlace upload paths look the block up by that name. Also removes the duplicated address/size parsing those callers each had, and makes lineFileDecompressMem ignore a too-small buffer. refs #38108 diff --git src/hg/lib/customTrack.c src/hg/lib/customTrack.c index e24a910f6cc..74df0b83f57 100644 --- src/hg/lib/customTrack.c +++ src/hg/lib/customTrack.c @@ -631,51 +631,70 @@ boolean customTrackIsCompressed(char *fileName) /* test for file suffix indicating compression */ { char *fileNameDecoded = cloneString(fileName); cgiDecode(fileName, fileNameDecoded, strlen(fileName)); boolean result = (endsWith(fileNameDecoded,".gz") || endsWith(fileNameDecoded,".Z") || endsWith(fileNameDecoded,".zip") || endsWith(fileNameDecoded,".bz2")); freeMem(fileNameDecoded); return result; } +#define CT_UPLOAD_GONE_MSG "The contents of the uploaded file are no longer available. " \ + "Please choose the file again." + +static char *memSpecFromCart(struct cart *cart, char *binVar, char *fileVar) +/* Return the "
" text naming the uploaded contents of a file + * field, or NULL if there are none. The binVar cart variable holds such text + * already, but any request can set that variable, so look up the block cheapcgi + * handed out rather than trusting the text. Otherwise the contents came + * through as an ordinary string in fileVar, and get a name of their own here. */ +{ +char *cFBin = cartOptionalString(cart, binVar); +if (cFBin != NULL) + { + unsigned long size; + char *mem = cgiMemBlobFind(cFBin, &size); + if (mem == NULL) + return NULL; + return cgiMemBlobRegister(mem, size); + } +char *cF = cartOptionalString(cart, fileVar); +if (cF == NULL) + return NULL; +return cgiMemBlobRegister(cF, strlen(cF)); +} + static char *prepCompressedFile(struct cart *cart, char *fileName, char *binVar, char *fileVar) /* determine compression type and format properly for parser */ { if (!customTrackIsCompressed(fileName)) return NULL; char buf[256]; -char *cFBin = cartOptionalString(cart, binVar); -if (cFBin) - { - safef(buf,sizeof(buf),"compressed://%s %s", fileName, cFBin); /* cgi functions preserve binary data, cart vars have been * cloneString-ed which is bad for a binary stream that might * contain 0s */ - } -else - { - char *cF = cartOptionalString(cart, fileVar); - safef(buf,sizeof(buf),"compressed://%s %lu %lu", - fileName, (unsigned long) cF, (unsigned long) strlen(cF)); - } +char *spec = memSpecFromCart(cart, binVar, fileVar); +if (spec == NULL) + return NULL; +safef(buf,sizeof(buf),"compressed://%s %s", fileName, spec); +freeMem(spec); return cloneString(buf); } char* customTrackTypeFromBigFile(char *url) /* return most likely type for a big file name or NULL, * has to be freed. */ { // pull out file part from the URL, strip off the query part after "?" char fileName[2000]; safecpy(fileName, sizeof(fileName), url); chopSuffixAt(fileName, '?'); // based on udc cache dir analysis by hiram in rm #12813 if (endsWith(fileName, ".bb") || endsWith(fileName, ".bigBed") || endsWith(fileName, ".bigbed") || endsWith(fileName, "%2Ebb") || endsWith(fileName, "%2EbigBed") || endsWith(fileName, "%2Ebigbed") @@ -722,45 +741,36 @@ char *type = customTrackTypeFromBigFile(fileNameDecoded); // exclude plain VCF (as opposed to vcfTabix) from bigData treatment result = (type != NULL && differentString(type, "vcf")); freeMem(type); freeMem(fileNameDecoded); return result; } static char *prepBigData(struct cart *cart, char *fileName, char *binVar, char *fileVar) /* Pass data's memory offset and size through to customFactory */ { if (!customTrackIsBigData(fileName)) return NULL; char buf[1024]; -char *cFBin = cartOptionalString(cart, binVar); -char *cF = cartOptionalString(cart, fileVar); -if (cFBin) - { - // cFBin already contains memory offset and size (search for __binary in cheapcgi.c) - safef(buf,sizeof(buf),"memory://%s %s", fileName, cFBin); - char *split[3]; - int splitCount = chopByWhite(cloneString(cFBin), split, sizeof(split)); - if (splitCount > 2) {errAbort("hgCustom: extra garbage in %s", binVar);} - } -else - { - safef(buf, sizeof(buf),"memory://%s %lu %lu", - fileName, (unsigned long) cF, (unsigned long) strlen(cF)); - } +// the spec holds the memory offset and size (search for __binary in cheapcgi.c) +char *spec = memSpecFromCart(cart, binVar, fileVar); +if (spec == NULL) + return NULL; +safef(buf,sizeof(buf),"memory://%s %s", fileName, spec); +freeMem(spec); return cloneString(buf); } boolean ctConfigUpdate(char *ctFile) /* CT update is needed if database has been enabled since * the custom tracks in this file were created. The only way to check is * by file mod time, unless we add the enable time to * browser metadata somewhere */ { if (!ctFile || !fileExists(ctFile)) return FALSE; return cfgModTime() > fileModTime(ctFile); } struct customTrack *customTracksParseCartDetailed(char *genomeDb, struct cart *cart, @@ -799,78 +809,91 @@ fileName = cartOptionalString(cart, CT_CUSTOM_FILE_NAME_VAR); char *fileContents = cartOptionalString(cart, CT_CUSTOM_FILE_VAR); if (isNotEmpty(fileName)) { /* handle file input, optionally with compression */ if (isNotEmpty(fileContents)) customText = fileContents; else { /* file contents not available -- check for compressed */ if (customTrackIsCompressed(fileName)) { customText = prepCompressedFile(cart, fileName, CT_CUSTOM_FILE_BIN_VAR, CT_CUSTOM_FILE_VAR); + if (customText == NULL) + err = cloneString(CT_UPLOAD_GONE_MSG); } else if (customTrackIsBigData(fileName)) { // User is trying to directly upload a bigData file; pass data to // customFactory, which will alert the user that they need bigDataUrl etc. customText = prepBigData(cart, fileName, CT_CUSTOM_FILE_BIN_VAR, CT_CUSTOM_FILE_VAR); + if (customText == NULL) + err = cloneString(CT_UPLOAD_GONE_MSG); } else { /* unreadable file */ struct dyString *ds = dyStringNew(0); dyStringPrintf(ds, "Unrecognized binary data format in file %s. You can only upload text files on this page. If you have a binary file, like bigBed, bigWig, BAM, etc, copy them to a webserver and paste the URL of the file into the text box here or create a track hub for them. For more details, our documentation discusses where you can host binary files.", fileName); err = dyStringCannibalize(&ds); } } } customText = skipLeadingSpaces(customText); /* get track description from cart */ char *html = NULL; char *docFileName = cartOptionalString(cart, CT_CUSTOM_DOC_FILE_NAME_VAR); char *docFileContents = cartOptionalString(cart, CT_CUSTOM_DOC_FILE_VAR); if (isNotEmpty(docFileContents)) html = docFileContents; else if (isNotEmpty(docFileName)) { if (customTrackIsCompressed(docFileName)) + { html = prepCompressedFile(cart, docFileName, CT_CUSTOM_DOC_FILE_BIN_VAR, CT_CUSTOM_DOC_FILE_VAR); + if (html == NULL) + { + err = cloneString(CT_UPLOAD_GONE_MSG); + customText = NULL; + } + } else { /* unreadable file */ struct dyString *ds = dyStringNew(0); dyStringPrintf(ds, "Can't read doc file: %s", docFileName); err = dyStringCannibalize(&ds); customText = NULL; } } else html = cartUsualString(cart, CT_CUSTOM_DOC_TEXT_VAR, ""); html = cloneString(html); /* do not let original cart var get eaten up */ html = customDocParse(html); /* this will chew up the input string */ if(html != NULL) { char *tmp = html; html = jsStripJavascript(html); freeMem(tmp); } +else + html = cloneString(""); /* the doc file could not be read, see above */ if ((strlen(html) > 50*1024) || startsWith("track ", html) || startsWith("browser ", html)) { err = cloneString( "Optional track documentation appears to be either too large (greater than 50k) or it starts with a track or browser line. " "This is usually an indication that the data has been accidentally put into the documentation field. " "Only html documentation is intended for this field. " "Please correct and re-submit."); html = NULL; /* we do not want to save this bad value */ customText = NULL; /* trigger a return to the edit page */ } struct customTrack *newCts = NULL, *ct = NULL; if (isNotEmpty(customText)) {