diff --git a/constants/globals.go b/constants/globals.go index f71e3a4..8f9dd1a 100644 --- a/constants/globals.go +++ b/constants/globals.go @@ -11,4 +11,3 @@ const ( // StatusError is an event response for event.Status signifying a call failed in error, ideally accompanied by a event.Error StatusError = "error" ) - diff --git a/features/groups/group_functionality.go b/features/groups/group_functionality.go index 27d1242..1dffcf0 100644 --- a/features/groups/group_functionality.go +++ b/features/groups/group_functionality.go @@ -50,7 +50,7 @@ func (gf *GroupFunctionality) GetServerInfoList(profile peer.CwtchPeer) []Server // 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) + serverInfo, _ := profile.FetchConversationInfo(serverOnion) keyTypes := []model.KeyType{model.KeyTypeServerOnion, model.KeyTypeTokenOnion, model.KeyTypePrivacyPass} var serverKeys []ServerKey @@ -60,4 +60,4 @@ func (gf *GroupFunctionality) GetServerInfo(serverOnion string, profile peer.Cwt } } return Server{Onion: serverOnion, Status: connections.ConnectionStateName[profile.GetPeerState(serverInfo.Handle)], Keys: serverKeys} -} \ No newline at end of file +} diff --git a/features/servers/servers_functionality.go b/features/servers/servers_functionality.go index e07144c..c8147c4 100644 --- a/features/servers/servers_functionality.go +++ b/features/servers/servers_functionality.go @@ -18,7 +18,7 @@ const ( ZeroServersLoaded = event.Type("ZeroServersLoaded") NewServer = event.Type("NewServer") ServerIntentUpdate = event.Type("ServerIntentUpdate") - ServerDeleted = event.Type("ServerDeleted") + ServerDeleted = event.Type("ServerDeleted") ) const ( diff --git a/lib.go b/lib.go index 8a9f386..f3d1d44 100644 --- a/lib.go +++ b/lib.go @@ -7,12 +7,12 @@ package main import "C" import ( - // Import SQL Cipher - _ "github.com/mutecomm/go-sqlcipher/v4" "crypto/rand" constants2 "cwtch.im/cwtch/model/constants" "encoding/json" "fmt" + // Import SQL Cipher + _ "github.com/mutecomm/go-sqlcipher/v4" "os/user" "runtime" "strconv" @@ -248,18 +248,18 @@ func ReconnectCwtchForeground() { } settings := utils.ReadGlobalSettings() - groupHandler,_ := groups.ExperimentGate(settings.Experiments) + groupHandler, _ := groups.ExperimentGate(settings.Experiments) for _, profileOnion := range peerList { // fix peerpeercontact message counts profile := application.GetPeer(profileOnion) - conversations,_ := profile.FetchConversations() + conversations, _ := profile.FetchConversations() for _, conversation := range conversations { if (conversation.IsGroup() && groupHandler != nil) || conversation.IsServer() == false { - totalMessages,_ := profile.GetChannelMessageCount(conversation.ID, 0) + totalMessages, _ := profile.GetChannelMessageCount(conversation.ID, 0) eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{ - event.Identity: profileOnion, + event.Identity: profileOnion, event.ConversationID: strconv.Itoa(conversation.ID), - event.Data: strconv.Itoa(totalMessages), + event.Data: strconv.Itoa(totalMessages), })) } } @@ -504,6 +504,7 @@ func c_GetMessage(profile_ptr *C.char, profile_len C.int, conversation_id C.int, // EnhancedMessage wraps a Cwtch model.Message with some additional data to reduce calls from the UI. type EnhancedMessage struct { model.Message + ID int // the actual ID of the message in the database (not the row number) ContactImage string } @@ -516,15 +517,18 @@ func GetMessage(profileOnion string, conversationID int, messageIndex int) strin // these requests complete almost immediately v.s. being stalled for seconds to minutes on large groups. if application != nil { profile := application.GetPeer(profileOnion) - messages, err := profile.GetMostRecentMessages(conversationID,0, messageIndex, 1) - if err == nil && len (messages) == 1 { + messages, err := profile.GetMostRecentMessages(conversationID, 0, messageIndex, 1) + if err == nil && len(messages) == 1 { + time, _ := time.Parse(time.RFC3339Nano, messages[0].Attr[constants2.AttrSentTimestamp]) message.Message = model.Message{ - Message: messages[0].Body, + Message: messages[0].Body, Acknowledged: messages[0].Attr[constants2.AttrAck] == constants2.True, - Error: messages[0].Attr[constants2.AttrErr], - PeerID: messages[0].Attr["Author"], + Error: messages[0].Attr[constants2.AttrErr], + PeerID: messages[0].Attr[constants2.AttrAuthor], + Timestamp: time, } - message.ContactImage = utils.RandomProfileImage(messages[0].Attr["Author"]) + message.ID = messages[0].ID + message.ContactImage = utils.RandomProfileImage(message.PeerID) } } bytes, _ := json.Marshal(message) @@ -542,7 +546,27 @@ func c_GetMessagesByContentHash(profile_ptr *C.char, profile_len C.int, conversa func GetMessagesByContentHash(profileOnion string, handle int, contentHash string) string { var indexedMessages []model.LocallyIndexedMessage if application != nil { - // TODO + profile := application.GetPeer(profileOnion) + id, err := profile.GetChannelMessageByContentHash(handle, 0, contentHash) + if err == nil { + // TODO this is terrible + messages, err := profile.GetMostRecentMessages(handle, 0, 0, 100) + if err == nil { + for i, message := range messages { + if message.ID == id { + time, _ := time.Parse(time.RFC3339Nano, messages[0].Attr[constants2.AttrSentTimestamp]) + msg := model.Message{ + Message: messages[0].Body, + Acknowledged: messages[0].Attr[constants2.AttrAck] == constants2.True, + Error: messages[0].Attr[constants2.AttrErr], + PeerID: messages[0].Attr[constants2.AttrAuthor], + Timestamp: time, + } + indexedMessages = append(indexedMessages, model.LocallyIndexedMessage{LocalIndex: i, Message: msg}) + } + } + } + } } bytes, _ := json.Marshal(indexedMessages) return string(bytes) @@ -554,8 +578,6 @@ func c_FreePointer(ptr *C.char) { C.free(unsafe.Pointer(ptr)) } - - //export c_SendMessage func c_SendMessage(profile_ptr *C.char, profile_len C.int, conversation_id C.int, msg_ptr *C.char, msg_len C.int) { profile := C.GoStringN(profile_ptr, profile_len) @@ -569,7 +591,7 @@ func SendMessage(profileOnion string, conversationID int, msg string) { } //export c_SendInvitation -func c_SendInvitation(profile_ptr *C.char, profile_len C.int, conversation_id C.int, target_id C.int) { +func c_SendInvitation(profile_ptr *C.char, profile_len C.int, conversation_id C.int, target_id C.int) { profile := C.GoStringN(profile_ptr, profile_len) SendInvitation(profile, int(conversation_id), int(target_id)) } @@ -589,7 +611,7 @@ func c_ShareFile(profile_ptr *C.char, profile_len C.int, conversation_id C.int, ShareFile(profile, int(conversation_id), sharefilepath) } -func ShareFile(profileOnion string , conversationID int, sharefilepath string) { +func ShareFile(profileOnion string, conversationID int, sharefilepath string) { profile := application.GetPeer(profileOnion) fh, err := filesharing.FunctionalityGate(utils.ReadGlobalSettings().Experiments) if err != nil { diff --git a/utils/contacts.go b/utils/contacts.go index 4c28052..1a5f2d1 100644 --- a/utils/contacts.go +++ b/utils/contacts.go @@ -13,5 +13,5 @@ type Contact struct { IsGroup bool `json:"isGroup"` GroupServer string `json:"groupServer"` IsArchived bool `json:"isArchived"` - Identifier int `json:"identifier"` + Identifier int `json:"identifier"` } diff --git a/utils/eventHandler.go b/utils/eventHandler.go index 79c392a..da6a4ef 100644 --- a/utils/eventHandler.go +++ b/utils/eventHandler.go @@ -123,7 +123,6 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string { } } - online, _ := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants2.PeerOnline) // Name always exists e.Data[constants.Name], _ = profile.GetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name) @@ -152,8 +151,19 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string { continue } - name,_ := conversationInfo.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name) - cpicPath := RandomProfileImage(conversationInfo.Handle) + name, exists := conversationInfo.GetAttribute(attr.LocalScope, attr.ProfileZone, constants.Name) + if !exists { + name, exists = conversationInfo.GetAttribute(attr.PeerScope, attr.ProfileZone, constants.Name) + if !exists { + name = conversationInfo.Handle + } + } + var cpicPath string + if conversationInfo.IsGroup() { + cpicPath = RandomGroupImage(conversationInfo.Handle) + } else { + cpicPath = RandomProfileImage(conversationInfo.Handle) + } saveHistory, set := conversationInfo.GetAttribute(attr.LocalScope, attr.ProfileZone, event.SaveHistoryKey) if !set { saveHistory = event.DeleteHistoryDefault @@ -177,19 +187,18 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string { authorization = model.AuthBlocked } - groupServer,_ := conversationInfo.GetAttribute(attr.LocalScope, attr.LegacyGroupZone, constants.GroupServer) + groupServer, _ := conversationInfo.GetAttribute(attr.LocalScope, attr.LegacyGroupZone, constants.GroupServer) count, err := profile.GetChannelMessageCount(conversationInfo.ID, 0) if err != nil { log.Errorf("error fetching channel message count %v %v", conversationInfo.ID, err) } - lastMessage,_ := profile.GetMostRecentMessages(conversationInfo.ID, 0, 0,1) - + lastMessage, _ := profile.GetMostRecentMessages(conversationInfo.ID, 0, 0, 1) contacts = append(contacts, Contact{ Name: name, - Identifier: conversationInfo.ID, + Identifier: conversationInfo.ID, Onion: conversationInfo.Handle, Status: connections.ConnectionStateName[state], Picture: cpicPath, @@ -235,18 +244,19 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string { profile.SetConversationAttribute(ci.ID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants2.Archived)), event.False) } else { // TODO This Conversation May Not Exist Yet...But we are not in charge of creating it... + log.Errorf("todo wait for contact to be added before processing this event...") } - ev.Event.Data["Nick"],_ = ci.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name) + ev.Event.Data["Nick"], _ = ci.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name) ev.Event.Data["Picture"] = RandomProfileImage(ev.Event.Data["RemotePeer"]) case event.NewMessageFromGroup: // only needs contact nickname and picture, for displaying on popup notifications ci, err := profile.FetchConversationInfo(ev.Event.Data["RemotePeer"]) if ci != nil && err == nil { - ev.Event.Data["Nick"],_ = ci.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name) + ev.Event.Data["Nick"], _ = ci.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name) } ev.Event.Data["Picture"] = RandomProfileImage(ev.Event.Data[event.GroupID]) - conversationID,_ := strconv.Atoi(ev.Event.Data[event.ConversationID]) + conversationID, _ := strconv.Atoi(ev.Event.Data[event.ConversationID]) profile.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants2.Archived)), event.False) case event.PeerAcknowledgement: ci, err := profile.FetchConversationInfo(ev.Event.Data["RemotePeer"]) @@ -254,14 +264,14 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string { ev.Event.Data[event.ConversationID] = strconv.Itoa(ci.ID) } case event.ContactCreated: - conversationID,_ := strconv.Atoi(ev.Event.Data[event.ConversationID]) + conversationID, _ := strconv.Atoi(ev.Event.Data[event.ConversationID]) handle := ev.Event.Data[event.RemotePeer] count, err := profile.GetChannelMessageCount(conversationID, 0) if err != nil { log.Errorf("error fetching channel message count %v %v", conversationID, err) } - lastMessage,_ := profile.GetMostRecentMessages(conversationID, 0, 0,1) + lastMessage, _ := profile.GetMostRecentMessages(conversationID, 0, 0, 1) ev.Event.Data["unread"] = strconv.Itoa(1) // we've just created this contact so by definition this must be 1... ev.Event.Data["picture"] = RandomProfileImage(handle) ev.Event.Data["numMessages"] = strconv.Itoa(count) @@ -288,7 +298,7 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string { } case event.PeerStateChange: cxnState := connections.ConnectionStateToType()[ev.Event.Data[event.ConnectionState]] - contact,_ := profile.FetchConversationInfo(ev.Event.Data[event.RemotePeer]) + contact, _ := profile.FetchConversationInfo(ev.Event.Data[event.RemotePeer]) if cxnState == connections.AUTHENTICATED && contact == nil { profile.NewContactConversation(ev.Event.Data[event.RemotePeer], model.AccessControl{Read: false, Append: false, Blocked: false}, false) @@ -307,13 +317,13 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string { case event.NewRetValMessageFromPeer: // auto handled event means the setting is already done, we're just deciding if we need to tell the UI - conversationID,_ := strconv.Atoi(ev.Event.Data[event.ConversationID]) + conversationID, _ := strconv.Atoi(ev.Event.Data[event.ConversationID]) scope := ev.Event.Data[event.Scope] path := ev.Event.Data[event.Path] exists, _ := strconv.ParseBool(ev.Event.Data[event.Exists]) if exists && attr.IntoScope(scope) == attr.PublicScope { - zone,path := attr.ParseZone(path) + zone, path := attr.ParseZone(path) if _, err := profile.GetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(zone.ConstructZonedPath(path))); err != nil { // we have a locally set override, don't pass this remote set public scope update to UI return "" @@ -396,4 +406,4 @@ func getLastMessageTime(conversationMessages []model.ConversationMessage) int { return 0 } return int(time.Unix()) -} \ No newline at end of file +} diff --git a/utils/utils.go b/utils/utils.go index 8561ad0..fd7ef3b 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -12,7 +12,7 @@ func RandomProfileImage(onion string) string { choices := []string{"001-centaur", "002-kraken", "003-dinosaur", "004-tree-1", "005-hand", "006-echidna", "007-robot", "008-mushroom", "009-harpy", "010-phoenix", "011-dragon-1", "012-devil", "013-troll", "014-alien", "015-minotaur", "016-madre-monte", "017-satyr", "018-karakasakozou", "019-pirate", "020-werewolf", "021-scarecrow", "022-valkyrie", "023-curupira", "024-loch-ness-monster", "025-tree", "026-cerberus", "027-gryphon", "028-mermaid", "029-vampire", "030-goblin", "031-yeti", "032-leprechaun", "033-medusa", "034-chimera", "035-elf", "036-hydra", "037-cyclops", "038-pegasus", "039-narwhal", "040-woodcutter", "041-zombie", "042-dragon", "043-frankenstein", "044-witch", "045-fairy", "046-genie", "047-pinocchio", "048-ghost", "049-wizard", "050-unicorn"} barr, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion)) if err != nil || len(barr) != 35 { - log.Errorf("error: %v %v %v\n", onion, err, barr) + log.Errorf("error finding image for profile: %v %v %v\n", onion, err, barr) return "extra/openprivacy.png" } return "profiles/" + choices[int(barr[33])%len(choices)] + ".png" @@ -22,7 +22,7 @@ func RandomGroupImage(handle string) string { choices := []string{"001-borobudur", "002-opera-house", "003-burj-al-arab", "004-chrysler", "005-acropolis", "006-empire-state-building", "007-temple", "008-indonesia-1", "009-new-zealand", "010-notre-dame", "011-space-needle", "012-seoul", "013-mosque", "014-milan", "015-statue", "016-pyramid", "017-cologne", "018-brandenburg-gate", "019-berlin-cathedral", "020-hungarian-parliament", "021-buckingham", "022-thailand", "023-independence", "024-angkor-wat", "025-vaticano", "026-christ-the-redeemer", "027-colosseum", "028-golden-gate-bridge", "029-sphinx", "030-statue-of-liberty", "031-cradle-of-humankind", "032-istanbul", "033-london-eye", "034-sagrada-familia", "035-tower-bridge", "036-burj-khalifa", "037-washington", "038-big-ben", "039-stonehenge", "040-white-house", "041-ahu-tongariki", "042-capitol", "043-eiffel-tower", "044-church-of-the-savior-on-spilled-blood", "045-arc-de-triomphe", "046-windmill", "047-louvre", "048-torii-gate", "049-petronas", "050-matsumoto-castle", "051-fuji", "052-temple-of-heaven", "053-pagoda", "054-chichen-itza", "055-forbidden-city", "056-merlion", "057-great-wall-of-china", "058-taj-mahal", "059-pisa", "060-indonesia"} barr, err := hex.DecodeString(handle) if err != nil || len(barr) == 0 { - log.Errorf("error: %v %v %v\n", handle, err, barr) + log.Errorf("error finding image for group: %v %v %v\n", handle, err, barr) return "extra/openprivacy.png" } return "servers/" + choices[int(barr[0])%len(choices)] + ".png"