05094dabf3d005ab35a998ff225443fc4255611f
galt
  Mon Feb 4 23:07:42 2019 -0800
Adding ability to backup and restore the cart and its custom tracks in hgSession.

diff --git src/hg/lib/cart.c src/hg/lib/cart.c
index 4d5f22d..da01c6b 100644
--- src/hg/lib/cart.c
+++ src/hg/lib/cart.c
@@ -1500,44 +1500,61 @@
 struct cart *cart = *pCart;
 if (cart != NULL)
     {
     saveState(cart);
     struct sqlConnection *conn = cartDefaultConnector();
     cartTrace(cart, "checkout", conn);
     cartDefaultDisconnector(&conn);
     cartDbFree(&cart->userInfo);
     cartDbFree(&cart->sessionInfo);
     freeHash(&cart->hash);
     freeHash(&cart->exclude);
     freez(pCart);
     }
 }
 
+void cartSaveState(struct cart *cart)
+/* Free up cart and save it to database.
+ * Intended for updating cart before background CGI runs.
+ * Use cartCheckout() instead. */
+{
+if (cart != NULL)
+    {
+    saveState(cart);
+    }
+}
+
 char *cartSessionVarName()
 /* Return name of CGI session ID variable. */
 {
 return sessionVar;
 }
 
 char *cartSessionId(struct cart *cart)
 /* Return session id. */
 {
 static char buf[256];
 cartDbSecureId(buf, sizeof buf, cart->sessionInfo);
 return buf;
 }
 
+unsigned cartSessionRawId(struct cart *cart)
+/* Return raw session id without security key. */
+{
+return cart->sessionInfo->id;
+}
+
 char *cartSidUrlString(struct cart *cart)
 /* Return session id string as in hgsid=N . */
 {
 static char buf[64];
 safef(buf, sizeof(buf), "%s=%s", cartSessionVarName(), cartSessionId(cart));
 return buf;
 }
 
 char *cartUserId(struct cart *cart)
 /* Return session id. */
 {
 static char buf[256];
 cartDbSecureId(buf, sizeof buf, cart->userInfo);
 return buf;
 }
@@ -3389,93 +3406,123 @@
     }
 
 anythingChanged = (anythingChanged || (clensed > 0));
 return anythingChanged;
 }
 
 void cgiExitTime(char *cgiName, long enteredMainTime)
 /* single stderr print out called at end of CGI binaries to record run
  * time in apache error_log */
 {
 if (sameWord("yes", cfgOptionDefault("browser.cgiTime", "yes")) )
   fprintf(stderr, "CGI_TIME: %s: Overall total time: %ld millis\n",
         cgiName, clock1000() - enteredMainTime);
 }
 
