package localserver import ( "context" "encoding/json" "fmt" "io" "io/ioutil" "os" "path" "path/filepath" "strings" "time" "github.com/pion/webrtc/v3" ) const ( CREATE_FOLDER = "create_folder" COPY_FOLDER = "copy_folder" COPY_FILE = "copy_file" CUT_FILE = "cut_file" CUT_FOLDER = "cut_folder" RENAME_FOLDER = "rename_folder" RENAME_FILE = "rename_file" DELETE_FOLDER = "delete_folder" DELETE_FILE = "delete_file" UPDATE_PERMISSIONS = "update_permissions" GET_FS_ENTITIES = "get_fs_entities" ADD_FS_ENTITY_MEMBERS = "add_fs_entity_members" REMOVE_FS_ENTITY_MEMBERS = "remove_fs_entity_members" ZONE_UPLOAD_FILE = "zone_upload_file" ZONE_DOWNLOAD_FILE = "zone_download_file" ) const ( CREATE_FOLDER_DONE = "create_folder_done" CREATE_FILE_DONE = "create_file_done" COPY_FOLDER_DONE = "copy_folder_done" COPY_FILE_DONE = "copy_file_done" CUT_FILE_DONE = "cut_file_done" CUT_FOLDER_DONE = "cut_folder_done" RENAME_FOLDER_DONE = "rename_folder_done" RENAME_FILE_DONE = "rename_file_done" DELETE_FOLDER_DONE = "delete_folder_done" DELETE_FILE_DONE = "delete_file_done" UPDATE_PERMISSIONS_DONE = "update_permissions_done" GET_FS_ENTITIES_DONE = "get_fs_entities_done" ADD_FS_ENTITY_MEMBERS_DONE = "add_fs_entity_members_done" REMOVE_FS_ENTITY_MEMBERS_DONE = "remove_fs_entity_members_done" ZONE_DOWNLOAD_FILE_DONE = "zone_download_file_done" ZONE_UPLOAD_FILE_DONE = "zone_upload_file_done" ) const ( PARENT = "parent" ) // ZoneFileHandler : Handle all interaction of the node zone related to the file system type ZoneFileHandler[T ZippytalFSInstance] struct { ZoneId string HostId string ZoneMembersId []string DataChannels map[string]*DataChannel FileDataChannels map[string]*DataChannel FileDataChannelsFlag *uint32 Flag *uint32 FSInstanceFlag *uint32 Publishers []<-chan *ZoneRequest FSInstance T reqChans []chan<- *ZoneRequest DB *ZoneFilesDBHandler } // FSEntityAccessRights : representation of the different rights users can have on a ZoneFSEntity type FSEntityAccessRights struct { // Read : this field for folder determine if the member is allowed to see this folder only because he is allowed to read a child folder or because he actually is authorized to read this folder (hard to explain) Read bool `json:"read"` // Write : this field for folder determine if the user can upload file in this folder and for file if the user can edit the file (future feature). Write bool `json:"write"` // Download : this field for folder determine if the user can download a zip of the folder (future feature) and for file if he can download the file. Download bool `json:"download"` // Rename : determine the ability of the user to change the name of the fsEntity Rename bool `json:"rename"` } // ZoneFSEntity : representation of a FSEntity (file or folder) for the zone fs feature type ZoneFSEntity struct { Name string `json:"name"` Owner string `json:"owner"` Type string `json:"type"` Folder bool `json:"folder"` ModTime string `json:"modTime"` CreationTime string `json:"creationTime"` Size uint64 `json:"size"` Members map[string]*FSEntityAccessRights `json:"members"` } // NewZoneFileHandler: factory function to create a zone file handler the name of the parameters are explicit, this is the only way to safely create a ZoneFSEntity func NewZoneFileHandler(_ string, zoneId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (zoneFileHandler *ZoneFileHandler[*FSInstance], err error) { db, err := NewZoneFilesDBHandler(zoneId) if err != nil { return } initFsDirectory := func(u string) (err error) { logger.Printf("creating fs directory for user %s...\n", u) baseConfig := ZoneFSEntity{ Name: u, Owner: u, Type: "private", Folder: true, Members: map[string]*FSEntityAccessRights{ u: { Read: true, Write: true, Download: true, Rename: true, }, }, } err = db.AddNewFSEntity("", &baseConfig) return } if _, dirErr := os.ReadDir(filepath.Join(dataPath, "data", "zones", zoneId, "fs")); os.IsNotExist(dirErr) { dirErr := os.MkdirAll(filepath.Join(dataPath, "data", "zones", zoneId, "fs"), 0700) if dirErr != nil { return } } for _, user := range authorizedMembers { if _, dirErr := os.ReadDir(filepath.Join(dataPath, "data", "zones", zoneId, "fs", user)); os.IsNotExist(dirErr) { if e := initFsDirectory(user); e != nil { logger.Println(e) } } } fSInstanceFlag := uint32(0) fSDCFlag := uint32(0) fsInstance := NewFSInstance(zoneId, owner, authorizedMembers) zoneFileHandler = &ZoneFileHandler[*FSInstance]{ ZoneId: zoneId, ZoneMembersId: authorizedMembers, DataChannels: dataChannels, FileDataChannels: make(map[string]*DataChannel), FileDataChannelsFlag: &fSDCFlag, FSInstanceFlag: &fSInstanceFlag, FSInstance: fsInstance, DB: db, Flag: flag, } return } // func (zfh *ZoneFileHandler) sendZoneRequest(reqType string, from string, payload map[string]interface{}) { // go func() { // for _, rc := range zfh.reqChans { // rc <- &ZoneRequest{ // ReqType: reqType, // From: from, // Payload: payload, // } // } // }() // } func (zfh *ZoneFileHandler[T]) Init(ctx context.Context, authorizedMembers []string) (err error) { for _, member := range authorizedMembers { if serr := zfh.SetPublicRootFolders(member); serr != nil { logger.Println(serr) } } return } func (zfh *ZoneFileHandler[T]) sendDataChannelMessage(reqType string, from string, to string, payload map[string]interface{}) (<-chan struct{}, <-chan error) { done, errCh := make(chan struct{}), make(chan error) go func() { if err := atomicallyExecute(zfh.FileDataChannelsFlag, func() (err error) { if _, ok := zfh.FileDataChannels[to]; ok { bs, jsonErr := json.Marshal(&ZoneResponse{ Type: reqType, From: from, To: to, Payload: payload, }) if jsonErr != nil { return jsonErr } <-time.After(20 * time.Millisecond) err = zfh.FileDataChannels[to].DataChannel.SendText(string(bs)) } return }); err != nil { errCh <- err return } done <- struct{}{} }() return done, errCh } func (zfh *ZoneFileHandler[T]) sendZoneRequest(reqType string, from string, payload map[string]interface{}) { go func() { for _, rc := range zfh.reqChans { rc <- &ZoneRequest{ ReqType: reqType, From: from, Payload: payload, } } }() } func (zfh *ZoneFileHandler[T]) Subscribe(ctx context.Context, publisher <-chan *ZoneRequest) (reqChan chan *ZoneRequest, done chan struct{}, errCh chan error) { reqChan, done, errCh = make(chan *ZoneRequest), make(chan struct{}), make(chan error) zfh.reqChans = append(zfh.reqChans, reqChan) go func() { for { select { case <-ctx.Done(): done <- struct{}{} return case req := <-publisher: if err := zfh.handleZoneRequest(ctx, req); err != nil { errCh <- err } } } }() return } // func (zfh *ZoneFileHandler[T]) signalCandidate(from string, to string, candidate *webrtc.ICECandidate) (err error) { // d, e := zfh.sendDataChannelMessage(string(ZONE_FS_WEBRTC_CANDIDATE), from, to, map[string]interface{}{ // "from": from, // "to": to, // "candidate": candidate.ToJSON().Candidate, // "sdpMid": *candidate.ToJSON().SDPMid, // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), // }) // select { // case <-d: // case err = <-e: // } // return // } // GetFSEntities: Return a list of ZoneFSEntity to the user with the provided userId func (zfh *ZoneFileHandler[T]) GetFSEntities(userId string, section string, basePath string, limit int, lastIndex int, backward bool) (err error) { var fsEntities []*ZoneFSEntity var currentPath string = basePath var listFSEntities func(string, string, int, int, bool) listFSEntities = func(userId, path string, limit, lastIndex int, backward bool) { fsEntities, _, err = zfh.DB.ListZoneFSEntity(path, userId, lastIndex, limit) if err != nil { return } currentPath = path if len(fsEntities) == 1 { if rights, ok := fsEntities[0].Members[userId]; ok { if !rights.Read { if !backward { listFSEntities(userId, filepath.Join(path, fsEntities[0].Name), 100, 0, backward) } else { splittedPath := filepath.SplitList(path) if len(splittedPath) > 1 { listFSEntities(userId, filepath.Dir(path), 100, 0, backward) } else { listFSEntities(userId, "", 100, 0, backward) } } } } } } listFSEntities(userId, basePath, 100, 0, backward) var parent *ZoneFSEntity if len(filepath.SplitList(basePath)) > 0 { parent, err = zfh.DB.GetFSEntity(filepath.Dir(basePath), filepath.Base(basePath)) if err != nil { return } logger.Println("------------------ parent is --------------") logger.Println(parent) } done, errCh := zfh.sendDataChannelMessage(GET_FS_ENTITIES, "node", userId, map[string]interface{}{ "fsEntities": fsEntities, "parent": parent, "currentPath": currentPath, "section": section, }) select { case <-done: fmt.Println("done fs entity get") case err = <-errCh: } return } // CreateFolder: create a ZoneFSEntity folder mapped to a real folder on the disk func (zfh *ZoneFileHandler[T]) CreateFolder(path string, config *ZoneFSEntity) (err error) { if config.Type == PARENT { parentPath := filepath.Dir(path) parentFolderName := filepath.Base(path) logger.Println(parentPath, "-------", parentFolderName) var folder *ZoneFSEntity if len(parentPath) > 1 { folder, err = zfh.DB.GetFSEntity(parentPath, parentFolderName) } else { folder, err = zfh.DB.GetFSEntity("", parentFolderName) } if err != nil { return } config.Members = make(map[string]*FSEntityAccessRights) for k, v := range folder.Members { config.Members[k] = &FSEntityAccessRights{ Read: v.Read, Write: v.Write, Download: v.Download, Rename: v.Rename, } } } if err = zfh.DB.AddNewFSEntity(path, config); err != nil { return } for member := range config.Members { done, errCh := zfh.sendDataChannelMessage(CREATE_FOLDER_DONE, "node", member, map[string]interface{}{ "path": path, "fsEntity": config, }) select { case <-done: case err = <-errCh: logger.Println(err) } } return } // CreateFile: create a ZoneFSEntity file mapped to a real file on the disk func (zfh *ZoneFileHandler[T]) CreateFile(path, fileName string, config *ZoneFSEntity) (err error) { splittedPath := strings.Split(path, "/") if config.Type == PARENT { logger.Println(strings.Join(splittedPath[:len(splittedPath)-1], "/"), "-------", splittedPath[len(splittedPath)-1]) var folder *ZoneFSEntity if len(splittedPath) > 1 { folder, err = zfh.DB.GetFSEntity(strings.Join(splittedPath[:len(splittedPath)-1], "/"), splittedPath[len(splittedPath)-1]) } else { folder, err = zfh.DB.GetFSEntity("", splittedPath[0]) } if err != nil { return } config.Members = make(map[string]*FSEntityAccessRights) for k, v := range folder.Members { config.Members[k] = &FSEntityAccessRights{ Read: v.Read, Write: v.Write, Download: v.Download, Rename: v.Rename, } } } if err = zfh.DB.AddNewFSEntity(path, config); err != nil { return } for member := range config.Members { done, errCh := zfh.sendDataChannelMessage(CREATE_FILE_DONE, "node", member, map[string]interface{}{ "path": path, "fsEntity": config, }) select { case <-done: case err = <-errCh: logger.Println(err) } } return } // EditFolderType: change the type of the folder and modify rights of the users according to the new ZoneFSEntity type provided func (zfh *ZoneFileHandler[T]) EditFolderType(userId string, path string, folderName string, newFolderType string) (err error) { folder, err := zfh.DB.GetFSEntity(path, folderName) if err != nil { return } folder.Type = newFolderType if err = zfh.DB.SetFSEntity(path, folderName, folder); err != nil { return } done, errCh := zfh.sendDataChannelMessage("", "node", userId, map[string]interface{}{ "newFolder": folder, }) select { case <-done: case err = <-errCh: } return } func (zfh *ZoneFileHandler[T]) EditFileType(path string, fileName string, newFileType string) (err error) { return } // AddFolderMember: add a list of members to the map of users allowed to access the folder with the provided folderName and set the rights func (zfh *ZoneFileHandler[T]) AddFolderMember(userId string, path string, folderName string, members []interface{}, read bool, write bool, download bool, rename bool) (err error) { var addMembersDb func(string, string, bool, bool, bool, bool, bool) (folder *ZoneFSEntity, err error) addMembersDb = func(path string, folderName string, read bool, write bool, download bool, rename bool, init bool) (folder *ZoneFSEntity, err error) { folder, err = zfh.DB.GetFSEntity(path, folderName) if err != nil { return } childrens := make([]*ZoneFSEntity, 0) if init { childrens, _, err = zfh.DB.ListZoneFSEntity(filepath.Join(path, folderName), userId, 0, 100) if err != nil { logger.Println("cannot get siblings folders:", err) return } } for _, newMember := range members { if m, ok := newMember.(string); ok { if _, ok := folder.Members[m]; !ok { folder.Members[m] = &FSEntityAccessRights{ Read: read, Write: write, Download: download, Rename: rename, } } if init { go func() { for _, children := range childrens { logger.Println("------:", filepath.Join(path, folderName), "-", children.Name) if children.Type != "private" { if _, err = addMembersDb(filepath.Join(path, folderName), children.Name, read, write, download, rename, true); err != nil { logger.Println("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*") logger.Println("error from ttttthheeere:", err) logger.Println("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*") } } } }() } } } err = zfh.DB.SetFSEntity(path, folderName, folder) return } folder, err := addMembersDb(path, folderName, read, write, download, rename, true) if err != nil { return } splittedPath := strings.Split(path, "/") for i := 1; i <= len(splittedPath); i++ { logger.Println(i) var addErr error if i == len(splittedPath) { _, addErr = addMembersDb("", splittedPath[0], false, false, false, false, false) } else { _, addErr = addMembersDb(strings.Join(splittedPath[:len(splittedPath)-i], "/"), splittedPath[len(splittedPath)-i], false, false, false, false, false) } if addErr != nil { logger.Println(addErr) break } } for member := range folder.Members { done, errCh := zfh.sendDataChannelMessage(ADD_FS_ENTITY_MEMBERS_DONE, "node", member, map[string]interface{}{ "path": path, }) select { case <-done: case sendErr := <-errCh: logger.Println(sendErr) } } for _,member := range members { go func(m string) { bs, err := json.Marshal(map[string]any{ "zoneId": zfh.ZoneId, "folderName": folderName, "folderPath": path, "zoneHost": NodeID, }) if err != nil { return } zfh.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{ "type": "added_in_folder", "title": "New folder shared 📁", "body": fmt.Sprintf("%s granted you access to the folder %s", userId, folderName), "isPushed": true, "payload": string(bs), "recipients": []string{m}, }) }(member.(string)) } return } func (zfh *ZoneFileHandler[T]) AddFileMember(path string, filename string, members []interface{}, read bool, write bool, download bool, rename bool) (err error) { return } func (zfh *ZoneFileHandler[T]) RemoveFolderMember(userId string, path string, folderName string, members []interface{}) (err error) { membersM := make(map[string]bool) staticMembersM := make(map[string]bool) for _, member := range members { if m, ok := member.(string); ok { membersM[m] = true staticMembersM[m] = true } } var removeChildMemberDb func(string, string) (folder *ZoneFSEntity, err error) removeChildMemberDb = func(path, folderName string) (folder *ZoneFSEntity, err error) { childrens, _, err := zfh.DB.ListZoneFSEntity(filepath.Join(path, folderName), userId, 0, 100) if err != nil { logger.Println("cannot get siblings folders:", err) return } go func() { for _, children := range childrens { for member := range staticMembersM { if _, ok := children.Members[member]; ok && member != children.Owner { delete(children.Members, member) } } logger.Println("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*") logger.Println(filepath.Join(path, folderName), ":", children.Name) logger.Println("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*") _, _ = removeChildMemberDb(filepath.Join(path, folderName), children.Name) err = zfh.DB.SetFSEntity(filepath.Join(path, folderName), children.Name, children) } }() return } removeMemberDb := func(path, folderName string, init bool) (folder *ZoneFSEntity, err error) { folder, err = zfh.DB.GetFSEntity(path, folderName) if err != nil { return } siblings, _, err := zfh.DB.ListZoneFSEntity(path, userId, 0, 100) if err != nil { logger.Println("cannot get siblings folders:", err) return } if init { _, _ = removeChildMemberDb(path, folderName) } for member := range membersM { if _, ok := folder.Members[member]; ok && member != folder.Owner { if !folder.Members[member].Read || init { delete(folder.Members, member) } else { delete(membersM, member) } for _, sibling := range siblings { if sibling.Name != folder.Name { if _, ok := sibling.Members[member]; ok { delete(membersM, member) } } } } } err = zfh.DB.SetFSEntity(path, folderName, folder) return } folder, err := removeMemberDb(path, folderName, true) if err != nil { return } splittedPath := strings.Split(path, "/") for i := 1; i <= len(splittedPath) && len(membersM) > 0; i++ { logger.Println(i) var addErr error if i == len(splittedPath) { _, addErr = removeMemberDb("", splittedPath[0], false) } else { _, addErr = removeMemberDb(strings.Join(splittedPath[:len(splittedPath)-i], "/"), splittedPath[len(splittedPath)-i], false) } if addErr != nil { logger.Println(addErr) break } } for member := range folder.Members { done, errCh := zfh.sendDataChannelMessage(REMOVE_FS_ENTITY_MEMBERS_DONE, "node", member, map[string]interface{}{ "path": filepath.Join(path, folderName), }) select { case <-done: case sendErr := <-errCh: logger.Println(sendErr) } } for _, member := range members { if _, ok := member.(string); ok { go func(m string) { fmt.Println("sending unshare notification",m) bs, err := json.Marshal(map[string]any{ "zoneId": zfh.ZoneId, "folderName": folderName, "zoneHost": NodeID, }) if err != nil { fmt.Println("error there",err) return } zfh.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{ "type": "removed_from_folder", "title": "Folder unshared ⛔️", "body": fmt.Sprintf("%s stopped your access to the folder %s", userId, folderName), "isPushed": true, "payload": string(bs), "recipients": []string{m}, }) }(member.(string)) done, errCh := zfh.sendDataChannelMessage(REMOVE_FS_ENTITY_MEMBERS_DONE, "node", member.(string), map[string]interface{}{ "path": filepath.Join(path, folderName), }) select { case <-done: case sendErr := <-errCh: logger.Println(sendErr) } } } return } func (zfh *ZoneFileHandler[T]) RemoveFileMember(path, filename, members, read, write, download, rename bool) (err error) { return } func (zfh *ZoneFileHandler[T]) ClearUserFolders(userId string) (err error) { fsEntities,_,err := zfh.DB.ListZoneFSEntity("",userId,0,100) if err != nil { return } for _, fsEntity := range fsEntities { go func(f *ZoneFSEntity) { if e := zfh.RemoveFolderMember(f.Owner,"",f.Name,[]any{userId}); e != nil { logger.Println(e) } }(fsEntity) } return } func (zfh *ZoneFileHandler[T]) RenameFolder(path, currentName, newName string) (err error) { fsEntity, err := zfh.DB.GetFSEntity(path, currentName) if err != nil { return } fsEntity.Name = newName if err = os.Rename(filepath.Join(filepath.Join(dataPath, "data", "zones", zfh.ZoneId, "fs"), path, currentName), filepath.Join(filepath.Join(dataPath, "data", "zones", zfh.ZoneId, "fs"), path, newName)); err != nil { return } if err = zfh.DB.SetFSEntity(path, currentName, fsEntity); err != nil { return } for member := range fsEntity.Members { done, errCh := zfh.sendDataChannelMessage(RENAME_FOLDER_DONE, "node", member, map[string]interface{}{ "path": filepath.Join(path, newName), "folderId": newName, }) select { case <-done: case sendErr := <-errCh: logger.Println(sendErr) } } return } func (zfh *ZoneFileHandler[T]) RenameFile(path, currentName, newName string) (err error) { fsEntity, err := zfh.DB.GetFSEntity(path, currentName) if err != nil { return } fsEntity.Name = newName if err = os.Rename(filepath.Join(filepath.Join(dataPath, "data", "zones", zfh.ZoneId, "fs"), path, "__files__", currentName), filepath.Join(filepath.Join(dataPath, "data", "zones", zfh.ZoneId, "fs"), path, "__files__", newName)); err != nil { return } if err = zfh.DB.SetFSEntity(path, currentName, fsEntity); err != nil { return } for member := range fsEntity.Members { done, errCh := zfh.sendDataChannelMessage(RENAME_FILE_DONE, "node", member, map[string]interface{}{ "path": filepath.Join(path, newName), "folderId": newName, }) select { case <-done: case sendErr := <-errCh: logger.Println(sendErr) } } return } func (zfh *ZoneFileHandler[T]) UpdatePermissions(path, name, userId string, newPermission *FSEntityAccessRights) (err error) { var updatePermission func(string, string) error updatePermission = func(path, name string) (err error) { fsEntity, err := zfh.DB.GetFSEntity(path, name) if err != nil { return } if fsEntity.Folder { splittedPath := append(filepath.SplitList(path), name) childrens, _, err := zfh.DB.ListZoneFSEntity(filepath.Join(splittedPath...), userId, 0, 100) if err != nil { return err } go func() { for _, children := range childrens { if children.Type == PARENT { if err = updatePermission(filepath.Join(splittedPath...), children.Name); err != nil { return } } } }() } fsEntity.Members[userId] = newPermission if err = zfh.DB.SetFSEntity(path, name, fsEntity); err != nil { return } for member := range fsEntity.Members { done, errCh := zfh.sendDataChannelMessage(UPDATE_PERMISSIONS_DONE, "node", member, map[string]interface{}{ "path": filepath.Join(path, name), }) select { case <-done: case sendErr := <-errCh: logger.Println(sendErr) } } return } err = updatePermission(path, name) return } func (zfh *ZoneFileHandler[T]) DeleteFolder(path, name string) (err error) { fsEntity, err := zfh.DB.GetFSEntity(path, name) if err != nil { return } if err = zfh.DB.DeleteFolder(path, name); err != nil { return } for member := range fsEntity.Members { done, errCh := zfh.sendDataChannelMessage(DELETE_FOLDER_DONE, "node", member, map[string]interface{}{ "path": filepath.Join(path, name), "folderId": name, }) select { case <-done: case sendErr := <-errCh: logger.Println(sendErr) } } return } func (zfh *ZoneFileHandler[T]) DeleteFile(path, name string) (err error) { fsEntity, err := zfh.DB.GetFSEntity(path, name) if err != nil { return } if err = zfh.DB.DeleteFile(path, name); err != nil { return } for member := range fsEntity.Members { done, errCh := zfh.sendDataChannelMessage(DELETE_FOLDER_DONE, "node", member, map[string]interface{}{ "path": filepath.Join(path, name), "folderId": name, }) select { case <-done: case sendErr := <-errCh: logger.Println(sendErr) } } return } func (zfh *ZoneFileHandler[T]) CopyFile(src, dst string, init bool, fsEntity *ZoneFSEntity) (err error) { var srcfd *os.File var dstfd *os.File var srcinfo os.FileInfo var srcPath string var dstPath string if init { srcPath = filepath.Join(dataPath, "data", "zones", zfh.ZoneId, "fs", filepath.Dir(src), "__files__", filepath.Base(src)) dstPath = filepath.Join(dataPath, "data", "zones", zfh.ZoneId, "fs", filepath.Dir(dst), "__files__", filepath.Base(dst)) } else { srcPath = filepath.Join(dataPath, "data", "zones", zfh.ZoneId, "fs", src) dstPath = filepath.Join(dataPath, "data", "zones", zfh.ZoneId, "fs", dst) } if err = os.MkdirAll(filepath.Dir(dstPath), 0700); err != nil && !os.IsExist(err) { return } if srcfd, err = os.Open(srcPath); err != nil { return } defer srcfd.Close() if dstfd, err = os.Create(dstPath); err != nil { return } defer dstfd.Close() if _, err = io.Copy(dstfd, srcfd); err != nil { return } if srcinfo, err = os.Stat(srcPath); err != nil { return } if err = os.Chmod(dstPath, srcinfo.Mode()); err != nil { return } if init { err = zfh.DB.AddNewFSEntity(filepath.Dir(dst), fsEntity) for member := range fsEntity.Members { done, errCh := zfh.sendDataChannelMessage(COPY_FILE_DONE, "node", member, map[string]interface{}{ "path": dst, }) select { case <-done: case sendErr := <-errCh: logger.Println(sendErr) } } } return } func (zfh *ZoneFileHandler[T]) CopyDir(src, dst string, init bool, fsEntity *ZoneFSEntity) (err error) { var fds []os.FileInfo var srcinfo os.FileInfo srcPath := filepath.Join(dataPath, "data", "zones", zfh.ZoneId, "fs", src) dstPath := filepath.Join(dataPath, "data", "zones", zfh.ZoneId, "fs", dst) if srcinfo, err = os.Stat(srcPath); err != nil { return } if init { logger.Println("---------------------adding new fs entity---------------------") if err = zfh.DB.AddNewFSEntity(filepath.Dir(dst), fsEntity); err != nil { return } } else { if err = os.MkdirAll(dstPath, srcinfo.Mode()); err != nil { return } } if fds, err = ioutil.ReadDir(srcPath); err != nil { return } for _, fd := range fds { srcfp := path.Join(src, fd.Name()) dstfp := path.Join(dst, fd.Name()) if fd.IsDir() { if err = zfh.CopyDir(srcfp, dstfp, false, nil); err != nil { logger.Println(err) } } else { if err = zfh.CopyFile(srcfp, dstfp, false, nil); err != nil { logger.Println(err) } } } if fsEntity != nil { for member := range fsEntity.Members { done, errCh := zfh.sendDataChannelMessage(COPY_FOLDER_DONE, "node", member, map[string]interface{}{ "path": dst, }) select { case <-done: case sendErr := <-errCh: logger.Println(sendErr) } } } return } func (zfh *ZoneFileHandler[T]) CutFolder(id, path, dstPath string, init bool, fsEntity *ZoneFSEntity) (err error) { if err = zfh.CopyDir(path, dstPath, init, fsEntity); err != nil { return } err = zfh.DeleteFolder(filepath.Dir(path), filepath.Base(path)) return } func (zfh *ZoneFileHandler[T]) CutFile(id, path, dstPath string, init bool, fsEntity *ZoneFSEntity) (err error) { if err = zfh.CopyFile(path, dstPath, init, fsEntity); err != nil { return } err = zfh.DeleteFile(filepath.Dir(path), filepath.Base(path)) return } // func (zfh *ZoneFileHandler[T]) ConnectToFSInstance(channelId string, userId string, sdp string) (err error) { // err = atomicallyExecute(zfh.FSInstanceFlag, func() (err error) { // d, e := zfh.FSInstance.HandleOffer(context.Background(), channelId, userId, sdp, zfh.HostId, zfh.sendDataChannelMessage, zfh.signalCandidate) // select { // case <-d: // case err = <-e: // } // return // }) // return // } // func (zfh *ZoneFileHandler[T]) LeaveFSInstance( // userId string) (err error) { // err = atomicallyExecute(zfh.FSInstanceFlag, func() (err error) { // zfh.FSInstance.HandleLeavingMember(userId) // return // }) // return // } func (zfh *ZoneFileHandler[T]) SetPublicRootFolders(user string) (err error) { return } func (zfh *ZoneFileHandler[T]) fileUpload(chatId string, filename string, userId string, dc *DataChannel) { var writePipe chan<- []byte var done bool uploadDone := func() { if writePipe != nil { close(writePipe) writePipe = nil } done = true } dc.DataChannel.OnError(func(err error) { logger.Println(err) logger.Println("abort...") if !done { uploadDone() } if fufErr := zfh.FSInstance.FileUploadFailed(chatId, filename, userId); fufErr != nil { logger.Println(fufErr) } }) dc.DataChannel.OnClose(func() { if done { logger.Println("closing gracefully...") } else { logger.Println("abort...") uploadDone() if fufErr := zfh.FSInstance.FileUploadFailed(chatId, filename, userId); fufErr != nil { logger.Println(fufErr) } } }) dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) { if msg.IsString { if string(msg.Data) == "init_upload" { logger.Println("init upload....") var initErr error if writePipe, initErr = zfh.FSInstance.SetupFileUpload(chatId, filename, userId, dc.DataChannel); initErr != nil { _ = dc.DataChannel.SendText("abort") _ = dc.DataChannel.Close() return } logger.Println("upload ready !") _ = dc.DataChannel.SendText("upload_ready") } else if string(msg.Data) == "upload_done" { uploadDone() } } else { writePipe <- msg.Data } }) dc.DataChannel.OnOpen(func() { _ = dc.DataChannel.SendText("channel_ready") }) } func (zfh *ZoneFileHandler[T]) fileDownload(chatId string, filename string, userId string, dc *DataChannel) { var done bool dc.DataChannel.OnError(func(err error) { if !done { logger.Println("abort...") if fdf := zfh.FSInstance.FileDownloadFailed(chatId, filename, userId); fdf != nil { logger.Println(fdf) } } }) dc.DataChannel.OnClose(func() { if !done { logger.Println("abort...") if fdf := zfh.FSInstance.FileDownloadFailed(chatId, filename, userId); fdf != nil { logger.Println(fdf) } } }) dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) { if msg.IsString { if string(msg.Data) == "init_download" { logger.Println("init download....") var initErr error if initErr = zfh.FSInstance.SetupFileDownload(chatId, filename, userId, dc.DataChannel); initErr != nil { _ = dc.DataChannel.SendText("abort") _ = dc.DataChannel.Close() return } logger.Println("download started !") } else if string(msg.Data) == "download_done" { done = true } } }) dc.DataChannel.OnOpen(func() { _ = dc.DataChannel.SendText("channel_ready") }) } func (zfh *ZoneFileHandler[T]) handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool) { var label string _ = atomicallyExecute(dc.l, func() (err error) { label = dc.DataChannel.Label() return }) if strings.Contains(label, "file_upload") { command := strings.Split(label, "|") if len(command) == 4 { catched = true go zfh.fileUpload(command[1], command[2], command[3], dc) } logger.Println(command) } else if strings.Contains(label, "file_download") { command := strings.Split(label, "|") catched = true go zfh.fileDownload(command[1], command[2], command[3], dc) logger.Println(command) } else if strings.Contains(label, "file_data") { command := strings.Split(label, "|") catched = true _ = atomicallyExecute(zfh.FileDataChannelsFlag, func() (err error) { zfh.FileDataChannels[command[1]] = dc return }) dc.DataChannel.OnClose(func() { fmt.Println("closing gratefully fs dc...") _ = atomicallyExecute(zfh.FileDataChannelsFlag, func() (err error) { delete(zfh.FileDataChannels, command[1]) return }) }) dc.DataChannel.OnError(func(err error) { fmt.Println("error in fs dc...") _ = atomicallyExecute(zfh.FileDataChannelsFlag, func() (err error) { delete(zfh.FileDataChannels, command[1]) return }) }) } return } func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneRequest) (err error) { switch req.ReqType { case string(REMOVED_ZONE_AUTHORIZED_MEMBER): if err = verifyFieldsString(req.Payload, "userId"); err != nil { return } var index int var found bool for i, m := range zfh.ZoneMembersId { if m == req.Payload["userId"].(string) { index = i break } } if !found { err = fmt.Errorf("no such user in zone") return } zfh.ZoneMembersId = append(zfh.ZoneMembersId[:index], zfh.ZoneMembersId[index+1:]...) err = zfh.DeleteFolder("", req.Payload["userId"].(string)) case string(NEW_AUTHORIZED_ZONE_MEMBER): if err = verifyFieldsString(req.Payload, "userId"); err != nil { return } var contain bool for _, m := range zfh.ZoneMembersId { if m == req.Payload["userId"].(string) { contain = true break } } if !contain { zfh.ZoneMembersId = append(zfh.ZoneMembersId, req.Payload["userId"].(string)) } err = zfh.DB.AddNewFSEntity("", &ZoneFSEntity{ Name: req.Payload["userId"].(string), Owner: req.Payload["userId"].(string), Type: PRIVATE, Folder: true, ModTime: time.Now().Format(time.UnixDate), CreationTime: time.Now().Format(time.UnixDate), Size: 0, Members: map[string]*FSEntityAccessRights{ req.Payload["userId"].(string): { Read: true, Write: true, Download: true, Rename: true, }, }, }) case LEAVE_ZONE: logger.Println("*-----------------handling leaving zone---------------*") if err = verifyFieldsString(req.Payload, "userId"); err != nil { return } //err = zfh.LeaveFSInstance(req.Payload["userId"].(string)) case ZONE_UPLOAD_FILE_DONE: if err = verifyFieldsBool(req.Payload, "failed"); err != nil { return } if err = verifyFieldsString(req.Payload, "path", "userId", "fileName", "type"); err != nil { return } if err = verifyFieldsFloat64(req.Payload, "size"); err != nil { return } if req.Payload["failed"].(bool) { logger.Println(zfh.FSInstance.FileUploadFailed(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string))) } else { defer func() { //logger.Println(zfh.FSInstance.FileUploadDone(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string))) }() err = zfh.CreateFile(req.Payload["path"].(string), req.Payload["fileName"].(string), &ZoneFSEntity{ Name: req.Payload["fileName"].(string), Owner: req.Payload["userId"].(string), Type: req.Payload["type"].(string), Size: uint64(req.Payload["size"].(float64)), ModTime: time.Now().Format(time.UnixDate), CreationTime: time.Now().Format(time.UnixDate), Folder: false, Members: map[string]*FSEntityAccessRights{ req.Payload["userId"].(string): { Read: true, Write: true, Download: true, Rename: true, }, }, }) } // case ZONE_UPLOAD_FILE: // if err = verifyFieldsString(req.Payload, "path", "userId", "fileName"); err != nil { // return // } // err = zfh.FSInstance.SetupFileUpload(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string)) // case ZONE_DOWNLOAD_FILE: // if err = verifyFieldsString(req.Payload, "path", "userId", "fileName"); err != nil { // return // } // err = zfh.FSInstance.SetupFileDownload(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string)) case GET_FS_ENTITIES: logger.Println("getting files entities") if err = verifyFieldsString(req.Payload, "path", "userId", "section"); err != nil { return } var backward bool if err = verifyFieldsBool(req.Payload, "backward"); err == nil { backward = req.Payload["backward"].(bool) } if err = verifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { return } err = zfh.GetFSEntities(req.Payload["userId"].(string), req.Payload["section"].(string), req.Payload["path"].(string), 0, 100, backward) case CREATE_FOLDER: logger.Println("creating folder") if err = verifyFieldsString(req.Payload, "path"); err != nil { return } if _, ok := req.Payload["config"]; !ok { err = fmt.Errorf("no field config in request payload") return } bs, jsonErr := json.Marshal(req.Payload["config"]) if jsonErr != nil { return jsonErr } var config ZoneFSEntity if err = json.Unmarshal(bs, &config); err != nil { err = fmt.Errorf("the config payload dont match ZoneFSEntity struct pattern : %v", err) return } err = zfh.CreateFolder(req.Payload["path"].(string), &config) case RENAME_FOLDER: if err = verifyFieldsString(req.Payload, "path", "name", "newName"); err != nil { return } err = zfh.RenameFolder(req.Payload["path"].(string), req.Payload["name"].(string), req.Payload["newName"].(string)) case RENAME_FILE: if err = verifyFieldsString(req.Payload, "path", "name", "newName"); err != nil { return } err = zfh.RenameFile(req.Payload["path"].(string), req.Payload["name"].(string), req.Payload["newName"].(string)) case COPY_FOLDER: if err = verifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { return } if _, ok := req.Payload["config"]; !ok { err = fmt.Errorf("no field config in request payload") return } bs, jsonErr := json.Marshal(req.Payload["config"]) if jsonErr != nil { return jsonErr } var config ZoneFSEntity if err = json.Unmarshal(bs, &config); err != nil { err = fmt.Errorf("the config payload dont match ZoneFSEntity struct pattern : %v", err) return } go func() { err = zfh.CopyDir(req.Payload["path"].(string), req.Payload["dstPath"].(string), true, &config) logger.Println(err) }() case COPY_FILE: if err = verifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { return } if _, ok := req.Payload["config"]; !ok { err = fmt.Errorf("no field config in request payload") return } bs, jsonErr := json.Marshal(req.Payload["config"]) if jsonErr != nil { return jsonErr } var config ZoneFSEntity if err = json.Unmarshal(bs, &config); err != nil { err = fmt.Errorf("the config payload dont match ZoneFSEntity struct pattern : %v", err) return } go func() { err = zfh.CopyFile(req.Payload["path"].(string), req.Payload["dstPath"].(string), true, &config) logger.Println(err) }() case CUT_FOLDER: if err = verifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { return } if _, ok := req.Payload["config"]; !ok { err = fmt.Errorf("no field config in request payload") return } bs, jsonErr := json.Marshal(req.Payload["config"]) if jsonErr != nil { return jsonErr } var config ZoneFSEntity if err = json.Unmarshal(bs, &config); err != nil { err = fmt.Errorf("the config payload dont match ZoneFSEntity struct pattern : %v", err) return } go func() { err = zfh.CutFolder(req.Payload["userId"].(string), req.Payload["path"].(string), req.Payload["dstPath"].(string), true, &config) logger.Println(err) }() case CUT_FILE: if err = verifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { return } if _, ok := req.Payload["config"]; !ok { err = fmt.Errorf("no field config in request payload") return } bs, jsonErr := json.Marshal(req.Payload["config"]) if jsonErr != nil { return jsonErr } var config ZoneFSEntity if err = json.Unmarshal(bs, &config); err != nil { err = fmt.Errorf("the config payload dont match ZoneFSEntity struct pattern : %v", err) return } go func() { err = zfh.CutFile(req.Payload["userId"].(string), req.Payload["path"].(string), req.Payload["dstPath"].(string), true, &config) logger.Println(err) }() case UPDATE_PERMISSIONS: if err = verifyFieldsString(req.Payload, "path", "name", "userId"); err != nil { return } if err = verifyFieldsBool(req.Payload, "read", "write", "download", "rename"); err != nil { return } fsAcessRights := &FSEntityAccessRights{ Read: req.Payload["read"].(bool), Write: req.Payload["write"].(bool), Download: req.Payload["download"].(bool), Rename: req.Payload["rename"].(bool), } err = zfh.UpdatePermissions(req.Payload["path"].(string), req.Payload["name"].(string), req.Payload["userId"].(string), fsAcessRights) case DELETE_FOLDER: if err = verifyFieldsString(req.Payload, "path", "name"); err != nil { return } err = zfh.DeleteFolder(req.Payload["path"].(string), req.Payload["name"].(string)) case DELETE_FILE: if err = verifyFieldsString(req.Payload, "path", "name"); err != nil { return } err = zfh.DeleteFile(req.Payload["path"].(string), req.Payload["name"].(string)) case ADD_FS_ENTITY_MEMBERS: if err = verifyFieldsString(req.Payload, "path", "userId", "folderName"); err != nil { return } if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } if err = verifyFieldsBool(req.Payload, "read", "write", "download", "rename"); err != nil { return } err = zfh.AddFolderMember(req.Payload["userId"].(string), req.Payload["path"].(string), req.Payload["folderName"].(string), req.Payload["members"].([]interface{}), req.Payload["read"].(bool), req.Payload["write"].(bool), req.Payload["download"].(bool), req.Payload["rename"].(bool)) case REMOVE_FS_ENTITY_MEMBERS: if err = verifyFieldsString(req.Payload, "path", "userId", "folderName"); err != nil { return } if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } err = zfh.RemoveFolderMember(req.Payload["userId"].(string), req.Payload["path"].(string), req.Payload["folderName"].(string), req.Payload["members"].([]interface{})) // case string(ZONE_FS_WEBRTC_OFFER): // if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { // return // } // err = zfh.ConnectToFSInstance(req.Payload["channelId"].(string), req.Payload["userId"].(string), req.Payload["sdp"].(string)) // case string(ZONE_FS_WEBRTC_COUNTER_OFFER): // logger.Println("handling fs instance counter offer") // if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { // return // } // err = atomicallyExecute(zfh.FSInstanceFlag, func() (err error) { // err = zfh.FSInstance.HandleCounterOffer(context.Background(), req.Payload["userId"].(string), zfh.sendDataChannelMessage) // return // }) // case string(ZONE_FS_WEBRTC_RENNEGOTIATION_OFFER): // if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { // return // } // err = atomicallyExecute(zfh.FSInstanceFlag, func() (err error) { // err = zfh.FSInstance.HandleRennegotiationOffer(req.Payload["userId"].(string), req.Payload["sdp"].(string), zfh.sendDataChannelMessage) // return // }) // case string(ZONE_FS_WEBRTC_RENNEGOTIATION_ANSWER): // if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { // return // } // err = atomicallyExecute(zfh.FSInstanceFlag, func() (err error) { // err = zfh.FSInstance.HandleRennegotiationAnswer(req.Payload["userId"].(string), req.Payload["sdp"].(string)) // return // }) // case string(ZONE_FS_WEBRTC_CANDIDATE): // logger.Println("handling fs instance webrtc candidate") // logger.Println(req.Payload) // if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMLineIndex", "sdpMid", "channelId", "userId"); err != nil { // return // } // logger.Println(req.Payload) // i, convErr := strconv.Atoi(req.Payload["sdpMLineIndex"].(string)) // if convErr != nil { // return convErr // } // SDPMLineIndex := uint16(i) // sdpMid := req.Payload["sdpMid"].(string) // logger.Println(sdpMid, SDPMLineIndex) // err = atomicallyExecute(zfh.FSInstanceFlag, func() (err error) { // err = zfh.FSInstance.AddCandidate(&webrtc.ICECandidateInit{ // Candidate: req.Payload["candidate"].(string), // SDPMid: &sdpMid, // SDPMLineIndex: &SDPMLineIndex, // }, req.Payload["userId"].(string)) // return // }) default: logger.Println("got your request in zone file handler", req) return } return }