2023-03-06 21:06:15 +00:00
|
|
|
package filesharing
|
|
|
|
|
|
|
|
import (
|
|
|
|
"cwtch.im/cwtch/event"
|
|
|
|
"cwtch.im/cwtch/model"
|
|
|
|
"cwtch.im/cwtch/model/attr"
|
|
|
|
"cwtch.im/cwtch/model/constants"
|
|
|
|
"cwtch.im/cwtch/peer"
|
2023-04-03 19:44:28 +00:00
|
|
|
"cwtch.im/cwtch/protocol/connections"
|
2023-03-06 21:06:15 +00:00
|
|
|
"cwtch.im/cwtch/settings"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"git.openprivacy.ca/openprivacy/log"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ImagePreviewsFunctionality struct {
|
|
|
|
downloadFolder string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *ImagePreviewsFunctionality) NotifySettingsUpdate(settings settings.GlobalSettings) {
|
|
|
|
i.downloadFolder = settings.DownloadPath
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i ImagePreviewsFunctionality) EventsToRegister() []event.Type {
|
2023-04-03 21:33:25 +00:00
|
|
|
return []event.Type{event.ProtocolEngineCreated, event.NewMessageFromPeer, event.NewMessageFromGroup, event.PeerStateChange, event.Heartbeat}
|
2023-03-06 21:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (i ImagePreviewsFunctionality) ExperimentsToRegister() []string {
|
|
|
|
return []string{constants.FileSharingExperiment, constants.ImagePreviewsExperiment}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *ImagePreviewsFunctionality) OnEvent(ev event.Event, profile peer.CwtchPeer) {
|
|
|
|
if profile.IsFeatureEnabled(constants.FileSharingExperiment) && profile.IsFeatureEnabled(constants.ImagePreviewsExperiment) {
|
|
|
|
switch ev.EventType {
|
|
|
|
case event.NewMessageFromPeer:
|
|
|
|
ci, err := profile.FetchConversationInfo(ev.Data["RemotePeer"])
|
|
|
|
if err == nil {
|
|
|
|
if ci.Accepted {
|
|
|
|
i.handleImagePreviews(profile, &ev, ci.ID, ci.ID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case event.NewMessageFromGroup:
|
|
|
|
ci, err := profile.FetchConversationInfo(ev.Data["RemotePeer"])
|
|
|
|
if err == nil {
|
|
|
|
if ci.Accepted {
|
|
|
|
i.handleImagePreviews(profile, &ev, ci.ID, ci.ID)
|
|
|
|
}
|
2023-04-03 19:44:28 +00:00
|
|
|
}
|
|
|
|
case event.PeerStateChange:
|
|
|
|
ci, err := profile.FetchConversationInfo(ev.Data["RemotePeer"])
|
|
|
|
if err == nil {
|
|
|
|
// if we have re-authenticated with thie peer then request their profile image...
|
|
|
|
if connections.ConnectionStateToType()[ev.Data[event.ConnectionState]] == connections.AUTHENTICATED {
|
|
|
|
profile.SendScopedZonedGetValToContact(ci.ID, attr.PublicScope, attr.ProfileZone, constants.CustomProfileImageKey)
|
|
|
|
}
|
2023-04-03 21:33:25 +00:00
|
|
|
}
|
|
|
|
case event.Heartbeat:
|
|
|
|
conversations, err := profile.FetchConversations()
|
|
|
|
if err == nil {
|
|
|
|
for _, ci := range conversations {
|
|
|
|
if profile.GetPeerState(ci.Handle) == connections.AUTHENTICATED {
|
|
|
|
profile.SendScopedZonedGetValToContact(ci.ID, attr.PublicScope, attr.ProfileZone, constants.CustomProfileImageKey)
|
|
|
|
}
|
|
|
|
}
|
2023-03-06 21:06:15 +00:00
|
|
|
}
|
|
|
|
case event.ProtocolEngineCreated:
|
|
|
|
// Now that the Peer Engine is Activated, Reshare Profile Images
|
|
|
|
key, exists := profile.GetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.CustomProfileImageKey)
|
|
|
|
if exists {
|
|
|
|
serializedManifest, _ := profile.GetScopedZonedAttribute(attr.ConversationScope, attr.FilesharingZone, fmt.Sprintf("%s.manifest", key))
|
|
|
|
// reset the share timestamp, currently file shares are hardcoded to expire after 30 days...
|
|
|
|
// we reset the profile image here so that it is always available.
|
|
|
|
profile.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.ts", key), strconv.FormatInt(time.Now().Unix(), 10))
|
|
|
|
log.Debugf("Custom Profile Image: %v %s", key, serializedManifest)
|
|
|
|
}
|
|
|
|
// If file sharing is enabled then reshare all active files...
|
|
|
|
fsf := FunctionalityGate()
|
|
|
|
fsf.ReShareFiles(profile)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i ImagePreviewsFunctionality) OnContactRequestValue(profile peer.CwtchPeer, conversation model.Conversation, eventID string, path attr.ScopedZonedPath) {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *ImagePreviewsFunctionality) OnContactReceiveValue(profile peer.CwtchPeer, conversation model.Conversation, path attr.ScopedZonedPath, value string, exists bool) {
|
|
|
|
if profile.IsFeatureEnabled(constants.FileSharingExperiment) && profile.IsFeatureEnabled(constants.ImagePreviewsExperiment) {
|
|
|
|
_, zone, path := path.GetScopeZonePath()
|
|
|
|
if zone == attr.ProfileZone && path == constants.CustomProfileImageKey {
|
|
|
|
fileKey := value
|
|
|
|
|
|
|
|
if conversation.Accepted {
|
|
|
|
fsf := FunctionalityGate()
|
|
|
|
basepath := i.downloadFolder
|
|
|
|
fp, mp := GenerateDownloadPath(basepath, fileKey, true)
|
|
|
|
|
|
|
|
if value, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.complete", fileKey)); exists && value == event.True {
|
|
|
|
if _, err := os.Stat(fp); err == nil {
|
|
|
|
// file is marked as completed downloaded and exists...
|
|
|
|
} else {
|
|
|
|
// the user probably deleted the file, mark completed as false...
|
|
|
|
profile.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.complete", fileKey), event.False)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Debugf("Downloading Profile Image %v %v %v", fp, mp, fileKey)
|
|
|
|
// ev.Event.Data[event.FilePath] = fp
|
|
|
|
fsf.DownloadFile(profile, conversation.ID, fp, mp, value, constants.ImagePreviewMaxSizeInBytes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleImagePreviews checks settings and, if appropriate, auto-downloads any images
|
|
|
|
func (i *ImagePreviewsFunctionality) handleImagePreviews(profile peer.CwtchPeer, ev *event.Event, conversationID, senderID int) {
|
|
|
|
if profile.IsFeatureEnabled(constants.FileSharingExperiment) && profile.IsFeatureEnabled(constants.ImagePreviewsExperiment) {
|
|
|
|
|
|
|
|
// Short-circuit failures
|
|
|
|
// Don't autodownload images if the download path does not exist.
|
|
|
|
if i.downloadFolder == "" {
|
2023-03-13 19:46:15 +00:00
|
|
|
log.Errorf("download folder %v is not set", i.downloadFolder)
|
2023-03-06 21:06:15 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't autodownload images if the download path does not exist.
|
|
|
|
if _, err := os.Stat(i.downloadFolder); os.IsNotExist(err) {
|
2023-03-13 19:46:15 +00:00
|
|
|
log.Errorf("download folder %v does not exist", i.downloadFolder)
|
2023-03-06 21:06:15 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// If file sharing is enabled then reshare all active files...
|
|
|
|
fsf := FunctionalityGate()
|
|
|
|
|
|
|
|
// Now look at the image preview experiment
|
|
|
|
var cm model.MessageWrapper
|
|
|
|
err := json.Unmarshal([]byte(ev.Data[event.Data]), &cm)
|
|
|
|
if err == nil && cm.Overlay == model.OverlayFileSharing {
|
|
|
|
log.Debugf("Received File Sharing Message")
|
|
|
|
var fm OverlayMessage
|
|
|
|
err = json.Unmarshal([]byte(cm.Data), &fm)
|
|
|
|
if err == nil {
|
|
|
|
if fm.ShouldAutoDL() {
|
|
|
|
basepath := i.downloadFolder
|
|
|
|
fp, mp := GenerateDownloadPath(basepath, fm.Name, false)
|
2023-03-13 19:46:15 +00:00
|
|
|
log.Debugf("autodownloading file! %v %v %v", basepath, fp, i.downloadFolder)
|
2023-03-06 21:06:15 +00:00
|
|
|
ev.Data["Auto"] = constants.True
|
|
|
|
mID, _ := strconv.Atoi(ev.Data["Index"])
|
|
|
|
profile.UpdateMessageAttribute(conversationID, 0, mID, constants.AttrDownloaded, constants.True)
|
|
|
|
fsf.DownloadFile(profile, senderID, fp, mp, fm.FileKey(), constants.ImagePreviewMaxSizeInBytes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|