342 lines
9.8 KiB
Go
342 lines
9.8 KiB
Go
//package cwtch
|
|
package main
|
|
|
|
import "C"
|
|
import (
|
|
"crypto/rand"
|
|
"cwtch.im/cwtch/app"
|
|
"cwtch.im/cwtch/event"
|
|
"cwtch.im/cwtch/model/attr"
|
|
|
|
"cwtch.im/cwtch/peer"
|
|
|
|
"encoding/json"
|
|
"fmt"
|
|
"git.openprivacy.ca/flutter/libcwtch-go/constants"
|
|
"git.openprivacy.ca/flutter/libcwtch-go/utils"
|
|
|
|
"encoding/base64"
|
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
|
"git.openprivacy.ca/openprivacy/log"
|
|
mrand "math/rand"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
var application app.Application
|
|
var eventHandler *utils.EventHandler
|
|
var acnQueue event.Queue
|
|
var contactEventsQueue event.Queue
|
|
|
|
//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)
|
|
}
|
|
|
|
func StartCwtch(appDir string, torPath string) {
|
|
log.SetLevel(log.LevelDebug)
|
|
//log.ExcludeFromPattern("service.go")
|
|
|
|
utils.InitGlobalSettingsFile(appDir, "be gay do crime")
|
|
|
|
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"))
|
|
acn, err := tor.NewTorACNWithAuth(path.Join(appDir, "/.tor"), torPath, controlPort, tor.HashedPasswordAuthenticator{base64.StdEncoding.EncodeToString(key)})
|
|
if err != nil {
|
|
log.Errorf("\nError connecting to Tor: %v\n", err)
|
|
}
|
|
//acn.WaitTillBootstrapped()
|
|
|
|
newApp := app.NewApp(acn, appDir)
|
|
acnQueue = event.NewQueue()
|
|
newApp.GetPrimaryBus().Subscribe(event.ACNStatus, acnQueue)
|
|
newApp.GetPrimaryBus().Subscribe(utils.UpdateGlobalSettings, acnQueue)
|
|
|
|
eventHandler = utils.NewEventHandler(newApp)
|
|
|
|
peer.DefaultEventsToHandle = []event.Type{
|
|
event.EncryptedGroupMessage,
|
|
event.NewMessageFromPeer,
|
|
event.PeerAcknowledgement,
|
|
event.NewGroupInvite,
|
|
event.PeerError,
|
|
event.SendMessageToGroupError,
|
|
event.NewGetValMessageFromPeer,
|
|
event.PeerStateChange,
|
|
event.NewRetValMessageFromPeer,
|
|
event.NewGroupInvite,
|
|
event.ServerStateChange,
|
|
}
|
|
settings := utils.ReadGlobalSettings()
|
|
settingsJson, _ := json.Marshal(settings)
|
|
|
|
newApp.LoadProfiles("be gay do crime")
|
|
newApp.LaunchPeers()
|
|
application = newApp
|
|
application.GetPrimaryBus().Publish(event.NewEvent(utils.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
|
|
log.Infof("libcwtch-go application SET\n")
|
|
}
|
|
|
|
//export c_ACNEvents
|
|
func c_ACNEvents() *C.char {
|
|
return C.CString(ACNEvents())
|
|
}
|
|
|
|
func ACNEvents() string {
|
|
select {
|
|
case myevent := <-acnQueue.OutChan():
|
|
return fmt.Sprintf("%v", myevent)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
//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)
|
|
default: // do nothing
|
|
}
|
|
}
|
|
|
|
//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) {
|
|
onion := C.GoStringN(onion_ptr, onion_len)
|
|
eventJson := C.GoStringN(json_ptr, json_len)
|
|
SendProfileEvent(onion, eventJson)
|
|
}
|
|
|
|
// 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 {
|
|
case event.SetAttribute:
|
|
peer.SetAttribute(new_event.Data[event.Key], new_event.Data[event.Data])
|
|
default:
|
|
// 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)
|
|
}
|
|
}
|
|
|
|
//export c_GetAppBusEvent
|
|
func c_GetAppBusEvent() *C.char {
|
|
return C.CString(GetAppBusEvent())
|
|
}
|
|
|
|
// GetAppBusEvent blocks until an event
|
|
func GetAppBusEvent() string {
|
|
var json = ""
|
|
for json == "" {
|
|
json = eventHandler.GetNextEvent()
|
|
}
|
|
return json
|
|
}
|
|
|
|
//export c_GetProfileRepaintEvent
|
|
func c_GetProfileRepaintEvent() int8 {
|
|
if GetProfileRepaintEvent() {
|
|
return 1
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func GetProfileRepaintEvent() bool {
|
|
<-acnQueue.OutChan()
|
|
return true
|
|
}
|
|
|
|
type Profile struct {
|
|
Name string `json:"name"`
|
|
Onion string `json:"onion"`
|
|
ImagePath string `json:"imagePath"`
|
|
}
|
|
|
|
//export c_GetProfiles
|
|
func c_GetProfiles() *C.char {
|
|
return C.CString(GetProfiles())
|
|
}
|
|
|
|
func GetProfiles() string {
|
|
peerList := application.ListPeers()
|
|
profiles := make([]Profile, len(peerList))
|
|
i := 0
|
|
for onion := range peerList {
|
|
name, _ := application.GetPeer(onion).GetAttribute(attr.GetPublicScope(constants.Name))
|
|
profiles[i] = Profile{
|
|
Name: name,
|
|
Onion: onion,
|
|
ImagePath: "",
|
|
}
|
|
i += 1
|
|
}
|
|
jsonBytes, _ := json.Marshal(profiles)
|
|
return string(jsonBytes)
|
|
}
|
|
|
|
//export c_GetContacts
|
|
func c_GetContacts(onion_ptr *C.char, onion_len C.int) *C.char {
|
|
return C.CString(GetContacts(C.GoStringN(onion_ptr, onion_len)))
|
|
}
|
|
|
|
func GetContacts(onion string) string {
|
|
log.Infof("Get Contacts for %v", onion)
|
|
mypeer := application.GetPeer(onion)
|
|
|
|
contactEventsQueue = event.NewQueue()
|
|
application.GetEventBus(onion).Subscribe(event.PeerStateChange, contactEventsQueue)
|
|
|
|
var contacts []utils.Contact
|
|
for _, contact := range mypeer.GetContacts() {
|
|
contactInfo := mypeer.GetContact(contact)
|
|
log.Infof("contactInfo %v", contactInfo)
|
|
contacts = append(contacts, utils.Contact{Name: contactInfo.Name, Onion: contactInfo.Onion, Status: contactInfo.State})
|
|
}
|
|
|
|
bytes, _ := json.Marshal(contacts)
|
|
return string(bytes)
|
|
}
|
|
|
|
//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))
|
|
}
|
|
|
|
func CreateProfile(nick, pass string) {
|
|
application.CreatePeer(nick, pass)
|
|
}
|
|
|
|
//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 {
|
|
log.Infof("Select Profile: %v", onion)
|
|
contactEventsQueue = event.NewQueue()
|
|
application.GetEventBus(onion).Subscribe(event.PeerStateChange, contactEventsQueue)
|
|
return ""
|
|
}
|
|
|
|
//export c_LoadProfiles
|
|
func c_LoadProfiles(passwordPtr *C.char, passwordLen C.int) {
|
|
LoadProfiles(C.GoStringN(passwordPtr, passwordLen))
|
|
}
|
|
|
|
func LoadProfiles(pass string) {
|
|
application.LoadProfiles(pass)
|
|
}
|
|
|
|
//export c_ContactEvents
|
|
func c_ContactEvents() *C.char {
|
|
return C.CString(ContactEvents())
|
|
}
|
|
|
|
func ContactEvents() string {
|
|
select {
|
|
case myevent := <-contactEventsQueue.OutChan():
|
|
return fmt.Sprintf("%v", myevent)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
//export c_NumMessages
|
|
func c_NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n int) {
|
|
profile := C.GoStringN(profile_ptr, profile_len)
|
|
handle := C.GoStringN(handle_ptr, handle_len)
|
|
return (NumMessages(profile, handle))
|
|
}
|
|
|
|
func NumMessages(profile, handle string) (n int) {
|
|
n = len(application.GetPeer(profile).GetContact(handle).Timeline.Messages)
|
|
log.Infof("NumMessagse(%s, %s) = %d", profile, handle, n)
|
|
return
|
|
}
|
|
|
|
//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 {
|
|
profile := C.GoStringN(profile_ptr, profile_len)
|
|
handle := C.GoStringN(handle_ptr, handle_len)
|
|
return C.CString(GetMessage(profile, handle, int(message_index)))
|
|
}
|
|
|
|
// Deprecate - 2021.01.14 - not used
|
|
func GetMessage(profile, handle string, message_index int) string {
|
|
message := application.GetPeer(profile).GetContact(handle).Timeline.Messages[message_index]
|
|
bytes, _ := json.Marshal(message)
|
|
log.Infof("GetMessage(%s, %s, %d) = %s", profile, handle, message_index, string(bytes))
|
|
return string(bytes)
|
|
}
|
|
|
|
//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]
|
|
bytes, _ := json.Marshal(messages)
|
|
return string(bytes)
|
|
}
|
|
|
|
// Leave as is, needed by ffi
|
|
func main() {}
|