diff --git a/functionality/filesharing/filesharing_functionality.go b/functionality/filesharing/filesharing_functionality.go index b714033..4747297 100644 --- a/functionality/filesharing/filesharing_functionality.go +++ b/functionality/filesharing/filesharing_functionality.go @@ -2,19 +2,21 @@ package filesharing import ( "crypto/rand" - "cwtch.im/cwtch/model" - "cwtch.im/cwtch/model/attr" - "cwtch.im/cwtch/peer" - "cwtch.im/cwtch/protocol/files" "encoding/hex" "encoding/json" "errors" "fmt" - "git.openprivacy.ca/openprivacy/log" "io" "math" path "path/filepath" "strconv" + "time" + + "cwtch.im/cwtch/model" + "cwtch.im/cwtch/model/attr" + "cwtch.im/cwtch/peer" + "cwtch.im/cwtch/protocol/files" + "git.openprivacy.ca/openprivacy/log" ) // Functionality groups some common UI triggered functions for contacts... @@ -45,8 +47,8 @@ func (f *Functionality) DownloadFile(profile peer.CwtchPeer, conversationID int, // Store local.filesharing.filekey.manifest as the location of the manifest profile.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.manifest", key), manifestFilePath) - // Store local.filesharing.filekey as the location of the download - profile.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, key, downloadFilePath) + // Store local.filesharing.filekey.path as the location of the download + profile.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.path", key), downloadFilePath) // Get the value of conversation.filesharing.filekey.manifest.size from `handle` profile.SendScopedZonedGetValToContact(conversationID, attr.ConversationScope, attr.FilesharingZone, fmt.Sprintf("%s.manifest.size", key)) @@ -89,6 +91,9 @@ func (f *Functionality) ShareFile(filepath string, profile peer.CwtchPeer, conve // 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.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.ts", key), strconv.FormatInt(time.Now().Unix(), 10)) + profile.SetScopedZonedAttribute(attr.ConversationScope, attr.FilesharingZone, fmt.Sprintf("%s.manifest", key), string(serializedManifest)) profile.SetScopedZonedAttribute(attr.ConversationScope, attr.FilesharingZone, 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 2e8d21f..00d8c4b 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -18,6 +18,8 @@ import ( "sync" "time" + "cwtch.im/cwtch/model/constants" + "cwtch.im/cwtch/event" "cwtch.im/cwtch/model" "cwtch.im/cwtch/model/attr" @@ -33,7 +35,7 @@ var autoHandleableEvents = map[event.Type]bool{event.EncryptedGroupMessage: true event.ServerStateChange: true, event.NewGroupInvite: true, event.NewMessageFromPeer: true, event.PeerAcknowledgement: true, event.PeerError: true, event.SendMessageToPeerError: true, event.SendMessageToGroupError: true, event.NewGetValMessageFromPeer: true, event.NewRetValMessageFromPeer: true, event.ProtocolEngineStopped: true, event.RetryServerRequest: true, - event.ManifestSizeReceived: true, event.ManifestReceived: true} + event.ManifestSizeReceived: true, event.ManifestReceived: true, event.FileDownloaded: true} // DefaultEventsToHandle specifies which events will be subscribed to // when a peer has its Init() function called @@ -594,13 +596,14 @@ func (cp *cwtchPeer) StartGroup(name string, server string) (int, error) { // AddServer takes in a serialized server specification (a bundle of related keys) and adds a contact for the // server assuming there are no errors and the contact doesn't already exist. +// Returns the onion of the new server if added // TODO in the future this function should also integrate with a trust provider to validate the key bundle. // Status: Ready for 1.5 func (cp *cwtchPeer) AddServer(serverSpecification string) error { // This confirms that the server did at least sign the bundle keyBundle, err := model.DeserializeAndVerify([]byte(serverSpecification)) if err != nil { - return err + return "", err } log.Debugf("Got new key bundle %v", keyBundle.Serialize()) @@ -608,7 +611,7 @@ func (cp *cwtchPeer) AddServer(serverSpecification string) error { // keys or subsets of keys, but for now they must commit only to a complete set of keys required for Cwtch Groups // (that way we can be assured that the keybundle we store is a valid one) if !keyBundle.HasKeyType(model.KeyTypeTokenOnion) || !keyBundle.HasKeyType(model.KeyTypeServerOnion) || !keyBundle.HasKeyType(model.KeyTypePrivacyPass) { - return errors.New("keybundle is incomplete") + return "", errors.New("keybundle is incomplete") } if keyBundle.HasKeyType(model.KeyTypeServerOnion) { @@ -930,6 +933,13 @@ func (cp *cwtchPeer) storeMessage(handle string, message string, sent time.Time) // ShareFile begins hosting the given serialized manifest // Status: Ready for 1.5 func (cp *cwtchPeer) ShareFile(fileKey string, serializedManifest string) { + tsStr, exists := cp.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.ts", fileKey)) + if exists { + ts, err := strconv.ParseInt(tsStr, 10, 64) + if err != nil || ts < time.Now().Unix()-2592000 { + log.Errorf("ignoring request to download a file offered more than 30 days ago") + } + } cp.eventBus.Publish(event.NewEvent(event.ShareManifest, map[event.Field]string{event.FileKey: fileKey, event.SerializedManifest: serializedManifest})) } @@ -1044,7 +1054,7 @@ func (cp *cwtchPeer) eventHandler() { manifestFilePath, exists := cp.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%v.manifest", fileKey)) if exists { - downloadFilePath, exists := cp.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fileKey) + downloadFilePath, exists := cp.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%v.path", fileKey)) if exists { log.Debugf("downloading manifest to %v, file to %v", manifestFilePath, downloadFilePath) var manifest files.Manifest @@ -1079,7 +1089,9 @@ func (cp *cwtchPeer) eventHandler() { } else { log.Errorf("no download path found for manifest: %v", fileKey) } - + case event.FileDownloaded: + fileKey := ev.Data[event.FileKey] + cp.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.complete", fileKey), "true") case event.NewRetValMessageFromPeer: onion := ev.Data[event.RemotePeer] scope := ev.Data[event.Scope]