diff --git a/event/common.go b/event/common.go index 727f335..5bd348e 100644 --- a/event/common.go +++ b/event/common.go @@ -329,6 +329,7 @@ const ( SerializedManifest = Field("SerializedManifest") TempFile = Field("TempFile") FilePath = Field("FilePath") + NameSuggestion = Field("NameSuggestion") ) // Defining Common errors diff --git a/functionality/filesharing/filesharing_functionality.go b/functionality/filesharing/filesharing_functionality.go index 6c8881f..59f5192 100644 --- a/functionality/filesharing/filesharing_functionality.go +++ b/functionality/filesharing/filesharing_functionality.go @@ -81,7 +81,10 @@ func (f *Functionality) ShareFile(filepath string, profile peer.CwtchPeer, handl // Store the size of the manifest (in chunks) as part of the public scope so contacts who we share the file with // can fetch the manifest as if it were a file. - profile.SetAttribute(attr.GetPublicScope(fmt.Sprintf("%s.manifest.size", key)), strconv.Itoa(int(math.Ceil(float64(len(serializedManifest))/float64(files.DefaultChunkSize))))) + // manifest.FileName gets redacted in filesharing_subsystem (to remove the system-specific file hierarchy), + // but we need to *store* the full path because the sender also uses it to locate the file + lenDiff := len(filepath) - len(path.Base(filepath)) + profile.SetAttribute(attr.GetPublicScope(fmt.Sprintf("%s.manifest.size", key)), strconv.Itoa(int(math.Ceil(float64(len(serializedManifest)-lenDiff)/float64(files.DefaultChunkSize))))) profile.ShareFile(key, string(serializedManifest)) diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index 5cace57..c6f6e97 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -846,6 +846,7 @@ func (cp *cwtchPeer) eventHandler() { var manifest files.Manifest err := json.Unmarshal([]byte(serializedManifest), &manifest) if err == nil { + manifest.Title = manifest.FileName manifest.FileName = downloadFilePath log.Debugf("saving manifest") err = manifest.Save(manifestFilePath) @@ -862,6 +863,7 @@ func (cp *cwtchPeer) eventHandler() { event.Handle: handle, event.SerializedManifest: string(manifest.Serialize()), event.TempFile: tempFile, + event.NameSuggestion: manifest.Title, })) } } else { diff --git a/protocol/connections/engine.go b/protocol/connections/engine.go index cab8500..3eef1bc 100644 --- a/protocol/connections/engine.go +++ b/protocol/connections/engine.go @@ -214,10 +214,11 @@ func (e *engine) eventHandler() { key := ev.Data[event.FileKey] serializedManifest := ev.Data[event.SerializedManifest] tempFile := ev.Data[event.TempFile] + title := ev.Data[event.NameSuggestion] // NOTE: for now there will probably only ever be a single chunk request. When we enable group // sharing and rehosting then this loop will serve as a a way of splitting the request among multiple // contacts - for _, message := range e.filesharingSubSystem.CompileChunkRequests(key, serializedManifest, tempFile) { + for _, message := range e.filesharingSubSystem.CompileChunkRequests(key, serializedManifest, tempFile, title) { if err := e.sendPeerMessage(handle, message); err != nil { e.eventManager.Publish(event.NewEvent(event.SendMessageToPeerError, map[event.Field]string{event.RemotePeer: ev.Data[event.RemotePeer], event.EventID: ev.EventID, event.Error: err.Error()})) } @@ -566,9 +567,9 @@ func (e *engine) handlePeerMessage(hostname string, eventID string, context stri } } } else if context == event.ContextSendFile { - fileKey, progress, totalChunks, _ := e.filesharingSubSystem.ProcessChunk(eventID, message) + fileKey, progress, totalChunks, _, title := e.filesharingSubSystem.ProcessChunk(eventID, message) if len(fileKey) != 0 { - e.eventManager.Publish(event.NewEvent(event.FileDownloadProgressUpdate, map[event.Field]string{event.FileKey: fileKey, event.Progress: strconv.Itoa(int(progress)), event.FileSizeInChunks: strconv.Itoa(int(totalChunks))})) + e.eventManager.Publish(event.NewEvent(event.FileDownloadProgressUpdate, map[event.Field]string{event.FileKey: fileKey, event.Progress: strconv.Itoa(int(progress)), event.FileSizeInChunks: strconv.Itoa(int(totalChunks)), event.NameSuggestion: title})) if progress == totalChunks { if tempFile, filePath, success := e.filesharingSubSystem.VerifyFile(fileKey); success { log.Debugf("file verified and downloaded!") diff --git a/protocol/files/filesharing_subsystem.go b/protocol/files/filesharing_subsystem.go index 0c95a13..9cd3876 100644 --- a/protocol/files/filesharing_subsystem.go +++ b/protocol/files/filesharing_subsystem.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "path" "strconv" "strings" "sync" @@ -50,12 +51,13 @@ func (fsss *FileSharingSubSystem) FetchManifest(fileKey string, manifestSize uin // CompileChunkRequests takes in a complete serializedManifest and returns a set of chunk request messages // TODO in the future we will want this to return the handles of contacts to request chunks from -func (fsss *FileSharingSubSystem) CompileChunkRequests(fileKey, serializedManifest, tempFile string) []model.PeerMessage { +func (fsss *FileSharingSubSystem) CompileChunkRequests(fileKey, serializedManifest, tempFile, title string) []model.PeerMessage { var manifest Manifest err := json.Unmarshal([]byte(serializedManifest), &manifest) var messages []model.PeerMessage if err == nil { manifest.TempFileName = tempFile + manifest.Title = title err := manifest.PrepareDownload() if err == nil { fsss.activeDownloads.Store(fileKey, &manifest) @@ -78,9 +80,18 @@ func (fsss *FileSharingSubSystem) RequestManifestParts(fileKey string) []model.P manifestI, exists := fsss.activeShares.Load(fileKey) var messages []model.PeerMessage if exists { - manifest := manifestI.(*Manifest) + oldManifest := manifestI.(*Manifest) + serializedOldManifest := oldManifest.Serialize() + log.Debugf("found serialized manifest: %s", serializedOldManifest) + + // copy so we dont get threading issues by modifying the original + // and then redact the file path before sending + // nb: manifest.size has already been corrected elsewhere + var manifest Manifest + json.Unmarshal([]byte(serializedOldManifest), &manifest) + manifest.FileName = path.Base(manifest.FileName) serializedManifest := manifest.Serialize() - log.Debugf("found serialized manifest: %s", serializedManifest) + chunkID := 0 for i := 0; i < len(serializedManifest); i += DefaultChunkSize { offset := i @@ -173,7 +184,7 @@ func (fsss *FileSharingSubSystem) ProcessChunkRequest(fileKey string, serialized // Always return the progress of a matched download if it exists along with the total number of chunks and the // given chunk ID // If not such active download exists then return an empty file key and ignore all further processing. -func (fsss *FileSharingSubSystem) ProcessChunk(chunkKey string, chunk []byte) (fileKey string, progress uint64, totalChunks uint64, chunkID uint64) { +func (fsss *FileSharingSubSystem) ProcessChunk(chunkKey string, chunk []byte) (fileKey string, progress uint64, totalChunks uint64, chunkID uint64, title string) { fileKeyParts := strings.Split(chunkKey, ".") log.Debugf("got chunk for %s", fileKeyParts) if len(fileKeyParts) == 3 { // fileKey is rootHash.nonce.chunk @@ -181,16 +192,18 @@ func (fsss *FileSharingSubSystem) ProcessChunk(chunkKey string, chunk []byte) (f fileKey = fmt.Sprintf("%s.%s", fileKeyParts[0], fileKeyParts[1]) derivedChunkID, err := strconv.Atoi(fileKeyParts[2]) if err == nil { - log.Debugf("got chunk id %d", chunkID) chunkID = uint64(derivedChunkID) + log.Debugf("got chunk id %d", chunkID) manifestI, exists := fsss.activeDownloads.Load(fileKey) if exists { manifest := manifestI.(*Manifest) totalChunks = uint64(len(manifest.Chunks)) + title = manifest.Title log.Debugf("found active manifest %v", manifest) progress, err = manifest.StoreChunk(chunkID, chunk) log.Debugf("attempts to store chunk %v %v", progress, err) if err != nil { + log.Debugf("error storing chunk: %v", err) // malicious contacts who share conversations can share random chunks // these will not match the chunk hash and as such will fail. // at this point we can't differentiate between a malicious chunk and failure to store a diff --git a/protocol/files/manifest.go b/protocol/files/manifest.go index 4dd058a..b944fd8 100644 --- a/protocol/files/manifest.go +++ b/protocol/files/manifest.go @@ -35,6 +35,7 @@ type Manifest struct { FileSizeInBytes uint64 ChunkSizeInBytes uint64 TempFileName string `json:"-"` + Title string `json:"-"` chunkComplete []bool openFd *os.File @@ -45,7 +46,6 @@ type Manifest struct { // CreateManifest takes in a file path and constructs a file sharing manifest of hashes along with // other information necessary to download, reconstruct and verify the file. func CreateManifest(path string) (*Manifest, error) { - // Process file into Chunks f, err := os.Open(path)