Compare commits

...

80 Commits
v0.0.1 ... main

Author SHA1 Message Date
Dan Ballard 7a65efaa78 change to use new go-cross-compile container
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is pending Details
2024-03-05 09:38:14 -08:00
Sarah Jamie Lewis 83c84b2049
Add Reload Field to CwtchStarted event
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2024-02-26 18:01:46 -08:00
Sarah Jamie Lewis 6618410cc9
Upgrade Cwtch
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2024-02-26 09:28:36 -08:00
Sarah Jamie Lewis 89f19928c2
Load Correct Profile Image when ProfileImageExperiment is Disabled
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2024-02-23 15:22:12 -08:00
Sarah Jamie Lewis 243727a131 Fix Cwtch on Tails > 5.13
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2024-02-12 11:04:54 -08:00
Sarah Jamie Lewis dd0f1cd868 Upgrade Cwtch for eventbus dedupe fix
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is passing Details
2024-02-09 13:23:42 -08:00
Sarah Jamie Lewis 5bf954f3d0 Upgrade Cwtch
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build is passing Details
2024-02-09 13:07:30 -08:00
Sarah Jamie Lewis 425c3e6030 Expose new Enhanced Permission APIs
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2024-01-15 10:14:52 -08:00
Sarah Jamie Lewis 32ea146ec0 Upgrade Cwtch 2024-01-15 10:07:19 -08:00
Sarah Jamie Lewis 6321f09aad Improve Quality Scripts and Fix nilaway issues. 2024-01-15 10:07:19 -08:00
Dan Ballard 9e3fc6123b fix building mac amd64 build on m1 arm machines
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2024-01-05 18:11:05 -08:00
Sarah Jamie Lewis cbe687ce2e Try out minimal version flags on Mac Builds
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2024-01-05 21:11:54 +00:00
Sarah Jamie Lewis 6c0b2e2a4f Explicitly Use Bullseye for Linux Builds
continuous-integration/drone/push Build is passing Details
2024-01-03 20:52:51 +00:00
Sarah Jamie Lewis 7c042334d3 Update '.drone.yml'
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is passing Details
2024-01-03 17:55:51 +00:00
Sarah Jamie Lewis c7c2259c2d More .drone updates
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2024-01-02 16:09:07 -08:00
Sarah Jamie Lewis c2c3c2894d Upgade Cwtch, Allow Unsigned APIs.
continuous-integration/drone/pr Build is failing Details
Also update .drone
2024-01-02 16:06:38 -08:00
Sarah Jamie Lewis 7ddf09caec Expose PublishServerInfo
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is passing Details
2023-09-26 13:15:16 -07:00
Sarah Jamie Lewis 41a554a198 Upgrade Cwtch
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is passing Details
2023-09-25 18:37:46 +00:00
Dan Ballard b80db89284 Remove auto loadProfiles with default password from StartCwtch. Leave for apps to do
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is passing Details
2023-09-24 09:56:37 -07:00
Sarah Jamie Lewis 20dc170a78 Upgrade Cwtch
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is passing Details
2023-09-19 17:02:09 -07:00
Sarah Jamie Lewis 267e79368e Upgrade Cwtch, Log Errors for Experiments
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-09-19 13:05:08 -07:00
Sarah Jamie Lewis f93616615a Upgrade Cwtch
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is passing Details
2023-09-18 08:09:50 -07:00
Sarah Jamie Lewis 3e9c9c3b70 Use Constant from Cwtch
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is passing Details
2023-09-13 11:52:11 -07:00
Sarah Jamie Lewis f60fbfc3d5 Upgrade Cwtch. Support Disconnection and Appearing Offline 2023-09-13 11:50:13 -07:00
Sarah Jamie Lewis f32ad741e0 Hashes
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is passing Details
2023-08-31 11:56:41 -07:00
Sarah Jamie Lewis a453ca9dc8 Update Cwtch
continuous-integration/drone/pr Build is running Details
2023-08-31 11:55:02 -07:00
Sarah Jamie Lewis 3d2124126b Update
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is passing Details
2023-08-22 14:06:27 -07:00
Sarah Jamie Lewis 0f40f0d12d Upgrade Cwtch Expose DeleteServerInfo
continuous-integration/drone/pr Build is failing Details
2023-08-22 13:27:09 -07:00
Sarah Jamie Lewis 6e0623170f Fix up Spec and Makefile
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is passing Details
2023-08-02 13:12:53 -07:00
Sarah Jamie Lewis 6fdcf5bac7 Upgrade Cwtch
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is passing Details
2023-08-02 09:30:19 -07:00
Sarah Jamie Lewis ae18550f71 Upgrade Cwtch
continuous-integration/drone/pr Build is running Details
2023-07-27 11:09:54 -07:00
Sarah Jamie Lewis 988ec07ebc Correct Package name
continuous-integration/drone/pr Build is running Details
2023-07-25 14:54:59 -07:00
Sarah Jamie Lewis 44241e2352 Update Server Info
continuous-integration/drone/pr Build is running Details
2023-07-25 14:48:33 -07:00
Sarah Jamie Lewis 403398b7a1 Patch Makefile 2023-07-25 14:46:52 -07:00
Sarah Jamie Lewis 5916333723 Expose Search and new ServerList Functions 2023-07-25 14:46:09 -07:00
Dan Ballard 24b534d436 give makefile a concept of cross compiling, use conditionals to determin what it is, and if it can
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is pending Details
2023-07-24 07:59:07 -07:00
Dan Ballard c8692cc1b2 use op golang x arm container 2023-07-23 17:02:32 -07:00
Dan Ballard dcec0f9274 add linux arm64 target 2023-07-23 17:02:32 -07:00
Sarah Jamie Lewis 2e7a9be6c1 Hashes
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is passing Details
2023-07-13 19:54:33 +00:00
Sarah Jamie Lewis 7ebbfe91c9 Upgrade Cwtch 2023-07-13 19:54:33 +00:00
Sarah Jamie Lewis 3029ed65ff Add 'LICENSE'
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is pending Details
2023-06-16 21:26:36 +00:00
Sarah Jamie Lewis e91db69e6e Update 'README.md'
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is pending Details
2023-06-16 20:52:17 +00:00
Sarah Jamie Lewis 1efc334258 Update Cwtch to 0.20.8
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-06-13 10:50:15 -07:00
Sarah Jamie Lewis 4713f25755 propagate ACN status
continuous-integration/drone/pr Build is running Details
2023-06-13 10:05:47 -07:00
Sarah Jamie Lewis 6165b03b75 Initializing ErrorACN with an error
continuous-integration/drone/pr Build is running Details
2023-06-12 10:25:16 -07:00
Sarah Jamie Lewis a18d9f0d17 Propagate update to ErrorACN
continuous-integration/drone/pr Build is running Details
2023-06-12 10:17:02 -07:00
Sarah Jamie Lewis 2b67e4b9b3 Remove commented out code
continuous-integration/drone/pr Build is pending Details
2023-05-24 14:06:09 -07:00
Sarah Jamie Lewis 6f427d282b Much nicer Tor Handling
continuous-integration/drone/pr Build is pending Details
2023-05-24 14:05:47 -07:00
Sarah Jamie Lewis 1b07a195be Update connectivity. Delay tor connection until the end of setup
continuous-integration/drone/pr Build is running Details
2023-05-24 12:27:04 -07:00
Sarah Jamie Lewis faf15f26e2 Upgrade Cwtch
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-16 16:25:25 -07:00
Sarah Jamie Lewis 5b2f3cfb4b Expose Peer With Onion
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is passing Details
2023-05-09 13:30:10 -07:00
Sarah Jamie Lewis a1d9872738 Update Cwtch 2023-05-09 13:27:51 -07:00
Dan Ballard 20065b238d update cwtch; lcg half of moving acnStatus handler to cwtch
continuous-integration/drone/pr Build was killed Details
continuous-integration/drone/push Build is passing Details
2023-05-01 21:41:41 -05:00
Sarah Jamie Lewis ece1df0ce9 Upgrade Cwtch
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is passing Details
2023-04-21 18:49:23 -07:00
Sarah Jamie Lewis b2a43b9cca Add LocalName it Contact struct
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is passing Details
2023-04-20 15:37:06 -07:00
Sarah Jamie Lewis 110d58f521 Update Cwtch
continuous-integration/drone/pr Build is pending Details
2023-04-20 15:28:28 -07:00
Sarah Jamie Lewis 131e08ccee Suppress Duplicate Retval Events
continuous-integration/drone/pr Build is pending Details
2023-04-20 14:34:16 -07:00
Sarah Jamie Lewis d5278ffb47 Upgrade Cwtch
continuous-integration/drone/pr Build is running Details
2023-04-20 14:15:24 -07:00
Sarah Jamie Lewis e12afc9d3d Upgrade Cwtch
continuous-integration/drone/pr Build was killed Details
continuous-integration/drone/push Build is passing Details
2023-04-18 13:51:22 -07:00
Sarah Jamie Lewis f485e372a7 ReconnectCwtchForeground should reload Profiles
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is passing Details
2023-04-17 10:52:45 -07:00
Dan Ballard afed5c3703 add back in missing DebugInfo
continuous-integration/drone/pr Build was killed Details
continuous-integration/drone/push Build is passing Details
2023-04-13 12:38:54 -05:00
Sarah Jamie Lewis b7a4bc2a18 Upgrade Cwtch
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-05 19:46:23 -07:00
Sarah Jamie Lewis 0e05650439 Fix Mac Build
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-04-04 19:59:53 -07:00
Sarah Jamie Lewis 9d7ef2322e Spelling
continuous-integration/drone/pr Build was killed Details
continuous-integration/drone/push Build is failing Details
2023-04-04 19:53:59 -07:00
Sarah Jamie Lewis 700d41c5f0 Copy of README
continuous-integration/drone/pr Build is pending Details
2023-04-04 19:53:14 -07:00
Sarah Jamie Lewis b0eaf75f65 Upgrade Bine
continuous-integration/drone/pr Build is running Details
2023-04-04 19:45:32 -07:00
Sarah Jamie Lewis fac630dbaf Tails 2023-04-04 15:56:17 -07:00
Sarah Jamie Lewis a2603f243e Remove other nick request
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is pending Details
2023-04-04 14:35:30 -07:00
Sarah Jamie Lewis 8fe1e67312 Remove Code
continuous-integration/drone/pr Build is running Details
2023-04-04 14:07:09 -07:00
Sarah Jamie Lewis cd5d65611b Upgrade Cwtch
continuous-integration/drone/pr Build is running Details
2023-04-04 13:57:33 -07:00
Sarah Jamie Lewis 36557da9b8 Add Status 2023-04-04 13:56:18 -07:00
Sarah Jamie Lewis 86e7001ae4 Support Profile Attributes 2023-04-04 13:56:18 -07:00
Dan Ballard e5e4084fae add Make target for lib.go, def vars for experiments
continuous-integration/drone/pr Build was killed Details
continuous-integration/drone/push Build is pending Details
2023-03-18 13:34:08 -05:00
Sarah Jamie Lewis 50c853afb6 Update Cwtch
continuous-integration/drone/pr Build was killed Details
continuous-integration/drone/push Build is passing Details
2023-03-16 15:07:39 -07:00
Sarah Jamie Lewis a9d8266dc0 Fix Mac Releases
continuous-integration/drone/pr Build is pending Details
continuous-integration/drone/push Build is passing Details
2023-03-14 11:33:37 -07:00
Sarah Jamie Lewis 47c55b9393 Makefile and Build Fixups
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build was killed Details
2023-03-13 13:50:14 -07:00
Sarah Jamie Lewis 7e3e1f977a Autodownloads Experiment Moved to Cwtch + Better Build Dirs
continuous-integration/drone/pr Build is passing Details
2023-03-13 13:18:42 -07:00
Sarah Jamie Lewis 6dee3bc1bd Generate Bindings Args Review Comments
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is pending Details
2023-03-02 16:15:32 -08:00
Sarah Jamie Lewis 8fd4a81523 Nicer generate_bindings flags
continuous-integration/drone/pr Build is running Details
2023-03-02 15:50:20 -08:00
Sarah Jamie Lewis 6b64ee0553 Add Enhanced Send Invite to Spec
continuous-integration/drone/pr Build is running Details
continuous-integration/drone/push Build is passing Details
2023-03-02 12:45:44 -08:00
19 changed files with 514 additions and 485 deletions

View File

@ -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

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ libCwtch.so
libCwtch.dylib
libCwtch.dll
ios/
build/

9
LICENSE Normal file
View File

@ -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.

View File

@ -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

View File

@ -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).

View File

@ -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"

View File

@ -1,4 +1,4 @@
package servers
package server_hosting
import (
"cwtch.im/cwtch/app"

View File

@ -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}
}

View File

@ -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")
}
}

View File

@ -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"`
}

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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
View File

@ -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

View File

@ -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}}

View File

@ -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"`

View File

@ -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)
}
}
}
}
}

View File

@ -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
}