+// TODO This should probably be moved to customFactory.c
+// Only used by hgSession 
+#include "errCatch.h"
 void cartCheckForCustomTracks(struct cart *cart, struct dyString *dyMessage)
 /* Scan cart for ctfile_<db> variables.  Tally up the databases that have
  * live custom tracks and those that have expired custom tracks. */
 /* While we're at it, also look for saved blat results. */
 {
 struct hashEl *helList = cartFindPrefix(cart, CT_FILE_VAR_PREFIX);
 if (helList != NULL)
     {
     struct hashEl *hel;
-    boolean gotLiveCT = FALSE, gotExpiredCT = FALSE;
-    struct slName *liveDbList = NULL, *expiredDbList = NULL, *sln = NULL;
+    boolean gotLiveCT = FALSE, gotExpiredCT = FALSE, gotErrorCT = FALSE;
+    struct slName *liveDbList = NULL, *expiredDbList = NULL,  *errorDbList = NULL, *sln = NULL;
     for (hel = helList;  hel != NULL;  hel = hel->next)
 	{
 	char *db = hel->name + strlen(CT_FILE_VAR_PREFIX);
-	boolean thisGotLiveCT = FALSE, thisGotExpiredCT = FALSE;
+	boolean thisGotLiveCT = FALSE, thisGotExpiredCT = FALSE, thisGotErrorCT = FALSE;
+	char errMsg[4096];
 	/* If the file doesn't exist, just remove the cart variable so it
 	 * doesn't get copied from session to session.  If it does exist,
 	 * leave it up to customFactoryTestExistence to parse the file for
 	 * possible customTrash table references, some of which may exist
 	 * and some not. */
 	if (!fileExists(hel->val))
 	    {
 	    cartRemove(cart, hel->name);
 	    thisGotExpiredCT = TRUE;
 	    }
 	else
 	    {
-	    customFactoryTestExistence(db, hel->val,
-				       &thisGotLiveCT, &thisGotExpiredCT);
+	    /* protect against errAbort */
+	    struct errCatch *errCatch = errCatchNew();
+	    if (errCatchStart(errCatch))
+		{
+		customFactoryTestExistence(db, hel->val, &thisGotLiveCT, &thisGotExpiredCT, NULL);
+		}
+	    errCatchEnd(errCatch);
+	    if (errCatch->gotError)  // tends to abort if db not found or hub not attached.
+		{
+		thisGotErrorCT = TRUE;
+		safef(errMsg, sizeof errMsg, "%s {%s}", db, errCatch->message->string);
+		}
+	    errCatchFree(&errCatch);
 	    }
 	if (thisGotLiveCT)
 	    slNameAddHead(&liveDbList, db);
 	if (thisGotExpiredCT)
 	    slNameAddHead(&expiredDbList, db);
+	if (thisGotErrorCT)
+	    slNameAddHead(&errorDbList, errMsg);
 	gotLiveCT |= thisGotLiveCT;
 	gotExpiredCT |= thisGotExpiredCT;
+	gotErrorCT |= thisGotErrorCT;
 	}
     if (gotLiveCT)
 	{
 	slSort(&liveDbList, slNameCmp);
 	dyStringPrintf(dyMessage,
 		       "<P>Note: the session has at least one active custom "
 		       "track (in database ");
 	for (sln = liveDbList;  sln != NULL;  sln = sln->next)
 	    dyStringPrintf(dyMessage, "<A HREF=\"hgCustom?%s&db=%s\">%s</A>%s",
 			   cartSidUrlString(cart), sln->name,
 			   sln->name, (sln->next ? sln->next->next ? ", " : " and " : ""));
 	dyStringAppend(dyMessage, "; click on the database link "
 		       "to manage custom tracks).  ");
 
 	}
     if (gotExpiredCT)
 	{
 	slSort(&expiredDbList, slNameCmp);
 	dyStringPrintf(dyMessage,
 		       "<P>Note: the session has at least one expired custom "
 		       "track (in database ");
 	for (sln = expiredDbList;  sln != NULL;  sln = sln->next)
 	    dyStringPrintf(dyMessage, "%s%s",
 			   sln->name, (sln->next ? sln->next->next ? ", " : " and " : ""));
 	dyStringPrintf(dyMessage,
 		       "), so it may not appear as originally intended.  ");
 	}
+    if (gotErrorCT)
+	{
+	slSort(&errorDbList, slNameCmp);
+	dyStringPrintf(dyMessage,
+		       "<P>Note: the session has at least one custom "
+		       "track with errors (in database ");
+	for (sln = errorDbList;  sln != NULL;  sln = sln->next)
+	    dyStringPrintf(dyMessage, "%s%s",
+			   sln->name, (sln->next ? sln->next->next ? ", " : " and " : ""));
+	dyStringPrintf(dyMessage,
+		       "), so it may not appear as originally intended.  ");
+	}
     dyStringPrintf(dyMessage,
 		   "These custom tracks should not expire, however, "
 		   "the UCSC Genome Browser is not a data storage service; "
 		   "<b>please keep a local backup of your sessions contents "
 		   "and custom track data</b>. </P>");
     slNameFreeList(&liveDbList);
     slNameFreeList(&expiredDbList);
     }
 /* Check for saved blat results (quasi custom track). */
 char *ss = cartOptionalString(cart, "ss");
 if (isNotEmpty(ss))
     {
     char buf[1024];
     char *words[2];
     int wordCount;