Compare commits
80 Commits
Author | SHA1 | Date |
---|---|---|
Dan Ballard | 7a65efaa78 | |
Sarah Jamie Lewis | 83c84b2049 | |
Sarah Jamie Lewis | 6618410cc9 | |
Sarah Jamie Lewis | 89f19928c2 | |
Sarah Jamie Lewis | 243727a131 | |
Sarah Jamie Lewis | dd0f1cd868 | |
Sarah Jamie Lewis | 5bf954f3d0 | |
Sarah Jamie Lewis | 425c3e6030 | |
Sarah Jamie Lewis | 32ea146ec0 | |
Sarah Jamie Lewis | 6321f09aad | |
Dan Ballard | 9e3fc6123b | |
Sarah Jamie Lewis | cbe687ce2e | |
Sarah Jamie Lewis | 6c0b2e2a4f | |
Sarah Jamie Lewis | 7c042334d3 | |
Sarah Jamie Lewis | c7c2259c2d | |
Sarah Jamie Lewis | c2c3c2894d | |
Sarah Jamie Lewis | 7ddf09caec | |
Sarah Jamie Lewis | 41a554a198 | |
Dan Ballard | b80db89284 | |
Sarah Jamie Lewis | 20dc170a78 | |
Sarah Jamie Lewis | 267e79368e | |
Sarah Jamie Lewis | f93616615a | |
Sarah Jamie Lewis | 3e9c9c3b70 | |
Sarah Jamie Lewis | f60fbfc3d5 | |
Sarah Jamie Lewis | f32ad741e0 | |
Sarah Jamie Lewis | a453ca9dc8 | |
Sarah Jamie Lewis | 3d2124126b | |
Sarah Jamie Lewis | 0f40f0d12d | |
Sarah Jamie Lewis | 6e0623170f | |
Sarah Jamie Lewis | 6fdcf5bac7 | |
Sarah Jamie Lewis | ae18550f71 | |
Sarah Jamie Lewis | 988ec07ebc | |
Sarah Jamie Lewis | 44241e2352 | |
Sarah Jamie Lewis | 403398b7a1 | |
Sarah Jamie Lewis | 5916333723 | |
Dan Ballard | 24b534d436 | |
Dan Ballard | c8692cc1b2 | |
Dan Ballard | dcec0f9274 | |
Sarah Jamie Lewis | 2e7a9be6c1 | |
Sarah Jamie Lewis | 7ebbfe91c9 | |
Sarah Jamie Lewis | 3029ed65ff | |
Sarah Jamie Lewis | e91db69e6e | |
Sarah Jamie Lewis | 1efc334258 | |
Sarah Jamie Lewis | 4713f25755 | |
Sarah Jamie Lewis | 6165b03b75 | |
Sarah Jamie Lewis | a18d9f0d17 | |
Sarah Jamie Lewis | 2b67e4b9b3 | |
Sarah Jamie Lewis | 6f427d282b | |
Sarah Jamie Lewis | 1b07a195be | |
Sarah Jamie Lewis | faf15f26e2 | |
Sarah Jamie Lewis | 5b2f3cfb4b | |
Sarah Jamie Lewis | a1d9872738 | |
Dan Ballard | 20065b238d | |
Sarah Jamie Lewis | ece1df0ce9 | |
Sarah Jamie Lewis | b2a43b9cca | |
Sarah Jamie Lewis | 110d58f521 | |
Sarah Jamie Lewis | 131e08ccee | |
Sarah Jamie Lewis | d5278ffb47 | |
Sarah Jamie Lewis | e12afc9d3d | |
Sarah Jamie Lewis | f485e372a7 | |
Dan Ballard | afed5c3703 | |
Sarah Jamie Lewis | b7a4bc2a18 | |
Sarah Jamie Lewis | 0e05650439 | |
Sarah Jamie Lewis | 9d7ef2322e | |
Sarah Jamie Lewis | 700d41c5f0 | |
Sarah Jamie Lewis | b0eaf75f65 | |
Sarah Jamie Lewis | fac630dbaf | |
Sarah Jamie Lewis | a2603f243e | |
Sarah Jamie Lewis | 8fe1e67312 | |
Sarah Jamie Lewis | cd5d65611b | |
Sarah Jamie Lewis | 36557da9b8 | |
Sarah Jamie Lewis | 86e7001ae4 | |
Dan Ballard | e5e4084fae | |
Sarah Jamie Lewis | 50c853afb6 | |
Sarah Jamie Lewis | a9d8266dc0 | |
Sarah Jamie Lewis | 47c55b9393 | |
Sarah Jamie Lewis | 7e3e1f977a | |
Sarah Jamie Lewis | 6dee3bc1bd | |
Sarah Jamie Lewis | 8fd4a81523 | |
Sarah Jamie Lewis | 6b64ee0553 |
75
.drone.yml
75
.drone.yml
|
@ -5,55 +5,62 @@ name: linux-android-windows-test
|
|||
|
||||
steps:
|
||||
- name: fetch
|
||||
image: golang:1.19.1
|
||||
image: openpriv/go-cross-compile:2024.02
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
commands:
|
||||
- go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor
|
||||
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/torrc
|
||||
- chmod a+x tor
|
||||
- go install go.uber.org/nilaway/cmd/nilaway@latest
|
||||
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-0.4.8.9-linux-x86_64.tar.gz -O tor.tar.gz
|
||||
- tar -xzf tor.tar.gz
|
||||
- chmod a+x Tor/tor
|
||||
- export PATH=$PWD/Tor/:$PATH
|
||||
- export LD_LIBRARY_PATH=$PWD/Tor/
|
||||
- tor --version
|
||||
- export GO111MODULE=on
|
||||
- git fetch --tags
|
||||
- go mod download
|
||||
- echo `git describe --tags` > VERSION
|
||||
- echo `git log -1 --format=%cd --date=format:%G-%m-%d-%H-%M` > COMMIT_DATE
|
||||
- export GOSUMDB="off"
|
||||
|
||||
- name: build-linux
|
||||
image: golang:1.19.1
|
||||
image: openpriv/go-cross-compile:2024.02
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
commands:
|
||||
- make clean
|
||||
# Compile Server Experiments
|
||||
- go run generate/generate_bindings.go -- serverExperiment
|
||||
- make linux
|
||||
|
||||
- name: build-android
|
||||
image: openpriv/android-go-mobile:2023.02
|
||||
image: openpriv/go-cross-compile:2024.02
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
commands:
|
||||
# Build Android without Server Experiment
|
||||
- go run generate/generate_bindings.go
|
||||
- make clean-autobindings
|
||||
- env EXPERIMENTS="" make lib.go
|
||||
- go mod download
|
||||
- gomobile init
|
||||
- make android
|
||||
|
||||
- name: build-windows
|
||||
image: openpriv/mingw-go:2023.01
|
||||
image: openpriv/go-cross-compile:2024.02
|
||||
environment:
|
||||
GOPATH: /go
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
commands:
|
||||
- go run generate/generate_bindings.go -- serverExperiment
|
||||
- make clean-autobindings
|
||||
- make windows
|
||||
|
||||
- name: deploy-buildfiles
|
||||
image: kroniak/ssh-client
|
||||
image: openpriv/go-cross-compile:2024.02
|
||||
pull: if-not-exists
|
||||
environment:
|
||||
BUILDFILES_KEY:
|
||||
|
@ -68,34 +75,15 @@ steps:
|
|||
- echo $BUILDFILES_KEY > ~/id_rsab64
|
||||
- base64 -d ~/id_rsab64 > ~/id_rsa
|
||||
- chmod 400 ~/id_rsa
|
||||
- export DIR=libCwtch-autobindings-`cat VERSION`
|
||||
- export DIR=libCwtch-autobindings-`cat COMMIT_DATE`-`cat VERSION`
|
||||
- mkdir -p $DIR
|
||||
- mv libCwtch.so libCwtch.dll cwtch.aar cwtch-sources.jar libCwtch.h $DIR/
|
||||
- mv build/* $DIR/
|
||||
- cd $DIR
|
||||
- find . -type f -exec sha256sum {} \; > ./../sha256s.txt
|
||||
- mv ./../sha256s.txt .
|
||||
- find . -type f -exec sha512sum {} \; > ./../sha512sum.txt
|
||||
- mv ./../sha512sum.txt .
|
||||
- cd ..
|
||||
- scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@build.openprivacy.ca:/home/buildfiles/buildfiles/
|
||||
|
||||
- name: gitea-release
|
||||
image: plugins/gitea-release
|
||||
pull: if-not-exists
|
||||
when:
|
||||
event: tag
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: gogs_account_token
|
||||
base_url: https://git.openprivacy.ca
|
||||
files:
|
||||
- libCwtch.so
|
||||
- libCwtch.dll
|
||||
- cwtch.aar
|
||||
- cwtch-sources.jar
|
||||
- libCwtch.h
|
||||
checksum:
|
||||
- sha256
|
||||
- sha512
|
||||
|
||||
- name: notify-gogs
|
||||
image: openpriv/drone-gogs
|
||||
pull: if-not-exists
|
||||
|
@ -136,10 +124,11 @@ steps:
|
|||
- export PATH=$PATH:/usr/local/go/bin/
|
||||
- git fetch --tags
|
||||
- echo `git describe --tags` > VERSION
|
||||
- echo `git log -1 --format=%cd --date=format:%G-%m-%d-%H-%M` > COMMIT_DATE
|
||||
- name: build-macos-x64
|
||||
commands:
|
||||
- export PATH=$PATH:/usr/local/go/bin/
|
||||
- go run generate/generate_bindings.go -- serverExperiment
|
||||
- env EXPERIMENTS="serverExperiment" make lib.go
|
||||
- make libCwtch.x64.dylib
|
||||
- name: build-macos-arm64
|
||||
when:
|
||||
|
@ -148,7 +137,8 @@ steps:
|
|||
status: [ success ]
|
||||
commands:
|
||||
- export PATH=$PATH:/usr/local/go/bin/
|
||||
- go run generate/generate_bindings.go -- serverExperiment
|
||||
- make clean-autobindings
|
||||
- env EXPERIMENTS="serverExperiment" make lib.go
|
||||
- go mod download
|
||||
- make libCwtch.arm64.dylib
|
||||
- name: deploy-buildfiles
|
||||
|
@ -161,15 +151,14 @@ steps:
|
|||
status: [ success ]
|
||||
commands:
|
||||
- echo $BUILDFILES_KEY > ~/id_rsab64
|
||||
- base64 -d ~/id_rsab64 > ~/id_rsa
|
||||
- base64 -d -i ~/id_rsab64 -o ~/id_rsa
|
||||
- chmod 400 ~/id_rsa
|
||||
- export DIR=libCwtch-autobindings-`cat VERSION`
|
||||
- export DIR=libCwtch-autobindings-`cat COMMIT_DATE`-`cat VERSION`
|
||||
- mkdir -p $DIR
|
||||
- mv libCwtch.x64.dylib $DIR/
|
||||
- mv libCwtch.arm64.dylib $DIR/
|
||||
- mv build/* $DIR/
|
||||
- cd $DIR
|
||||
- find . -type f -exec shasum -a 512 {} \; > ./../sha512s.txt
|
||||
- mv ./../sha512s.txt .
|
||||
- find . -type f -exec shasum -a 512 {} \; > ./../macsha512s.txt
|
||||
- mv ./../macsha512s.txt .
|
||||
- cd ..
|
||||
- scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@build.openprivacy.ca:/home/buildfiles/buildfiles/
|
||||
|
||||
|
@ -178,4 +167,4 @@ trigger:
|
|||
branch: main
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
- pull_request
|
||||
|
|
|
@ -9,3 +9,4 @@ libCwtch.so
|
|||
libCwtch.dylib
|
||||
libCwtch.dll
|
||||
ios/
|
||||
build/
|
|
@ -0,0 +1,9 @@
|
|||
MIT License
|
||||
Copyright (c) 2023 Open Privacy Research Society
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
56
Makefile
56
Makefile
|
@ -2,33 +2,75 @@ IOS_OUT := ./ios
|
|||
|
||||
.PHONY: all linux android windows macos clean ios
|
||||
|
||||
|
||||
DEFAULT_GOAL: linux
|
||||
|
||||
all: linux android windows
|
||||
|
||||
linux: libCwtch.so
|
||||
ARCH := $(shell uname -m)
|
||||
ARM_X_CC := aarch64-linux-gnu-gcc
|
||||
|
||||
# determine if the x64 cross arm64 compiler is available so we can add that target
|
||||
LINUX_X_ARM=
|
||||
ifneq (,$(shell which $(ARM_X_CC)))
|
||||
LINUX_X_ARM = libCwtch.x.so
|
||||
endif
|
||||
|
||||
linux: EXPERIMENTS ?= "serverExperiment"
|
||||
linux: libCwtch.so $(LINUX_X_ARM)
|
||||
|
||||
macos: EXPERIMENTS ?= "serverExperiment"
|
||||
macos: libCwtch.x64.dylib libCwtch.arm64.dylib
|
||||
|
||||
android: EXPERIMENTS ?= ""
|
||||
android: cwtch.aar
|
||||
|
||||
windows: EXPERIMENTS ?= "serverExperiment"
|
||||
windows: libCwtch.dll
|
||||
|
||||
|
||||
lib.go: generate/generate_bindings.go spec
|
||||
go run generate/generate_bindings.go --experiments "$(EXPERIMENTS)"
|
||||
|
||||
|
||||
libCwtch.so: lib.go
|
||||
./switch-ffi.sh
|
||||
go build -trimpath -ldflags "-buildid=autobindings-$(shell git describe --tags) -X main.buildVer=autobindings-$(shell git describe --tags) -X main.buildDate=$(shell git log -1 --format=%cd --date=format:%G-%m-%d-%H-%M)" -buildmode c-shared -o libCwtch.so
|
||||
mkdir -p build/linux
|
||||
ifeq ($(ARCH),x86_64)
|
||||
mv libCwtch.so build/linux/libCwtch.x64.so
|
||||
else ifeq ($(ARCH),aarch64)
|
||||
mv libCwtch.so build/linux/libCwtch.arm64.so
|
||||
endif
|
||||
mv libCwtch.h build/linux/libCwtch.h
|
||||
|
||||
libCwtch.x.so: lib.go
|
||||
./switch-ffi.sh
|
||||
mkdir -p build/linux
|
||||
ifeq ($(ARCH),x86_64)
|
||||
env CGO_ENABLED=1 CC=$(ARM_X_CC) GOARCH=arm64 go build -trimpath -ldflags "-buildid=autobindings-$(shell git describe --tags) -X main.buildVer=autobindings-$(shell git describe --tags) -X main.buildDate=$(shell git log -1 --format=%cd --date=format:%G-%m-%d-%H-%M)" -buildmode c-shared -o libCwtch.x.so
|
||||
mv libCwtch.x.so build/linux/libCwtch.arm64.so
|
||||
mv libCwtch.x.h build/linux/libCwtch.h
|
||||
endif
|
||||
|
||||
libCwtch.x64.dylib: lib.go
|
||||
./switch-ffi.sh
|
||||
go build -trimpath -ldflags "-buildid=autobindings-$(shell git describe --tags) -X main.buildVer=autobindings-$(shell git describe --tags) -X main.buildDate=$(shell git log -1 --format=%cd --date=format:%G-%m-%d-%H-%M)" -buildmode c-shared -o libCwtch.x64.dylib
|
||||
env CGO_CFLAGS="-mmacosx-version-min=10.12" CGO_LDFLAGS="-mmacosx-version-min=10.12" GOARCH=amd64 GOOS=darwin CGO_ENABLED=1 go build -trimpath -ldflags "-buildid=autobindings-$(shell git describe --tags) -X main.buildVer=autobindings-$(shell git describe --tags) -X main.buildDate=$(shell git log -1 --format=%cd --date=format:%G-%m-%d-%H-%M)" -buildmode c-shared -o libCwtch.x64.dylib
|
||||
mkdir -p build/macos
|
||||
mv libCwtch.x64.dylib build/macos/
|
||||
|
||||
libCwtch.arm64.dylib: lib.go
|
||||
./switch-ffi.sh
|
||||
env GOARCH=arm64 GOOS=darwin CGO_ENABLED=1 go build -trimpath -ldflags "-buildid=$(shell git describe --tags) -X main.buildVer=$(shell git describe --tags) -X main.buildDate=$(shell git log -1 --format=%cd --date=format:%G-%m-%d-%H-%M)" -buildmode c-shared -o libCwtch.arm64.dylib
|
||||
env CGO_CFLAGS="-mmacosx-version-min=10.12" CGO_LDFLAGS="-mmacosx-version-min=10.12" GOARCH=arm64 GOOS=darwin CGO_ENABLED=1 go build -trimpath -ldflags "-buildid=$(shell git describe --tags) -X main.buildVer=$(shell git describe --tags) -X main.buildDate=$(shell git log -1 --format=%cd --date=format:%G-%m-%d-%H-%M)" -buildmode c-shared -o libCwtch.arm64.dylib
|
||||
mkdir -p build/macos
|
||||
mv libCwtch.arm64.dylib build/macos/
|
||||
|
||||
cwtch.aar: lib.go
|
||||
./switch-gomobile.sh
|
||||
gomobile bind -trimpath -target android/arm,android/arm64,android/amd64 -ldflags="-buildid=$(shell git describe --tags) -X cwtch.buildVer=$(shell git describe --tags) -X cwtch.buildDate=$(shell git log -1 --format=%cd --date=format:%G-%m-%d-%H-%M)"
|
||||
mkdir -p build/android
|
||||
mv cwtch.aar build/android/
|
||||
mv cwtch-sources.jar build/android/
|
||||
|
||||
libCwtch.dll: lib.go
|
||||
./switch-ffi.sh
|
||||
|
@ -37,9 +79,15 @@ libCwtch.dll: lib.go
|
|||
# note: the above documentation also references an ability to set an optional timestamp - this behaviour seems to no longer be supported in more recent versions of mingw32-gcc (the help docs no longer reference that functionality)
|
||||
# these flags have to be passed through to the underlying gcc process using the -extldflags option in the underlying go linker, note that the whole flag is quoted...this is necessary.
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc-win32 go build -trimpath -ldflags "-buildid=$(shell git describe --tags) -X main.buildVer=$(shell git describe --tags) -X main.buildDate=$(shell git log -1 --format=%cd --date=format:%G-%m-%d-%H-%M) '-extldflags=-Xlinker --no-insert-timestamp'" -buildmode c-shared -o libCwtch.dll
|
||||
mkdir -p build/windows
|
||||
mv libCwtch.dll build/windows/
|
||||
mv libCwtch.h build/windows/
|
||||
|
||||
clean-autobindings:
|
||||
rm -f lib.go
|
||||
|
||||
clean:
|
||||
rm -f cwtch.aar cwtch_go.apk libCwtch.h libCwtch.so cwtch-sources.jar libCwtch.dll libCwtch.dylib
|
||||
rm -fr lib.go cwtch.aar cwtch_go.apk libCwtch.*.h libCwtch.*.so cwtch-sources.jar libCwtch.dll libCwtch.*.dylib build
|
||||
|
||||
# iOS - for testing purposes only for now, not officially supported
|
||||
|
||||
|
|
37
README.md
37
README.md
|
@ -10,7 +10,11 @@ Note for the Flutter-based Cwtch UI Application see: https://git.openprivacy.ca/
|
|||
go run generate/generate_bindings.go
|
||||
make linux // alternatively windows, android, macos
|
||||
|
||||
**NOTE:** Autobindings currently depends on an unreleased version of Cwtch (`cwtch.im/cwtch v0.18.10-0.20230221235514-49e0d849fa3e`).
|
||||
### Experiments
|
||||
|
||||
Autobindings allow for comile time selection of experiments. Default selections for each target platform are supplied in Makefile but can be overridden with:
|
||||
|
||||
env EXPERIMENTS="serverExperiment otherExperiment" make android
|
||||
|
||||
## Spec File Format
|
||||
|
||||
|
@ -40,6 +44,37 @@ Other directives:
|
|||
* `import <go pkg>` - includes an additional go importin the compiled lib.go (needed for experiments)
|
||||
* Functions that start with `Enhanced` are automatically stripped of that prefix for their binding names e.g. `EnhancedGetMessage` generated a binding `c_GetMessage` - for certain functions Cwtch has two potential calling options `<Function>` and `Enhanced<Function>`. "Enhanced" in this case means that the response is optimised for calling by a user-facing application by e.g. directly making a number of calls under the hood and returning a json blob of the results.
|
||||
|
||||
# Using
|
||||
|
||||
## General Environment Variables
|
||||
|
||||
- `LOG_FILE` if defined will mean all go logging will go to a file instead of stdout
|
||||
- `LOG_LEVEL` if set to `debug` will cause debug logging to be included in log output
|
||||
- `CWTCH_PROFILE` if set to `1` will cause a memory profile to be written to `mem.prof` and all active goroutines
|
||||
written to `stdout` when `DebugInfo()` is called.
|
||||
- `CWTCH_TAILS` if set to `true` will override the Tor configuration to allow Cwtch to work in a Tail-like environment.
|
||||
|
||||
## Linux Desktop:
|
||||
|
||||
- `LD_LIBRARY_PATH` set to point to `libCwtch.so`
|
||||
- or drop a symlink into `/usr/lib`
|
||||
|
||||
### Cross compile arm 64
|
||||
|
||||
Needs `sudo apt install gcc-aarch64-linux-gnu`
|
||||
|
||||
## Android
|
||||
|
||||
- copy `cwtch.aar` into `flutter_app/android/cwtch`
|
||||
|
||||
## Windows
|
||||
|
||||
- copy libCwtch.dll into the directory of the `.exe` using it
|
||||
|
||||
## MacOS
|
||||
|
||||
- copy libCwtch.x64.dylib and libCwtch.arm.dylib into the directory you are executing from
|
||||
|
||||
## Support Cwtch Development
|
||||
|
||||
We couldn't do what we do without all the wonderful community support we get, from [one-off donations](https://openprivacy.ca/donate) to [recurring support via Patreon](https://www.patreon.com/openprivacy).
|
||||
|
|
|
@ -13,8 +13,5 @@ const PeerOnline = "peer-online"
|
|||
|
||||
const PeerAutostart = "autostart"
|
||||
|
||||
// Description is used on server contacts,
|
||||
const Description = "description"
|
||||
|
||||
// ConversationNotificationPolicy is the attribute label for conversations. When App NotificationPolicy is OptIn a true value here opts in
|
||||
const ConversationNotificationPolicy = "notification-policy"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package servers
|
||||
package server_hosting
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/app"
|
|
@ -1,66 +0,0 @@
|
|||
package groups
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/model/attr"
|
||||
constants2 "cwtch.im/cwtch/model/constants"
|
||||
"cwtch.im/cwtch/peer"
|
||||
"cwtch.im/cwtch/protocol/connections"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/cwtch.im/cwtch-autobindings/constants"
|
||||
)
|
||||
|
||||
const groupExperiment = "tapir-groups-experiment"
|
||||
|
||||
const (
|
||||
// ServerList is a json encoded list of servers
|
||||
ServerList = event.Field("ServerList")
|
||||
)
|
||||
|
||||
const (
|
||||
// UpdateServerInfo is an event containing a ProfileOnion and a ServerList
|
||||
UpdateServerInfo = event.Type("UpdateServerInfo")
|
||||
)
|
||||
|
||||
// GroupFunctionality provides experiment gated server functionality
|
||||
type GroupFunctionality struct {
|
||||
}
|
||||
|
||||
// ExperimentGate returns GroupFunctionality if the experiment is enabled, and an error otherwise.
|
||||
func ExperimentGate(experimentMap map[string]bool) (*GroupFunctionality, error) {
|
||||
if experimentMap[groupExperiment] {
|
||||
return new(GroupFunctionality), nil
|
||||
}
|
||||
return nil, fmt.Errorf("gated by %v", groupExperiment)
|
||||
}
|
||||
|
||||
// GetServerInfoList compiles all the information the UI might need regarding all servers..
|
||||
func (gf *GroupFunctionality) GetServerInfoList(profile peer.CwtchPeer) []Server {
|
||||
var servers []Server
|
||||
for _, server := range profile.GetServers() {
|
||||
servers = append(servers, gf.GetServerInfo(server, profile))
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
// GetServerInfo compiles all the information the UI might need regarding a particular server including any verified
|
||||
// cryptographic keys
|
||||
func (gf *GroupFunctionality) GetServerInfo(serverOnion string, profile peer.CwtchPeer) Server {
|
||||
serverInfo, _ := profile.FetchConversationInfo(serverOnion)
|
||||
keyTypes := []model.KeyType{model.KeyTypeServerOnion, model.KeyTypeTokenOnion, model.KeyTypePrivacyPass}
|
||||
var serverKeys []ServerKey
|
||||
|
||||
for _, keyType := range keyTypes {
|
||||
if key, has := serverInfo.GetAttribute(attr.PublicScope, attr.ServerKeyZone, string(keyType)); has {
|
||||
serverKeys = append(serverKeys, ServerKey{Type: string(keyType), Key: key})
|
||||
}
|
||||
}
|
||||
|
||||
description, _ := serverInfo.GetAttribute(attr.LocalScope, attr.ServerZone, constants.Description)
|
||||
startTimeStr := serverInfo.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants2.SyncPreLastMessageTime)).ToString()]
|
||||
recentTimeStr := serverInfo.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants2.SyncMostRecentMessageTime)).ToString()]
|
||||
syncStatus := SyncStatus{startTimeStr, recentTimeStr}
|
||||
|
||||
return Server{Onion: serverOnion, Identifier: serverInfo.ID, Status: connections.ConnectionStateName[profile.GetPeerState(serverInfo.Handle)], Keys: serverKeys, Description: description, SyncProgress: syncStatus}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package groups
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGroupFunctionality_IsEnabled(t *testing.T) {
|
||||
|
||||
_, err := ExperimentGate(map[string]bool{})
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("group functionality should be disabled")
|
||||
}
|
||||
|
||||
_, err = ExperimentGate(map[string]bool{groupExperiment: true})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("group functionality should be enabled")
|
||||
}
|
||||
|
||||
_, err = ExperimentGate(map[string]bool{groupExperiment: false})
|
||||
if err == nil {
|
||||
t.Fatalf("group functionality should be disabled")
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package groups
|
||||
|
||||
type ServerKey struct {
|
||||
Type string `json:"type"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type SyncStatus struct {
|
||||
StartTime string `json:"startTime"`
|
||||
LastMessageTime string `json:"lastMessageTime"`
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Onion string `json:"onion"`
|
||||
Identifier int `json:"identifier"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description"`
|
||||
Keys []ServerKey `json:"keys"`
|
||||
SyncProgress SyncStatus `json:"syncProgress"`
|
||||
}
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -11,8 +12,11 @@ import (
|
|||
|
||||
func main() {
|
||||
|
||||
var experiments string
|
||||
flag.StringVar(&experiments, "experiments", "", "experiments to enable")
|
||||
flag.Parse()
|
||||
loadedExperiments := make(map[string]bool)
|
||||
for _, exp := range os.Args[1:] {
|
||||
for _, exp := range strings.Split(experiments, ",") {
|
||||
loadedExperiments[exp] = true
|
||||
}
|
||||
|
||||
|
@ -92,7 +96,9 @@ func main() {
|
|||
case "profile":
|
||||
generatedBindings = generateProfileFunction(generatedBindings, fName, args)
|
||||
case "(json)profile":
|
||||
generatedBindings = generateJsonProfileFunction(generatedBindings, fName, args)
|
||||
generatedBindings = generateJsonProfileFunction(generatedBindings, fName, args, false)
|
||||
case "(json-err)profile":
|
||||
generatedBindings = generateJsonProfileFunction(generatedBindings, fName, args, true)
|
||||
case "@profile-experiment":
|
||||
experiment := args[0]
|
||||
generatedBindings = generateExperimentalProfileFunction(generatedBindings, experiment, fName, args[1:])
|
||||
|
@ -142,6 +148,10 @@ func intArgPrototype(varName string) (string, string, string, string) {
|
|||
return fmt.Sprintf(`%s C.int`, ToSnakeCase(varName)), fmt.Sprintf(`int(%v)`, ToSnakeCase(varName)), fmt.Sprintf(`%v int`, varName), varName
|
||||
}
|
||||
|
||||
func uintArgPrototype(varName string) (string, string, string, string) {
|
||||
return fmt.Sprintf(`%s C.uint`, ToSnakeCase(varName)), fmt.Sprintf(`int(%v)`, ToSnakeCase(varName)), fmt.Sprintf(`%v int`, varName), varName
|
||||
}
|
||||
|
||||
func conversationArgPrototype(varName string) (string, string, string, string) {
|
||||
return intArgPrototype(varName)
|
||||
}
|
||||
|
@ -231,7 +241,7 @@ func mapArgs(argsTypes []string) (string, string, string, string) {
|
|||
gUse = append(gUse, c4)
|
||||
case "int":
|
||||
if len(argTypeParts) != 2 {
|
||||
fmt.Printf("generic bool arg must have have e.g. bool:<name>\n")
|
||||
fmt.Printf("generic int arg must have have e.g. int:<name>\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
c1, c2, c3, c4 := intArgPrototype(argTypeParts[1])
|
||||
|
@ -239,6 +249,20 @@ func mapArgs(argsTypes []string) (string, string, string, string) {
|
|||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
gUse = append(gUse, c4)
|
||||
case "uint":
|
||||
if len(argTypeParts) != 2 {
|
||||
fmt.Printf("generic uint arg must have have e.g. uint:<name>\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
c1, c2, c3, c4 := uintArgPrototype(argTypeParts[1])
|
||||
cArgs = append(cArgs, c1)
|
||||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
// because of java/kotlin/android/gomobile inability to recognize unsigned integers
|
||||
// we need to pretent this is a signed interface...so do the final cast here...
|
||||
// this will cause bad behavior if a negative number is passed through the java
|
||||
// interface...so...don't do that...
|
||||
gUse = append(gUse, fmt.Sprintf("uint(%s)", c4))
|
||||
case "string":
|
||||
if len(argTypeParts) != 2 {
|
||||
fmt.Printf("generic string arg must have have e.g. string:<name>\n")
|
||||
|
@ -360,7 +384,7 @@ func {{FNAME}}({{GO_ARGS_SPEC}}) {
|
|||
return bindings
|
||||
}
|
||||
|
||||
func generateJsonProfileFunction(bindings string, name string, argsTypes []string) string {
|
||||
func generateJsonProfileFunction(bindings string, name string, argsTypes []string, handleErr bool) string {
|
||||
appPrototype := `
|
||||
//export c_{{FNAME}}
|
||||
func c_{{FNAME}}({{C_ARGS}}) *C.char {
|
||||
|
@ -370,14 +394,26 @@ func c_{{FNAME}}({{C_ARGS}}) *C.char {
|
|||
func {{FNAME}}({{GO_ARGS_SPEC}}) string {
|
||||
cwtchProfile := application.GetPeer(profile)
|
||||
if cwtchProfile != nil {
|
||||
return cwtchProfile.{{LIBNAME}}({{GO_ARG}})
|
||||
{{HANDLE_FUNC}}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
`
|
||||
|
||||
noErrorPrototype := `return cwtchProfile.{{LIBNAME}}({{GO_ARG}})`
|
||||
withErrorPrototype := `res,err := cwtchProfile.{{LIBNAME}}({{GO_ARG}})
|
||||
if err != nil {
|
||||
log.Errorf("could not {{FNAME}} %v", err)
|
||||
}
|
||||
return res`
|
||||
functionPrototype := noErrorPrototype
|
||||
if handleErr {
|
||||
functionPrototype = withErrorPrototype
|
||||
}
|
||||
|
||||
cArgs, c2GoArgs, goSpec, gUse := mapArgs(argsTypes)
|
||||
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{HANDLE_FUNC}}", functionPrototype)
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{FNAME}}", strings.TrimPrefix(name, "Enhanced"))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{LIBNAME}}", name)
|
||||
// We need to prepend a set of profile handle arguments...
|
||||
|
@ -401,9 +437,12 @@ func c_{{FNAME}}({{C_ARGS}}) {
|
|||
func {{FNAME}}({{GO_ARGS_SPEC}}) {
|
||||
cwtchProfile := application.GetPeer(profile)
|
||||
if cwtchProfile != nil {
|
||||
functionality, _ := {{EXPERIMENT}}.FunctionalityGate(application.ReadSettings().Experiments)
|
||||
functionality := {{EXPERIMENT}}.FunctionalityGate()
|
||||
if functionality != nil {
|
||||
functionality.{{LIBNAME}}(cwtchProfile, {{GO_ARG}})
|
||||
err := functionality.{{LIBNAME}}(cwtchProfile, {{GO_ARG}})
|
||||
if err != nil {
|
||||
log.Errorf("error calling experimental feature {{FNAME}}: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +474,7 @@ func c_{{FNAME}}({{C_ARGS}}) *C.char {
|
|||
func {{FNAME}}({{GO_ARGS_SPEC}}) string {
|
||||
cwtchProfile := application.GetPeer(profile)
|
||||
if cwtchProfile != nil {
|
||||
functionality, _ := {{EXPERIMENT}}.FunctionalityGate(application.ReadSettings().Experiments)
|
||||
functionality := {{EXPERIMENT}}.FunctionalityGate()
|
||||
if functionality != nil {
|
||||
return functionality.{{LIBNAME}}(cwtchProfile, {{GO_ARG}})
|
||||
}
|
||||
|
|
10
go.mod
10
go.mod
|
@ -1,11 +1,11 @@
|
|||
module git.openprivacy.ca/cwtch.im/cwtch-autobindings
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
cwtch.im/cwtch v0.19.0
|
||||
cwtch.im/cwtch v0.27.0
|
||||
git.openprivacy.ca/cwtch.im/server v1.4.5
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.8.6
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.11.0
|
||||
git.openprivacy.ca/openprivacy/log v1.0.3
|
||||
github.com/mutecomm/go-sqlcipher/v4 v4.4.2
|
||||
)
|
||||
|
@ -13,13 +13,13 @@ require (
|
|||
require (
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
git.openprivacy.ca/cwtch.im/tapir v0.6.0 // indirect
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4 // indirect
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.5 // indirect
|
||||
github.com/gtank/merlin v0.1.1 // indirect
|
||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c // indirect
|
||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect
|
||||
golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c // indirect
|
||||
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
|
||||
|
|
14
go.sum
14
go.sum
|
@ -1,6 +1,6 @@
|
|||
cwtch.im/cwtch v0.18.0/go.mod h1:StheazFFY7PKqBbEyDVLhzWW6WOat41zV0ckC240c5Y=
|
||||
cwtch.im/cwtch v0.19.0 h1:s5YkU3od1ZJB+8OXoJAy8vjgi6lkIVhbdkXIE8V+iZY=
|
||||
cwtch.im/cwtch v0.19.0/go.mod h1:h8S7EgEM+8pE1k+XLB5jAFdIPlOzwoXEY0GH5mQye5A=
|
||||
cwtch.im/cwtch v0.27.0 h1:MkNIZ+pT5ZwiGlKHk20GMUTlzBsmCWJEibbj6hr+WHw=
|
||||
cwtch.im/cwtch v0.27.0/go.mod h1:A3i92aFuhyHI2DYO2Qnvl5iqEw0Cox22pdiypHnMOy4=
|
||||
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
|
@ -9,10 +9,12 @@ git.openprivacy.ca/cwtch.im/server v1.4.5/go.mod h1:dGB1bePUgDU9xwk7gGkioNeshrbN
|
|||
git.openprivacy.ca/cwtch.im/tapir v0.5.5/go.mod h1:bWWHrDYBtHvxMri59RwIB/w7Eg1aC0BrQ/ycKlnbB5k=
|
||||
git.openprivacy.ca/cwtch.im/tapir v0.6.0 h1:TtnKjxitkIDMM7Qn0n/u+mOHRLJzuQUYjYRu5n0/QFY=
|
||||
git.openprivacy.ca/cwtch.im/tapir v0.6.0/go.mod h1:iQIq4y7N+DuP3CxyG66WNEC/d6vzh+wXvvOmelB+KoY=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.8.6 h1:g74PyDGvpMZ3+K0dXy3mlTJh+e0rcwNk0XF8owzkmOA=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.5 h1:DJs5gqw3SkvLSgRDvroqJxZ7F+YsbxbBRg5t0rU5gYE=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.5/go.mod h1:fwdeq6RO08WDkV0k7HfArsjRvurVULoUQmT//iaABZM=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.8.6/go.mod h1:Hn1gpOx/bRZp5wvCtPQVJPXrfeUH0EGiG/Aoa0vjGLg=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.11.0 h1:roASjaFtQLu+HdH5fa2wx6F00NL3YsUTlmXBJh8aLZk=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.11.0/go.mod h1:OQO1+7OIz/jLxDrorEMzvZA6SEbpbDyLGpjoFqT3z1Y=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
|
@ -90,8 +92,8 @@ golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c h1:Gk61ECugwEHL6IiyyNLXNzmu8XslmRP2dS0xjIYhbb4=
|
||||
golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
|
||||
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda h1:O+EUvnBNPwI4eLthn8W5K+cS8zQZfgTABPLNm6Bna34=
|
||||
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
|
|
27
quality.sh
27
quality.sh
|
@ -3,22 +3,29 @@
|
|||
echo "Checking code quality (you want to see no output here)"
|
||||
echo ""
|
||||
|
||||
echo "Vetting:"
|
||||
go vet generate/*
|
||||
|
||||
echo ""
|
||||
echo "Linting:"
|
||||
echo "Running staticcheck..."
|
||||
|
||||
staticcheck ./generate
|
||||
staticcheck lib.go
|
||||
staticcheck ./utils/*
|
||||
|
||||
# In the future we should remove include-pkgs. However, there are a few false positives in the overall go stdlib that make this
|
||||
# too noisy right now, specifically assigning nil to initialize slices (safe), and using go internal context channels assigned
|
||||
# nil (also safe).
|
||||
# We also have one file infinite_channel.go written in a way that static analysis cannot reason about easily. So it is explictly
|
||||
# ignored.
|
||||
echo "Running nilaway..."
|
||||
nilaway -include-pkgs="git.openprivacy.ca/cwtch.im/cwtch-autobindings,cwtch.im/cwtch,cwtch.im/tapir,git.openprivacy.ca/openprivacy/connectivity" -exclude-errors-in-files="./templates/lib_template.go" -exclude-file-docstrings="nolint:nilaway" lib.go
|
||||
nilaway -include-pkgs="git.openprivacy.ca/cwtch.im/cwtch-autobindings,cwtch.im/cwtch,cwtch.im/tapir,git.openprivacy.ca/openprivacy/connectivity" -exclude-errors-in-files="./templates/lib_template.go" -exclude-file-docstrings="nolint:nilaway" ./utils/
|
||||
|
||||
echo "Time to format"
|
||||
gofmt -l -s -w .
|
||||
gofmt -l -s -w ./utils/*
|
||||
gofmt -l -s -w ./generate/*
|
||||
|
||||
# ineffassign (https://github.com/gordonklaus/ineffassign)
|
||||
echo "Checking for ineffectual assignment of errors (unchecked errors...)"
|
||||
ineffassign .
|
||||
# echo "Checking for ineffectual assignment of errors (unchecked errors...)"
|
||||
# ineffassign .
|
||||
|
||||
# misspell (https://github.com/client9/misspell/cmd/misspell)
|
||||
echo "Checking for misspelled words..."
|
||||
misspell . | grep -v "testing/" | grep -v "vendor/" | grep -v "go.sum" | grep -v ".idea"
|
||||
# echo "Checking for misspelled words..."
|
||||
# misspell . | grep -v "testing/" | grep -v "vendor/" | grep -v "go.sum" | grep -v ".idea"
|
||||
|
|
26
spec
26
spec
|
@ -3,6 +3,7 @@
|
|||
# Peer Engine
|
||||
app ActivatePeerEngine profile
|
||||
app DeactivatePeerEngine profile
|
||||
app ConfigureConnections profile bool:listen bool:peers bool:servers
|
||||
|
||||
# Profile Management
|
||||
app CreateProfile name password bool:autostart
|
||||
|
@ -19,17 +20,32 @@ profile AcceptConversation conversation
|
|||
profile BlockConversation conversation
|
||||
profile UnblockConversation conversation
|
||||
profile DeleteConversation conversation
|
||||
profile PeerWithOnion string:handle
|
||||
profile DisconnectFromPeer string:handle
|
||||
(json-err)profile EnhancedGetConversationAccessControlList conversation
|
||||
profile EnhancedUpdateConversationAccessControlList conversation string:json
|
||||
|
||||
# Search
|
||||
(json)profile SearchConversations string:pattern
|
||||
|
||||
# Message Management
|
||||
(json)profile EnhancedSendMessage conversation string:msg
|
||||
(json)profile EnhancedGetMessageById conversation message
|
||||
(json)profile EnhancedGetMessageByContentHash conversation string:contentHash
|
||||
(json)profile EnhancedGetMessages conversation int:index int:count
|
||||
# (json)profile SendInviteToConversation conversation conversation:target
|
||||
(json)profile EnhancedGetMessages conversation int:index uint:count
|
||||
(json)profile EnhancedSendInviteMessage conversation conversation:target
|
||||
profile UpdateMessageAttribute conversation channel message string:attributeKey string:attributeValue
|
||||
|
||||
# Group Management
|
||||
profile StartGroup string:name string:server
|
||||
profile QueueJoinServer string:handle
|
||||
profile DisconnectFromServer string:handle
|
||||
|
||||
## Server List Management...
|
||||
import "cwtch.im/cwtch/functionality/servers"
|
||||
@profile-experiment PublishServerUpdate servers
|
||||
@profile-experiment GetServerInfoList servers
|
||||
@profile-experiment DeleteServerInfo servers string:serverOnion
|
||||
|
||||
# Filesharing Management
|
||||
import "cwtch.im/cwtch/functionality/filesharing"
|
||||
|
@ -37,14 +53,14 @@ import "cwtch.im/cwtch/functionality/filesharing"
|
|||
@profile-experiment RestartFileShare filesharing string:filekey
|
||||
@profile-experiment StopFileShare filesharing string:filekey
|
||||
@profile-experiment CheckDownloadStatus filesharing string:filekey
|
||||
@profile-experiment VerifyOrResumeDownload filesharing conversation string:filekey
|
||||
@profile-experiment VerifyOrResumeDownloadDefaultLimit filesharing conversation string:filekey
|
||||
@(json)profile-experiment EnhancedShareFile filesharing conversation string:filepath
|
||||
@(json)profile-experiment EnhancedGetSharedFiles filesharing conversation
|
||||
|
||||
|
||||
# Server Hosting Experiment
|
||||
!serverExperiment import "git.openprivacy.ca/cwtch.im/cwtch-autobindings/experiments/servers"
|
||||
!serverExperiment global serverExperiment *servers.ServersFunctionality servers
|
||||
!serverExperiment import "git.openprivacy.ca/cwtch.im/cwtch-autobindings/experiments/server_hosting"
|
||||
!serverExperiment global serverExperiment *server_hosting.ServersFunctionality server_hosting
|
||||
!serverExperiment exp CreateServer application password string:description bool:autostart
|
||||
!serverExperiment exp SetServerAttribute application string:handle string:key string:val
|
||||
!serverExperiment exp LoadServers application acn password
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/model/attr"
|
||||
"cwtch.im/cwtch/model/constants"
|
||||
"cwtch.im/cwtch/settings"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/cwtch.im/cwtch-autobindings/utils"
|
||||
|
@ -19,7 +20,9 @@ import (
|
|||
"os/user"
|
||||
path "path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"strconv"
|
||||
"time"
|
||||
mrand "math/rand"
|
||||
"crypto/rand"
|
||||
|
@ -28,6 +31,7 @@ import (
|
|||
_ "github.com/mutecomm/go-sqlcipher/v4"
|
||||
"cwtch.im/cwtch/app"
|
||||
"git.openprivacy.ca/openprivacy/connectivity"
|
||||
"sync"
|
||||
|
||||
{{IMPORTS}}
|
||||
)
|
||||
|
@ -149,7 +153,7 @@ func _startCwtch(appDir string, torPath string) {
|
|||
}
|
||||
|
||||
log.Infof("Loading Cwtch Directory %v and tor path: %v", appDir, torPath)
|
||||
app.InitGlobalSettingsFile(appDir, app.DefactoPasswordForUnencryptedProfiles)
|
||||
settings.InitGlobalSettingsFile(appDir, app.DefactoPasswordForUnencryptedProfiles)
|
||||
|
||||
log.Infof("making directory %v", appDir)
|
||||
err = os.MkdirAll(path.Join(appDir, "tor"), 0700)
|
||||
|
@ -164,18 +168,14 @@ func _startCwtch(appDir string, torPath string) {
|
|||
globalAppDir = appDir
|
||||
globalTorPath = torPath
|
||||
settingsFile := app.LoadAppSettings(appDir)
|
||||
newACN, settings := buildACN(settingsFile.ReadGlobalSettings(), globalTorPath, globalAppDir)
|
||||
globalACN = connectivity.NewProxyACN(newACN)
|
||||
settingsFile.WriteGlobalSettings(settings)
|
||||
// start with an Error ACN
|
||||
erracn := connectivity.NewErrorACN(fmt.Errorf("initializing tor"))
|
||||
globalACN = connectivity.NewProxyACN(&erracn)
|
||||
application = app.NewApp(&globalACN, appDir, settingsFile)
|
||||
|
||||
// Subscribe to all App Events...
|
||||
eventHandler.HandleApp(application)
|
||||
|
||||
// Settings may have changed...
|
||||
settings = settings
|
||||
settingsJson, _ := json.Marshal(settings)
|
||||
|
||||
// FIXME: This code exists to allow the Splash Screen test in the new UI integration tests to pass
|
||||
// it doesn't actually fix the problem in theory, and we should get around to ensuring that application
|
||||
// is safe to access even if shutdown is called concurrently...
|
||||
|
@ -184,15 +184,19 @@ func _startCwtch(appDir string, torPath string) {
|
|||
return
|
||||
}
|
||||
// Send global settings to the UI...
|
||||
application.UpdateSettings(application.ReadSettings())
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(app.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
|
||||
globalSettings := application.ReadSettings()
|
||||
settingsJson, _ := json.Marshal(globalSettings)
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(settings.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
|
||||
log.Infof("libcwtch-go application launched")
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(app.CwtchStarted, map[event.Field]string{}))
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(settings.CwtchStarted, map[event.Field]string{}))
|
||||
application.QueryACNVersion()
|
||||
application.LoadProfiles(app.DefactoPasswordForUnencryptedProfiles)
|
||||
|
||||
{{EXPERIMENT_REGISTER}}
|
||||
|
||||
|
||||
// Finally attempt to set up a proper Tor
|
||||
// Note: ResetTor launches an internal goroutine so this is non-blocking...
|
||||
ResetTor()
|
||||
}
|
||||
|
||||
// the pointer returned from this function **must** be freed using c_Free
|
||||
|
@ -230,11 +234,20 @@ func ReconnectCwtchForeground() {
|
|||
return
|
||||
}
|
||||
|
||||
// TODO: Need To Repopulate UI
|
||||
// Repopulate the UI on Android by iterating through all loaded profiles and treating them as New Peer loads. The ReloadEvent field
|
||||
// suppresses any listen/connection actions and simply sends updated attributes to the UI.
|
||||
// The UI is designed in such a way that "NewPeer" events are treated as updates.
|
||||
// TODO: if/when we break apart NewPeer into smaller chunks for the UI to fetch, the need to do this here will go away.
|
||||
peerList := application.ListProfiles()
|
||||
for _, onion := range peerList {
|
||||
eventHandler.Push(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: onion, utils.ReloadEvent: event.True}))
|
||||
}
|
||||
|
||||
|
||||
|
||||
settingsJson, _ := json.Marshal(application.ReadSettings())
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(app.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(app.CwtchStarted, map[event.Field]string{}))
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(settings.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(settings.CwtchStarted, map[event.Field]string{utils.ReloadEvent: event.True}))
|
||||
application.QueryACNStatus()
|
||||
application.QueryACNVersion()
|
||||
}
|
||||
|
@ -287,12 +300,22 @@ func SetProfileAttribute(profileOnion string, key string, value string) {
|
|||
if profile != nil {
|
||||
zone, key := attr.ParseZone(key)
|
||||
|
||||
// TODO We only allow public.profile.key to be set for now.
|
||||
// TODO We only allow certain public.profile.key to be set for now.
|
||||
// All other scopes and zones need to be added explicitly or handled by Cwtch.
|
||||
if zone == attr.ProfileZone && key == constants.Name {
|
||||
profile.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name, value)
|
||||
} else if zone == attr.ProfileZone && key == constants.ProfileAttribute1 {
|
||||
profile.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.ProfileAttribute1, value)
|
||||
} else if zone == attr.ProfileZone && key == constants.ProfileAttribute2 {
|
||||
profile.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.ProfileAttribute2, value)
|
||||
} else if zone == attr.ProfileZone && key == constants.ProfileAttribute3 {
|
||||
profile.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.ProfileAttribute3, value)
|
||||
} else if zone == attr.ProfileZone && key == constants.ProfileStatus {
|
||||
profile.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.ProfileStatus, value)
|
||||
} else if zone == attr.ProfileZone && key == constants.PeerAutostart {
|
||||
profile.SetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.PeerAutostart, value)
|
||||
} else if zone == attr.ProfileZone && key == constants.PeerAppearOffline {
|
||||
profile.SetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.PeerAppearOffline, value)
|
||||
} else {
|
||||
log.Errorf("attempted to set an attribute with an unknown zone: %v", key)
|
||||
}
|
||||
|
@ -374,21 +397,41 @@ func c_ResetTor() {
|
|||
ResetTor()
|
||||
}
|
||||
|
||||
|
||||
var torLock sync.Mutex
|
||||
|
||||
func ResetTor() {
|
||||
log.Infof("Replacing ACN with new Tor...")
|
||||
settings := application.ReadSettings()
|
||||
go func() {
|
||||
// prevent concurrent calls to this method...
|
||||
torLock.Lock()
|
||||
defer torLock.Unlock()
|
||||
log.Infof("Replacing ACN with new Tor...")
|
||||
settings := application.ReadSettings()
|
||||
|
||||
globalACN.Close() // we need to close first if dateDir is the same, otherwise buildACN can't launch tor.
|
||||
newAcn, settings, err := buildACN(settings, globalTorPath, globalAppDir)
|
||||
// only update settings if successful.
|
||||
if err == nil {
|
||||
// Only update Tor specific settings...
|
||||
currentSettings := application.ReadSettings()
|
||||
currentSettings.TorCacheDir = settings.TorCacheDir
|
||||
currentSettings.CustomControlPort = settings.CustomControlPort
|
||||
currentSettings.CustomSocksPort = settings.CustomSocksPort
|
||||
currentSettings.CustomTorrc = settings.CustomTorrc
|
||||
application.UpdateSettings(currentSettings)
|
||||
|
||||
// We need to update settings on reset as buildACN can alter settings, otherwise the next reset will be broken...
|
||||
settings = application.ReadSettings()
|
||||
settingsJson, _ := json.Marshal(settings)
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
|
||||
}
|
||||
|
||||
globalACN.Close() // we need to close first if dateDir is the same, otherwise buildACN can't launch tor.
|
||||
newAcn, settings := buildACN(settings, globalTorPath, globalAppDir)
|
||||
application.UpdateSettings(settings)
|
||||
globalACN.ReplaceACN(newAcn)
|
||||
application.QueryACNVersion()
|
||||
|
||||
// We need to update settings on reset as buildACN can alter settings, otherwise the next reset will be broken...
|
||||
settings = application.ReadSettings()
|
||||
settingsJson, _ := json.Marshal(settings)
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
|
||||
log.Infof("Restarted")
|
||||
// replace ACN regardlesss
|
||||
globalACN.ReplaceACN(newAcn)
|
||||
application.QueryACNStatus()
|
||||
application.QueryACNVersion()
|
||||
log.Infof("Restarted")
|
||||
}()
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -397,9 +440,8 @@ const (
|
|||
UpdateGlobalSettings = event.Type("UpdateGlobalSettings")
|
||||
)
|
||||
|
||||
func buildACN(settings app.GlobalSettings, torPath string, appDir string) (connectivity.ACN, app.GlobalSettings) {
|
||||
func buildACN(globalSettings settings.GlobalSettings, torPath string, appDir string) (connectivity.ACN, settings.GlobalSettings, error) {
|
||||
|
||||
mrand.Seed(int64(time.Now().Nanosecond()))
|
||||
socksPort := mrand.Intn(1000) + 9600
|
||||
controlPort := socksPort + 1
|
||||
|
||||
|
@ -416,24 +458,58 @@ func buildACN(settings app.GlobalSettings, torPath string, appDir string) (conne
|
|||
if err != nil {
|
||||
log.Errorf("error creating tor data directory: %v. Aborting app start up", err)
|
||||
eventHandler.Push(event.NewEventList(CwtchStartError, event.Error, fmt.Sprintf("Error connecting to Tor: %v", err)))
|
||||
return &connectivity.ErrorACN{}, settings
|
||||
erracn := connectivity.NewErrorACN(err)
|
||||
return &erracn, globalSettings, err
|
||||
}
|
||||
|
||||
if settings.AllowAdvancedTorConfig {
|
||||
controlPort = settings.CustomControlPort
|
||||
socksPort = settings.CustomSocksPort
|
||||
if globalSettings.AllowAdvancedTorConfig {
|
||||
controlPort = globalSettings.CustomControlPort
|
||||
socksPort = globalSettings.CustomSocksPort
|
||||
}
|
||||
|
||||
// Override Ports if on Tails...
|
||||
if cwtchTails := os.Getenv("CWTCH_TAILS"); strings.ToLower(cwtchTails) == "true" {
|
||||
log.Infof("CWTCH_TAILS environment variable set... overriding tor config...")
|
||||
|
||||
controlPort = 9051
|
||||
|
||||
// In tails 5.13 the control port was changed to 951
|
||||
// so read the Tails Version File and if it exists...
|
||||
b, err := os.ReadFile("/etc/amnesia/version")
|
||||
if err == nil {
|
||||
// the file should start with the version followed
|
||||
// by a space...
|
||||
versionEnd := strings.Index(string(b), " ")
|
||||
versionStr := string(b)[:versionEnd]
|
||||
version, err := strconv.ParseFloat(versionStr, 64)
|
||||
if err == nil {
|
||||
log.Infof("Confirming Tails Version: %v", version)
|
||||
// assert the control port if we are at the dedicated version...
|
||||
// we know this change happened sometime after 5.11
|
||||
if version >= 5.13 {
|
||||
controlPort = 951
|
||||
}
|
||||
} else {
|
||||
log.Errorf("Unable to confirm Tails version. CWTCH_TAILS options may not function correctly.")
|
||||
}
|
||||
}
|
||||
|
||||
socksPort = 9050
|
||||
globalSettings.CustomControlPort = controlPort
|
||||
globalSettings.CustomSocksPort = socksPort
|
||||
globalSettings.AllowAdvancedTorConfig = true
|
||||
}
|
||||
|
||||
torrc := tor.NewTorrc().WithSocksPort(socksPort).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key))
|
||||
// torrc.WithLog(path.Join(appDir, "tor", "tor.log"), tor.TorLogLevelNotice)
|
||||
if settings.UseCustomTorrc {
|
||||
customTorrc := settings.CustomTorrc
|
||||
if globalSettings.UseCustomTorrc {
|
||||
customTorrc := globalSettings.CustomTorrc
|
||||
torrc.WithCustom(strings.Split(customTorrc, "\n"))
|
||||
} else {
|
||||
// Fallback to showing the freshly generated torrc for this session.
|
||||
settings.CustomTorrc = torrc.Preview()
|
||||
settings.CustomControlPort = controlPort
|
||||
settings.CustomSocksPort = socksPort
|
||||
globalSettings.CustomTorrc = torrc.Preview()
|
||||
globalSettings.CustomControlPort = controlPort
|
||||
globalSettings.CustomSocksPort = socksPort
|
||||
}
|
||||
|
||||
err = torrc.Build(path.Join(appDir, "tor", "torrc"))
|
||||
|
@ -441,11 +517,12 @@ func buildACN(settings app.GlobalSettings, torPath string, appDir string) (conne
|
|||
if err != nil {
|
||||
log.Errorf("error constructing torrc: %v", err)
|
||||
eventHandler.Push(event.NewEventList(CwtchStartError, event.Error, fmt.Sprintf("Error connecting to Tor: %v", err)))
|
||||
return &connectivity.ErrorACN{}, settings
|
||||
erracn := connectivity.NewErrorACN(err)
|
||||
return &erracn, globalSettings, err
|
||||
}
|
||||
|
||||
dataDir := settings.TorCacheDir
|
||||
if !settings.UseTorCache {
|
||||
dataDir := globalSettings.TorCacheDir
|
||||
if !globalSettings.UseTorCache {
|
||||
|
||||
// purge data dir directories if we are not using them for a cache
|
||||
torDir := path.Join(appDir, "tor")
|
||||
|
@ -461,20 +538,22 @@ func buildACN(settings app.GlobalSettings, torPath string, appDir string) (conne
|
|||
|
||||
if dataDir, err = os.MkdirTemp(torDir, "data-dir-"); err != nil {
|
||||
eventHandler.Push(event.NewEventList(CwtchStartError, event.Error, fmt.Sprintf("Error connecting to Tor: %v", err)))
|
||||
return &connectivity.ErrorACN{}, settings
|
||||
erracn := connectivity.NewErrorACN(err)
|
||||
return &erracn, globalSettings, err
|
||||
}
|
||||
}
|
||||
|
||||
// Persist Current Data Dir as Tor Cache...
|
||||
settings.TorCacheDir = dataDir
|
||||
globalSettings.TorCacheDir = dataDir
|
||||
|
||||
acn, err := tor.NewTorACNWithAuth(appDir, torPath, dataDir, controlPort, tor.HashedPasswordAuthenticator{Password: base64.StdEncoding.EncodeToString(key)})
|
||||
if err != nil {
|
||||
log.Errorf("Error connecting to Tor replacing with ErrorACN: %v\n", err)
|
||||
eventHandler.Push(event.NewEventList(CwtchStartError, event.Error, fmt.Sprintf("Error connecting to Tor: %v", err)))
|
||||
acn = &connectivity.ErrorACN{}
|
||||
erracn := connectivity.NewErrorACN(err)
|
||||
acn = &erracn
|
||||
}
|
||||
return acn, settings
|
||||
return acn, globalSettings, err
|
||||
}
|
||||
|
||||
|
||||
|
@ -485,12 +564,54 @@ func c_UpdateSettings(json_ptr *C.char, json_len C.int) {
|
|||
}
|
||||
|
||||
func UpdateSettings(settingsJson string) {
|
||||
var newSettings app.GlobalSettings
|
||||
var newSettings settings.GlobalSettings
|
||||
json.Unmarshal([]byte(settingsJson), &newSettings)
|
||||
application.UpdateSettings(newSettings)
|
||||
{{EXPERIMENT_UPDATESETTINGS}}
|
||||
}
|
||||
|
||||
//export c_GetDebugInfo
|
||||
func c_GetDebugInfo() *C.char {
|
||||
return C.CString(GetDebugInfo())
|
||||
}
|
||||
|
||||
type DebugInfo struct {
|
||||
BuildVersion string
|
||||
BuildDate string
|
||||
HeapAllocated float64
|
||||
HeapInUse float64
|
||||
HeapReleased float64
|
||||
HeapObjects uint64
|
||||
NumThreads uint64
|
||||
SystemMemory float64
|
||||
}
|
||||
|
||||
func GetDebugInfo() string {
|
||||
|
||||
var memstats runtime.MemStats
|
||||
runtime.ReadMemStats(&memstats)
|
||||
|
||||
const MegaByte = 1024.0 * 1024.0
|
||||
|
||||
debugInfo := new(DebugInfo)
|
||||
debugInfo.BuildVersion = buildVer
|
||||
debugInfo.BuildDate = buildDate
|
||||
debugInfo.HeapAllocated = float64(memstats.HeapAlloc) / MegaByte
|
||||
debugInfo.HeapObjects = memstats.HeapObjects
|
||||
debugInfo.NumThreads = uint64(runtime.NumGoroutine())
|
||||
debugInfo.HeapReleased = float64(memstats.HeapReleased) / MegaByte
|
||||
debugInfo.HeapInUse = float64(memstats.HeapInuse) / MegaByte
|
||||
debugInfo.SystemMemory = float64(memstats.Sys) / MegaByte
|
||||
|
||||
if os.Getenv("CWTCH_PROFILE") == "1" {
|
||||
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
||||
f, _ := os.Create("mem.prof")
|
||||
pprof.WriteHeapProfile(f)
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(debugInfo)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
{{BINDINGS}}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import "cwtch.im/cwtch/model"
|
|||
|
||||
type Contact struct {
|
||||
Name string `json:"name"`
|
||||
LocalName string `json:"localname"`
|
||||
Onion string `json:"onion"`
|
||||
Status string `json:"status"`
|
||||
Picture string `json:"picture"`
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/cwtch.im/cwtch-autobindings/experiments/servers"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"cwtch.im/cwtch/app"
|
||||
"cwtch.im/cwtch/app/plugins"
|
||||
"cwtch.im/cwtch/functionality/servers"
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/model/attr"
|
||||
"cwtch.im/cwtch/model/constants"
|
||||
"cwtch.im/cwtch/peer"
|
||||
"cwtch.im/cwtch/protocol/connections"
|
||||
"cwtch.im/cwtch/settings"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
constants2 "git.openprivacy.ca/cwtch.im/cwtch-autobindings/constants"
|
||||
"git.openprivacy.ca/cwtch.im/cwtch-autobindings/features/groups"
|
||||
"git.openprivacy.ca/cwtch.im/cwtch-autobindings/experiments/server_hosting"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"time"
|
||||
|
||||
|
@ -29,11 +29,6 @@ type EventProfileEnvelope struct {
|
|||
Profile string
|
||||
}
|
||||
|
||||
type LCG_API_Handler struct {
|
||||
LaunchServers func()
|
||||
StopServers func()
|
||||
}
|
||||
|
||||
type EventHandler struct {
|
||||
app app.Application
|
||||
appBusQueue event.Queue
|
||||
|
@ -45,6 +40,8 @@ type EventHandlerInterface interface {
|
|||
OnACNStatusEvent(appl app.Application, e *event.Event)
|
||||
}
|
||||
|
||||
const ReloadEvent = event.Field("Reload")
|
||||
|
||||
// We should be reading from profile events pretty quickly, but we make this buffer fairly large...
|
||||
const profileEventsBufferSize = 512
|
||||
|
||||
|
@ -66,12 +63,12 @@ func (eh *EventHandler) HandleApp(application app.Application) {
|
|||
application.GetPrimaryBus().Subscribe(event.AppError, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(event.ACNStatus, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(event.ACNVersion, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(app.UpdateGlobalSettings, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(app.CwtchStarted, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(servers.NewServer, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(servers.ServerIntentUpdate, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(servers.ServerDeleted, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(servers.ServerStatsUpdate, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(settings.UpdateGlobalSettings, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(settings.CwtchStarted, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(server_hosting.NewServer, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(server_hosting.ServerIntentUpdate, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(server_hosting.ServerDeleted, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(server_hosting.ServerStatsUpdate, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(event.StartingStorageMiragtion, eh.appBusQueue)
|
||||
application.GetPrimaryBus().Subscribe(event.DoneStorageMigration, eh.appBusQueue)
|
||||
}
|
||||
|
@ -105,30 +102,7 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
if err != nil {
|
||||
break
|
||||
}
|
||||
if newAcnStatus == 100 {
|
||||
if acnStatus != 100 {
|
||||
for _, onion := range eh.app.ListProfiles() {
|
||||
profile := eh.app.GetPeer(onion)
|
||||
if profile != nil {
|
||||
autostart, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants2.PeerAutostart)
|
||||
if !exists || autostart == "true" {
|
||||
eh.app.ActivatePeerEngine(onion)
|
||||
}
|
||||
}
|
||||
}
|
||||
//eh.api.LaunchServers()
|
||||
}
|
||||
} else {
|
||||
if acnStatus == 100 {
|
||||
// just fell offline
|
||||
for _, onion := range eh.app.ListProfiles() {
|
||||
eh.app.DeactivatePeerEngine(onion)
|
||||
}
|
||||
//eh.api.StopServers()
|
||||
}
|
||||
}
|
||||
acnStatus = newAcnStatus
|
||||
|
||||
for _, module := range eh.modules {
|
||||
module.OnACNStatusEvent(eh.app, e)
|
||||
}
|
||||
|
@ -142,7 +116,7 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
}
|
||||
log.Debug("New Peer Event: %v", e)
|
||||
|
||||
if e.Data["Reload"] != event.True {
|
||||
if e.Data[ReloadEvent] != event.True {
|
||||
eh.startHandlingPeer(onion)
|
||||
}
|
||||
|
||||
|
@ -171,9 +145,15 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
|
||||
// Start up the Profile
|
||||
if acnStatus == 100 {
|
||||
autostart, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants2.PeerAutostart)
|
||||
autostart, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.PeerAutostart)
|
||||
appearOffline, appearOfflineExists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.PeerAppearOffline)
|
||||
if !exists || autostart == "true" {
|
||||
eh.app.ActivatePeerEngine(onion)
|
||||
if appearOfflineExists && appearOffline == "true" {
|
||||
// don't configure any connections...
|
||||
} else {
|
||||
eh.app.ConfigureConnections(onion, true, true, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,6 +167,13 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
}
|
||||
e.Data["autostart"] = autostart
|
||||
|
||||
appearOffline, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.PeerAppearOffline)
|
||||
// legacy profiles should not appearOffline by default
|
||||
if !exists {
|
||||
appearOffline = "false"
|
||||
}
|
||||
e.Data["appearOffline"] = appearOffline
|
||||
|
||||
// Name always exists
|
||||
e.Data[constants.Name], _ = profile.GetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||
e.Data[constants2.DefaultProfilePicture] = RandomProfileImage(onion)
|
||||
|
@ -199,7 +186,7 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
}
|
||||
// Construct our conversations and our srever lists
|
||||
var contacts []Contact
|
||||
var servers []groups.Server
|
||||
var knownServers []servers.Server
|
||||
|
||||
conversations, err := profile.FetchConversations()
|
||||
|
||||
|
@ -211,20 +198,23 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
// has been disabled and then is later re-enabled. As such we need to ensure that this list is
|
||||
// re-fetched when the group experiment is enabled via a dedicated ListServerInfo event...
|
||||
if conversationInfo.IsServer() {
|
||||
groupHandler, err := groups.ExperimentGate(eh.app.ReadSettings().Experiments)
|
||||
groupHandler := servers.FunctionalityGate()
|
||||
if err == nil {
|
||||
servers = append(servers, groupHandler.GetServerInfo(conversationInfo.Handle, profile))
|
||||
serverInfo, _ := groupHandler.GetServerInfo(profile, conversationInfo.Handle)
|
||||
knownServers = append(knownServers, serverInfo)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Prefer local override to public name...
|
||||
name, exists := conversationInfo.GetAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
||||
localname, exists := conversationInfo.GetAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
||||
if !exists {
|
||||
name, exists = conversationInfo.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||
if !exists {
|
||||
name = conversationInfo.Handle
|
||||
}
|
||||
localname = ""
|
||||
}
|
||||
|
||||
name, exists := conversationInfo.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||
if !exists {
|
||||
name = conversationInfo.Handle
|
||||
}
|
||||
|
||||
// Resolve the profile image of the contact
|
||||
|
@ -234,7 +224,7 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
cpicPath = RandomGroupImage(conversationInfo.Handle)
|
||||
defaultPath = RandomGroupImage(conversationInfo.Handle)
|
||||
} else {
|
||||
cpicPath = GetProfileImage(profile, conversationInfo, settings.DownloadPath)
|
||||
cpicPath = eh.GetProfileImage(profile, conversationInfo, settings.DownloadPath)
|
||||
defaultPath = RandomProfileImage(conversationInfo.Handle)
|
||||
}
|
||||
|
||||
|
@ -305,6 +295,7 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
|
||||
contacts = append(contacts, Contact{
|
||||
Name: name,
|
||||
LocalName: localname,
|
||||
Identifier: conversationInfo.ID,
|
||||
Onion: conversationInfo.Handle,
|
||||
Status: connections.ConnectionStateName[state],
|
||||
|
@ -331,8 +322,8 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
e.Data["ContactsJson"] = string(bytes)
|
||||
|
||||
// Marshal the server list into the new peer event...
|
||||
serversListBytes, _ := json.Marshal(servers)
|
||||
e.Data[groups.ServerList] = string(serversListBytes)
|
||||
serversListBytes, _ := json.Marshal(knownServers)
|
||||
e.Data[servers.ServerList] = string(serversListBytes)
|
||||
|
||||
log.Debugf("contactsJson %v", e.Data["ContactsJson"])
|
||||
}
|
||||
|
@ -342,15 +333,17 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
return string(json)
|
||||
}
|
||||
|
||||
func GetProfileImage(profile peer.CwtchPeer, conversationInfo *model.Conversation, basepath string) string {
|
||||
fileKey, err := profile.GetConversationAttribute(conversationInfo.ID, attr.PublicScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.CustomProfileImageKey)))
|
||||
if err == nil {
|
||||
if value, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.complete", fileKey)); exists && value == event.True {
|
||||
fp, _ := filesharing.GenerateDownloadPath(basepath, fileKey, true)
|
||||
// check if the file exists...if it does then set the path...
|
||||
if _, err := os.Stat(fp); err == nil {
|
||||
image, _ := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.path", fileKey))
|
||||
return image
|
||||
func (eh *EventHandler) GetProfileImage(profile peer.CwtchPeer, conversationInfo *model.Conversation, basepath string) string {
|
||||
if eh.app.IsFeatureEnabled(constants.ImagePreviewsExperiment) {
|
||||
fileKey, err := profile.GetConversationAttribute(conversationInfo.ID, attr.PublicScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.CustomProfileImageKey)))
|
||||
if err == nil {
|
||||
if value, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.complete", fileKey)); exists && value == event.True {
|
||||
fp, _ := filesharing.GenerateDownloadPath(basepath, fileKey, true)
|
||||
// check if the file exists...if it does then set the path...
|
||||
if _, err := os.Stat(fp); err == nil {
|
||||
image, _ := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.path", fileKey))
|
||||
return image
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,6 +359,10 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
|||
log.Errorf("eh.app == nil in handleProfileEvent... this shouldnt happen?")
|
||||
} else {
|
||||
profile := eh.app.GetPeer(ev.Profile)
|
||||
if profile == nil {
|
||||
log.Errorf("something has gone very wrong. profile is nil in handleProfileEvent")
|
||||
return ""
|
||||
}
|
||||
log.Debugf("New Profile Event to Handle: %v", ev)
|
||||
switch ev.Event.EventType {
|
||||
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
|
||||
|
@ -375,7 +372,7 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
|||
if ci != nil && err == nil {
|
||||
ev.Event.Data[event.ConversationID] = strconv.Itoa(ci.ID)
|
||||
profile.SetConversationAttribute(ci.ID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants2.Archived)), event.False)
|
||||
ev.Event.Data[constants2.Picture] = GetProfileImage(profile, ci, eh.app.ReadSettings().DownloadPath)
|
||||
ev.Event.Data[constants2.Picture] = eh.GetProfileImage(profile, ci, eh.app.ReadSettings().DownloadPath)
|
||||
} else {
|
||||
// TODO This Conversation May Not Exist Yet...But we are not in charge of creating it...
|
||||
log.Errorf("todo wait for contact to be added before processing this event...")
|
||||
|
@ -387,16 +384,9 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
|||
ev.Event.Data["Nick"], exists = ci.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||
if !exists {
|
||||
ev.Event.Data["Nick"] = ev.Event.Data["RemotePeer"]
|
||||
// If we dont have a name val for a peer, but they have sent us a message, we might be approved now, re-ask
|
||||
profile.SendScopedZonedGetValToContact(ci.ID, attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||
profile.SendScopedZonedGetValToContact(ci.ID, attr.PublicScope, attr.ProfileZone, constants.CustomProfileImageKey)
|
||||
}
|
||||
}
|
||||
|
||||
if ci.Accepted {
|
||||
handleImagePreviews(profile, &ev.Event, ci.ID, ci.ID, eh.app.ReadSettings())
|
||||
}
|
||||
|
||||
ev.Event.Data["notification"] = string(determineNotification(ci, eh.app.ReadSettings()))
|
||||
case event.NewMessageFromGroup:
|
||||
// only needs contact nickname and picture, for displaying on popup notifications
|
||||
|
@ -411,17 +401,17 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
|||
ev.Event.Data["Nick"] = ev.Event.Data["RemotePeer"]
|
||||
}
|
||||
}
|
||||
ev.Event.Data[constants2.Picture] = GetProfileImage(profile, ci, eh.app.ReadSettings().DownloadPath)
|
||||
ev.Event.Data[constants2.Picture] = eh.GetProfileImage(profile, ci, eh.app.ReadSettings().DownloadPath)
|
||||
}
|
||||
|
||||
conversationID, _ := strconv.Atoi(ev.Event.Data[event.ConversationID])
|
||||
profile.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants2.Archived)), event.False)
|
||||
|
||||
if ci != nil && ci.Accepted {
|
||||
handleImagePreviews(profile, &ev.Event, conversationID, ci.ID, eh.app.ReadSettings())
|
||||
gci, err := profile.GetConversationInfo(conversationID)
|
||||
if err != nil {
|
||||
log.Errorf("new message from non-existant group: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
gci, _ := profile.GetConversationInfo(conversationID)
|
||||
groupServer := gci.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupServer)).ToString()]
|
||||
state := profile.GetPeerState(groupServer)
|
||||
// if syncing, don't flood with notifications
|
||||
|
@ -440,11 +430,13 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
|||
count, err := profile.GetChannelMessageCount(conversationID, 0)
|
||||
if err != nil {
|
||||
log.Errorf("error fetching channel message count %v %v", conversationID, err)
|
||||
break
|
||||
}
|
||||
|
||||
conversationInfo, err := profile.GetConversationInfo(conversationID)
|
||||
if err != nil {
|
||||
log.Errorf("error fetching conversation info for %v %v", conversationID, err)
|
||||
break
|
||||
}
|
||||
|
||||
blocked := constants.False
|
||||
|
@ -457,7 +449,11 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
|||
accepted = constants.True
|
||||
}
|
||||
|
||||
acl, _ := json.Marshal(conversationInfo.ACL)
|
||||
acl, err := json.Marshal(conversationInfo.ACL)
|
||||
if err != nil {
|
||||
log.Errorf("received invalid ACL in conversation: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
lastMessage, _ := profile.GetMostRecentMessages(conversationID, 0, 0, 1)
|
||||
ev.Event.Data["unread"] = strconv.Itoa(count) // if this is a new contact with messages attached then by-definition these are unread...
|
||||
|
@ -477,8 +473,16 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
|||
groupPic := RandomGroupImage(ev.Event.Data[event.GroupID])
|
||||
ev.Event.Data[constants2.Picture] = groupPic
|
||||
|
||||
conversationID, _ := strconv.Atoi(ev.Event.Data[event.ConversationID])
|
||||
conversationInfo, _ := profile.GetConversationInfo(conversationID)
|
||||
conversationID, err := strconv.Atoi(ev.Event.Data[event.ConversationID])
|
||||
if err != nil {
|
||||
log.Errorf("invalid conversation id recieved %v", err)
|
||||
break
|
||||
}
|
||||
conversationInfo, err := profile.GetConversationInfo(conversationID)
|
||||
if err != nil {
|
||||
log.Errorf("error fetching conversation info for %v %v", conversationID, err)
|
||||
break
|
||||
}
|
||||
acl, _ := json.Marshal(conversationInfo.ACL)
|
||||
ev.Event.Data["accessControlList"] = string(acl)
|
||||
case event.NewGroup:
|
||||
|
@ -489,111 +493,35 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
|||
groupPic := RandomGroupImage(invite.GroupID)
|
||||
ev.Event.Data[constants2.Picture] = groupPic
|
||||
|
||||
conversationID, _ := strconv.Atoi(ev.Event.Data[event.ConversationID])
|
||||
conversationInfo, _ := profile.GetConversationInfo(conversationID)
|
||||
acl, _ := json.Marshal(conversationInfo.ACL)
|
||||
conversationID, err := strconv.Atoi(ev.Event.Data[event.ConversationID])
|
||||
if err != nil {
|
||||
log.Errorf("invalid conversation id recieved %v", err)
|
||||
break
|
||||
}
|
||||
conversationInfo, err := profile.GetConversationInfo(conversationID)
|
||||
if err != nil {
|
||||
log.Errorf("error fetching conversation info for %v %v", conversationID, err)
|
||||
break
|
||||
}
|
||||
acl, err := json.Marshal(conversationInfo.ACL)
|
||||
ev.Event.Data["accessControlList"] = string(acl)
|
||||
if err != nil {
|
||||
log.Errorf("received invalid ACL in conversation: %v", err)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
log.Errorf("received a new group event which contained an invalid invite %v. this should never happen and likely means there is a bug in cwtch. Please file a ticket @ https://git.openprivacy.ca/cwtch.im/cwtch", err)
|
||||
return ""
|
||||
}
|
||||
case event.PeerStateChange:
|
||||
cxnState := connections.ConnectionStateToType()[ev.Event.Data[event.ConnectionState]]
|
||||
|
||||
// skip events the UI doesn't act on
|
||||
if cxnState == connections.CONNECTING || cxnState == connections.CONNECTED {
|
||||
return ""
|
||||
}
|
||||
|
||||
contact, err := profile.FetchConversationInfo(ev.Event.Data[event.RemotePeer])
|
||||
|
||||
if ev.Event.Data[event.RemotePeer] == profile.GetOnion() {
|
||||
return "" // suppress events from our own profile...
|
||||
}
|
||||
|
||||
// We do not know who this is...don't send any event until we see a message from them
|
||||
// (at that point the conversation will have been created...)
|
||||
if contact == nil || err != nil || contact.ID == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// if we already know this state, suppress
|
||||
if knownState, exists := contactStateCache[ev.Event.Data[event.RemotePeer]]; exists && cxnState == knownState {
|
||||
return ""
|
||||
}
|
||||
contactStateCache[ev.Event.Data[event.RemotePeer]] = cxnState
|
||||
|
||||
if contact != nil {
|
||||
// No enrichment needed
|
||||
if cxnState == connections.AUTHENTICATED {
|
||||
// if known and authed, get vars
|
||||
profile.SendScopedZonedGetValToContact(contact.ID, attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||
profile.SendScopedZonedGetValToContact(contact.ID, attr.PublicScope, attr.ProfileZone, constants.CustomProfileImageKey)
|
||||
}
|
||||
}
|
||||
case event.ServerStateChange:
|
||||
cxnState := connections.ConnectionStateToType()[ev.Event.Data[event.ConnectionState]]
|
||||
|
||||
// skip events the UI doesn't act on
|
||||
if cxnState == connections.CONNECTING || cxnState == connections.CONNECTED {
|
||||
return ""
|
||||
}
|
||||
|
||||
// if we already know this state, suppress
|
||||
if knownState, exists := contactStateCache[ev.Event.Data[event.RemotePeer]]; exists && cxnState == knownState {
|
||||
return ""
|
||||
}
|
||||
contactStateCache[ev.Event.Data[event.RemotePeer]] = cxnState
|
||||
|
||||
case event.NewRetValMessageFromPeer:
|
||||
// auto handled event means the setting is already done, we're just deciding if we need to tell the UI
|
||||
onion := ev.Event.Data[event.RemotePeer]
|
||||
scope := ev.Event.Data[event.Scope]
|
||||
path := ev.Event.Data[event.Path]
|
||||
val := ev.Event.Data[event.Data]
|
||||
exists, _ := strconv.ParseBool(ev.Event.Data[event.Exists])
|
||||
|
||||
conversation, err := profile.FetchConversationInfo(onion)
|
||||
if err == nil {
|
||||
|
||||
if exists && attr.IntoScope(scope) == attr.PublicScope {
|
||||
zone, path := attr.ParseZone(path)
|
||||
|
||||
// auto download profile images from contacts...
|
||||
settings := eh.app.ReadSettings()
|
||||
if settings.ExperimentsEnabled && zone == attr.ProfileZone && path == constants.CustomProfileImageKey {
|
||||
fileKey := val
|
||||
fsf, err := filesharing.FunctionalityGate(settings.Experiments)
|
||||
imagePreviewsEnabled := settings.Experiments["filesharing-images"]
|
||||
if err == nil && imagePreviewsEnabled && conversation.Accepted {
|
||||
|
||||
basepath := settings.DownloadPath
|
||||
fp, mp := filesharing.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...
|
||||
return ""
|
||||
} 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, val, constants.ImagePreviewMaxSizeInBytes)
|
||||
} else {
|
||||
// if image previews are disabled then ignore this event...
|
||||
return ""
|
||||
}
|
||||
}
|
||||
if val, err := profile.GetConversationAttribute(conversation.ID, attr.LocalScope.ConstructScopedZonedPath(zone.ConstructZonedPath(path))); err == nil || val != "" {
|
||||
// we have a locally set override, don't pass this remote set public scope update to UI
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
case event.TokenManagerInfo:
|
||||
conversations, err := profile.FetchConversations()
|
||||
if err == nil {
|
||||
|
@ -623,24 +551,6 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
|||
} else {
|
||||
profile.AllowUnknownConnections()
|
||||
}
|
||||
|
||||
// Now that the Peer Engine is Activated, Share Files
|
||||
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.Infof("Custom Profile Image: %v %s", key, serializedManifest)
|
||||
|
||||
}
|
||||
// If file sharing is enabled then reshare all active files...
|
||||
fsf, err := filesharing.FunctionalityGate(settings.Experiments)
|
||||
if err == nil {
|
||||
fsf.ReShareFiles(profile)
|
||||
info, _ := fsf.GetFileShareInfo(profile, key)
|
||||
log.Infof("Custom Profile Image: %v %v", key, info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -656,6 +566,10 @@ func unwrap(original *EventProfileEnvelope) *event.Event {
|
|||
|
||||
func (eh *EventHandler) startHandlingPeer(onion string) {
|
||||
eventBus := eh.app.GetEventBus(onion)
|
||||
if eventBus == nil {
|
||||
log.Errorf("could not start handling peer events .. event bus is nil")
|
||||
return
|
||||
}
|
||||
q := event.NewQueue()
|
||||
|
||||
eventBus.Subscribe(event.NetworkStatus, q)
|
||||
|
@ -673,8 +587,7 @@ func (eh *EventHandler) startHandlingPeer(onion string) {
|
|||
eventBus.Subscribe(event.NewGroup, q)
|
||||
eventBus.Subscribe(event.ServerStateChange, q)
|
||||
eventBus.Subscribe(event.PeerStateChange, q)
|
||||
eventBus.Subscribe(event.NewRetValMessageFromPeer, q)
|
||||
eventBus.Subscribe(event.ShareManifest, q)
|
||||
eventBus.Subscribe(event.UpdatedConversationAttribute, q)
|
||||
eventBus.Subscribe(event.ManifestSizeReceived, q)
|
||||
eventBus.Subscribe(event.ManifestError, q)
|
||||
eventBus.Subscribe(event.ManifestReceived, q)
|
||||
|
@ -683,6 +596,9 @@ func (eh *EventHandler) startHandlingPeer(onion string) {
|
|||
eventBus.Subscribe(event.FileDownloaded, q)
|
||||
eventBus.Subscribe(event.TokenManagerInfo, q)
|
||||
eventBus.Subscribe(event.ProtocolEngineCreated, q)
|
||||
eventBus.Subscribe(event.SearchResult, q)
|
||||
eventBus.Subscribe(event.SearchCancelled, q)
|
||||
eventBus.Subscribe(servers.UpdateServerInfo, q)
|
||||
go eh.forwardProfileMessages(onion, q)
|
||||
|
||||
}
|
||||
|
@ -718,46 +634,3 @@ func getLastMessageTime(conversationMessages []model.ConversationMessage) int {
|
|||
}
|
||||
return int(time.Unix())
|
||||
}
|
||||
|
||||
// handleImagePreviews checks settings and, if appropriate, auto-downloads any images
|
||||
func handleImagePreviews(profile peer.CwtchPeer, ev *event.Event, conversationID, senderID int, settings app.GlobalSettings) {
|
||||
fh, err := filesharing.PreviewFunctionalityGate(settings.Experiments)
|
||||
|
||||
// Short-circuit if file sharing is disabled
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Short-circuit failures
|
||||
// Don't autodownload images if the download path does not exist.
|
||||
if settings.DownloadPath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't autodownload images if the download path does not exist.
|
||||
if _, err := os.Stat(settings.DownloadPath); os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Now look at the image preview experiment
|
||||
imagePreviewsEnabled := settings.Experiments["filesharing-images"]
|
||||
if imagePreviewsEnabled {
|
||||
var cm model.MessageWrapper
|
||||
err := json.Unmarshal([]byte(ev.Data[event.Data]), &cm)
|
||||
if err == nil && cm.Overlay == model.OverlayFileSharing {
|
||||
var fm filesharing.OverlayMessage
|
||||
err = json.Unmarshal([]byte(cm.Data), &fm)
|
||||
if err == nil {
|
||||
if fm.ShouldAutoDL() {
|
||||
basepath := settings.DownloadPath
|
||||
fp, mp := filesharing.GenerateDownloadPath(basepath, fm.Name, false)
|
||||
log.Debugf("autodownloading file!")
|
||||
ev.Data["Auto"] = constants.True
|
||||
mID, _ := strconv.Atoi(ev.Data["Index"])
|
||||
profile.UpdateMessageAttribute(conversationID, 0, mID, constants.AttrDownloaded, constants.True)
|
||||
fh.DownloadFile(profile, senderID, fp, mp, fm.FileKey(), constants.ImagePreviewMaxSizeInBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/app"
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/model/attr"
|
||||
"cwtch.im/cwtch/settings"
|
||||
"git.openprivacy.ca/cwtch.im/cwtch-autobindings/constants"
|
||||
)
|
||||
|
||||
func determineNotification(ci *model.Conversation, settings app.GlobalSettings) constants.NotificationType {
|
||||
switch settings.NotificationPolicy {
|
||||
case app.NotificationPolicyMute:
|
||||
func determineNotification(ci *model.Conversation, gsettings settings.GlobalSettings) constants.NotificationType {
|
||||
switch gsettings.NotificationPolicy {
|
||||
case settings.NotificationPolicyMute:
|
||||
return constants.NotificationNone
|
||||
case app.NotificationPolicyOptIn:
|
||||
case settings.NotificationPolicyOptIn:
|
||||
if ci != nil {
|
||||
if policy, exists := ci.GetAttribute(attr.LocalScope, attr.ProfileZone, constants.ConversationNotificationPolicy); exists {
|
||||
switch policy {
|
||||
|
@ -20,12 +20,12 @@ func determineNotification(ci *model.Conversation, settings app.GlobalSettings)
|
|||
case constants.ConversationNotificationPolicyNever:
|
||||
return constants.NotificationNone
|
||||
case constants.ConversationNotificationPolicyOptIn:
|
||||
return notificationContentToNotificationType(settings.NotificationContent)
|
||||
return notificationContentToNotificationType(gsettings.NotificationContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return constants.NotificationNone
|
||||
case app.NotificationPolicyDefaultAll:
|
||||
case settings.NotificationPolicyDefaultAll:
|
||||
if ci != nil {
|
||||
if policy, exists := ci.GetAttribute(attr.LocalScope, attr.ProfileZone, constants.ConversationNotificationPolicy); exists {
|
||||
switch policy {
|
||||
|
@ -34,11 +34,11 @@ func determineNotification(ci *model.Conversation, settings app.GlobalSettings)
|
|||
case constants.ConversationNotificationPolicyDefault:
|
||||
fallthrough
|
||||
case constants.ConversationNotificationPolicyOptIn:
|
||||
return notificationContentToNotificationType(settings.NotificationContent)
|
||||
return notificationContentToNotificationType(gsettings.NotificationContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return notificationContentToNotificationType(settings.NotificationContent)
|
||||
return notificationContentToNotificationType(gsettings.NotificationContent)
|
||||
}
|
||||
return constants.NotificationNone
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue