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))
{