From d8bf2d3227d25081561c0d88600e8e1f9c424db3 Mon Sep 17 00:00:00 2001 From: erinn Date: Tue, 14 Dec 2021 13:21:45 -0800 Subject: [PATCH 1/4] image previews wip --- .../filesharing/filesharing_functionality.go | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/functionality/filesharing/filesharing_functionality.go b/functionality/filesharing/filesharing_functionality.go index 0dae38a..1cd6005 100644 --- a/functionality/filesharing/filesharing_functionality.go +++ b/functionality/filesharing/filesharing_functionality.go @@ -8,8 +8,10 @@ import ( "fmt" "io" "math" + "os" path "path/filepath" "strconv" + "strings" "time" "cwtch.im/cwtch/model" @@ -31,6 +33,13 @@ func FunctionalityGate(experimentMap map[string]bool) (*Functionality, error) { return nil, errors.New("filesharing is not enabled") } +func PreviewFunctionalityGate(experimentMap map[string]bool) (*Functionality, error) { + if experimentMap["filesharing"] == true && experimentMap["filesharing-images"] == true { + return new(Functionality), nil + } + return nil, errors.New("image previews are not enabled") +} + // OverlayMessage presents the canonical format of the File Sharing functionality Overlay Message // This is the format that the UI will parse to display the message type OverlayMessage struct { @@ -40,6 +49,10 @@ type OverlayMessage struct { Size uint64 `json:"s"` } +func (om *OverlayMessage) FileKey() string { + return fmt.Sprintf("%s.%s", om.Hash, om.Nonce) +} + // DownloadFile given a profile, a conversation handle and a file sharing key, start off a download process // to downloadFilePath func (f *Functionality) DownloadFile(profile peer.CwtchPeer, handle string, downloadFilePath string, manifestFilePath string, key string) { @@ -101,3 +114,26 @@ func (f *Functionality) ShareFile(filepath string, profile peer.CwtchPeer, handl return nil } + +func GenerateDownloadPath(basePath, fileName string) (filePath, manifestPath string) { + filePath = fmt.Sprintf("%s%s", basePath, fileName) + manifestPath = fmt.Sprintf("%s.manifest", filePath) + + parts := strings.Split(fileName, ".") + fileNameBase := parts[0] + fileNameExt := "" + if len(parts) > 1 { + fileNameBase = strings.Join(parts[0:len(parts)-1], ".") + fileNameExt = fmt.Sprintf(".%s", parts[len(parts)-1]) + } + + for i := 2; ; i++ { + if _, err := os.Open(filePath); os.IsNotExist(err) { + if _, err := os.Open(manifestPath); os.IsNotExist(err) { + return + } + } + filePath = fmt.Sprintf("%s%s (%d)%s", basePath, fileNameBase, i, fileNameExt) + manifestPath = fmt.Sprintf("%s.manifest", filePath) + } +} From 5ae269c531857a76cfd467e89891ff44f4c92437 Mon Sep 17 00:00:00 2001 From: erinn Date: Thu, 16 Dec 2021 16:40:28 -0800 Subject: [PATCH 2/4] message previews - safety checks --- .../filesharing/filesharing_functionality.go | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/functionality/filesharing/filesharing_functionality.go b/functionality/filesharing/filesharing_functionality.go index 26a192f..0b8510f 100644 --- a/functionality/filesharing/filesharing_functionality.go +++ b/functionality/filesharing/filesharing_functionality.go @@ -10,6 +10,7 @@ import ( "math" "os" path "path/filepath" + "regexp" "strconv" "strings" "time" @@ -54,6 +55,12 @@ func (om *OverlayMessage) FileKey() string { return fmt.Sprintf("%s.%s", om.Hash, om.Nonce) } +// checks file size and file name. *DOES NOT* check user settings or contact state +func (om *OverlayMessage) ShouldAutoDL() bool { + lname := strings.ToLower(om.Name) + return om.Size <= 20971520 && (strings.HasSuffix(lname, "jpg") || strings.HasSuffix(lname, "jpeg") || strings.HasSuffix(lname, "png") || strings.HasSuffix(lname, "gif") || strings.HasSuffix(lname, "webp") || strings.HasSuffix(lname, "bmp")) +} + // DownloadFile given a profile, a conversation handle and a file sharing key, start off a download process // to downloadFilePath func (f *Functionality) DownloadFile(profile peer.CwtchPeer, conversationID int, downloadFilePath string, manifestFilePath string, key string) { @@ -118,9 +125,26 @@ func (f *Functionality) ShareFile(filepath string, profile peer.CwtchPeer, conve } func GenerateDownloadPath(basePath, fileName string) (filePath, manifestPath string) { + // avoid all kina funky shit + re := regexp.MustCompile(`[^A-Za-z0-9._-]`) + filePath = re.ReplaceAllString(filePath, "") + // avoid hidden files on linux + for strings.HasPrefix(filePath, ".") { + filePath = strings.TrimPrefix(filePath, ".") + } + // avoid empties + if strings.TrimSpace(filePath) == "" { + filePath = "untitled" + } + // if you like it, put a / on it + if !strings.HasSuffix(basePath, string(os.PathSeparator)) { + basePath = fmt.Sprintf("%s%s", basePath, string(os.PathSeparator)) + } filePath = fmt.Sprintf("%s%s", basePath, fileName) manifestPath = fmt.Sprintf("%s.manifest", filePath) + // if file is named "file", iterate "file", "file (2)", "file (3)", ... until DNE + // if file is named "file.ext", iterate "file.ext", "file (2).ext", "file (3).ext", ... until DNE parts := strings.Split(fileName, ".") fileNameBase := parts[0] fileNameExt := "" From a392fa0cdac524324c0281b93cf9c4f4b3be195a Mon Sep 17 00:00:00 2001 From: erinn Date: Sat, 18 Dec 2021 16:15:05 -0800 Subject: [PATCH 3/4] image previews - dan comments --- .../filesharing/filesharing_functionality.go | 15 ++++++++++++--- model/constants/experiments.go | 14 ++++++++++++++ .../filesharing/file_sharing_integration_test.go | 15 +++++++++------ 3 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 model/constants/experiments.go diff --git a/functionality/filesharing/filesharing_functionality.go b/functionality/filesharing/filesharing_functionality.go index 0b8510f..b3436f4 100644 --- a/functionality/filesharing/filesharing_functionality.go +++ b/functionality/filesharing/filesharing_functionality.go @@ -17,6 +17,7 @@ import ( "cwtch.im/cwtch/model" "cwtch.im/cwtch/model/attr" + "cwtch.im/cwtch/model/constants" "cwtch.im/cwtch/peer" "cwtch.im/cwtch/protocol/files" "git.openprivacy.ca/openprivacy/log" @@ -29,14 +30,14 @@ type Functionality struct { // FunctionalityGate returns filesharing if enabled in the given experiment map // Note: Experiment maps are currently in libcwtch-go func FunctionalityGate(experimentMap map[string]bool) (*Functionality, error) { - if experimentMap["filesharing"] { + if experimentMap[constants.FileSharingExperiment] { return new(Functionality), nil } return nil, errors.New("filesharing is not enabled") } func PreviewFunctionalityGate(experimentMap map[string]bool) (*Functionality, error) { - if experimentMap["filesharing"] == true && experimentMap["filesharing-images"] == true { + if experimentMap[constants.FileSharingExperiment] == true && experimentMap[constants.ImagePreviewsExperiment] == true { return new(Functionality), nil } return nil, errors.New("image previews are not enabled") @@ -57,8 +58,16 @@ func (om *OverlayMessage) FileKey() string { // checks file size and file name. *DOES NOT* check user settings or contact state func (om *OverlayMessage) ShouldAutoDL() bool { + if om.Size > constants.ImagePreviewMaxSizeInBytes { + return false + } lname := strings.ToLower(om.Name) - return om.Size <= 20971520 && (strings.HasSuffix(lname, "jpg") || strings.HasSuffix(lname, "jpeg") || strings.HasSuffix(lname, "png") || strings.HasSuffix(lname, "gif") || strings.HasSuffix(lname, "webp") || strings.HasSuffix(lname, "bmp")) + for _, s := range constants.AUTODL_FILE_EXTS { + if strings.HasSuffix(lname, s) { + return true + } + } + return false } // DownloadFile given a profile, a conversation handle and a file sharing key, start off a download process diff --git a/model/constants/experiments.go b/model/constants/experiments.go new file mode 100644 index 0000000..9adb50e --- /dev/null +++ b/model/constants/experiments.go @@ -0,0 +1,14 @@ +package constants + +// Allows file sharing +const FileSharingExperiment = "filesharing" + +// Causes images (up to ImagePreviewMaxSizeInBytes, from accepted contacts) to auto-dl and preview +// requires FileSharingExperiment to be enabled +const ImagePreviewsExperiment = "filesharing-images" + +// Files up to this size will be autodownloaded using ImagePreviewsExperiment +const ImagePreviewMaxSizeInBytes = 20971520 + +// Files with these extensions will be autodownloaded using ImagePreviewsExperiment +var AUTODL_FILE_EXTS = [...]string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"} diff --git a/testing/filesharing/file_sharing_integration_test.go b/testing/filesharing/file_sharing_integration_test.go index 5c1bd13..47a604f 100644 --- a/testing/filesharing/file_sharing_integration_test.go +++ b/testing/filesharing/file_sharing_integration_test.go @@ -2,6 +2,11 @@ package filesharing import ( "crypto/rand" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + app2 "cwtch.im/cwtch/app" "cwtch.im/cwtch/app/utils" "cwtch.im/cwtch/event" @@ -12,14 +17,10 @@ import ( "cwtch.im/cwtch/peer" "cwtch.im/cwtch/protocol/connections" "cwtch.im/cwtch/protocol/files" - "encoding/base64" - "encoding/hex" - "encoding/json" - "fmt" "git.openprivacy.ca/openprivacy/connectivity/tor" "git.openprivacy.ca/openprivacy/log" + // Import SQL Cipher - _ "github.com/mutecomm/go-sqlcipher/v4" mrand "math/rand" "os" "os/user" @@ -28,6 +29,8 @@ import ( "runtime/pprof" "testing" "time" + + _ "github.com/mutecomm/go-sqlcipher/v4" ) func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.CwtchPeer) { @@ -121,7 +124,7 @@ func TestFileSharing(t *testing.T) { fmt.Println("Alice and Bob are Connected!!") - filesharingFunctionality, _ := filesharing.FunctionalityGate(map[string]bool{"filesharing": true}) + filesharingFunctionality, _ := filesharing.FunctionalityGate(map[string]bool{constants.FileSharingExperiment: true}) err = filesharingFunctionality.ShareFile("cwtch.png", alice, 1) From 158881ed9c199b88e9af0215182234236b9d7de4 Mon Sep 17 00:00:00 2001 From: erinn Date: Sat, 18 Dec 2021 16:34:07 -0800 Subject: [PATCH 4/4] quality --- functionality/filesharing/filesharing_functionality.go | 9 ++++++--- model/constants/experiments.go | 10 +++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/functionality/filesharing/filesharing_functionality.go b/functionality/filesharing/filesharing_functionality.go index b3436f4..8d76ddf 100644 --- a/functionality/filesharing/filesharing_functionality.go +++ b/functionality/filesharing/filesharing_functionality.go @@ -36,8 +36,9 @@ func FunctionalityGate(experimentMap map[string]bool) (*Functionality, error) { return nil, errors.New("filesharing is not enabled") } +// PreviewFunctionalityGate returns filesharing if image previews are enabled func PreviewFunctionalityGate(experimentMap map[string]bool) (*Functionality, error) { - if experimentMap[constants.FileSharingExperiment] == true && experimentMap[constants.ImagePreviewsExperiment] == true { + if experimentMap[constants.FileSharingExperiment] && experimentMap[constants.ImagePreviewsExperiment] { return new(Functionality), nil } return nil, errors.New("image previews are not enabled") @@ -52,17 +53,18 @@ type OverlayMessage struct { Size uint64 `json:"s"` } +// FileKey is the unique reference to a file offer func (om *OverlayMessage) FileKey() string { return fmt.Sprintf("%s.%s", om.Hash, om.Nonce) } -// checks file size and file name. *DOES NOT* check user settings or contact state +// ShouldAutoDL checks file size and file name. *DOES NOT* check user settings or contact state func (om *OverlayMessage) ShouldAutoDL() bool { if om.Size > constants.ImagePreviewMaxSizeInBytes { return false } lname := strings.ToLower(om.Name) - for _, s := range constants.AUTODL_FILE_EXTS { + for _, s := range constants.AutoDLFileExts { if strings.HasSuffix(lname, s) { return true } @@ -133,6 +135,7 @@ func (f *Functionality) ShareFile(filepath string, profile peer.CwtchPeer, conve return nil } +// GenerateDownloadPath creates a file path that doesn't currently exist on the filesystem func GenerateDownloadPath(basePath, fileName string) (filePath, manifestPath string) { // avoid all kina funky shit re := regexp.MustCompile(`[^A-Za-z0-9._-]`) diff --git a/model/constants/experiments.go b/model/constants/experiments.go index 9adb50e..665a856 100644 --- a/model/constants/experiments.go +++ b/model/constants/experiments.go @@ -1,14 +1,14 @@ package constants -// Allows file sharing +// FileSharingExperiment Allows file sharing const FileSharingExperiment = "filesharing" -// Causes images (up to ImagePreviewMaxSizeInBytes, from accepted contacts) to auto-dl and preview +// ImagePreviewsExperiment Causes images (up to ImagePreviewMaxSizeInBytes, from accepted contacts) to auto-dl and preview // requires FileSharingExperiment to be enabled const ImagePreviewsExperiment = "filesharing-images" -// Files up to this size will be autodownloaded using ImagePreviewsExperiment +// ImagePreviewMaxSizeInBytes Files up to this size will be autodownloaded using ImagePreviewsExperiment const ImagePreviewMaxSizeInBytes = 20971520 -// Files with these extensions will be autodownloaded using ImagePreviewsExperiment -var AUTODL_FILE_EXTS = [...]string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"} +// AutoDLFileExts Files with these extensions will be autodownloaded using ImagePreviewsExperiment +var AutoDLFileExts = [...]string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"}