package localserver import ( "bufio" "fmt" "io" "os" "path/filepath" "github.com/pion/webrtc/v3" ) const ( ZONE_FS_WEBRTC_OFFER ReqType = "zone_fs_offer" ZONE_FS_WEBRTC_ANSWER ReqType = "zone_fs_answer" ZONE_FS_WEBRTC_RENNEGOTIATION_OFFER ReqType = "zone_fs_rennegotiation_offer" ZONE_FS_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "zone_fs_rennegotiation_answer" ZONE_FS_WEBRTC_COUNTER_OFFER ReqType = "zone_fs_webrtc_counter_offer" ZONE_FS_WEBRTC_CANDIDATE ReqType = "zone_fs_webrtc_candidate" ) type FSInstance struct { ZoneID string `json:"id"` Owner string `json:"owner"` Members []string `json:"members"` OpenFiles map[string]*os.File `json:"-"` OpenFilesForUser map[string][]string `json:"-"` // localSD map[string]*webrtc.SessionDescription `json:"-"` // rtcPeerConnections map[string]*ZoneRTCPeerConnection `json:"-"` // zoneFSDataChannels map[string]map[string]*DataChannel `json:"-"` // pendingCandidates map[string][]*webrtc.ICECandidate `json:"-"` // middlewares []interface{} `json:"-"` // candidateFlag *uint32 `json:"-"` filesFlag *uint32 `json:"-"` // rtcPeerConnectionMapFlag *uint32 `json:"-"` // dataChannelMapFlag *uint32 `json:"-"` // localSDMapFlag *uint32 `json:"-"` } type FSInstanceOnICECandidateFunc = func(string, string, *webrtc.ICECandidate) error func NewFSInstance(id, owner string, members []string) (audioChannel *FSInstance) { // candidateFlag := uint32(0) // rtcPeerConnectionMapFlag := uint32(0) // dataChannelMapFlag := uint32(0) filesFlag := uint32(0) // localSDMapFlag := uint32(0) audioChannel = &FSInstance{ ZoneID: id, Owner: owner, Members: members, OpenFiles: make(map[string]*os.File), OpenFilesForUser: make(map[string][]string), // localSD: make(map[string]*webrtc.SessionDescription), // rtcPeerConnections: make(map[string]*ZoneRTCPeerConnection), // zoneFSDataChannels: make(map[string]map[string]*DataChannel), // pendingCandidates: make(map[string][]*webrtc.ICECandidate), // middlewares: make([]interface{}, 0), // candidateFlag: &candidateFlag, filesFlag: &filesFlag, // rtcPeerConnectionMapFlag: &rtcPeerConnectionMapFlag, // dataChannelMapFlag: &dataChannelMapFlag, // localSDMapFlag: &localSDMapFlag, } return } func (fs *FSInstance) SetupFileUpload(path, filename, userId string, dc *webrtc.DataChannel) (writePipe chan []byte, err error) { concretePath := filepath.Join(dataPath, "data", "zones", fs.ZoneID, "fs", path, "__files__", filename) if _, rErr := os.ReadDir(filepath.Join(dataPath, "data", "zones", fs.ZoneID, "fs", path, "__files__")); os.IsNotExist(rErr) { if err = os.MkdirAll(filepath.Join(dataPath, "data", "zones", fs.ZoneID, "fs", path, "__files__"), 0700); err != nil { return } } else if rErr != nil { return nil, rErr } if err = os.Remove(concretePath); err != nil { logger.Println(err) } file, err := os.OpenFile(concretePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0755) if err != nil { return } // _ = atomicallyExecute(fs.filesFlag, func() (err error) { // fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)] = file // if _, ok := fs.OpenFilesForUser[userId]; !ok { // fs.OpenFilesForUser[userId] = []string{} // } // fs.OpenFilesForUser[userId] = append(fs.OpenFilesForUser[userId], fmt.Sprintf("%s/%s", filename, userId)) // return // }) writePipe = make(chan []byte) go func(f *os.File) { defer func() { _ = f.Close() // _ = atomicallyExecute(fs.filesFlag, func() (err error) { // if f, ok := fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)]; ok { // err = f.Close() // } // delete(fs.OpenFiles, fmt.Sprintf("%s/%s", filename, userId)) // if _, ok := fs.OpenFilesForUser[userId]; ok { // var index int // for i, v := range fs.OpenFilesForUser[userId] { // if v == fmt.Sprintf("%s/%s", filename, userId) { // index = i // break // } // } // if len(fs.OpenFilesForUser[userId]) > 1 { // fs.OpenFilesForUser[userId] = append(fs.OpenFilesForUser[userId][:index], fs.OpenFilesForUser[userId][:index+1]...) // } else { // delete(fs.OpenFilesForUser, userId) // } // } // return // }) }() for chunk := range writePipe { if _, err = file.Write(chunk); err != nil { return } } }(file) return } // func (fs *FSInstance) FileUploadDone(path, filename, userId string) (err error) { // err = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) { // if _, ok := fs.zoneFSDataChannels[userId]; ok { // if dc, ok := fs.zoneFSDataChannels[userId][filename]; ok { // err = dc.DataChannel.Close() // return // } // } // return // }) // return // } func (fs *FSInstance) FileUploadFailed(path, filename, userId string) (err error) { concretePath := filepath.Join(dataPath, "data", "zones", fs.ZoneID, "fs", path, "__files__", filename) err = os.Remove(concretePath) return } func (fs *FSInstance) SetupFileDownload(path, filename, userId string, dc *webrtc.DataChannel) (err error) { concretePath := filepath.Join(dataPath, "data", "zones", fs.ZoneID, "fs", path, "__files__", filename) file, err := os.OpenFile(concretePath, os.O_RDONLY, 0755) if err != nil { return } // _ = atomicallyExecute(fs.filesFlag, func() (err error) { // fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)] = file // if _, ok := fs.OpenFilesForUser[userId]; !ok { // fs.OpenFilesForUser[userId] = []string{} // } // fs.OpenFilesForUser[userId] = append(fs.OpenFilesForUser[userId], fmt.Sprintf("%s/%s", filename, userId)) // return // }) if dc != nil { dc.SetBufferedAmountLowThreshold(12000000) bufferedAmountLock := make(chan struct{}) go func(f *os.File) { defer func() { close(bufferedAmountLock) bufferedAmountLock = nil _ = f.Close() // _ = atomicallyExecute(fs.filesFlag, func() (err error) { // if f, ok := fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)]; ok { // err = f.Close() // } // delete(fs.OpenFiles, fmt.Sprintf("%s/%s", filename, userId)) // if _, ok := fs.OpenFilesForUser[userId]; ok { // var index int // for i, v := range fs.OpenFilesForUser[userId] { // if v == fmt.Sprintf("%s/%s", filename, userId) { // index = i // break // } // } // if len(fs.OpenFilesForUser[userId]) > 1 { // fs.OpenFilesForUser[userId] = append(fs.OpenFilesForUser[userId][:index], fs.OpenFilesForUser[userId][:index+1]...) // } else { // delete(fs.OpenFilesForUser, userId) // } // } // return // }) }() r := bufio.NewReader(file) buf := make([]byte, 0, 50000) sendingLoop: for { n, readErr := r.Read(buf[:cap(buf)]) buf = buf[:n] if n == 0 { if err == nil { logger.Println("n is 0 weird") break sendingLoop } if err == io.EOF { break sendingLoop } logger.Println(readErr) return } if err = dc.Send(buf); err != nil { dc.Close() logger.Println(err) break sendingLoop } if dc.BufferedAmount() > dc. BufferedAmountLowThreshold() { <-bufferedAmountLock } } logger.Println("done") if err = dc.SendText("download_done"); err != nil { logger.Println(err) } }(file) dc.OnBufferedAmountLow(func() { if bufferedAmountLock != nil { bufferedAmountLock <- struct{}{} } }) } else { err = fmt.Errorf("datachannel not created") _ = atomicallyExecute(fs.filesFlag, func() (err error) { if file, ok := fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)]; ok { file.Close() } delete(fs.OpenFiles, fmt.Sprintf("%s/%s", filename, userId)) return }) } return } func (fs *FSInstance) FileDownloadFailed(path, filename, userId string) (err error) { err = atomicallyExecute(fs.filesFlag, func() (err error) { if f, ok := fs.OpenFiles[filename]; ok { err = f.Close() } delete(fs.OpenFiles, filename) if _, ok := fs.OpenFilesForUser[userId]; ok { var index int for i, v := range fs.OpenFilesForUser[userId] { if v == fmt.Sprintf("%s/%s", filename, userId) { index = i break } } if len(fs.OpenFilesForUser[userId]) > 1 { fs.OpenFilesForUser[userId] = append(fs.OpenFilesForUser[userId][:index], fs.OpenFilesForUser[userId][:index+1]...) } else { delete(fs.OpenFilesForUser, userId) } } return }) return } // func (fs *FSInstance) HandleOffer(ctx context.Context, channelId, userId, sdp, hostId string, sendDCMessage SendDCMessageFunc, cb FSInstanceOnICECandidateFunc) (done chan struct{}, errCh chan error) { // done, errCh = make(chan struct{}), make(chan error) // go func() { // peerConnection, err := fs.createPeerConnection(userId, fs.ZoneID, webrtc.SDPTypeAnswer, cb, sendDCMessage) // if err != nil { // errCh <- err // return // } // _ = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { // fs.rtcPeerConnections[userId] = &ZoneRTCPeerConnection{ // PeerConnection: peerConnection, // makingOffer: false, // makingOfferLock: &sync.Mutex{}, // } // return // }) // offer := webrtc.SessionDescription{ // Type: webrtc.SDPTypeOffer, // SDP: sdp, // } // if err = peerConnection.SetRemoteDescription(offer); err != nil { // errCh <- err // return // } // rawAnswer, err := peerConnection.CreateAnswer(nil) // if err != nil { // errCh <- err // return // } // if err = peerConnection.SetLocalDescription(rawAnswer); err != nil { // errCh <- err // return // } // _, _ = sendDCMessage(string(ZONE_FS_WEBRTC_ANSWER), hostId, userId, map[string]interface{}{ // "to": userId, // "from": fs.ZoneID, // "channelId": channelId, // "sdp": rawAnswer.SDP, // }) // done <- struct{}{} // logger.Println("handle offer done") // }() // return // } // func (fs *FSInstance) HandleCounterOffer(ctx context.Context, userId string, sendDCMessage SendDCMessageFunc) (err error) { // // if err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { // // if _, ok := fs.rtcPeerConnections[userId]; !ok { // // err = fmt.Errorf("no field corresponding peer connection for id %s", userId) // // return // // } // // logger.Println("handling counter offer") // // connection := fs.rtcPeerConnections[userId] // // err = atomicallyExecute(fs.localSDMapFlag, func() (err error) { // // err = connection.SetLocalDescription(*fs.localSD[userId]) // // return // // }) // // return // // }); err != nil { // // return // // } // // _ = atomicallyExecute(fs.localSDMapFlag, func() (err error) { // // delete(fs.localSD, userId) // // return // // }) // if err = atomicallyExecute(fs.candidateFlag, func() (err error) { // for _, candidate := range fs.pendingCandidates[userId] { // logger.Println("sending candidate to", userId, candidate) // d, e := sendDCMessage(string(ZONE_FS_WEBRTC_CANDIDATE), "", userId, map[string]interface{}{ // "from": fs.ZoneID, // "to": userId, // "candidate": candidate.ToJSON().Candidate, // "sdpMid": *candidate.ToJSON().SDPMid, // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), // }) // select { // case <-d: // case err = <-e: // return // } // } // delete(fs.pendingCandidates, userId) // return // }); err != nil { // return // } // return // } // func (fs *FSInstance) HandleRennegotiationOffer(from, sdp string, sendDCMessage SendDCMessageFunc) (err error) { // err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { // if _, ok := fs.rtcPeerConnections[from]; !ok { // err = fmt.Errorf("no corresponding peer connection for id %s", from) // return // } // // fs.rtcPeerConnections[from].makingOfferLock.Lock() // // if fs.rtcPeerConnections[from].makingOffer { // // fs.rtcPeerConnections[from].makingOfferLock.Unlock() // // return fmt.Errorf("already making an offer or state is stable") // // } // // fs.rtcPeerConnections[from].makingOfferLock.Unlock() // if err = fs.rtcPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil { // return // } // localSd, err := fs.rtcPeerConnections[from].CreateAnswer(nil) // if err != nil { // return // } // if err = fs.rtcPeerConnections[from].SetLocalDescription(localSd); err != nil { // return // } // d, e := sendDCMessage(string(ZONE_FS_WEBRTC_RENNEGOTIATION_ANSWER), fs.ZoneID, from, map[string]interface{}{ // "from": fs.ZoneID, // "to": from, // "sdp": localSd.SDP, // }) // select { // case <-d: // case err = <-e: // } // return // }) // return // } // func (fs *FSInstance) HandleRennegotiationAnswer(from, sdp string) (err error) { // logger.Println("---------------------handling rennego answer") // err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { // err = fs.rtcPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer}) // return // }) // return // } // func (fs *FSInstance) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) { // logger.Println("adding ice candidate", candidate) // err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { // if _, ok := fs.rtcPeerConnections[from]; ok && candidate != nil { // err = fs.rtcPeerConnections[from].AddICECandidate(*candidate) // } // return // }) // return // } // func (fs *FSInstance) createPeerConnection(target, from string, peerType webrtc.SDPType, cb FSInstanceOnICECandidateFunc, sendDCMessage SendDCMessageFunc) (peerConnection *webrtc.PeerConnection, err error) { // defer func() { // if r := recover(); err != nil { // logger.Printf("recover from panic : %v\n", r) // } // }() // config := webrtc.Configuration{ // ICEServers: []webrtc.ICEServer{ // { // URLs: []string{"stun:stun.l.google.com:19302", "stun:stun.l.google.com:19302"}, // }, // }, // SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback, // } // peerConnection, err = webrtc.NewPeerConnection(config) // if err != nil { // return // } // logger.Println("---------------------------------------------------") // if peerType == webrtc.SDPTypeAnswer { // maxRetransmits := uint16(100) // channel, err := peerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{ // MaxRetransmits: &maxRetransmits, // }) // if err != nil { // return nil, err // } // channel.OnOpen(func() { // logger.Println("channel opened") // if chanErr := channel.SendText("yooo man this is open"); chanErr != nil { // logger.Println(chanErr) // } // }) // channel.OnMessage(func(msg webrtc.DataChannelMessage) { // var event CallEvent // if err := json.Unmarshal(msg.Data, &event); err != nil { // logger.Println(err) // return // } // if e := fs.HandleDataChannelEvents(event.From, event.EventId, event.Payload); e != nil { // logger.Println("*-------------- datachannel error: ", e) // } // }) // logger.Println("new channel for target : ", target) // channel.SetBufferedAmountLowThreshold(bufferedAmountLowThreshold) // channel.OnBufferedAmountLow(func() { // }) // _ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) { // logger.Println(target) // l := uint32(0) // if _, ok := fs.zoneFSDataChannels[target]; !ok { // fs.zoneFSDataChannels[target] = make(map[string]*DataChannel) // } // fs.zoneFSDataChannels[target][channel.Label()] = &DataChannel{ // DataChannel: channel, // l: &l, // } // return // }) // } else { // peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) { // _ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) { // l := uint32(0) // if _, ok := fs.zoneFSDataChannels[target]; !ok { // fs.zoneFSDataChannels[target] = make(map[string]*DataChannel) // } // fs.zoneFSDataChannels[target][dc.Label()] = &DataChannel{ // DataChannel: dc, // l: &l, // } // return // }) // dc.OnOpen(func() { // logger.Printf("got a new open datachannel %s\n", dc.Label()) // }) // dc.OnMessage(func(msg webrtc.DataChannelMessage) { // var event CallEvent // if err := json.Unmarshal(msg.Data, &event); err != nil { // logger.Println(err) // return // } // if e := fs.HandleDataChannelEvents(event.From, event.EventId, event.Payload); e != nil { // logger.Println("*-------------- datachannel error: ", e) // } // }) // }) // } // peerConnection.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) { // if pcs == webrtc.PeerConnectionStateClosed || pcs == webrtc.PeerConnectionStateDisconnected || pcs == webrtc.PeerConnectionStateFailed { // logger.Println(pcs) // //fs.HandleLeavingMember(target) // } // }) // peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) { // logger.Printf("ICE connection state has changed %s\n", is.String()) // if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed { // logger.Println(is) // } // }) // peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) { // if i == nil { // return // } // _ = atomicallyExecute(fs.candidateFlag, func() (err error) { // desc := peerConnection.RemoteDescription() // if desc == nil { // logger.Println("generated candidate appended to list : ", i) // fs.pendingCandidates[target] = append(fs.pendingCandidates[target], i) // } else { // logger.Println("generated candidate : ", i) // if iceCandidateErr := cb(from, target, i); iceCandidateErr != nil { // logger.Println(iceCandidateErr) // } // } // return // }) // }) // // peerConnection.OnNegotiationNeeded(func() { // // logger.Println("---------------- rennego is needed -----------") // // _ = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { // // logger.Println("----------------- sending renego to peer with id", target) // // if peerConnection.SignalingState() == webrtc.SignalingStateStable { // // localSd, localSdErr := peerConnection.CreateOffer(nil) // // if localSdErr != nil { // // logger.Println(localSdErr) // // return localSdErr // // } // // if err = peerConnection.SetLocalDescription(localSd); err != nil { // // logger.Println(err) // // return // // } // // d, e := sendDCMessage(string(ZONE_FS_WEBRTC_RENNEGOTIATION_OFFER), fs.ZoneID, target, map[string]interface{}{ // // "from": fs.ZoneID, // // "to": target, // // "sdp": localSd.SDP, // // }) // // select { // // case <-d: // // case err = <-e: // // logger.Println(err) // // } // // } // // return // // }) // // }) // return // } // func (fs *FSInstance) HandleLeavingMember(id string) { // _ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) { // if _, ok := fs.zoneFSDataChannels[id]; ok { // for _, dc := range fs.zoneFSDataChannels[id] { // dc.DataChannel.Close() // } // } // delete(fs.zoneFSDataChannels, id) // return // }) // logger.Println("chatfs datachannels cleaned") // _ = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { // if pc, ok := fs.rtcPeerConnections[id]; ok { // if closeErr := pc.Close(); closeErr != nil { // err = closeErr // logger.Println("peer connection close error", closeErr) // } // } // delete(fs.rtcPeerConnections, id) // return // }) // logger.Println("chats perrconnections cleaned") // _ = atomicallyExecute(fs.candidateFlag, func() (err error) { // delete(fs.pendingCandidates, id) // return // }) // _ = atomicallyExecute(fs.filesFlag, func() (err error) { // for _, openFile := range fs.OpenFilesForUser[id] { // if f, ok := fs.OpenFiles[openFile]; ok { // _ = f.Close() // } // delete(fs.OpenFiles, openFile) // } // delete(fs.OpenFilesForUser, id) // return // }) // } func (fs *FSInstance) HandleDataChannelEvents(from, eventId string, payload map[string]interface{}) (err error) { switch eventId { } return }