diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c24322b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +cwtch-sources.jar +cwtch.aar +libCwtch.h +libCwtch.so diff --git a/README.md b/README.md index 4bb7050..2a98146 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ # Build Instructions... - go build -buildmode c-shared -o libCwtch.so + make linux + make android # Using -LD_LIBRARY_PATH set to point to libCwtch.so +## Linux Desktop: + + - `LD_LIBRARY_PATH` set to point to `libCwtch.so` + - or drop a symlink into `/usr/lib` + +## Android + +- copy `cwtch.aar` into `flutter_app/android/cwtch` diff --git a/lib.go b/lib.go index d714b6a..a9b71db 100644 --- a/lib.go +++ b/lib.go @@ -21,19 +21,21 @@ import ( ) var application app.Application +var appBusQueue event.Queue var acnQueue event.Queue var contactEventsQueue event.Queue -//export HelloWorld -func HelloWorld(a C.int, b C.int) C.int { - return a+b +//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) } -//export StartCwtch -func StartCwtch(dir_c *C.char, len C.int) { - dir := C.GoStringN(dir_c, len) +func StartCwtch(appDir string, torPath string) { log.SetLevel(log.LevelDebug) - log.Infof("Loading Cwtch Directory %v", dir) + + log.Infof("Loading Cwtch Directory %v and tor path: %v", appDir, torPath) mrand.Seed(int64(time.Now().Nanosecond())) port := mrand.Intn(1000) + 9600 @@ -46,20 +48,28 @@ func StartCwtch(dir_c *C.char, len C.int) { panic(err) } - log.Infof("making directory %v", dir) - os.MkdirAll(path.Join(dir, "/.tor","tor"),0700) - tor.NewTorrc().WithSocksPort(port).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key)).Build(filepath.Join(dir, ".tor", "tor", "torrc")) - acn, err := tor.NewTorACNWithAuth(path.Join(dir, "/.tor"), "", controlPort, tor.HashedPasswordAuthenticator{base64.StdEncoding.EncodeToString(key)}) + 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, dir) - id := mrand.Int31() + newApp := app.NewApp(acn, appDir) acnQueue = event.NewQueue() newApp.GetPrimaryBus().Subscribe(event.ACNStatus, acnQueue) + + appBusQueue = event.NewQueue() + newApp.GetPrimaryBus().Subscribe(event.NewPeer, appBusQueue) + newApp.GetPrimaryBus().Subscribe(event.PeerError, appBusQueue) + newApp.GetPrimaryBus().Subscribe(event.AppError, appBusQueue) + newApp.GetPrimaryBus().Subscribe(event.ACNStatus, appBusQueue) + newApp.GetPrimaryBus().Subscribe(event.ReloadDone, appBusQueue) + newApp.GetPrimaryBus().Subscribe(event.ACNVersion, appBusQueue) + + // Lol this wasn't an intended use peer.DefaultEventsToHandle = []event.Type{ event.EncryptedGroupMessage, event.NewMessageFromPeer, @@ -73,41 +83,81 @@ func StartCwtch(dir_c *C.char, len C.int) { newApp.LoadProfiles("be gay do crime") newApp.LaunchPeers() application = newApp - log.Infof("Providing Handle %v", id) + log.Infof("libcwtch-go application SET\n") } -//export ACNEvents -func ACNEvents() *C.char { +//export c_ACNEvents +func c_ACNEvents() *C.char { + return C.CString(ACNEvents()) +} + +func ACNEvents() string { select { case myevent := <- acnQueue.OutChan(): - return C.CString(fmt.Sprintf("%v", myevent)) + return fmt.Sprintf("%v", myevent) default: - return C.CString("") + return "" } } -//export NextEvent -func NextEvent() { - +//export c_AppBusEvent +func c_AppBusEvent() *C.char { + return C.CString(GetAppBusEvent()) } -//export GetProfiles -func GetProfiles() *C.char { - profiles := application.ListPeers() + +// GetAppBusEvent blocks until an event +func GetAppBusEvent() string { + select { + case myevent := <- acnQueue.OutChan(): + return fmt.Sprintf("%v", myevent) + default: + return "" + } + event := appBusQueue.Next() + return fmt.Sprintf("%v", event) +} + +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, name := range peerList { + profiles[i] = Profile{ + Name: name, + Onion: onion, + ImagePath: "", + } + i += 1 + } jsonBytes,_ := json.Marshal(profiles) - return C.CString(string(jsonBytes)) + return string(jsonBytes) } + type Contact struct { Name string `json:"name"` Onion string `json:"onion"` Status string `json:"status"` } -//export GetContacts -func GetContacts(onion_ptr *C.char, onion_len C.int) *C.char { +//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))) +} - onion := C.GoStringN(onion_ptr, onion_len) +func GetContacts(onion string)string { log.Infof("Get Contacts for %v", onion) mypeer := application.GetPeer(onion) @@ -122,47 +172,92 @@ func GetContacts(onion_ptr *C.char, onion_len C.int) *C.char { } bytes,_ := json.Marshal(contacts) - return C.CString(string(bytes)) + 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 SelectProfile -func SelectProfile(onion_ptr *C.char, onion_len C.int) *C.char { - onion := C.GoStringN(onion_ptr, onion_len) +//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 C.CString("") + return "" } -//export ContactEvents -func ContactEvents() *C.char { +//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 C.CString(fmt.Sprintf("%v", myevent)) + return fmt.Sprintf("%v", myevent) default: - return C.CString("") + return "" } } -//export NumMessages -func NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n C.int) { +//export c_NumMessages +func c_NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n C.int) { profile := C.GoStringN(profile_ptr, profile_len) handle := C.GoStringN(handle_ptr, handle_len) - n = C.int(len(application.GetPeer(profile).GetContact(handle).Timeline.Messages)) + return C.int(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 GetMessage -func GetMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, message_index C.int) *C.char { +//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 C.CString(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