Zippytal-Node/zoneFSHandler.go

1401 lines
44 KiB
Go

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
}