Browse Source

Added experimental share vroot folder

Toby Chui 3 years ago
parent
commit
7392910324

+ 81 - 64
file_system.go

@@ -2025,14 +2025,6 @@ func system_fs_specialURIEncode(inputPath string) string {
 	return inputPath
 }
 
-func system_fs_matchFileExt(inputFilename string, extArray []string) bool {
-	inputExt := filepath.Ext(inputFilename)
-	if stringInSlice(inputExt, extArray) {
-		return true
-	}
-	return false
-}
-
 //Handle file properties request
 func system_fs_getFileProperties(w http.ResponseWriter, r *http.Request) {
 	userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
@@ -2137,7 +2129,6 @@ func system_fs_getFileProperties(w http.ResponseWriter, r *http.Request) {
 */
 
 func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
-
 	currentDir, _ := mv(r, "dir", true)
 	//Commented this line to handle dirname that contains "+" sign
 	//currentDir, _ = url.QueryUnescape(currentDir)
@@ -2159,77 +2150,97 @@ func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
 	if currentDir[len(currentDir)-1:] != "/" {
 		currentDir = currentDir + "/"
 	}
-	//Convert the virutal path to realpath
-	realpath, err := userinfo.VirtualPathToRealPath(currentDir)
 
+	VirtualRootID, subpath, err := fs.GetIDFromVirtualPath(currentDir)
 	if err != nil {
-		sendErrorResponse(w, "Error. Unable to parse path. "+err.Error())
+		sendErrorResponse(w, "Unable to resolve requested path: "+err.Error())
 		return
 	}
 
-	if !fileExists(realpath) {
-		userRoot, _ := userinfo.VirtualPathToRealPath("user:/")
-		if filepath.Clean(realpath) == filepath.Clean(userRoot) {
-			//Initiate user folder (Initiaed in user object)
-			userinfo.GetHomeDirectory()
-		} else if !strings.Contains(filepath.ToSlash(filepath.Clean(currentDir)), "/") {
-			//User root not created. Create the root folder
-			os.MkdirAll(filepath.Clean(realpath), 0775)
-		} else {
-			//Folder not exists
-			log.Println("[File Explorer] Requested path: ", realpath, " does not exists!")
-			sendErrorResponse(w, "Folder not exists")
+	var parsedFilelist []fs.FileData
+	var realpath string = ""
+	//Handle some special virtual file systems / mount points
+	if VirtualRootID == "share" && subpath == "" {
+		userpgs := userinfo.GetUserPermissionGroupNames()
+		files, err := shareEntryTable.RouteShareVroot(subpath, userinfo.Username, userpgs)
+		if err != nil {
+			sendErrorResponse(w, "Error. Unable to parse path. "+err.Error())
 			return
 		}
+		parsedFilelist = files
+	} else {
+		//Normal file systems
 
-	}
-	if sortMode == "" {
-		sortMode = "default"
-	}
-
-	//Check for really special exception in where the path contains [ or ] which cannot be handled via Golang Glob function
-	files, _ := system_fs_specialGlob(filepath.Clean(realpath) + "/*")
+		//Convert the virutal path to realpath
+		realpath, err = userinfo.VirtualPathToRealPath(currentDir)
 
-	var parsedFilelist []fs.FileData
-	var shortCutInfo *shortcut.ShortcutData = nil
-	for _, v := range files {
-		//Check if it is hidden file
-		isHidden, _ := hidden.IsHidden(v, false)
-		if showHidden != "true" && isHidden {
-			//Skipping hidden files
-			continue
+		if err != nil {
+			sendErrorResponse(w, err.Error())
+			return
 		}
 
-		//Check if this is an aodb file
-		if filepath.Base(v) == "aofs.db" || filepath.Base(v) == "aofs.db.lock" {
-			//Database file (reserved)
-			continue
+		if !fileExists(realpath) {
+			userRoot, _ := userinfo.VirtualPathToRealPath("user:/")
+			if filepath.Clean(realpath) == filepath.Clean(userRoot) {
+				//Initiate user folder (Initiaed in user object)
+				userinfo.GetHomeDirectory()
+			} else if !strings.Contains(filepath.ToSlash(filepath.Clean(currentDir)), "/") {
+				//User root not created. Create the root folder
+				os.MkdirAll(filepath.Clean(realpath), 0775)
+			} else {
+				//Folder not exists
+				log.Println("[File Explorer] Requested path: ", realpath, " does not exists!")
+				sendErrorResponse(w, "Folder not exists")
+				return
+			}
+
+		}
+		if sortMode == "" {
+			sortMode = "default"
 		}
 
-		//Check if it is shortcut file. If yes, render a shortcut data struct
-		if filepath.Ext(v) == ".shortcut" {
-			//This is a shortcut file
-			shorcutData, err := shortcut.ReadShortcut(v)
-			if err == nil {
-				shortCutInfo = shorcutData
+		//Check for really special exception in where the path contains [ or ] which cannot be handled via Golang Glob function
+		files, _ := system_fs_specialGlob(filepath.Clean(realpath) + "/*")
+		var shortCutInfo *shortcut.ShortcutData = nil
+		for _, v := range files {
+			//Check if it is hidden file
+			isHidden, _ := hidden.IsHidden(v, false)
+			if showHidden != "true" && isHidden {
+				//Skipping hidden files
+				continue
 			}
-		}
 
-		rawsize := fs.GetFileSize(v)
-		modtime, _ := fs.GetModTime(v)
-		thisFile := fs.FileData{
-			Filename:    filepath.Base(v),
-			Filepath:    currentDir + filepath.Base(v),
-			Realpath:    v,
-			IsDir:       IsDir(v),
-			Filesize:    rawsize,
-			Displaysize: fs.GetFileDisplaySize(rawsize, 2),
-			ModTime:     modtime,
-			IsShared:    shareManager.FileIsShared(v),
-			Shortcut:    shortCutInfo,
-		}
+			//Check if this is an aodb file
+			if filepath.Base(v) == "aofs.db" || filepath.Base(v) == "aofs.db.lock" {
+				//Database file (reserved)
+				continue
+			}
 
-		parsedFilelist = append(parsedFilelist, thisFile)
+			//Check if it is shortcut file. If yes, render a shortcut data struct
+			if filepath.Ext(v) == ".shortcut" {
+				//This is a shortcut file
+				shorcutData, err := shortcut.ReadShortcut(v)
+				if err == nil {
+					shortCutInfo = shorcutData
+				}
+			}
+
+			rawsize := fs.GetFileSize(v)
+			modtime, _ := fs.GetModTime(v)
+			thisFile := fs.FileData{
+				Filename:    filepath.Base(v),
+				Filepath:    currentDir + filepath.Base(v),
+				Realpath:    v,
+				IsDir:       IsDir(v),
+				Filesize:    rawsize,
+				Displaysize: fs.GetFileDisplaySize(rawsize, 2),
+				ModTime:     modtime,
+				IsShared:    shareManager.FileIsShared(v),
+				Shortcut:    shortCutInfo,
+			}
+
+			parsedFilelist = append(parsedFilelist, thisFile)
+		}
 	}
 
 	//Sort the filelist
@@ -2272,6 +2283,12 @@ func system_fs_handleDirHash(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	VirtualRootID, subpath, _ := fs.GetIDFromVirtualPath(currentDir)
+	if VirtualRootID == "share" && subpath == "" {
+		sendTextResponse(w, hex.EncodeToString([]byte("0")))
+		return
+	}
+
 	rpath, err := userinfo.VirtualPathToRealPath(currentDir)
 	if err != nil {
 		sendErrorResponse(w, "Invalid dir given")

+ 17 - 3
mod/filesystem/static.go

@@ -60,10 +60,24 @@ type FileProperties struct {
 
 //Check if the two file system are identical.
 func MatchingFileSystem(fsa *FileSystemHandler, fsb *FileSystemHandler) bool {
-	if fsa.Filesystem == fsb.Filesystem {
-		return true
+	return fsa.Filesystem == fsb.Filesystem
+}
+
+//Get the ID part of a virtual path, return ID, subpath and error
+func GetIDFromVirtualPath(vpath string) (string, string, error) {
+	if !strings.Contains(vpath, ":") {
+		return "", "", errors.New("Path missing Virtual Device ID. Given: " + vpath)
 	}
-	return false
+
+	//Clean up the virutal path
+	vpath = filepath.ToSlash(filepath.Clean(vpath))
+
+	tmp := strings.Split(vpath, ":")
+	vdID := tmp[0]
+	pathSlice := tmp[1:]
+	path := strings.Join(pathSlice, ":")
+
+	return vdID, path, nil
 }
 
 func GetFileDataFromPath(vpath string, realpath string, sizeRounding int) FileData {

+ 83 - 0
mod/share/shareEntry/shareEntry.go

@@ -3,10 +3,13 @@ package shareEntry
 import (
 	"encoding/json"
 	"errors"
+	"log"
 	"path/filepath"
+	"strings"
 	"sync"
 
 	"imuslab.com/arozos/mod/database"
+	fs "imuslab.com/arozos/mod/filesystem"
 )
 
 /*
@@ -156,3 +159,83 @@ func (s *ShareEntryTable) RemoveShareByUUID(uuid string) error {
 	}
 	return nil
 }
+
+func (s *ShareEntryTable) ResolveShareVrootPath(subpath string, username string, usergroup []string) (string, error) {
+	//Get a list of accessible files from this user
+	subpathElements := strings.Split(filepath.ToSlash(filepath.Clean(subpath))[1:], "/")
+
+	if len(subpathElements) == 0 {
+		//Requesting root folder.
+		return "", errors.New("This virtual file system router do not support root listing")
+	}
+
+	//Analysis the subpath elements
+	if len(subpathElements) == 1 {
+		return "", errors.New("Redirect: parent")
+	} else if len(subpathElements) == 2 {
+		//ID only or ID with the target filename
+		shareObject := s.GetShareObjectFromUUID(subpathElements[0])
+		if shareObject == nil {
+			return "", errors.New("Share file not found")
+		}
+
+		return shareObject.FileRealPath, nil
+	} else if len(subpathElements) > 2 {
+		//Loading folder / files inside folder type shares
+		shareObject := s.GetShareObjectFromUUID(subpathElements[0])
+		folderSubpaths := append([]string{shareObject.FileRealPath}, subpathElements[2:]...)
+		targetFolder := filepath.Join(folderSubpaths...)
+		log.Println("Loading folder:", targetFolder)
+		return targetFolder, nil
+	}
+
+	return "", errors.New("Not implemented")
+}
+
+func (s *ShareEntryTable) RouteShareVroot(subpath string, username string, usergroup []string) ([]fs.FileData, error) {
+	if subpath == "" {
+		return s.listRootForUser(username, usergroup), nil
+	} else {
+		log.Println("Checkpoint!", subpath)
+		return []fs.FileData{}, nil
+	}
+}
+
+func (s *ShareEntryTable) listRootForUser(username string, usergroup []string) []fs.FileData {
+	//Iterate through all shares in the system to see which of the share is user accessible
+	userAccessiableShare := []*ShareOption{}
+	s.FileToUrlMap.Range(func(fp, so interface{}) bool {
+		fileRealpath := fp.(string)
+		thisShareOption := so.(*ShareOption)
+		if fs.FileExists(fileRealpath) {
+			//File exists. Check if the share options allow this user access (Only signed in user has username)
+			if thisShareOption.Permission == "anyone" || thisShareOption.Permission == "signedin" {
+				userAccessiableShare = append(userAccessiableShare, thisShareOption)
+			} else if thisShareOption.Permission == "samegroup" || thisShareOption.Permission == "groups" {
+				for _, thisUserGroup := range usergroup {
+					if stringInSlice(thisUserGroup, thisShareOption.Accessibles) {
+						//User's group is in the allowed group
+						userAccessiableShare = append(userAccessiableShare, thisShareOption)
+						break
+					}
+				}
+			} else if thisShareOption.Permission == "users" {
+				if stringInSlice(username, thisShareOption.Accessibles) {
+					//User's group is in the allowed group
+					userAccessiableShare = append(userAccessiableShare, thisShareOption)
+				}
+			}
+		}
+		return true
+	})
+
+	results := []fs.FileData{}
+	for _, thisShareObject := range userAccessiableShare {
+		rpath := thisShareObject.FileRealPath
+		thisFile := fs.GetFileDataFromPath("share:/"+thisShareObject.UUID+"/"+filepath.Base(rpath), rpath, 2)
+		thisFile.IsShared = true
+		results = append(results, thisFile)
+	}
+
+	return results
+}

+ 10 - 0
mod/share/shareEntry/utils.go

@@ -0,0 +1,10 @@
+package shareEntry
+
+func stringInSlice(e string, s []string) bool {
+	for _, a := range s {
+		if a == e {
+			return true
+		}
+	}
+	return false
+}

+ 1 - 1
mod/user/directoryHandler.go

@@ -106,7 +106,7 @@ func (u *User) VirtualPathToRealPath(vpath string) (string, error) {
 			} else if storage.Hierarchy == "public" {
 				return filepath.ToSlash(filepath.Clean(storage.Path) + subpath), nil
 			} else if storage.Hierarchy == "share" {
-				//return storage.HierarchyConfig.(share.ShareVFS).Resolve(subpath)
+				return (*u.parent.shareEntryTable).ResolveShareVrootPath(subpath, u.Username, u.GetUserPermissionGroupNames())
 			} else {
 				return "", errors.New("Unknown Filesystem Handler Hierarchy")
 			}

+ 8 - 0
mod/user/permissionHandler.go

@@ -170,6 +170,14 @@ func (u *User) GetUserPermissionGroup() []*permission.PermissionGroup {
 	return u.PermissionGroup
 }
 
+func (u *User) GetUserPermissionGroupNames() []string {
+	userPermissionGroups := []string{}
+	for _, pg := range u.PermissionGroup {
+		userPermissionGroups = append(userPermissionGroups, pg.Name)
+	}
+	return userPermissionGroups
+}
+
 //Check if the user is in one of the permission groups, require groupname
 func (u *User) UserIsInOneOfTheGroupOf(groupnames []string) bool {
 	userpg := u.GetUserPermissionGroup()

+ 12 - 9
mod/user/user.go

@@ -12,6 +12,7 @@ import (
 	db "imuslab.com/arozos/mod/database"
 	permission "imuslab.com/arozos/mod/permission"
 	quota "imuslab.com/arozos/mod/quota"
+	"imuslab.com/arozos/mod/share/shareEntry"
 	storage "imuslab.com/arozos/mod/storage"
 )
 
@@ -31,19 +32,21 @@ type User struct {
 }
 
 type UserHandler struct {
-	authAgent *auth.AuthAgent
-	database  *db.Database
-	phandler  *permission.PermissionHandler
-	basePool  *storage.StoragePool
+	authAgent       *auth.AuthAgent
+	database        *db.Database
+	phandler        *permission.PermissionHandler
+	basePool        *storage.StoragePool
+	shareEntryTable **shareEntry.ShareEntryTable
 }
 
 //Initiate a new user handler
-func NewUserHandler(systemdb *db.Database, authAgent *auth.AuthAgent, permissionHandler *permission.PermissionHandler, baseStoragePool *storage.StoragePool) (*UserHandler, error) {
+func NewUserHandler(systemdb *db.Database, authAgent *auth.AuthAgent, permissionHandler *permission.PermissionHandler, baseStoragePool *storage.StoragePool, shareEntryTable **shareEntry.ShareEntryTable) (*UserHandler, error) {
 	return &UserHandler{
-		authAgent: authAgent,
-		database:  systemdb,
-		phandler:  permissionHandler,
-		basePool:  baseStoragePool,
+		authAgent:       authAgent,
+		database:        systemdb,
+		phandler:        permissionHandler,
+		basePool:        baseStoragePool,
+		shareEntryTable: shareEntryTable,
 	}, nil
 }
 

+ 17 - 17
storage.go

@@ -63,25 +63,9 @@ func LoadBaseStoragePool() error {
 	}
 	fsHandlers = append(fsHandlers, baseHandler)
 
-	//Load the tmp folder as storage unit
-	tmpHandler, err := fs.NewFileSystemHandler(fs.FileSystemOption{
-		Name:       "tmp",
-		Uuid:       "tmp",
-		Path:       filepath.ToSlash(filepath.Clean(*tmp_directory)) + "/",
-		Hierarchy:  "user",
-		Automount:  false,
-		Filesystem: localFileSystem,
-	})
-
-	if err != nil {
-		log.Println("Failed to initiate tmp storage directory: "+*tmp_directory, err.Error())
-		return err
-	}
-	fsHandlers = append(fsHandlers, tmpHandler)
-
 	//Load the special share folder as storage unit
 	shareHandler, err := fs.NewFileSystemHandler(fs.FileSystemOption{
-		Name:       "share",
+		Name:       "Share",
 		Uuid:       "share",
 		Path:       filepath.ToSlash(filepath.Clean(*tmp_directory)) + "/",
 		Access:     "readonly",
@@ -96,6 +80,22 @@ func LoadBaseStoragePool() error {
 	}
 	fsHandlers = append(fsHandlers, shareHandler)
 
+	//Load the tmp folder as storage unit
+	tmpHandler, err := fs.NewFileSystemHandler(fs.FileSystemOption{
+		Name:       "tmp",
+		Uuid:       "tmp",
+		Path:       filepath.ToSlash(filepath.Clean(*tmp_directory)) + "/",
+		Hierarchy:  "user",
+		Automount:  false,
+		Filesystem: localFileSystem,
+	})
+
+	if err != nil {
+		log.Println("Failed to initiate tmp storage directory: "+*tmp_directory, err.Error())
+		return err
+	}
+	fsHandlers = append(fsHandlers, tmpHandler)
+
 	//Load all the storage config from file
 	rawConfig, err := ioutil.ReadFile(*storage_config_file)
 	if err != nil {

+ 1 - 1
user.go

@@ -23,7 +23,7 @@ import (
 
 func UserSystemInit() {
 	//Create a new User Handler
-	uh, err := user.NewUserHandler(sysdb, authAgent, permissionHandler, baseStoragePool)
+	uh, err := user.NewUserHandler(sysdb, authAgent, permissionHandler, baseStoragePool, &shareEntryTable)
 	if err != nil {
 		panic(err)
 	}

+ 16 - 0
web/SystemAO/file_system/file_explorer.html

@@ -1533,6 +1533,22 @@
 
                                 if (data.error !== undefined){
                                     //Parse path error. Try to refresh the page
+                                    if (data.error.length >= "Redirect:".length && data.error.substr(0,9) == "Redirect:"){
+                                        var redirectAction = data.error.substr(9).trim();
+                                        if (redirectAction == "parent"){
+                                            var pdir = currentPath.split("/");
+                                            pdir.pop(); pdir.pop();
+                                            pdir = pdir.join("/");
+                                            currentPath = pdir;
+                                            listDirectory(currentPath);
+                                        }else if (redirectAction == "root"){
+                                            var currentRoot = currentPath.split("/").shift();
+                                            listDirectory(currentRoot);
+                                        }else if (redirectAction == "userroot"){
+                                            listDirectory("user:/")
+                                        }
+                                        return
+                                    }
                                     //console.log("Path parse error! Redirecting to parent directory.", data.error);
                                     //var pdir = currentPath.split("/");
                                     //pdir.pop(); pdir.pop();

+ 6 - 2
web/SystemAO/file_system/file_operation.html

@@ -639,7 +639,9 @@ mainloop:
                 if (data.error !== undefined){
                     $("#progressbar").css("background-color","#db2828");
                     $("#progressbar").parent().removeClass('active');
-                    $(".title").html(`<i class="remove icon"></i>` + data.error);
+                    $(".title").html(`<i class="remove icon"></i>` + applocale.getString("error/" + data.error,data.error));
+                    enterErrorMode = true;
+                    return
                 }else{
                     $("#progressbar").css("background-color","#21ba45");
                     $("#progressbar").parent().removeClass('active');
@@ -670,7 +672,9 @@ mainloop:
                                 parent.refresh(undefined, true);
                             }
 
-                            ao_module_close();
+                            if (!enterErrorMode){
+                                ao_module_close();
+                            }
                         }, 1000);
                     }else if (opr == "download"){
                         //Create download from buffer

+ 8 - 0
web/SystemAO/locale/file_operation.json

@@ -23,6 +23,14 @@
                 "title/unzipping":"正在解壓 ",
                 "title/file":" 個檔案",
                 "title/files":" 個檔案",
+                "error/Access Denied.":"存取被拒",
+                "error/Source file not exists":"來源檔案不存在",
+                "error/Storage Quota Full":"儲存空間已滿",
+                "error/Dest folder not found":"目標資料夾不存在",
+                "error/Undefined dest location.":"目標路徑錯誤",
+                "error/This source file is Read Only.":"此來源檔案是唯讀檔案",
+                "error/File already exists":"檔案已經存在",
+                "error/This directory is Read Only.":"此資料夾是唯讀檔案",
                 "":""
             },
             "titles":{