commit
0ecda3d3d5
@ -0,0 +1,11 @@
|
||||
.idea/
|
||||
lib.go
|
||||
templates/bindings.go
|
||||
templates/imports.go
|
||||
cwtch-sources.jar
|
||||
cwtch.aar
|
||||
libCwtch.h
|
||||
libCwtch.so
|
||||
libCwtch.dylib
|
||||
libCwtch.dll
|
||||
ios/
|
@ -0,0 +1,65 @@
|
||||
IOS_OUT := ./ios
|
||||
|
||||
.PHONY: all linux android windows macos clean ios
|
||||
|
||||
DEFAULT_GOAL: linux
|
||||
|
||||
all: linux android windows
|
||||
|
||||
linux: libCwtch.so
|
||||
|
||||
macos: libCwtch.x64.dylib libCwtch.arm64.dylib
|
||||
|
||||
android: cwtch.aar
|
||||
|
||||
windows: libCwtch.dll
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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)"
|
||||
|
||||
libCwtch.dll: lib.go
|
||||
./switch-ffi.sh
|
||||
# '-Xlinker --no-insert-timestamp` sets the output dll PE timestamp header to all zeros, instead of the actual time
|
||||
# this is necessary for reproducible builds (see: https://wiki.debian.org/ReproducibleBuilds/TimestampsInPEBinaries for additional information)
|
||||
# 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
|
||||
|
||||
clean:
|
||||
rm -f cwtch.aar cwtch_go.apk libCwtch.h libCwtch.so cwtch-sources.jar libCwtch.dll libCwtch.dylib
|
||||
|
||||
# iOS - for testing purposes only for now, not officially supported
|
||||
|
||||
ios-arm64:
|
||||
CGO_ENABLED=1 \
|
||||
GOOS=darwin \
|
||||
GOARCH=arm64 \
|
||||
SDK=iphoneos \
|
||||
CGO_CFLAGS="-fembed-bitcode" \
|
||||
CC=$(PWD)/clangwrap.sh \
|
||||
go build -buildmode=c-archive -tags ios -o $(IOS_OUT)/arm64.a .
|
||||
|
||||
ios-x86_64:
|
||||
CGO_ENABLED=1 \
|
||||
GOOS=darwin \
|
||||
GOARCH=amd64 \
|
||||
SDK=iphonesimulator \
|
||||
CC=$(PWD)/clangwrap.sh \
|
||||
go build -buildmode=c-archive -tags ios -o $(IOS_OUT)/x86_64.a .
|
||||
|
||||
ios: ios-arm64 ios-x86_64
|
||||
lipo $(IOS_OUT)/x86_64.a $(IOS_OUT)/arm64.a -create -output $(IOS_OUT)/cwtch.a
|
||||
cp $(IOS_OUT)/arm64.h $(IOS_OUT)/cwtch.h
|
@ -0,0 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"cwtch.im/cwtch/app"
|
||||
"cwtch.im/cwtch/event"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
mrand "math/rand"
|
||||
"os"
|
||||
path "path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
CwtchStarted = event.Type("CwtchStarted")
|
||||
CwtchStartError = event.Type("CwtchStartError")
|
||||
UpdateGlobalSettings = event.Type("UpdateGlobalSettings")
|
||||
)
|
||||
|
||||
func buildACN(settings app.GlobalSettings, torPath string, appDir string) (connectivity.ACN, app.GlobalSettings) {
|
||||
|
||||
mrand.Seed(int64(time.Now().Nanosecond()))
|
||||
socksPort := mrand.Intn(1000) + 9600
|
||||
controlPort := socksPort + 1
|
||||
|
||||
// generate a random password (actually random, stored in memory, for the control port)
|
||||
key := make([]byte, 64)
|
||||
_, err := rand.Read(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Infof("making directory %v", appDir)
|
||||
err = os.MkdirAll(path.Join(appDir, "tor"), 0700)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if settings.AllowAdvancedTorConfig {
|
||||
controlPort = settings.CustomControlPort
|
||||
socksPort = settings.CustomSocksPort
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
err = torrc.Build(path.Join(appDir, "tor", "torrc"))
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
dataDir := settings.TorCacheDir
|
||||
if !settings.UseTorCache {
|
||||
|
||||
// purge data dir directories if we are not using them for a cache
|
||||
torDir := path.Join(appDir, "tor")
|
||||
files, err := path.Glob(path.Join(torDir, "data-dir-*"))
|
||||
if err != nil {
|
||||
log.Errorf("could not construct filesystem glob: %v", err)
|
||||
}
|
||||
for _, f := range files {
|
||||
if err := os.RemoveAll(f); err != nil {
|
||||
log.Errorf("could not remove data-dir: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Persist Current Data Dir as Tor Cache...
|
||||
settings.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{}
|
||||
}
|
||||
return acn, settings
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"cwtch.im/cwtch/model/attr"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// TODO: At some point these functions should also be autogenerated
|
||||
|
||||
// Attribute is a struct to return the dual values of an attempt at a Get*Attribute API call, meant to be json serialized
|
||||
type Attribute struct {
|
||||
Exists bool
|
||||
Value string
|
||||
}
|
||||
|
||||
//export c_GetProfileAttribute
|
||||
func c_GetProfileAttribute(profile_ptr *C.char, profile_len C.int, key_ptr *C.char, key_len C.int) *C.char {
|
||||
profileOnion := C.GoStringN(profile_ptr, profile_len)
|
||||
key := C.GoStringN(key_ptr, key_len)
|
||||
return C.CString(GetProfileAttribute(profileOnion, key))
|
||||
}
|
||||
|
||||
// GetProfileAttribute provides a wrapper around profile.GetScopedZonedAttribute
|
||||
// Key must have the format zone.key where Zone is defined in Cwtch. Unknown zones are not permitted.
|
||||
// Currently forcing the Public Scope
|
||||
// Returns json of Attribute
|
||||
func GetProfileAttribute(profileOnion string, key string) string {
|
||||
profile := application.GetPeer(profileOnion)
|
||||
if profile != nil {
|
||||
zone, key := attr.ParseZone(key)
|
||||
|
||||
res, exists := profile.GetScopedZonedAttribute(attr.PublicScope, zone, key)
|
||||
attr := Attribute{exists, res}
|
||||
json, _ := json.Marshal(attr)
|
||||
return string(json)
|
||||
|
||||
}
|
||||
empty := Attribute{false, ""}
|
||||
json, _ := json.Marshal(empty)
|
||||
return (string(json))
|
||||
}
|
||||
|
||||
//export c_SetConversationAttribute
|
||||
func c_SetConversationAttribute(profile_ptr *C.char, profile_len C.int, conversation_id C.int, key_ptr *C.char, key_len C.int, val_ptr *C.char, val_len C.int) {
|
||||
profileOnion := C.GoStringN(profile_ptr, profile_len)
|
||||
key := C.GoStringN(key_ptr, key_len)
|
||||
value := C.GoStringN(val_ptr, val_len)
|
||||
SetConversationAttribute(profileOnion, int(conversation_id), key, value)
|
||||
}
|
||||
|
||||
// SetConversationAttribute provides a wrapper around profile.SetProfileAttribute
|
||||
// key is of format Zone.Key, and the API forces the Local Scope
|
||||
func SetConversationAttribute(profileOnion string, conversationID int, key string, value string) {
|
||||
profile := application.GetPeer(profileOnion)
|
||||
zone, key := attr.ParseZone(key)
|
||||
profile.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(zone.ConstructZonedPath(key)), value)
|
||||
}
|
||||
|
||||
//export c_GetConversationAttribute
|
||||
func c_GetConversationAttribute(profile_ptr *C.char, profile_len C.int, conversation_id C.int, key_ptr *C.char, key_len C.int) *C.char {
|
||||
profileOnion := C.GoStringN(profile_ptr, profile_len)
|
||||
key := C.GoStringN(key_ptr, key_len)
|
||||
return C.CString(GetConversationAttribute(profileOnion, int(conversation_id), key))
|
||||
}
|
||||
|
||||
// GetGonversationAttribute provides a wrapper around profile.GetGonversationAttribute
|
||||
// key is of format Scope.Zone.Key
|
||||
// Returns json of an Attribute
|
||||
func GetConversationAttribute(profileOnion string, conversationID int, key string) string {
|
||||
profile := application.GetPeer(profileOnion)
|
||||
if profile != nil {
|
||||
scope, zonekey := attr.ParseScope(key)
|
||||
zone, key := attr.ParseZone(zonekey)
|
||||
|
||||
res, err := profile.GetConversationAttribute(conversationID, scope.ConstructScopedZonedPath(zone.ConstructZonedPath(key)))
|
||||
attr := Attribute{err == nil, res}
|
||||
json, _ := json.Marshal(attr)
|
||||
return string(json)
|
||||
}
|
||||
empty := Attribute{false, ""}
|
||||
json, _ := json.Marshal(empty)
|
||||
return (string(json))
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package constants
|
||||
|
||||
const SchemaVersion = "schemaVersion"
|
||||
|
||||
const Name = "name"
|
||||
const LastRead = "last-read"
|
||||
const Picture = "picture"
|
||||
const DefaultProfilePicture = "defaultPicture"
|
||||
const ShowBlocked = "show-blocked"
|
||||
const Archived = "archived"
|
||||
const LastSeenTime = "lastMessageSeenTime"
|
||||
|
||||
const ProfileTypeV1DefaultPassword = "v1-defaultPassword"
|
||||
const ProfileTypeV1Password = "v1-userPassword"
|
||||
|
||||
// PeerOnline stores state on if the peer believes it is online
|
||||
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"
|
||||
|
||||
const StateProfilePane = "state-profile-pane"
|
||||
const StateSelectedConversation = "state-selected-conversation"
|
||||
const StateSelectedProfileTime = "state-selected-profile-time"
|
||||
|
||||
// Settings
|
||||
const BlockUnknownPeersSetting = "blockunknownpeers"
|
||||
const LocaleSetting = "locale"
|
||||
const ZoomSetting = "zoom"
|
||||
|
||||
// App Experiments
|
||||
const MessageFormattingExperiment = "message-formatting"
|
@ -0,0 +1,35 @@
|
||||
package constants
|
||||
|
||||
// We offer "un-passworded" profiles but our storage encrypts everything with a password. We need an agreed upon
|
||||
// password to use in that case, that the app case use behind the scenes to password and unlock with
|
||||
// https://docs.openprivacy.ca/cwtch-security-handbook/profile_encryption_and_storage.html
|
||||
const DefactoPasswordForUnencryptedProfiles = "be gay do crime"
|
||||
|
||||
const (
|
||||
// StatusSuccess is an event response for event.Status signifying a call succeeded
|
||||
StatusSuccess = "success"
|
||||
// StatusError is an event response for event.Status signifying a call failed in error, ideally accompanied by a event.Error
|
||||
StatusError = "error"
|
||||
)
|
||||
|
||||
type NotificationType string
|
||||
|
||||
const (
|
||||
// NotificationNone enum for message["notification"] that means no notification
|
||||
NotificationNone = NotificationType("None")
|
||||
// NotificationEvent enum for message["notification"] that means emit a notification that a message event happened only
|
||||
NotificationEvent = NotificationType("SimpleEvent")
|
||||
// NotificationConversation enum for message["notification"] that means emit a notification event with Conversation handle included
|
||||
NotificationConversation = NotificationType("ContactInfo")
|
||||
)
|
||||
|
||||
const (
|
||||
// ConversationNotificationPolicyDefault enum for conversations indicating to use global notification policy
|
||||
ConversationNotificationPolicyDefault = "ConversationNotificationPolicy.Default"
|
||||
// ConversationNotificationPolicyOptIn enum for conversation indicating to opt in to nofitications when allowed
|
||||
ConversationNotificationPolicyOptIn = "ConversationNotificationPolicy.OptIn"
|
||||
// ConversationNotificationPolicyNever enum for conversation indicating to opt in to never do notifications
|
||||
ConversationNotificationPolicyNever = "ConversationNotificationPolicy.Never"
|
||||
)
|
||||
|
||||
const DartIso8601 = "2006-01-02T15:04:05.999Z"
|
@ -0,0 +1,66 @@
|
||||
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/libcwtch-go/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}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
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")
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
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"`
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package features
|
||||
|
||||
import "errors"
|
||||
|
||||
// Response is a wrapper to better semantically convey the response type...
|
||||
type Response error
|
||||
|
||||
const errorSeparator = "."
|
||||
|
||||
// ConstructResponse is a helper function for creating Response structures.
|
||||
func ConstructResponse(prefix string, error string) Response {
|
||||
return errors.New(prefix + errorSeparator + error)
|
||||
}
|
@ -0,0 +1,220 @@
|
||||
package servers
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/event"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/cwtch.im/server"
|
||||
"git.openprivacy.ca/openprivacy/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const serversExperiment = "servers-experiment"
|
||||
|
||||
const (
|
||||
ZeroServersLoaded = event.Type("ZeroServersLoaded")
|
||||
NewServer = event.Type("NewServer")
|
||||
ServerIntentUpdate = event.Type("ServerIntentUpdate")
|
||||
ServerDeleted = event.Type("ServerDeleted")
|
||||
ServerStatsUpdate = event.Type("ServerStatsUpdate")
|
||||
)
|
||||
|
||||
const (
|
||||
Intent = event.Field("Intent")
|
||||
TotalMessages = event.Field("TotalMessages")
|
||||
Connections = event.Field("Connections")
|
||||
)
|
||||
|
||||
const (
|
||||
IntentRunning = "running"
|
||||
IntentStopped = "stopped"
|
||||
)
|
||||
|
||||
// TODO: move into Cwtch model/attr
|
||||
|
||||
type ServerInfo struct {
|
||||
Onion string
|
||||
ServerBundle string
|
||||
Autostart bool
|
||||
Running bool
|
||||
Description string
|
||||
StorageType string
|
||||
}
|
||||
|
||||
type PublishFn func(event.Event)
|
||||
|
||||
var lock sync.Mutex
|
||||
var appServers server.Servers
|
||||
var publishFn PublishFn
|
||||
var killStatsUpdate chan bool = make(chan bool, 1)
|
||||
var enabled bool = false
|
||||
|
||||
func InitServers(acn connectivity.ACN, appdir string, pfn PublishFn) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
if appServers == nil {
|
||||
serversDir := filepath.Join(appdir, "servers")
|
||||
err := os.MkdirAll(serversDir, 0700)
|
||||
if err != nil {
|
||||
log.Errorf("Could not init servers directory: %s", err)
|
||||
}
|
||||
appServers = server.NewServers(acn, serversDir)
|
||||
publishFn = pfn
|
||||
}
|
||||
}
|
||||
|
||||
func Disable() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
if appServers != nil {
|
||||
appServers.Stop()
|
||||
}
|
||||
if enabled {
|
||||
enabled = false
|
||||
killStatsUpdate <- true
|
||||
}
|
||||
}
|
||||
|
||||
func Enabled() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
return enabled
|
||||
}
|
||||
|
||||
// ServersFunctionality provides experiment gated server functionality
|
||||
type ServersFunctionality struct {
|
||||
}
|
||||
|
||||
// ExperimentGate returns ServersFunctionality if the experiment is enabled, and an error otherwise.
|
||||
func ExperimentGate(experimentMap map[string]bool) (*ServersFunctionality, error) {
|
||||
if experimentMap[serversExperiment] {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
return &ServersFunctionality{}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("gated by %v", serversExperiment)
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) Enable() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
if appServers != nil && !enabled {
|
||||
enabled = true
|
||||
go cacheForwardServerMetricUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) LoadServers(password string) ([]string, error) {
|
||||
servers, err := appServers.LoadServers(password)
|
||||
// server:1.3/libcwtch-go:1.4 accidentally enabled monitor logging by default. make sure it's turned off
|
||||
for _, onion := range servers {
|
||||
server := appServers.GetServer(onion)
|
||||
server.SetMonitorLogging(false)
|
||||
}
|
||||
return servers, err
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) CreateServer(password string) (server.Server, error) {
|
||||
return appServers.CreateServer(password)
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) GetServer(onion string) server.Server {
|
||||
return appServers.GetServer(onion)
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) GetServerStatistics(onion string) server.Statistics {
|
||||
s := appServers.GetServer(onion)
|
||||
if s != nil {
|
||||
return s.GetStatistics()
|
||||
}
|
||||
return server.Statistics{}
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) ListServers() []string {
|
||||
return appServers.ListServers()
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) DeleteServer(onion string, currentPassword string) error {
|
||||
return appServers.DeleteServer(onion, currentPassword)
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) LaunchServer(onion string) {
|
||||
appServers.LaunchServer(onion)
|
||||
server := appServers.GetServer(onion)
|
||||
if server != nil {
|
||||
newStats := server.GetStatistics()
|
||||
publishFn(event.NewEventList(ServerStatsUpdate, event.Identity, onion, TotalMessages, strconv.Itoa(newStats.TotalMessages), Connections, strconv.Itoa(newStats.TotalConnections)))
|
||||
}
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) StopServer(onion string) {
|
||||
appServers.StopServer(onion)
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) DestroyServers() {
|
||||
appServers.Destroy()
|
||||
}
|
||||
|
||||
func (sf *ServersFunctionality) GetServerInfo(onion string) *ServerInfo {
|
||||
s := sf.GetServer(onion)
|
||||
var serverInfo ServerInfo
|
||||
serverInfo.Onion = s.Onion()
|
||||
serverInfo.ServerBundle = s.ServerBundle()
|
||||
serverInfo.Autostart = s.GetAttribute(server.AttrAutostart) == "true"
|
||||
running, _ := s.CheckStatus()
|
||||
serverInfo.Running = running
|
||||
serverInfo.Description = s.GetAttribute(server.AttrDescription)
|
||||
serverInfo.StorageType = s.GetAttribute(server.AttrStorageType)
|
||||
return &serverInfo
|
||||
}
|
||||
|
||||
func (si *ServerInfo) EnrichEvent(e *event.Event) {
|
||||
e.Data["Onion"] = si.Onion
|
||||
e.Data["ServerBundle"] = si.ServerBundle
|
||||
e.Data["Description"] = si.Description
|
||||
e.Data["StorageType"] = si.StorageType
|
||||
if si.Autostart {
|
||||
e.Data["Autostart"] = "true"
|
||||
} else {
|
||||
e.Data["Autostart"] = "false"
|
||||
}
|
||||
if si.Running {
|
||||
e.Data["Running"] = "true"
|
||||
} else {
|
||||
e.Data["Running"] = "false"
|
||||
}
|
||||
}
|
||||
|
||||
// cacheForwardServerMetricUpdates every minute gets metrics for all servers, and if they have changed, sends events to the UI
|
||||
func cacheForwardServerMetricUpdates() {
|
||||
var cache map[string]server.Statistics = make(map[string]server.Statistics)
|
||||
duration := time.Second // allow first load
|
||||
for {
|
||||
select {
|
||||
case <-time.After(duration):
|
||||
duration = time.Minute
|
||||
serverList := appServers.ListServers()
|
||||
for _, serverOnion := range serverList {
|
||||
server := appServers.GetServer(serverOnion)
|
||||
if running, err := server.CheckStatus(); running && err == nil {
|
||||
newStats := server.GetStatistics()
|
||||
if stats, ok := cache[serverOnion]; !ok || stats.TotalConnections != newStats.TotalConnections || stats.TotalMessages != newStats.TotalMessages {
|
||||
cache[serverOnion] = newStats
|
||||
publishFn(event.NewEventList(ServerStatsUpdate, event.Identity, serverOnion, TotalMessages, strconv.Itoa(newStats.TotalMessages), Connections, strconv.Itoa(newStats.TotalConnections)))
|
||||
}
|
||||
}
|
||||
}
|
||||
case <-killStatsUpdate:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Shutdown() {
|
||||
Disable()
|
||||
appServers.Destroy()
|
||||
}
|
@ -0,0 +1,373 @@
|
||||
package main
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
generatedBindingsPrefix := ``
|
||||
generatedBindings := ``
|
||||
file, err := os.Open("spec")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
// optionally, resize scanner's capacity for lines over 64K, see next example
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if strings.HasPrefix(line, "#") || len(line) == 0 {
|
||||
// ignore
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.Split(line, " ")
|
||||
if len(parts) < 2 {
|
||||
fmt.Printf("all spec lines must start with a type prefix and a function name: %v\n", parts)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fType := parts[0]
|
||||
fName := parts[1]
|
||||
|
||||
if strings.HasPrefix(line, "import") {
|
||||
generatedBindingsPrefix += fName + "\n"
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("generating %v function for %v\n", fType, fName)
|
||||
|
||||
switch fType {
|
||||
|
||||
case "app":
|
||||
generatedBindings = generateAppFunction(generatedBindings, fName, parts[2:])
|
||||
case "profile":
|
||||
generatedBindings = generateProfileFunction(generatedBindings, fName, parts[2:])
|
||||
case "(json)profile":
|
||||
generatedBindings = generateJsonProfileFunction(generatedBindings, fName, parts[2:])
|
||||
case "@profile-experiment":
|
||||
experiment := parts[2]
|
||||
generatedBindings = generateExperimentalProfileFunction(generatedBindings, experiment, fName, parts[3:])
|
||||
case "@(json)profile-experiment":
|
||||
experiment := parts[2]
|
||||
generatedBindings = generateExperimentalJsonProfileFunction(generatedBindings, experiment, fName, parts[3:])
|
||||
default:
|
||||
fmt.Printf("unknown function type %v\n", parts)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%v\n", generatedBindings)
|
||||
os.WriteFile("templates/bindings.go", []byte(generatedBindings), 0644)
|
||||
os.WriteFile("templates/imports.go", []byte(generatedBindingsPrefix), 0644)
|
||||
|
||||
template, _ := os.ReadFile("templates/lib_template.go")
|
||||
templateString := string(template)
|
||||
templateString = strings.ReplaceAll(templateString, "{{BINDINGS}}", generatedBindings)
|
||||
templateString = strings.ReplaceAll(templateString, "{{IMPORTS}}", generatedBindingsPrefix)
|
||||
os.WriteFile("lib.go", []byte(templateString), 0644)
|
||||
}
|
||||
|
||||
var uniqueVarCounter = 0
|
||||
|
||||
func profileHandleArgPrototype() (string, string, string, string) {
|
||||
return `onion_ptr *C.char, onion_len C.int`, `C.GoStringN(onion_ptr, onion_len)`, `profile string`, "profile"
|
||||
}
|
||||
|
||||
func nameArgPrototype() (string, string, string, string) {
|
||||
return `name_ptr *C.char, name_len C.int`, `C.GoStringN(name_ptr, name_len)`, `name string`, "name"
|
||||
}
|
||||
|
||||
func passwordArgPrototype() (string, string, string, string) {
|
||||
return `password_ptr *C.char, password_len C.int`, `C.GoStringN(password_ptr, password_len)`, `password string`, "password"
|
||||
}
|
||||
|
||||
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 conversationArgPrototype(varName string) (string, string, string, string) {
|
||||
return intArgPrototype(varName)
|
||||
}
|
||||
|
||||
func channelArgPrototype() (string, string, string, string) {
|
||||
return intArgPrototype("channel_id")
|
||||
}
|
||||
|
||||
func messageArgPrototype() (string, string, string, string) {
|
||||
return intArgPrototype("message_id")
|
||||
}
|
||||
|
||||
func boolArgPrototype(name string) (string, string, string, string) {
|
||||
uniqueVarCounter += 1
|
||||
varName := fmt.Sprintf("%s%d", name, uniqueVarCounter)
|
||||
return fmt.Sprintf(`%s C.char`, ToSnakeCase(varName)), fmt.Sprintf(`%v == 1`, ToSnakeCase(varName)), fmt.Sprintf(`%v bool`, varName), varName
|
||||
}
|
||||
|
||||
func stringArgPrototype(name string) (string, string, string, string) {
|
||||
uniqueVarCounter += 1
|
||||
varName := fmt.Sprintf("%s%d", name, uniqueVarCounter)
|
||||
return fmt.Sprintf(`%s_ptr *C.char, %s_len C.int`, varName, varName), fmt.Sprintf(`C.GoStringN(%s_ptr, %s_len)`, varName, varName), fmt.Sprintf(`%v string`, varName), varName
|
||||
}
|
||||
|
||||
func mapArgs(argsTypes []string) (string, string, string, string) {
|
||||
var cArgs []string
|
||||
var c2GoArgs []string
|
||||
var goSpec []string
|
||||
var gUse []string
|
||||
for _, argSpec := range argsTypes {
|
||||
argTypeParts := strings.Split(argSpec, ":")
|
||||
argType := argTypeParts[0]
|
||||
switch argType {
|
||||
case "profile":
|
||||
c1, c2, c3, c4 := profileHandleArgPrototype()
|
||||
cArgs = append(cArgs, c1)
|
||||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
gUse = append(gUse, c4)
|
||||
case "name":
|
||||
c1, c2, c3, c4 := nameArgPrototype()
|
||||
cArgs = append(cArgs, c1)
|
||||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
gUse = append(gUse, c4)
|
||||
case "password":
|
||||
c1, c2, c3, c4 := passwordArgPrototype()
|
||||
cArgs = append(cArgs, c1)
|
||||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
gUse = append(gUse, c4)
|
||||
case "conversation":
|
||||
name := "conversation"
|
||||
if len(argTypeParts) == 2 {
|
||||
name = argTypeParts[1]
|
||||
}
|
||||
c1, c2, c3, c4 := conversationArgPrototype(name)
|
||||
cArgs = append(cArgs, c1)
|
||||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
gUse = append(gUse, c4)
|
||||
case "channel":
|
||||
c1, c2, c3, c4 := channelArgPrototype()
|
||||
cArgs = append(cArgs, c1)
|
||||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
gUse = append(gUse, c4)
|
||||
case "message":
|
||||
c1, c2, c3, c4 := messageArgPrototype()
|
||||
cArgs = append(cArgs, c1)
|
||||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
gUse = append(gUse, c4)
|
||||
case "bool":
|
||||
if len(argTypeParts) != 2 {
|
||||
fmt.Printf("generic bool arg must have have e.g. bool:<name>\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
c1, c2, c3, c4 := boolArgPrototype(argTypeParts[1])
|
||||
cArgs = append(cArgs, c1)
|
||||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
gUse = append(gUse, c4)
|
||||
case "int":
|
||||
if len(argTypeParts) != 2 {
|
||||
fmt.Printf("generic bool arg must have have e.g. bool:<name>\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
c1, c2, c3, c4 := intArgPrototype(argTypeParts[1])
|
||||
cArgs = append(cArgs, c1)
|
||||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
gUse = append(gUse, c4)
|
||||
case "string":
|
||||
if len(argTypeParts) != 2 {
|
||||
fmt.Printf("generic string arg must have have e.g. string:<name>\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
c1, c2, c3, c4 := stringArgPrototype(argTypeParts[1])
|
||||
cArgs = append(cArgs, c1)
|
||||
c2GoArgs = append(c2GoArgs, c2)
|
||||
goSpec = append(goSpec, c3)
|
||||
gUse = append(gUse, c4)
|
||||
default:
|
||||
fmt.Printf("unknown arg type [%v]\n", argType)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
return strings.Join(cArgs, ","), strings.Join(c2GoArgs, ","), strings.Join(goSpec, ","), strings.Join(gUse, ",")
|
||||
}
|
||||
|
||||
func generateAppFunction(bindings string, name string, argsTypes []string) string {
|
||||
appPrototype := `
|
||||
//export c_{{FNAME}}
|
||||
func c_{{FNAME}}({{C_ARGS}}) {
|
||||
{{FNAME}}({{C2GO_ARGS}})
|
||||
}
|
||||
|
||||
func {{FNAME}}({{GO_ARGS_SPEC}}) {
|
||||
application.{{LIBNAME}}({{GO_ARG}})
|
||||
}
|
||||
`
|
||||
|
||||
cArgs, c2GoArgs, goSpec, gUse := mapArgs(argsTypes)
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{FNAME}}", strings.TrimPrefix(name, "Enhanced"))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{LIBNAME}}", name)
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{C_ARGS}}", cArgs)
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{C2GO_ARGS}}", c2GoArgs)
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{GO_ARGS_SPEC}}", goSpec)
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{GO_ARG}}", gUse)
|
||||
|
||||
bindings += appPrototype
|
||||
return bindings
|
||||
}
|
||||
|
||||
func generateProfileFunction(bindings string, name string, argsTypes []string) string {
|
||||
appPrototype := `
|
||||
//export c_{{FNAME}}
|
||||
func c_{{FNAME}}({{C_ARGS}}) {
|
||||
{{FNAME}}({{C2GO_ARGS}})
|
||||
}
|
||||
|
||||
func {{FNAME}}({{GO_ARGS_SPEC}}) {
|
||||
cwtchProfile := application.GetPeer(profile)
|
||||
if cwtchProfile != nil {
|
||||
cwtchProfile.{{LIBNAME}}({{GO_ARG}})
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
cArgs, c2GoArgs, goSpec, gUse := mapArgs(argsTypes)
|
||||
|
||||
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...
|
||||
pArgs, c2GoPArg, goSpecP, _ := profileHandleArgPrototype()
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{C_ARGS}}", strings.Join(append([]string{pArgs}, cArgs), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{C2GO_ARGS}}", strings.Join(append([]string{c2GoPArg}, c2GoArgs), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{GO_ARGS_SPEC}}", strings.Join(append([]string{goSpecP}, goSpec), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{GO_ARG}}", gUse)
|
||||
|
||||
bindings += appPrototype
|
||||
return bindings
|
||||
}
|
||||
|
||||
func generateJsonProfileFunction(bindings string, name string, argsTypes []string) string {
|
||||
appPrototype := `
|
||||
//export c_{{FNAME}}
|
||||
func c_{{FNAME}}({{C_ARGS}}) *C.char {
|
||||
return C.CString({{FNAME}}({{C2GO_ARGS}}))
|
||||
}
|
||||
|
||||
func {{FNAME}}({{GO_ARGS_SPEC}}) string {
|
||||
cwtchProfile := application.GetPeer(profile)
|
||||
if cwtchProfile != nil {
|
||||
return cwtchProfile.{{LIBNAME}}({{GO_ARG}})
|
||||
}
|
||||
return ""
|
||||
}
|
||||
`
|
||||
|
||||
cArgs, c2GoArgs, goSpec, gUse := mapArgs(argsTypes)
|
||||
|
||||
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...
|
||||
pArgs, c2GoPArg, goSpecP, _ := profileHandleArgPrototype()
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{C_ARGS}}", strings.Join(append([]string{pArgs}, cArgs), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{C2GO_ARGS}}", strings.Join(append([]string{c2GoPArg}, c2GoArgs), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{GO_ARGS_SPEC}}", strings.Join(append([]string{goSpecP}, goSpec), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{GO_ARG}}", gUse)
|
||||
|
||||
bindings += appPrototype
|
||||
return bindings
|
||||
}
|
||||
|
||||
func generateExperimentalProfileFunction(bindings string, experiment string, name string, argsTypes []string) string {
|
||||
appPrototype := `
|
||||
//export c_{{FNAME}}
|
||||
func c_{{FNAME}}({{C_ARGS}}) {
|
||||
{{FNAME}}({{C2GO_ARGS}})
|
||||
}
|
||||
|
||||
func {{FNAME}}({{GO_ARGS_SPEC}}) {
|
||||
cwtchProfile := application.GetPeer(profile)
|
||||
if cwtchProfile != nil {
|
||||
functionality, _ := {{EXPERIMENT}}.FunctionalityGate(application.ReadSettings().Experiments)
|
||||
if functionality != nil {
|
||||
functionality.{{LIBNAME}}(cwtchProfile, {{GO_ARG}})
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
cArgs, c2GoArgs, goSpec, gUse := mapArgs(argsTypes)
|
||||
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{FNAME}}", strings.TrimPrefix(name, "Enhanced"))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{LIBNAME}}", name)
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{EXPERIMENT}}", experiment)
|
||||
// We need to prepend a set of profile handle arguments...
|
||||
pArgs, c2GoPArg, goSpecP, _ := profileHandleArgPrototype()
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{C_ARGS}}", strings.Join(append([]string{pArgs}, cArgs), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{C2GO_ARGS}}", strings.Join(append([]string{c2GoPArg}, c2GoArgs), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{GO_ARGS_SPEC}}", strings.Join(append([]string{goSpecP}, goSpec), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{GO_ARG}}", gUse)
|
||||
|
||||
bindings += appPrototype
|
||||
return bindings
|
||||
}
|
||||
|
||||
func generateExperimentalJsonProfileFunction(bindings string, experiment string, name string, argsTypes []string) string {
|
||||
appPrototype := `
|
||||
//export c_{{FNAME}}
|
||||
func c_{{FNAME}}({{C_ARGS}}) *C.char {
|
||||
return C.CString({{FNAME}}({{C2GO_ARGS}}))
|
||||
}
|
||||
|
||||
func {{FNAME}}({{GO_ARGS_SPEC}}) string {
|
||||
cwtchProfile := application.GetPeer(profile)
|
||||
if cwtchProfile != nil {
|
||||
functionality, _ := {{EXPERIMENT}}.FunctionalityGate(application.ReadSettings().Experiments)
|
||||
if functionality != nil {
|
||||
return functionality.{{LIBNAME}}(cwtchProfile, {{GO_ARG}})
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
`
|
||||
|
||||
cArgs, c2GoArgs, goSpec, gUse := mapArgs(argsTypes)
|
||||
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{FNAME}}", strings.TrimPrefix(name, "Enhanced"))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{LIBNAME}}", name)
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{EXPERIMENT}}", experiment)
|
||||
// We need to prepend a set of profile handle arguments...
|
||||
pArgs, c2GoPArg, goSpecP, _ := profileHandleArgPrototype()
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{C_ARGS}}", strings.Join(append([]string{pArgs}, cArgs), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{C2GO_ARGS}}", strings.Join(append([]string{c2GoPArg}, c2GoArgs), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{GO_ARGS_SPEC}}", strings.Join(append([]string{goSpecP}, goSpec), ","))
|
||||
appPrototype = strings.ReplaceAll(appPrototype, "{{GO_ARG}}", gUse)
|
||||
|
||||
bindings += appPrototype
|
||||
return bindings
|
||||
}
|
||||
|
||||
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
|
||||
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
|
||||
|
||||
func ToSnakeCase(str string) string {
|
||||
snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
|
||||
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
|
||||
return strings.ToLower(snake)
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
module git.openprivacy.ca/cwtch.im/cwtch-autobindings
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
cwtch.im/cwtch v0.18.10
|
||||
git.openprivacy.ca/cwtch.im/libcwtch-go v1.10.5
|
||||
git.openprivacy.ca/cwtch.im/server v1.4.5
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.8.6
|
||||
git.openprivacy.ca/openprivacy/log v1.0.3
|
||||
github.com/mutecomm/go-sqlcipher/v4 v4.4.2
|
||||
)
|
||||
|
||||
replace cwtch.im/cwtch => /home/sarah/workspace/src/cwtch.im/cwtch
|
||||
|
||||
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
|
||||
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/net v0.0.0-20220826154423-83b083e8dc8b // indirect
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
|
||||
)
|
@ -0,0 +1,170 @@
|
||||
cwtch.im/cwtch v0.18.0/go.mod h1:StheazFFY7PKqBbEyDVLhzWW6WOat41zV0ckC240c5Y=
|
||||
cwtch.im/cwtch v0.18.10 h1:iTzLzlms1mgn8kLfClU/yAWIVWVRRT8UmfbDNli9dzE=
|
||||
cwtch.im/cwtch v0.18.10/go.mod h1:h8S7EgEM+8pE1k+XLB5jAFdIPlOzwoXEY0GH5mQye5A=
|
||||
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=
|
||||
git.openprivacy.ca/cwtch.im/libcwtch-go v1.10.5 h1:xqLna6aNK5lj08hkspmK3VWVOyOMEnwAfcWQpR8ZCm0=
|
||||
git.openprivacy.ca/cwtch.im/libcwtch-go v1.10.5/go.mod h1:FTW3OcWTy5oQYCUe8ACz0D2jAzCembDyl8FfATjsVmM=
|
||||
git.openprivacy.ca/cwtch.im/server v1.4.5 h1:QuNAIxld+aWeQfWuGHB2QYZXsqJMmTyl55Pcmdn8FQA=
|
||||
git.openprivacy.ca/cwtch.im/server v1.4.5/go.mod h1:dGB1bePUgDU9xwk7gGkioNeshrbNgGWhSH8zMQwIAUg=
|
||||
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/connectivity v1.8.6/go.mod h1:Hn1gpOx/bRZp5wvCtPQVJPXrfeUH0EGiG/Aoa0vjGLg=
|
||||
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=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
||||
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c h1:gkfmnY4Rlt3VINCo4uKdpvngiibQyoENVj5Q88sxXhE=
|
||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c/go.mod h1:tDPFhGdt3hJWqtKwx57i9baiB1Cj0yAg22VOPUqm5vY=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
|
||||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU=
|
||||
github.com/mutecomm/go-sqlcipher/v4 v4.4.2 h1:eM10bFtI4UvibIsKr10/QT7Yfz+NADfjZYh0GKrXUNc=
|
||||
github.com/mutecomm/go-sqlcipher/v4 v4.4.2/go.mod h1:mF2UmIpBnzFeBdu/ypTDb/LdbS0nk0dfSN1WUsWTjMA=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
||||
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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/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/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Checking code quality (you want to see no output here)"
|
||||
echo ""
|
||||
|
||||
echo "Vetting:"
|
||||
go vet generate/*
|
||||
|
||||
echo ""
|
||||
echo "Linting:"
|
||||
|
||||
staticcheck ./generate
|
||||
|
||||
|
||||
echo "Time to format"
|
||||
gofmt -l -s -w .
|
||||
|
||||
# ineffassign (https://github.com/gordonklaus/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"
|
@ -0,0 +1,42 @@
|
||||
# ( app | profile) FunctionName (profile)
|
||||
|
||||
# Peer Engine
|
||||
app ActivatePeerEngine profile
|
||||
app DeactivatePeerEngine profile
|
||||
|
||||
# Profile Management
|
||||
app CreateProfile name password bool:autostart
|
||||
app LoadProfiles password
|
||||
app DeleteProfile profile password
|
||||
app ImportProfile string:file password
|
||||
profile ChangePassword string:current string:newPassword string:newPasswordAgain
|
||||
profile ExportProfile string:file
|
||||
|
||||
# Conversation Management
|
||||
profile ImportBundle string:bundle
|
||||
profile ArchiveConversation conversation
|
||||
profile AcceptConversation conversation
|
||||
profile BlockConversation conversation
|
||||
profile UnblockConversation conversation
|
||||
profile DeleteConversation conversation
|
||||
|
||||
# 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
|
||||
profile UpdateMessageAttribute conversation channel message string:attributeKey string:attributeValue
|
||||
|
||||
# Group Management
|
||||
profile StartGroup string:name string:server
|
||||
|
||||
# Filesharing Management
|
||||
import "cwtch.im/cwtch/functionality/filesharing"
|
||||
@profile-experiment DownloadFileDefaultLimit filesharing conversation string:filepath string:manifest string:filekey
|
||||
@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
|
||||
@(json)profile-experiment EnhancedShareFile filesharing conversation string:filepath
|
||||
@(json)profile-experiment EnhancedGetSharedFiles filesharing conversation
|
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
# using '-i.bak' and 'rm .bak' for gnu sed (linux) and bsd sed (macosx) compat
|
||||
sed -i.bak "s/^package cwtch/\/\/package cwtch/" lib.go
|
||||
sed -i.bak "s/^\/\/package main/package main/" lib.go
|
||||
sed -i.bak "s/^\/\/func main()/func main()/" lib.go
|
||||
rm lib.go.bak
|
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
# using '-i.bak' and 'rm .bak' for gnu sed (linux) and bsd sed (macosx) compat
|
||||
sed -i.bak "s/^\/\/package cwtch/package cwtch/" lib.go
|
||||
sed -i.bak "s/^package main/\/\/package main/" lib.go
|
||||
sed -i.bak "s/^func main()/\/\/func main()/" lib.go
|
||||
rm lib.go.bak
|
@ -0,0 +1,282 @@
|
||||
//package cwtch
|
||||
|
||||
package main
|
||||
|
||||
// //Needed to invoke C.free
|
||||
// #include <stdlib.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/event"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/cwtch.im/cwtch-autobindings/utils"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"os"
|
||||
"os/user"
|
||||
path "path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
// Import SQL Cipher
|
||||
_ "github.com/mutecomm/go-sqlcipher/v4"
|
||||
|
||||
"cwtch.im/cwtch/app"
|
||||
"git.openprivacy.ca/openprivacy/connectivity"
|
||||
|
||||
{{IMPORTS}}
|
||||
)
|
||||
|
||||
// supplied by make
|
||||
var (
|
||||
buildVer string
|
||||
buildDate string
|
||||
)
|
||||
|
||||
var application app.Application
|
||||
var globalAppDir string
|
||||
var globalTorPath string
|
||||
var eventHandler *utils.EventHandler
|
||||
var globalACN connectivity.ProxyACN
|
||||
|
||||
// Dangerous function. Should only be used as documented in `MEMORY.md`
|
||||
//
|
||||
//export c_FreePointer
|
||||
func c_FreePointer(ptr *C.char) {
|
||||
C.free(unsafe.Pointer(ptr))
|
||||
}
|
||||
|
||||
//export c_Started
|
||||
func c_Started() C.int {
|
||||
return C.int(Started())
|
||||
}
|
||||
|
||||
// Started returns 1 if application is initialized and 0 if it is null
|
||||
func Started() int {
|
||||
if application == nil {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
//export c_StartCwtch
|
||||
func c_StartCwtch(dir_c *C.char, len C.int, tor_c *C.char, torLen C.int) C.int {
|
||||
applicationDirectory := C.GoStringN(dir_c, len)
|
||||
torDirectory := C.GoStringN(tor_c, torLen)
|
||||
return C.int(StartCwtch(applicationDirectory, torDirectory))
|
||||
}
|
||||
|
||||
// StartCwtch starts cwtch in the library and initlaizes all data structures
|
||||
//
|
||||
// GetAppbusEvents is always safe to use
|
||||
// the rest of functions are unsafe until the CwtchStarted event has been received indicating StartCwtch has completed
|
||||
// returns:
|
||||
// message: CwtchStarted when start up is complete and app is safe to use
|
||||
// CwtchStartError message when start up fails (includes event.Error data field)
|
||||
func StartCwtch(appDir string, torPath string) int {
|
||||
if logfile := os.Getenv("LOG_FILE"); logfile != "" {
|
||||
filelog, err := log.NewFile(log.LevelInfo, logfile)
|
||||
if err == nil {
|
||||
filelog.SetUseColor(false)
|
||||
log.SetStd(filelog)
|
||||
} else {
|
||||
// not so likely to be seen since we're usually creating file log in situations we can't access console logs...
|
||||
log.Errorf("could not create file log: %v\n", err)
|
||||
}
|
||||
}
|
||||
if runtime.GOOS == "android" {
|
||||
log.SetUseColor(false)
|
||||
}
|
||||
log.SetLevel(log.LevelInfo)
|
||||
if logLevel := os.Getenv("LOG_LEVEL"); strings.ToLower(logLevel) == "debug" {
|
||||
log.SetLevel(log.LevelDebug)
|
||||
}
|
||||
log.Infof("StartCwtch(...)")
|
||||
log.Debugf("builddate: %v buildver: %v", buildDate, buildVer)
|
||||
|
||||
// Quick hack check that we're being called with the correct params
|
||||
// On android a stale worker could be calling us with "last apps" directory. Best to abort fast so the app can make a new worker
|
||||
if runtime.GOOS == "android" {
|
||||
fh, err := os.Open(torPath)
|
||||
if err != nil {
|
||||
log.Errorf("%v", err)
|
||||
log.Errorf("failed to stat tor, skipping StartCwtch(). potentially normal if the app was reinstalled or the device was restarted; this workorder should get canceled soon")
|
||||
return 1
|
||||
}
|
||||
_ = fh.Close()
|
||||
}
|
||||
go _startCwtch(appDir, torPath)
|
||||
return 0
|
||||
}
|
||||
|
||||
func _startCwtch(appDir string, torPath string) {
|
||||
log.Infof("application: %v eventHandler: %v", application, eventHandler)
|
||||
|
||||
if application != nil {
|
||||
log.Infof("_startCwtch detected existing application; resuming instead of relaunching")
|
||||
ReconnectCwtchForeground()
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Creating new EventHandler()")
|
||||
|
||||
eventHandler = utils.NewEventHandler()
|
||||
|
||||
// Exclude Tapir wire Messages
|
||||
//(We need a TRACE level)
|
||||
log.ExcludeFromPattern("service.go")
|
||||
|
||||
// Environment variables don't get '~' expansion so if CWTCH_DIR was set, it likely needs manual handling
|
||||
usr, _ := user.Current()
|
||||
homeDir := usr.HomeDir
|
||||
if appDir == "~" {
|
||||
appDir = homeDir
|
||||
} else if strings.HasPrefix(appDir, "~/") {
|
||||