75593a4e317eac40b781c83718961f0eb7f84f3a
max
  Thu Aug 27 06:32:34 2026 -0700
Blue bar login and logout now return to the page they were clicked on

#Preview2 week - bugs introduced now will need a build patch to fix
The Login link in the blue bar sent everyone to the My Sessions page after
they signed in, and the Sign out and account links in the logged-in dialog
did the same, so a visitor reading a help page or looking at an item details
page lost their place. Only hgTracks was handled, as a special case.

Adds wikiLinkEncodeCurrentPageReturnUrl(), which builds a returnto for the
page the CGI is currently serving, including its query string, since the
track and item parameters of pages like hgTrackUi and hgc are not all kept in
the cart. hgTracks keeps its old behaviour of returning to hgTracks?hgsid=,
because its state is in the cart and its query string can hold a one-shot
zoom or drag. hgMenubar does the same for the static pages it is included
into, using the page path from the SSI environment.

The two checks hgLogin runs on an incoming returnto (URL shape, and the
optional login.approvedReturn host list) move to wikiLink.c so that the CGIs
building a link apply the same rules before writing it. A URL hgLogin would
refuse now becomes an ordinary login link instead of an error page: an
over-long or oddly-quoted query string costs the query string, and a host
that login.approvedReturn does not cover falls back to the old My Sessions
target. Note that with login.approvedReturn set, static-page returns need
the bare host added to the list.

Also converts the fixed 2 kB buffers in the wikiLink URL builders to
dyStrings, since a cgi-encoded return URL can nearly triple in length and
safef would have aborted.

refs #38192

diff --git src/hg/inc/wikiLink.h src/hg/inc/wikiLink.h
index 806db598ccb..fbab19a8c2d 100644
--- src/hg/inc/wikiLink.h
+++ src/hg/inc/wikiLink.h
@@ -10,30 +10,32 @@
 #define CFG_WIKI_HOST "wiki.host"
 #define CFG_WIKI_USER_NAME_COOKIE "wiki.userNameCookie"
 #define CFG_WIKI_LOGGED_IN_COOKIE "wiki.loggedInCookie"
 #define CFG_WIKI_SESSION_COOKIE "wiki.sessionCookie"
 
 /* hg.conf login system parameter -- using non-wiki login system (hgLogin) if defined */
 #define CFG_LOGIN_SYSTEM_NAME "login.systemName"
 #define CFG_LOGIN_USE_HTTPS "login.https"
 #define CFG_LOGIN_COOKIE_SALT "login.cookieSalt"
 #define CFG_LOGIN_ACCEPT_ANY_ID "login.acceptAnyId"
 #define CFG_LOGIN_ACCEPT_IDX "login.acceptIdx"
 #define CFG_LOGIN_BASICAUTH "login.basicAuth"
 #define CFG_LOGIN_RELATIVE "login.relativeLink"
 /* Enables passwordless email-link sign-in and the "change email" option (default off). */
 #define CFG_LOGIN_EMAIL_LINK "login.emailLink"
+/* Comma-separated list of hosts that hgLogin will return a visitor to after login or logout. */
+#define CFG_APPROVED_HOSTS "login.approvedReturn"
 
 /* hg.conf central db parameters */
 #define CFG_CENTRAL_DOMAIN "central.domain"
 #define CFG_CENTRAL_COOKIE "central.cookie"
 
 char *loginSystemName();
 /* Return the wiki host specified in hg.conf, or NULL.  Allocd here. */
 
 boolean loginSystemEnabled();
 /* Return TRUE if login.systemName  parameter is defined in hg.conf . */
 
 boolean loginUseHttps();
 /* Return TRUE unless https is disabled in hg.conf. */
 
 boolean loginUseBasicAuth();
@@ -65,41 +67,64 @@
 char *getUserName();
 
 char *wikiLinkUserId();
 /* Return the user ID specified in cookies from the browser. Does not check if user is logged in.
  * To make sure that the ID is valid, call this only after you have checked with wikiLinkUserName() that the user is logged in. */
 
 char *wikiLinkUserLoginUrl(char *hgsid);
 /* Return the URL for the wiki user login page. */
 
 char *wikiLinkUserLoginUrlReturning(char *hgsid, char *returnUrl);
 /* Return the URL for the wiki user login page. */
 
 char *wikiLinkEncodeReturnUrl(char *hgsid, char *cgiName, char* urlSuffix);
 /* Return a CGI-encoded URL with hgsid to a CGI.  Free when done. */
 
+boolean loginReturnUrlIsAcceptable(char *returnUrl);
+/* Return TRUE if hgLogin will accept returnUrl as its returnto: an http or https URL with no
+ * character that could break out of the page hgLogin prints it into, on an approved host.
+ * hgLogin checks this on the way in; callers that build a returnto check it on the way out,
+ * so that a URL hgLogin would refuse becomes a plain login link instead of an error page. */
+
+char *wikiLinkEncodePageReturnUrl(char *url);
+/* Return url CGI-encoded for use as a returnto, or NULL if hgLogin would refuse it.
+ * Free when done. */
+
+char *wikiLinkEncodeCurrentPageReturnUrl(char *hgsid);
+/* Return a CGI-encoded URL for the page we are on right now, to hand to hgLogin as its
+ * returnto, so login and logout come back here instead of dropping the visitor on hgSession.
+ * Returns NULL when there is no page worth returning to, and the caller should then fall back
+ * to its own default.  Free when done. */
+
 char *wikiLinkUserLogoutUrl(char *hgsid);
 /* Return the URL for the wiki user logout page. */
 
 char *wikiLinkUserLogoutUrlReturning(char *hgsid, char *returnUrl);
 /* Return the URL for the wiki user logout page. */
 
 char *wikiLinkUserSignupUrl(char *hgsid);
 /* Return the URL for the user signup  page. */
 
 char *wikiLinkChangePasswordUrl(char *hgsid);
+/* Return the URL for the user change password page, returning to hgSession. */
+
+char *wikiLinkChangePasswordUrlReturning(char *hgsid, char *returnUrl);
 /* Return the URL for the user change password page. */
 
 char *wikiLinkChangeEmailUrl(char *hgsid);
-/* Return the URL for the user change email page. */
+/* Return the URL for the user change email page, returning to hgSession, or NULL if
+ * unavailable. */
+
+char *wikiLinkChangeEmailUrlReturning(char *hgsid, char *returnUrl);
+/* Return the URL for the user change email page, or NULL if unavailable. */
 
 char *wikiServerAndCgiDir();
 /* return the current full absolute URL up to the CGI name, like
  * http://genome.ucsc.edu/cgi-bin/. If login.relativeLink=on is
  * set, return only /cgi-bin/. Takes care of of non-root location of cgi-bin
  * and https. Result has to be free'd. */
 
 void wikiFixLogoutLinkWithJs();
 /* HTTP Basic Auth requires a strange hack to logout. This code prints a script 
  * that fixes an html link with id=logoutLink */
 
 #endif /* WIKILINK_H */