0201e8ea6a448b2124e117c5909ced7ff4a253c8
hiram
  Tue May 19 13:43:53 2026 -0700
adding login verification to the liftRequest functions refs #31811

diff --git src/hg/hubApi/liftOver.c src/hg/hubApi/liftOver.c
index a66498470b6..dfa3b03f982 100644
--- src/hg/hubApi/liftOver.c
+++ src/hg/hubApi/liftOver.c
@@ -1,27 +1,29 @@
 /* liftOver functions */
 
 #include "dataApi.h"
 #include "hgFind.h"
 #include "cartTrackDb.h"
 #include "cartJson.h"
 #include "genark.h"
 #include "asmAlias.h"
 #include "assemblyList.h"
 #include "liftOver.h"
 #include "liftOverChain.h"
 #include "net.h"
+#include "wikiLink.h"
+#include "userdata.h"
 /* do not need the mailViaPipe function here
 #include "mailViaPipe.h"
 */
 
 /**** SHOULD BE IN LIBRARY - code from hgConvert.c ******/
 static long chainTotalBlockSize(struct chain *chain)
 /* Return sum of sizes of all blocks in chain */
 {
 struct cBlock *block;
 long total = 0;
 for (block = chain->blockList; block != NULL; block = block->next)
     total += block->tEnd - block->tStart;
 return total;
 }
 /**** SHOULD BE IN LIBRARY - code from hgConvert.c ******/
@@ -157,30 +159,102 @@
         struct sqlResult *sr = sqlGetResult(conn, dyStringCannibalize(&pq));
         if ((row = sqlNextRow(sr)) != NULL)
             {
             jsonWriteBoolean(jw, "pending", TRUE);
             jsonWriteNumber(jw, "pendingStatus", sqlSigned(row[1]));
             jsonWriteString(jw, "pendingRequestTime", row[2]);
             }
         sqlFreeResult(&sr);
         }
     }
 
 apiFinishOutput(0, NULL, jw);
 hDisconnectCentral(&conn);
 }
 
+static void loginStatus()
+/* output current user login status as JSON */
+{
+char *userName = (loginSystemEnabled() || wikiLinkEnabled()) ? wikiLinkUserName() : NULL;
+struct jsonWrite *jw = apiStartOutput();
+
+if (userName != NULL)
+    {
+    // Get both email and realName from gbMembers table
+    struct sqlConnection *sc = hConnectCentral();
+    struct dyString *query = sqlDyStringCreate("select email, realName from gbMembers where userName = '%s'", userName);
+    struct sqlResult *sr = sqlGetResult(sc, dyStringCannibalize(&query));
+    char **row = sqlNextRow(sr);
+
+    char *email = NULL;
+    char *realName = NULL;
+    if (row != NULL)
+        {
+        email = cloneString(row[0] ? row[0] : "");
+        realName = cloneString(row[1] ? row[1] : "");
+        }
+    sqlFreeResult(&sr);
+    hDisconnectCentral(&sc);
+
+    // Build logout URL with returnto parameter
+    char *returnTo = cgiOptionalString("returnTo");
+    struct dyString *logoutUrl = dyStringNew(0);
+    if (isNotEmpty(returnTo))
+        {
+        char *encodedReturnUrl = cgiEncodeFull(returnTo);
+        dyStringPrintf(logoutUrl, "/cgi-bin/hgLogin?hgLogin.do.displayLogout=1&returnto=%s", encodedReturnUrl);
+        freeMem(encodedReturnUrl);
+        }
+    else
+        {
+        dyStringPrintf(logoutUrl, "/cgi-bin/hgLogin?hgLogin.do.displayLogout=1");
+        }
+
+    jsonWriteString(jw, "userName", userName);
+    jsonWriteString(jw, "email", email ? email : "");
+    jsonWriteString(jw, "realName", realName ? realName : "");
+    jsonWriteString(jw, "logoutUrl", dyStringCannibalize(&logoutUrl));
+
+    if (email)
+        freeMem(email);
+    if (realName)
+        freeMem(realName);
+    }
+else
+    {
+    jsonWriteString(jw, "userName", NULL);
+    // Use returnTo parameter passed by calling JavaScript
+    char *returnTo = cgiOptionalString("returnTo");
+    if (isNotEmpty(returnTo))
+        {
+        char *encodedReturnUrl = cgiEncodeFull(returnTo);
+        struct dyString *loginUrl = dyStringNew(0);
+        dyStringPrintf(loginUrl, "/cgi-bin/hgLogin?hgLogin.do.displayLoginPage=1&returnto=%s", encodedReturnUrl);
+        jsonWriteString(jw, "loginUrl", dyStringCannibalize(&loginUrl));
+        freeMem(encodedReturnUrl);
+        }
+    else
+        {
+        // No returnTo provided, just give basic login URL
+        jsonWriteString(jw, "loginUrl", "/cgi-bin/hgLogin?hgLogin.do.displayLoginPage=1");
+        }
+    jsonWriteString(jw, "signupUrl", "/cgi-bin/hgLogin?hgLogin.do.displaySignupPage=1");
+    }
+
+apiFinishOutput(0, NULL, jw);
+}
+
 /**** SHOULD BE IN LIBRARY - code from hgConvert.c ******/
 static char *skipWord(char *fw)
 /* skips over current word to start of next.
  * Error for this not to exist. */
 {
 char *s;
 s = skipToSpaces(fw);
 if (s == NULL)
     errAbort("Expecting two words in .ra file line %s\n", fw);
 s = skipLeadingSpaces(s);
 if (s == NULL)
     errAbort("Expecting two words in .ra file line %s\n", fw);
 return s;
 }
 
@@ -252,30 +326,35 @@
 }
 /**** SHOULD BE IN LIBRARY - code from hgConvert.c ******/
 
 void apiLiftOver(char *words[MAX_PATH_INFO])
 /* 'liftOver' function words[1] is the subCommand */
 {
 char *extraArgs = verifyLegalArgs(argLiftOver);
 if (extraArgs)
     apiErrAbort(err400, err400Msg, "extraneous arguments found for function /liftOver '%s'", extraArgs);
 
 if (sameWordOk("listExisting", words[1]))
     {
     listExisting();
     return;
     }
+else if (sameWordOk("loginStatus", words[1]))
+    {
+    loginStatus();
+    return;
+    }
 
 char *fromGenome = cgiOptionalString(argFromGenome);
 char *toGenome = cgiOptionalString(argToGenome);
 char *chrom = cgiOptionalString(argChrom);
 char *start = cgiOptionalString(argStart);
 char *end = cgiOptionalString(argEnd);
 
 if (isEmpty(fromGenome) || isEmpty(toGenome) || isEmpty(chrom) || isEmpty(start) || isEmpty(end))
     apiErrAbort(err400, err400Msg, "must have all arguments: %s, %s, %s, %s, %s  for endpoint '/liftOver", argFromGenome, argToGenome, argChrom, argStart, argEnd);
 
 unsigned uStart = 0;
 unsigned uEnd = 0;
 uStart = sqlUnsigned(start);
 uEnd = sqlUnsigned(end);
 if (uEnd < uStart)