d2800e10690b66bf2ab04804e124958459f0ff0d
chmalee
  Thu Nov 7 11:11:15 2024 -0800
Add a parentDir field to the hubSpace table and make an index on it

diff --git src/hg/lib/userdata.c src/hg/lib/userdata.c
index cb333bd..3d98b71 100644
--- src/hg/lib/userdata.c
+++ src/hg/lib/userdata.c
@@ -1,336 +1,329 @@
 /* userdata.c - code for managing data stored on a per user basis */
 
 /* Copyright (C) 2014 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 
 #include "common.h"
 #include "hash.h"
 #include "portable.h"
 #include "trashDir.h"
 #include "md5.h"
 #include "hgConfig.h"
 #include "dystring.h"
 #include "cheapcgi.h"
 #include "customFactory.h"
 #include "wikiLink.h"
 #include "userdata.h"
 #include "jksql.h"
 #include "hdb.h"
 #include "hubSpace.h"
 
 char *getUserName()
 /* Query the right system for the users name */
 {
 return (loginSystemEnabled() || wikiLinkEnabled()) ? wikiLinkUserName() : NULL;
 }
 
 char *emailForUserName(char *userName)
 /* Fetch the email for this user from gbMembers hgcentral table */
 {
 struct sqlConnection *sc = hConnectCentral();
 struct dyString *query = sqlDyStringCreate("select email from gbMembers where userName = '%s'", userName);
 char *email = sqlQuickString(sc, dyStringCannibalize(&query));
 hDisconnectCentral(&sc);
 // this should be freeMem'd:
 return email;
 }
 
 char *getDataDir(char *userName)
 /* Return the full path to the user specific data directory, can be configured via hg.conf
  * on hgwdev, this is /data/apache/userdata/userStore/hash/userName/
  * on the RR, this is /userdata/userStore/hash/userName/ */
 {
 char *userDataBaseDir = cfgOption("userDataDir");
 if (!userDataBaseDir  || isEmpty(userDataBaseDir))
     errAbort("trying to save user file but no userDataDir defined in hg.conf");
 if (userDataBaseDir[0] != '/')
     errAbort("config setting userDataDir must be an absolute path (starting with '/')");
 
 char *encUserName = cgiEncode(userName);
 char *userPrefix = md5HexForString(encUserName);
 userPrefix[2] = '\0';
 
 struct dyString *newDataDir = dyStringNew(0);
 dyStringPrintf(newDataDir, "%s/%s/%s/", 
     userDataBaseDir, userPrefix, encUserName);
 
 fprintf(stderr, "userDataDir = '%s'\n", newDataDir->string);
 return dyStringCannibalize(&newDataDir);
 }
 
 char *getHubDataDir(char *userName, char *hub)
 {
 char *dataDir = getDataDir(userName);
 return catTwoStrings(dataDir, hub);
 }
 
 char *webDataDir(char *userName)
 /* Return a web accesible path to the userDataDir, this is different from the full path tusd uses */
 {
 char *retUrl = NULL;
 if (userName)
     {
     char *encUserName = cgiEncode(userName);
     char *userPrefix = md5HexForString(encUserName);
     userPrefix[2] = '\0';
     struct dyString *userDirDy = dyStringNew(0);
     dyStringPrintf(userDirDy, "%s/%s/%s/", HUB_SPACE_URL, userPrefix, encUserName);
     retUrl = dyStringCannibalize(&userDirDy);
     }
 return retUrl;
 }
 
 char *prefixUserFile(char *userName, char *fname)
 /* Allocate a new string that contains the full per-user path to fname, NULL otherwise */
 {
 char *pathPrefix = getDataDir(userName);
 if (pathPrefix)
     return catTwoStrings(pathPrefix, fname);
 else
     return NULL;
 }
 
 void addHubSpaceRowForFile(struct hubSpace *row)
 /* We created a file for a user, now add an entry to the hubSpace table for it */
 {
 struct sqlConnection *conn = hConnectCentral();
 
 // now write out row to hubSpace table
 if (!sqlTableExistsOnMain(conn, "hubSpace"))
     {
     errAbort("No hubSpace MySQL table is present. Please send an email to genome-www@soe.ucsc.edu  describing the exact steps you took just before you got this error");
     }
 hubSpaceSaveToDb(conn, row, "hubSpace", 0);
 }
 
 char *writeHubText(char *path, char *userName, char *encodedHubName, char *hubName, char *db)
 /* Create a hub.txt file, optionally creating the directory holding it. For convenience, return
  * the file name of the created hub, which can be freed. */
 {
 int oldUmask = 00;
 oldUmask = umask(0);
 makeDirsOnPath(path);
 // restore umask
 umask(oldUmask);
 // now make the hub.txt with some basic information
 char *hubFile = catTwoStrings(path, "/hub.txt");
 FILE *f = mustOpen(hubFile, "w");
 //fprintf(stderr, "would write \"hub %s\nemail %s\nshortLabel %s\nlongLabel %s\nuseOneFile on\n\ngenome %s\n\n\" to %s", hubName, emailForUserName(userName), hubName, hubName, db, hubFile);
 fprintf(f, "hub %s\n"
     "email %s\n"
     "shortLabel %s\n"
     "longLabel %s\n"
     "useOneFile on\n"
     "\n"
     "genome %s\n"
     "\n",
     encodedHubName, emailForUserName(userName), hubName, hubName, db);
 carefulClose(&f);
 return hubFile;
 }
 
