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/visiGene/hgVisiGene/printCaption.c src/hg/visiGene/hgVisiGene/printCaption.c index 3f9d678..27c274d 100644 --- src/hg/visiGene/hgVisiGene/printCaption.c +++ src/hg/visiGene/hgVisiGene/printCaption.c @@ -1,954 +1,954 @@ /* printCaption - query database for info and print it out * in a caption. */ /* Copyright (C) 2013 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 "dystring.h" #include "jksql.h" #include "cart.h" #include "htmshell.h" #include "hdb.h" #include "visiGene.h" #include "hgVisiGene.h" #include "captionElement.h" #include "printCaption.h" struct probeAndColor /* Just a little structure to store probe and probeColor. */ { struct probeAndColor *next; int probe; /* Probe id. */ int probeColor; /* ProbeColor id. */ }; char *getKnownGeneUrl(struct sqlConnection *conn, int geneId) /* Given gene ID, try and find known gene on browser in same * species. */ { char query[256]; char tableName[256]; int taxon; char *url = NULL; char *genomeDb = NULL; /* Figure out taxon. */ sqlSafef(query, sizeof(query), "select taxon from gene where id = %d", geneId); taxon = sqlQuickNum(conn, query); genomeDb = hDbForTaxon(taxon); if (genomeDb != NULL) { /* Make sure known genes track exists - we may need * to tweak this at some point for model organisms. */ safef(tableName, sizeof(tableName), "%s.knownToVisiGene", genomeDb); if (!sqlTableExists(conn, tableName)) genomeDb = NULL; } /* If no db for that organism revert to human. */ if (genomeDb == NULL) genomeDb = hDefaultDb(); safef(tableName, sizeof(tableName), "%s.knownToVisiGene", genomeDb); if (sqlTableExists(conn, tableName)) { struct dyString *dy = dyStringNew(0); char *knownGene = NULL; if (sqlCountColumnsInTable(conn, tableName) == 3) { sqlDyStringPrintf(dy, "select name from %s.knownToVisiGene where geneId = %d", genomeDb, geneId); } else { struct slName *imageList, *image; sqlSafef(query, sizeof(query), "select imageProbe.image from probe,imageProbe " "where probe.gene=%d and imageProbe.probe=probe.id", geneId); imageList = sqlQuickList(conn, query); if (imageList != NULL) { sqlDyStringPrintf(dy, "select name from %s.knownToVisiGene ", genomeDb); - dyStringAppend(dy, + sqlDyStringPrintf(dy, "where value in("); for (image = imageList; image != NULL; image = image->next) { sqlDyStringPrintf(dy, "'%s'", image->name); if (image->next != NULL) - dyStringAppendC(dy, ','); + sqlDyStringPrintf(dy, ","); } - dyStringAppend(dy, ")"); + sqlDyStringPrintf(dy, ")"); slFreeList(&imageList); } } if (dy->stringSize > 0) { knownGene = sqlQuickString(conn, dy->string); if (knownGene != NULL) { char temp[1024]; safef(temp, sizeof temp, "../cgi-bin/hgGene?db=%s&hgg_gene=%s&hgg_chrom=none", genomeDb, knownGene); url = cloneString(temp); } } dyStringFree(&dy); } freez(&genomeDb); return url; } static struct slName *geneProbeList(struct sqlConnection *conn, int id) /* Get list of gene names with hyperlinks to probe info page. */ { struct slName *returnList = NULL; char query[256], **row; struct sqlResult *sr; struct dyString *dy = dyStringNew(0); struct probeAndColor *pcList = NULL, *pc; int probeCount = 0; /* Make up a list of all probes in this image. */ sqlSafef(query, sizeof(query), "select probe,probeColor from imageProbe where image=%d", id); sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { AllocVar(pc); pc->probe = sqlUnsigned(row[0]); pc->probeColor = sqlUnsigned(row[1]); slAddHead(&pcList, pc); ++probeCount; } slReverse(&pcList); for (pc = pcList; pc != NULL; pc = pc->next) { int geneId; char *geneName; int probe = pc->probe; char *geneUrl = NULL; /* Get gene ID and name. */ sqlSafef(query, sizeof(query), "select gene from probe where id = %d", probe); geneId = sqlQuickNum(conn, query); geneName = vgGeneNameFromId(conn, geneId); /* Get url for known genes page if any. */ geneUrl = getKnownGeneUrl(conn, geneId); /* Print gene name, surrounded by hyperlink to known genes * page if possible. */ dyStringClear(dy); if (geneUrl != NULL) dyStringPrintf(dy, "", geneUrl); dyStringPrintf(dy, "%s", geneName); if (geneUrl != NULL) dyStringAppend(dy, ""); freez(&geneName); /* Add color if there's more than one probe for this image. */ if (probeCount > 1) { char *color; sqlSafef(query, sizeof(query), "select probeColor.name from probeColor " "where probeColor.id = %d" , pc->probeColor); color = sqlQuickString(conn, query); if (color != NULL) dyStringPrintf(dy, " (%s)", color); freez(&color); } /* Add to return list. */ slNameAddTail(&returnList, dy->string); } slFreeList(&pcList); slReverse(&returnList); return returnList; } static struct slName *getProbeList(struct sqlConnection *conn, int id) /* Get list of probes with hyperlinks to probe info page. */ { struct slName *returnList = NULL; char query[256]; char *sidUrl = cartSidUrlString(cart); struct dyString *dy = dyStringNew(0); struct slInt *probeList = NULL, *probe; int submissionSource = 0; /* Make up a list of all probes in this image. */ sqlSafef(query, sizeof(query), "select probe from imageProbe where image=%d", id); probeList = sqlQuickNumList(conn, query); sqlSafef(query, sizeof(query), "select submissionSet.submissionSource from image, submissionSet" " where image.submissionSet = submissionSet.id and image.id=%d", id); submissionSource = sqlQuickNum(conn, query); for (probe = probeList; probe != NULL; probe = probe->next) { char *type; /* Create hyperlink to probe page around gene name. */ dyStringClear(dy); dyStringPrintf(dy, "", hgVisiGeneCgiName(), sidUrl, hgpDoProbe, probe->val, hgpSs, submissionSource); sqlSafef(query, sizeof(query), "select probeType.name from probeType,probe where probe.id = %d " "and probe.probeType = probeType.id", probe->val); type = sqlQuickString(conn, query); dyStringPrintf(dy, "%s", naForEmpty(type)); if (sameWord(type, "antibody")) { char *abName; sqlSafef(query, sizeof(query), "select antibody.name from probe,antibody " "where probe.id = %d and probe.antibody = antibody.id" , probe->val); abName = sqlQuickString(conn, query); if (abName != NULL) { dyStringPrintf(dy, " %s", abName); freeMem(abName); } } else if (sameWord(type, "RNA")) { sqlSafef(query, sizeof(query), "select length(seq) from probe where id=%d", probe->val); if (sqlQuickNum(conn, query) > 0) dyStringPrintf(dy, " sequenced"); else { sqlSafef(query, sizeof(query), "select length(fPrimer) from probe where id=%d", probe->val); if (sqlQuickNum(conn, query) > 0) dyStringPrintf(dy, " from primers"); } } else if (sameWord(type, "BAC")) { char *name; sqlSafef(query, sizeof(query), "select bac.name from probe,bac " "where probe.id = %d and probe.bac = bac.id" , probe->val); name = sqlQuickString(conn, query); if (name != NULL) { dyStringPrintf(dy, " %s", name); freeMem(name); } } dyStringPrintf(dy, ""); freez(&type); /* Add to return list. */ slNameAddTail(&returnList, dy->string); } slFreeList(&probeList); slReverse(&returnList); return returnList; } #ifdef UNUSED static char *genomeDbForImage(struct sqlConnection *conn, int imageId) /* Return the genome database to associate with image or NULL if none. */ { char *db; char *scientificName = visiGeneOrganism(conn, imageId); struct sqlConnection *cConn = hConnectCentral(); char query[256]; sqlSafef(query, sizeof(query), "select defaultDb.name from defaultDb,dbDb where " "defaultDb.genome = dbDb.organism and " "dbDb.active = 1 and " "dbDb.scientificName = '%s'" , scientificName); db = sqlQuickString(cConn, query); hDisconnectCentral(&cConn); return db; } #endif /* UNUSED */ struct slName *vgImageFileGenes(struct sqlConnection *conn, int imageFile) /* Return alphabetical list of all genes associated with image file. */ { struct hash *uniqHash = hashNew(0); struct slInt *imageList, *image; struct slName *geneList = NULL, *gene; imageList = visiGeneImagesForFile(conn, imageFile); for (image = imageList; image != NULL; image = image->next) { struct slName *oneList, *next; oneList = visiGeneGeneName(conn, image->val); for (gene = oneList; gene != NULL; gene = next) { next = gene->next; if (hashLookup(uniqHash, gene->name)) freeMem(gene); else { hashAdd(uniqHash, gene->name, NULL); slAddHead(&geneList, gene); } } } slFreeList(&imageList); hashFree(&uniqHash); slSort(&geneList, slNameCmp); return geneList; } struct slName *vgImageFileGenotypes(struct sqlConnection *conn, int imageFile) /* Return unique alphabetical list of all genotypes associated with image file. */ { struct hash *uniqHash = hashNew(0); struct slInt *imageList, *image; struct slName *genoList=NULL,*genotype=NULL; imageList = visiGeneImagesForFile(conn, imageFile); for (image = imageList; image != NULL; image = image->next) { struct slName *oneList, *next; oneList = visiGeneGenotypes(conn, image->val); for (genotype = oneList; genotype != NULL; genotype = next) { next = genotype->next; if (hashLookup(uniqHash, genotype->name)) freeMem(genotype); else { hashAdd(uniqHash, genotype->name, NULL); slAddHead(&genoList, genotype); } } } slFreeList(&imageList); hashFree(&uniqHash); slSort(&genoList, slNameCmp); return genoList; } struct slName *vgImageFileOrganisms(struct sqlConnection *conn, int imageFile) /* Return unique alphabetical list of all organisms associated with image file. */ { struct hash *uniqHash = hashNew(0); struct slInt *imageList, *image; struct slName *orgList=NULL; imageList = visiGeneImagesForFile(conn, imageFile); for (image = imageList; image != NULL; image = image->next) { char *organism = visiGeneOrganism(conn, image->val); char *org = cloneString(shortOrgName(organism)); freeMem(organism); if (hashLookup(uniqHash, org)) freeMem(org); else { hashAdd(uniqHash, org, NULL); slNameAddHead(&orgList, org); } } slFreeList(&imageList); hashFree(&uniqHash); slSort(&orgList, slNameCmp); return orgList; } void smallCaption(struct sqlConnection *conn, int imageId) /* Write out small format caption. */ { struct slName *geneList, *gene; struct slName *genotypeList, *genotype; struct slName *orgList, *org; int imageFile = visiGeneImageFile(conn, imageId); boolean mutant = FALSE; char *sep = ""; genotypeList = vgImageFileGenotypes(conn, imageFile); for (genotype = genotypeList; genotype != NULL; genotype = genotype->next) { if (!sameString(genotype->name,"wild type")) mutant = TRUE; } slFreeList(&genotypeList); if (mutant) printf("Mutant "); orgList = vgImageFileOrganisms(conn, imageFile); for (org = orgList; org != NULL; org = org->next) { printf("%s%s", sep, org->name); sep = " "; } slFreeList(&orgList); geneList = vgImageFileGenes(conn, imageFile); for (gene = geneList; gene != NULL; gene = gene->next) { printf(" %s", gene->name); } slFreeList(&geneList); } static struct slName *expTissuesForProbeInImage(struct sqlConnection *conn, int imageId, int probeId) /* Get list of tissue where we have expression info in gene. * Put + or - depending on expression level. */ { struct dyString *dy = dyStringNew(0); struct slName *tissueList = NULL, *tissue; char query[512], **row; struct sqlResult *sr; sqlSafef(query, sizeof(query), "select bodyPart.name,expressionLevel.level,expressionPattern.description " "from expressionLevel join bodyPart join imageProbe " "left join expressionPattern on expressionLevel.expressionPattern = expressionPattern.id " "where imageProbe.image = %d " "and imageProbe.probe = %d " "and imageProbe.id = expressionLevel.imageProbe " "and expressionLevel.bodyPart = bodyPart.id " "order by bodyPart.name" , imageId, probeId); sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { double level = atof(row[1]); char *pattern = row[2]; if (pattern) tolowers(pattern); dyStringClear(dy); dyStringAppend(dy, row[0]); if (level == 1.0) dyStringAppend(dy, "(+)"); else if (level == 0.0) dyStringAppend(dy, "(-)"); else dyStringPrintf(dy, "(%.2f)",level); if (pattern && !sameWord(pattern,"Not Applicable") && !sameWord(pattern,"Not Specified")) dyStringPrintf(dy, " %s",pattern); tissue = slNameNew(dy->string); slAddHead(&tissueList, tissue); } sqlFreeResult(&sr); slReverse(&tissueList); dyStringFree(&dy); return tissueList; } struct expInfo /* Info on expression in various tissues for a particular gene. */ { struct expInfo *next; /* Next in list. */ int probeId; /* Id in probe table. */ int geneId; /* Id in gene table. */ char *geneName; /* Name of gene. */ struct slName *tissueList; /* List of tissues where gene expressed */ }; #ifdef UNUSED static void expInfoFree(struct expInfo **pExp) /* Free up memory associated with expInfo. */ { struct expInfo *exp = *pExp; if (exp != NULL) { freeMem(exp->geneName); slFreeList(&exp->tissueList); freez(pExp); } } static void expInfoFreeList(struct expInfo **pList) /* Free a list of dynamically allocated expInfo's */ { struct expInfo *el, *next; for (el = *pList; el != NULL; el = next) { next = el->next; expInfoFree(&el); } *pList = NULL; } #endif /* UNUSED */ static struct expInfo *expInfoForImage(struct sqlConnection *conn, int imageId) /* Return list of expression info for a given pane. */ { struct expInfo *expList = NULL, *exp; char query[512], **row; struct sqlResult *sr; /* Get list of probes associated with image, and start building * expList around it. */ sqlSafef(query, sizeof(query), "select imageProbe.probe,probe.gene from imageProbe,probe " "where imageProbe.image = %d " "and imageProbe.probe = probe.id", imageId); sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { AllocVar(exp); exp->probeId = sqlUnsigned(row[0]); exp->geneId = sqlUnsigned(row[1]); slAddHead(&expList, exp); } sqlFreeResult(&sr); slReverse(&expList); /* Finish filling in expInfo and return. */ for (exp = expList; exp != NULL; exp = exp->next) { exp->geneName = vgGeneNameFromId(conn, exp->geneId); exp->tissueList = expTissuesForProbeInImage(conn, imageId, exp->probeId); } return expList; } static void addExpressionInfo(struct sqlConnection *conn, int imageId, struct captionElement **pCeList) /* Add information about expression to caption element list. * Since the pane can contain multiple genes each with * separate caption information this is more complex than * the other caption elements. We'll make one captionElement * for each gene that we have expression info on. */ { int geneWithDataCount = 0, expCount = 0; struct expInfo *exp, *expList = expInfoForImage(conn, imageId); struct captionElement *ce; /* Figure out how much data we really have. */ for (exp = expList; exp != NULL; exp = exp->next) { if (exp->tissueList != NULL) ++geneWithDataCount; ++expCount; } /* If no data just add n/a */ if (geneWithDataCount == 0) { ce = captionElementNew(imageId, "Expression", cloneString("n/a")); slAddHead(pCeList, ce); } else { for (exp = expList; exp != NULL; exp = exp->next) { if (exp->tissueList != NULL) { char label[256]; if (expCount == 1) safef(label, sizeof(label), "Expression"); else safef(label, sizeof(label), "Expression of %s", exp->geneName); ce = captionElementNew(imageId, label, makeCommaSpacedList(exp->tissueList)); slAddHead(pCeList, ce); } } } } static struct slName *wrapUrlList(struct slName *labelList, struct slName *idList, char *url) /* Return list that substitutes id into url and wraps * url as a hyperlink around label. */ { struct slName *list = NULL, *label, *id; struct dyString *dy = dyStringNew(0); for (id = idList, label = labelList; id != NULL && label != NULL; id = id->next, label = label->next) { dyStringClear(dy); dyStringAppend(dy, "name); dyStringAppend(dy, "\" target=_blank>"); dyStringAppend(dy, label->name); dyStringAppend(dy, ""); slNameAddHead(&list, dy->string); } slReverse(&list); return list; } static char *getSimplifiedAllele(char *geneName, char *allele) /* If allele is of form geneName then return just * varient. Else return allele. Do a freeMem on result when * done. */ { int len = strlen(geneName); if (startsWith(geneName, allele) && allele[len] == '<') { char *s = allele + len + 1; char *e = strrchr(s, '>'); if (e == NULL) e = s + strlen(s); return cloneStringZ(s, e - s); } else return cloneString(allele); } char *visiGeneHypertextGenotype(struct sqlConnection *conn, int id) /* Return genotype of organism if any in nifty hypertext format. */ { int genotypeId; struct slName *geneIdList, *geneId; char query[256]; struct dyString *html; /* Look up genotype ID. */ sqlSafef(query, sizeof(query), "select specimen.genotype from image,specimen " "where image.id=%d and image.specimen = specimen.id", id); genotypeId = sqlQuickNum(conn, query); if (genotypeId == 0) return NULL; /* Get list of genes involved. */ sqlSafef(query, sizeof(query), "select distinct allele.gene from genotypeAllele,allele " "where genotypeAllele.genotype=%d " "and genotypeAllele.allele = allele.id" , genotypeId); geneIdList = sqlQuickList(conn, query); if (geneIdList == NULL) return cloneString("wild type"); /* Loop through each gene adding information to html. */ html = dyStringNew(0); for (geneId = geneIdList; geneId != NULL; geneId = geneId->next) { char *geneName; struct slName *alleleList, *allele; int alleleCount; boolean needsSlash = FALSE; /* Get gene name. */ sqlSafef(query, sizeof(query), "select name from gene where id='%s'", geneId->name); geneName = sqlQuickString(conn, query); if (geneName == NULL) internalErr(); /* Process each allele of gene. */ sqlSafef(query, sizeof(query), "select allele.name from genotypeAllele,allele " "where genotypeAllele.genotype=%d " "and genotypeAllele.allele = allele.id " "and allele.gene=%s" , genotypeId, geneId->name); alleleList = sqlQuickList(conn, query); alleleCount = slCount(alleleList); for (allele = alleleList; allele != NULL; allele = allele->next) { char *simplifiedAllele = getSimplifiedAllele(geneName, allele->name); int repCount = 1, rep; if (alleleCount == 1) repCount = 2; for (rep = 0; rep < repCount; ++rep) { if (needsSlash) dyStringAppendC(html, '/'); else needsSlash = TRUE; dyStringAppend(html, geneName); dyStringPrintf(html, "%s", simplifiedAllele); } freeMem(simplifiedAllele); } if (geneId->next != NULL) dyStringAppendC(html, ' '); slFreeList(&alleleList); freeMem(geneName); } slFreeList(&geneIdList); return dyStringCannibalize(&html); } static struct captionElement *makePaneCaptionElements( struct sqlConnection *conn, struct slInt *imageList) /* Make list of all caption elements */ { struct slInt *image; struct captionElement *ceList = NULL, *ce; for (image = imageList; image != NULL; image = image->next) { int paneId = image->val; struct slName *geneList = geneProbeList(conn, paneId); struct slName *genbankRawList = visiGeneGenbank(conn, paneId); struct slName *genbankUrlList = wrapUrlList(genbankRawList, genbankRawList, "https://www.ncbi.nlm.nih.gov/entrez/query.fcgi?" "cmd=Search&db=Nucleotide&term=%s&doptcmdl=GenBank&tool=genome.ucsc.edu"); struct slName *probeList = getProbeList(conn, paneId); ce = captionElementNew(paneId, "Gene", makeCommaSpacedList(geneList)); ce->hasHtml = TRUE; slAddHead(&ceList, ce); ce = captionElementNew(paneId, "Probe", makeCommaSpacedList(probeList)); ce->hasHtml = TRUE; slAddHead(&ceList, ce); ce = captionElementNew(paneId, "GenBank", makeCommaSpacedList(genbankUrlList)); slAddHead(&ceList, ce); ce->hasHtml = TRUE; ce = captionElementNew(paneId, "Organism", visiGeneOrganism(conn, paneId)); slAddHead(&ceList, ce); ce = captionElementNew(paneId, "Sex", naForNull(visiGeneSex(conn, paneId))); slAddHead(&ceList, ce); ce = captionElementNew(paneId, "Strain", naForNull(visiGeneStrain(conn, paneId))); slAddHead(&ceList, ce); ce = captionElementNew(paneId, "Genotype", naForNull(visiGeneHypertextGenotype(conn, paneId))); ce->hasHtml = TRUE; slAddHead(&ceList, ce); ce = captionElementNew(paneId, "Stage", visiGeneStage(conn, paneId, TRUE)); slAddHead(&ceList, ce); ce = captionElementNew(paneId, "Body Part", naForNull(visiGeneBodyPart(conn, paneId))); slAddHead(&ceList, ce); addExpressionInfo(conn, paneId, &ceList); ce = captionElementNew(paneId, "Section Type", naForNull(visiGeneSliceType(conn, paneId))); slAddHead(&ceList, ce); ce = captionElementNew(paneId, "Permeablization", naForNull(visiGenePermeablization(conn, paneId))); } slReverse(&ceList); return ceList; } static int nonTagStrlen(char *s) /* Count up string length excluding everything between '<' and '>' * Since this is a non-critical function just for layout purposes * it is simple, not dealing with corner cases like characters in * quotes. */ { char c; int count = 0; boolean inTag = FALSE; while ((c = *s++) != 0) { if (inTag) { if (c == '>') inTag = FALSE; } else { if (c == '<') inTag = TRUE; else count += 1; } } return count; } static void printCaptionElements(struct sqlConnection *conn, struct captionElement *captionElements, struct slInt *imageList) /* Print out caption elements - common elements first and then * pane-specific ones. */ { struct captionBundle *bundleList, *bundle; struct slRef *ref; struct captionElement *ce; int bundleCount; int maxCharsInLine = 70; bundleList = captionElementBundle(captionElements, imageList); bundleCount = slCount(bundleList); for (bundle = bundleList; bundle != NULL; bundle = bundle->next) { int charsInLine = 0, charsInEl; if (bundleCount > 1) printf("
\n"); if (bundle->image != 0) { char *label = visiGenePaneLabel(conn, bundle->image); if (label != NULL) printf("Pane %s
\n", label); } else if (bundleCount > 1) { printf("All Panes
\n"); } for (ref = bundle->elements; ref != NULL; ref = ref->next) { ce = ref->val; charsInEl = strlen(ce->type) + 2; if (ce->hasHtml) charsInEl += nonTagStrlen(ce->value); else charsInEl += strlen(ce->value); if (charsInLine != 0) { if (charsInLine + charsInEl > maxCharsInLine ) { printf("
\n"); charsInLine = 0; } else if (charsInLine != 0) printf(" "); } printf("%s: ", ce->type); if (ce->hasHtml) printf("%s", ce->value); else htmTextOut(stdout, ce->value); charsInLine += charsInEl; } if (charsInLine != 0) printf("
\n"); } if (bundleCount > 1) printf("
\n"); } static int visiGeneForwardedImageFile(struct sqlConnection *conn, int imageFile) /* Given imageFile ID, return ID of imageFile with better * caption info. If no such better info, just return zero. */ { char query[256]; sqlSafef(query, sizeof(query), "select toIf from imageFileFwd where fromIf = %d " , imageFile); return sqlQuickNum(conn, query); } static int vgForwardedImage(struct sqlConnection *conn, int image) /* Return id of image with better caption information if available. * Otherwise return zero. We are working in image ids instead of * imageFile ids only because much of the software works off image ids. */ { int imageFile = visiGeneImageFile(conn, image); int forwardedImage = 0; int forwardedImageFile = visiGeneForwardedImageFile(conn, imageFile); if (forwardedImageFile) { char query[256]; sqlSafef(query, sizeof(query), "select id from image where imageFile=%d", forwardedImageFile); forwardedImage = sqlQuickNum(conn, query); } return forwardedImage; } static void showSource(struct sqlConnection *conn, int id) /* Put up source info for id if available. */ { char *itemUrl = visiGeneItemUrl(conn, id); if (itemUrl != NULL) { printf("source: "); char *source = visiGeneSubmissionSource(conn, id); if (sameString(itemUrl,"")) printf("%s ", source); else { printf("%s ", source); } } } static void showAcknowledgement(struct sqlConnection *conn, int id) /* Print acknowledgement info if any. */ { char *acknowledgement = visiGeneAcknowledgement(conn, id); if (acknowledgement != NULL) printf("Acknowledgements: %s
\n", acknowledgement); } void fullCaption(struct sqlConnection *conn, int id) /* Print information about image. */ { char *publication, *copyright; char *caption = NULL; int imageFile = -1; struct slInt *imageList; int oldId = 0; struct captionElement *captionElements; int forwardedId = vgForwardedImage(conn, id); if (forwardedId) { oldId = id; id = forwardedId; } caption = visiGeneCaption(conn, id); imageFile = visiGeneImageFile(conn, id); showSource(conn, oldId); showSource(conn, id); publication = visiGenePublication(conn,id); if (publication != NULL) { char *pubUrl = visiGenePubUrl(conn,id); printf("Reference: "); if (pubUrl != NULL && pubUrl[0] != 0) printf("%s", pubUrl, publication); else { printf("%s", naForEmpty(publication)); } printf("
\n"); } printf("Year: %d ", visiGeneYear(conn,id)); printf("Contributors: %s
\n", naForNull(visiGeneContributors(conn,id))); if (caption != NULL) { printf("Notes: %s
\n", caption); freez(&caption); } imageList = visiGeneImagesForFile(conn, imageFile); captionElements = makePaneCaptionElements(conn, imageList); printCaptionElements(conn, captionElements, imageList); copyright = visiGeneCopyright(conn, id); if (copyright != NULL) printf("Copyright: %s
\n", copyright); showAcknowledgement(conn, oldId); showAcknowledgement(conn, id); printf("
\n"); }