b02d071f2f6d5629bc86dfa7313f649198b91841
chmalee
  Tue Nov 19 12:10:33 2024 -0800
Rename post-finish hook to pre-finish. Also restart tusd with the enabled hooks pre-create and pre-finish. This way when there is an error during the upload the error will get communicated back to the client as post-finish errors don't get sent back to the client

diff --git src/hg/hgHubConnect/hooks/pre-finish.c src/hg/hgHubConnect/hooks/pre-finish.c
new file mode 100644
index 0000000..4822096
--- /dev/null
+++ src/hg/hgHubConnect/hooks/pre-finish.c
@@ -0,0 +1,206 @@
+/* pre-finish  - tus daemon pre-finish hook program. Reads
+ * a JSON encoded request to finsh an upload from a tus
+ * client and moves a downloaded file to a specific user
+ * directory. */
+#include "common.h"
+#include "linefile.h"
+#include "hash.h"
+#include "options.h"
+#include "wikiLink.h"
+#include "customTrack.h"
+#include "userdata.h"
+#include "jsonQuery.h"
+#include "jsHelper.h"
+#include "errCatch.h"
+#include "obscure.h"
+#include "hooklib.h"
+#include "jksql.h"
+#include "hdb.h"
+#include "hubSpace.h"
+#include "md5.h"
+#include "cheapcgi.h"
+
+void usage()
+/* Explain usage and exit. */
+{
+errAbort(
+  "pre-finish - tus daemon pre-finish hook program\n"
+  "usage:\n"
+  "   pre-finish < input\n"
+  );
+}
+
+/* Command line validation table. */
+static struct optionSpec options[] = {
+   {NULL, 0},
+};
+
+int preFinish()
+/* pre-finish hook for tus daemon. Read JSON encoded hook request from
+ * stdin and write a JSON encoded hook to stdout. Writing to stderr
+ * will be redirected to the tusd log and not seen by the user, so for
+ * errors that the user needs to see, they need to be in the JSON response */
+{
+// TODO: create response object and do all error catching through that
+char *reqId = getenv("TUS_ID");
+// always return an exit status to the daemon and print to stdout, as
+// stdout gets sent by the daemon back to the client
+int exitStatus = 0;
+struct jsonElement *response = makeDefaultResponse();
+if (!(reqId))
+    {
+    rejectUpload(response, "not a TUS request");
+    exitStatus = 1;
+    }
+else
+    {
+    struct errCatch *errCatch = errCatchNew(0);
+    if (errCatchStart(errCatch))
+        {
+        // the variables for the row entry for this file, some can be NULL
+        char *userName = NULL;
+        char *fileName = NULL;
+        long long fileSize = 0;
+        char *fileType = NULL;
+        char *db = NULL;
+        char *location = NULL;
+        char *reqLm = NULL;
+        time_t lastModified = 0;
+        char *parentDir = NULL;
+
+        struct lineFile *lf = lineFileStdin(FALSE);
+        char *request = lineFileReadAll(lf);
+        struct jsonElement *req = jsonParse(request);
+        fprintf(stderr, "Hook request:\n");
+        jsonPrintToFile(req, NULL, stderr, 0);
+        char *reqCookie= jsonQueryString(req, "", "Event.HTTPRequest.Header.Cookie[0]", NULL);
+        if (reqCookie)
+            {
+            setenv("HTTP_COOKIE", reqCookie, 0);
+            }
+        fprintf(stderr, "reqCookie='%s'\n", reqCookie);
+        userName = (loginSystemEnabled() || wikiLinkEnabled()) ? wikiLinkUserName() : NULL;
+        fprintf(stderr, "userName='%s'\n'", userName);
+        if (!userName)
+            {
+            errAbort("not logged in");
+            }
+        else
+            {
+            // NOTE: All Upload.MetaData values are strings
+            fileName = cgiEncodeFull(jsonQueryString(req, "", "Event.Upload.MetaData.fileName", NULL));
+            fileSize = jsonQueryInt(req, "",  "Event.Upload.Size", 0, NULL);
+            fileType = jsonQueryString(req, "", "Event.Upload.MetaData.fileType", NULL);
+            db = jsonQueryString(req, "", "Event.Upload.MetaData.genome", NULL);
+            reqLm = jsonQueryString(req, "", "Event.Upload.MetaData.lastModified", NULL);
+            lastModified = sqlLongLong(reqLm) / 1000; // yes Javascript dates are in millis
+            parentDir = jsonQueryString(req, "", "Event.Upload.MetaData.parentDir", NULL);
+            fprintf(stderr, "parentDir = '%s'\n", parentDir);
+            fflush(stderr);
+            // strip out plain leading '.' and '/' components
+            // middle '.' components are dealt with later
+            if (startsWith("./", parentDir) || startsWith("/", parentDir))
+                parentDir = skipBeyondDelimit(parentDir, '/');
+            fprintf(stderr, "parentDir = '%s'\n", parentDir);
+            fflush(stderr);
+            char *tusFile = jsonQueryString(req, "", "Event.Upload.Storage.Path", NULL);
+            if (fileName == NULL)
+                {
+                errAbort("No Event.Upload.fileName setting");
+                }
+            else if (tusFile == NULL)
+                {
+                errAbort("No Event.Path setting");
+                }
+            else
+                {
+                char *tusInfo = catTwoStrings(tusFile, ".info");
+                char *dataDir = getDataDir(userName);
+                struct dyString *newFile = dyStringNew(0);
+                // if parentDir provided we are throwing the files in there
+                if (parentDir)
+                    {
+                    if (!endsWith(parentDir, "/"))
+                        parentDir = catTwoStrings(parentDir, "/");
+                    dataDir = catTwoStrings(dataDir, parentDir);
+                    }
+                dyStringPrintf(newFile, "%s%s", dataDir, fileName);
+
+                fprintf(stderr, "moving %s to %s\n", tusFile, dyStringContents(newFile));
+                // TODO: check if file exists or not and let user choose to overwrite
+                // and re-call this hook, for now just exit if the file exists
+                if (fileExists(dyStringContents(newFile)))
+                    {
+                    errAbort("file '%s' exists already, not overwriting", dyStringContents(newFile));
+                    }
+                else
+                    {
+                    // set our mysql table location:
+                    location = dyStringContents(newFile);
+                    // the directory may not exist yet
+                    int oldUmask = 00;
+                    if (!isDirectory(dataDir))
+                        {
+                        fprintf(stderr, "making directory '%s'\n", dataDir);
+                        // the directory needs to be 777, so ignore umask for now
+                        oldUmask = umask(0);
+                        makeDirsOnPath(dataDir);
+                        // restore umask
+                        umask(oldUmask);
+                        }
+                    copyFile(tusFile, dyStringContents(newFile));
+                    // the files definitely should not be executable!
+                    chmod(dyStringContents(newFile), 0666);
+                    mustRemove(tusFile);
+                    mustRemove(tusInfo);
+                    dyStringCannibalize(&newFile);
+                    }
+                }
+            }
+
+        // we've passed all the checks so we can write a new or updated row
+        // to the mysql table and return to the client that we were successful
+        if (exitStatus == 0)
+            {
+            // create a hub for this upload, which can be edited later
+            createNewTempHubForUpload(reqId, userName, db, fileName, fileType, parentDir);
+            fprintf(stderr, "added hub.txt and hubSpace row for hub for file: '%s'\n", fileName);
+            fflush(stderr);
+            struct hubSpace *row = NULL;
+            AllocVar(row);
+            row->userName = userName;
+            row->fileName = fileName;
+            row->fileSize = fileSize;
+            row->fileType = fileType;
+            row->creationTime = NULL; // automatically handled by mysql
+            row->lastModified = sqlUnixTimeToDate(&lastModified, TRUE);
+            row->parentDir = parentDir;
+            row->db = db;
+            row->location = location;
+            row->md5sum = md5HexForFile(row->location);
+            row->parentDir = parentDir ? parentDir : "";
+            addHubSpaceRowForFile(row);
+            fprintf(stderr, "added hubSpace row for file '%s'\n", fileName);
+            fflush(stderr);
+            }
+        }
+    if (errCatch->gotError)
+        {
+        rejectUpload(response, errCatch->message->string);
+        exitStatus = 1;
+        }
+    errCatchEnd(errCatch);
+    }
+// always print a response no matter what
+jsonPrintToFile(response, NULL, stdout, 0);
+return exitStatus;
+}
+
+int main(int argc, char *argv[])
+/* Process command line. */
+{
+optionInit(&argc, argv, options);
+if (argc != 1)
+    usage();
+return preFinish();
+}