package localserver import ( "bufio" "encoding/json" "fmt" "io" "log" "os" "sync" "time" "github.com/pion/webrtc/v3" ) const ( HOME = "home" ) const ( FS_GET_FOLDER = "fs_get_folder" FS_GET_FOLDER_RESPONSE = "fs_get_folder_response" FS_UPLOAD_FILE = "fs_upload_file" FS_UPLOAD_FILE_INIT = "fs_upload_file_init" FS_UPLOAD_FILE_END = "fs_upload_file_response_end" FS_DOWNLOAD_FILE = "fs_download_file" FS_DOWNLOAD_FILE_RESPONSE = "fs_download_file_response" FS_DOWNLOAD_FILE_RESPONSE_INIT = "fs_download_file_response_init" FS_DOWNLOAD_FILE_RESPONSE_END = "fs_download_file_response_end" FS_CREATE_FOLDER = "fs_create_folder" ) type DataChannelManager interface { HandleMessage(message *DatachannelMessage, channel *webrtc.DataChannel) (done chan struct{}, errChan chan error) } type P2PFSDatachannelManager struct { l *sync.Mutex uploadFile map[string]*os.File } type DatachannelMessage struct { From string `json:"from"` Type string `json:"type"` Payload *DatachannelMessagePayload `json:"payload"` } type DatachannelMessagePayload struct { Path string Content []byte } type FSResultInfo struct { Name string Type string Path string Size int64 ModTime string } func NewP2PFSDatachannelManager() (manager *P2PFSDatachannelManager) { manager = &P2PFSDatachannelManager{&sync.Mutex{}, make(map[string]*os.File)} return } func (pdm *P2PFSDatachannelManager) HandleMessage(message *DatachannelMessage, channel *webrtc.DataChannel) (done chan struct{}, errChan chan error) { done, errChan = make(chan struct{}), make(chan error) go func() { switch message.Type { case FS_GET_FOLDER: logger.Println("try to get folder") done, err := pdm.handleGetFolder(message.Payload.Path, channel) select { case <-done: logger.Println("operation succeed") case e := <-err: errChan <- e return } case FS_DOWNLOAD_FILE: logger.Println("tried to download a file") done, err := pdm.sendFile(message.Payload.Path, channel) select { case <-done: logger.Println("operation succeed") case e := <-err: errChan <- e return } case FS_UPLOAD_FILE_INIT: logger.Println("tried to upload a file") if err := pdm.downloadFileInit(message.Payload.Path); err != nil { errChan <- err return } case FS_UPLOAD_FILE: if err := pdm.downloadFile(message.Payload.Path, message.Payload.Content); err != nil { errChan <- err return } case FS_UPLOAD_FILE_END: if err := pdm.downloadFileEnd(message.Payload.Path); err != nil { errChan <- err return } case FS_CREATE_FOLDER: if err := pdm.createFolder(message.Payload.Path); err != nil { errChan <- err return } default: logger.Printf("got a new message from %s with payload %v\n", message.From, message.Payload) } done <- struct{}{} }() return done, errChan } func (pdm *P2PFSDatachannelManager) handleGetFolder(path string, channel *webrtc.DataChannel) (done chan struct{}, errCh chan error) { done, errCh = make(chan struct{}), make(chan error) go func() { var dirPath string if path == HOME { homeDir, err := os.UserHomeDir() logger.Println(homeDir) if err != nil { errCh <- err return } dirPath = homeDir } else { dirPath = path } dir, err := os.ReadDir(dirPath) if err != nil { errCh <- err return } index := 1 for i, v := range dirPath { if i > 0 { if v == '/' { index = i } } } dirs := []*FSResultInfo{{Name: "..", Type: "directory", Path: dirPath[:index], Size: 0, ModTime: time.Now().Format("January 2, 2006")}} for _, d := range dir { f := &FSResultInfo{} info, err := d.Info() if err != nil { logger.Println(err) continue } f.ModTime = fmt.Sprintf("%s at %s", info.ModTime().Format("January 2, 2006"), info.ModTime().Format("15:04:05")) f.Path = fmt.Sprintf("%s/%s", dirPath, info.Name()) f.Size = info.Size() f.Name = info.Name() if info.IsDir() { f.Type = "directory" } else { f.Type = "file" } dirs = append(dirs, f) } if len(dirs) < 100 { logger.Println("dir smaller than 100") bs, err := json.Marshal(map[string]interface{}{ "type": FS_GET_FOLDER_RESPONSE, "from": "lolo_local_serv", "payload": map[string]interface{}{ "folderPath": dirPath, "dirContent": dirs, }, }) if err != nil { errCh <- err return } if err := channel.SendText(string(bs)); err != nil { errCh <- err return } } else { logger.Println("dir greater than 10000") reste := len(dirs) % 100 x := (len(dirs) - reste) / 100 for j := 0; j < x; j++ { logger.Println("dir sending packet than 100") d := dirs[j*100 : (j+1)*100] logger.Println("length of d :", len(d)) bs, err := json.Marshal(map[string]interface{}{ "type": FS_GET_FOLDER_RESPONSE, "from": "lolo_local_serv", "payload": map[string]interface{}{ "folderPath": dirPath, "dirContent": d, }, }) if err != nil { errCh <- err return } if err := channel.SendText(string(bs)); err != nil { errCh <- err return } } bs, err := json.Marshal(map[string]interface{}{ "type": FS_GET_FOLDER_RESPONSE, "from": "lolo_local_serv", "payload": map[string]interface{}{ "folderPath": dirPath, "dirContent": dirs[len(dirs)-reste:], }, }) if err != nil { errCh <- err return } if err := channel.SendText(string(bs)); err != nil { errCh <- err return } } done <- struct{}{} }() return } func (pdm *P2PFSDatachannelManager) sendFile(path string, channel *webrtc.DataChannel) (<-chan struct{}, <-chan error) { done, errCh := make(chan struct{}), make(chan error) go func() { f, err := os.Open(path) if err != nil { errCh <- err return } bsInit, err := json.Marshal(map[string]interface{}{ "type": FS_DOWNLOAD_FILE_RESPONSE_INIT, "from": "lolo_local_serv", "payload": map[string]string{ "path": path, }, }) if err != nil { errCh <- err return } if err := channel.SendText(string(bsInit)); err != nil { errCh <- err return } r := bufio.NewReader(f) buf := make([]byte, 0, 10000) for { n, err := r.Read(buf[:cap(buf)]) buf = buf[:n] if n == 0 { if err == nil { continue } if err == io.EOF { break } log.Fatal(err) } bs, err := json.Marshal(map[string]interface{}{ "type": FS_DOWNLOAD_FILE_RESPONSE, "from": "lolo_local_serv", "payload": map[string]interface{}{ "path": path, "content": buf, }, }) if err != nil { errCh <- err return } if err := channel.SendText(string(bs)); err != nil { errCh <- err return } } bs, err := json.Marshal(map[string]interface{}{ "type": FS_DOWNLOAD_FILE_RESPONSE_END, "from": "lolo_local_serv", "payload": map[string]string{ "path": path, }, }) if err != nil { errCh <- err return } if err := channel.SendText(string(bs)); err != nil { errCh <- err return } done <- struct{}{} }() return done, errCh } func (pdm *P2PFSDatachannelManager) downloadFileInit(path string) (err error) { logger.Println("upload path name is", path) if _, ok := pdm.uploadFile[path]; ok { err = fmt.Errorf("the file %s is already being uploaded", path) return } file, e := os.Create(path) if e != nil { err = fmt.Errorf("an error occured in download file init : %v", e) return } index := 1 for i, v := range path { if i > 0 { if v == '/' { index = i } } } logger.Println(path) logger.Println(path[index+1:]) pdm.uploadFile[path[index+1:]] = file return } func (pdm *P2PFSDatachannelManager) downloadFile(path string, content []byte) (err error) { if _, ok := pdm.uploadFile[path]; !ok { err = fmt.Errorf("no upload file open for path %s", path) return } _, err = pdm.uploadFile[path].Write(content) return } func (pdm *P2PFSDatachannelManager) downloadFileEnd(path string) (err error) { logger.Println("closing file") if _, ok := pdm.uploadFile[path]; !ok { err = fmt.Errorf("no upload file open for path %s", path) return } err = pdm.uploadFile[path].Close() delete(pdm.uploadFile, path) return } func (pdm *P2PFSDatachannelManager) createFolder(path string) (err error) { err = os.Mkdir(path, 0770) return }