2021-05-05 22:19:07 +00:00
|
|
|
//package cwtch
|
2021-03-17 19:03:25 +00:00
|
|
|
|
2021-05-05 22:19:07 +00:00
|
|
|
package main
|
2021-01-06 23:07:09 +00:00
|
|
|
|
|
|
|
import "C"
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"cwtch.im/cwtch/app"
|
|
|
|
"cwtch.im/cwtch/event"
|
2021-03-17 21:46:51 +00:00
|
|
|
"cwtch.im/cwtch/model"
|
2021-02-06 00:31:03 +00:00
|
|
|
"cwtch.im/cwtch/model/attr"
|
2021-03-04 02:05:22 +00:00
|
|
|
"cwtch.im/cwtch/peer"
|
2021-03-24 23:24:42 +00:00
|
|
|
contact "git.openprivacy.ca/flutter/libcwtch-go/features/contacts"
|
2021-04-15 22:17:50 +00:00
|
|
|
"git.openprivacy.ca/flutter/libcwtch-go/features/groups"
|
2021-04-13 22:26:23 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/connectivity"
|
2021-05-28 09:12:19 +00:00
|
|
|
"strings"
|
2021-02-06 00:31:03 +00:00
|
|
|
|
2021-01-06 23:07:09 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2021-02-06 00:31:03 +00:00
|
|
|
"git.openprivacy.ca/flutter/libcwtch-go/constants"
|
|
|
|
"git.openprivacy.ca/flutter/libcwtch-go/utils"
|
2021-01-06 23:07:09 +00:00
|
|
|
|
|
|
|
"encoding/base64"
|
|
|
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
|
|
|
"git.openprivacy.ca/openprivacy/log"
|
|
|
|
mrand "math/rand"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2021-04-15 22:17:50 +00:00
|
|
|
const (
|
2021-04-28 22:13:43 +00:00
|
|
|
// ProfileOnion is an event field that contains the handle for a given profile.
|
|
|
|
// todo: this should probably be moved back into Cwtch, and renamed ProfileHandle (onions are too tor-specific)
|
|
|
|
ProfileOnion = event.Field("ProfileOnion")
|
2021-04-15 22:17:50 +00:00
|
|
|
)
|
|
|
|
|
2021-01-06 23:07:09 +00:00
|
|
|
var application app.Application
|
2021-03-04 23:57:48 +00:00
|
|
|
var eventHandler *utils.EventHandler
|
2021-01-06 23:07:09 +00:00
|
|
|
var acnQueue event.Queue
|
|
|
|
var contactEventsQueue event.Queue
|
2021-04-13 22:26:23 +00:00
|
|
|
var globalACN connectivity.ACN
|
2021-01-06 23:07:09 +00:00
|
|
|
|
2021-05-11 22:35:58 +00:00
|
|
|
// ChatMessage API currently not officially documented, see
|
|
|
|
// https://git.openprivacy.ca/cwtch.im/secure-development-handbook/issues/3
|
|
|
|
// for latest updates for now
|
|
|
|
//
|
|
|
|
// A ChatMessage is the application-layer Cwtch message, delivered to the UI
|
|
|
|
// as serialized json.
|
2021-05-10 23:58:25 +00:00
|
|
|
type ChatMessage struct {
|
|
|
|
O int `json:"o"`
|
|
|
|
D string `json:"d"`
|
|
|
|
}
|
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
//export c_StartCwtch
|
|
|
|
func c_StartCwtch(dir_c *C.char, len C.int, tor_c *C.char, torLen C.int) {
|
|
|
|
dir := C.GoStringN(dir_c, len)
|
|
|
|
tor := C.GoStringN(tor_c, torLen)
|
|
|
|
StartCwtch(dir, tor)
|
2021-01-06 23:07:09 +00:00
|
|
|
}
|
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
func StartCwtch(appDir string, torPath string) {
|
2021-03-17 19:03:25 +00:00
|
|
|
log.SetLevel(log.LevelInfo)
|
2021-03-19 19:39:20 +00:00
|
|
|
|
|
|
|
// Exclude Tapir wire Messages (We need a TRACE level)
|
2021-03-17 19:03:25 +00:00
|
|
|
log.ExcludeFromPattern("service.go")
|
2021-01-14 23:34:08 +00:00
|
|
|
|
2021-04-26 22:58:46 +00:00
|
|
|
// Ensure that the application directory exists...and then initialize settings..
|
|
|
|
os.MkdirAll(path.Join(appDir), 0700)
|
2021-03-10 17:41:09 +00:00
|
|
|
utils.InitGlobalSettingsFile(appDir, "be gay do crime")
|
|
|
|
|
2021-01-14 23:34:08 +00:00
|
|
|
log.Infof("Loading Cwtch Directory %v and tor path: %v", appDir, torPath)
|
|
|
|
|
|
|
|
mrand.Seed(int64(time.Now().Nanosecond()))
|
|
|
|
port := mrand.Intn(1000) + 9600
|
|
|
|
controlPort := port + 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)
|
|
|
|
os.MkdirAll(path.Join(appDir, "/.tor", "tor"), 0700)
|
|
|
|
tor.NewTorrc().WithSocksPort(port).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key)).Build(filepath.Join(appDir, ".tor", "tor", "torrc"))
|
2021-03-17 20:16:27 +00:00
|
|
|
acn, err := tor.NewTorACNWithAuth(path.Join(appDir, "/.tor"), torPath, controlPort, tor.HashedPasswordAuthenticator{Password: base64.StdEncoding.EncodeToString(key)})
|
2021-01-14 23:34:08 +00:00
|
|
|
if err != nil {
|
2021-06-02 19:31:39 +00:00
|
|
|
log.Errorf("\nError connecting to Tor replacing with ErrorACN: %v\n", err)
|
2021-06-02 19:36:21 +00:00
|
|
|
// Replace the nil acn with a stub that will do nothing but report this error to the user...
|
2021-06-02 19:31:39 +00:00
|
|
|
acn = new(utils.ErrorACN)
|
2021-01-14 23:34:08 +00:00
|
|
|
}
|
2021-04-13 22:26:23 +00:00
|
|
|
globalACN = acn
|
2021-01-14 23:34:08 +00:00
|
|
|
newApp := app.NewApp(acn, appDir)
|
|
|
|
acnQueue = event.NewQueue()
|
|
|
|
newApp.GetPrimaryBus().Subscribe(event.ACNStatus, acnQueue)
|
2021-03-10 17:41:09 +00:00
|
|
|
newApp.GetPrimaryBus().Subscribe(utils.UpdateGlobalSettings, acnQueue)
|
2021-03-17 19:03:25 +00:00
|
|
|
newApp.GetPrimaryBus().Subscribe(utils.SetLoggingLevel, acnQueue)
|
2021-03-24 23:24:42 +00:00
|
|
|
newApp.GetPrimaryBus().Subscribe(event.AppError, acnQueue)
|
2021-01-20 20:05:37 +00:00
|
|
|
|
2021-03-04 23:57:48 +00:00
|
|
|
eventHandler = utils.NewEventHandler(newApp)
|
2021-01-20 20:05:37 +00:00
|
|
|
|
2021-01-14 23:34:08 +00:00
|
|
|
peer.DefaultEventsToHandle = []event.Type{
|
|
|
|
event.EncryptedGroupMessage,
|
|
|
|
event.NewMessageFromPeer,
|
|
|
|
event.PeerAcknowledgement,
|
|
|
|
event.PeerError,
|
2021-05-26 22:21:01 +00:00
|
|
|
event.SendMessageToPeerError,
|
2021-01-14 23:34:08 +00:00
|
|
|
event.SendMessageToGroupError,
|
|
|
|
event.NewGetValMessageFromPeer,
|
|
|
|
event.PeerStateChange,
|
2021-03-04 23:57:48 +00:00
|
|
|
event.NewRetValMessageFromPeer,
|
|
|
|
event.NewGroupInvite,
|
|
|
|
event.ServerStateChange,
|
2021-04-13 22:26:23 +00:00
|
|
|
event.ProtocolEngineStopped,
|
2021-05-07 23:35:07 +00:00
|
|
|
event.RetryServerRequest,
|
2021-01-14 23:34:08 +00:00
|
|
|
}
|
2021-03-10 17:41:09 +00:00
|
|
|
settings := utils.ReadGlobalSettings()
|
|
|
|
settingsJson, _ := json.Marshal(settings)
|
2021-02-06 00:31:03 +00:00
|
|
|
|
2021-01-14 23:34:08 +00:00
|
|
|
newApp.LoadProfiles("be gay do crime")
|
|
|
|
application = newApp
|
2021-03-19 19:39:20 +00:00
|
|
|
|
|
|
|
// Send global settings to the UI...
|
2021-03-10 17:41:09 +00:00
|
|
|
application.GetPrimaryBus().Publish(event.NewEvent(utils.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
|
2021-03-19 19:39:20 +00:00
|
|
|
log.Infof("libcwtch-go application launched")
|
2021-01-13 05:46:33 +00:00
|
|
|
}
|
2021-01-06 23:07:09 +00:00
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
//export c_ACNEvents
|
|
|
|
func c_ACNEvents() *C.char {
|
|
|
|
return C.CString(ACNEvents())
|
2021-01-06 23:07:09 +00:00
|
|
|
}
|
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
func ACNEvents() string {
|
2021-01-06 23:07:09 +00:00
|
|
|
select {
|
2021-03-04 02:05:22 +00:00
|
|
|
case myevent := <-acnQueue.OutChan():
|
2021-01-13 05:46:33 +00:00
|
|
|
return fmt.Sprintf("%v", myevent)
|
2021-01-06 23:07:09 +00:00
|
|
|
default:
|
2021-01-13 05:46:33 +00:00
|
|
|
return ""
|
2021-01-06 23:07:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 17:41:09 +00:00
|
|
|
//export c_SendAppEvent
|
|
|
|
// A generic method for Rebroadcasting App Events from a UI
|
|
|
|
func c_SendAppEvent(json_ptr *C.char, json_len C.int) {
|
|
|
|
eventJson := C.GoStringN(json_ptr, json_len)
|
|
|
|
SendAppEvent(eventJson)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendAppEvent is a generic method for Rebroadcasting App Events from a UI
|
|
|
|
func SendAppEvent(eventJson string) {
|
|
|
|
// Convert the Event Json back to a typed Event Struct, this will make the
|
|
|
|
// rest of the logic nicer.
|
|
|
|
var new_event event.Event
|
|
|
|
json.Unmarshal([]byte(eventJson), &new_event)
|
|
|
|
log.Infof("Event: %v", new_event)
|
|
|
|
|
|
|
|
// We need to update the local cache
|
|
|
|
// Ideally I think this would be pusgit hed back into Cwtch
|
|
|
|
switch new_event.EventType {
|
|
|
|
case utils.UpdateGlobalSettings:
|
|
|
|
var globalSettings utils.GlobalSettings
|
|
|
|
err := json.Unmarshal([]byte(new_event.Data[event.Data]), &globalSettings)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Error Unmarshalling Settings %v [%v]", err, new_event.Data[event.Data])
|
|
|
|
}
|
|
|
|
log.Debugf("New Settings %v", globalSettings)
|
|
|
|
utils.WriteGlobalSettings(globalSettings)
|
2021-04-06 21:56:01 +00:00
|
|
|
|
2021-04-15 22:17:50 +00:00
|
|
|
// Group Experiment Refresh
|
|
|
|
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
|
|
|
if err == nil {
|
|
|
|
for profileOnion := range application.ListPeers() {
|
|
|
|
serverListForOnion := groupHandler.GetServerInfoList(application.GetPeer(profileOnion))
|
|
|
|
serversListBytes, _ := json.Marshal(serverListForOnion)
|
|
|
|
eventHandler.Push(event.NewEvent(groups.UpdateServerInfo, map[event.Field]string{"ProfileOnion": profileOnion, groups.ServerList: string(serversListBytes)}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-06 21:56:01 +00:00
|
|
|
// Explicitly toggle blocking/unblocking of unknown connections for profiles
|
|
|
|
// that have been loaded.
|
|
|
|
if utils.ReadGlobalSettings().BlockUnknownConnections {
|
|
|
|
for onion := range application.ListPeers() {
|
|
|
|
application.GetPeer(onion).BlockUnknownConnections()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for onion := range application.ListPeers() {
|
|
|
|
application.GetPeer(onion).AllowUnknownConnections()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-17 19:03:25 +00:00
|
|
|
case utils.SetLoggingLevel:
|
|
|
|
_, warn := new_event.Data[utils.Warn]
|
|
|
|
_, error := new_event.Data[utils.Error]
|
|
|
|
_, debug := new_event.Data[utils.Debug]
|
|
|
|
_, info := new_event.Data[utils.Info]
|
|
|
|
// Assign logging level in priority order. The highest logging level wins in the
|
|
|
|
// event of multiple fields.
|
|
|
|
if info {
|
|
|
|
log.SetLevel(log.LevelInfo)
|
|
|
|
} else if warn {
|
|
|
|
log.SetLevel(log.LevelWarn)
|
|
|
|
} else if error {
|
|
|
|
log.SetLevel(log.LevelError)
|
|
|
|
} else if debug {
|
|
|
|
log.SetLevel(log.LevelDebug)
|
|
|
|
}
|
2021-03-10 17:41:09 +00:00
|
|
|
default: // do nothing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-04 01:25:24 +00:00
|
|
|
//export c_SendProfileEvent
|
|
|
|
// A generic method for Rebroadcasting Profile Events from a UI
|
|
|
|
func c_SendProfileEvent(onion_ptr *C.char, onion_len C.int, json_ptr *C.char, json_len C.int) {
|
2021-03-04 02:05:22 +00:00
|
|
|
onion := C.GoStringN(onion_ptr, onion_len)
|
|
|
|
eventJson := C.GoStringN(json_ptr, json_len)
|
2021-03-04 01:25:24 +00:00
|
|
|
SendProfileEvent(onion, eventJson)
|
|
|
|
}
|
|
|
|
|
2021-03-24 23:24:42 +00:00
|
|
|
const (
|
|
|
|
AddContact = event.Type("AddContact")
|
|
|
|
ImportString = event.Field("ImportString")
|
|
|
|
)
|
|
|
|
|
2021-03-04 01:25:24 +00:00
|
|
|
// SendProfileEvent is a generic method for Rebroadcasting Profile Events from a UI
|
|
|
|
func SendProfileEvent(onion string, eventJson string) {
|
|
|
|
// Convert the Event Json back to a typed Event Struct, this will make the
|
|
|
|
// rest of the logic nicer.
|
|
|
|
var new_event event.Event
|
|
|
|
json.Unmarshal([]byte(eventJson), &new_event)
|
|
|
|
log.Infof("Event: %v %v", onion, new_event)
|
|
|
|
|
|
|
|
// Get the correct Peer
|
|
|
|
peer := application.GetPeer(onion)
|
|
|
|
if peer == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to update the local cache
|
|
|
|
// Ideally I think this would be pushed back into Cwtch
|
|
|
|
switch new_event.EventType {
|
2021-03-24 23:24:42 +00:00
|
|
|
case AddContact:
|
|
|
|
// Peer Functionality is Always Enabled, so we forgo the existence check...
|
|
|
|
// TODO: Combine with GroupFunctionality to make a meta-handleimportstring that can do both!
|
|
|
|
pf, _ := contact.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
|
|
|
err := pf.HandleImportString(peer, new_event.Data[ImportString])
|
|
|
|
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: err.Error()}))
|
2021-03-04 02:05:22 +00:00
|
|
|
case event.SetAttribute:
|
2021-03-04 01:25:24 +00:00
|
|
|
peer.SetAttribute(new_event.Data[event.Key], new_event.Data[event.Data])
|
2021-03-17 21:46:51 +00:00
|
|
|
case event.SetPeerAttribute:
|
|
|
|
peer.SetContactAttribute(new_event.Data[event.RemotePeer], new_event.Data[event.Key], new_event.Data[event.Data])
|
|
|
|
case event.SetPeerAuthorization:
|
|
|
|
peer.SetContactAuthorization(new_event.Data[event.RemotePeer], model.Authorization(new_event.Data[event.Authorization]))
|
2021-03-19 19:39:20 +00:00
|
|
|
|
|
|
|
// If approved (e.g. after an unblock) we want to kick off peering again...
|
|
|
|
if model.Authorization(new_event.Data[event.Authorization]) == model.AuthApproved {
|
|
|
|
peer.PeerWithOnion(new_event.Data[event.RemotePeer])
|
|
|
|
}
|
2021-03-04 02:05:22 +00:00
|
|
|
default:
|
2021-03-04 01:25:24 +00:00
|
|
|
// rebroadcast catch all
|
|
|
|
log.Infof("Received Event %v for %v but no libCwtch handler found, relaying the event directly", new_event, onion)
|
|
|
|
application.GetEventBus(onion).Publish(new_event)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-06 00:31:03 +00:00
|
|
|
//export c_GetAppBusEvent
|
|
|
|
func c_GetAppBusEvent() *C.char {
|
2021-01-26 22:38:06 +00:00
|
|
|
return C.CString(GetAppBusEvent())
|
|
|
|
}
|
|
|
|
|
2021-01-20 20:05:37 +00:00
|
|
|
// GetAppBusEvent blocks until an event
|
|
|
|
func GetAppBusEvent() string {
|
2021-03-09 00:44:00 +00:00
|
|
|
var json = ""
|
|
|
|
for json == "" {
|
|
|
|
json = eventHandler.GetNextEvent()
|
|
|
|
}
|
|
|
|
return json
|
2021-01-20 20:05:37 +00:00
|
|
|
}
|
|
|
|
|
2021-02-06 00:31:03 +00:00
|
|
|
//export c_GetProfileRepaintEvent
|
|
|
|
func c_GetProfileRepaintEvent() int8 {
|
|
|
|
if GetProfileRepaintEvent() {
|
|
|
|
return 1
|
|
|
|
} else {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetProfileRepaintEvent() bool {
|
2021-03-04 02:05:22 +00:00
|
|
|
<-acnQueue.OutChan()
|
2021-02-06 00:31:03 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-01-22 08:00:03 +00:00
|
|
|
type Profile struct {
|
2021-03-04 02:05:22 +00:00
|
|
|
Name string `json:"name"`
|
|
|
|
Onion string `json:"onion"`
|
2021-01-22 08:00:03 +00:00
|
|
|
ImagePath string `json:"imagePath"`
|
|
|
|
}
|
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
//export c_GetProfiles
|
|
|
|
func c_GetProfiles() *C.char {
|
|
|
|
return C.CString(GetProfiles())
|
2021-01-06 23:07:09 +00:00
|
|
|
}
|
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
func GetProfiles() string {
|
2021-01-22 08:00:03 +00:00
|
|
|
peerList := application.ListPeers()
|
|
|
|
profiles := make([]Profile, len(peerList))
|
|
|
|
i := 0
|
2021-03-04 02:05:22 +00:00
|
|
|
for onion := range peerList {
|
|
|
|
name, _ := application.GetPeer(onion).GetAttribute(attr.GetPublicScope(constants.Name))
|
2021-01-22 08:00:03 +00:00
|
|
|
profiles[i] = Profile{
|
2021-03-04 02:05:22 +00:00
|
|
|
Name: name,
|
|
|
|
Onion: onion,
|
2021-01-22 08:00:03 +00:00
|
|
|
ImagePath: "",
|
|
|
|
}
|
|
|
|
i += 1
|
|
|
|
}
|
2021-03-04 02:05:22 +00:00
|
|
|
jsonBytes, _ := json.Marshal(profiles)
|
2021-01-13 05:46:33 +00:00
|
|
|
return string(jsonBytes)
|
2021-01-06 23:07:09 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 08:00:03 +00:00
|
|
|
//export c_CreateProfile
|
|
|
|
func c_CreateProfile(nick_ptr *C.char, nick_len C.int, pass_ptr *C.char, pass_len C.int) {
|
|
|
|
CreateProfile(C.GoStringN(nick_ptr, nick_len), C.GoStringN(pass_ptr, pass_len))
|
|
|
|
}
|
2021-01-06 23:07:09 +00:00
|
|
|
|
2021-01-22 08:00:03 +00:00
|
|
|
func CreateProfile(nick, pass string) {
|
|
|
|
application.CreatePeer(nick, pass)
|
|
|
|
}
|
2021-01-06 23:07:09 +00:00
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
//export c_SelectProfile
|
|
|
|
func c_SelectProfile(onion_ptr *C.char, onion_len C.int) *C.char {
|
|
|
|
return C.CString(SelectProfile(C.GoStringN(onion_ptr, onion_len)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func SelectProfile(onion string) string {
|
2021-01-06 23:07:09 +00:00
|
|
|
contactEventsQueue = event.NewQueue()
|
|
|
|
application.GetEventBus(onion).Subscribe(event.PeerStateChange, contactEventsQueue)
|
2021-01-13 05:46:33 +00:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2021-01-26 22:38:06 +00:00
|
|
|
//export c_LoadProfiles
|
|
|
|
func c_LoadProfiles(passwordPtr *C.char, passwordLen C.int) {
|
|
|
|
LoadProfiles(C.GoStringN(passwordPtr, passwordLen))
|
|
|
|
}
|
|
|
|
|
|
|
|
func LoadProfiles(pass string) {
|
|
|
|
application.LoadProfiles(pass)
|
|
|
|
}
|
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
//export c_ContactEvents
|
|
|
|
func c_ContactEvents() *C.char {
|
|
|
|
return C.CString(ContactEvents())
|
2021-01-06 23:07:09 +00:00
|
|
|
}
|
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
func ContactEvents() string {
|
2021-01-06 23:07:09 +00:00
|
|
|
select {
|
2021-03-04 02:05:22 +00:00
|
|
|
case myevent := <-contactEventsQueue.OutChan():
|
2021-01-13 05:46:33 +00:00
|
|
|
return fmt.Sprintf("%v", myevent)
|
2021-01-06 23:07:09 +00:00
|
|
|
default:
|
2021-01-13 05:46:33 +00:00
|
|
|
return ""
|
2021-01-06 23:07:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-08 05:06:21 +00:00
|
|
|
//export c_AcceptContact
|
|
|
|
func c_AcceptContact(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
|
|
|
|
AcceptContact(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
|
|
|
|
}
|
|
|
|
|
2021-04-28 22:13:43 +00:00
|
|
|
// AcceptContact takes in a profileOnion and a handle to either a group or a peer and authorizes the handle
|
|
|
|
// for further action (e.g. messaging / connecting to the server / joining the group etc.)
|
|
|
|
func AcceptContact(profileOnion string, handle string) {
|
|
|
|
profile := application.GetPeer(profileOnion)
|
|
|
|
profileHandler := utils.NewPeerHelper(profile)
|
|
|
|
if profileHandler.IsGroup(handle) {
|
|
|
|
profile.AcceptInvite(handle)
|
2021-04-10 02:31:05 +00:00
|
|
|
} else {
|
2021-04-28 22:13:43 +00:00
|
|
|
err := profile.SetContactAuthorization(handle, model.AuthApproved)
|
|
|
|
if err == nil {
|
|
|
|
eventHandler.Push(event.NewEvent(event.PeerStateChange, map[event.Field]string{
|
|
|
|
ProfileOnion: profileOnion,
|
|
|
|
event.RemotePeer: handle,
|
|
|
|
"authorization": string(model.AuthApproved),
|
|
|
|
}))
|
|
|
|
} else {
|
|
|
|
log.Errorf("error accepting contact: %s", err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//export c_RejectInvite
|
|
|
|
func c_RejectInvite(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
|
|
|
|
RejectInvite(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
|
|
|
|
}
|
|
|
|
|
|
|
|
// RejectInvite rejects a group invite
|
|
|
|
func RejectInvite(profileOnion string, handle string) {
|
|
|
|
log.Debugf("rejecting invite %v for %v", handle, profileOnion)
|
|
|
|
profile := application.GetPeer(profileOnion)
|
|
|
|
profileHandler := utils.NewPeerHelper(profile)
|
|
|
|
if profileHandler.IsGroup(handle) {
|
|
|
|
profile.RejectInvite(handle)
|
|
|
|
log.Debugf("successfully rejected invite %v for %v", handle, profileOnion)
|
2021-04-08 05:06:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//export c_BlockContact
|
|
|
|
func c_BlockContact(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
|
|
|
|
BlockContact(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
|
|
|
|
}
|
|
|
|
|
|
|
|
func BlockContact(profile, handle string) {
|
|
|
|
err := application.GetPeer(profile).SetContactAuthorization(handle, model.AuthBlocked)
|
2021-04-10 02:31:05 +00:00
|
|
|
if err == nil {
|
2021-04-12 22:27:53 +00:00
|
|
|
eventHandler.Push(event.NewEvent(event.PeerStateChange, map[event.Field]string{
|
2021-04-28 22:13:43 +00:00
|
|
|
ProfileOnion: profile,
|
2021-04-12 22:27:53 +00:00
|
|
|
event.RemotePeer: handle,
|
|
|
|
"authorization": string(model.AuthBlocked),
|
2021-04-10 02:31:05 +00:00
|
|
|
}))
|
|
|
|
} else {
|
2021-04-08 05:06:21 +00:00
|
|
|
log.Errorf("error blocking contact: %s", err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//export c_DebugResetContact
|
|
|
|
func c_DebugResetContact(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
|
|
|
|
DebugResetContact(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
|
|
|
|
}
|
|
|
|
|
|
|
|
func DebugResetContact(profile, handle string) {
|
|
|
|
err := application.GetPeer(profile).SetContactAuthorization(handle, model.AuthUnknown)
|
2021-04-10 02:31:05 +00:00
|
|
|
if err == nil {
|
|
|
|
application.GetPrimaryBus().Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
|
2021-04-28 22:13:43 +00:00
|
|
|
ProfileOnion: profile,
|
2021-04-15 22:17:50 +00:00
|
|
|
event.RemotePeer: handle,
|
|
|
|
"authorization": string(model.AuthUnknown),
|
2021-04-10 02:31:05 +00:00
|
|
|
}))
|
2021-04-08 05:06:21 +00:00
|
|
|
} else {
|
2021-04-10 02:31:05 +00:00
|
|
|
log.Errorf("error resetting contact: %s", err.Error())
|
2021-04-08 05:06:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
//export c_NumMessages
|
2021-02-06 00:31:03 +00:00
|
|
|
func c_NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n int) {
|
2021-01-07 19:38:58 +00:00
|
|
|
profile := C.GoStringN(profile_ptr, profile_len)
|
|
|
|
handle := C.GoStringN(handle_ptr, handle_len)
|
2021-02-06 00:31:03 +00:00
|
|
|
return (NumMessages(profile, handle))
|
2021-01-13 05:46:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NumMessages(profile, handle string) (n int) {
|
|
|
|
n = len(application.GetPeer(profile).GetContact(handle).Timeline.Messages)
|
2021-01-07 19:38:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-01-13 05:46:33 +00:00
|
|
|
//export c_GetMessage
|
|
|
|
func c_GetMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, message_index C.int) *C.char {
|
2021-01-07 19:38:58 +00:00
|
|
|
profile := C.GoStringN(profile_ptr, profile_len)
|
|
|
|
handle := C.GoStringN(handle_ptr, handle_len)
|
2021-01-13 05:46:33 +00:00
|
|
|
return C.CString(GetMessage(profile, handle, int(message_index)))
|
|
|
|
}
|
|
|
|
|
2021-05-03 03:43:28 +00:00
|
|
|
// EnhancedMessage wraps a Cwtch model.Message with some additional data to reduce calls from the UI.
|
|
|
|
type EnhancedMessage struct {
|
|
|
|
model.Message
|
|
|
|
ContactImage string
|
|
|
|
}
|
|
|
|
|
2021-04-20 22:23:20 +00:00
|
|
|
func GetMessage(profileOnion, handle string, message_index int) string {
|
|
|
|
profile := application.GetPeer(profileOnion)
|
|
|
|
ph := utils.NewPeerHelper(profile)
|
2021-05-03 03:43:28 +00:00
|
|
|
var message EnhancedMessage
|
2021-04-20 22:23:20 +00:00
|
|
|
if ph.IsGroup(handle) {
|
2021-04-22 21:13:15 +00:00
|
|
|
if len(profile.GetGroup(handle).Timeline.Messages) > message_index {
|
2021-05-03 03:43:28 +00:00
|
|
|
message.Message = profile.GetGroup(handle).Timeline.Messages[message_index]
|
|
|
|
message.ContactImage = ph.GetProfilePic(message.Message.PeerID)
|
2021-05-05 20:05:02 +00:00
|
|
|
} else {
|
|
|
|
// Message Index Request exceeded Timeline, most likely reason is this is a request for an
|
|
|
|
// unacknowledged sent message (it can take a many seconds for a message to be confirmed in the worst
|
|
|
|
// case).
|
|
|
|
offset := message_index - len(profile.GetGroup(handle).Timeline.Messages)
|
|
|
|
if len(profile.GetGroup(handle).UnacknowledgedMessages) > offset {
|
|
|
|
message.Message = profile.GetGroup(handle).UnacknowledgedMessages[offset]
|
|
|
|
message.ContactImage = ph.GetProfilePic(message.Message.PeerID)
|
|
|
|
} else {
|
|
|
|
log.Errorf("Couldn't find message in timeline or unacked messages, probably transient threading issue, but logging for visibility..")
|
|
|
|
}
|
2021-04-22 21:13:15 +00:00
|
|
|
}
|
2021-04-20 22:23:20 +00:00
|
|
|
} else {
|
2021-05-05 20:05:02 +00:00
|
|
|
message.Message = profile.GetContact(handle).Timeline.Messages[message_index]
|
2021-05-03 03:43:28 +00:00
|
|
|
message.ContactImage = ph.GetProfilePic(handle)
|
2021-04-20 22:23:20 +00:00
|
|
|
}
|
2021-03-04 02:05:22 +00:00
|
|
|
bytes, _ := json.Marshal(message)
|
2021-01-13 05:46:33 +00:00
|
|
|
return string(bytes)
|
2021-01-07 19:38:58 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 23:34:08 +00:00
|
|
|
//export c_GetMessages
|
|
|
|
func c_GetMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, start C.int, end C.int) *C.char {
|
|
|
|
profile := C.GoStringN(profile_ptr, profile_len)
|
|
|
|
handle := C.GoStringN(handle_ptr, handle_len)
|
|
|
|
return C.CString(GetMessages(profile, handle, int(start), int(end)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetMessages(profile, handle string, start, end int) string {
|
|
|
|
messages := application.GetPeer(profile).GetContact(handle).Timeline.Messages[start:end]
|
2021-03-04 02:05:22 +00:00
|
|
|
bytes, _ := json.Marshal(messages)
|
2021-01-14 23:34:08 +00:00
|
|
|
return string(bytes)
|
|
|
|
}
|
|
|
|
|
2021-04-08 05:06:21 +00:00
|
|
|
//export c_SendMessage
|
|
|
|
func c_SendMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, msg_ptr *C.char, msg_len C.int) {
|
|
|
|
profile := C.GoStringN(profile_ptr, profile_len)
|
|
|
|
handle := C.GoStringN(handle_ptr, handle_len)
|
|
|
|
msg := C.GoStringN(msg_ptr, msg_len)
|
|
|
|
SendMessage(profile, handle, msg)
|
|
|
|
}
|
|
|
|
|
2021-04-22 21:13:15 +00:00
|
|
|
func SendMessage(profileOnion, handle, msg string) {
|
|
|
|
profile := application.GetPeer(profileOnion)
|
|
|
|
ph := utils.NewPeerHelper(profile)
|
|
|
|
if ph.IsGroup(handle) {
|
|
|
|
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
|
|
|
if err == nil {
|
|
|
|
groupHandler.SendMessage(profile, handle, msg)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
contactHandler, _ := contact.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
|
|
|
contactHandler.SendMessage(profile, handle, msg)
|
|
|
|
}
|
2021-04-08 05:06:21 +00:00
|
|
|
}
|
|
|
|
|
2021-05-10 23:58:25 +00:00
|
|
|
//export c_SendInvitation
|
|
|
|
func c_SendInvitation(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, target_ptr *C.char, target_len C.int) {
|
|
|
|
profile := C.GoStringN(profile_ptr, profile_len)
|
|
|
|
handle := C.GoStringN(handle_ptr, handle_len)
|
|
|
|
target := C.GoStringN(target_ptr, target_len)
|
|
|
|
SendInvitation(profile, handle, target)
|
|
|
|
}
|
|
|
|
|
2021-05-11 22:35:58 +00:00
|
|
|
// Send an invitation from `profileOnion` to contact `handle` (peer or group)
|
|
|
|
// asking them to add the contact `target` (also peer or group).
|
|
|
|
// For groups, the profile must already have `target` as a contact.
|
2021-05-10 23:58:25 +00:00
|
|
|
func SendInvitation(profileOnion, handle, target string) {
|
|
|
|
profile := application.GetPeer(profileOnion)
|
|
|
|
ph := utils.NewPeerHelper(profile)
|
|
|
|
|
|
|
|
var invite ChatMessage
|
|
|
|
if ph.IsGroup(target) {
|
|
|
|
bundle, _ := profile.GetContact(profile.GetGroup(target).GroupServer).GetAttribute(string(model.BundleType))
|
|
|
|
inviteStr, err := profile.GetGroup(target).Invite()
|
|
|
|
if err == nil {
|
|
|
|
invite = ChatMessage{O: 101, D: fmt.Sprintf("tofubundle:server:%s||%s", base64.StdEncoding.EncodeToString([]byte(bundle)), inviteStr)}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
invite = ChatMessage{O: 100, D: target}
|
|
|
|
}
|
|
|
|
|
|
|
|
inviteBytes, err := json.Marshal(invite)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("malformed invite: %v", err)
|
|
|
|
} else {
|
2021-05-11 22:35:58 +00:00
|
|
|
SendMessage(profileOnion, handle, string(inviteBytes))
|
2021-05-10 23:58:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-13 22:26:23 +00:00
|
|
|
//export c_ResetTor
|
|
|
|
func c_ResetTor() {
|
|
|
|
ResetTor()
|
|
|
|
}
|
|
|
|
|
|
|
|
func ResetTor() {
|
|
|
|
globalACN.Restart()
|
|
|
|
}
|
|
|
|
|
2021-06-06 17:40:58 +00:00
|
|
|
//export c_QueryACNVersion
|
|
|
|
func c_QueryACNVersion() {
|
|
|
|
QueryACNVersion()
|
|
|
|
}
|
|
|
|
|
|
|
|
func QueryACNVersion() {
|
|
|
|
application.QueryACNVersion()
|
|
|
|
}
|
|
|
|
|
2021-04-15 22:17:50 +00:00
|
|
|
//export c_CreateGroup
|
2021-04-20 22:23:20 +00:00
|
|
|
func c_CreateGroup(profile_ptr *C.char, profile_len C.int, server_ptr *C.char, server_len C.int, name_ptr *C.char, name_len C.int) {
|
2021-04-15 22:17:50 +00:00
|
|
|
profile := C.GoStringN(profile_ptr, profile_len)
|
|
|
|
server := C.GoStringN(server_ptr, server_len)
|
2021-04-20 22:23:20 +00:00
|
|
|
name := C.GoStringN(name_ptr, name_len)
|
|
|
|
CreateGroup(profile, server, name)
|
2021-04-15 22:17:50 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 22:23:20 +00:00
|
|
|
// CreateGroup takes in a profile and server in addition to a name and creates a new group.
|
|
|
|
func CreateGroup(profile string, server string, name string) {
|
2021-04-15 22:17:50 +00:00
|
|
|
peer := application.GetPeer(profile)
|
|
|
|
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
|
|
|
if err == nil {
|
|
|
|
gid, _, err := peer.StartGroup(server)
|
|
|
|
if err == nil {
|
|
|
|
log.Debugf("created group %v on %v: $v", profile, server, gid)
|
2021-04-20 22:23:20 +00:00
|
|
|
// set the group name
|
2021-05-28 09:12:19 +00:00
|
|
|
peer.SetGroupAttribute(gid, attr.GetLocalScope("name"), name)
|
2021-04-15 22:17:50 +00:00
|
|
|
} else {
|
|
|
|
log.Errorf("error creating group or %v on server %v: %v", profile, server, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-28 09:12:19 +00:00
|
|
|
//export c_LeaveGroup
|
|
|
|
func c_LeaveGroup(profile_ptr *C.char, profile_len C.int, group_ptr *C.char, group_len C.int) {
|
|
|
|
profile := C.GoStringN(profile_ptr, profile_len)
|
|
|
|
groupID := C.GoStringN(group_ptr, group_len)
|
|
|
|
LeaveGroup(profile, groupID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LeaveGroup forces profile to leave the group groupID
|
|
|
|
func LeaveGroup(profile string, groupID string) {
|
|
|
|
peer := application.GetPeer(profile)
|
|
|
|
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
|
|
|
if err == nil {
|
|
|
|
peer.DeleteGroup(groupID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-22 21:13:15 +00:00
|
|
|
//export c_ImportBundle
|
|
|
|
func c_ImportBundle(profile_ptr *C.char, profile_len C.int, bundle_ptr *C.char, bundle_len C.int) {
|
|
|
|
profile := C.GoStringN(profile_ptr, profile_len)
|
|
|
|
name := C.GoStringN(bundle_ptr, bundle_len)
|
|
|
|
ImportBundle(profile, name)
|
|
|
|
}
|
|
|
|
|
2021-05-28 09:12:19 +00:00
|
|
|
// ImportBundle takes in a handle to a profile and an invite string which could have one of many
|
|
|
|
// different formats (e.g. a peer address, a group invite, a server key bundle, or a combination)
|
2021-04-22 21:13:15 +00:00
|
|
|
func ImportBundle(profileOnion string, bundle string) {
|
|
|
|
profile := application.GetPeer(profileOnion)
|
2021-05-28 09:12:19 +00:00
|
|
|
peerHandler, _ := contact.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
|
|
|
response := peerHandler.HandleImportString(profile, bundle)
|
|
|
|
if strings.Contains(response.Error(), "invalid_import_string") {
|
|
|
|
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
|
|
|
if err == nil {
|
|
|
|
response = groupHandler.HandleImportString(profile, bundle)
|
|
|
|
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: response.Error()}))
|
2021-05-31 23:26:19 +00:00
|
|
|
|
|
|
|
// We might have added a new server, so refresh the server list...
|
|
|
|
serverListForOnion := groupHandler.GetServerInfoList(profile)
|
|
|
|
serversListBytes, _ := json.Marshal(serverListForOnion)
|
|
|
|
eventHandler.Push(event.NewEvent(groups.UpdateServerInfo, map[event.Field]string{"ProfileOnion": profileOnion, groups.ServerList: string(serversListBytes)}))
|
|
|
|
|
2021-05-28 09:12:19 +00:00
|
|
|
}
|
2021-04-22 21:13:15 +00:00
|
|
|
}
|
2021-05-28 09:12:19 +00:00
|
|
|
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: response.Error()}))
|
2021-04-22 21:13:15 +00:00
|
|
|
}
|
|
|
|
|
2021-04-23 19:56:20 +00:00
|
|
|
//export c_SetGroupAttribute
|
|
|
|
func c_SetGroupAttribute(profile_ptr *C.char, profile_len C.int, group_ptr *C.char, group_len 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)
|
|
|
|
groupHandle := C.GoStringN(group_ptr, group_len)
|
|
|
|
key := C.GoStringN(key_ptr, key_len)
|
|
|
|
value := C.GoStringN(val_ptr, val_len)
|
|
|
|
SetGroupAttribute(profileOnion, groupHandle, key, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetGroupAttribute provides a wrapper around profile.SetGroupAttribute, gated by global experiments...
|
|
|
|
func SetGroupAttribute(profileOnion string, groupHandle string, key string, value string) {
|
|
|
|
profile := application.GetPeer(profileOnion)
|
|
|
|
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
|
|
|
if err == nil {
|
|
|
|
profile.SetGroupAttribute(groupHandle, key, value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-10 00:35:54 +00:00
|
|
|
// Leave as is, needed by ffi
|
2021-05-05 22:19:07 +00:00
|
|
|
func main() {}
|