Merge pull request 'image previews' (#413) from ipreview into master
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
Reviewed-on: #413 Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
This commit is contained in:
commit
27c2524cd8
|
@ -8,12 +8,16 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
"os"
|
||||||
path "path/filepath"
|
path "path/filepath"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"cwtch.im/cwtch/model"
|
"cwtch.im/cwtch/model"
|
||||||
"cwtch.im/cwtch/model/attr"
|
"cwtch.im/cwtch/model/attr"
|
||||||
|
"cwtch.im/cwtch/model/constants"
|
||||||
"cwtch.im/cwtch/peer"
|
"cwtch.im/cwtch/peer"
|
||||||
"cwtch.im/cwtch/protocol/files"
|
"cwtch.im/cwtch/protocol/files"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
|
@ -26,12 +30,20 @@ type Functionality struct {
|
||||||
// FunctionalityGate returns filesharing if enabled in the given experiment map
|
// FunctionalityGate returns filesharing if enabled in the given experiment map
|
||||||
// Note: Experiment maps are currently in libcwtch-go
|
// Note: Experiment maps are currently in libcwtch-go
|
||||||
func FunctionalityGate(experimentMap map[string]bool) (*Functionality, error) {
|
func FunctionalityGate(experimentMap map[string]bool) (*Functionality, error) {
|
||||||
if experimentMap["filesharing"] {
|
if experimentMap[constants.FileSharingExperiment] {
|
||||||
return new(Functionality), nil
|
return new(Functionality), nil
|
||||||
}
|
}
|
||||||
return nil, errors.New("filesharing is not enabled")
|
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] && experimentMap[constants.ImagePreviewsExperiment] {
|
||||||
|
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
|
// 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
|
// This is the format that the UI will parse to display the message
|
||||||
type OverlayMessage struct {
|
type OverlayMessage struct {
|
||||||
|
@ -41,6 +53,25 @@ type OverlayMessage struct {
|
||||||
Size uint64 `json:"s"`
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.AutoDLFileExts {
|
||||||
|
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
|
// DownloadFile given a profile, a conversation handle and a file sharing key, start off a download process
|
||||||
// to downloadFilePath
|
// to downloadFilePath
|
||||||
func (f *Functionality) DownloadFile(profile peer.CwtchPeer, conversationID int, downloadFilePath string, manifestFilePath string, key string) {
|
func (f *Functionality) DownloadFile(profile peer.CwtchPeer, conversationID int, downloadFilePath string, manifestFilePath string, key string) {
|
||||||
|
@ -103,3 +134,44 @@ func (f *Functionality) ShareFile(filepath string, profile peer.CwtchPeer, conve
|
||||||
|
|
||||||
return nil
|
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._-]`)
|
||||||
|
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 := ""
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package constants
|
||||||
|
|
||||||
|
// FileSharingExperiment Allows file sharing
|
||||||
|
const FileSharingExperiment = "filesharing"
|
||||||
|
|
||||||
|
// ImagePreviewsExperiment Causes images (up to ImagePreviewMaxSizeInBytes, from accepted contacts) to auto-dl and preview
|
||||||
|
// requires FileSharingExperiment to be enabled
|
||||||
|
const ImagePreviewsExperiment = "filesharing-images"
|
||||||
|
|
||||||
|
// ImagePreviewMaxSizeInBytes Files up to this size will be autodownloaded using ImagePreviewsExperiment
|
||||||
|
const ImagePreviewMaxSizeInBytes = 20971520
|
||||||
|
|
||||||
|
// AutoDLFileExts Files with these extensions will be autodownloaded using ImagePreviewsExperiment
|
||||||
|
var AutoDLFileExts = [...]string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"}
|
|
@ -2,6 +2,11 @@ package filesharing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
app2 "cwtch.im/cwtch/app"
|
app2 "cwtch.im/cwtch/app"
|
||||||
"cwtch.im/cwtch/app/utils"
|
"cwtch.im/cwtch/app/utils"
|
||||||
"cwtch.im/cwtch/event"
|
"cwtch.im/cwtch/event"
|
||||||
|
@ -12,14 +17,10 @@ import (
|
||||||
"cwtch.im/cwtch/peer"
|
"cwtch.im/cwtch/peer"
|
||||||
"cwtch.im/cwtch/protocol/connections"
|
"cwtch.im/cwtch/protocol/connections"
|
||||||
"cwtch.im/cwtch/protocol/files"
|
"cwtch.im/cwtch/protocol/files"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
|
|
||||||
// Import SQL Cipher
|
// Import SQL Cipher
|
||||||
_ "github.com/mutecomm/go-sqlcipher/v4"
|
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
@ -28,6 +29,8 @@ import (
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
_ "github.com/mutecomm/go-sqlcipher/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.CwtchPeer) {
|
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!!")
|
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)
|
err = filesharingFunctionality.ShareFile("cwtch.png", alice, 1)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue