16a4a0cbcbf56172a2e2e35dfe20c541d9cc46d7
hiram
  Wed Jan 14 16:49:35 2026 -0800
starting a liftRequest endpoint refs #31811

diff --git src/hg/hubApi/liftOver.c src/hg/hubApi/liftOver.c
index 8592d415495..ead207c3f70 100644
--- src/hg/hubApi/liftOver.c
+++ src/hg/hubApi/liftOver.c
@@ -1,26 +1,27 @@
 /* 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 "net.h"
+#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 ******/
 
 static void chainListOut(char *fromDb, char* toDb, int origSize, char *fromPos, struct chain *chainList)
 /* given the list of chains, output the list in JSON */
@@ -255,15 +256,62 @@
 if (isEmpty(fileName))
     apiErrAbort(err400, err400Msg, "Unable to find a chain file from %s to %s - please contact support", fromGenome, toGenome);
 fileName = hReplaceGbdbMustDownload(fileName);
 char fromPos[4096];
 safef(fromPos, sizeof(fromPos), "%s:%u-%u", chrom, uStart, uEnd);
 char *nChrom;
 int nStart, nEnd;
 if (!hgParseChromRange(NULL, fromPos, &nChrom, &nStart, &nEnd))
     apiErrAbort(err400, err400Msg, "position %s is not in chrom:start-end format", fromPos);
 int origSize = nEnd - nStart;
 struct chain *chainList = chainLoadAndTrimIntersecting(fileName, nChrom, nStart, nEnd);
 if (chainList == NULL)
     apiErrAbort(err400, err400Msg, "Sorry, this position %s is not found in the %s assembly", fromPos, toGenome);
 chainListOut(fromGenome, toGenome, origSize, fromPos, chainList);
 }
+
+// char *argLiftRequest[] = {argFromGenome, argToGenome, argEmail, argComment, NULL};
+void apiLiftRequest(char *words[MAX_PATH_INFO])
+/* 'liftOver' function words[1] is the subCommand */
+{
+char *extraArgs = verifyLegalArgs(argLiftRequest);
+if (extraArgs)
+    apiErrAbort(err400, err400Msg, "extraneous arguments found for function /liftRequest'%s'", extraArgs);
+
+char *fromGenome = cgiOptionalString(argFromGenome);
+char *toGenome = cgiOptionalString(argToGenome);
+char *email = cgiOptionalString(argEmail);
+char *comment = cgiOptionalString(argComment);
+
+/* probably want a silent exit here */
+if (isEmpty(fromGenome) || isEmpty(toGenome) || isEmpty(email) || isEmpty(comment))
+    apiErrAbort(err400, err400Msg, "must have all arguments: %s, %s, %s, %s for endpoint '/liftRequest", argFromGenome, argToGenome, argEmail, argComment);
+
+char *cookieName = hUserCookie();
+char *userId = findCookieData(cookieName);
+char *referer = getenv("HTTP_REFERER");
+char dir[PATH_LEN];
+char name[FILENAME_LEN];
+char ext[FILEEXT_LEN];
+/* expecting request to come from something.ucsc.edu/liftRequest.html */
+if (isNotEmpty(referer) && isNotEmpty(userId))
+    {
+    splitPath(referer, dir, name, ext);
+    if (! (endsWith(dir, ".ucsc.edu/") && sameWord(name, "liftRequest") && sameWord(ext, ".html"))) {
+          apiErrAbort(err400, err400Msg, "can not find required inputs for endpoint '/liftRequest");
+       }
+    } else {
+      if (! debug)
+          apiErrAbort(err400, err400Msg, "can not find required inputs for endpoint '/liftRequest");
+    }
+struct dyString *msg = newDyString(0);
+dyStringPrintf(msg, "Lift over request\nfrom: %s\nto: %s\nemail '%s'\ncomment: '%s'", fromGenome, toGenome, email, comment);
+/* our mailViaPipa never has any relevant return code indicating
+ *    success or failure.  So, ignore the return integer:
+ */
+// (void) mailViaPipe("TBD:GoogleEmailGroupAddress", "liftOver request", msg->string, "genome-www@ucsc.edu");
+
+/* some kind of response here back to the request page */
+struct jsonWrite *jw = apiStartOutput();
+jsonWriteString(jw, "msg", dyStringCannibalize(&msg));
+apiFinishOutput(0,NULL,jw);
+}