356 lines
8.4 KiB
Go
356 lines
8.4 KiB
Go
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
|
|
}
|