f043c937a34269023b71d7a89b7ff25103e6c125
angie
  Mon Feb 24 15:35:59 2020 -0800
Check user custom track group setting instead of allowing any string.  Don't silently drop warnings.  refs #25003, #25016

diff --git src/hg/lib/customFactory.c src/hg/lib/customFactory.c
index feb5f71..062e1aa 100644
--- src/hg/lib/customFactory.c
+++ src/hg/lib/customFactory.c
@@ -29,30 +29,31 @@
 #include "customFactory.h"
 #include "trashDir.h"
 #include "jsHelper.h"
 #include "encode/encodePeak.h"
 #include "udc.h"
 #include "bbiFile.h"
 #include "bigWig.h"
 #include "bigBed.h"
 #include "hgBam.h"
 #include "vcf.h"
 #include "makeItemsItem.h"
 #include "bedDetail.h"
 #include "pgSnp.h"
 #include "regexHelper.h"
 #include "chromInfo.h"
+#include "grp.h"
 #include "trackHub.h"
 #include "bedTabix.h"
 #include "barChartBed.h"
 #include "barChartUi.h"
 #include "interact.h"
 #include "interactUi.h"
 #include "hic.h"
 #include "cgiApoptosis.h"
 
 // placeholder when custom track uploaded file name is not known
 #define CT_NO_FILE_NAME         "custom track"
 
 static boolean doExtraChecking = FALSE;
 
 /*** Utility routines used by many factories. ***/
@@ -3538,30 +3539,48 @@
 if ((wordCount != 3) || (!isdigit(row[0][0]) || !isdigit(row[1][0]) || !isdigit(row[2][0])))
     errAbort("line %d of custom input, Expecting 3 comma separated numbers in color definition.", lineIx);
 *retR = atoi(row[0]);
 *retG = atoi(row[1]);
 *retB = atoi(row[2]);
 }
 
 static void stripJavascript(char **pString)
 /* Replace *pString (which is dynamically allocated) with javascript free version of itself. */
 {
 char *tmp = *pString;
 *pString = jsStripJavascript(tmp);
 freeMem(tmp);
 }
 
+static boolean checkGroup(char *db, char *group)
+/* Check if group is valid in db (if mysql, in grp table; if hub, in groups file or default) */
+{
+static struct hash *dbToGroups = NULL;
+if (dbToGroups == NULL)
+    dbToGroups = hashNew(0);
+struct hash *groups = hashFindVal(dbToGroups, db);
+if (groups == NULL)
+    {
+    groups = hashNew(0);
+    hashAdd(dbToGroups, db, groups);
+    struct grp *groupList = hLoadGrps(db), *grp;
+    for (grp = groupList;  grp != NULL;  grp = grp->next)
+        hashAddInt(groups, grp->name, TRUE);
+    }
+return hashIntValDefault(groups, group, FALSE);
+}
+
 static void customTrackUpdateFromSettings(struct customTrack *track,
                                           char *genomeDb,
 					  char *line, int lineIx)
 /* replace settings in track with those from new track line */
 {
 char *pLine = line;
 nextWord(&pLine);
 line = skipLeadingSpaces(pLine);
 struct hash *newSettings = hashVarLine(line, lineIx);
 struct hashCookie hc = hashFirst(newSettings);
 struct hashEl *hel = NULL;
 
 /* there is a memory leak in this business because these values in the
  * existing settings hash were maybe cloned strings and if they get replaced
  * those previous strings are leaking.  We can't fix this because we don't
@@ -3666,39 +3685,54 @@
 	}
     errCatchEnd(errCatch);
     if (errCatch->gotError)
 	warn("%s", errCatch->message->string);
     errCatchFree(&errCatch);
     }
 
 tdb->url = hashFindVal(hash, "url");
 
 if ((val = hashFindVal(hash, "visibility")) != NULL)
     {
     if (isdigit(val[0]))
 	{
 	tdb->visibility = atoi(val);
 	if (tdb->visibility > tvSquish)
-	    errAbort("line %d of custom input: Expecting visibility 0 to 4 got %s", lineIx, val);
+	    errAbort("Line %d of custom input: Expecting visibility 0 to 4 got %s. ", lineIx, val);
 	}
     else
         {
 	tdb->visibility = hTvFromString(val);
 	}
     }
 if ((val = hashFindVal(hash, "group")) != NULL)
+    {
+    if (checkGroup(genomeDb, val))
         tdb->grp = val;
+    else
+        {
+        boolean isHub = trackHubDatabase(genomeDb);
+        char *groupSource = isHub ? "the hub's groups.txt file" : "the table 'grp'";
+        warn("Line %d of custom input: group '%s' is not valid. "
+            "Either remove the group setting or use a group named in %s.",
+             lineIx, val, groupSource);
+        if (isHub)
+            hashRemove(hash, "group");
+        else
+            hashReplace(hash, "group", "user");
+        }
+    }
 if ((val = hashFindVal(hash, "useScore")) != NULL)
     tdb->useScore = !sameString(val, "0");
 if ((val = hashFindVal(hash, "priority")) != NULL)
     tdb->priority = atof(val);
 if ((val = hashFindVal(hash, "color")) != NULL)
     {
     char *c = cloneString(val);
     parseRgb(c, lineIx, &tdb->colorR, &tdb->colorG, &tdb->colorB);
     freeMem(c);
     }
 if ((val = hashFindVal(hash, "altColor")) != NULL)
     {
     char *c = cloneString(val);
     parseRgb(c, lineIx, &tdb->altColorR, &tdb->altColorG, &tdb->altColorB);
     freeMem(c);