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-06-11 21:21:09 +00:00
"runtime"
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
2021-06-11 21:21:09 +00:00
func c_StartCwtch ( dir_c * C . char , len C . int , tor_c * C . char , torLen C . int ) int8 {
2021-01-13 05:46:33 +00:00
dir := C . GoStringN ( dir_c , len )
tor := C . GoStringN ( tor_c , torLen )
2021-06-15 17:17:05 +00:00
return int8 ( StartCwtch ( dir , tor ) )
2021-01-06 23:07:09 +00:00
}
2021-06-15 17:17:05 +00:00
// 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 {
eventHandler = utils . NewEventHandler ( )
2021-03-17 19:03:25 +00:00
log . SetLevel ( log . LevelInfo )
2021-03-19 19:39:20 +00:00
2021-06-15 17:17:05 +00:00
// 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
2021-06-11 21:21:09 +00:00
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 ( )
}
2021-06-15 17:17:05 +00:00
go _startCwtch ( appDir , torPath )
return 0
}
2021-06-11 21:21:09 +00:00
2021-06-15 17:17:05 +00:00
func _startCwtch ( appDir string , torPath string ) {
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-15 17:17:05 +00:00
eventHandler . PublishAppEvent ( event . NewEventList ( utils . CwtchStartError , event . Error , err ) )
return
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-06-15 17:17:05 +00:00
eventHandler . HandleApp ( 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-06-15 17:17:05 +00:00
application . GetPrimaryBus ( ) . Publish ( event . NewEvent ( utils . CwtchStarted , map [ event . Field ] string { } ) )
2021-06-11 21:21:09 +00:00
}
//export c_ReconnectCwtchForeground
func c_ReconnectCwtchForeground ( ) {
ReconnectCwtchForeground ( )
}
// Like StartCwtch, but StartCwtch has already been called so we don't need to restart Tor etc (probably)
// Do need to re-send initial state tho, eg profiles that are already loaded
func ReconnectCwtchForeground ( ) {
peerList := application . ListPeers ( )
for onion := range peerList {
eventHandler . Push ( event . NewEvent ( event . NewPeer , map [ event . Field ] string { event . Identity : onion , event . Created : event . False } ) )
}
2021-01-13 05:46:33 +00:00
}
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-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-13 05:46:33 +00:00
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-06-09 18:25:39 +00:00
//export c_UpdateMessageFlags
2021-06-09 18:57:20 +00:00
func c_UpdateMessageFlags ( profile_ptr * C . char , profile_len C . int , handle_ptr * C . char , handle_len C . int , mIdx C . int , message_flags C . ulong ) {
2021-06-09 18:25:39 +00:00
profile := C . GoStringN ( profile_ptr , profile_len )
handle := C . GoStringN ( handle_ptr , handle_len )
2021-06-09 18:57:20 +00:00
UpdateMessageFlags ( profile , handle , int ( mIdx ) , int64 ( message_flags ) )
2021-06-09 18:25:39 +00:00
}
// UpdateMessageFlags sets the messages flags on a given message for a given profile.
2021-06-09 18:57:20 +00:00
// gomobile doesn't support uint64...so here we are....
func UpdateMessageFlags ( profileOnion , handle string , mIdx int , flags int64 ) {
2021-06-09 18:25:39 +00:00
profile := application . GetPeer ( profileOnion )
if profile != nil {
2021-06-09 18:57:20 +00:00
profile . UpdateMessageFlags ( handle , mIdx , uint64 ( flags ) )
2021-06-09 18:25:39 +00:00
} else {
log . Errorf ( "called updatemessageflags with invalid profile onion" )
}
}
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-06-11 21:21:09 +00:00
if message_index < len ( profile . GetContact ( handle ) . Timeline . Messages ) {
message . Message = profile . GetContact ( handle ) . Timeline . Messages [ message_index ]
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-06-15 00:23:47 +00:00
//export c_DeleteProfile
func c_DeleteProfile ( profile_ptr * C . char , profile_len C . int , password_ptr * C . char , password_len C . int ) {
profile := C . GoStringN ( profile_ptr , profile_len )
password := C . GoStringN ( password_ptr , password_len )
DeleteProfile ( profile , password )
}
// DeleteProfile deletes a profile given the right password
func DeleteProfile ( profile string , password string ) {
application . DeletePeer ( profile , password )
}
//export c_LeaveConversation
func c_LeaveConversation ( profile_ptr * C . char , profile_len C . int , contact_ptr * C . char , contact_len C . int ) {
profile := C . GoStringN ( profile_ptr , profile_len )
contact := C . GoStringN ( contact_ptr , contact_len )
LeaveConversation ( profile , contact )
}
// LeaveConversation forces profile to leave the peer
func LeaveConversation ( profile string , contact string ) {
peer := application . GetPeer ( profile )
peer . DeleteContact ( contact )
}
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 ( ) { }