1c9352a7c5c2550a52b1d9fb2bae806b6aaed1be
chmalee
  Thu Jan 18 13:28:56 2024 -0800
Starting on pre-create hook for tusd

diff --git src/hg/hgHubConnect/hooks/pre-create.c src/hg/hgHubConnect/hooks/pre-create.c
new file mode 100644
index 0000000..947df6e
--- /dev/null
+++ src/hg/hgHubConnect/hooks/pre-create.c
@@ -0,0 +1,122 @@
+/* pre-create - tus daemon pre-create hook program. Reads
+ * a JSON encoded request to create an upload from a tus
+ * client and verifies that the user is logged in and the
+ * to be uploaded file will not exceed the quota. Regardless
+ * of success, return a JSON encoded response back to the
+ * client. */
+#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"
+
+void usage()
+/* Explain usage and exit. */
+{
+errAbort(
+  "pre-create - tus daemon pre-create hook program\n"
+  "usage:\n"
+  "   pre-create < input\n"
+  );
+}
+
+/* Command line validation table. */
+static struct optionSpec options[] = {
+   {NULL, 0},
+};
+struct jsonElement *makeDefaultResponse()
+/* Create the default response json with some fields pre-filled */
+{
+struct hash *defHash = hashNew(8);
+struct jsonElement *response = newJsonObject(defHash);
+// only the HTTP Response object is important to have by default, the other
+// fields will be created as needed
+jsonObjectAdd(response, HTTP_NAME, newJsonObject(hashNew(8)));
+return response;
+}
+
+int preCreate()
+/* pre-create 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");
+char *reqOffsetEnv = getenv("TUS_OFFSET");
+char *reqSizeEnv = getenv("TUS_SIZE");
+// 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 && reqOffsetEnv && reqSizeEnv))
+    {
+    rejectUpload(response, "not a TUS request");
+    exitStatus = 1;
+    }
+else
+    {
+    struct errCatch *errCatch = errCatchNew(0);
+    if (errCatchStart(errCatch))
+        {
+        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);
+        char *userName = (loginSystemEnabled() || wikiLinkEnabled()) ? wikiLinkUserName() : NULL;
+        fprintf(stderr, "userName='%s'\n'", userName);
+        if (!userName)
+            {
+            rejectUpload(response, "not logged in");
+            exitStatus = 1;
+            }
+        else
+            {
+            long reqFileSize = jsonQueryInt(req, "", "Event.Upload.Size", 0, NULL);
+            long currQuota = checkUserQuota(userName);
+            long newQuota = currQuota + reqFileSize;
+            if (newQuota > MAX_QUOTA)
+                {
+                rejectUpload(response, "file too large, current stored files is %0.2fgb", currQuota / 1000000000.0);
+                exitStatus = 1;
+                }
+            }
+
+        // we've passed all the checks so we can return that we are safe
+        if (exitStatus == 0)
+            fillOutHttpResponseSuccess(response);
+        }
+    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 preCreate();
+}