44ccfacbe3a3d4b300f80d48651c77837a4b571e galt Tue Apr 26 11:12:02 2022 -0700 SQL INJECTION Prevention Version 2 - this improves our methods by making subclauses of SQL that get passed around be both easy and correct to use. The way that was achieved was by getting rid of the obscure and not well used functions sqlSafefFrag and sqlDyStringPrintfFrag and replacing them with the plain versions of those functions, since these are not needed anymore. The new version checks for NOSQLINJ in unquoted %-s which is used to include SQL clauses, and will give an error the NOSQLINJ clause is not present, and this will automatically require the correct behavior by developers. sqlDyStringPrint is a very useful function, however because it was not enforced, users could use various other dyString functions and they operated without any awareness or checking for SQL correct use. Now those dyString functions are prohibited and it will produce an error if you try to use a dyString function on a SQL string, which is simply detected by the presence of the NOSQLINJ prefix. diff --git src/hg/hgTables/bedList.c src/hg/hgTables/bedList.c index 892b560..b7ea289 100644 --- src/hg/hgTables/bedList.c +++ src/hg/hgTables/bedList.c @@ -1,842 +1,842 @@ /* bedList - get list of beds in region that pass filtering. */ /* Copyright (C) 2014 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "hash.h" #include "linefile.h" #include "localmem.h" #include "dystring.h" #include "jksql.h" #include "cheapcgi.h" #include "trackDb.h" #include "customTrack.h" #include "hdb.h" #include "hui.h" #include "hCommon.h" #include "web.h" #include "featureBits.h" #include "portable.h" #include "hgTables.h" #include "wiggle.h" #include "correlate.h" #include "bedCart.h" #include "trashDir.h" #include "wikiTrack.h" boolean htiIsPsl(struct hTableInfo *hti) /* Return TRUE if table looks to be in psl format. */ { return sameString("tStarts", hti->startsField); } void bedSqlFieldsExceptForChrom(struct hTableInfo *hti, int *retFieldCount, char **retFields) /* Given tableInfo figure out what fields are needed to * add to a database query to have information to create * a bed. The chromosome is not one of these fields - we * assume that is already known since we're processing one * chromosome at a time. Return a comma separated (no last * comma) list of fields that can be freeMem'd, and the count * of fields (this *including* the chromosome). */ { struct dyString *fields = dyStringNew(128); int fieldCount = hTableInfoBedFieldCount(hti); dyStringPrintf(fields, "%s,%s", hti->startField, hti->endField); if (fieldCount >= 4) { if (hti->nameField[0] != 0) dyStringPrintf(fields, ",%s", hti->nameField); else /* Put in . as placeholder. */ dyStringPrintf(fields, ",'.'"); } if (fieldCount >= 5) { if (hti->scoreField[0] != 0) dyStringPrintf(fields, ",%s", hti->scoreField); else dyStringPrintf(fields, ",0"); } if (fieldCount >= 6) { if (hti->strandField[0] != 0) dyStringPrintf(fields, ",%s", hti->strandField); else dyStringPrintf(fields, ",'.'"); } if (fieldCount >= 8) { if (hti->cdsStartField[0] != 0) dyStringPrintf(fields, ",%s,%s", hti->cdsStartField, hti->cdsEndField); else dyStringPrintf(fields, ",%s,%s", hti->startField, hti->endField); } if (fieldCount >= 12) { dyStringPrintf(fields, ",%s,%s,%s", hti->countField, hti->endsSizesField, hti->startsField); } if (htiIsPsl(hti)) { /* For psl format we need to know chrom size as well * to handle reverse strand case. */ dyStringPrintf(fields, ",tSize"); } *retFieldCount = fieldCount; *retFields = dyStringCannibalize(&fields); } struct bed *bedFromRow( char *chrom, /* Chromosome bed is on. */ char **row, /* Row with other data for bed. */ int fieldCount, /* Number of fields in final bed. */ boolean isPsl, /* True if in PSL format. */ boolean isGenePred, /* True if in GenePred format. */ boolean isBedWithBlocks, /* True if BED with block list. */ boolean *pslKnowIfProtein,/* Have we figured out if psl is protein? */ boolean *pslIsProtein, /* True if we know psl is protien. */ struct lm *lm) /* Local memory pool */ /* Create bed from a database row when we already understand * the format pretty well. The bed is allocated inside of * the local memory pool lm. Generally use this in conjunction * with the results of a SQL query constructed with the aid * of the bedSqlFieldsExceptForChrom function. */ { char *strand, tStrand, qStrand; struct bed *bed; int i, blockCount; lmAllocVar(lm, bed); bed->chrom = chrom; bed->chromStart = sqlUnsigned(row[0]); bed->chromEnd = sqlUnsigned(row[1]); if (fieldCount < 4) return bed; bed->name = lmCloneString(lm, row[2]); if (fieldCount < 5) return bed; bed->score = atoi(row[3]); if (fieldCount < 6) return bed; strand = row[4]; qStrand = strand[0]; tStrand = strand[1]; if (tStrand == 0) bed->strand[0] = qStrand; else { /* psl: use XOR of qStrand,tStrand if both are given. */ if (tStrand == qStrand) bed->strand[0] = '+'; else bed->strand[0] = '-'; } if (fieldCount < 8) return bed; bed->thickStart = sqlUnsigned(row[5]); bed->thickEnd = sqlUnsigned(row[6]); if (fieldCount < 12) return bed; bed->blockCount = blockCount = sqlUnsigned(row[7]); lmAllocArray(lm, bed->blockSizes, blockCount); sqlSignedArray(row[8], bed->blockSizes, blockCount); lmAllocArray(lm, bed->chromStarts, blockCount); sqlSignedArray(row[9], bed->chromStarts, blockCount); if (isGenePred) { /* Translate end coordinates to sizes. */ for (i=0; i<bed->blockCount; ++i) bed->blockSizes[i] -= bed->chromStarts[i]; } else if (isPsl) { if (!*pslKnowIfProtein) { /* Figure out if is protein using a rather elaborate but * working test I think Angie or Brian must have figured out. */ if (tStrand == '-') { int tSize = sqlUnsigned(row[10]); *pslIsProtein = (bed->chromStart == tSize - ( 3*bed->blockSizes[bed->blockCount - 1] + bed->chromStarts[bed->blockCount - 1])); } else { *pslIsProtein = (bed->chromEnd == 3*bed->blockSizes[bed->blockCount - 1] + bed->chromStarts[bed->blockCount - 1]); } *pslKnowIfProtein = TRUE; } if (*pslIsProtein) { /* if protein then blockSizes are in protein space */ for (i=0; i<blockCount; ++i) bed->blockSizes[i] *= 3; } if (tStrand == '-') { /* psl: if target strand is '-', flip the coords. * (this is the target part of pslRc from src/lib/psl.c) */ int tSize = sqlUnsigned(row[10]); for (i=0; i<blockCount; ++i) { bed->chromStarts[i] = tSize - (bed->chromStarts[i] + bed->blockSizes[i]); } reverseInts(bed->chromStarts, bed->blockCount); reverseInts(bed->blockSizes, bed->blockCount); } } if (!isBedWithBlocks) { /* non-bed: translate absolute starts to relative starts */ for (i=0; i < bed->blockCount; i++) bed->chromStarts[i] -= bed->chromStart; } return bed; } struct bed *getRegionAsBed( char *db, char *table, /* Database and table. */ struct region *region, /* Region to get data for. */ char *filter, /* Filter to add to SQL where clause if any. */ struct hash *idHash, /* Restrict to id's in this hash if non-NULL. */ struct lm *lm, /* Where to allocate memory. */ int *retFieldCount) /* Number of fields. */ /* Return a bed list of all items in the given range in table. * Cleanup result via lmCleanup(&lm) rather than bedFreeList. */ { char *fields = NULL; struct sqlResult *sr; struct hTableInfo *hti; struct bed *bedList=NULL, *bed; char **row; int fieldCount; boolean isPsl, isGenePred, isBedWithBlocks; boolean pslKnowIfProtein = FALSE, pslIsProtein = FALSE; struct sqlConnection *conn = NULL; char *dbTable = NULL; if (isCustomTrack(table)) { struct customTrack *ct = ctLookupName(table); dbTable = ct->dbTableName; conn = hAllocConn(CUSTOM_TRASH); hti = hFindTableInfo(CUSTOM_TRASH, region->chrom, dbTable); } else { dbTable = table; struct trackDb *tdb; if(sameWord(db, database)) tdb = tdbForTrack(db, table, &fullTrackList); else tdb = hTrackDbForTrack(db, table); conn = (tdb ? hAllocConnTrack(db, tdb) : hAllocConn(db)); hti = hFindTableInfo(db, region->chrom, table); } if (hti == NULL) errAbort("Could not find table info for table %s.%s", db,table); if (isWiggle(db, table)) { bedList = getWiggleAsBed(db, table, region, filter, idHash, lm, conn); fieldCount = 4; } else { bedSqlFieldsExceptForChrom(hti, &fieldCount, &fields); isPsl = htiIsPsl(hti); isGenePred = sameString("exonEnds", hti->endsSizesField); isBedWithBlocks = ( (sameString("chromStarts", hti->startsField) || sameString("blockStarts", hti->startsField)) && sameString("blockSizes", hti->endsSizesField)); /* All beds have at least chrom,start,end. We omit the chrom * from the query since we already know it. */ sr = regionQuery(conn, dbTable, fields, region, TRUE, filter); while (sr != NULL && (row = sqlNextRow(sr)) != NULL) { /* If have a name field apply hash filter. */ if (fieldCount >= 4 && idHash != NULL) if (!hashLookup(idHash, row[2])) continue; bed = bedFromRow(region->chrom, row, fieldCount, isPsl, isGenePred, isBedWithBlocks, &pslKnowIfProtein, &pslIsProtein, lm); slAddHead(&bedList, bed); } freez(&fields); sqlFreeResult(&sr); slReverse(&bedList); } hFreeConn(&conn); if (retFieldCount) *retFieldCount = fieldCount; return(bedList); } struct bed *getFilteredBeds(struct sqlConnection *conn, char *table, struct region *region, struct lm *lm, int *retFieldCount) /* Get list of beds on single region that pass filtering. */ { boolean isTabix = FALSE; /* region may be part of a list, and the routines we call work on lists of * regions. Temporarily force region->next to NULL and restore at end. */ struct region *oldNext = region->next; struct bed *bedList = NULL; region->next = NULL; if (isBigBed(database, table, curTrack, ctLookupName)) bedList = bigBedGetFilteredBedsOnRegions(conn, database, table, region, lm, retFieldCount); else if (isLongTabixTable(table)) bedList = longTabixGetFilteredBedsOnRegions(conn, database, table, region, lm, retFieldCount); else if (isBamTable(table)) bedList = bamGetFilteredBedsOnRegions(conn, database, table, region, lm, retFieldCount); else if (isVcfTable(table, &isTabix)) bedList = vcfGetFilteredBedsOnRegions(conn, database, table, region, lm, retFieldCount, isTabix); else if (isHicTable(table)) bedList = hicGetFilteredBedsOnRegions(conn, database, table, region, lm, retFieldCount); else if (isCustomTrack(table)) bedList = customTrackGetFilteredBeds(database, table, region, lm, retFieldCount); else if (sameWord(table, WIKI_TRACK_TABLE)) bedList = wikiTrackGetFilteredBeds(table, region, lm, retFieldCount); else bedList = dbGetFilteredBedsOnRegions(conn, database, database, table, table, region, lm, retFieldCount); region->next = oldNext; return bedList; } /* Droplist menu for custom track visibility: */ char *ctVisMenu[] = { "hide", "dense", "squish", "pack", "full", }; int ctVisMenuSize = 5; char *ctVisWigMenu[] = { "hide", "dense", "full", }; int ctVisWigMenuSize = 3; void doBedOrCtOptions(char *table, struct sqlConnection *conn, boolean doCt) /* Put up form to get options on BED or custom track output. */ /* (Taken from hgText.c/doBedCtOptions) */ { char *table2 = NULL; /* For now... */ struct hTableInfo *hti = getHti(database, table, conn); char buf[256]; char *setting; htmlOpen("Output %s as %s", table, (doCt ? "Custom Track" : "BED")); if (doGalaxy()) startGalaxyForm(); else if (doGreat()) { verifyGreatAssemblies(); startGreatForm(); } else hPrintf("<FORM ACTION=\"%s\" METHOD=GET>\n", getScriptName()); cartSaveSession(cart); if (!doGreat()) { hPrintf("%s\n", "<TABLE><TR><TD>"); if (doCt) { hPrintf("%s\n", "</TD><TD>" "<A HREF=\"../goldenPath/help/customTrack.html\" TARGET=_blank>" "Custom track</A> header: </B>"); } else { cgiMakeCheckBox(hgtaPrintCustomTrackHeaders, cartCgiUsualBoolean(cart, hgtaPrintCustomTrackHeaders, FALSE)); hPrintf("%s\n", "</TD><TD> <B> Include " "<A HREF=\"../goldenPath/help/customTrack.html\" TARGET=_blank>" "custom track</A> header: </B>"); } hPrintf("%s\n", "</TD></TR><TR><TD></TD><TD>name="); safef(buf, sizeof(buf), "tb_%s", hti->rootName); setting = cgiUsualString(hgtaCtName, buf); cgiMakeTextVar(hgtaCtName, setting, 16); hPrintf("%s\n", "</TD></TR><TR><TD></TD><TD>description="); safef(buf, sizeof(buf), "table browser query on %s%s%s", table, (table2 ? ", " : ""), (table2 ? table2 : "")); setting = cgiUsualString(hgtaCtDesc, buf); cgiMakeTextVar(hgtaCtDesc, setting, 50); hPrintf("%s\n", "</TD></TR><TR><TD></TD><TD>visibility="); if (isWiggle(database, table) || isBigWigTable(table)) { setting = cartCgiUsualString(cart, hgtaCtVis, ctVisWigMenu[2]); cgiMakeDropList(hgtaCtVis, ctVisWigMenu, ctVisWigMenuSize, setting); } else { setting = cartCgiUsualString(cart, hgtaCtVis, ctVisMenu[3]); cgiMakeDropList(hgtaCtVis, ctVisMenu, ctVisMenuSize, setting); } hPrintf("%s\n", "</TD></TR><TR><TD></TD><TD>url="); setting = cartCgiUsualString(cart, hgtaCtUrl, ""); cgiMakeTextVar(hgtaCtUrl, setting, 50); hPrintf("%s\n", "</TD></TR><TR><TD></TD><TD>"); hPrintf("%s\n", "</TD></TR></TABLE>"); } if (isWiggle(database, table) || isBedGraph(table) || isBigWigTable(table) ) { char *setting = NULL; hPrintf("<P> <B> Select type of data output: </B> <BR>\n"); setting = cartCgiUsualString(cart, hgtaCtWigOutType, outWigData); cgiMakeRadioButton(hgtaCtWigOutType, outWigBed, sameString(setting, outWigBed)); hPrintf("BED format (no data value information, only position)<BR>\n"); cgiMakeRadioButton(hgtaCtWigOutType, outWigData, sameString(setting, outWigData)); hPrintf("DATA VALUE format (position and real valued data)</P>\n"); } else { cgiDown(0.9); hPrintf("<B> Create one BED record per: </B>\n"); if ((anyIntersection() && intersectionIsBpWise()) || (anySubtrackMerge(database, table) && subtrackMergeIsBpWise())) { /* The original table may have blocks/CDS, described in hti, but * that info will be lost after base pair-wise operations. So make * a temporary copy of hti with its flags tweaked: */ struct hTableInfo simplifiedHti; memcpy(&simplifiedHti, hti, sizeof(simplifiedHti)); simplifiedHti.hasBlocks = FALSE; simplifiedHti.hasCDS = FALSE; fbOptionsHtiCart(&simplifiedHti, cart); } else fbOptionsHtiCart(hti, cart); } if (doCt) { if (doGalaxy()) { /* send the action parameter with the form as well */ cgiMakeHiddenVar(hgtaDoGetCustomTrackFile, "get custom track in file"); printGalaxySubmitButtons(); } else { cgiMakeButton(hgtaDoGetCustomTrackTb, "get custom track in table browser"); hPrintf(" "); cgiMakeButton(hgtaDoGetCustomTrackFile, "get custom track in file"); hPrintf("<BR>\n"); cgiMakeButton(hgtaDoGetCustomTrackGb, "get custom track in genome browser"); } } else { if (doGalaxy()) { cgiMakeHiddenVar(hgtaDoGetBed, "get BED"); printGalaxySubmitButtons(); } else if (doGreat()) { cgiMakeHiddenVar(hgtaDoGetBed, "get BED"); printGreatSubmitButtons(); } else cgiMakeButton(hgtaDoGetBed, "get BED"); } if (!doGalaxy() && !doGreat()) { hPrintf(" "); cgiMakeButton(hgtaDoMainPage, "cancel"); hPrintf("</FORM>\n"); } cgiDown(0.9); htmlClose(); } void doOutBed(char *table, struct sqlConnection *conn) /* Put up form to select BED output format. */ { doBedOrCtOptions(table, conn, FALSE); } void doOutCustomTrack(char *table, struct sqlConnection *conn) /* Put up form to select Custom Track output format. */ { doBedOrCtOptions(table, conn, TRUE); } static struct customTrack *beginCustomTrack(char *table, int fields, boolean doCt, boolean doWigHdr, boolean doDataPoints) /* If doCt, return a new custom track object from TB cart settings and params; * if !doCt, return NULL but print out a header line. */ { char *ctName = cgiUsualString(hgtaCtName, table); char *ctDesc = cgiUsualString(hgtaCtDesc, table); char *ctUrl = cgiUsualString(hgtaCtUrl, ""); char *ctVis = cgiUsualString(hgtaCtVis, "dense"); int visNum = (int) hTvFromStringNoAbort(ctVis); struct customTrack *ctNew = NULL; if (visNum < 0) visNum = 0; if (doCt) { ctNew = newCt(ctName, ctDesc, visNum, ctUrl, fields); if (doDataPoints) { - struct dyString *wigSettings = newDyString(0); + struct dyString *wigSettings = dyStringNew(0); struct tempName tn; trashDirFile(&tn, "ct", hgtaCtTempNamePrefix, ".wib"); ctNew->wibFile = cloneString(tn.forCgi); char *wiggleFile = cloneString(ctNew->wibFile); chopSuffix(wiggleFile); strcat(wiggleFile, ".wia"); ctNew->wigAscii = cloneString(wiggleFile); chopSuffix(wiggleFile); /* .wig file will be created upon encoding in customFactory */ /*strcat(wiggleFile, ".wig"); ctNew->wigFile = cloneString(wiggleFile); */ ctNew->wiggle = TRUE; dyStringPrintf(wigSettings, "type wiggle_0\nwibFile %s\n", ctNew->wibFile); ctNew->tdb->settings = dyStringCannibalize(&wigSettings); freeMem(wiggleFile); } } else { if (doWigHdr) hPrintf("track type=wiggle_0 name=\"%s\" description=\"%s\" " "visibility=%d url=%s \n", ctName, ctDesc, visNum, ctUrl); else hPrintf("track name=\"%s\" description=\"%s\" visibility=%d url=%s \n", ctName, ctDesc, visNum, ctUrl); } return ctNew; } boolean doGetBedOrCt(struct sqlConnection *conn, boolean doCt, boolean doCtFile, boolean redirectToGb) /* Actually output bed or custom track. Return TRUE unless no results. */ { char *db = cloneString(database); char *table = curTable; struct hTableInfo *hti = getHti(db, table, conn); struct featureBits *fbList = NULL, *fbPtr; struct customTrack *ctNew = NULL; boolean doCtHdr = (cartUsualBoolean(cart, hgtaPrintCustomTrackHeaders, FALSE) || doCt || doCtFile); char *ctWigOutType = cartCgiUsualString(cart, hgtaCtWigOutType, outWigData); char *fbQual = fbOptionsToQualifier(); char fbTQ[128]; int fields = hTableInfoBedFieldCount(hti); boolean gotResults = FALSE; struct region *region, *regionList = getRegions(); boolean isBedGr = isBedGraph(curTable); boolean isBgWg = isBigWigTable(curTable); boolean needSubtrackMerge = anySubtrackMerge(database, curTable); boolean doDataPoints = FALSE; boolean isWig = isWiggle(database, table); struct wigAsciiData *wigDataList = NULL; struct dataVector *dataVectorList = NULL; boolean doRgb = bedItemRgb(hTrackDbForTrack(db, curTable)); if (!cartUsualBoolean(cart, hgtaDoGreatOutput, FALSE) && !doCt) { textOpen(); } if (cartUsualBoolean(cart, hgtaDoGreatOutput, FALSE)) fputs("#", stdout); if ((isWig || isBedGr || isBgWg) && sameString(outWigData, ctWigOutType)) doDataPoints = TRUE; for (region = regionList; region != NULL; region = region->next) { struct bed *bedList = NULL, *bed; struct lm *lm = lmInit(64*1024); struct dataVector *dv = NULL; if (isWig && doDataPoints) { if (needSubtrackMerge) { dv = wiggleDataVector(curTrack, curTable, conn, region); if (dv != NULL) slAddHead(&dataVectorList, dv); } else { int count = 0; struct wigAsciiData *wigData = NULL; struct wigAsciiData *asciiData; struct wigAsciiData *next; wigData = getWiggleAsData(conn, curTable, region); for (asciiData = wigData; asciiData; asciiData = next) { next = asciiData->next; if (asciiData->count) { slAddHead(&wigDataList, asciiData); ++count; } } slReverse(&wigDataList); } } else if (isBedGr && doDataPoints) { dv = bedGraphDataVector(curTable, conn, region); if (dv != NULL) slAddHead(&dataVectorList, dv); } else if (isBgWg && doDataPoints) { dv = bigWigDataVector(curTable, conn, region); if (dv != NULL) slAddHead(&dataVectorList, dv); } else if (isWig || isBgWg) { dv = wiggleDataVector(curTrack, curTable, conn, region); bedList = dataVectorToBedList(dv); dataVectorFree(&dv); } else if (isBedGr) { bedList = getBedGraphAsBed(conn, curTable, region); } else { bedList = cookedBedList(conn, curTable, region, lm, &fields); } /* this is a one-time only initial creation of the custom track * structure to receive the results. gotResults turns it off after * the first time. */ if (doCtHdr && !gotResults && ((bedList != NULL) || (wigDataList != NULL) || (dataVectorList != NULL))) { ctNew = beginCustomTrack(table, fields, doCt, (isWig || isBedGr || isBgWg), doDataPoints); } if (doDataPoints && (wigDataList || dataVectorList)) gotResults = TRUE; else { if ((fbQual == NULL) || (fbQual[0] == 0)) { for (bed = bedList; bed != NULL; bed = bed->next) { if (bed->name != NULL) { subChar(bed->name, ' ', '_'); if (strcmp(bed->name, "") == 0) bed->name = cloneString("."); } if (doCt) { struct bed *dupe = cloneBed(bed); /* Out of local memory. */ slAddHead(&ctNew->bedList, dupe); } else { if (doRgb) bedTabOutNitemRgb(bed, fields, stdout); else bedTabOutN(bed, fields, stdout); } gotResults = TRUE; } } else { safef(fbTQ, sizeof(fbTQ), "%s:%s", hti->rootName, fbQual); fbList = fbFromBed(db, fbTQ, hti, bedList, 0, 0, FALSE, FALSE); if (fields >= 6) fields = 6; else if (fields >= 4) fields = 4; else fields = 3; if (doCt && ctNew) { ctNew->fieldCount = fields; safef(ctNew->tdb->type, strlen(ctNew->tdb->type)+1, "bed %d", fields); } for (fbPtr=fbList; fbPtr != NULL; fbPtr=fbPtr->next) { if (fbPtr->name != NULL) { char *ptr = strchr(fbPtr->name, ' '); if (ptr != NULL) *ptr = 0; } if (doCt) { struct bed *fbBed = fbToBedOne(fbPtr); slAddHead(&ctNew->bedList, fbBed ); } else { if (fields >= 6) hPrintf("%s\t%d\t%d\t%s\t%d\t%c\n", fbPtr->chrom, fbPtr->start, fbPtr->end, fbPtr->name, 0, fbPtr->strand); else if (fields >= 4) hPrintf("%s\t%d\t%d\t%s\n", fbPtr->chrom, fbPtr->start, fbPtr->end, fbPtr->name); else hPrintf("%s\t%d\t%d\n", fbPtr->chrom, fbPtr->start, fbPtr->end); } gotResults = TRUE; } featureBitsFreeList(&fbList); } } bedList = NULL; lmCleanup(&lm); } if (!gotResults) { hPrintf(NO_RESULTS); } else if (doCt) { int wigDataSize = 0; /* Load existing custom tracks and add this new one: */ struct customTrack *ctList = getCustomTracks(); removeNamedCustom(&ctList, ctNew->tdb->table); if (doDataPoints) { if (needSubtrackMerge || isBedGr || isBgWg) { slReverse(&dataVectorList); wigDataSize = dataVectorWriteWigAscii(dataVectorList, ctNew->wigAscii, 0, NULL); // TODO: see if can make prettier wig output here that // doesn't necessarily have one value per base } else { struct wiggleDataStream *wds = NULL; /* create an otherwise empty wds so we can print out the list */ wds = wiggleDataStreamNew(); wds->ascii = wigDataList; wigDataSize = wds->asciiOut(wds, db, ctNew->wigAscii, TRUE, FALSE); #if defined(DEBUG) /* dbg */ /* allow file readability for debug */ chmod(ctNew->wigAscii, 0666); #endif wiggleDataStreamFree(&wds); } } else slReverse(&ctNew->bedList); slAddHead(&ctList, ctNew); /* Save the custom tracks out to file (overwrite the old file): */ customTracksSaveCart(db, cart, ctList); /* Put up redirect-to-browser page. */ if (redirectToGb) { char browserUrl[256]; char headerText[512]; int redirDelay = 3; safef(browserUrl, sizeof(browserUrl), "%s?%s&db=%s", hgTracksName(), cartSidUrlString(cart), database); safef(headerText, sizeof(headerText), "<META HTTP-EQUIV=\"REFRESH\" CONTENT=\"%d;URL=%s\">", redirDelay, browserUrl); webStartHeader(cart, database, headerText, "Table Browser: %s %s: %s", hOrganism(database), freezeName, "get custom track"); if (doDataPoints) { hPrintf("There are %d data points in custom track. ", wigDataSize); } else { hPrintf("There are %d items in custom track. ", slCount(ctNew->bedList)); } hPrintf("You will be automatically redirected to the genome browser in\n" "%d seconds, or you can \n" "<A HREF=\"%s\">click here to continue</A>.\n", redirDelay, browserUrl); } } else if (doDataPoints) { if (needSubtrackMerge || isBedGr || isBgWg) { slReverse(&dataVectorList); dataVectorWriteWigAscii(dataVectorList, "stdout", 0, NULL); } else { /* create an otherwise empty wds so we can print out the list */ struct wiggleDataStream *wds = NULL; wds = wiggleDataStreamNew(); wds->ascii = wigDataList; wds->asciiOut(wds, db, "stdout", TRUE, FALSE); wiggleDataStreamFree(&wds); } } return gotResults; } void doGetBed(struct sqlConnection *conn) /* Get BED output (UI has already told us how). */ { doGetBedOrCt(conn, FALSE, FALSE, FALSE); } void doGetCustomTrackGb(struct sqlConnection *conn) /* Get Custom Track output (UI has already told us how). */ { doGetBedOrCt(conn, TRUE, FALSE, TRUE); } void doGetCustomTrackTb(struct sqlConnection *conn) /* Get Custom Track output (UI has already told us how). */ { boolean gotResults = doGetBedOrCt(conn, TRUE, FALSE, FALSE); if (gotResults) { flushCustomTracks(); initGroupsTracksTables(); doMainPage(conn); } } void doGetCustomTrackFile(struct sqlConnection *conn) /* Get Custom Track file output (UI has already told us how). */ { doGetBedOrCt(conn, FALSE, TRUE, FALSE); }