Compare commits
101 Commits
alpha0
...
5aec7b3ad4
Author | SHA1 | Date | |
---|---|---|---|
5aec7b3ad4 | |||
d709cb9454 | |||
05df08efcb | |||
813611bde7 | |||
7fa997d443 | |||
2513f0303a | |||
99a9aa14af | |||
09892709ec | |||
3ac6b02e56 | |||
8fca09d853 | |||
aaa4d88a2f | |||
3bf75eb990 | |||
54c36c68ad | |||
caab80f346 | |||
dfa2b5fa83 | |||
2ac70d5448 | |||
2a246744db | |||
b4f7071990 | |||
903702c719 | |||
54b932e9c1 | |||
13cfda928d | |||
b556cd0361 | |||
657fdbbf48 | |||
a65d4f1a69 | |||
a5cfbf854d | |||
385c5f3298 | |||
31df45e771 | |||
0b8e3c4c90 | |||
ce479cc5b9 | |||
bdfa8c7bb1 | |||
8300a699a3 | |||
729ba7e02a | |||
b15f571938 | |||
ead810e666 | |||
4b412ae0f3 | |||
e4efff1824 | |||
788512c391 | |||
6bf6fadaaa | |||
0070a64d5f | |||
1398c6040a | |||
12ad5ced49 | |||
53145f1c5e | |||
6867086c4f | |||
f80411bf21 | |||
0a70206b11 | |||
f40f6520d2 | |||
b47ef2480c | |||
cba13ad91a | |||
1ba84dcefc | |||
0c0aa6e807 | |||
3467ea15d9 | |||
aa63bb745f | |||
f20681adab | |||
4b3d7548bd | |||
c4b61e16c5 | |||
9dcb579d93 | |||
defafcf996 | |||
db4c3cbbc8 | |||
07dfae8f0e | |||
05cc2ee218 | |||
034dcf5215 | |||
c58199385e | |||
b6b9dc238a | |||
9ec682d708 | |||
465a366e79 | |||
2969227656 | |||
9561531c7c | |||
8c8326780f | |||
0466b1fe05 | |||
69a07d77d5 | |||
a19f228c8e | |||
bcb3489de4 | |||
0fd7548ba4 | |||
24183ff581 | |||
df9c6b5d46 | |||
423ef5c4b1 | |||
b87c0bff3e | |||
978b6fdfd1 | |||
93e972900f | |||
f8a1cb6f68 | |||
44661de993 | |||
48b2e78b41 | |||
a9f3b548e5 | |||
379b40b2fb | |||
cbedad7178 | |||
9283764f42 | |||
25cf1808e3 | |||
6788487368 | |||
e406010374 | |||
289e39c677 | |||
6511ff6280 | |||
206dda0761 | |||
7a6c1cd085 | |||
04e81fcef1 | |||
0998845817 | |||
922668e2a3 | |||
5eb6be1415 | |||
a3b2473eed | |||
043980042d | |||
58118bd6bb | |||
86d51d6dfb |
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ client/test.cfg
|
||||
.VSCodeCouter/
|
||||
meowlib-sources.jar
|
||||
meowlib.aar
|
||||
client/test.db
|
@ -19,3 +19,10 @@ run the shell scripts
|
||||
# Tests
|
||||
|
||||
|
||||
# Design notes
|
||||
Config is written as a json file
|
||||
Identity is stored as an encrypted json file
|
||||
Message servers (messaging and my contact's messaging) are stored in an encrypted badger db with server url as key
|
||||
Received servers are stored in a sqlite db for selective searches, with storage limits
|
||||
Messages are stored in several badger? or sqlite? db per user with send/receive time as key
|
||||
|
||||
|
@ -8,8 +8,10 @@ import (
|
||||
|
||||
type Config struct {
|
||||
// UserConfig
|
||||
SavePassword bool `json:"save_password,omitempty"`
|
||||
SavedPassword string `json:"saved_password,omitempty"`
|
||||
SavePassword bool `json:"save_password,omitempty"`
|
||||
SavedPassword string `json:"saved_password,omitempty"`
|
||||
PasswordTip string `json:"password_tip,omitempty"`
|
||||
PasswordTipUnlock string `json:"password_tip_unlock,omitempty"`
|
||||
// Technical
|
||||
IdentityFile string `json:"identity_file,omitempty"`
|
||||
StoragePath string `json:"storage_path,omitempty"`
|
||||
@ -42,9 +44,9 @@ type Config struct {
|
||||
DbSuffix string `json:"db_suffix,omitempty"`
|
||||
|
||||
// Inner
|
||||
memoryPassword string `json:"memory_password,omitempty"`
|
||||
additionalPasswords []string `json:"additional_passwords,omitempty"`
|
||||
me *Identity `json:"me,omitempty"`
|
||||
memoryPassword string
|
||||
additionalPasswords []string
|
||||
me *Identity
|
||||
}
|
||||
|
||||
var instance *Config
|
||||
@ -74,7 +76,7 @@ func (c *Config) Save(filename string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.WriteFile(filename, data, 0644)
|
||||
os.WriteFile(filename, data, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
38
client/dbmessage.go
Normal file
38
client/dbmessage.go
Normal file
@ -0,0 +1,38 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
)
|
||||
|
||||
func DbMessageToInternalUserMessage(id int64, dbFile string, dbm *meowlib.DbMessage) *InternalUserMessage {
|
||||
var ium InternalUserMessage
|
||||
ium.Dbid = id
|
||||
ium.Dbfile = dbFile
|
||||
if dbm.Outbound {
|
||||
ium.Outbound = true
|
||||
} else {
|
||||
ium.Outbound = false
|
||||
}
|
||||
ium.Message = string(dbm.Data)
|
||||
ium.ConversationStatus = dbm.Status
|
||||
ium.Contact = dbm.Contact
|
||||
ium.CurrentLocation = dbm.CurrentLocation
|
||||
ium.Messagetype = dbm.Type
|
||||
ium.Appdata = dbm.Appdata
|
||||
ium.FilePaths = dbm.FilePaths
|
||||
return &ium
|
||||
}
|
||||
|
||||
func UserMessageToDbMessage(outbound bool, um *meowlib.UserMessage, filepaths []string) *meowlib.DbMessage {
|
||||
var dbm meowlib.DbMessage
|
||||
dbm.Outbound = outbound
|
||||
dbm.Type = um.Type
|
||||
dbm.Data = um.Data
|
||||
dbm.Appdata = um.Appdata
|
||||
dbm.Contact = um.Contact
|
||||
dbm.CurrentLocation = um.CurrentLocation
|
||||
dbm.Status = um.Status
|
||||
dbm.FilePaths = filepaths
|
||||
return &dbm
|
||||
|
||||
}
|
185
client/helpers/backgroundHelper.go
Normal file
185
client/helpers/backgroundHelper.go
Normal file
@ -0,0 +1,185 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
"github.com/google/uuid"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type ReceivedMessage struct {
|
||||
Text string
|
||||
files []string
|
||||
Server string
|
||||
Sent uint64
|
||||
Received uint64
|
||||
LocalUuid string
|
||||
LocalSequence uint64
|
||||
AppData string
|
||||
Location meowlib.Location
|
||||
}
|
||||
|
||||
// CheckForMessages checks for messages on a single server
|
||||
func CheckForMessages(storage_path string, job *client.RequestsJob) (int, string, error) {
|
||||
|
||||
count := 0
|
||||
|
||||
// if folder does not exist, create it
|
||||
if _, err := os.Stat(filepath.Join(storage_path, "inbox")); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(filepath.Join(storage_path, "inbox"), 0700)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: MkdirAll", err
|
||||
}
|
||||
}
|
||||
//convert server to a server object
|
||||
|
||||
var crl []*meowlib.ConversationRequest
|
||||
// build conversation requests
|
||||
if job.LookupKeys != nil {
|
||||
for _, key := range job.LookupKeys {
|
||||
var cr meowlib.ConversationRequest
|
||||
cr.LookupKey = key.Public
|
||||
cr.SendTimestamp = time.Now().UTC().Unix()
|
||||
// todo sign it
|
||||
//cr.LookupSignature =
|
||||
crl = append(crl, &cr)
|
||||
}
|
||||
// get server public key
|
||||
if job.Server.PublicKey == "" {
|
||||
key, err := meowlib.HttpGetId(job.Server.Url)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: HttpGetId", err
|
||||
}
|
||||
job.Server.PublicKey = key["publicKey"]
|
||||
}
|
||||
// build server message
|
||||
var toSrv meowlib.ToServerMessage
|
||||
toSrv.PullRequest = crl
|
||||
toSrv.From = job.Server.UserKp.Public
|
||||
data, err := job.Server.ProcessOutboundMessage(&toSrv)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: ProcessOutboundMessage", err
|
||||
}
|
||||
|
||||
response, err := meowlib.HttpPostMessage(job.Server.Url, data)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: httpPostMessage", err
|
||||
}
|
||||
fs_msg, err := job.Server.ProcessInboundServerResponse(response)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: ProcessInboundServerResponse", err
|
||||
}
|
||||
if len(fs_msg.Chat) > 0 || (fs_msg.Invitation != nil && fs_msg.Invitation.Step == 3) {
|
||||
// chat or invitation answer => save the server message
|
||||
|
||||
out, err := proto.Marshal(fs_msg)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: protobuf marshal", err
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(storage_path, "inbox", strconv.FormatInt(time.Now().UTC().UnixNano(), 10)), out, 0644); err != nil {
|
||||
return -1, "CheckMessages: WriteFile", err
|
||||
}
|
||||
}
|
||||
count = len(fs_msg.Chat)
|
||||
} else {
|
||||
// manage non uszer messages like devices or server
|
||||
}
|
||||
|
||||
return count, "", nil
|
||||
}
|
||||
|
||||
// SaveCheckJobs
|
||||
func SaveCheckJobs() (string, error) {
|
||||
me := client.GetConfig().GetIdentity()
|
||||
err := me.SaveBackgroundJob()
|
||||
if err != nil {
|
||||
|
||||
return "CheckMessages: json.Marshal", err
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// ReadMessage
|
||||
func ReadMessage(messageFilename string) ([]string, []string, string, error) {
|
||||
|
||||
messagesOverview := []string{}
|
||||
filenames := []string{}
|
||||
identity := client.GetConfig().GetIdentity()
|
||||
// read message file
|
||||
msg, err := os.ReadFile(messageFilename)
|
||||
if err != nil {
|
||||
return nil, nil, "ReadMessage: ReadFile", err
|
||||
}
|
||||
// protobuf unmarshal message
|
||||
var fromServerMessage meowlib.FromServerMessage
|
||||
err = proto.Unmarshal(msg, &fromServerMessage)
|
||||
if err != nil {
|
||||
return nil, nil, "ReadMessage: Unmarshal FromServerMessage", err
|
||||
}
|
||||
// check if invitation answer
|
||||
if fromServerMessage.Invitation != nil {
|
||||
invitationGetAnswerReadResponse(fromServerMessage.Invitation)
|
||||
}
|
||||
// Chat messages
|
||||
if len(fromServerMessage.Chat) > 0 {
|
||||
for _, packedUserMessage := range fromServerMessage.Chat {
|
||||
|
||||
// find the peer with that lookup key
|
||||
peer := identity.Peers.GetFromMyLookupKey(packedUserMessage.Destination)
|
||||
if peer == nil {
|
||||
return nil, nil, "ReadMessage: GetFromMyLookupKey", errors.New("no visible peer for that message")
|
||||
}
|
||||
// Unpack the message
|
||||
usermsg, err := peer.ProcessInboundUserMessage(packedUserMessage.Payload, packedUserMessage.Signature)
|
||||
if err != nil {
|
||||
return nil, nil, "ReadMessage: ProcessInboundUserMessage", err
|
||||
}
|
||||
|
||||
//fmt.Println("From:", usermsg.From)
|
||||
//jsonUserMessage, _ := json.Marshal(usermsg)
|
||||
//fmt.Println(string(jsonUserMessage))
|
||||
peer = client.GetConfig().GetIdentity().Peers.GetFromPublicKey(usermsg.From)
|
||||
|
||||
// detach files
|
||||
if usermsg.Files != nil {
|
||||
// create files folder
|
||||
if _, err := os.Stat(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files")); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files"), 0700)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, file := range usermsg.Files {
|
||||
filename := uuid.New().String() + "_" + file.Filename
|
||||
filenames = append(filenames, peer.Name+" sent: "+filename)
|
||||
// detach file
|
||||
os.WriteFile(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files", filename), file.Data, 0600)
|
||||
}
|
||||
//? result["invitation finalized"] = peer.Name
|
||||
}
|
||||
// user message
|
||||
|
||||
messagesOverview = append(messagesOverview, peer.Name+" > "+string(usermsg.Data))
|
||||
// add message to storage
|
||||
err = peer.StoreMessage(usermsg, filenames)
|
||||
if err != nil {
|
||||
return nil, nil, "ReadMessage: StoreMessage", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = os.Remove(messageFilename)
|
||||
if err != nil {
|
||||
return nil, nil, "ReadMessage: Remove", err
|
||||
}
|
||||
|
||||
// list of messages & detached files
|
||||
return messagesOverview, filenames, "", nil
|
||||
}
|
1
client/helpers/contactHelper.go
Normal file
1
client/helpers/contactHelper.go
Normal file
@ -0,0 +1 @@
|
||||
package helpers
|
153
client/helpers/invitationAnswerHelper.go
Normal file
153
client/helpers/invitationAnswerHelper.go
Normal file
@ -0,0 +1,153 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"C"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// InvitationAnswer
|
||||
func InvitationAnswer(cc *meowlib.ContactCard, nickname string, myNickname string, serverUids []string) (*client.Peer, string, error) {
|
||||
|
||||
mynick := myNickname
|
||||
// my nickname for that contact
|
||||
|
||||
if myNickname == "" {
|
||||
mynick = client.GetConfig().GetIdentity().Nickname
|
||||
}
|
||||
|
||||
// build my contact card for that friend
|
||||
peer := client.GetConfig().GetIdentity().AnswerInvitation(mynick, nickname, serverUids, cc)
|
||||
|
||||
//peerstr, err := json.Marshal(peer)
|
||||
//fmt.Println("InvitationAnswer: " + string(peerstr))
|
||||
c := client.GetConfig()
|
||||
c.GetIdentity().Save()
|
||||
|
||||
return peer, "", nil
|
||||
}
|
||||
|
||||
// InvitationAnswerFile
|
||||
func InvitationAnswerFile(invitationFile string, nickname string, myNickname string, serverUids []string) (string, error) {
|
||||
format := "qr"
|
||||
var filename string = ""
|
||||
var cc *meowlib.ContactCard
|
||||
c := client.GetConfig()
|
||||
if _, err := os.Stat(invitationFile); os.IsNotExist(err) {
|
||||
return "InvitationAnswerFile : os.Stat", err
|
||||
}
|
||||
if strings.HasSuffix(invitationFile, ".mwiv") {
|
||||
format = "mwiv"
|
||||
data, err := os.ReadFile(invitationFile)
|
||||
if err != nil {
|
||||
return "InvitationAnswerFile : os.ReadFile", err
|
||||
}
|
||||
cc, err = meowlib.NewContactCardFromCompressed(data)
|
||||
if err != nil {
|
||||
return "InvitationAnswerFile : NewContactCardFromCompressed", err
|
||||
}
|
||||
}
|
||||
identity := client.GetConfig().GetIdentity()
|
||||
if cc != nil {
|
||||
isAnswer, proposed, received, _ := identity.CheckInvitation(cc)
|
||||
if isAnswer {
|
||||
fmt.Fprintln(os.Stdout, "This is already a response "+proposed+" to your invitation.")
|
||||
fmt.Fprintln(os.Stdout, "You cannot answer again.")
|
||||
fmt.Fprintln(os.Stdout, "You should finalize it by importing "+proposed+" contact card to your meow.")
|
||||
fmt.Fprintln(os.Stdout, "Use : 'meow invitation finalize "+invitationFile+"' to do it.")
|
||||
|
||||
} else {
|
||||
mynick := myNickname
|
||||
// my nickname for that contact
|
||||
|
||||
if myNickname == "" {
|
||||
mynick = client.GetConfig().GetIdentity().Nickname
|
||||
}
|
||||
|
||||
response := identity.AnswerInvitation(mynick, nickname, serverUids, cc)
|
||||
fmt.Fprintln(os.Stdout, "Invitation sent by "+received)
|
||||
if format == "qr" {
|
||||
filename = c.StoragePath + string(os.PathSeparator) + mynick + "-" + nickname + ".png"
|
||||
response.GetMyContact().WriteQr(filename)
|
||||
} else {
|
||||
filename = c.StoragePath + string(os.PathSeparator) + mynick + "-" + nickname + ".mwiv"
|
||||
response.GetMyContact().WriteCompressed(filename)
|
||||
}
|
||||
client.GetConfig().GetIdentity().Save()
|
||||
}
|
||||
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// InvitationAnswerMessage
|
||||
func InvitationAnswerMessage(invitationId string, invitationServerUid string, timeout int) ([]byte, string, error) {
|
||||
|
||||
// find the peer with that invitation id
|
||||
var peer *client.Peer
|
||||
for i := len(client.GetConfig().GetIdentity().Peers) - 1; i >= 0; i-- { //! to allow self invitation : testing only, findinc the received peer before myself
|
||||
// for i := 0; i < len(client.GetConfig().GetIdentity().Peers); i++ {
|
||||
if client.GetConfig().GetIdentity().Peers[i].InvitationId == invitationId {
|
||||
peer = client.GetConfig().GetIdentity().Peers[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if peer == nil {
|
||||
// declare a custom go error for no peer found
|
||||
return nil, "InvitationAnswerMessage: loop for peer", errors.New("no peer with that invitation id")
|
||||
}
|
||||
answermsg, err := peer.BuildInvitationAnswerMessage(peer.GetMyContact())
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessage: BuildInvitationAnswserMessage", err
|
||||
}
|
||||
// Server: get the invitation server
|
||||
invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessage: LoadServer", err
|
||||
}
|
||||
|
||||
// this will be the invitation's payload
|
||||
packedMsg, err := peer.ProcessOutboundUserMessage(answermsg)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessage: ProcessOutboundUserMessage", err
|
||||
}
|
||||
// Creating Server message for transporting the user message
|
||||
toServerMessage, err := invitationServer.BuildToServerMessageInvitationAnswer(packedMsg, peer.MyIdentity.Public, invitationId, timeout)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessage: BuildToServerMessageInvitationAnswer", err
|
||||
}
|
||||
|
||||
// Server outbound processing
|
||||
bytemsg, err := invitationServer.ProcessOutboundMessage(toServerMessage)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessage: ProcessOutboundMessage", err
|
||||
}
|
||||
return bytemsg, "", nil
|
||||
}
|
||||
|
||||
// InvitationAnswerMessageReadResponse
|
||||
// Called by the invitation receiver
|
||||
// invitationData: the data received from the server
|
||||
// invitationServerUid: the uid of the server holding the invitation
|
||||
func InvitationAnswerMessageReadResponse(invitationData []byte, invitationServerUid string) (*meowlib.Invitation, string, error) {
|
||||
|
||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessageReadResponse: LoadServer", err
|
||||
}
|
||||
// Server inbound processing : get the invitation server
|
||||
serverMsg, err := server.ProcessInboundServerResponse(invitationData)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessageReadResponse: ProcessInboundServerResponse", err
|
||||
}
|
||||
|
||||
return serverMsg.Invitation, "", nil
|
||||
|
||||
}
|
128
client/helpers/invitationCheckHelper.go
Normal file
128
client/helpers/invitationCheckHelper.go
Normal file
@ -0,0 +1,128 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
|
||||
// InvitationCheck
|
||||
// todo
|
||||
/*
|
||||
func InvitationCheck(invitationdata []byte) *C.char {
|
||||
var jsoninv map[string]interface{}
|
||||
err := json.Unmarshal([]byte(C.GoString(invitationdata)), &jsoninv)
|
||||
if err != nil {
|
||||
return C.CString(errorToJson(err, "InvitationCheck: "))
|
||||
}
|
||||
var cc *meowlib.ContactCard
|
||||
if _, err := os.Stat(jsoninv["filename"].(string)); os.IsNotExist(err) {
|
||||
return C.CString(errorToJson(err, "InvitationCheck: "))
|
||||
}
|
||||
if strings.HasSuffix(jsoninv["filename"].(string), ".mwiv") {
|
||||
data, err := os.ReadFile(jsoninv["filename"].(string))
|
||||
if err != nil {
|
||||
return C.CString(errorToJson(err, "InvitationCheck: "))
|
||||
}
|
||||
cc, err = meowlib.NewContactCardFromCompressed(data)
|
||||
if err != nil {
|
||||
return C.CString(errorToJson(err, "InvitationCheck: "))
|
||||
}
|
||||
}
|
||||
identity := client.GetConfig().GetIdentity()
|
||||
result := map[string]string{}
|
||||
if cc != nil {
|
||||
isAnswer, proposed, received, invitationMessage := identity.CheckInvitation(cc)
|
||||
if isAnswer { // answer to infitation
|
||||
result["type"] = "answer"
|
||||
result["to"] = proposed
|
||||
result["from"] = received
|
||||
result["invitation_message"] = invitationMessage
|
||||
//fmt.Fprintln(os.Stdout, "Invitation sent to "+proposed+" received with "+received+" as suggested nickname")
|
||||
} else { // finalization message
|
||||
result["type"] = "finalize"
|
||||
result["from"] = received
|
||||
//fmt.Fprintln(os.Stdout, "Invitation sent by "+received)
|
||||
}
|
||||
|
||||
}
|
||||
val, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return C.CString(errorToJson(err, "InvitationCheck: "))
|
||||
}
|
||||
return C.CString(string(val))
|
||||
}*/
|
||||
|
||||
// InvitationGetMessage
|
||||
// Called by the invitation receiver
|
||||
// invitationUrl: the url of server holding the invitation
|
||||
// serverPublicKey: the public key of the server holding the invitation
|
||||
// invitationPassword: the password of the invitation
|
||||
func InvitationGetMessage(invitationUrl string, serverPublicKey string, invitationPassword string) ([]byte, string, error) {
|
||||
|
||||
meowurl := strings.Split(invitationUrl[7:], "?")
|
||||
|
||||
serverurl := meowurl[0]
|
||||
shortcode := meowurl[1]
|
||||
srv := client.Server{}
|
||||
// check if already in msg servers
|
||||
dbsrv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(serverurl)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessage: LoadServer", err
|
||||
}
|
||||
if dbsrv == nil {
|
||||
// create a server object with url & pubkey
|
||||
srv.Url = serverurl
|
||||
srv.PublicKey = serverPublicKey
|
||||
srv.UserKp = meowlib.NewKeyPair()
|
||||
// save it
|
||||
err = client.GetConfig().GetIdentity().MessageServers.StoreServer(&srv)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessage: StoreServer", err
|
||||
}
|
||||
} else {
|
||||
if dbsrv.PublicKey != serverPublicKey {
|
||||
dbsrv.PublicKey = serverPublicKey
|
||||
}
|
||||
srv = *dbsrv
|
||||
}
|
||||
// buildserver message
|
||||
toSrvMsg, err := srv.BuildToServerMessageInvitationRequest(shortcode, invitationPassword)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessage: BuildToServerMessageInvitationRequest", err
|
||||
}
|
||||
// processoutbound
|
||||
bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessage: ProcessOutboundMessage", err
|
||||
}
|
||||
|
||||
return bytemsg, "", nil
|
||||
}
|
||||
|
||||
// InvitationGetMessageReadResponse
|
||||
// Called by the invitation receiver
|
||||
// invitationData: the data received from the server
|
||||
// invitationServerUid: the uid of the server holding the invitation
|
||||
func InvitationGetMessageReadResponse(invitationData []byte, invitationServerUid string) (*meowlib.ContactCard, string, error) {
|
||||
|
||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessageReadResponse: LoadServer", err
|
||||
}
|
||||
// Server inbound processing : get the invitation server
|
||||
serverMsg, err := server.ProcessInboundServerResponse(invitationData)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessageReadResponse: ProcessInboundServerResponse", err
|
||||
}
|
||||
// fmt.Println("Inbound OK, Invitation Step: ", serverMsg.Invitation.Step, len(serverMsg.Invitation.Payload))
|
||||
// fmt.Println("Invitation Check")
|
||||
// fmt.Println(hex.EncodeToString(serverMsg.Invitation.Payload))
|
||||
// contactCard decode
|
||||
cc, err := meowlib.NewContactCardFromCompressed(serverMsg.Invitation.Payload)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessageReadResponse: NewContactCardFromCompressed", err
|
||||
}
|
||||
return cc, "", nil
|
||||
}
|
135
client/helpers/invitationCreateHelper.go
Normal file
135
client/helpers/invitationCreateHelper.go
Normal file
@ -0,0 +1,135 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
|
||||
// InvitationCreatePeer creates a new peer and returns it
|
||||
// Called by invitation initiator
|
||||
// name: the name of the peer
|
||||
// myNickname: my nickname for that peer
|
||||
// invitationMessage: the message to send to the peer
|
||||
// serverUids: the list of server uids
|
||||
func InvitationCreatePeer(name string, myNickname string, invitationMessage string, serverUids []string) (*client.Peer, string, error) {
|
||||
|
||||
mynick := myNickname
|
||||
if myNickname == "" {
|
||||
mynick = client.GetConfig().GetIdentity().Nickname
|
||||
}
|
||||
|
||||
// build my contact card for that friend
|
||||
peer, err := client.GetConfig().GetIdentity().InvitePeer(mynick, name, serverUids, invitationMessage)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreate: InvitePeer", err
|
||||
}
|
||||
client.GetConfig().GetIdentity().Save()
|
||||
|
||||
return peer, "", nil
|
||||
}
|
||||
|
||||
// InvitationCreateFile creates a new peer and writes the invitation to a file
|
||||
// Called by invitation initiator
|
||||
// name: the name of the peer
|
||||
// myNickname: my nickname for that peer
|
||||
// invitationMessage: the message to send to the peer
|
||||
// serverUids: the list of server uids
|
||||
// format: the format of the file (qr or mwiv)
|
||||
func InvitationCreateFile(name string, myNickname string, invitationMessage string, serverUids []string, format string) (*client.Peer, string, error) {
|
||||
|
||||
peer, errdata, err := InvitationCreatePeer(name, myNickname, invitationMessage, serverUids)
|
||||
if err != nil {
|
||||
return nil, errdata, err
|
||||
}
|
||||
c := client.GetConfig()
|
||||
var filename string = ""
|
||||
if format == "qr" {
|
||||
filename = c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".png"
|
||||
err := peer.GetMyContact().WriteQr(filename)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateFile: WriteQr", err
|
||||
}
|
||||
} else {
|
||||
filename = c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".mwiv"
|
||||
err := peer.GetMyContact().WriteCompressed(filename)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateFile: WriteCompressed", err
|
||||
}
|
||||
}
|
||||
return peer, "", nil
|
||||
}
|
||||
|
||||
// InvitationCreateMessage creates a new invitation message for an invited peer
|
||||
// Called by invitation initiator
|
||||
// invitationId: the invitation id of the peer
|
||||
// invitationServerUid: the uid of the server for sending the invitation
|
||||
// timeOut: the timeout for the invitation
|
||||
// urlLen: the length of the invitation url
|
||||
// password: the password for the invitation
|
||||
func InvitationCreateMessage(invitationId string, invitationServerUid string, timeOut int, urlLen int, password string) ([]byte, string, error) {
|
||||
|
||||
// lookup for peer with "invitation_id"
|
||||
var myContact *meowlib.ContactCard
|
||||
for i := 0; i < len(client.GetConfig().GetIdentity().Peers); i++ {
|
||||
if client.GetConfig().GetIdentity().Peers[i].InvitationId == invitationId {
|
||||
myContact = client.GetConfig().GetIdentity().Peers[i].GetMyContact()
|
||||
break
|
||||
}
|
||||
}
|
||||
// todo handle not found !!
|
||||
// lookup for message server with "invitation_server"
|
||||
invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid) //.GetServerByIdx(int(jsoninv["invitation_server"].(float64)))
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateMessage: LoadServer", err
|
||||
}
|
||||
// call server.buildinviattion
|
||||
msg, err := invitationServer.BuildToServerMessageInvitationCreation(myContact, password, timeOut, urlLen)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateMessage: BuildToServerMessageInvitationCreation", err
|
||||
}
|
||||
// fmt.Println("Invitation Create")
|
||||
// fmt.Println(hex.EncodeToString(msg.Invitation.Payload))
|
||||
bytemsg, err := invitationServer.ProcessOutboundMessage(msg)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateMessage: ProcessOutboundMessage", err
|
||||
}
|
||||
return bytemsg, "", nil
|
||||
}
|
||||
|
||||
// InvitationCreateReadResponse reads the response of an invitation creation (url, expiry)
|
||||
// Called by invitation initiator
|
||||
// invitationServerUid: the uid of the server where we sent the invitation
|
||||
// invitationResponse: the response we got from the server
|
||||
func InvitationCreateReadResponse(invitationServerUid string, invitationResponse []byte) (*meowlib.Invitation, string, error) {
|
||||
|
||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateReadResponse: LoadServer", err
|
||||
}
|
||||
serverMsg, err := server.ProcessInboundServerResponse(invitationResponse)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateReadResponse: ProcessInboundServerResponse", err
|
||||
}
|
||||
|
||||
return serverMsg.Invitation, "", nil
|
||||
}
|
||||
|
||||
// InvitationSetUrlInfo sets the url info for an invitation
|
||||
// Called by invitation initiator
|
||||
// invitationId: the invitation id of the peer
|
||||
// url: the url of the invitation we got from the server
|
||||
func InvitationSetUrlInfo(invitationId string, url string, expiry int64) {
|
||||
|
||||
for i := 0; i < len(client.GetConfig().GetIdentity().Peers); i++ {
|
||||
if client.GetConfig().GetIdentity().Peers[i].InvitationId == invitationId {
|
||||
client.GetConfig().GetIdentity().Peers[i].InvitationUrl = url
|
||||
client.GetConfig().GetIdentity().Peers[i].InvitationExpiry = time.Unix(expiry, 0)
|
||||
break
|
||||
}
|
||||
}
|
||||
client.GetConfig().GetIdentity().Save()
|
||||
|
||||
}
|
53
client/helpers/invitationFinalizeHelper.go
Normal file
53
client/helpers/invitationFinalizeHelper.go
Normal file
@ -0,0 +1,53 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Got it by the message background check
|
||||
// => noInvitationGetAnswer
|
||||
|
||||
// invitationGetAnswerReadResponse
|
||||
// Called by the initiator's background service only
|
||||
// invitationAnswerData: the data received from the server
|
||||
// invitationServerUid: the uid of the server holding the invitation
|
||||
func invitationGetAnswerReadResponse(invitation *meowlib.Invitation) (*client.Peer, string, error) {
|
||||
|
||||
// decode the payload
|
||||
var invitationAnswer meowlib.PackedUserMessage
|
||||
err := proto.Unmarshal(invitation.Payload, &invitationAnswer)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetAnswerReadResponse: Unmarshal", err
|
||||
}
|
||||
// retreive user public key to check usermessage signature
|
||||
// contactPublikKey := serverMsg.Invitation.From
|
||||
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitation.Uuid)
|
||||
peer.ContactPublicKey = invitation.From
|
||||
if peer != nil {
|
||||
|
||||
// process the packed user message
|
||||
usermsg, err := peer.ProcessInboundUserMessage(invitationAnswer.Payload, invitationAnswer.Signature)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetAnswerReadResponse: ProcessInboundUserMessage", err
|
||||
}
|
||||
decodedInvitation := usermsg.Invitation
|
||||
var cc meowlib.ContactCard
|
||||
err = proto.Unmarshal(decodedInvitation.Payload, &cc)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetAnswerReadResponse: Unmarshal", err
|
||||
}
|
||||
|
||||
// finalize the invitation
|
||||
// id := client.GetConfig().GetIdentity()
|
||||
peer.ContactLookupKey = cc.ContactPublicKey
|
||||
peer.ContactEncryption = cc.EncryptionPublicKey
|
||||
for _, server := range cc.PullServers {
|
||||
peer.ContactPullServers = append(peer.ContactPullServers, server.GetUid())
|
||||
}
|
||||
client.GetConfig().GetIdentity().Save()
|
||||
|
||||
}
|
||||
return peer, "", nil
|
||||
}
|
41
client/helpers/messageHelper.go
Normal file
41
client/helpers/messageHelper.go
Normal file
@ -0,0 +1,41 @@
|
||||
package helpers
|
||||
|
||||
import "forge.redroom.link/yves/meowlib/client"
|
||||
|
||||
func PrepareUserMessage(message string, srvuid string, peer_idx int, replyToUid string, filelist []string) ([]byte, string, error) {
|
||||
|
||||
peer := client.GetConfig().GetIdentity().Peers[peer_idx]
|
||||
srv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(srvuid)
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : LoadServer", err
|
||||
}
|
||||
// Creating User message
|
||||
usermessage, err := peer.BuildSimpleUserMessage([]byte(message))
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : BuildSimpleUserMessage", err
|
||||
}
|
||||
for _, file := range filelist {
|
||||
err = usermessage.AddFile(file, client.GetConfig().Chunksize)
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : AddFile", err
|
||||
}
|
||||
}
|
||||
usermessage.Status.AnswerToUuid = replyToUid
|
||||
// Prepare cyphered + packed user message
|
||||
packedMsg, err := peer.ProcessOutboundUserMessage(usermessage)
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : ProcessOutboundUserMessage", err
|
||||
}
|
||||
// Creating Server message for transporting the user message
|
||||
toServerMessage := srv.BuildToServerMessageFromUserMessage(packedMsg)
|
||||
data, err := srv.ProcessOutboundMessage(toServerMessage)
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : ProcessOutboundMessage", err
|
||||
}
|
||||
// Store message
|
||||
err = peer.StoreMessage(usermessage, filelist)
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : StoreMessage", err
|
||||
}
|
||||
return data, "", nil
|
||||
}
|
30
client/helpers/networkHelper.go
Normal file
30
client/helpers/networkHelper.go
Normal file
@ -0,0 +1,30 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
|
||||
func HttpSendMessage(serverUid string, message []byte) ([]byte, error) {
|
||||
id := client.GetConfig().GetIdentity()
|
||||
srv, err := id.MessageServers.LoadServer(serverUid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// gettig server Public key if missing
|
||||
if srv.PublicKey == "" {
|
||||
srvdata, err := meowlib.HttpGetId(srv.Url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//print(srvdata["publicKey"])
|
||||
srv.PublicKey = srvdata["publicKey"]
|
||||
id.MessageServers.StoreServer(srv)
|
||||
}
|
||||
|
||||
response, err := meowlib.HttpPostMessage(srv.Url, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
1
client/helpers/serverHelper.go
Normal file
1
client/helpers/serverHelper.go
Normal file
@ -0,0 +1 @@
|
||||
package helpers
|
15
client/helpers/storageHelper.go
Normal file
15
client/helpers/storageHelper.go
Normal file
@ -0,0 +1,15 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
|
||||
func LoadMessagesHistory(peer_id int) ([]client.InternalUserMessage, string, error) {
|
||||
id := client.GetConfig().GetIdentity()
|
||||
peer := id.Peers[peer_id]
|
||||
msgs, err := peer.LoadMessagesHistory(0, 0, 50)
|
||||
if err != nil {
|
||||
return nil, "LoadLastMessages: LoadMessagesHistory", err
|
||||
}
|
||||
return msgs, "", nil
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
@ -16,75 +17,106 @@ const maxHiddenCount = 30
|
||||
|
||||
type Identity struct {
|
||||
Nickname string `json:"nickname,omitempty"`
|
||||
DefaultAvatarUuid string `json:"default_avatar_uuid,omitempty"`
|
||||
DefaultAvatar string `json:"default_avatar,omitempty"`
|
||||
RootKp meowlib.KeyPair `json:"id_kp,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Peers PeerList `json:"peers,omitempty"`
|
||||
HiddenPeers [][]byte `json:"hiddend_peers,omitempty"`
|
||||
HiddenPeers [][]byte `json:"hidden_peers,omitempty"`
|
||||
Device meowlib.KeyPair `json:"device,omitempty"`
|
||||
KnownServers ServerList `json:"known_servers,omitempty"`
|
||||
MessageServers ServerList `json:"message_servers,omitempty"`
|
||||
MessageServers ServerStorage `json:"message_servers,omitempty"`
|
||||
DefaultDbPassword string `json:"default_db_password,omitempty"`
|
||||
DbPasswordStore bool `json:"db_password_store,omitempty"`
|
||||
OwnedDevices PeerList `json:"owned_devices,omitempty"`
|
||||
StaticMtkServerPaths []ServerList `json:"static_mtk_server_paths,omitempty"`
|
||||
DynamicMtkServeRules []string `json:"dynamic_mtk_serve_rules,omitempty"`
|
||||
InvitationTimeout int `json:"invitation_timeout,omitempty"`
|
||||
Uuid string `json:"uuid,omitempty"`
|
||||
unlockedHiddenPeers PeerList
|
||||
}
|
||||
|
||||
func CreateIdentity(nickname string) *Identity {
|
||||
func CreateIdentity(nickname string) (*Identity, error) {
|
||||
var id Identity
|
||||
id.Nickname = nickname
|
||||
id.Uuid = uuid.New().String()
|
||||
id.RootKp = meowlib.NewKeyPair()
|
||||
GetConfig().me = &id
|
||||
id.MessageServers = ServerStorage{DbFile: uuid.NewString()}
|
||||
id.generateRandomHiddenStuff()
|
||||
return &id
|
||||
err := id.CreateFolder()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &id, nil
|
||||
}
|
||||
|
||||
// Creates an invitation for a peer, returns the peer containing
|
||||
func (id *Identity) InvitePeer(MyName string, ContactName string, MessageServerIdxs []int) (*Peer, *meowlib.ContactCard, error) {
|
||||
func (id *Identity) CreateFolder() error {
|
||||
err := os.MkdirAll(filepath.Join(GetConfig().StoragePath, id.Uuid), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (id *Identity) WipeFolder() error {
|
||||
err := os.RemoveAll(filepath.Join(GetConfig().StoragePath, id.Uuid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates an invitation for a peer, returns the newly created peer including infos to provide a ContactCard
|
||||
func (id *Identity) InvitePeer(MyName string, ContactName string, MessageServerUids []string, InvitationMessage string) (*Peer, error) {
|
||||
var peer Peer
|
||||
var myContactCard meowlib.ContactCard
|
||||
peer.Uid = uuid.New().String()
|
||||
peer.MyIdentity = meowlib.NewKeyPair()
|
||||
peer.MyEncryptionKp = meowlib.NewKeyPair()
|
||||
peer.MyLookupKp = meowlib.NewKeyPair()
|
||||
peer.Name = ContactName
|
||||
peer.InvitationId = uuid.New().String()
|
||||
if id.MessageServers.Servers == nil {
|
||||
return nil, nil, errors.New("no message servers defined in your identity")
|
||||
}
|
||||
for _, i := range MessageServerIdxs {
|
||||
if i > len(id.MessageServers.Servers)-1 {
|
||||
return nil, nil, errors.New("requested server out of range of defined message servers")
|
||||
peer.InvitationId = uuid.New().String() // todo as param to identify then update url
|
||||
/* if id.MessageServers.Servers == nil {
|
||||
return nil, errors.New("no message servers defined in your identity")
|
||||
}
|
||||
}
|
||||
for _, i := range MessageServerIdxs {
|
||||
srv := &id.MessageServers.Servers[i].ServerData
|
||||
myContactCard.PullServers = append(myContactCard.PullServers, srv)
|
||||
}
|
||||
myContactCard.Name = MyName
|
||||
myContactCard.ContactPublicKey = peer.MyIdentity.Public
|
||||
myContactCard.EncryptionPublicKey = peer.MyEncryptionKp.Public
|
||||
myContactCard.LookupPublicKey = peer.MyLookupKp.Public
|
||||
myContactCard.InvitationId = peer.InvitationId
|
||||
id.Peers = append(id.Peers, peer)
|
||||
for _, i := range MessageServerIdxs {
|
||||
if i > len(id.MessageServers.Servers)-1 {
|
||||
return nil, errors.New("requested server out of range of defined message servers")
|
||||
}
|
||||
}
|
||||
for _, i := range MessageServerIdxs {
|
||||
srv := id.MessageServers.Servers[i].GetServerCard()
|
||||
peer.MyContact.PullServers = append(peer.MyContact.PullServers, srv)
|
||||
}*/
|
||||
/* pullServers, err := id.MessageServers.LoadServerCardsFromUids(MessageServerUids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}*/
|
||||
peer.MyPullServers = MessageServerUids
|
||||
peer.MyName = MyName
|
||||
peer.InvitationMessage = InvitationMessage
|
||||
id.Peers = append(id.Peers, &peer)
|
||||
|
||||
return &peer, &myContactCard, nil
|
||||
return &peer, nil
|
||||
}
|
||||
|
||||
func (id *Identity) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string) {
|
||||
// Checks if the received contact card is an answer to an invitation, returns true if it is, and the proposed and received nicknames
|
||||
func (id *Identity) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string, invitationMessage string) {
|
||||
// invitation Id found, this is an answer to an invitation
|
||||
for _, p := range id.Peers {
|
||||
if p.InvitationId == ReceivedContact.InvitationId {
|
||||
return true, p.Name, ReceivedContact.Name
|
||||
return true, p.Name, ReceivedContact.Name, ReceivedContact.InvitationMessage
|
||||
}
|
||||
}
|
||||
return false, "", ReceivedContact.Name
|
||||
|
||||
// it's an invitation
|
||||
return false, "", ReceivedContact.Name, ReceivedContact.InvitationMessage
|
||||
}
|
||||
|
||||
func (id *Identity) AnswerInvitation(MyName string, ContactName string, MessageServerIdxs []int, ReceivedContact *meowlib.ContactCard) *meowlib.ContactCard {
|
||||
// Answers an invitation, returns the newly created peer including infos to provide a ContactCard
|
||||
func (id *Identity) AnswerInvitation(MyName string, ContactName string, MessageServerIdxs []string, ReceivedContact *meowlib.ContactCard) *Peer {
|
||||
var peer Peer
|
||||
var myContactCard meowlib.ContactCard
|
||||
//var myContactCard meowlib.ContactCard
|
||||
peer.Uid = uuid.New().String()
|
||||
peer.MyIdentity = meowlib.NewKeyPair()
|
||||
peer.MyEncryptionKp = meowlib.NewKeyPair()
|
||||
peer.MyLookupKp = meowlib.NewKeyPair()
|
||||
@ -93,32 +125,50 @@ func (id *Identity) AnswerInvitation(MyName string, ContactName string, MessageS
|
||||
} else {
|
||||
peer.Name = ReceivedContact.Name
|
||||
}
|
||||
peer.Contact = *ReceivedContact
|
||||
for _, i := range MessageServerIdxs {
|
||||
srv := id.MessageServers.Servers[i].ServerData
|
||||
myContactCard.PullServers = append(myContactCard.PullServers, &srv)
|
||||
peer.ContactEncryption = ReceivedContact.EncryptionPublicKey
|
||||
peer.ContactLookupKey = ReceivedContact.LookupPublicKey
|
||||
peer.ContactPublicKey = ReceivedContact.ContactPublicKey
|
||||
peer.InvitationId = ReceivedContact.InvitationId
|
||||
peer.InvitationMessage = ReceivedContact.InvitationMessage
|
||||
for srv := range ReceivedContact.PullServers {
|
||||
peer.ContactPullServers = append(peer.ContactPullServers, ReceivedContact.PullServers[srv].GetUid())
|
||||
}
|
||||
myContactCard.Name = MyName
|
||||
myContactCard.ContactPublicKey = peer.MyIdentity.Public
|
||||
myContactCard.EncryptionPublicKey = peer.MyEncryptionKp.Public
|
||||
myContactCard.LookupPublicKey = peer.MyLookupKp.Public
|
||||
myContactCard.InvitationId = ReceivedContact.InvitationId
|
||||
/* for _, i := range MessageServerIdxs {
|
||||
srv := id.MessageServers.Servers[i].GetServerCard()
|
||||
peer.MyContact.PullServers = append(peer.MyContact.PullServers, srv)
|
||||
}*/
|
||||
/* srvCards, err := GetConfig().GetIdentity().MessageServers.LoadServerCardsFromUids(MessageServerIdxs)
|
||||
if err != nil {
|
||||
peer.MyContact.PullServers = srvCards
|
||||
}*/
|
||||
peer.MyPullServers = MessageServerIdxs
|
||||
peer.MyName = MyName
|
||||
peer.InvitationId = ReceivedContact.InvitationId
|
||||
id.Peers = append(id.Peers, &peer)
|
||||
|
||||
id.Peers = append(id.Peers, peer)
|
||||
|
||||
return &myContactCard
|
||||
return &peer
|
||||
}
|
||||
|
||||
// Finalizes an invitation, returns nil if successful
|
||||
func (id *Identity) FinalizeInvitation(ReceivedContact *meowlib.ContactCard) error {
|
||||
for i, p := range id.Peers {
|
||||
if p.InvitationId == ReceivedContact.InvitationId {
|
||||
id.Peers[i].Contact = *ReceivedContact
|
||||
//id.Peers[i].Name = ReceivedContact.Name
|
||||
id.Peers[i].ContactEncryption = ReceivedContact.EncryptionPublicKey
|
||||
id.Peers[i].ContactLookupKey = ReceivedContact.LookupPublicKey
|
||||
id.Peers[i].ContactPublicKey = ReceivedContact.ContactPublicKey
|
||||
srvs := []string{}
|
||||
for srv := range ReceivedContact.PullServers {
|
||||
srvs = append(srvs, ReceivedContact.PullServers[srv].GetUid())
|
||||
}
|
||||
id.Peers[i].ContactPullServers = srvs
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("no matching contact found for invitationId " + ReceivedContact.InvitationId)
|
||||
}
|
||||
|
||||
// LoadIdentity loads an identity from an encrypted file
|
||||
func LoadIdentity(filename string, password string) (*Identity, error) {
|
||||
var id Identity
|
||||
GetConfig().memoryPassword = password
|
||||
@ -132,6 +182,9 @@ func LoadIdentity(filename string, password string) (*Identity, error) {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal([]byte(pass), &id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
GetConfig().me = &id
|
||||
return &id, err
|
||||
}
|
||||
@ -160,7 +213,7 @@ func (id *Identity) TryUnlockHidden(password string) error {
|
||||
return err
|
||||
}
|
||||
p.dbPassword = password
|
||||
id.unlockedHiddenPeers = append(id.unlockedHiddenPeers, p)
|
||||
id.unlockedHiddenPeers = append(id.unlockedHiddenPeers, &p)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
@ -187,36 +240,104 @@ func (id *Identity) HidePeer(peerIdx int, password string) error {
|
||||
}
|
||||
|
||||
func (id *Identity) generateRandomHiddenStuff() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
count := rand.Intn(maxHiddenCount) + 1
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
count := r.Intn(maxHiddenCount) + 1
|
||||
for i := 1; i < count; i++ {
|
||||
var p Peer
|
||||
p.Name = randomLenString(4, 20)
|
||||
p.MyEncryptionKp = meowlib.NewKeyPair()
|
||||
p.MyIdentity = meowlib.NewKeyPair()
|
||||
p.MyLookupKp = meowlib.NewKeyPair()
|
||||
p.Contact.Name = randomLenString(4, 20)
|
||||
p.Contact.ContactPublicKey = p.MyLookupKp.Public
|
||||
p.Contact.EncryptionPublicKey = p.MyIdentity.Public
|
||||
p.Contact.LookupPublicKey = p.MyEncryptionKp.Public
|
||||
p.Contact.AddUrls([]string{randomLenString(14, 60), randomLenString(14, 60)})
|
||||
id.Peers = append(id.Peers, p)
|
||||
p.Name = randomLenString(4, 20)
|
||||
p.ContactPublicKey = p.MyLookupKp.Public
|
||||
p.ContactEncryption = p.MyIdentity.Public
|
||||
p.ContactLookupKey = p.MyEncryptionKp.Public
|
||||
// p.Contact.AddUrls([]string{randomLenString(14, 60), randomLenString(14, 60)}) // todo add servers
|
||||
id.Peers = append(id.Peers, &p)
|
||||
id.HidePeer(0, randomLenString(8, 14))
|
||||
// TODO Add conversations
|
||||
// TODO Add random conversations
|
||||
}
|
||||
}
|
||||
|
||||
type BackgroundJob struct {
|
||||
RootPublic string `json:"root_public,omitempty"`
|
||||
Device meowlib.KeyPair `json:"device,omitempty"`
|
||||
Jobs []*RequestsJob `json:"jobs,omitempty"`
|
||||
}
|
||||
|
||||
type RequestsJob struct {
|
||||
Server *Server `json:"server,omitempty"`
|
||||
LookupKeys []meowlib.KeyPair `json:"lookup_keys,omitempty"`
|
||||
}
|
||||
|
||||
func (id *Identity) GetRequestJobs() []*RequestsJob {
|
||||
var list []*RequestsJob
|
||||
srvs := map[string]*RequestsJob{}
|
||||
// get all servers
|
||||
servers, err := id.MessageServers.LoadAllServers()
|
||||
if err == nil {
|
||||
// build a server map
|
||||
for _, server := range servers {
|
||||
var rj RequestsJob
|
||||
rj.Server = server
|
||||
srvs[server.GetServerCard().GetUid()] = &rj
|
||||
}
|
||||
// add ids to the map
|
||||
for _, peer := range id.Peers {
|
||||
// check if peer inviation is accepted
|
||||
for _, server := range peer.MyPullServers {
|
||||
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.MyLookupKp)
|
||||
}
|
||||
}
|
||||
// add hidden peers
|
||||
for _, peer := range id.unlockedHiddenPeers {
|
||||
for _, server := range peer.MyPullServers {
|
||||
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.MyLookupKp)
|
||||
}
|
||||
}
|
||||
// todo add garbage
|
||||
|
||||
// todo random reorder
|
||||
|
||||
// build list
|
||||
for _, srv := range srvs {
|
||||
if len(srv.LookupKeys) > 0 {
|
||||
list = append(list, srv)
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (id *Identity) SaveBackgroundJob() error {
|
||||
var bj BackgroundJob
|
||||
bj.Jobs = id.GetRequestJobs()
|
||||
bj.RootPublic = id.RootKp.Public
|
||||
bj.Device = id.Device
|
||||
jsonjobs, err := json.Marshal(bj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id.CreateFolder()
|
||||
err = os.WriteFile(filepath.Join(GetConfig().StoragePath, id.Uuid, ".jobs"), jsonjobs, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func randomLenString(min int, max int) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
n := rand.Intn(max-min) + min
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
n := r.Intn(max-min) + min
|
||||
return randomString(n)
|
||||
}
|
||||
|
||||
func randomString(n int) string {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
s := make([]rune, n)
|
||||
for i := range s {
|
||||
s[i] = letters[rand.Intn(len(letters))]
|
||||
s[i] = letters[r.Intn(len(letters))]
|
||||
}
|
||||
return string(s)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package client
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
@ -23,8 +24,11 @@ func createId() *Identity {
|
||||
config.memoryPassword = "generalPassword"
|
||||
// ! Extension to quickly open db : Debug only !
|
||||
config.DbSuffix = ".sqlite"
|
||||
id := CreateIdentity("myname")
|
||||
err := id.Save()
|
||||
id, err := CreateIdentity("myname")
|
||||
if err != nil {
|
||||
log.Fatal("CreateIdentity failed")
|
||||
}
|
||||
err = id.Save()
|
||||
if err != nil {
|
||||
log.Fatal("Save failed")
|
||||
}
|
||||
@ -33,12 +37,12 @@ func createId() *Identity {
|
||||
p.MyEncryptionKp = meowlib.NewKeyPair()
|
||||
p.MyIdentity = meowlib.NewKeyPair()
|
||||
p.MyLookupKp = meowlib.NewKeyPair()
|
||||
p.Contact.Name = "foo"
|
||||
p.Contact.ContactPublicKey = p.MyLookupKp.Public
|
||||
p.Contact.EncryptionPublicKey = p.MyIdentity.Public
|
||||
p.Contact.LookupPublicKey = p.MyEncryptionKp.Public
|
||||
p.Contact.AddUrls([]string{"http:/127.0.0.1/meow", "tcp://localhost:1234"})
|
||||
id.Peers = append(id.Peers, p)
|
||||
p.Name = "foo"
|
||||
p.ContactPublicKey = p.MyLookupKp.Public
|
||||
p.ContactEncryption = p.MyIdentity.Public
|
||||
p.ContactLookupKey = p.MyEncryptionKp.Public
|
||||
//p.Contact.AddUrls([]string{"http:/127.0.0.1/meow", "tcp://localhost:1234"}) //todo add servers
|
||||
id.Peers = append(id.Peers, &p)
|
||||
return id
|
||||
}
|
||||
|
||||
@ -48,7 +52,10 @@ func TestLoad(t *testing.T) {
|
||||
}
|
||||
id, err := LoadIdentity("test.id", "toto")
|
||||
if err != nil {
|
||||
id := CreateIdentity("myname")
|
||||
id, err1 := CreateIdentity("myname")
|
||||
if err1 != nil {
|
||||
log.Fatal("CreateIdentity failed")
|
||||
}
|
||||
id.Save()
|
||||
} else {
|
||||
log.Println(id.Nickname)
|
||||
@ -79,3 +86,52 @@ func TestHidePeer(t *testing.T) {
|
||||
os.Remove("test.id")
|
||||
}
|
||||
}
|
||||
|
||||
// test GetRequestJobs
|
||||
func TestGetRequestJobs(t *testing.T) {
|
||||
// Create a mock Identity object
|
||||
id := &Identity{
|
||||
Peers: []*Peer{
|
||||
{
|
||||
MyPullServers: []string{"server1", "server2"},
|
||||
MyLookupKp: meowlib.NewKeyPair(),
|
||||
},
|
||||
{
|
||||
MyPullServers: []string{"server3", "server4"},
|
||||
MyLookupKp: meowlib.NewKeyPair(),
|
||||
},
|
||||
},
|
||||
unlockedHiddenPeers: []*Peer{
|
||||
{
|
||||
MyPullServers: []string{"server5", "server6"},
|
||||
MyLookupKp: meowlib.NewKeyPair(),
|
||||
},
|
||||
},
|
||||
}
|
||||
id.MessageServers = ServerStorage{
|
||||
DbFile: "test.db",
|
||||
}
|
||||
GetConfig().SetMemPass("test")
|
||||
GetConfig().SetIdentity(id)
|
||||
for i := 1; i < 10; i++ {
|
||||
// initialize a Server with name "server+i"
|
||||
srv := CreateServerFromUrl("server" + strconv.Itoa(i))
|
||||
id.MessageServers.StoreServer(srv)
|
||||
}
|
||||
// Call GetRequestJobs
|
||||
jobs := id.GetRequestJobs()
|
||||
|
||||
// Check that the returned list is as expected
|
||||
assert.Equal(t, 6, len(jobs), "Expected 6 jobs")
|
||||
|
||||
// Check that each job has the correct server and lookup keys
|
||||
for _, job := range jobs {
|
||||
//fmt.Println(job.Server.GetUid(), job.LookupKeys)
|
||||
assert.Contains(t, []string{"server1", "server2", "server3", "server4", "server5", "server6"}, job.Server.GetUid(), "Unexpected server UID")
|
||||
assert.Len(t, job.LookupKeys, 1, "Expected 1 lookup key per job")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
||||
|
@ -26,14 +26,14 @@ func ProcessForOutput(usermessage *meowlib.UserMessage, peer *Peer, servers *Ser
|
||||
srv = &servers.Servers[i]
|
||||
var toServerMessage meowlib.ToServerMessage
|
||||
toServerMessage.MatriochkaMessage.Data = lastmsg
|
||||
toServerMessage.MatriochkaMessage.Next.Url = servers.Servers[i+1].ServerData.Url
|
||||
toServerMessage.MatriochkaMessage.Next.PublicKey = servers.Servers[i+1].ServerData.PublicKey
|
||||
toServerMessage.MatriochkaMessage.Next.Url = servers.Servers[i+1].Url
|
||||
toServerMessage.MatriochkaMessage.Next.PublicKey = servers.Servers[i+1].PublicKey
|
||||
toServerMessage.MatriochkaMessage.Next.Delay = int32(servers.Servers[i+1].AllowedDelay)
|
||||
if trackingLookupKey != "" {
|
||||
toServerMessage.MatriochkaMessage.Next.Uuid = lastuuid // change tracking uuid at each server
|
||||
if i > 0 {
|
||||
toServerMessage.MatriochkaMessage.Prev.Url = servers.Servers[i-1].ServerData.Url
|
||||
toServerMessage.MatriochkaMessage.Prev.PublicKey = servers.Servers[i+1].ServerData.PublicKey
|
||||
toServerMessage.MatriochkaMessage.Prev.Url = servers.Servers[i-1].Url
|
||||
toServerMessage.MatriochkaMessage.Prev.PublicKey = servers.Servers[i+1].PublicKey
|
||||
toServerMessage.MatriochkaMessage.Prev.Delay = int32(servers.Servers[i-1].AllowedDelay)
|
||||
toServerMessage.MatriochkaMessage.Prev.Uuid = uuid.NewString()
|
||||
lastuuid = toServerMessage.MatriochkaMessage.Prev.Uuid
|
||||
|
@ -3,8 +3,32 @@ package client
|
||||
import "forge.redroom.link/yves/meowlib"
|
||||
|
||||
type InternalUserMessage struct {
|
||||
message *meowlib.UserMessage
|
||||
dbid int64
|
||||
Outbound bool `json:"outbound"`
|
||||
Messagetype string `json:"messagetype,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
ConversationStatus *meowlib.ConversationStatus `json:"conversation_status,omitempty"`
|
||||
Contact *meowlib.ContactCard `json:"contact,omitempty"`
|
||||
//Group group
|
||||
FilePaths []string `json:"file_paths,omitempty"`
|
||||
CurrentLocation *meowlib.Location `json:"current_location,omitempty"`
|
||||
Appdata []byte `json:"appdata,omitempty"`
|
||||
Dbfile string `json:"dbfile,omitempty"`
|
||||
Dbid int64 `json:"dbid,omitempty"`
|
||||
}
|
||||
|
||||
// InternalUserMessageFromUserMessage creates an InternalUserMessage from a UserMessage
|
||||
func InternalUserMessageFromUserMessage(peer *Peer, msg *meowlib.UserMessage) *InternalUserMessage {
|
||||
iu := new(InternalUserMessage)
|
||||
if peer.ContactPublicKey == msg.From {
|
||||
iu.Outbound = false
|
||||
} else {
|
||||
iu.Outbound = true
|
||||
}
|
||||
iu.Messagetype = msg.Type
|
||||
iu.Message = string(msg.Data)
|
||||
iu.ConversationStatus = msg.Status
|
||||
iu.Contact = msg.Contact
|
||||
return iu
|
||||
}
|
||||
|
||||
func ProcessOutboundTextMessage(peer *Peer, text string, srv *Server) ([]byte, error) {
|
||||
|
334
client/messagestorage.go
Normal file
334
client/messagestorage.go
Normal file
@ -0,0 +1,334 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"github.com/google/uuid"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func StoreMessage(peer *Peer, usermessage *meowlib.UserMessage, filenames []string, password string) error {
|
||||
var dbid string
|
||||
cfg := GetConfig()
|
||||
identity := cfg.GetIdentity()
|
||||
// If no db/no ID create DB + Tablz
|
||||
// TODO : if file size > X new db
|
||||
if len(peer.DbIds) == 0 {
|
||||
dbid = uuid.NewString()
|
||||
peer.DbIds = []string{dbid}
|
||||
|
||||
identity.Save()
|
||||
identity.CreateFolder()
|
||||
file, err := os.Create(filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
peer.DbIds = append(peer.DbIds, dbid)
|
||||
sqliteDatabase, _ := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
err = createMessageTable(sqliteDatabase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqliteDatabase.Close()
|
||||
} else {
|
||||
dbid = peer.DbIds[len(peer.DbIds)-1]
|
||||
}
|
||||
// Open Db
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
// Detach Files
|
||||
hiddenFilenames := []string{}
|
||||
if len(usermessage.Files) > 0 {
|
||||
for _, f := range usermessage.Files {
|
||||
hiddenFilename := uuid.NewString()
|
||||
// Cypher file
|
||||
encData, err := meowlib.SymEncrypt(password, f.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles")); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles"), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
os.WriteFile(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles", hiddenFilename), encData, 0600)
|
||||
hiddenFilenames = append(hiddenFilenames, filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles", hiddenFilename))
|
||||
// replace f.Data by uuid filename
|
||||
f.Data = []byte(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles", hiddenFilename))
|
||||
}
|
||||
}
|
||||
outbound := true
|
||||
if usermessage.From == peer.ContactPublicKey {
|
||||
outbound = false
|
||||
}
|
||||
// Convert UserMessage to DbMessage
|
||||
dbm := UserMessageToDbMessage(outbound, usermessage, hiddenFilenames)
|
||||
// Encrypt message
|
||||
out, err := proto.Marshal(dbm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encData, err := meowlib.SymEncrypt(password, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Insert message
|
||||
insertMessageSQL := `INSERT INTO message(m) VALUES (?) RETURNING ID`
|
||||
statement, err := db.Prepare(insertMessageSQL) // Prepare statement.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := statement.Exec(encData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ium := DbMessageToInternalUserMessage(id, dbid, dbm)
|
||||
peer.LastMessage = ium
|
||||
identity.Save()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get new messages from a peer
|
||||
func GetNewMessages(peer *Peer, lastDbId int, password string) ([]*InternalUserMessage, error) {
|
||||
var messages []*InternalUserMessage
|
||||
cfg := GetConfig()
|
||||
identity := cfg.GetIdentity()
|
||||
// handle no db yet
|
||||
if len(peer.DbIds) == 0 {
|
||||
return messages, nil
|
||||
}
|
||||
fileidx := len(peer.DbIds) - 1
|
||||
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
// if it's first app query, it won't hold a lastIndex, so let's start from end
|
||||
if lastDbId == 0 {
|
||||
lastDbId = math.MaxInt64
|
||||
}
|
||||
stm, err := db.Prepare("SELECT id, m FROM message WHERE id > ? ORDER BY id DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stm.Close()
|
||||
rows, err := stm.Query(lastDbId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var ium *InternalUserMessage
|
||||
var dbm meowlib.DbMessage
|
||||
var id int64
|
||||
var m []byte
|
||||
err = rows.Scan(&id, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decdata, err := meowlib.SymDecrypt(password, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = proto.Unmarshal(decdata, &dbm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ium = DbMessageToInternalUserMessage(id, peer.DbIds[fileidx], &dbm)
|
||||
ium.Dbid = id
|
||||
ium.Dbfile = peer.DbIds[fileidx]
|
||||
messages = append(messages, ium)
|
||||
}
|
||||
// TODO DB overlap
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
// Get old messages from a peer
|
||||
func GetMessagesHistory(peer *Peer, inAppMsgCount int, lastDbId int, wantMore int, password string) ([]InternalUserMessage, error) {
|
||||
var messages []InternalUserMessage
|
||||
// handle no db yet
|
||||
if len(peer.DbIds) == 0 {
|
||||
return messages, nil
|
||||
}
|
||||
fileidx := len(peer.DbIds) - 1
|
||||
// initialize count with last db message count
|
||||
countStack, err := getMessageCount(peer.DbIds[fileidx])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// while the db message count < what we already have in app, step to next db file
|
||||
for inAppMsgCount > countStack {
|
||||
fileidx--
|
||||
if fileidx < 0 {
|
||||
return nil, nil
|
||||
}
|
||||
newCount, err := getMessageCount(peer.DbIds[fileidx])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
countStack += newCount
|
||||
}
|
||||
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
// if it's first app query, it won't hold a lastIndex, so let's start from end
|
||||
if lastDbId == 0 {
|
||||
lastDbId = math.MaxInt64
|
||||
}
|
||||
stm, err := db.Prepare("SELECT id, m FROM message WHERE id < ? ORDER BY id DESC LIMIT ?")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stm.Close()
|
||||
rows, err := stm.Query(lastDbId, wantMore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var ium *InternalUserMessage
|
||||
var dbm meowlib.DbMessage
|
||||
var id int64
|
||||
var m []byte
|
||||
err = rows.Scan(&id, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decdata, err := meowlib.SymDecrypt(password, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = proto.Unmarshal(decdata, &dbm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ium = DbMessageToInternalUserMessage(id, peer.DbIds[fileidx], &dbm)
|
||||
ium.Dbid = id
|
||||
ium.Dbfile = peer.DbIds[fileidx]
|
||||
|
||||
messages = append(messages, *ium)
|
||||
}
|
||||
// TODO DB overlap
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
// Get old messages from a peer
|
||||
func GetMessagePreview(peer *Peer, dbFile string, dbId int64, password string) ([]byte, error) {
|
||||
|
||||
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbFile+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
|
||||
stm, err := db.Prepare("SELECT id, m FROM message WHERE id=?")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stm.Close()
|
||||
rows, err := stm.Query(dbId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var dbm meowlib.DbMessage
|
||||
for rows.Next() {
|
||||
|
||||
var id int64
|
||||
var m []byte
|
||||
err = rows.Scan(&id, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decdata, err := meowlib.SymDecrypt(password, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = proto.Unmarshal(decdata, &dbm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
return FilePreview(dbm.FilePaths[0], password)
|
||||
}
|
||||
|
||||
func FilePreview(filename string, password string) ([]byte, error) {
|
||||
// get the hidden file
|
||||
encData, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// decrypt the file
|
||||
data, err := meowlib.SymDecrypt(password, encData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// make an image from the files content (loads the first image, or build a more complex view)
|
||||
func InternalUserMessagePreview(msg *InternalUserMessage, password string) ([]byte, error) {
|
||||
// get the hidden file name
|
||||
if len(msg.FilePaths) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return FilePreview(msg.FilePaths[0], password)
|
||||
}
|
||||
|
||||
func getMessageCount(dbid string) (int, error) {
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
var count int
|
||||
query := "SELECT COUNT(*) FROM message"
|
||||
err := db.QueryRow(query).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func createMessageTable(db *sql.DB) error {
|
||||
createMessageTableSQL := `CREATE TABLE message (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"m" BLOB);` // SQL Statement for Create Table
|
||||
statement, err := db.Prepare(createMessageTableSQL) // Prepare SQL Statement
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statement.Exec() // Execute SQL Statements
|
||||
return nil
|
||||
}
|
||||
|
||||
func createServerTable(db *sql.DB) error {
|
||||
createServerTableSQL := `CREATE TABLE servers (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"country" varchar(2),
|
||||
"public" bool,
|
||||
"uptime" int,
|
||||
"bandwith" float,
|
||||
"load" float,
|
||||
"url" varchar(2000)
|
||||
"name" varchar(255);
|
||||
"description" varchar(5000)
|
||||
"publickey" varchar(10000)
|
||||
)` // SQL Statement for Create Table
|
||||
statement, err := db.Prepare(createServerTableSQL) // Prepare SQL Statement
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statement.Exec() // Execute SQL Statements
|
||||
return nil
|
||||
}
|
@ -14,17 +14,17 @@ func TestStoreMessage(t *testing.T) {
|
||||
id := createId()
|
||||
var um meowlib.UserMessage
|
||||
um.Data = []byte("blabla")
|
||||
err := StoreMessage(&id.Peers[0], &um, GetConfig().memoryPassword)
|
||||
err := StoreMessage(id.Peers[0], &um, []string{}, GetConfig().memoryPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
messages, err := GetLastMessages(&id.Peers[0], 0, 0, 10, GetConfig().memoryPassword)
|
||||
messages, err := GetMessagesHistory(id.Peers[0], 0, 0, 10, GetConfig().memoryPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Checks
|
||||
assert.Equal(t, len(messages), 1, "not 1 message")
|
||||
assert.Equal(t, messages[0].message.Data, um.Data, "not 1 message")
|
||||
assert.Equal(t, messages[0].Message, string(um.Data), "not 1 message")
|
||||
// Cleanup
|
||||
if exists("test.id") {
|
||||
os.Remove("test.id")
|
||||
@ -42,15 +42,21 @@ func TestStoreMessage(t *testing.T) {
|
||||
|
||||
func TestManyStoreMessage(t *testing.T) {
|
||||
id := createId()
|
||||
// test with zero messages
|
||||
messages, err := GetMessagesHistory(id.Peers[0], 0, 0, 10, GetConfig().memoryPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, len(messages), 0, "not 0 message")
|
||||
for i := 1; i < 100; i++ {
|
||||
var um meowlib.UserMessage
|
||||
um.Data = []byte(randomLenString(20, 200))
|
||||
err := StoreMessage(&id.Peers[0], &um, GetConfig().memoryPassword)
|
||||
err := StoreMessage(id.Peers[0], &um, []string{}, GetConfig().memoryPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
messages, err := GetLastMessages(&id.Peers[0], 0, 0, 10, GetConfig().memoryPassword)
|
||||
messages, err = GetMessagesHistory(id.Peers[0], 0, 0, 10, GetConfig().memoryPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
135
client/peer.go
135
client/peer.go
@ -15,43 +15,95 @@ import (
|
||||
// - Building simple user messages
|
||||
// - Utility functions for packing/unpacking, encrypting/decrypting messages for peer communication
|
||||
type Peer struct {
|
||||
Uid string `json:"uid,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
MyName string `json:"my_name,omitempty"`
|
||||
MyAvatar string `json:"my_avatar,omitempty"`
|
||||
// Conversation []InternalMessage `json:"conversation,omitempty"`
|
||||
// My own keys for that peer
|
||||
MyIdentity meowlib.KeyPair `json:"my_identity,omitempty"`
|
||||
MyEncryptionKp meowlib.KeyPair `json:"my_encryption_kp,omitempty"`
|
||||
MyLookupKp meowlib.KeyPair `json:"my_lookup_kp,omitempty"`
|
||||
MyPullServers []meowlib.ServerCard `json:"my_pull_servers,omitempty"`
|
||||
MyIdentity meowlib.KeyPair `json:"my_identity,omitempty"`
|
||||
MyEncryptionKp meowlib.KeyPair `json:"my_encryption_kp,omitempty"`
|
||||
MyLookupKp meowlib.KeyPair `json:"my_lookup_kp,omitempty"`
|
||||
MyPullServers []string `json:"my_pull_servers,omitempty"`
|
||||
// Peer keys and infos
|
||||
Contact meowlib.ContactCard `json:"contact,omitempty"`
|
||||
InvitationId string `json:"invitation_id,omitempty"`
|
||||
//Contact meowlib.ContactCard `json:"contact,omitempty"` // todo : remove
|
||||
ContactPublicKey string `json:"contact_public_key,omitempty"`
|
||||
ContactLookupKey string `json:"contact_lookup_key,omitempty"`
|
||||
ContactEncryption string `json:"contact_encryption,omitempty"`
|
||||
ContactPullServers []string `json:"contact_pull_servers,omitempty"`
|
||||
InvitationId string `json:"invitation_id,omitempty"`
|
||||
InvitationUrl string `json:"invitation_url,omitempty"`
|
||||
InvitationMessage string `json:"invitation_message,omitempty"`
|
||||
InvitationExpiry time.Time `json:"invitation_expiry,omitempty"`
|
||||
LastMessage *InternalUserMessage `json:"last_message,omitempty"`
|
||||
// Internal management attributes
|
||||
Visible bool `json:"visible,omitempty"`
|
||||
VisiblePassword string `json:"visible_password,omitempty"`
|
||||
PasswordType string `json:"password_type,omitempty"`
|
||||
Blocked bool `json:"blocked,omitempty"`
|
||||
MessageNotification string `json:"message_notification,omitempty"`
|
||||
OnionMode bool `json:"onion_mode,omitempty"`
|
||||
LastMessage time.Time `json:"last_message,omitempty"`
|
||||
DbIds []string `json:"db_ids,omitempty"`
|
||||
AvatarUuid string `json:"avatar_uid,omitempty"`
|
||||
Visible bool `json:"visible,omitempty"`
|
||||
VisiblePassword string `json:"visible_password,omitempty"`
|
||||
PasswordType string `json:"password_type,omitempty"`
|
||||
Blocked bool `json:"blocked,omitempty"`
|
||||
MessageNotification string `json:"message_notification,omitempty"`
|
||||
MatriochkaMode bool `json:"matriochka_mode,omitempty"`
|
||||
DirectMode bool `json:"direct_mode,omitempty"`
|
||||
DbIds []string `json:"db_ids,omitempty"`
|
||||
IsDevice bool `json:"is_device,omitempty"`
|
||||
dbPassword string
|
||||
}
|
||||
|
||||
//
|
||||
// getters and setters
|
||||
//
|
||||
|
||||
func (p *Peer) GetMyContact() *meowlib.ContactCard {
|
||||
var c meowlib.ContactCard
|
||||
c.ContactPublicKey = p.MyIdentity.Public
|
||||
c.LookupPublicKey = p.MyLookupKp.Public
|
||||
c.EncryptionPublicKey = p.MyEncryptionKp.Public
|
||||
srvCards, err := GetConfig().GetIdentity().MessageServers.LoadServerCardsFromUids(p.MyPullServers)
|
||||
if err == nil {
|
||||
c.PullServers = srvCards
|
||||
}
|
||||
c.InvitationId = p.InvitationId
|
||||
c.InvitationMessage = p.InvitationMessage
|
||||
c.Name = p.MyName
|
||||
return &c
|
||||
}
|
||||
|
||||
func (p *Peer) GetContact() *meowlib.ContactCard {
|
||||
var c meowlib.ContactCard
|
||||
c.ContactPublicKey = p.ContactPublicKey
|
||||
c.LookupPublicKey = p.ContactLookupKey
|
||||
c.EncryptionPublicKey = p.ContactEncryption
|
||||
srvCards, err := GetConfig().GetIdentity().MessageServers.LoadServerCardsFromUids(p.ContactPullServers)
|
||||
if err == nil {
|
||||
c.PullServers = srvCards
|
||||
}
|
||||
c.InvitationId = p.InvitationId
|
||||
c.InvitationMessage = p.InvitationMessage
|
||||
c.Name = p.Name
|
||||
return &c
|
||||
}
|
||||
|
||||
func (p *Peer) InvitationPending() bool {
|
||||
if p.ContactPublicKey == "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//
|
||||
// Messages building
|
||||
//
|
||||
|
||||
func (p *Peer) BuildSimpleUserMessage(message []byte) (*meowlib.UserMessage, error) {
|
||||
var msg meowlib.UserMessage
|
||||
msg.Destination = p.Contact.LookupPublicKey
|
||||
msg.Destination = p.ContactLookupKey
|
||||
msg.From = p.MyIdentity.Public
|
||||
msg.Data = message
|
||||
msg.Type = "1"
|
||||
msg.Status = &meowlib.ConversationStatus{}
|
||||
msg.Status.LocalUuid = uuid.New().String()
|
||||
msg.Status.Uuid = uuid.New().String()
|
||||
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
@ -82,7 +134,7 @@ func (p *Peer) BuildSingleFileMessage(filename string, message []byte) ([]meowli
|
||||
}
|
||||
var msg meowlib.UserMessage
|
||||
var file meowlib.File
|
||||
msg.Destination = p.Contact.LookupPublicKey
|
||||
msg.Destination = p.ContactLookupKey
|
||||
msg.From = p.MyIdentity.Public
|
||||
file.Filename = fi.Name()
|
||||
file.Chunk = uint32(chunk)
|
||||
@ -92,7 +144,7 @@ func (p *Peer) BuildSingleFileMessage(filename string, message []byte) ([]meowli
|
||||
msg.Type = "2"
|
||||
if chunk == 0 {
|
||||
msg.Status = &meowlib.ConversationStatus{}
|
||||
msg.Status.LocalUuid = uuid.New().String()
|
||||
msg.Status.Uuid = uuid.New().String()
|
||||
}
|
||||
msgs = append(msgs, msg)
|
||||
chunk++
|
||||
@ -102,7 +154,7 @@ func (p *Peer) BuildSingleFileMessage(filename string, message []byte) ([]meowli
|
||||
|
||||
// Builds an invitation answer user message.
|
||||
// it takes as input a contactcard generated by Identity.AnswerInvitation
|
||||
func (p *Peer) BuildInvitationAnswserMessage(myContactCard *meowlib.ContactCard) (*meowlib.UserMessage, error) {
|
||||
func (p *Peer) BuildInvitationAnswerMessage(myContactCard *meowlib.ContactCard) (*meowlib.UserMessage, error) {
|
||||
var msg meowlib.UserMessage
|
||||
var invitation meowlib.Invitation
|
||||
invitation.Step = 3
|
||||
@ -110,8 +162,10 @@ func (p *Peer) BuildInvitationAnswserMessage(myContactCard *meowlib.ContactCard)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invitation.Uuid = p.InvitationId
|
||||
invitation.Payload = out
|
||||
msg.Destination = p.Contact.LookupPublicKey
|
||||
msg.Destination = p.ContactLookupKey
|
||||
msg.Invitation = &invitation
|
||||
msg.From = p.MyIdentity.Public
|
||||
msg.Type = "1"
|
||||
return &msg, nil
|
||||
@ -141,7 +195,11 @@ func (p *Peer) DeserializeUserMessage(data []byte) (*meowlib.UserMessage, error)
|
||||
// AsymEncryptMessage prepares a message to send to a specific peer contact
|
||||
func (p *Peer) AsymEncryptMessage(Message []byte) (*meowlib.EncryptedMessage, error) {
|
||||
var enc *meowlib.EncryptedMessage
|
||||
enc, err := meowlib.AsymEncryptAndSign(p.Contact.EncryptionPublicKey, p.MyIdentity.Private, Message)
|
||||
// fmt.Println("[AsymEncryptMessage] Destination is:", p.ContactLookupKey)
|
||||
// fmt.Println("[AsymEncryptMessage] Contact encryption key is:", p.ContactEncryption)
|
||||
// fmt.Println("[AsymEncryptMessage] My signing key is:", p.MyIdentity.Private)
|
||||
// fmt.Println("[AsymEncryptMessage] Signature should be verified with:", p.MyIdentity.Public)
|
||||
enc, err := meowlib.AsymEncryptAndSign(p.ContactEncryption, p.MyIdentity.Private, Message)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return enc, err
|
||||
@ -151,7 +209,10 @@ func (p *Peer) AsymEncryptMessage(Message []byte) (*meowlib.EncryptedMessage, er
|
||||
|
||||
// AsymDecryptMessage reads a message from a specific peer contact
|
||||
func (p *Peer) AsymDecryptMessage(Message []byte, Signature []byte) (DecryptedMessage []byte, err error) {
|
||||
DecryptedMessage, err = meowlib.AsymDecryptAndCheck(p.MyEncryptionKp.Private, p.Contact.ContactPublicKey, Message, Signature)
|
||||
// fmt.Println("[AsymDecryptMessage] Decrypting key is:", p.MyEncryptionKp.Private)
|
||||
// fmt.Println("[AsymDecryptMessage] Should have been encrypted with:", p.MyEncryptionKp.Public)
|
||||
// fmt.Println("[AsymDecryptMessage] Signature will be verified with:", p.ContactPublicKey)
|
||||
DecryptedMessage, err = meowlib.AsymDecryptAndCheck(p.MyEncryptionKp.Private, p.ContactPublicKey, Message, Signature)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return nil, err
|
||||
@ -162,7 +223,7 @@ func (p *Peer) AsymDecryptMessage(Message []byte, Signature []byte) (DecryptedMe
|
||||
// PackUserMessage will package the previously encrypted message
|
||||
func (p *Peer) PackUserMessage(message []byte, signature []byte) *meowlib.PackedUserMessage {
|
||||
var msg meowlib.PackedUserMessage
|
||||
msg.Destination = p.Contact.LookupPublicKey
|
||||
msg.Destination = p.ContactLookupKey
|
||||
msg.Payload = message
|
||||
msg.Signature = signature
|
||||
return &msg
|
||||
@ -221,19 +282,35 @@ func (p *Peer) SetDbPassword(password string) {
|
||||
}
|
||||
|
||||
func (p *Peer) GetDbPassword() string {
|
||||
if p.dbPassword == "" {
|
||||
return GetConfig().memoryPassword
|
||||
}
|
||||
return p.dbPassword
|
||||
}
|
||||
|
||||
func (p *Peer) StoreMessage(msg []byte) {
|
||||
func (p *Peer) StoreMessage(msg *meowlib.UserMessage, filenames []string) error {
|
||||
return StoreMessage(p, msg, filenames, p.GetDbPassword())
|
||||
}
|
||||
|
||||
func (p *Peer) GetFilePreview(filename string) ([]byte, error) {
|
||||
return FilePreview(filename, p.GetDbPassword())
|
||||
}
|
||||
|
||||
func (p *Peer) UpdateMessage(msg InternalUserMessage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Peer) LoadMessagesHistory(alreadyLoadedCount int, oldestMessageId int, qty int) ([]InternalUserMessage, error) {
|
||||
return GetMessagesHistory(p, alreadyLoadedCount, oldestMessageId, qty, p.GetDbPassword())
|
||||
|
||||
}
|
||||
|
||||
func (p *Peer) LoadMessage(uid string) {
|
||||
|
||||
func (p *Peer) LoadNewMessages(lastMessageId int) ([]*InternalUserMessage, error) {
|
||||
return GetNewMessages(p, lastMessageId, p.GetDbPassword())
|
||||
}
|
||||
|
||||
func (p *Peer) LoadLastMessages(qty int) {
|
||||
|
||||
func (p *Peer) LoadMessage(uid string) (*InternalUserMessage, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *Peer) GetLastMessageUuid(msg []byte) {
|
||||
|
@ -8,13 +8,16 @@ import (
|
||||
)
|
||||
|
||||
func TestGetFromPublicKey(t *testing.T) {
|
||||
id := CreateIdentity("test")
|
||||
id, err := CreateIdentity("test")
|
||||
if err != nil {
|
||||
t.Fatal("CreateIdentity failed")
|
||||
}
|
||||
id.Save()
|
||||
for i := 1; i < 10; i++ {
|
||||
var p Peer
|
||||
p.Name = "test" + strconv.Itoa(i)
|
||||
p.Contact.ContactPublicKey = "stringToFind" + strconv.Itoa(i)
|
||||
id.Peers = append(id.Peers, p)
|
||||
p.ContactPublicKey = "stringToFind" + strconv.Itoa(i)
|
||||
id.Peers = append(id.Peers, &p)
|
||||
}
|
||||
p5 := id.Peers.GetFromPublicKey("stringToFind5")
|
||||
assert.Equal(t, p5.Name, "test5")
|
||||
|
@ -4,12 +4,21 @@ import (
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
)
|
||||
|
||||
type PeerList []Peer
|
||||
type PeerList []*Peer
|
||||
|
||||
func (pl *PeerList) GetFromPublicKey(publickey string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.Contact.ContactPublicKey == publickey {
|
||||
return &peer
|
||||
if peer.ContactPublicKey == publickey {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pl *PeerList) GetFromInvitationId(invitationId string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.InvitationId == invitationId {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -18,7 +27,7 @@ func (pl *PeerList) GetFromPublicKey(publickey string) *Peer {
|
||||
func (pl *PeerList) GetFromMyLookupKey(publickey string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.MyLookupKp.Public == publickey {
|
||||
return &peer
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -26,13 +35,14 @@ func (pl *PeerList) GetFromMyLookupKey(publickey string) *Peer {
|
||||
|
||||
func (pl *PeerList) GetFromName(name string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.Contact.Name == name {
|
||||
return &peer
|
||||
if peer.Name == name {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ! Wrong implementation, does not discriminate on different servers
|
||||
func (pl *PeerList) GetConversationRequests() []*meowlib.ConversationRequest {
|
||||
var list []*meowlib.ConversationRequest
|
||||
for _, peer := range *pl {
|
||||
|
131
client/server.go
131
client/server.go
@ -2,6 +2,7 @@ package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
@ -17,30 +18,95 @@ import (
|
||||
// - Utility functions for packing/unpacking, encrypting/decrypting messages for server communication
|
||||
// - Server remote management if ManagerKp is available for that server
|
||||
type Server struct {
|
||||
ServerData meowlib.ServerCard `json:"server_data,omitempty"`
|
||||
Presence bool `json:"presence,omitempty"`
|
||||
LastCheck time.Time `json:"last_check,omitempty"`
|
||||
Uptime time.Duration `json:"uptime,omitempty"`
|
||||
Login string `json:"login,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
UserKp meowlib.KeyPair `json:"user_kp,omitempty"`
|
||||
ManagerKp meowlib.KeyPair `json:"manager_kp,omitempty"`
|
||||
Country string `json:"country,omitempty"`
|
||||
AllowedDelay int `json:"allowed_delay,omitempty"`
|
||||
Backup bool `json:"backup,omitempty"`
|
||||
//ServerCard meowlib.ServerCard `json:"server_data,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
PublicKey string `json:"public_key,omitempty"`
|
||||
Url string `json:"url,omitempty"`
|
||||
Login string `json:"login,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Presence bool `json:"presence,omitempty"`
|
||||
LastCheck time.Time `json:"last_check,omitempty"`
|
||||
Uptime time.Duration `json:"uptime,omitempty"`
|
||||
UserKp meowlib.KeyPair `json:"user_kp,omitempty"`
|
||||
ManagerKp meowlib.KeyPair `json:"manager_kp,omitempty"`
|
||||
Country string `json:"country,omitempty"`
|
||||
AllowedDelay int `json:"allowed_delay,omitempty"`
|
||||
Backup bool `json:"backup,omitempty"`
|
||||
}
|
||||
|
||||
// CreateServerFromUrl creates a server from a basic url, ex : https://my.meowserver.example:8443/meow/
|
||||
func CreateServerFromUrl(url string) *Server {
|
||||
var is Server
|
||||
is.ServerData.Url = url
|
||||
is.Url = url
|
||||
return &is
|
||||
}
|
||||
|
||||
// CreateServerFromUid creates a server from a uid string, ex : mylogin:mypassword@https://my.meowserver.example:8443/meow/
|
||||
func CreateServerFromUid(uid string) *Server {
|
||||
var is Server
|
||||
uidTable := strings.Split(uid, "@") //! Weak test, use regexp
|
||||
if len(uidTable) == 2 {
|
||||
loginpw := strings.Split(uidTable[0], ":")
|
||||
is.Url = uidTable[1]
|
||||
is.Login = loginpw[0]
|
||||
is.Password = loginpw[1]
|
||||
} else {
|
||||
is.Url = uidTable[0]
|
||||
}
|
||||
return &is
|
||||
}
|
||||
|
||||
// CreateServerFromMeowUrl creates a server from a meow url, ex : meow://mylogin:mypassword@https://my.meowserver.example:8443/meow/
|
||||
func CreateServerFromMeowUrl(meowurl string) *Server {
|
||||
uid := meowurl[7:]
|
||||
return CreateServerFromUid(uid)
|
||||
}
|
||||
|
||||
// CreateServerFromInvitationLink creates a server from a meow url, ex : meow://mylogin:mypassword@https://my.meowserver.example:8443/meow?invitationCode
|
||||
func CreateServerFromInvitationLink(meowurl string) *Server {
|
||||
// remove the invitation code, last token after a /
|
||||
meowurlTable := strings.Split(meowurl, "?")
|
||||
// join all elements with / except the last one
|
||||
meowSrvUrl := meowurlTable[0]
|
||||
return CreateServerFromMeowUrl(meowSrvUrl)
|
||||
}
|
||||
|
||||
// GetServerCard returns a server card from a server
|
||||
func (ints *Server) GetServerCard() *meowlib.ServerCard {
|
||||
var sc meowlib.ServerCard
|
||||
sc.Name = ints.Name
|
||||
sc.PublicKey = ints.PublicKey
|
||||
sc.Description = ints.Description
|
||||
sc.Url = ints.Url
|
||||
sc.Login = ints.Login
|
||||
sc.Password = ints.Password
|
||||
return &sc
|
||||
}
|
||||
|
||||
func (sc *Server) GetUid() string {
|
||||
if len(sc.Login) > 0 || len(sc.Password) > 0 {
|
||||
return sc.Login + ":" + sc.Password + "@" + sc.Url
|
||||
}
|
||||
return sc.Url
|
||||
}
|
||||
|
||||
func (sc *Server) GetMeowUrl() string {
|
||||
if len(sc.Login) > 0 || len(sc.Password) > 0 {
|
||||
return sc.Login + ":" + sc.Password + "@" + sc.Url
|
||||
}
|
||||
return "meow://" + sc.Url
|
||||
}
|
||||
|
||||
// Create a server from a server card
|
||||
func CreateServerFromServerCard(server *meowlib.ServerCard) *Server {
|
||||
var is Server
|
||||
is.ServerData = *server
|
||||
is.Name = server.Name
|
||||
is.PublicKey = server.PublicKey
|
||||
is.Description = server.Description
|
||||
is.Url = server.Url
|
||||
is.Login = server.Login
|
||||
is.Password = server.Password
|
||||
is.UserKp = meowlib.NewKeyPair()
|
||||
return &is
|
||||
}
|
||||
@ -48,7 +114,7 @@ func CreateServerFromServerCard(server *meowlib.ServerCard) *Server {
|
||||
// AsymEncryptMessage prepares a message to send to a specific internal server
|
||||
func (ints *Server) AsymEncryptMessage(Message []byte) (*meowlib.EncryptedMessage, error) {
|
||||
var enc *meowlib.EncryptedMessage
|
||||
enc, err := meowlib.AsymEncryptAndSign(ints.ServerData.PublicKey, ints.UserKp.Private, Message)
|
||||
enc, err := meowlib.AsymEncryptAndSign(ints.PublicKey, ints.UserKp.Private, Message)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return nil, err
|
||||
@ -58,7 +124,7 @@ func (ints *Server) AsymEncryptMessage(Message []byte) (*meowlib.EncryptedMessag
|
||||
|
||||
// AsymDecryptMessage reads a message from a specific internal server
|
||||
func (ints *Server) AsymDecryptMessage(Message []byte, Signature []byte) (DecryptedMessage []byte, err error) {
|
||||
DecryptedMessage, err = meowlib.AsymDecryptAndCheck(ints.UserKp.Private, ints.ServerData.PublicKey, Message, Signature)
|
||||
DecryptedMessage, err = meowlib.AsymDecryptAndCheck(ints.UserKp.Private, ints.PublicKey, Message, Signature)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return nil, err
|
||||
@ -101,7 +167,7 @@ func (ints *Server) BuildMessageRequestMessage(lookupKeys []string) ([]byte, err
|
||||
|
||||
// BuildToServerMessageInvitation creates an invitation message to server and returns it as a meowlib.ToServerMessage
|
||||
// it takes as input a contactcard generated by Identity.InvitePeer
|
||||
func (ints *Server) BuildToServerMessageInvitationCreation(invitation *meowlib.ContactCard, password string, timeout int, invitationIdLen int) (*meowlib.ToServerMessage, error) {
|
||||
func (ints *Server) BuildToServerMessageInvitationCreation(invitation *meowlib.ContactCard, password string, timeout int, shortCodeLen int) (*meowlib.ToServerMessage, error) {
|
||||
var msg meowlib.ToServerMessage
|
||||
var inv meowlib.Invitation
|
||||
payload, err := invitation.Compress()
|
||||
@ -113,7 +179,7 @@ func (ints *Server) BuildToServerMessageInvitationCreation(invitation *meowlib.C
|
||||
inv.Step = 1
|
||||
inv.Password = password
|
||||
inv.Timeout = int32(timeout)
|
||||
inv.ShortcodeLen = int32(invitationIdLen)
|
||||
inv.ShortcodeLen = int32(shortCodeLen)
|
||||
inv.Payload = payload
|
||||
msg.Invitation = &inv
|
||||
return &msg, nil
|
||||
@ -132,6 +198,37 @@ func (ints *Server) BuildToServerMessageInvitationRequest(shortcode string, pass
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
// BuildToServerMessageInvitationAnswer creates an invitation answer to server and returns it as a meowlib.ToServerMessage
|
||||
// it takes as input a contactcard generated by Identity.InvitePeer
|
||||
func (ints *Server) BuildToServerMessageInvitationAnswer(invitationAnswer *meowlib.PackedUserMessage, myPublicKeyForThatPeer string, invitation_id string, timeout int) (*meowlib.ToServerMessage, error) {
|
||||
var msg meowlib.ToServerMessage
|
||||
var inv meowlib.Invitation
|
||||
invitationPayload, err := proto.Marshal(invitationAnswer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inv.Step = 3
|
||||
inv.Uuid = invitation_id
|
||||
msg.Type = "1"
|
||||
msg.From = ints.UserKp.Public
|
||||
inv.From = myPublicKeyForThatPeer
|
||||
inv.Payload = invitationPayload
|
||||
msg.Invitation = &inv
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
// BuildToServerMessageInvitationAnswerRequest requests invitation answer with provided id from server and returns it as a meowlib.ToServerMessage
|
||||
func (ints *Server) BuildToServerMessageInvitationAnswerRequest(invitationId string) (*meowlib.ToServerMessage, error) {
|
||||
var msg meowlib.ToServerMessage
|
||||
var inv meowlib.Invitation
|
||||
msg.Type = "1"
|
||||
msg.From = ints.UserKp.Public
|
||||
inv.Step = 4
|
||||
inv.Uuid = invitationId
|
||||
msg.Invitation = &inv
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
// PackServerMessage
|
||||
func (ints *Server) PackServerMessage(payload []byte, signature []byte) (protoPackedMessage []byte, err error) {
|
||||
var msg meowlib.PackedServerMessage
|
||||
|
@ -37,7 +37,7 @@ func (sl *ServerList) GetServerByIdx(idx int) (server *Server, err error) {
|
||||
// GetServerByPubkey returns a server from it's public key
|
||||
func (sl *ServerList) GetServerByPubkey(pubkey string) (filtered *Server) {
|
||||
for _, srv := range sl.Servers {
|
||||
if srv.ServerData.PublicKey == pubkey {
|
||||
if srv.PublicKey == pubkey {
|
||||
return &srv
|
||||
}
|
||||
}
|
||||
|
235
client/serverstorage.go
Normal file
235
client/serverstorage.go
Normal file
@ -0,0 +1,235 @@
|
||||
package client
|
||||
|
||||
//
|
||||
// Storage
|
||||
//
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"path/filepath"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"github.com/dgraph-io/badger"
|
||||
)
|
||||
|
||||
type ServerStorage struct {
|
||||
DbFile string `json:"db_file,omitempty"`
|
||||
db *badger.DB
|
||||
}
|
||||
|
||||
// Open a badger database from struct ServerStorage
|
||||
func (ss *ServerStorage) open() error {
|
||||
|
||||
opts := badger.DefaultOptions(filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, ss.DbFile))
|
||||
opts.Logger = nil
|
||||
var err error
|
||||
ss.db, err = badger.Open(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Store function StoreServer stores a server in a badger database with Server.GetUid() as key
|
||||
func (ss *ServerStorage) StoreServer(sc *Server) error {
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ss.close()
|
||||
// first marshal the Server to bytes with protobuf
|
||||
jsonsrv, err := json.Marshal(sc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := meowlib.SymEncrypt(GetConfig().memoryPassword, jsonsrv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
shakey := sha256.Sum256([]byte(sc.GetServerCard().GetUid()))
|
||||
key := shakey[:]
|
||||
// then store it in the database
|
||||
return ss.db.Update(func(txn *badger.Txn) error {
|
||||
return txn.Set(key, data)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// LoadServer function loads a Server from a badger database with Server.GetUid() as key
|
||||
func (ss *ServerStorage) LoadServer(uid string) (*Server, error) {
|
||||
var sc Server
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ss.close()
|
||||
shakey := sha256.Sum256([]byte(uid))
|
||||
key := shakey[:]
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
item, err := txn.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(GetConfig().memoryPassword, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
})
|
||||
return &sc, err
|
||||
}
|
||||
|
||||
// DeleteServer function deletes a Server from a badger database with Server.GetUid() as key
|
||||
func (ss *ServerStorage) DeleteServer(uid string) error {
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ss.close()
|
||||
shakey := sha256.Sum256([]byte(uid))
|
||||
key := shakey[:]
|
||||
return ss.db.Update(func(txn *badger.Txn) error {
|
||||
return txn.Delete(key)
|
||||
})
|
||||
}
|
||||
|
||||
// LoadAllServers function loads all Servers from a badger database
|
||||
func (ss *ServerStorage) LoadAllServers() ([]*Server, error) {
|
||||
var scs []*Server
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ss.close()
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
opts := badger.DefaultIteratorOptions
|
||||
opts.PrefetchSize = 10
|
||||
it := txn.NewIterator(opts)
|
||||
defer it.Close()
|
||||
for it.Rewind(); it.Valid(); it.Next() {
|
||||
item := it.Item()
|
||||
var sc Server
|
||||
err := item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(GetConfig().memoryPassword, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scs = append(scs, &sc)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return scs, err
|
||||
}
|
||||
|
||||
// LoadAllServers function loads all ServersCards from a badger database
|
||||
func (ss *ServerStorage) LoadAllServerCards() ([]*meowlib.ServerCard, error) {
|
||||
var scs []*meowlib.ServerCard
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ss.close()
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
opts := badger.DefaultIteratorOptions
|
||||
opts.PrefetchSize = 10
|
||||
it := txn.NewIterator(opts)
|
||||
defer it.Close()
|
||||
for it.Rewind(); it.Valid(); it.Next() {
|
||||
item := it.Item()
|
||||
var sc Server
|
||||
err := item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(GetConfig().memoryPassword, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scs = append(scs, sc.GetServerCard())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return scs, err
|
||||
}
|
||||
|
||||
// LoadServersFromUids function loads Servers with id in []Uid parameter from a badger database
|
||||
func (ss *ServerStorage) LoadServersFromUids(uids []string) ([]*Server, error) {
|
||||
var scs []*Server
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ss.close()
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
for _, uid := range uids {
|
||||
shakey := sha256.Sum256([]byte(uid))
|
||||
key := shakey[:]
|
||||
item, err := txn.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sc Server
|
||||
err = item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(GetConfig().memoryPassword, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scs = append(scs, &sc)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return scs, err
|
||||
}
|
||||
|
||||
// LoadServersFromUids function loads Servers with id in []Uid parameter from a badger database
|
||||
func (ss *ServerStorage) LoadServerCardsFromUids(uids []string) ([]*meowlib.ServerCard, error) {
|
||||
var scs []*meowlib.ServerCard
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ss.close()
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
for _, uid := range uids {
|
||||
shakey := sha256.Sum256([]byte(uid))
|
||||
key := shakey[:]
|
||||
item, err := txn.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sc Server
|
||||
err = item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(GetConfig().memoryPassword, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scs = append(scs, sc.GetServerCard())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return scs, err
|
||||
}
|
||||
|
||||
// close a badger database
|
||||
func (ss *ServerStorage) close() {
|
||||
ss.db.Close()
|
||||
}
|
103
client/serverstorage_test.go
Normal file
103
client/serverstorage_test.go
Normal file
@ -0,0 +1,103 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
)
|
||||
|
||||
func TestGetUid(t *testing.T) {
|
||||
srv := Server{
|
||||
Name: "test",
|
||||
Url: "http://127.0.0.1:8080",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
uid := srv.GetUid()
|
||||
if uid != "http://127.0.0.1:8080" {
|
||||
log.Fatal("uid not correct")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreServer(t *testing.T) {
|
||||
GetConfig().SetMemPass("test")
|
||||
ss := ServerStorage{DbFile: "test.db"}
|
||||
srv := Server{
|
||||
Name: "test",
|
||||
Url: "http://127.0.0.1",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
err := ss.StoreServer(&srv)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
sout, err := ss.LoadServer(srv.GetServerCard().GetUid())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if sout == nil {
|
||||
log.Fatal("server not found")
|
||||
}
|
||||
if sout.Name != srv.Name {
|
||||
log.Fatal("name not found")
|
||||
}
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
||||
|
||||
func TestLoadServersFromUids(t *testing.T) {
|
||||
GetConfig().SetMemPass("test")
|
||||
ss := ServerStorage{DbFile: "test.db"}
|
||||
srv := Server{
|
||||
Name: "test",
|
||||
Url: "http://localhost:8080",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
err := ss.StoreServer(&srv)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
sout, err := ss.LoadServersFromUids([]string{srv.GetServerCard().GetUid()})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if sout == nil {
|
||||
log.Fatal("server not found")
|
||||
}
|
||||
if sout[0].Name != srv.Name {
|
||||
log.Fatal("name not found")
|
||||
}
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
||||
|
||||
func TestLoadServerCardsFromUids(t *testing.T) {
|
||||
GetConfig().SetMemPass("test")
|
||||
ss := ServerStorage{DbFile: "test.db"}
|
||||
srv := Server{
|
||||
Name: "test",
|
||||
Url: "http://localhost:8080",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
err := ss.StoreServer(&srv)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
sout, err := ss.LoadServerCardsFromUids([]string{srv.GetServerCard().GetUid()})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if sout == nil {
|
||||
log.Fatal("server not found")
|
||||
}
|
||||
if sout[0].Name != srv.Name {
|
||||
log.Fatal("name not found")
|
||||
}
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"github.com/google/uuid"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func StoreMessage(peer *Peer, usermessage *meowlib.UserMessage, password string) error {
|
||||
var dbid string
|
||||
// If no db/no ID create DB + Tablz
|
||||
// TODO : if file size > X new db
|
||||
if len(peer.DbIds) == 0 {
|
||||
dbid = uuid.NewString()
|
||||
file, err := os.Create(filepath.Join(GetConfig().StoragePath, dbid+GetConfig().DbSuffix))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
peer.DbIds = append(peer.DbIds, dbid)
|
||||
sqliteDatabase, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
err = createMessageTable(sqliteDatabase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqliteDatabase.Close()
|
||||
GetConfig().me.Save()
|
||||
} else {
|
||||
dbid = peer.DbIds[len(peer.DbIds)-1]
|
||||
}
|
||||
// Open Db
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
// Detach Files
|
||||
if len(usermessage.Files) > 0 {
|
||||
for _, f := range usermessage.Files {
|
||||
hiddenFilename := uuid.NewString()
|
||||
// Cypher file
|
||||
encData, err := meowlib.SymEncrypt(password, f.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.WriteFile(hiddenFilename, encData, 0600)
|
||||
// replace f.Data by uuid filename
|
||||
f.Data = []byte(hiddenFilename)
|
||||
}
|
||||
}
|
||||
// Encrypt message
|
||||
out, err := proto.Marshal(usermessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encData, err := meowlib.SymEncrypt(password, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Insert message
|
||||
insertMessageSQL := `INSERT INTO message(m) VALUES (?) RETURNING ID`
|
||||
statement, err := db.Prepare(insertMessageSQL) // Prepare statement.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = statement.Exec(encData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get last messages from a peer
|
||||
func GetLastMessages(peer *Peer, inAppMsgCount int, lastDbId int, wantMore int, password string) ([]InternalUserMessage, error) {
|
||||
var messages []InternalUserMessage
|
||||
fileidx := len(peer.DbIds) - 1
|
||||
// initialize count with last db message count
|
||||
countStack, err := getMessageCount(peer.DbIds[fileidx])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// while the db message count < what we already have in app, step to next db file
|
||||
for inAppMsgCount > countStack {
|
||||
fileidx--
|
||||
if fileidx < 0 {
|
||||
return nil, nil
|
||||
}
|
||||
newCount, err := getMessageCount(peer.DbIds[fileidx])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
countStack += newCount
|
||||
}
|
||||
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
// if it's first app query, it won't hold a lastIndex, so let's start from end
|
||||
if lastDbId == 0 {
|
||||
lastDbId = math.MaxInt64
|
||||
}
|
||||
stm, err := db.Prepare("SELECT id, m FROM message WHERE id < ? ORDER BY id DESC LIMIT ?")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stm.Close()
|
||||
rows, err := stm.Query(lastDbId, wantMore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var ium InternalUserMessage
|
||||
var um meowlib.UserMessage
|
||||
var id int64
|
||||
var m []byte
|
||||
err = rows.Scan(&id, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decdata, err := meowlib.SymDecrypt(password, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = proto.Unmarshal(decdata, &um)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ium.dbid = id
|
||||
ium.message = &um
|
||||
messages = append(messages, ium)
|
||||
}
|
||||
// TODO DB overlap
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func getMessageCount(dbid string) (int, error) {
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
var count int
|
||||
query := "SELECT COUNT(*) FROM message"
|
||||
err := db.QueryRow(query).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func createMessageTable(db *sql.DB) error {
|
||||
createMessageTableSQL := `CREATE TABLE message (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"m" BLOB);` // SQL Statement for Create Table
|
||||
statement, err := db.Prepare(createMessageTableSQL) // Prepare SQL Statement
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statement.Exec() // Execute SQL Statements
|
||||
return nil
|
||||
}
|
||||
|
||||
func createServerTable(db *sql.DB) error {
|
||||
createServerTableSQL := `CREATE TABLE servers (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"country" varchar(2),
|
||||
"public" bool,
|
||||
"uptime" int,
|
||||
"bandwith" float,
|
||||
"load" float,
|
||||
"url" varchar(2000)
|
||||
"name" varchar(255);
|
||||
"description" varchar(5000)
|
||||
"publickey" varchar(10000)
|
||||
)` // SQL Statement for Create Table
|
||||
statement, err := db.Prepare(createServerTableSQL) // Prepare SQL Statement
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statement.Exec() // Execute SQL Statements
|
||||
return nil
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
package meowlib
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCompress(t *testing.T) {
|
||||
func TestCompressAndJson(t *testing.T) {
|
||||
kp1 := NewKeyPair()
|
||||
kp2 := NewKeyPair()
|
||||
kp3 := NewKeyPair()
|
||||
@ -13,9 +16,50 @@ func TestCompress(t *testing.T) {
|
||||
cc.ContactPublicKey = kp1.Public
|
||||
cc.EncryptionPublicKey = kp2.Public
|
||||
cc.LookupPublicKey = kp3.Public
|
||||
cc.InvitationMessage = "hello, it's me"
|
||||
cc.AddUrls([]string{"https://meow.myfirstdomain.com/services/meow:8080", "https://meow.myseconddomain.com/services/meow:8080", "http://meow.mythirddomain.com/services/meow:8080"})
|
||||
serialized, _ := cc.Serialize()
|
||||
println(len(serialized))
|
||||
compressed, _ := cc.Compress()
|
||||
println(len(compressed))
|
||||
ncc, err := NewContactCardFromCompressed(compressed)
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
assert.Equal(t, ncc.Name, cc.Name)
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
jsoncc, err := json.Marshal(cc)
|
||||
var cc1 ContactCard
|
||||
err = json.Unmarshal(jsoncc, &cc1)
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStaticJson(t *testing.T) {
|
||||
ccsrt := `
|
||||
{
|
||||
"contact_public_key": "LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuNy40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWlpST1pCWUpLd1lCQkFIYVJ3OEJBUWRBZy9iRnR4WG5hRjZLOFEzdGVWcmt3Y3YvRTV6TE94WnVZaXVvCm5MVkdtdWZOQzI1aGJXVWdQRzFoYVd3K3dvOEVFeFlJQUVFRkFtV1VUbVFKRUhmcmlVWXZsWnhyRmlFRXFFMDIKVVRmOTRTY1hJWVdjZCt1SlJpK1ZuR3NDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdVbkNRSUhBZ0FBYmN3QgpBT1VsTHJEbWpCM0pKeGNWUFNHaU1KTlZrem1idlhMTDVSSnh4aTNuNVVrMUFRQ2NHN29QeDYwTUdHRVNhc0V0CnBTS2VqUGFmNjNTVXhMelRoRFFacTlqOUI4NDRCR1dVVG1RU0Npc0dBUVFCbDFVQkJRRUJCMEJXeXMvaFZHSGcKRFN0V2Jid3VnbnlCdTFWdUlJbVBZMDRsKzRCQWd4QUFZQU1CQ2duQ2VBUVlGZ2dBS2dVQ1paUk9aQWtRZCt1SgpSaStWbkdzV0lRU29UVFpSTi8zaEp4Y2hoWngzNjRsR0w1V2Nhd0liREFBQWkyTUEvMTdUYksyT3FMdzZDSWZmCkE3YnlwYitxNzdHVmZlQmtmY2l3aXlCM2xRSGxBUUROZzJONisxcklEbG40cXRRc0pFSWR1OUlMMzVlMjR6cWwKbEJMSVR0YVBBQT09Cj1KeHZrCi0tLS0tRU5EIFBHUCBQVUJMSUMgS0VZIEJMT0NLLS0tLS0=",
|
||||
"encryption_public_key": "LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tCkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnClZlcnNpb246IEdvcGVuUEdQIDIuNy40Cgp4ak1FWlpST1pCWUpLd1lCQkFIYVJ3OEJBUWRBY3FocFM5dnFzVGE2WXJCWVEra1JaY1VDWnFSRUhVUTMrbWQyClZOdlNLYS9OQzI1aGJXVWdQRzFoYVd3K3dvOEVFeFlJQUVFRkFtV1VUbVFKRUNHb3JhR2xRMVVoRmlFRVcrZVUKRzJjZnVSaU5CVDNwSWFpdG9hVkRWU0VDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdVbkNRSUhBZ0FBNmRRQgpBSVN3OUFKeVphem83TWs2R2NVZzR6ZDROR1p2dVorZnNxMThoTmtNd0EwRUFRQzNaNmNUT2kraGlURjJLazVGClBtSnI4aHlQWlREVGgwa1I5NE14TzhBa0NzNDRCR1dVVG1RU0Npc0dBUVFCbDFVQkJRRUJCMEQzYWZpeS9ZT3YKZFRxMXJ0UTZhVTVLNS9COTFKTW5SaVptSGtTYW9YRDZYd01CQ2duQ2VBUVlGZ2dBS2dVQ1paUk9aQWtRSWFpdApvYVZEVlNFV0lRUmI1NVFiWngrNUdJMEZQZWtocUsyaHBVTlZJUUliREFBQVpxZ0EvMElaRTR2MmRZbFB5NURJCnNPeTlDbTNFakRJSy80SElJK1VuRm5qeGFtVENBUDQwbUJqaEJKZVRNbzV5ZWpDN2xlYXliUlNMcE1yY1NIeEcKSmpnOGJ0eDVBdz09Cj1rd1VTCi0tLS0tRU5EIFBHUCBQVUJMSUMgS0VZIEJMT0NLLS0tLS0=",
|
||||
"invitation_id": "38373ab8-f423-48bc-9136-628f2a7e0e18",
|
||||
"invitation_message": "Hi ! it's me !",
|
||||
"lookup_public_key": "LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuNy40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWlpST1pCWUpLd1lCQkFIYVJ3OEJBUWRBWmh2NGY3WG55aGhoU3JPQkUrc0VteGsrWkFGNFdjTjY4KythCnAwV25FU0xOQzI1aGJXVWdQRzFoYVd3K3dvOEVFeFlJQUVFRkFtV1VUbVFKRUVPTnRxN202L2h3RmlFRU9EaWMKL1cyMVJqNmJMWkdXUTQyMnJ1YnIrSEFDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdVbkNRSUhBZ0FBbi84QQovMWRjRHVIY3Fjd3JrNW9sclVPMUlIbVhSYWg1aC9wbm9HSDZMM0JSdlphbkFRRHozZTc0TmxZWFpnank3SVBFCittbnM5MDR6TnVqdnpFYks2SHg2dTh1VkJNNDRCR1dVVG1RU0Npc0dBUVFCbDFVQkJRRUJCMEFzVnBZd000TEMKK0JNT201WXFRWUEzRlFiTXI4alp3Wk5IelFvQ29URDRJZ01CQ2duQ2VBUVlGZ2dBS2dVQ1paUk9aQWtRUTQyMgpydWJyK0hBV0lRUTRPSno5YmJWR1Bwc3RrWlpEamJhdTV1djRjQUliREFBQUJxb0EvaUFOMGpIV0FHTXV2MTYxCkdxcXF6aGxPQXFzVjVKNW5iMy9LMk43TU9Pek1BUURzZEVaMlU5VmxXY3ljWDFuZGFoMnkzUnEvQ1QwWkJ0R2IKUzM1dU5HbDFBZz09Cj1kT2lnCi0tLS0tRU5EIFBHUCBQVUJMSUMgS0VZIEJMT0NLLS0tLS0=",
|
||||
"name": "me",
|
||||
"pull_servers": [
|
||||
{
|
||||
"name": "local",
|
||||
"public_key": "LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuNS4wCkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWSs2TUd4WUpLd1lCQkFIYVJ3OEJBUWRBVmk1ZnQyTmlmSzByVmVmYzNQd3JTRXhxNHRhVUtaTzhQeXprClB4SmNTT1BOQzI1aGJXVWdQRzFoYVd3K3dvd0VFeFlJQUQ0RkFtUHVqQnNKa0M4aTlzQjVvVjNyRmlFRWpNeEgKZldiZmxMblRhZzFRTHlMMndIbWhYZXNDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdJaUFRQUF4L0lCQU5kMgpVK3hZM09LQVk5elFSbmlXQXdlVEpoMWxySEpaMHd6RmVQS3JycUJvQVAwYjRISHBoT2dJWWx2TXlqajZ0TXZRCk01RTIzY3ZiWjRPZXRjNmNmeWxIQ2M0NEJHUHVqQnNTQ2lzR0FRUUJsMVVCQlFFQkIwQ2t1bWlndUpNT003Sy8KNEl1NVppYkJUYXAwSzBkNXNybkNCN2tIU2pObGV3TUJDZ25DZUFRWUZnZ0FLZ1VDWSs2TUd3bVFMeUwyd0htaApYZXNXSVFTTXpFZDladCtVdWROcURWQXZJdmJBZWFGZDZ3SWJEQUFBMlcwQS9qY2pZTUtQY3ZXcTA2QVpVKzRHClQwUmQxU2VNUXpzNndCUU9ZejEwQkVMWEFRQ3hTQ2kvN2RqRjZUWFl0SFpBSytrVUEvUHpYaW14bnRvVFpKbjMKV3l1Z0NnPT0KPTFjU20KLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ==",
|
||||
"url": "http://localhost:8080"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
var cc1 ContactCard
|
||||
jsoncc := []byte(ccsrt)
|
||||
err := json.Unmarshal(jsoncc, &cc1)
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,13 @@ go-plantuml generate -o generated/client.puml -d ../client
|
||||
sed -i 's/\.\.\/client/client/g' generated/client.puml
|
||||
go-plantuml generate -o generated/server.puml -d ../server
|
||||
sed -i 's/\.\.\/server/server/g' generated/server.puml
|
||||
cp *.puml generated/
|
||||
cd generated
|
||||
plantuml .
|
||||
plantuml -latex .
|
||||
mv *.tex tex/
|
||||
mv *.png png/
|
||||
rm *.log *.aux *.fdb_latexmk *.fls *.gz *.puml
|
||||
|
||||
|
||||
|
||||
|
13
doc/meow.tex
13
doc/meow.tex
@ -43,10 +43,17 @@ The server requires very few ressources and will run on any low cost single boar
|
||||
\textffm{Meow} also provides an anonymizing transfer service very similar to the Tor Onion protocol, we call it the Matriochka protocol.
|
||||
Any server can be used for building the transfer chain.
|
||||
Some of them might be marked as trusted.
|
||||
Random delays and random payload padding might be set for each forwarding step, making the overall message tracking much more difficult, even for organizations having capabilities of global network surveillance.
|
||||
Random delays and random size payload padding might be set for each forwarding step, making the overall message tracking much more difficult, even for organizations having capabilities of global network surveillance.
|
||||
It is strongly advised to use trusted servers as your first node and message server (the one that holds your incoming messages).
|
||||
|
||||
\subsubsection{Presence protocol for direct messaging}
|
||||
|
||||
\subsubsection{Message lookup obfuscation}
|
||||
Your device will request for messages using conversation keys on a very regular basis to your messaging(s) server(s).
|
||||
The device will check for conversation keys for all your contacts. If you check that option, it will also check for hidden contact keys.
|
||||
In case of data interception on your device link, in order to prevent statistical analysis, every request might be answered with size useful data (server's known server list).
|
||||
Moreover, some random keys will be added to your requests list.
|
||||
|
||||
\subsubsection{Presence protocol for direct messaging TBC}
|
||||
A presence service associating your conversation keys to your IP address for direct peer to peer connection is also provided.
|
||||
The presence protocol is simply activated by setting a flag in the message poll requests.
|
||||
If that flag is set, your encrypted IP will be published on the server, allowing your only your peer(s) to decrypt it and directly communicate with your terminal.
|
||||
@ -208,7 +215,7 @@ You just have to live with the goverment decisions.
|
||||
In the best scenario that government was elected, and might represent at most 25\% of the population.
|
||||
In most case, they will vote laws to satisfy the powerful people who supported their election, and the most powerful lobbies.
|
||||
|
||||
\textffm{Meow} Nations aim to be the next lobbying power to influence real life politics, "the poor man's lobby".
|
||||
\textffm{Meow} Nations aims to be the next lobbying power to influence real life politics, "the poor man's lobby".
|
||||
|
||||
Virtual nation in that perspective will be probably quickly flagged as terrorist nation by the old world media, but well,
|
||||
one man's terrorist is another man's freedom fighter.
|
||||
|
7
doc/sq_inv.puml
Normal file
7
doc/sq_inv.puml
Normal file
@ -0,0 +1,7 @@
|
||||
@startuml Server Invitation Step 01
|
||||
Bob -> MeowBob: Create invitation for alice
|
||||
Bob -> Alice: Send invitation
|
||||
Alice -> MeowAlice: Accept Invitation and create answer
|
||||
Alice -> Bob: Send answer
|
||||
Bob -> MeowBob: Review Answer, invitation finalize
|
||||
@enduml
|
12
doc/sq_srvinv01.puml
Normal file
12
doc/sq_srvinv01.puml
Normal file
@ -0,0 +1,12 @@
|
||||
@startuml Server Invitation Step 01
|
||||
User -> Bastet : fill invitation
|
||||
User -> Bastet : select servers
|
||||
Bastet -> NativeLib : get server cards for selected uids
|
||||
NativeLib -> Bastet : server cards
|
||||
Bastet -> NativeLib : invitationCreateMessage
|
||||
NativeLib -> Bastet : invitationMessage
|
||||
Bastet -> Server : send invitation
|
||||
Server -> Redis : Store invitation
|
||||
Server -> Bastet : invitation URL
|
||||
Bastet -> User : invitation URL
|
||||
@enduml
|
11
doc/sq_srvinv02.puml
Normal file
11
doc/sq_srvinv02.puml
Normal file
@ -0,0 +1,11 @@
|
||||
@startuml Server Invitation Step 02
|
||||
User -> Bastet : paste URL
|
||||
Bastet -> NativeLib : build invitationGetMessage
|
||||
NativeLib -> Bastet : invitationGetMessage
|
||||
Bastet -> Server : send invitationGetMessage
|
||||
Redis -> Server : retrieve invitation
|
||||
Server -> Bastet : invitation message
|
||||
Bastet -> NativeLib : decode invitation message
|
||||
NativeLib -> Bastet : invitation data
|
||||
Bastet -> User : invitation data
|
||||
@enduml
|
12
doc/sq_srvinv03.puml
Normal file
12
doc/sq_srvinv03.puml
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
@startuml Server Invitation Step 03
|
||||
User -> Bastet : select servers
|
||||
User -> Bastet : accept invitation
|
||||
Bastet -> NativeLib : accept invitation
|
||||
Bastet -> NativeLib : build accept message
|
||||
NativeLib -> Bastet : invitationGetMessage
|
||||
Bastet -> Server : send accept message
|
||||
Server -> Redis : store accept message
|
||||
Server -> Bastet : accept message ok
|
||||
Bastet -> User : accept msg sent
|
||||
@enduml
|
9
doc/sq_srvinv04.puml
Normal file
9
doc/sq_srvinv04.puml
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
@startuml Server Invitation Step 03
|
||||
Bastet -> NativeLib : periodic message check
|
||||
NativeLib -> Server : get new messages
|
||||
Server -> NativeLib : send invitation message
|
||||
Server -> Redis : store accept message
|
||||
Server -> Bastet : accept message ok
|
||||
Bastet -> User : invitation sent is accepted
|
||||
@enduml
|
@ -22,29 +22,34 @@ func TestEndToEnd(t *testing.T) {
|
||||
///////////////////////////
|
||||
// Creating New Identity //
|
||||
///////////////////////////
|
||||
Me = client.CreateIdentity("myname")
|
||||
Me, err = client.CreateIdentity("myname")
|
||||
|
||||
// define my preferences (servers)
|
||||
Me.MessageServers.Name = "Message Servers"
|
||||
Me.MessageServers.AddUrls([]string{"http://127.0.0.1/meow/", "mqtt://127.0.0.1", "meow://127.0.0.1"})
|
||||
srv := client.Server{Name: "MyServer", Url: "http://127.0.0.1/meow/"}
|
||||
Me.MessageServers.StoreServer(&srv)
|
||||
srv = client.Server{Name: "MyServer", Url: "mqtt://127.0.0.1"}
|
||||
Me.MessageServers.StoreServer(&srv)
|
||||
srv = client.Server{Name: "MyServer", Url: "meow://127.0.0.1"}
|
||||
Me.MessageServers.StoreServer(&srv)
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Create an invitation for a friend, I want him/her to know me as Bender //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
fmt.Println("Creating an invitation for the first friend...")
|
||||
peer, myContactCard, err := Me.InvitePeer("Bender", "myfirstfriend", []int{1, 2})
|
||||
peer, err := Me.InvitePeer("Bender", "myfirstfriend", []string{"http://127.0.0.1/meow/", "mqtt://127.0.0.1"}, "welcome, it's me!")
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
println(peer.Name)
|
||||
// print my invitation
|
||||
a, _ := json.Marshal(myContactCard)
|
||||
a, _ := json.Marshal(peer.GetMyContact())
|
||||
fmt.Println(string(a))
|
||||
// TODO : Convert invitation to QR Code
|
||||
myContactCard.WritePng("invitation.png")
|
||||
data, err := myContactCard.Compress()
|
||||
peer.GetMyContact().WritePng("invitation.png")
|
||||
data, err := peer.GetMyContact().Compress()
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
myContactCard.WriteQr("qrcode.png")
|
||||
peer.GetMyContact().WriteQr("qrcode.png")
|
||||
println("Compressed contact card :", len(data))
|
||||
///////////////////////////////////////
|
||||
// Simulate peer invitation response //
|
||||
@ -60,7 +65,7 @@ func TestEndToEnd(t *testing.T) {
|
||||
ReceivedContact.ContactPublicKey = FirstFriendContactKp.Public
|
||||
ReceivedContact.EncryptionPublicKey = FirstFriendEncryptionKp.Public
|
||||
ReceivedContact.LookupPublicKey = FirstFriendLookupKp.Public
|
||||
ReceivedContact.InvitationId = myContactCard.InvitationId
|
||||
ReceivedContact.InvitationId = peer.GetMyContact().InvitationId
|
||||
FriendServer1KP := meowlib.NewKeyPair()
|
||||
FriendServer1 := meowlib.ServerCard{Name: "FriendServer1", Url: "http://myfriend.org/meow/", PublicKey: FriendServer1KP.Public, Description: "Fancy description"}
|
||||
ReceivedContact.PullServers = append(ReceivedContact.PullServers, &FriendServer1)
|
||||
@ -100,9 +105,10 @@ func TestEndToEnd(t *testing.T) {
|
||||
// Packing it
|
||||
packedMsg := MyFirstFriend.PackUserMessage(enc.Data, enc.Signature)
|
||||
|
||||
srv := MyFirstFriend.Contact.PullServers[0]
|
||||
intS1 := client.CreateServerFromServerCard(srv)
|
||||
|
||||
intS1, err := Me.MessageServers.LoadServer("http://127.0.0.1/meow/")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
// Creating Server message for transporting the user message
|
||||
toServerMessage, err := intS1.BuildMessageSendingMessage(packedMsg)
|
||||
if err != nil {
|
||||
@ -130,9 +136,9 @@ func TestEndToEnd(t *testing.T) {
|
||||
// Simulating server side processing //
|
||||
///////////////////////////////////////
|
||||
var server1 server.Identity
|
||||
server1.ServerName = intS1.ServerData.Name
|
||||
server1.ServerName = intS1.Name
|
||||
server1.ServerKp = FriendServer1KP
|
||||
server1.ServerDesc = intS1.ServerData.Description
|
||||
server1.ServerDesc = intS1.Description
|
||||
// Unpack
|
||||
srv_from, srv_encmsg, srv_signature, err := server1.UnpackReceived(protoPackedServerMsg)
|
||||
if err != nil {
|
||||
|
17
go.mod
17
go.mod
@ -3,18 +3,21 @@ module forge.redroom.link/yves/meowlib
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.3
|
||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.5
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/dgraph-io/badger v1.6.2
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/makiuchi-d/gozxing v0.1.1
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.22.1 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/zerolog v1.31.0
|
||||
github.com/rs/zerolog v1.32.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/net v0.16.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/protobuf v1.31.0
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/protobuf v1.33.0
|
||||
)
|
||||
|
108
go.sum
108
go.sum
@ -1,20 +1,41 @@
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
|
||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.3 h1:AJu1OI/1UWVYZl6QcCLKGu9OTngS2r52618uGlje84I=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.3/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7NjbQWPJkA=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
||||
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
||||
github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po=
|
||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
@ -23,6 +44,7 @@ github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8w
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
@ -30,6 +52,7 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@ -38,19 +61,30 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I=
|
||||
github.com/makiuchi-d/gozxing v0.1.1/go.mod h1:eRIHbOjX7QWxLIDJoQuMLhuXg9LAuw6znsUtRkNw9DU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
@ -71,28 +105,47 @@ github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeR
|
||||
github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
|
||||
github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
|
||||
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
||||
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
|
||||
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
@ -112,8 +165,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
|
||||
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -121,8 +174,10 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -146,15 +201,19 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@ -163,8 +222,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
@ -175,8 +234,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@ -186,10 +245,11 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
|
40
http.go
Normal file
40
http.go
Normal file
@ -0,0 +1,40 @@
|
||||
package meowlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func HttpGetId(url string) (response map[string]string, err error) {
|
||||
srvId := make(map[string]string)
|
||||
resp, err := http.Get(url + "/id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(body, &srvId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return srvId, nil
|
||||
}
|
||||
|
||||
func HttpPostMessage(url string, msg []byte) (response []byte, err error) {
|
||||
resp, err := http.Post(url+"/msg",
|
||||
"application/octet-stream", bytes.NewBuffer(msg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body, nil
|
||||
}
|
@ -3,6 +3,7 @@ package meowlib
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@ -30,13 +31,13 @@ func (msg *UserMessage) AddFile(filename string, maxMessageSize int64) error {
|
||||
}
|
||||
|
||||
var file File
|
||||
file.Filename = filename
|
||||
file.Filename = filepath.Base(filename)
|
||||
file.Size = uint64(fi.Size())
|
||||
file.Data = data
|
||||
msg.Files = append(msg.Files, &file)
|
||||
|
||||
msg.Status = &ConversationStatus{}
|
||||
msg.Status.LocalUuid = uuid.New().String()
|
||||
msg.Status.Uuid = uuid.New().String()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
963
messages.pb.go
963
messages.pb.go
File diff suppressed because it is too large
Load Diff
@ -30,17 +30,23 @@ message Invitation {
|
||||
string uuid = 6; // id that the friend gave you, that you should include to your reply to get recognized
|
||||
int64 expiry = 7; // the server allowed expiry date, it may be samller than the requested timeout according to server policy
|
||||
int32 step = 8; // progress in the inviattion process : 1=invite friend, 2=friend requests invitation, 3=friend's answer
|
||||
string from=9; // used in step 3 the answer public key to check the signature in user message
|
||||
}
|
||||
|
||||
|
||||
// structure for requesting incoming messages
|
||||
message ConversationRequest {
|
||||
string lookupKey = 1; // lookup key for a conversation
|
||||
string lastServerUuidOK = 2; // Last Server message UUID received (send me all after that one)
|
||||
bool publishOnline = 3; // ?? Publish my online status for that contact ?
|
||||
string lookupSignature = 4; // prove that I own the private key by signing that block
|
||||
string lookup_key = 1; // lookup key for a conversation
|
||||
// removed string lastServerUuidOK = 2; // Last Server message UUID received (send me all after that one)
|
||||
int64 send_timestamp = 2;
|
||||
string lookup_signature = 3; // prove that I own the private key by signing that block
|
||||
}
|
||||
|
||||
message Meet {
|
||||
string public_status = 1; // Publish my online status, if the server is a meeting server
|
||||
ContactCard contact_card = 2; // mine or the requester
|
||||
string message = 3; // short description
|
||||
}
|
||||
|
||||
// structure defining a message for a server, that will be encrypted, then sent in a "packedmessage" payload
|
||||
message ToServerMessage {
|
||||
@ -48,18 +54,19 @@ message ToServerMessage {
|
||||
string from = 2 ; // My pub key for the server to send me an encrypter answer
|
||||
bytes payload = 3 ; // optional payload for server
|
||||
|
||||
repeated ConversationRequest pullRequest = 4;
|
||||
repeated ConversationRequest pull_request = 4;
|
||||
|
||||
repeated PackedUserMessage messages = 5;
|
||||
|
||||
repeated ServerCard knownServers = 6;
|
||||
repeated ServerCard known_servers = 6;
|
||||
|
||||
Matriochka matriochkaMessage = 7;
|
||||
Matriochka matriochka_message = 7;
|
||||
|
||||
string uuid = 8;
|
||||
|
||||
Invitation invitation = 9; // invitation for the 2 first steps of a "through server" invitation process
|
||||
|
||||
repeated PackedUserMessage device_messages = 10; // messages to another device belonging to the same user
|
||||
}
|
||||
|
||||
message ConversationResponse {
|
||||
@ -69,17 +76,20 @@ message ToServerMessage {
|
||||
// structure defining a from server receiver message decrypted from a "packedmessage" payload
|
||||
message FromServerMessage {
|
||||
string type = 1; // Type
|
||||
string serverPublicKey = 2 ; // Pub key from the server
|
||||
string server_public_key = 2 ; // Pub key from the server
|
||||
bytes payload = 3 ; //
|
||||
string uuidAck = 4 ; // Ack for the last received ToServerMessage Uuid
|
||||
string serverUuid = 5 ; // Provides the server uuid that replaced the client uuid
|
||||
string uuid_ack = 4 ; // Ack for the last received ToServerMessage Uuid
|
||||
string server_uuid = 5 ; // Provides the server uuid that replaced the client uuid
|
||||
|
||||
repeated PackedUserMessage chat = 6;
|
||||
|
||||
repeated ServerCard knownServers = 7;
|
||||
repeated ServerCard known_servers = 7;
|
||||
|
||||
Invitation invitation = 8; // invitation answer, for the third steps of any invitation
|
||||
|
||||
repeated PackedUserMessage device_messages = 9; // messages from other devices belonging to the same user
|
||||
|
||||
|
||||
}
|
||||
|
||||
message MatriochkaServer {
|
||||
@ -100,7 +110,7 @@ message Matriochka {
|
||||
message ServerCard {
|
||||
string name = 1; // friendly server name
|
||||
string description=2; // description : owner type (company/private/university...),
|
||||
string publicKey = 3; // public key you must use to send encrypted messages to that server
|
||||
string public_key = 3; // public key you must use to send encrypted messages to that server
|
||||
string url = 4; // meow server url
|
||||
string login = 5; // required login to access the server
|
||||
string password = 6; // password associated to the login
|
||||
@ -110,12 +120,13 @@ message ServerCard {
|
||||
// structure describing a user contact card ie the minimum set of attributes for exchanging identities
|
||||
message ContactCard {
|
||||
string name=1; // contact nickname
|
||||
string contactPublicKey =2; // contact public key, will be used to authenticate her/his messages
|
||||
string encryptionPublicKey= 3; // public key you must use to to write encrypted messages to that contact
|
||||
string lookupPublicKey =4; // public key you will use as "destination identifier" for her/him to lookup for your messages on the servers
|
||||
repeated ServerCard pullServers =5; // list the servers where the contact will look for messages from you
|
||||
string contact_public_key =2; // contact public key, will be used to authenticate her/his messages
|
||||
string encryption_public_key= 3; // public key you must use to to write encrypted messages to that contact
|
||||
string lookup_public_key =4; // public key you will use as "destination identifier" for her/him to lookup for your messages on the servers
|
||||
repeated ServerCard pull_servers =5; // list the servers where the contact will look for messages from you
|
||||
uint32 version = 6;
|
||||
string invitationId=7;
|
||||
string invitation_id=7;
|
||||
string invitation_message=8;
|
||||
}
|
||||
|
||||
// structure for sending a message to be forwarded to another user in protobuf format
|
||||
@ -127,13 +138,14 @@ message PackedUserMessage {
|
||||
}
|
||||
|
||||
message ConversationStatus {
|
||||
string localUuid = 1;
|
||||
uint64 localSequence = 2 ;
|
||||
uint64 sent = 3 ;
|
||||
uint64 received = 4;
|
||||
uint64 processed = 5;
|
||||
ContactCard myNextIdentity = 6;
|
||||
int32 peerNextIdentityAck = 7; // version of the new peer accepted id
|
||||
string uuid = 1;
|
||||
string answer_to_uuid=2; // message is an answer to another one, specify uuid here
|
||||
uint64 localSequence = 3 ;
|
||||
uint64 sent = 4 ;
|
||||
uint64 received = 5;
|
||||
uint64 processed = 6;
|
||||
ContactCard my_next_identity = 7;
|
||||
int32 peer_next_identityAck = 8; // version of the new peer accepted id
|
||||
}
|
||||
|
||||
message Group{
|
||||
@ -147,22 +159,15 @@ message UserMessage {
|
||||
string from = 2; // My public key for that contact
|
||||
string type = 3;
|
||||
bytes data = 4;
|
||||
|
||||
ConversationStatus Status = 5;
|
||||
|
||||
ConversationStatus status = 5;
|
||||
ContactCard contact = 6;
|
||||
|
||||
ServerCard knownServers = 7;
|
||||
|
||||
Group group = 8;
|
||||
|
||||
repeated File files = 9;
|
||||
|
||||
Location currentLocation = 10;
|
||||
|
||||
bytes appdata = 11;
|
||||
|
||||
Invitation invitation = 12;
|
||||
|
||||
}
|
||||
|
||||
message File {
|
||||
@ -177,4 +182,17 @@ message Location {
|
||||
float latitude=2;
|
||||
float longitude=3;
|
||||
int32 altitude=4;
|
||||
}
|
||||
}
|
||||
|
||||
message DbMessage {
|
||||
bool outbound = 1; // direction of the message
|
||||
string type = 2;
|
||||
bytes data = 3; // text data
|
||||
ConversationStatus status = 4;
|
||||
ContactCard contact = 5;
|
||||
Group group = 6;
|
||||
repeated string file_paths = 7;
|
||||
Location current_location = 8;
|
||||
bytes appdata = 9;
|
||||
Invitation invitation = 10;
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
#!/bin/bash
|
||||
echo Generating Golang
|
||||
protoc -I=. --go_out=.. messages.proto
|
||||
mv ../forge.redroom.link/yves/meowlib/messages.pb.go ../
|
||||
rm -rf ../forge.redroom.link
|
||||
|
||||
echo Generating HTML doc
|
||||
protoc --plugin=protoc-gen-doc=/usr/bin/protoc-gen-doc \
|
||||
--doc_out=../doc/generated \
|
||||
--doc_opt=html,index.html \
|
||||
*.proto
|
||||
|
||||
echo Generating UML
|
||||
protoc --plugin=protoc-gen-uml=/usr/bin/protoc-gen-uml \
|
||||
--uml_out=../doc/generated -I=. *.proto
|
||||
|
@ -52,7 +52,7 @@ func (id *Identity) Save(file string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(file, []byte(armor), 0644)
|
||||
err = os.WriteFile(file, []byte(armor), 0600)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -8,14 +8,14 @@ import (
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
func (r *RedisRouter) CreateInvitation(invitation []byte, timeout int, password string, serverTimeout int, urlLen int) (string, time.Time) {
|
||||
func (r *RedisRouter) StoreInvitation(invitation []byte, timeout int, password string, serverTimeout int, urlLen int) (string, time.Time) {
|
||||
id := r.createShortId(urlLen)
|
||||
if timeout > serverTimeout {
|
||||
timeout = serverTimeout
|
||||
}
|
||||
r.Client.Set("mwiv:"+id, invitation, time.Duration(timeout*1000000))
|
||||
r.Client.Set("mwiv:"+id, invitation, 0) //, time.Duration(timeout*1000000))
|
||||
if len(password) > 0 {
|
||||
r.Client.Set("mwpw:"+id, password, time.Duration(timeout*1000000))
|
||||
r.Client.Set("mwpw:"+id, password, 0) //, time.Duration(timeout*1000000))
|
||||
}
|
||||
return id, time.Now().Add(time.Duration(timeout * 1000000)).UTC()
|
||||
}
|
||||
@ -38,7 +38,7 @@ func (r *RedisRouter) GetInvitation(id string, password string) ([]byte, error)
|
||||
return []byte(mwiv), nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) AnswerInvitation(id string, timeout int, invitation []byte, serverTimeout int) time.Time {
|
||||
func (r *RedisRouter) StoreAnswerToInvitation(id string, timeout int, invitation []byte, serverTimeout int) time.Time {
|
||||
if timeout > serverTimeout {
|
||||
timeout = serverTimeout
|
||||
}
|
||||
@ -46,8 +46,8 @@ func (r *RedisRouter) AnswerInvitation(id string, timeout int, invitation []byte
|
||||
return time.Now().Add(time.Duration(timeout * 1000000)).UTC()
|
||||
}
|
||||
|
||||
func (r *RedisRouter) GetInvitationAnswer(id string) ([]byte, error) {
|
||||
mwan, err := r.Client.Get("mwiv:" + id).Result()
|
||||
func (r *RedisRouter) GetAnswerToInvitation(id string) ([]byte, error) {
|
||||
mwan, err := r.Client.Get("mwan:" + id).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
253
server/router.go
253
server/router.go
@ -37,78 +37,33 @@ func NewRedisRouter(server *Identity, redisUrl string, password string, db int,
|
||||
}
|
||||
|
||||
func (r *RedisRouter) Route(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
var from_server *meowlib.FromServerMessage
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:total").Err()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// user message
|
||||
// user message => store
|
||||
if len(msg.Messages) > 0 {
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:usermessages").Err()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, usrmsg := range msg.Messages {
|
||||
// serialize the message to store it as byte array into redis
|
||||
out, err := proto.Marshal(usrmsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Client.ZAdd(usrmsg.Destination, redis.Z{Score: float64(time.Now().Unix()), Member: out})
|
||||
}
|
||||
from_server.UuidAck = msg.Uuid
|
||||
}
|
||||
// check for messages
|
||||
if len(msg.PullRequest) > 0 {
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:messagelookups").Err()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, rq := range msg.PullRequest {
|
||||
msgcnt, err := r.Client.ZCount(rq.LookupKey, "-inf", "+inf").Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := r.Client.ZPopMin(rq.LookupKey, msgcnt).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, redismsg := range res {
|
||||
//println(redismsg.Score)
|
||||
val := redismsg.Member
|
||||
test := val.(string)
|
||||
var usrmsg meowlib.PackedUserMessage
|
||||
err := proto.Unmarshal([]byte(test), &usrmsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// add server timestamp
|
||||
usrmsg.ServerTimestamp = append(usrmsg.ServerTimestamp, int64(redismsg.Score))
|
||||
|
||||
from_server.Chat = append(from_server.Chat, &usrmsg)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// manage Matriochka
|
||||
if msg.MatriochkaMessage != nil {
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:matriochka").Err()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
out, err := proto.Marshal(msg)
|
||||
from_server, err = r.storeMessage(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Client.ZAdd("mtk", redis.Z{Score: float64(time.Now().Unix()), Member: out})
|
||||
if msg.MatriochkaMessage.LookupKey != "" {
|
||||
//r.Client.ZAdd("trk:" + msg.MatriochkaMessage.Next.Uuid,{})
|
||||
}
|
||||
// check for messages
|
||||
if len(msg.PullRequest) > 0 {
|
||||
from_server, err = r.checkForMessage(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
// manage Matriochka
|
||||
if msg.MatriochkaMessage != nil {
|
||||
from_server, err = r.handleMatriochka(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
from_server.UuidAck = msg.Uuid
|
||||
}
|
||||
// Server list exchange
|
||||
if len(msg.KnownServers) > 0 {
|
||||
@ -116,40 +71,9 @@ func (r *RedisRouter) Route(msg *meowlib.ToServerMessage) (*meowlib.FromServerMe
|
||||
}
|
||||
// Through server invitation process
|
||||
if msg.Invitation != nil {
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:invitation").Err()
|
||||
from_server, err = r.handleInvitation(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
switch msg.Invitation.Step {
|
||||
case 1: // create invitation
|
||||
url, expiry := r.CreateInvitation(msg.Invitation.Payload, int(msg.Invitation.Timeout), msg.Invitation.Password, r.InvitationTimeout, int(msg.Invitation.ShortcodeLen))
|
||||
from_server.Invitation.Shortcode = url
|
||||
from_server.Invitation.Expiry = expiry.UTC().Unix()
|
||||
case 2: // get invitation
|
||||
invitation, err := r.GetInvitation(msg.Invitation.Shortcode, msg.Invitation.Password)
|
||||
if err != nil {
|
||||
if err.Error() == "auth failed" {
|
||||
from_server.Invitation.Payload = []byte("authentication failure")
|
||||
} else {
|
||||
from_server.Invitation.Payload = []byte("invitation expired")
|
||||
}
|
||||
} else {
|
||||
from_server.Invitation.Payload = invitation
|
||||
}
|
||||
|
||||
/* should not happen
|
||||
case 3: // answer invitation
|
||||
expiry := r.AnswerInvitation(msg.Invitation.Id, int(msg.Invitation.Timeout), msg.Invitation.Payload, r.InvitationTimeout)
|
||||
from_server.Invitation.Expiry = expiry.UTC().Unix()
|
||||
case 4: // get answer
|
||||
answer, err := r.GetInvitationAnswer(msg.Invitation.Id)
|
||||
if err != nil {
|
||||
from_server.Invitation.Payload = []byte("invitation expired")
|
||||
} else {
|
||||
from_server.Invitation.Payload = answer
|
||||
}
|
||||
*/
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
/*
|
||||
@ -165,5 +89,144 @@ func (r *RedisRouter) Route(msg *meowlib.ToServerMessage) (*meowlib.FromServerMe
|
||||
break
|
||||
}
|
||||
*/
|
||||
return from_server, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) storeMessage(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:usermessages").Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, usrmsg := range msg.Messages {
|
||||
// serialize the message to store it as byte array into redis
|
||||
out, err := proto.Marshal(usrmsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Client.ZAdd(usrmsg.Destination, redis.Z{Score: float64(time.Now().Unix()), Member: out})
|
||||
}
|
||||
from_server.UuidAck = msg.Uuid
|
||||
return &from_server, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) checkForMessage(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
//dataFound := false
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:messagelookups").Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rq := range msg.PullRequest {
|
||||
// get messages from redis
|
||||
msgcnt, err := r.Client.ZCount(rq.LookupKey, "-inf", "+inf").Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := r.Client.ZPopMin(rq.LookupKey, msgcnt).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// iterate over messages
|
||||
for _, redismsg := range res {
|
||||
//println(redismsg.Score)
|
||||
val := redismsg.Member
|
||||
test := val.(string)
|
||||
var usrmsg meowlib.PackedUserMessage
|
||||
err := proto.Unmarshal([]byte(test), &usrmsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// add server timestamp
|
||||
usrmsg.ServerTimestamp = append(usrmsg.ServerTimestamp, int64(redismsg.Score))
|
||||
|
||||
from_server.Chat = append(from_server.Chat, &usrmsg)
|
||||
}
|
||||
// if no messages check for invitationanswer payload
|
||||
if msgcnt == 0 {
|
||||
// get invitation answer
|
||||
var answer meowlib.Invitation
|
||||
storedAnswer, _ := r.GetAnswerToInvitation(rq.LookupKey)
|
||||
if storedAnswer != nil {
|
||||
err := proto.Unmarshal(storedAnswer, &answer)
|
||||
if err != nil {
|
||||
from_server.Invitation.Payload = []byte("invitation answer corrupted")
|
||||
}
|
||||
from_server.Invitation = &answer
|
||||
// exit loop if invitation found, cannot store several in a message
|
||||
return &from_server, nil
|
||||
}
|
||||
// add invitation answer to the response
|
||||
}
|
||||
|
||||
}
|
||||
return &from_server, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) handleInvitation(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:invitation").Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch msg.Invitation.Step {
|
||||
// create invitation => provide shortcode and expiry
|
||||
case 1:
|
||||
url, expiry := r.StoreInvitation(msg.Invitation.Payload, int(msg.Invitation.Timeout), msg.Invitation.Password, r.InvitationTimeout, int(msg.Invitation.ShortcodeLen))
|
||||
from_server.Invitation = &meowlib.Invitation{}
|
||||
from_server.Invitation.Shortcode = url
|
||||
from_server.Invitation.Expiry = expiry.UTC().Unix()
|
||||
// get invitation => retrieve invitation from redis and send
|
||||
case 2:
|
||||
from_server.Invitation = &meowlib.Invitation{}
|
||||
invitation, err := r.GetInvitation(msg.Invitation.Shortcode, msg.Invitation.Password)
|
||||
|
||||
if err != nil {
|
||||
if err.Error() == "auth failed" {
|
||||
from_server.Invitation.Payload = []byte("authentication failure")
|
||||
} else {
|
||||
from_server.Invitation.Payload = []byte("invitation expired")
|
||||
}
|
||||
} else {
|
||||
from_server.Invitation.Payload = invitation // protobuf invitation
|
||||
}
|
||||
|
||||
// accept invitation => store accepted invitation for initiator
|
||||
case 3:
|
||||
var usermsg meowlib.PackedUserMessage
|
||||
err := proto.Unmarshal(msg.Invitation.Payload, &usermsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := proto.Marshal(msg.Invitation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expiry := r.StoreAnswerToInvitation(usermsg.Destination, int(msg.Invitation.Timeout), data, r.InvitationTimeout)
|
||||
from_server.Invitation = &meowlib.Invitation{}
|
||||
from_server.Invitation.Expiry = expiry.UTC().Unix()
|
||||
}
|
||||
return &from_server, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) handleMatriochka(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:matriochka").Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Client.ZAdd("mtk", redis.Z{Score: float64(time.Now().Unix()), Member: out})
|
||||
if msg.MatriochkaMessage.LookupKey != "" {
|
||||
//r.Client.ZAdd("trk:" + msg.MatriochkaMessage.Next.Uuid,{})
|
||||
}
|
||||
from_server.UuidAck = msg.Uuid
|
||||
return &from_server, nil
|
||||
}
|
||||
|
12
servercard.go
Normal file
12
servercard.go
Normal file
@ -0,0 +1,12 @@
|
||||
package meowlib
|
||||
|
||||
func (sc *ServerCard) GetUid() string {
|
||||
if len(sc.Login) > 0 || len(sc.Password) > 0 {
|
||||
return sc.Login + ":" + sc.Password + "@" + sc.Url
|
||||
}
|
||||
return sc.Url
|
||||
}
|
||||
|
||||
func (sc *ServerCard) IsSame(sc1 *ServerCard) bool {
|
||||
return sc.GetUid() == sc1.GetUid()
|
||||
}
|
Reference in New Issue
Block a user