-char *createNewTempHubForUpload(char *requestId, char *userName, char *db, char *trackFileName, char *trackType, char *reqHubName)
+char *createNewTempHubForUpload(char *requestId, char *userName, char *db, char *trackFileName, char *trackType, char *reqHubName, char *parentDir)
 /* Creates a hub.txt for this upload with a random hub name. Returns the full path to the hub
  * for convenience. If the reqHubName argument is non-NULL, use that as the hub name instead of
  * a random string AND do not create a hub.txt, only for use from hubtools up command */
 {
-char *encodedHubName = reqHubName != NULL ? cgiEncodeFull(reqHubName) : cgiEncodeFull(requestId);
 char *hubFileName = NULL;
 char *path = NULL;
-if (reqHubName)
-    {
-    // coming from hubtools command
 struct dyString *hubPath = dyStringNew(0);
-    dyStringPrintf(hubPath, "%s%s/hub.txt", getDataDir(userName), reqHubName);
-    path = hubFileName = dyStringCannibalize(&hubPath);
-    }
-else
-    {
-    path = prefixUserFile(userName, encodedHubName);
-    hubFileName = writeHubText(path, userName, encodedHubName, requestId, db);
+dyStringPrintf(hubPath, "%s%s", getDataDir(userName), reqHubName);
+// the reqHubName was used to make a directory, so it is cgi-encoded and has a '/' at the end
+char *decoded = needMem(strlen(reqHubName));
+cgiDecodeFull(reqHubName, decoded, strlen(reqHubName));
+path = hubFileName = writeHubText(dyStringCannibalize(&hubPath), userName, reqHubName, decoded, db);
 char *encodedTrack = cgiEncodeFull(trackFileName);
 struct dyString *trackFilePath = dyStringCreate("../%s", encodedTrack);
 FILE *f = mustOpen(hubFileName, "a");
 fprintf(f, "track %s\n"
     "bigDataUrl %s\n"
     "type %s\n"
     "shortLabel %s\n"
     "longLabel %s\n"
     "\n",
     encodedTrack, dyStringCannibalize(&trackFilePath),
     trackType, trackFileName, trackFileName);
 carefulClose(&f);
-    }
 
 // we should update the mysql table now with a record of the hub.txt
 struct hubSpace *row = NULL;
 AllocVar(row);
 row->userName = userName;
-row->fileName = hubFileName;
+row->fileName = "hub.txt";
 row->fileSize = fileSize(hubFileName);
 row->fileType = "hub";
 row->creationTime = NULL;
 time_t lastModTime = fileModTime(hubFileName);
 row->lastModified = sqlUnixTimeToDate(&lastModTime, TRUE);
-row->hubNameList = "";
 row->db = db;
 row->location = path;
 row->md5sum = md5HexForFile(hubFileName);
+row->parentDir = parentDir ? parentDir : "";
 addHubSpaceRowForFile(row);
-return encodedHubName;
+return reqHubName;
 }
 
 static void deleteHubSpaceRow(char *fname)
 /* Deletes a row from the hubspace table for a given fname */
 {
 struct sqlConnection *conn = hConnectCentral();
 struct dyString *deleteQuery = sqlDyStringCreate("delete from hubSpace where location='%s'", fname);
 sqlUpdate(conn, dyStringCannibalize(&deleteQuery));
 }
 
 void removeFileForUser(char *fname, char *userName)
 /* Remove a file for this user if it exists */
 {
 // The file to remove must be prefixed by the hg.conf userDataDir
 if (!startsWith(getDataDir(userName), fname))
     return;
 if (fileExists(fname))
     {
     // delete the actual file
     mustRemove(fname);
     // delete the table row
     deleteHubSpaceRow(fname);
     }
 }
 
 void removeHubForUser(char *path, char *userName)
 /* Remove a hub directory for this user (and all files in the directory), if it exists */
 {
 if (!startsWith(getDataDir(userName), path))
     return;
 if (isDirectory(path))
     {
     struct fileInfo *f, *flist = listDirX(path, NULL, TRUE);
     for (f = flist; f != NULL; f = f->next)
         mustRemove(f->name);
     // now we have deleted all the files in the dir we can safely rmdir
     mustRemove(path);
     deleteHubSpaceRow(path);
     }
 }
 
 void uploadTrack()
 /* Saves a new track to the persistent storage for this user */
 {
 //char *userName = getUserName();
 }
 
 static time_t getFileListLatestTime(struct userFiles *userFiles)
 /* Return the greatest last access time of the files in userFiles->fileList */
 {
 if (!userFiles->fileList)
     errAbort("no files in userFiles->fileList");
 time_t modTime = 0;
 struct fileInfo *f;
 for (f = userFiles->fileList; f != NULL; f = f->next)
     {
     if (f->lastAccess > modTime)
         {
         modTime = f->lastAccess;
         }
     }
 return modTime;
 }
 
 time_t getHubLatestTime(struct userHubs *hub)
 /* Return the latest access time of the files in a hub */
 {
 // NOTE: every hub is guaranteed to have at least one file
 return getFileListLatestTime(hub->fileList);
 }
 
 struct userFiles *listFilesForUserHub(char *userName, char *hubName)
 /* Get all the files for a particular hub for a particular user */
 {
 struct userFiles *userListing;
 AllocVar(userListing);
 char *path = getHubDataDir(userName, hubName);
 struct fileInfo *fiList = listDirX(path,NULL,FALSE);
 userListing->userName = userName;
 userListing->fileList = fiList;
 return userListing;
 }
 
 struct userHubs *listHubsForUser(char *userName)
 /* Lists the directories for a particular user */
 {
 struct userHubs *userHubs = NULL;
 char *path = getDataDir(userName);
 struct fileInfo *fi, *fiList = listDirX(path,NULL,FALSE);
 for (fi = fiList; fi != NULL; fi = fi->next)
     {
     if (fi->isDir)
         {
         struct userHubs *hub;
         AllocVar(hub);
         hub->hubName = cloneString(fi->name);
         hub->userName = cloneString(userName);
         char hubPath[PATH_LEN];
         safef(hubPath, sizeof(hubPath), "%s%s", path, fi->name);
         struct userFiles *hubFileList = listFilesForUserHub(userName, hub->hubName);
         hub->lastModified = getFileListLatestTime(hubFileList);
         hub->fileList = hubFileList;
         slAddHead(&userHubs, hub);
         }
     }
 return userHubs;
 }
 
 struct hubSpace *listFilesForUser(char *userName)
 /* Return the files the user has uploaded */
 {
 struct sqlConnection *conn = hConnectCentral();
 struct dyString *query = sqlDyStringCreate("select * from hubSpace where userName='%s' order by creationTime, fileName", userName);
 struct hubSpace *fileList = hubSpaceLoadByQuery(conn, dyStringCannibalize(&query));
 return fileList;
 }
 
 long long getMaxUserQuota(char *userName)
 /* Return how much space is allocated for this user or the default */
 {
 return HUB_SPACE_DEFAULT_QUOTA;
 }
 
 long long checkUserQuota(char *userName)
 /* Return the amount of space a user is currently using */
 {
 long long quota = 0;
 struct hubSpace *hubSpace, *hubSpaceList = listFilesForUser(userName);
 for (hubSpace = hubSpaceList; hubSpace != NULL; hubSpace = hubSpace->next)
     {
     quota += hubSpace->fileSize;
     }
 return quota;
 }
 
 char *storeUserFile(char *userName, char *newFileName, void *data, size_t dataSize)
 /* Give a fileName and a data stream, write the data to:
  * userDataDir/hashedUserName/userName/fileName
  * where userDataDir comes from hg.conf and
  * hashedUserName is based on the md5sum of the userName
  * to prevent proliferation of too many directories.
  *
  * After sucessfully saving the file, return a web accessible url
  * to the file. */
 {
 char *userDir = getDataDir(userName);
 makeDirsOnPath(userDir);
 char *pathToFile = catTwoStrings(userDir, newFileName);
 FILE *newFile = mustOpen(pathToFile, "wb");
 // the data will start with a line feed so get rid of that
 mustWrite(newFile, data, dataSize);
 // missing an EOF?
 carefulClose(&newFile);
 return pathToFile;
 }