4a030195664cbdd1d3c1a4b81b1ed41dbf6d9bc5
angie
Fri Feb 15 14:02:58 2019 -0800
Prevent hgFind lib (specifically genomePos -> hgPositionsHtml) from opening a web page; instead, pass up results and warning messages to the calling CGI so it can open the page its own way. refs #22945
hgTracks, hgTables and hgVai used to call findGenomePos{,Web} to resolve positions; hgTables and hgVai had to detect after the fact whether an HTML page had been started (with warnings and/or multiple results).
In fact, hgPositionsHtml called webEnd which could cause conflicts with what the CGI was doing afterwards.
Now, instead of findGenomePos{,Web} there is hgFindSearch which returns hgp and also warning messages, if any, via a dyString parameter.
The calling CGI decides how to open the page if necessary (for hgTracks, it's already open) and displays warnings/multiple results -- or just proceeds as usual with the single position result.
diff --git src/hg/hgTracks/extTools.c src/hg/hgTracks/extTools.c
index 99cb7c5..f28eb44 100644
--- src/hg/hgTracks/extTools.c
+++ src/hg/hgTracks/extTools.c
@@ -1,334 +1,335 @@
// Code to parse list of external tools and redirect to it via a hidden form */
//
// The link from hgTracks has the form ?hgt.redirectTool=<name>
// Note that you can add the parameter &debug=1 to the URL to see and modify the hidden form
// cgi-bin/extTools.ra defines the parameters and labels of the tools
/* Copyright (C) 2014 The Regents of the University of California
* See README in this or parent directory for licensing information. */
#include "common.h"
#include "dystring.h"
#include "hCommon.h"
#include "htmshell.h"
#include "hash.h"
#include "web.h"
#include "ra.h"
#include "hgTracks.h"
#include "extTools.h"
#include "hgFind.h"
#include "obscure.h"
#include "net.h"
boolean extToolsEnabled()
/* Return TRUE if we can display the external tools menu. */
{
return fileExists("extTools.ra");
}
void printExtMenuData(char *chromName)
/* print the external tools aka "send to" menu entries as a javascript list to stdout */
{
if (!extToolsEnabled())
return;
struct extTool *extTools = NULL;
// we allow an alternative location in /gbdb/hgFixed
// This means that we do not have to do a build patch when
// we have to change this .ra file and can do it with a simple
// file push to the RR
if (fileExists("/gbdb/hgFixed/extTools.ra"))
extTools = readExtToolRa("/gbdb/hgFixed/extTools.ra");
else
extTools = readExtToolRa("extTools.ra");
struct extTool *et;
struct dyString *dy = dyStringNew(1024);
dyStringAppend(dy, "extTools = [\n");
for(et = extTools; et != NULL; et = et->next)
{
// special case for alternate chroms on hg38, skip the Ensembl links
if (chromName!=NULL && startsWith("ensembl", et->tool) && endsWith(chromName, "_alt"))
continue;
if (et->dbs!=NULL)
{
if (!slNameInList(et->dbs, database))
continue;
}
if (et->notDbs!=NULL)
{
if (slNameInList(et->notDbs, database))
continue;
}
char* tool = jsonStringEscape(et->tool);
char* shortLabel = jsonStringEscape(et->shortLabel);
char* longLabel = jsonStringEscape(et->longLabel);
dyStringPrintf(dy, " ['%s', '%s', '%s', %d]", tool, shortLabel, longLabel, et->maxSize);
if (et->next)
dyStringAppend(dy, ",");
dyStringAppend(dy, "\n");
}
dyStringAppend(dy, "];\n");
jsInline(dy->string);
dyStringFree(&dy);
}
static char *cloneRaSetting(struct hash *hash, char *name, struct lineFile *lf, bool mustExist)
/* clone a setting out of the ra hash. errAbort if mustExit and not found, otherwise NULL. */
{
char *str;
if ((str = hashFindVal(hash, name)) == NULL)
{
if (mustExist)
errAbort("missing required setting '%s' on line %d in file %s\n",
name, lf->lineIx, lf->fileName);
else
return NULL;
}
return cloneString(str);
}
struct extTool *readExtToolRa(char *raFileName)
/* parse the extTools.ra file. Inspired by trackHub.c:readGroupRa() */
{
struct lineFile *lf = udcWrapShortLineFile(raFileName, NULL, 1*1024*1024);
struct extTool *list = NULL;
//struct hash *ra;
struct slPair *raList;
while ((raList = raNextStanzAsPairs(lf)) != NULL)
{
struct extTool *et;
AllocVar(et);
struct slPair *paramList = NULL;
struct hash *raHash;
raHash = hashNew(0);
// the "param" field is there more than once, so cannot use a hash for it
struct slPair *raPair;
for (raPair = raList; raPair != NULL; raPair = raPair->next)
{
if (sameWord(raPair->name, "param"))
{
// split the val into parameter name and value
char *paramArr[2];
int fCount = chopByWhite(raPair->val, paramArr, 2);
// hacky way to allow spaces in parameter names and values
char *name = replaceChars(paramArr[0], "%20", " ");
char *val;
if (fCount==2)
val = cloneString(paramArr[1]);
else
val = cloneString("");
val = replaceChars(val, "%20", " ");
slPairAdd(¶mList, name, val);
}
else
// add all other name/val pairs to the hash
hashAdd(raHash, raPair->name, cloneString(raPair->val));
}
// pull out the required fields from the hash
et->tool = cloneRaSetting(raHash, "tool", lf, TRUE);
et->shortLabel = cloneRaSetting(raHash, "shortLabel", lf, TRUE);
et->longLabel = cloneRaSetting(raHash, "longLabel", lf, TRUE);
et->url = cloneRaSetting(raHash, "url", lf, TRUE);
et->dbs = NULL;
// optional fields
et->email = cloneRaSetting(raHash, "email", lf, FALSE);
if (hashFindVal(raHash, "onlyDbs"))
et->dbs = commaSepToSlNames(hashFindVal(raHash, "onlyDbs"));
if (hashFindVal(raHash, "notDbs"))
et->notDbs = commaSepToSlNames(hashFindVal(raHash, "notDbs"));
char *isHttpGet = hashFindVal(raHash, "isHttpGet");
if (isHttpGet && \
(sameString(isHttpGet, "yes") || sameString(isHttpGet, "on") || sameString(isHttpGet, "true")))
et->isHttpGet = TRUE;
char *maxSize = hashFindVal(raHash, "maxSize");
if (maxSize!=NULL)
et->maxSize = sqlUnsignedOrError(maxSize, "Error: maxSize %s for tool %s is not a number", \
maxSize, et->tool);
slReverse(¶mList);
et->params = paramList;
slAddHead(&list, et);
freeHashAndVals(&raHash);
slPairFree(&raList);
}
if (list)
slReverse(&list);
lineFileClose(&lf);
return list;
}
void extToolRedirect(struct cart *cart, char *tool)
/* redirect to an external tool, sending the data specified in the ext tools config file */
{
bool debug = cgiVarExists("debug");
struct extTool *extTools = readExtToolRa("extTools.ra");
struct extTool *et;
for (et = extTools; et != NULL; et = et->next)
if (sameWord(et->tool, tool))
break;
if (et==NULL)
errAbort("No configuration found for tool %s", tool);
// construct an invisible CGI form with the given parameters
printf("<html>\n<head>\n");
generateCspMetaHeader(stdout);
printf("</head>\n<body>\n");
if (debug)
printf("Target URL: %s<p>", et->url);
char *chromName;
int winStart, winEnd;
char *db = cartString(cart, "db");
char *pos = cartString(cart, "position");
// Try to deal with virt chrom position used by hgTracks.
if (startsWith("virt:", cartUsualString(cart, "position", "")))
pos = cartString(cart, "nonVirtPosition");
-findGenomePos(db, pos, &chromName, &winStart, &winEnd, cart);
+if (!parsePosition(pos, &chromName, (uint *)&winStart, (uint *)&winEnd))
+ errAbort("Can't parse position '%s'", pos);
int len = winEnd-winStart;
char start1[255];
safef(start1, sizeof(start1), "%d", winStart+1);
char *url = replaceInUrl(et->url, "", cart, db, chromName, winStart, winEnd, NULL, TRUE, NULL);
char *method = "POST";
if (et->isHttpGet)
method = "GET";
printf("<form id=\"redirForm\" method=\"%s\" action=\"%s\">\n", method, url);
struct slPair *slp;
if (et->maxSize!=0 && len > et->maxSize)
{
printf("Sorry, this tool accepts only a sequence with less than %d base pairs<p>\n"
"Please zoom in some more.<p>\n", et->maxSize);
return;
}
printf("You're being redirected from the UCSC Genome Browser to the site %s<br>\n", url);
if (et->email)
printf("Please contact %s for questions on this tool.<br>\n", et->email);
boolean submitDone = FALSE;
for (slp=et->params; slp!=NULL; slp=slp->next)
{
char* val = slp->val;
if (sameWord(val, "$db"))
val = db;
else if (sameWord(val, "$position"))
val = pos;
else if (sameWord(val, "$start1"))
val = start1;
else if (sameWord(val, "$returnUrl"))
{
// get the full URL of this hgTracks page, so external page can construct a custom track
// and link back to us
char* host = getenv("HTTP_HOST");
char* reqUrl = getenv("REQUEST_URI");
// remove everything after ? in URL
char *e = strchr(reqUrl, '?');
if (e) *e = 0;
char url[4000];
// cannot find a way to find out if the request came in via http or https
safef(url, sizeof(url), "http://%s%s", host, reqUrl);
val = url;
}
// half the current window size
else if (stringIn("$halfLen", val))
{
char buf[64];
safef(buf, sizeof(buf), "%d", len/2);
val = replaceChars(val, "$halfLen", buf);
}
else if (sameWord(val, "$seq") || sameWord(val, "$faSeq"))
{
static struct dnaSeq *seq = NULL;
seq = hDnaFromSeq(db, chromName, winStart, winEnd, dnaLower);
if (sameWord(val, "$seq"))
val = seq->dna;
else
{
val = catTwoStrings(">sequence\n",seq->dna);
freez(&seq);
}
}
else if (sameWord(val, "$ncbiGca"))
{
char *gca = hNcbiGcaId(db);
if (gca)
val = gca;
else
// Really we shouldn't be making this entire form... pass db as hail-mary
val = db;
}
else if (sameWord(val, "$ncbiGcf"))
{
char *gcf = hNcbiGcfId(db);
if (gcf)
val = gcf;
else
// Really we shouldn't be making this entire form... pass db as hail-mary
val = db;
}
// any remaining $-expression might be one of the general ones
else if (stringIn("$", val))
{
val = replaceInUrl(val, "", cart, db, chromName, winStart, winEnd, NULL, TRUE, NULL);
}
// output
if (debug)
{
printf("%s: ", slp->name);
printf("<input name=\"%s\" value=\"%s\"><p>\n", slp->name, val);
}
else
{
// parameter named submit is a special case
if (sameWord(slp->name, "submit"))
{
submitDone = TRUE;
printf("<input type=\"submit\" value=\"%s\" style=\"position: absolute; left: -9999px; "
"width: 1px; height: 1px;\">\n", val);
}
else
printf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n", slp->name, val);
}
}
// a hidden submit button, see
// http://stackoverflow.com/questions/477691/submitting-a-form-by-pressing-enter-without-a-submit-button
if (debug)
printf("<input type=\"submit\">\n");
else if (!submitDone)
printf("<input type=\"submit\" style=\"position: absolute; left: -9999px; width: 1px; height: 1px;\">\n");
printf("</form>\n");
// a little javascript that clicks the submit button
if (!debug)
{
jsInline("document.getElementById(\"redirForm\").submit();\n");
}
jsInlineFinish();
printf("</body></html>\n");
}