5a249cd50f592a3b7598110eec792cfc942fab3f max Wed Sep 9 08:52:06 2026 -0700 Address the v504 code review hgSession: an anonymous share name that arrives with the request is saved only when it is not already in the table. Every anonymous link sits under the one reserved user "l", so a name already there stays as it is and the caller is told so. The top-right Share dialog is unaffected, since it passes a name it has just reserved and such a name does not exist yet. Snapshot names are now left out of both My Sessions listings, which is what their "__" prefix has claimed all along. Share dialog: "Create link & copy" reports the copy instead of promising it. copyToClipboard says whether the text reached the clipboard, the dialog passes that on when a browser refuses, and it tries the asynchronous clipboard API before giving up. The preview is built with the same encoding the server uses, so a name holding a hyphen or a slash previews as the link that really gets made. Cancelling out of the name editor no longer copies a second time, and a reply with no link in it says so rather than showing "undefined". hgBlat: a second click on the share button while the first request is still out no longer mints a second snapshot session, and a box dismissed during the wait stays closed. Also: a snapshot moves a cart value into durable storage only when it is a trash path, the way sessionData's own callers check; sqlAddressMatch keeps to its own documented precondition when handed an empty address; alphaGenomeToWig compares its output with its input rather than with itself, rejects a position that is not all digits and skips an empty score; and hgc's default iframe width reaches the browser as one percent sign. refs #38294 diff --git src/hg/lib/snapshotSession.c src/hg/lib/snapshotSession.c index b85d37d18e8..007a24897be 100644 --- src/hg/lib/snapshotSession.c +++ src/hg/lib/snapshotSession.c @@ -2,30 +2,31 @@ /* Copyright (C) 2026 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 "cheapcgi.h" #include "jksql.h" #include "cart.h" #include "hgConfig.h" #include "portable.h" #include "md5.h" #include "htmshell.h" #include "sessionData.h" +#include "trashDir.h" #include "snapshotSession.h" /* Bits of randomness in a server-generated snapshot token. 128 bits -> ~24 URL-safe chars, so * collisions are astronomically unlikely; snapshotNewName() also checks the DB and retries, so the * name is guaranteed unique regardless. */ #define snapshotTokenBits 128 /* ---- Registry of snapshot types ------------------------------------------------------------- */ /* BLAT: a single alignment (or the hgBlat results table) rebuilds from just the pinned bigPsl file; * the query sequence lives inside the bigPsl, so no .fa/.pslx is needed. "db" is added implicitly. */ static char *blatVars[] = { "blatLastBigBed", NULL }; static struct snapshotType snapshotTypes[] = { @@ -131,33 +132,36 @@ struct dyString *dy = dyStringNew(512); char *db = cartOptionalString(cart, "db"); if (isNotEmpty(db)) appendVar(dy, "db", db); int i; for (i = 0; type->vars[i] != NULL; i++) { char *var = type->vars[i]; char *val = cartOptionalString(cart, var); if (isEmpty(val)) continue; /* Move the referenced trash file into durable storage when sessionData is configured, and store * the durable path. sessionDataSaveTrashFile returns NULL if the file is gone (expired) - in - * that case keep the original value so the reconstruct path can report a clean "expired". */ + * that case keep the original value so the reconstruct path can report a clean "expired". + * Only a trash path is ours to move, and the value came out of the cart, so check it the way + * sessionData.c's own callers do. A value that is already a durable path (a snapshot being + * re-shared) or anything else is stored as it stands. */ char *durable = NULL; - if (isNotEmpty(sessionDir)) + if (isNotEmpty(sessionDir) && isTrashPath(val)) durable = sessionDataSaveTrashFile(val, sessionDir); appendVar(dy, var, isNotEmpty(durable) ? durable : val); freez(&durable); } freez(&sessionDir); return dyStringCannibalize(&dy); } int saveSnapshotSession(struct sqlConnection *conn, char *snapshotTypeName, char *encUserName, char *encSessionName, struct cart *cart) /* See snapshotSession.h. */ { struct snapshotType *type = snapshotTypeFind(snapshotTypeName); if (type == NULL) errAbort("saveSnapshotSession: unknown snapshot type '%s'", snapshotTypeName);