35c5facfb9e64030b6eeb70aadd2026bd9296c21
braney
Wed Sep 30 17:11:59 2026 -0700
hgSession: a Save request without the share flag or the name no longer ends in a stack dump, refs #38456
doNewSession() read the session name with cartString() and the share flag with
cartBoolean(), and both abort when the variable is missing. The Save form always
sends both, so only a hand-made request reached this. doReSaveSession() read the
name the same way, and outDefaultTracks() read db the same way, which a brand-new
cart does not have.
The name and db now have defaults, and a missing name gets the existing "without a
name" message. For the share flag the code asks the request, not the cart, because
the cart keeps hgS_newSessionShare on purpose. When the request carries neither the
flag nor the checkbox's shadow, an existing session keeps its own sharing level and a
new one is saved private.
diff --git src/hg/hgSession/hgSession.c src/hg/hgSession/hgSession.c
index 86f6b239a04..59cc28fda19 100644
--- src/hg/hgSession/hgSession.c
+++ src/hg/hgSession/hgSession.c
@@ -858,31 +858,31 @@
for(; statusList; statusList = statusList->next)
{
if (dy)
dyStringPrintf(dy,"%d=%s ", statusList->id, cgiEncode(statusList->hubUrl));
else
printf("%d=%s ", statusList->id, statusList->hubUrl);
}
if (dy == NULL)
printf("\n");
}
static void outDefaultTracks(struct cart *cart, struct dyString *dy)
/* Output the default trackDb visibility for all tracks
* in trackDb if the track is not mentioned in the cart. */
{
-database = cartString(cart, "db");
+database = cartUsualString(cart, "db", hDefaultDb());
struct trackDb *tdb = NULL;
// Some old sessions reference databases that are no longer present, and that triggers an errAbort
// when calling hgTrackDb. Just move on instead of errAborting.
struct errCatch *errCatch = errCatchNew();
if (errCatchStart(errCatch))
tdb = hTrackDb(database);
errCatchEnd(errCatch);
if (errCatch->gotError)
{
fprintf(stderr, "outDefaultTracks: Error from hTrackDb: '%s'; Continuing...",
errCatch->message->string);
tdb = NULL;
}
errCatchFree(&errCatch);
@@ -997,42 +997,58 @@
/* Prevent modification of the custom track collection just saved to namedSessionDb. Under
* copy-on-write hgCollection asks for its own trash copy before it writes, so this does
* nothing. refs #38273 */
cartCopyLocalHubsOnSessionLoad(cart);
return useCount;
}
char *doNewSession(char *userName)
/* Save current settings in a new named session.
* Return a message confirming what we did. */
{
if (userName == NULL)
return "Unable to save session -- please log in and try again.";
struct dyString *dyMessage = dyStringNew(2048);
/* Clone: saveCartAsSession() removes this cart variable, which frees the cart's own copy. */
-char *sessionName = trimSpaces(cloneString(cartString(cart, hgsNewSessionName)));
+char *sessionName = trimSpaces(cloneString(cartUsualString(cart, hgsNewSessionName, "")));
if (isEmpty(sessionName))
return "Error: Unable to save a session without a name. Please add one and try again.";
char *encSessionName = cgiEncodeFull(sessionName);
-boolean shareSession = cartBoolean(cart, hgsNewSessionShare);
char *encUserName = cgiEncodeFull(userName);
struct sqlConnection *conn = hConnectCentral();
if (sqlTableExists(conn, namedSessionTable))
{
- int useCount = saveCartAsSession(conn, encUserName, encSessionName, shareSession);
+ int sharingLevel;
+ /* Ask the request, not the cart: the cart keeps hgsNewSessionShare on purpose (see
+ * cleanHgSessionFromCart), so it is there long after the form that set it. */
+ if (cgiBooleanDefined(hgsNewSessionShare) || cgiVarExists(hgsNewSessionShare))
+ sharingLevel = cartBoolean(cart, hgsNewSessionShare);
+ else
+ {
+ /* The Save form always sends the checkbox's shadow, so this request did not come from it.
+ * Do not widen who can load the session: keep an existing session's level, and make a new
+ * one private (sqlQuickNum returns 0 when there is no row). */
+ struct dyString *query = sqlDyStringCreate(
+ "select shared from %s where userName = '%s' and sessionName = '%s'",
+ namedSessionTable, encUserName, encSessionName);
+ sharingLevel = sqlQuickNum(conn, query->string);
+ dyStringFree(&query);
+ }
+ boolean shareSession = (sharingLevel > 0);
+ int useCount = saveCartAsSession(conn, encUserName, encSessionName, sharingLevel);
if (useCount > INITIAL_USE_COUNT)
dyStringPrintf(dyMessage,
"Overwrote the contents of session %s "
"(that %s be shared with other users). "
"%s %s",
htmlEncode(sessionName), (shareSession ? "may" : "may not"),
getSessionLink(encUserName, encSessionName),
getSessionEmailLink(encUserName, encSessionName));
else
dyStringPrintf(dyMessage,
"Added a new session %s that %s be shared with other users. "
"%s %s",
htmlEncode(sessionName), (shareSession ? "may" : "may not"),
getSessionLink(encUserName, encSessionName),
getSessionEmailLink(encUserName, encSessionName));
@@ -2072,31 +2088,31 @@
encUserName, encSessionName, query);
return atoi(sharedStr);
}
char *doReSaveSession(char *userName, char *actionVar)
/* Load a session (which may have old trash and customTrash references) and re-save it
* so that customTrash tables will be moved to customData* databases and trash paths
* will be replaced with userdata (hg.conf sessionDataDir) paths.
* NOTE: this is not intended to be reachable by the UI; it is for a script to update
* old sessions to use the new sessionData locations. */
{
if (userName == NULL)
return "Unable to re-save session -- please log in and try again.";
struct sqlConnection *conn = hConnectCentral();
/* Clone: cartLoadUserSession() and saveCartAsSession() both free the cart's own copy. */
-char *sessionName = trimSpaces(cloneString(cartString(cart, hgsNewSessionName)));
+char *sessionName = trimSpaces(cloneString(cartUsualString(cart, hgsNewSessionName, "")));
if (isEmpty(sessionName))
return "Error: Unable to save a session without a name. Please add one and try again.";
char *encUserName = cgiEncodeFull(userName);
char *encSessionName = cgiEncodeFull(sessionName);
int sharingLevel = getSharingLevel(conn, encUserName, encSessionName);
cartLoadUserSession(conn, userName, sessionName, cart, NULL, actionVar);
// No cartCopyLocalHubsOnSessionLoad because we're not going to make any track collection changes
hubConnectLoadHubs(cart);
// Some old sessions reference databases that are no longer present, and that triggers an errAbort
// when cartHideDefaultTracks calls hgTrackDb. Don't let that stop the process of updating other
// stuff in the session.
struct errCatch *errCatch = errCatchNew();
if (errCatchStart(errCatch))
cartHideDefaultTracks(cart);