invitation process upgrade
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
ycc
2026-04-02 18:50:04 +02:00
committed by yc
parent 9f130a80b7
commit 1906431061
21 changed files with 1185 additions and 638 deletions

View File

@@ -129,9 +129,20 @@ func ConsumeInboxFile(messageFilename string) ([]string, []string, string, error
if err != nil { if err != nil {
return nil, nil, "ReadMessage: Unmarshal FromServerMessage", err return nil, nil, "ReadMessage: Unmarshal FromServerMessage", err
} }
// check if invitation answer // check if invitation answer (step-2 answer waiting for the initiator)
if fromServerMessage.Invitation != nil { if fromServerMessage.Invitation != nil {
invitationGetAnswerReadResponse(fromServerMessage.Invitation) peer, _, _, invErr := InvitationStep3ProcessAnswer(fromServerMessage.Invitation)
if invErr == nil && peer != nil {
// Auto-send step-3 CC to invitee's servers.
msgs, _, sendErr := InvitationStep3Message(peer.InvitationId)
if sendErr == nil {
for i, bytemsg := range msgs {
if i < len(peer.ContactPullServers) {
meowlib.HttpPostMessage(peer.ContactPullServers[i], bytemsg, client.GetConfig().HttpTimeOut)
}
}
}
}
} }
// Chat messages // Chat messages
if len(fromServerMessage.Chat) > 0 { if len(fromServerMessage.Chat) > 0 {
@@ -142,12 +153,41 @@ func ConsumeInboxFile(messageFilename string) ([]string, []string, string, error
if peer == nil { if peer == nil {
return nil, nil, "ReadMessage: GetFromMyLookupKey", errors.New("no visible peer for that message") return nil, nil, "ReadMessage: GetFromMyLookupKey", errors.New("no visible peer for that message")
} }
// Unpack the message // Unpack the message — step-3 messages arrive before the initiator's identity
usermsg, err := peer.ProcessInboundUserMessage(packedUserMessage) // key is known, so skip signature verification for pending peers.
var usermsg *meowlib.UserMessage
if peer.InvitationPending() {
usermsg, err = peer.ProcessInboundStep3UserMessage(packedUserMessage)
} else {
usermsg, err = peer.ProcessInboundUserMessage(packedUserMessage)
}
if err != nil { if err != nil {
return nil, nil, "ReadMessage: ProcessInboundUserMessage", err return nil, nil, "ReadMessage: ProcessInboundUserMessage", err
} }
// Handle invitation step 3: initiator's full ContactCard arriving at the invitee.
if usermsg.Invitation != nil && usermsg.Invitation.Step == 3 {
finalizedPeer, _, finalErr := InvitationStep4ProcessStep3(usermsg)
if finalErr == nil && finalizedPeer != nil {
// Auto-send step-4 confirmation to initiator's servers.
step4msgs, _, sendErr := InvitationStep4Message(finalizedPeer.InvitationId)
if sendErr == nil {
for i, bytemsg := range step4msgs {
if i < len(finalizedPeer.ContactPullServers) {
meowlib.HttpPostMessage(finalizedPeer.ContactPullServers[i], bytemsg, client.GetConfig().HttpTimeOut)
}
}
}
}
continue
}
// Handle invitation step 4: invitee's confirmation arriving at the initiator.
if usermsg.Invitation != nil && usermsg.Invitation.Step == 4 {
// Contact is fully active — nothing more to do on the initiator side.
continue
}
// Check for received or processed already filled => it's an ack for one of our sent messages // Check for received or processed already filled => it's an ack for one of our sent messages
if len(usermsg.Data) == 0 && usermsg.Status != nil && usermsg.Status.Uuid != "" && if len(usermsg.Data) == 0 && usermsg.Status != nil && usermsg.Status.Uuid != "" &&
(usermsg.Status.Received != 0 || usermsg.Status.Processed != 0) { (usermsg.Status.Received != 0 || usermsg.Status.Processed != 0) {

View File

@@ -1,161 +1,110 @@
package helpers package helpers
import ( import (
"C" "errors"
"fmt"
"os" "os"
"strings" "strings"
"forge.redroom.link/yves/meowlib" "forge.redroom.link/yves/meowlib"
"forge.redroom.link/yves/meowlib/client" "forge.redroom.link/yves/meowlib/client"
) )
import (
"errors"
)
// InvitationAnswer
func InvitationAnswer(cc *meowlib.ContactCard, nickname string, myNickname string, serverUids []string) (*client.Peer, string, error) {
// InvitationStep2Answer creates the invitee's peer from an InvitationInitPayload and returns
// the new peer (STEP_2, invitee side — in-memory, no server involved).
func InvitationStep2Answer(payload *meowlib.InvitationInitPayload, nickname string, myNickname string, serverUids []string) (*client.Peer, string, error) {
mynick := myNickname mynick := myNickname
// my nickname for that contact
if myNickname == "" { if myNickname == "" {
mynick = client.GetConfig().GetIdentity().Nickname mynick = client.GetConfig().GetIdentity().Nickname
} }
peer, err := client.GetConfig().GetIdentity().InvitationStep2(mynick, nickname, serverUids, payload)
// build my contact card for that friend
peer, err := client.GetConfig().GetIdentity().AnswerInvitation(mynick, nickname, serverUids, cc)
if err != nil { if err != nil {
return nil, "InvitationAnswer: AnswerInvitation", err return nil, "InvitationStep2Answer: InvitationStep2", err
} }
client.GetConfig().GetIdentity().Save()
//peerstr, err := json.Marshal(peer)
//fmt.Println("InvitationAnswer: " + string(peerstr))
c := client.GetConfig()
c.GetIdentity().Save()
return peer, "", nil return peer, "", nil
} }
// InvitationAnswerFile // InvitationStep2AnswerFile reads an InvitationInitPayload from a .mwiv file and creates the
func InvitationAnswerFile(invitationFile string, nickname string, myNickname string, serverUids []string) (string, error) { // invitee's peer. It also writes the invitee's ContactCard response to a file (STEP_2_SEND, file variant).
format := "qr" func InvitationStep2AnswerFile(invitationFile string, nickname string, myNickname string, serverUids []string) (string, error) {
var filename string = ""
var cc *meowlib.ContactCard
c := client.GetConfig()
if _, err := os.Stat(invitationFile); os.IsNotExist(err) { if _, err := os.Stat(invitationFile); os.IsNotExist(err) {
return "InvitationAnswerFile : os.Stat", err return "InvitationStep2AnswerFile: os.Stat", err
} }
if strings.HasSuffix(invitationFile, ".mwiv") { if !strings.HasSuffix(invitationFile, ".mwiv") {
format = "mwiv" return "InvitationStep2AnswerFile: unsupported format", errors.New("only .mwiv files are supported")
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() data, err := os.ReadFile(invitationFile)
if cc != nil { if err != nil {
isAnswer, proposed, received, _ := identity.CheckInvitation(cc) return "InvitationStep2AnswerFile: os.ReadFile", err
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, err := identity.AnswerInvitation(mynick, nickname, serverUids, cc)
if err != nil {
return "InvitationAnswerFile : AnswerInvitation", err
}
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()
}
} }
payload, err := meowlib.NewInvitationInitPayloadFromCompressed(data)
if err != nil {
return "InvitationStep2AnswerFile: NewInvitationInitPayloadFromCompressed", err
}
mynick := myNickname
if myNickname == "" {
mynick = client.GetConfig().GetIdentity().Nickname
}
c := client.GetConfig()
response, err := c.GetIdentity().InvitationStep2(mynick, nickname, serverUids, payload)
if err != nil {
return "InvitationStep2AnswerFile: InvitationStep2", err
}
filename := c.StoragePath + string(os.PathSeparator) + mynick + "-" + nickname + ".mwiv"
if err := response.GetMyContact().WriteCompressed(filename); err != nil {
return "InvitationStep2AnswerFile: WriteCompressed", err
}
c.GetIdentity().Save()
return "", nil return "", nil
} }
// InvitationAnswerMessage // InvitationStep2AnswerMessage builds and returns the packed server message that posts the
func InvitationAnswerMessage(invitationId string, invitationServerUid string, timeout int) ([]byte, string, error) { // invitee's ContactCard (encrypted with the initiator's temp key) to the invitation server
// (STEP_2_SEND, through-server variant).
// find the peer with that invitation id func InvitationStep2AnswerMessage(invitationId string, invitationServerUid string, timeout int) ([]byte, string, error) {
/*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
}
}*/
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitationId) peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitationId)
if peer == nil { if peer == nil {
// declare a custom go error for no peer found return nil, "InvitationStep2AnswerMessage: peer not found", errors.New("no peer with that invitation id")
return nil, "InvitationAnswerMessage: loop for peer", errors.New("no peer with that invitation id")
} }
answermsg, err := peer.BuildInvitationAnswerMessage(peer.GetMyContact())
answermsg, err := peer.BuildInvitationStep2Message(peer.GetMyContact())
if err != nil { if err != nil {
return nil, "InvitationAnswerMessage: BuildInvitationAnswserMessage", err return nil, "InvitationStep2AnswerMessage: BuildInvitationStep2Message", err
} }
// Server: get the invitation server
invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid) invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
if err != nil { if err != nil {
return nil, "InvitationAnswerMessage: LoadServer", err return nil, "InvitationStep2AnswerMessage: LoadServer", err
} }
// this will be the invitation's payload
packedMsg, err := peer.ProcessOutboundUserMessage(answermsg) packedMsg, err := peer.ProcessOutboundUserMessage(answermsg)
if err != nil { if err != nil {
return nil, "InvitationAnswerMessage: ProcessOutboundUserMessage", err return nil, "InvitationStep2AnswerMessage: ProcessOutboundUserMessage", err
} }
// Creating Server message for transporting the user message
toServerMessage, err := invitationServer.BuildToServerMessageInvitationAnswer(packedMsg, peer.MyIdentity.Public, invitationId, timeout) toServerMessage, err := invitationServer.BuildToServerMessageInvitationAnswer(packedMsg, peer.MyIdentity.Public, invitationId, timeout)
if err != nil { if err != nil {
return nil, "InvitationAnswerMessage: BuildToServerMessageInvitationAnswer", err return nil, "InvitationStep2AnswerMessage: BuildToServerMessageInvitationAnswer", err
} }
// Server outbound processing
bytemsg, err := invitationServer.ProcessOutboundMessage(toServerMessage) bytemsg, err := invitationServer.ProcessOutboundMessage(toServerMessage)
if err != nil { if err != nil {
return nil, "InvitationAnswerMessage: ProcessOutboundMessage", err return nil, "InvitationStep2AnswerMessage: ProcessOutboundMessage", err
} }
return bytemsg, "", nil return bytemsg, "", nil
} }
// InvitationAnswerMessageReadResponse // InvitationStep2AnswerMessageReadResponse reads the server acknowledgement of a Step2 answer.
// Called by the invitation receiver func InvitationStep2AnswerMessageReadResponse(invitationData []byte, invitationServerUid string) (*meowlib.Invitation, string, error) {
// 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) server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
if err != nil { if err != nil {
return nil, "InvitationAnswerMessageReadResponse: LoadServer", err return nil, "InvitationStep2AnswerMessageReadResponse: LoadServer", err
} }
// Server inbound processing : get the invitation server
serverMsg, err := server.ProcessInboundServerResponse(invitationData) serverMsg, err := server.ProcessInboundServerResponse(invitationData)
if err != nil { if err != nil {
return nil, "InvitationAnswerMessageReadResponse: ProcessInboundServerResponse", err return nil, "InvitationStep2AnswerMessageReadResponse: ProcessInboundServerResponse", err
} }
return serverMsg.Invitation, "", nil return serverMsg.Invitation, "", nil
} }

View File

@@ -7,84 +7,31 @@ import (
"forge.redroom.link/yves/meowlib/client" "forge.redroom.link/yves/meowlib/client"
) )
// InvitationCheck // InvitationStep2GetMessage builds and returns the packed server message that retrieves
// todo // the InvitationInitPayload from the server using the shortcode URL (STEP_2, invitee side).
/* func InvitationStep2GetMessage(invitationUrl string, serverPublicKey string, invitationPassword string) ([]byte, string, error) {
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, "?") meowurl := strings.Split(invitationUrl, "?")
shortcode := meowurl[1] shortcode := meowurl[1]
srv, err := client.CreateServerFromMeowUrl(meowurl[0]) srv, err := client.CreateServerFromMeowUrl(meowurl[0])
if err != nil { if err != nil {
return nil, "InvitationGetMessage: CreateServerFromMeowUrl", err return nil, "InvitationStep2GetMessage: CreateServerFromMeowUrl", err
} }
// check if already in msg servers
// Reuse the server entry if already known.
dbsrv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(srv.Url) dbsrv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(srv.Url)
if err != nil { if err != nil {
return nil, "InvitationGetMessage: LoadServer", err return nil, "InvitationStep2GetMessage: LoadServer", err
} }
if dbsrv == nil { if dbsrv == nil {
// create a server object with url & pubkey
srv.PublicKey = serverPublicKey srv.PublicKey = serverPublicKey
k, err := meowlib.NewKeyPair() k, err := meowlib.NewKeyPair()
if err != nil { if err != nil {
return nil, "InvitationGetMessage: NewKeyPair", err return nil, "InvitationStep2GetMessage: NewKeyPair", err
} }
srv.UserKp = k srv.UserKp = k
// save it if err := client.GetConfig().GetIdentity().MessageServers.StoreServer(srv); err != nil {
err = client.GetConfig().GetIdentity().MessageServers.StoreServer(srv) return nil, "InvitationStep2GetMessage: StoreServer", err
if err != nil {
return nil, "InvitationGetMessage: StoreServer", err
} }
} else { } else {
if dbsrv.PublicKey != serverPublicKey { if dbsrv.PublicKey != serverPublicKey {
@@ -92,42 +39,32 @@ func InvitationGetMessage(invitationUrl string, serverPublicKey string, invitati
} }
srv = dbsrv srv = dbsrv
} }
// buildserver message
toSrvMsg, err := srv.BuildToServerMessageInvitationRequest(shortcode, invitationPassword) toSrvMsg, err := srv.BuildToServerMessageInvitationRequest(shortcode, invitationPassword)
if err != nil { if err != nil {
return nil, "InvitationGetMessage: BuildToServerMessageInvitationRequest", err return nil, "InvitationStep2GetMessage: BuildToServerMessageInvitationRequest", err
} }
// processoutbound
bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg) bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg)
if err != nil { if err != nil {
return nil, "InvitationGetMessage: ProcessOutboundMessage", err return nil, "InvitationStep2GetMessage: ProcessOutboundMessage", err
} }
return bytemsg, "", nil return bytemsg, "", nil
} }
// InvitationGetMessageReadResponse // InvitationStep2ReadResponse decodes the server response to a Step2 get-message and returns
// Called by the invitation receiver // the InvitationInitPayload sent by the initiator.
// invitationData: the data received from the server func InvitationStep2ReadResponse(invitationData []byte, invitationServerUid string) (*meowlib.InvitationInitPayload, string, error) {
// 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) server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
if err != nil { if err != nil {
return nil, "InvitationGetMessageReadResponse: LoadServer", err return nil, "InvitationStep2ReadResponse: LoadServer", err
} }
// Server inbound processing : get the invitation server
serverMsg, err := server.ProcessInboundServerResponse(invitationData) serverMsg, err := server.ProcessInboundServerResponse(invitationData)
if err != nil { if err != nil {
return nil, "InvitationGetMessageReadResponse: ProcessInboundServerResponse", err return nil, "InvitationStep2ReadResponse: ProcessInboundServerResponse", err
} }
// fmt.Println("Inbound OK, Invitation Step: ", serverMsg.Invitation.Step, len(serverMsg.Invitation.Payload)) payload, err := meowlib.NewInvitationInitPayloadFromCompressed(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 { if err != nil {
return nil, "InvitationGetMessageReadResponse: NewContactCardFromCompressed", err return nil, "InvitationStep2ReadResponse: NewInvitationInitPayloadFromCompressed", err
} }
return cc, "", nil return payload, "", nil
} }

View File

@@ -8,136 +8,95 @@ import (
"forge.redroom.link/yves/meowlib/client" "forge.redroom.link/yves/meowlib/client"
) )
// InvitationCreatePeer creates a new peer and returns it // InvitationStep1CreatePeer creates a minimal pending peer and returns the InvitationInitPayload
// Called by invitation initiator // to be transmitted to the invitee (STEP_1).
// name: the name of the peer func InvitationStep1CreatePeer(contactName string, myNickname string, invitationMessage string, serverUids []string) (*meowlib.InvitationInitPayload, *client.Peer, string, error) {
// 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 mynick := myNickname
if myNickname == "" { if myNickname == "" {
mynick = client.GetConfig().GetIdentity().Nickname mynick = client.GetConfig().GetIdentity().Nickname
} }
payload, peer, err := client.GetConfig().GetIdentity().InvitationStep1(mynick, contactName, serverUids, invitationMessage)
// build my contact card for that friend
peer, err := client.GetConfig().GetIdentity().InvitePeer(mynick, name, serverUids, invitationMessage)
if err != nil { if err != nil {
return nil, "InvitationCreate: InvitePeer", err return nil, nil, "InvitationStep1CreatePeer: InvitationStep1", err
} }
client.GetConfig().GetIdentity().Save() client.GetConfig().GetIdentity().Save()
return payload, peer, "", nil
return peer, "", nil
} }
// InvitationCreateFile creates a new peer and writes the invitation to a file // InvitationStep1File creates a pending peer and writes the InvitationInitPayload to a file
// Called by invitation initiator // (format: "qr" for QR-code PNG, anything else for compressed binary .mwiv).
// name: the name of the peer func InvitationStep1File(contactName string, myNickname string, invitationMessage string, serverUids []string, format string) (*client.Peer, string, error) {
// myNickname: my nickname for that peer payload, peer, errdata, err := InvitationStep1CreatePeer(contactName, myNickname, invitationMessage, serverUids)
// 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 { if err != nil {
return nil, errdata, err return nil, errdata, err
} }
c := client.GetConfig() c := client.GetConfig()
var filename string = ""
if format == "qr" { if format == "qr" {
filename = c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".png" filename := c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".png"
err := peer.GetMyContact().WriteQr(filename) if err := payload.WriteQr(filename); err != nil {
if err != nil { return nil, "InvitationStep1File: WriteQr", err
return nil, "InvitationCreateFile: WriteQr", err
} }
} else { } else {
filename = c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".mwiv" filename := c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".mwiv"
err := peer.GetMyContact().WriteCompressed(filename) if err := payload.WriteCompressed(filename); err != nil {
if err != nil { return nil, "InvitationStep1File: WriteCompressed", err
return nil, "InvitationCreateFile: WriteCompressed", err
} }
} }
return peer, "", nil return peer, "", nil
} }
// InvitationCreateMessage creates a new invitation message for an invited peer // InvitationStep1Message builds and returns the packed server message that posts the
// Called by invitation initiator // InvitationInitPayload to the invitation server (STEP_1 through-server variant).
// invitationId: the invitation id of the peer func InvitationStep1Message(invitationId string, invitationServerUid string, timeOut int, urlLen int, password string) ([]byte, string, error) {
// 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
}
}*/
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitationId) peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitationId)
myContact = peer.GetMyContact() if peer == nil {
// todo handle not found !! return nil, "InvitationStep1Message: peer not found", nil
// 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 if peer.InvitationKp == nil {
msg, err := invitationServer.BuildToServerMessageInvitationCreation(myContact, password, timeOut, urlLen) return nil, "InvitationStep1Message: peer has no InvitationKp", nil
if err != nil { }
return nil, "InvitationCreateMessage: BuildToServerMessageInvitationCreation", err initPayload := &meowlib.InvitationInitPayload{
Uuid: peer.InvitationId,
Name: peer.MyName,
PublicKey: peer.InvitationKp.Public,
InvitationMessage: peer.InvitationMessage,
}
invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
if err != nil {
return nil, "InvitationStep1Message: LoadServer", err
}
msg, err := invitationServer.BuildToServerMessageInvitationStep1(initPayload, password, timeOut, urlLen)
if err != nil {
return nil, "InvitationStep1Message: BuildToServerMessageInvitationStep1", err
} }
// fmt.Println("Invitation Create")
// fmt.Println(hex.EncodeToString(msg.Invitation.Payload))
bytemsg, err := invitationServer.ProcessOutboundMessage(msg) bytemsg, err := invitationServer.ProcessOutboundMessage(msg)
if err != nil { if err != nil {
return nil, "InvitationCreateMessage: ProcessOutboundMessage", err return nil, "InvitationStep1Message: ProcessOutboundMessage", err
} }
return bytemsg, "", nil return bytemsg, "", nil
} }
// InvitationCreateReadResponse reads the response of an invitation creation (url, expiry) // InvitationStep1ReadResponse reads the server response to a Step1 message (shortcode URL + expiry).
// Called by invitation initiator func InvitationStep1ReadResponse(invitationServerUid string, invitationResponse []byte) (*meowlib.Invitation, string, error) {
// 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) server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
if err != nil { if err != nil {
return nil, "InvitationCreateReadResponse: LoadServer", err return nil, "InvitationStep1ReadResponse: LoadServer", err
} }
serverMsg, err := server.ProcessInboundServerResponse(invitationResponse) serverMsg, err := server.ProcessInboundServerResponse(invitationResponse)
if err != nil { if err != nil {
return nil, "InvitationCreateReadResponse: ProcessInboundServerResponse", err return nil, "InvitationStep1ReadResponse: ProcessInboundServerResponse", err
} }
return serverMsg.Invitation, "", nil return serverMsg.Invitation, "", nil
} }
// InvitationSetUrlInfo sets the url info for an invitation // InvitationSetUrlInfo stores the shortcode URL and expiry on the pending peer.
// 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) { func InvitationSetUrlInfo(invitationId string, url string, expiry int64) {
id := client.GetConfig().GetIdentity() id := client.GetConfig().GetIdentity()
// lookup for peer with "invitation_id"
peer := id.Peers.GetFromInvitationId(invitationId) peer := id.Peers.GetFromInvitationId(invitationId)
if peer == nil {
return
}
peer.InvitationUrl = url peer.InvitationUrl = url
peer.InvitationExpiry = time.Unix(expiry, 0) peer.InvitationExpiry = time.Unix(expiry, 0)
id.Peers.StorePeer(peer) id.Peers.StorePeer(peer)
/* 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()*/
} }

View File

@@ -1,53 +1,150 @@
package helpers package helpers
import ( import (
"errors"
"forge.redroom.link/yves/meowlib" "forge.redroom.link/yves/meowlib"
"forge.redroom.link/yves/meowlib/client" "forge.redroom.link/yves/meowlib/client"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
// Got it by the message background check // InvitationStep3ProcessAnswer is called by the initiator's background service when a
// => noInvitationGetAnswer // step-2 answer (invitee's ContactCard) arrives via the invitation server poll.
// It decrypts the answer, calls InvitationStep3 to generate the initiator's full keypairs,
// invitationGetAnswerReadResponse // and returns the peer and the initiator's ContactCard ready for STEP_3_SEND.
// Called by the initiator's background service only func InvitationStep3ProcessAnswer(invitation *meowlib.Invitation) (*client.Peer, *meowlib.ContactCard, string, error) {
// 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 var invitationAnswer meowlib.PackedUserMessage
err := proto.Unmarshal(invitation.Payload, &invitationAnswer) if err := proto.Unmarshal(invitation.Payload, &invitationAnswer); err != nil {
if err != nil { return nil, nil, "InvitationStep3ProcessAnswer: Unmarshal PackedUserMessage", err
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 := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitation.Uuid)
peer.ContactPublicKey = invitation.From if peer == nil {
if peer != nil { return nil, nil, "InvitationStep3ProcessAnswer: peer not found", errors.New("no peer for invitation uuid " + invitation.Uuid)
// process the packed user message
usermsg, err := peer.ProcessInboundUserMessage(&invitationAnswer)
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()
} }
// Guard against duplicate delivery (e.g., same answer from multiple servers).
if peer.InvitationKp == nil {
return nil, nil, "", nil
}
// Decrypt invitee's ContactCard using the initiator's temporary InvitationKp.
usermsg, err := peer.ProcessInboundStep2UserMessage(&invitationAnswer, invitation.From)
if err != nil {
return nil, nil, "InvitationStep3ProcessAnswer: ProcessInboundStep2UserMessage", err
}
var inviteeCC meowlib.ContactCard
if err := proto.Unmarshal(usermsg.Invitation.Payload, &inviteeCC); err != nil {
return nil, nil, "InvitationStep3ProcessAnswer: Unmarshal ContactCard", err
}
myCC, peer, err := client.GetConfig().GetIdentity().InvitationStep3(&inviteeCC)
if err != nil {
return nil, nil, "InvitationStep3ProcessAnswer: InvitationStep3", err
}
client.GetConfig().GetIdentity().Save()
return peer, myCC, "", nil
}
// InvitationStep3Message builds and returns the packed server messages that send the
// initiator's full ContactCard to the invitee through the invitee's servers (STEP_3_SEND).
func InvitationStep3Message(invitationId string) ([][]byte, string, error) {
id := client.GetConfig().GetIdentity()
peer := id.Peers.GetFromInvitationId(invitationId)
if peer == nil {
return nil, "InvitationStep3Message: peer not found", errors.New("no peer for invitation id " + invitationId)
}
step3msg, err := peer.BuildInvitationStep3Message(peer.GetMyContact())
if err != nil {
return nil, "InvitationStep3Message: BuildInvitationStep3Message", err
}
// Step-3 must NOT use DR or sym layers: the invitee hasn't received those
// keys yet (they are carried inside this very message). Use plain asym only.
serialized, err := peer.SerializeUserMessage(step3msg)
if err != nil {
return nil, "InvitationStep3Message: SerializeUserMessage", err
}
enc, err := peer.AsymEncryptMessage(serialized)
if err != nil {
return nil, "InvitationStep3Message: AsymEncryptMessage", err
}
packedMsg := peer.PackUserMessage(enc.Data, enc.Signature)
var results [][]byte
for _, srvUid := range peer.ContactPullServers {
srv, err := id.MessageServers.LoadServer(srvUid)
if err != nil {
continue
}
toSrvMsg := srv.BuildToServerMessageFromUserMessage(packedMsg)
bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg)
if err != nil {
continue
}
results = append(results, bytemsg)
}
if len(results) == 0 {
return nil, "InvitationStep3Message: no reachable invitee server", errors.New("could not build message for any invitee server")
}
return results, "", nil
}
// InvitationStep4ProcessStep3 is called by the invitee's message processing when a UserMessage
// with invitation.step==3 is received. It finalizes the initiator's peer entry.
func InvitationStep4ProcessStep3(usermsg *meowlib.UserMessage) (*client.Peer, string, error) {
if usermsg.Invitation == nil || usermsg.Invitation.Step != 3 {
return nil, "InvitationStep4ProcessStep3: unexpected step", errors.New("expected invitation step 3")
}
var initiatorCC meowlib.ContactCard
if err := proto.Unmarshal(usermsg.Invitation.Payload, &initiatorCC); err != nil {
return nil, "InvitationStep4ProcessStep3: Unmarshal ContactCard", err
}
// Patch the invitation ID from the outer message in case it was not set in the CC.
if initiatorCC.InvitationId == "" {
initiatorCC.InvitationId = usermsg.Invitation.Uuid
}
if err := client.GetConfig().GetIdentity().InvitationStep4(&initiatorCC); err != nil {
return nil, "InvitationStep4ProcessStep3: InvitationStep4", err
}
client.GetConfig().GetIdentity().Save()
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(initiatorCC.InvitationId)
return peer, "", nil return peer, "", nil
} }
// InvitationStep4Message builds and returns the packed server messages that send the
// invitee's confirmation to the initiator through the initiator's servers (STEP_4).
func InvitationStep4Message(invitationId string) ([][]byte, string, error) {
id := client.GetConfig().GetIdentity()
peer := id.Peers.GetFromInvitationId(invitationId)
if peer == nil {
return nil, "InvitationStep4Message: peer not found", errors.New("no peer for invitation id " + invitationId)
}
step4msg, err := peer.BuildInvitationStep4Message()
if err != nil {
return nil, "InvitationStep4Message: BuildInvitationStep4Message", err
}
packedMsg, err := peer.ProcessOutboundUserMessage(step4msg)
if err != nil {
return nil, "InvitationStep4Message: ProcessOutboundUserMessage", err
}
var results [][]byte
for _, srvUid := range peer.ContactPullServers {
srv, err := id.MessageServers.LoadServer(srvUid)
if err != nil {
continue
}
toSrvMsg := srv.BuildToServerMessageFromUserMessage(packedMsg)
bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg)
if err != nil {
continue
}
results = append(results, bytemsg)
}
if len(results) == 0 {
return nil, "InvitationStep4Message: no reachable initiator server", errors.New("could not build message for any initiator server")
}
return results, "", nil
}

View File

@@ -80,89 +80,44 @@ func (id *Identity) WipeFolder() error {
return nil return nil
} }
// Creates an invitation for a peer, returns the newly created peer including infos to provide a ContactCard // InvitationStep1 creates a minimal pending peer with only a temporary keypair and returns
func (id *Identity) InvitePeer(MyName string, ContactName string, MessageServerUids []string, InvitationMessage string) (*Peer, error) { // the InvitationInitPayload to be transmitted to the invitee (via file, QR code, or server).
// Full keypairs are only generated in InvitationStep3, after the invitee's answer is received.
func (id *Identity) InvitationStep1(MyName string, ContactName string, MessageServerUids []string, InvitationMessage string) (*meowlib.InvitationInitPayload, *Peer, error) {
var peer Peer var peer Peer
var err error var err error
peer.Uid = uuid.New().String() peer.Uid = uuid.New().String()
peer.MyIdentity, err = meowlib.NewKeyPair()
if err != nil {
return nil, err
}
peer.MyEncryptionKp, err = meowlib.NewKeyPair()
if err != nil {
return nil, err
}
peer.MyLookupKp, err = meowlib.NewKeyPair()
if err != nil {
return nil, err
}
peer.Name = ContactName peer.Name = ContactName
peer.InvitationId = uuid.New().String() // todo as param to identify then update url
symKeyBytes := make([]byte, 32)
if _, err = rand.Read(symKeyBytes); err != nil {
return nil, err
}
peer.MySymKey = base64.StdEncoding.EncodeToString(symKeyBytes)
/* if id.MessageServers.Servers == nil {
return nil, errors.New("no message servers defined in your identity")
}
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.MyName = MyName
peer.InvitationId = uuid.New().String()
peer.InvitationMessage = InvitationMessage peer.InvitationMessage = InvitationMessage
peer.MyPullServers = MessageServerUids
// Generate DR keypair and root key for the initiator side // Temporary keypair: public key is sent to invitee for step-2 encryption and as
drKp, err := doubleratchet.DefaultCrypto{}.GenerateDH() // the server-side lookup key where the invitee will post their answer.
peer.InvitationKp, err = meowlib.NewKeyPair()
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
peer.DrKpPrivate = base64.StdEncoding.EncodeToString(drKp.PrivateKey())
peer.DrKpPublic = base64.StdEncoding.EncodeToString(drKp.PublicKey())
drRootKey := make([]byte, 32)
if _, err = rand.Read(drRootKey); err != nil {
return nil, err
}
peer.DrRootKey = base64.StdEncoding.EncodeToString(drRootKey)
peer.DrInitiator = true
id.Peers.StorePeer(&peer) id.Peers.StorePeer(&peer)
return &peer, nil payload := &meowlib.InvitationInitPayload{
} Uuid: peer.InvitationId,
Name: MyName,
// Checks if the received contact card is an answer to an invitation, returns true if it is, and the proposed and received nicknames PublicKey: peer.InvitationKp.Public,
func (id *Identity) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string, invitationMessage string) { InvitationMessage: InvitationMessage,
// 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, ReceivedContact.InvitationMessage
}
} }
return payload, &peer, nil
// it's an invitation
return false, "", ReceivedContact.Name, ReceivedContact.InvitationMessage*/
return id.Peers.CheckInvitation(ReceivedContact)
} }
// Answers an invitation, returns the newly created peer including infos to provide a ContactCard // InvitationStep2 creates the invitee's peer entry from the received InvitationInitPayload
func (id *Identity) AnswerInvitation(MyName string, ContactName string, MessageServerIdxs []string, ReceivedContact *meowlib.ContactCard) (*Peer, error) { // and returns the peer. The invitee generates their full keypairs here.
// The initiator's temporary public key (payload.PublicKey) is used both as the encryption
// target for the step-2 answer and as the server-side lookup address.
func (id *Identity) InvitationStep2(MyName string, ContactName string, MessageServerUids []string, payload *meowlib.InvitationInitPayload) (*Peer, error) {
var peer Peer var peer Peer
var err error var err error
var newsrv *Server
//var myContactCard meowlib.ContactCard
peer.Uid = uuid.New().String() peer.Uid = uuid.New().String()
peer.MyIdentity, err = meowlib.NewKeyPair() peer.MyIdentity, err = meowlib.NewKeyPair()
if err != nil { if err != nil {
@@ -179,66 +134,116 @@ func (id *Identity) AnswerInvitation(MyName string, ContactName string, MessageS
if ContactName != "" { if ContactName != "" {
peer.Name = ContactName peer.Name = ContactName
} else { } else {
peer.Name = ReceivedContact.Name peer.Name = payload.Name
} }
peer.ContactEncryption = ReceivedContact.EncryptionPublicKey // The initiator's temp key is used for both encrypting the answer and as destination.
peer.ContactLookupKey = ReceivedContact.LookupPublicKey peer.ContactEncryption = payload.PublicKey
peer.ContactPublicKey = ReceivedContact.ContactPublicKey peer.ContactLookupKey = payload.PublicKey
peer.MySymKey = ReceivedContact.SymetricKey peer.InvitationId = payload.Uuid
peer.InvitationId = ReceivedContact.InvitationId peer.InvitationMessage = payload.InvitationMessage
peer.InvitationMessage = ReceivedContact.InvitationMessage peer.MyPullServers = MessageServerUids
for srv := range ReceivedContact.PullServers {
peer.ContactPullServers = append(peer.ContactPullServers, ReceivedContact.PullServers[srv].GetUid())
newsrv, err = CreateServerFromUid(ReceivedContact.PullServers[srv].GetUid())
id.MessageServers.StoreServerIfNotExists(newsrv)
}
/* 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.MyName = MyName
peer.InvitationId = ReceivedContact.InvitationId
// Adopt DR material from the initiator's ContactCard
peer.DrRootKey = ReceivedContact.DrRootKey
peer.ContactDrPublicKey = ReceivedContact.DrPublicKey
peer.DrInitiator = false
id.Peers.StorePeer(&peer) id.Peers.StorePeer(&peer)
return &peer, nil return &peer, nil
} }
// Finalizes an invitation, returns nil if successful // InvitationStep3 is called by the initiator after receiving and decrypting the invitee's
func (id *Identity) FinalizeInvitation(ReceivedContact *meowlib.ContactCard) error { // ContactCard (step-2 answer). It generates the initiator's full keypairs and DR material,
// updates the pending peer with the invitee's contact info, and returns the initiator's
// full ContactCard to be sent to the invitee (STEP_3_SEND).
func (id *Identity) InvitationStep3(inviteeContact *meowlib.ContactCard) (*meowlib.ContactCard, *Peer, error) {
var err error var err error
var newsrv *Server peer := id.Peers.GetFromInvitationId(inviteeContact.InvitationId)
/*for i, p := range id.Peers { if peer == nil {
if p.InvitationId == ReceivedContact.InvitationId { return nil, nil, errors.New("no pending peer found for invitation id " + inviteeContact.InvitationId)
//id.Peers[i].Name = ReceivedContact.Name }
id.Peers[i].ContactEncryption = ReceivedContact.EncryptionPublicKey
id.Peers[i].ContactLookupKey = ReceivedContact.LookupPublicKey // Generate full keypairs now that the invitee's identity is known.
id.Peers[i].ContactPublicKey = ReceivedContact.ContactPublicKey peer.MyIdentity, err = meowlib.NewKeyPair()
srvs := []string{} if err != nil {
for srv := range ReceivedContact.PullServers { return nil, nil, err
srvs = append(srvs, ReceivedContact.PullServers[srv].GetUid()) }
} peer.MyEncryptionKp, err = meowlib.NewKeyPair()
id.Peers[i].ContactPullServers = srvs if err != nil {
return nil return nil, nil, err
}
peer.MyLookupKp, err = meowlib.NewKeyPair()
if err != nil {
return nil, nil, err
}
symKeyBytes := make([]byte, 32)
if _, err = rand.Read(symKeyBytes); err != nil {
return nil, nil, err
}
peer.MySymKey = base64.StdEncoding.EncodeToString(symKeyBytes)
// Generate DR keypair and root key.
drKp, err := doubleratchet.DefaultCrypto{}.GenerateDH()
if err != nil {
return nil, nil, err
}
peer.DrKpPrivate = base64.StdEncoding.EncodeToString(drKp.PrivateKey())
peer.DrKpPublic = base64.StdEncoding.EncodeToString(drKp.PublicKey())
drRootKey := make([]byte, 32)
if _, err = rand.Read(drRootKey); err != nil {
return nil, nil, err
}
peer.DrRootKey = base64.StdEncoding.EncodeToString(drRootKey)
peer.DrInitiator = true
// Store invitee contact info.
peer.ContactPublicKey = inviteeContact.ContactPublicKey
peer.ContactEncryption = inviteeContact.EncryptionPublicKey
peer.ContactLookupKey = inviteeContact.LookupPublicKey
for _, srv := range inviteeContact.PullServers {
peer.ContactPullServers = append(peer.ContactPullServers, srv.GetUid())
newsrv, err := CreateServerFromUid(srv.GetUid())
if err == nil {
id.MessageServers.StoreServerIfNotExists(newsrv)
} }
} }
return errors.New("no matching contact found for invitationId " + ReceivedContact.InvitationId)*/ // Drop the temporary invitation keypair — no longer needed.
for srv := range ReceivedContact.PullServers { peer.InvitationKp = nil
newsrv, err = CreateServerFromUid(ReceivedContact.PullServers[srv].GetUid())
id.Peers.StorePeer(peer)
return peer.GetMyContact(), peer, nil
}
// InvitationStep4 is called by the invitee upon receiving the initiator's full ContactCard
// (carried as a regular UserMessage with invitation.step=3). It finalizes the peer entry.
func (id *Identity) InvitationStep4(initiatorContact *meowlib.ContactCard) error {
var err error
var newsrv *Server
for _, srv := range initiatorContact.PullServers {
newsrv, err = CreateServerFromUid(srv.GetUid())
if err != nil { if err != nil {
return err return err
} }
id.MessageServers.StoreServerIfNotExists(newsrv) id.MessageServers.StoreServerIfNotExists(newsrv)
} }
return id.Peers.FinalizeInvitation(ReceivedContact) peer := id.Peers.GetFromInvitationId(initiatorContact.InvitationId)
if peer == nil {
return errors.New("no pending peer found for invitation id " + initiatorContact.InvitationId)
}
peer.ContactPublicKey = initiatorContact.ContactPublicKey
peer.ContactEncryption = initiatorContact.EncryptionPublicKey
peer.ContactLookupKey = initiatorContact.LookupPublicKey
peer.MySymKey = initiatorContact.SymetricKey
peer.DrRootKey = initiatorContact.DrRootKey
peer.ContactDrPublicKey = initiatorContact.DrPublicKey
peer.DrInitiator = false
for _, srv := range initiatorContact.PullServers {
peer.ContactPullServers = append(peer.ContactPullServers, srv.GetUid())
}
id.Peers.StorePeer(peer)
return nil
}
// CheckInvitation checks if the received ContactCard is an answer to one of our pending
// invitations. Returns true when it is, with the proposed and received nicknames.
func (id *Identity) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string, invitationMessage string) {
return id.Peers.CheckInvitation(ReceivedContact)
} }
// LoadIdentity loads an identity from an encrypted file // LoadIdentity loads an identity from an encrypted file
@@ -386,9 +391,18 @@ func (id *Identity) GetRequestJobs() []RequestsJob {
return nil return nil
} }
for _, peer := range peers { for _, peer := range peers {
// check if peer inviation is accepted
for _, server := range peer.MyPullServers { for _, server := range peer.MyPullServers {
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.MyLookupKp) if srvs[server] == nil {
continue
}
if peer.MyLookupKp != nil {
// Active peer — use the permanent lookup key.
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.MyLookupKp)
} else if peer.InvitationKp != nil {
// Step-1 pending peer — poll using the temp invitation keypair so the
// server-stored step-2 answer can be retrieved.
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.InvitationKp)
}
} }
} }
// add hidden peers // add hidden peers

View File

@@ -142,14 +142,12 @@ func TestGetRequestJobs(t *testing.T) {
// Call GetRequestJobs // Call GetRequestJobs
jobs := id.GetRequestJobs() jobs := id.GetRequestJobs()
// Check that the returned list is as expected // All 10 test peers use server1 and server2, so exactly 2 jobs are expected.
assert.Equal(t, 6, len(jobs), "Expected 6 jobs") assert.Equal(t, 2, len(jobs), "Expected 2 jobs (server1 and server2)")
// Check that each job has the correct server and lookup keys
for _, job := range jobs { for _, job := range jobs {
//fmt.Println(job.Server.GetUid(), job.LookupKeys) assert.Contains(t, []string{"server1", "server2"}, job.Server.GetUid(), "Unexpected server UID")
assert.Contains(t, []string{"server1", "server2", "server3", "server4", "server5", "server6"}, job.Server.GetUid(), "Unexpected server UID") assert.Len(t, job.LookupKeys, 10, "Expected 10 lookup keys (one per test peer) per job")
assert.Len(t, job.LookupKeys, 1, "Expected 1 lookup key per job")
} }
// Clean up // Clean up

44
client/inv_test_init.id Normal file
View File

@@ -0,0 +1,44 @@
-----BEGIN PGP MESSAGE-----
Comment: https://gopenpgp.org
Version: GopenPGP 2.8.3
wy4ECQMIgUuEGbIAQdTg1Y0LVbCcIFEHJ3MkTGXl7hjJ6KuaEkdm83kI3ID/mesB
0uoB/RojNQvrAnW+1W4xFutE/1S0gG9ejWYhCWiI7sxDmLoNnB1H3Rld2N7dEYnf
sD4baoJC3dOhfbjCUqwtA1aMEmsvJI0VsxEWAj6Uq16iTNmL7HcIaH8aDL7EA8UZ
RTC0bQGdvkf+azASRM6uB29Cm7aIviVyt5MfF/BDoauefibHrP4Z0sYH5P0KJC2i
AqnObuyiqeYNp9yUzVtZywSjjt2C72DkuQIwgPf0FNE3zduxOZ2Ds80tS2Zyobxx
6e+9KUaadUEkcdv/AOOqvQOtRYSVlF5o6gWRF+A16NuwalWAnHJ41k9Y3SSIQLiz
Ppbkw77hrHYIXqopCyxnls2FJaO4QDDjd4JGEdejpxIKognZlgJIIK03khFjUc8/
ilM3Hgbjs6dudJ76lHT8BKaiJPfJPNPL1wf45kLhFc383OdWGJ30NB/w6TbeQKvw
fNNyI/ksfsGbssFm6Zlc0xCpnkEjW9Q9aeHqn34n2jLiDyugwigYhYFKMD8gsQVw
0CRcde7A13/FTa83X9sZ1/rm05FN9M24bIhvG3+8YE4B6nIX43LvYkq18tpGbRLD
uZ33c3bHjbE4PvSf0AdXaML0vGZzxMhBHpgSvPMKt1YiBVr9Kx05txuEAAQ8xaax
KLhhTzVUF7jo4qVeMzvgne6As02yQBdMRYSk92uKm49IWSRzaprP8bx+HktaXJCy
tG/98FXa+05BlTceL4BPaNWrYJlYi4Vpcd3jBm6DAT30gTprJPizUVcGfTkBXII9
sHXLYvca72ItcCzIozOJIdB+y4pV/ZWH8DQdAeZEOfaNUpYbNs9DufxuOhbgx5xQ
JvCKBHAz6fo5O/vkJ1AatihNQ8I8R+7iJ3q4xXxKuDhv+9+V2KG1kG6L1RLKfzpy
GZ6pnmEKbLSa0SO048g6LBhDJyk9I955LHps3HIGoFtE9Oq/2T3fBuZjJgQW0kKj
9ddK3sDOo0/U0Ojz5tfPTkIZvYiEmDoJdfj/jBtTc4F16pf9r4chhzKnkxw9JzfR
Ntj9KThmWOmKHNNlHlwSerxBfNmRjKjfrJ4l1nJPQRDbynTPLzCR59uKVFj5e2t4
F6pGVBrwARQ/kX0QqyqOB6UaE2ulV2EYwnNljegOd1NoDf5kr59K5IBZNx2PvEZe
dM+7jPIojk7pbM6sCCneVXvMG5nzG82boevlc8HJnGEP/9dJ9uWHHu+LFXf71EIQ
npcVOrw8JXTLYhiI9ssH0Tr0C2otkAMkr3DNXcfC5BxLQ+0Ayw0Wr+MNnUbP40Dq
vLhI5YjFdFF/X0QUeVQ9srGk/JWTTPOR1liIGYbzouGQjzzmJOBLtEPoGAdjXbhg
QXZDkpWMTh6qwbWroyQw06Ywwiex0NkTZ+I2UDdby7Dk1V33KmL6EKYm07I3eorn
QRyL/Qs8DpYlwjw1yvbsbj2EIF9UakNLUfFg+VAd6gsgSG2500e6+5Eyjvs8Htpa
wdxqyKgjURK7BkDYSdC6z/eNU7AhkdhYEo0PIOf0loXu2boKKtau7oSWfrJKep9Y
qlpKOzvgxGUx3dRNGmJKAOOLhyHVjBfl5dalzVMikpt3AXhy+an4ogiY6AZgg+gH
bSOJ73h5V/w0xCtD/Lrc4vSDlx1+93B/4m1wXItkBXSi1C2ivjDcPY2d5gd4EfCE
JaHak6zI+P//9zoXJLycJnl/tw0Guw5oJBrhn9ReINNV/CO1pur1H19zBEwuV9c6
u+vx9gcwN6EJEh5nDIOXXU/NoNsMpXERwzohob1plWpYUgB7cLyW4sNsHSSdWrOH
ipAatW+uyPJXQd0YuMm6FLB/DfkNl1BAI3QhmAyGLBxma4KesxcjDImuiGNFvWvZ
M7D3vz4ziOzauanZ/HNDYRa/ey9XJ0iLyLIDsZ0ZrK0T1E2z7PdY4y5JWUGu3a2c
C71RBuTfAmXIAGn/jaF9jfx7dezW91VO0PZ9fKcU7x5khA4Z9gK3oCD2RhXOkIje
bgtYGyWnaz0qcV1JUmRSo1Zwb84NVr5jCc5n743D7+fjedGMZtLQAGCUFttgO/9u
KZbI3UUVcTREZvUKEAyWN/EhixL3Uf7Uv4M12v3RRTydxFPhUUNPbX0+kL9flTaF
Fph4UBuGguu5VygBq0p3YUVYdlS9L8U5WD9DGL4tKW+WJb02jAnsRyWQQcc7PDFw
u1jGIDbaCu/JQco95wpDx0rUGtC1NOVIJFSqPcNf+NHRQaLNks6zzUa67qbJgS5p
nvrfSEVBd7AoSGP1gAuL0qzDHR0x06Fxe9uREHg1R7eojRyAHHs6ZEuK6CmzbTrr
Ky8vdxcfOBwfzJF/J2VHY8lkIfNULqjQMIYpJcD7bMeH12Q0Y0BV11LsYA==
=C05v
-----END PGP MESSAGE-----

205
client/invitation_test.go Normal file
View File

@@ -0,0 +1,205 @@
package client
import (
"os"
"testing"
"forge.redroom.link/yves/meowlib"
"github.com/stretchr/testify/assert"
)
// setupInvitationTest creates two independent identities with separate storage paths.
func setupInvitationTest(t *testing.T) (initiator *Identity, invitee *Identity, cleanup func()) {
t.Helper()
cfg := GetConfig()
cfg.IdentityFile = "inv_test_init.id"
cfg.SetMemPass("testpass")
initiator, err := CreateIdentity("initiator")
if err != nil {
t.Fatal(err)
}
invitee, err = CreateIdentity("invitee")
if err != nil {
t.Fatal(err)
}
// Give each identity a pull server UID.
srvInit, _ := CreateServerFromUrl("http://init.server/meow/")
initiator.MessageServers.StoreServer(srvInit)
srvInvitee, _ := CreateServerFromUrl("http://invitee.server/meow/")
invitee.MessageServers.StoreServer(srvInvitee)
cleanup = func() {
os.Remove("inv_test_init.id")
os.RemoveAll(cfg.StoragePath + "/" + initiator.Uuid)
os.RemoveAll(cfg.StoragePath + "/" + invitee.Uuid)
}
return initiator, invitee, cleanup
}
// TestInvitationStep1 verifies that InvitationStep1 creates a minimal peer with only
// InvitationKp set (no full keypairs yet) and returns a valid InvitationInitPayload.
func TestInvitationStep1(t *testing.T) {
initiator, _, cleanup := setupInvitationTest(t)
defer cleanup()
payload, peer, err := initiator.InvitationStep1("Alice", "Bob", []string{"http://init.server/meow/"}, "Hello Bob!")
assert.NoError(t, err)
assert.NotNil(t, payload)
assert.NotNil(t, peer)
assert.NotEmpty(t, payload.Uuid)
assert.Equal(t, "Alice", payload.Name)
assert.NotEmpty(t, payload.PublicKey)
assert.Equal(t, "Hello Bob!", payload.InvitationMessage)
// Full keypairs must NOT be set yet.
assert.Nil(t, peer.MyIdentity)
assert.Nil(t, peer.MyEncryptionKp)
assert.Nil(t, peer.MyLookupKp)
// Temp keypair must be set.
assert.NotNil(t, peer.InvitationKp)
assert.Equal(t, payload.PublicKey, peer.InvitationKp.Public)
}
// TestInvitationStep1PayloadRoundTrip verifies Compress/Decompress of InvitationInitPayload.
func TestInvitationStep1PayloadRoundTrip(t *testing.T) {
initiator, _, cleanup := setupInvitationTest(t)
defer cleanup()
payload, _, err := initiator.InvitationStep1("Alice", "Bob", nil, "test msg")
assert.NoError(t, err)
compressed, err := payload.Compress()
assert.NoError(t, err)
assert.NotEmpty(t, compressed)
restored, err := meowlib.NewInvitationInitPayloadFromCompressed(compressed)
assert.NoError(t, err)
assert.Equal(t, payload.Uuid, restored.Uuid)
assert.Equal(t, payload.Name, restored.Name)
assert.Equal(t, payload.PublicKey, restored.PublicKey)
assert.Equal(t, payload.InvitationMessage, restored.InvitationMessage)
}
// TestInvitationStep2 verifies that InvitationStep2 creates a peer with full keypairs and
// sets the initiator's temp key as both ContactEncryption and ContactLookupKey.
func TestInvitationStep2(t *testing.T) {
initiator, invitee, cleanup := setupInvitationTest(t)
defer cleanup()
payload, _, err := initiator.InvitationStep1("Alice", "Bob", nil, "Hi")
assert.NoError(t, err)
peer, err := invitee.InvitationStep2("Bob", "Alice", []string{"http://invitee.server/meow/"}, payload)
assert.NoError(t, err)
assert.NotNil(t, peer)
// Full keypairs must be set on invitee's peer.
assert.NotNil(t, peer.MyIdentity)
assert.NotNil(t, peer.MyEncryptionKp)
assert.NotNil(t, peer.MyLookupKp)
// Contact fields must point to initiator's temp key.
assert.Equal(t, payload.PublicKey, peer.ContactEncryption)
assert.Equal(t, payload.PublicKey, peer.ContactLookupKey)
assert.Equal(t, payload.Uuid, peer.InvitationId)
}
// TestInvitationFullFlow exercises the complete 4-step invitation handshake end-to-end,
// verifying that both peers end up with each other's full contact information.
func TestInvitationFullFlow(t *testing.T) {
initiator, invitee, cleanup := setupInvitationTest(t)
defer cleanup()
// STEP_1: initiator creates init payload.
payload, initPeer, err := initiator.InvitationStep1("Alice", "Bob", []string{"http://init.server/meow/"}, "Hello!")
assert.NoError(t, err)
assert.NotNil(t, initPeer.InvitationKp)
assert.Nil(t, initPeer.MyIdentity)
// STEP_2: invitee creates their peer from the payload.
srvCard := &meowlib.ServerCard{Name: "InviteeServer", Url: "http://invitee.server/meow/"}
inviteePeer, err := invitee.InvitationStep2("Bob", "Alice", []string{"http://invitee.server/meow/"}, payload)
assert.NoError(t, err)
inviteeCC := inviteePeer.GetMyContact()
inviteeCC.PullServers = append(inviteeCC.PullServers, srvCard)
// STEP_3: initiator receives invitee's CC, generates full keypairs.
myCC, _, err := initiator.InvitationStep3(inviteeCC)
assert.NoError(t, err)
assert.NotNil(t, myCC)
assert.NotEmpty(t, myCC.ContactPublicKey)
assert.NotEmpty(t, myCC.EncryptionPublicKey)
assert.NotEmpty(t, myCC.LookupPublicKey)
assert.NotEmpty(t, myCC.DrRootKey)
assert.NotEmpty(t, myCC.DrPublicKey)
// After step 3, initiator's peer must have full keypairs and invitee's contact info.
updatedInitPeer := initiator.Peers.GetFromInvitationId(payload.Uuid)
assert.NotNil(t, updatedInitPeer.MyIdentity)
assert.NotNil(t, updatedInitPeer.MyEncryptionKp)
assert.NotNil(t, updatedInitPeer.MyLookupKp)
assert.Equal(t, inviteePeer.MyIdentity.Public, updatedInitPeer.ContactPublicKey)
assert.Equal(t, inviteePeer.MyEncryptionKp.Public, updatedInitPeer.ContactEncryption)
assert.Nil(t, updatedInitPeer.InvitationKp) // temp key must be cleared
// STEP_4: invitee finalizes from initiator's full CC.
srvCardInit := &meowlib.ServerCard{Name: "InitServer", Url: "http://init.server/meow/"}
myCC.PullServers = append(myCC.PullServers, srvCardInit)
err = invitee.InvitationStep4(myCC)
assert.NoError(t, err)
// Both peers must now be fully finalized (ContactPublicKey set → not pending).
finalInitPeer := initiator.Peers.GetFromInvitationId(payload.Uuid)
assert.False(t, finalInitPeer.InvitationPending())
finalInviteePeer := invitee.Peers.GetFromInvitationId(payload.Uuid)
assert.False(t, finalInviteePeer.InvitationPending())
assert.Equal(t, updatedInitPeer.MyIdentity.Public, finalInviteePeer.ContactPublicKey)
assert.Equal(t, updatedInitPeer.MyEncryptionKp.Public, finalInviteePeer.ContactEncryption)
assert.Equal(t, updatedInitPeer.MyLookupKp.Public, finalInviteePeer.ContactLookupKey)
assert.NotEmpty(t, finalInviteePeer.DrRootKey)
}
// TestInvitationStep3NotFound verifies that InvitationStep3 returns an error when no
// pending peer exists for the given invitation ID.
func TestInvitationStep3NotFound(t *testing.T) {
initiator, _, cleanup := setupInvitationTest(t)
defer cleanup()
cc := &meowlib.ContactCard{InvitationId: "nonexistent-uuid", ContactPublicKey: "pub"}
_, _, err := initiator.InvitationStep3(cc)
assert.Error(t, err)
}
// TestGetRequestJobsPendingPeer verifies that pending (step-1 only) peers contribute
// their InvitationKp to GetRequestJobs instead of MyLookupKp.
func TestGetRequestJobsPendingPeer(t *testing.T) {
cfg := GetConfig()
cfg.SetMemPass("testpass")
id, err := CreateIdentity("testjobs")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(cfg.StoragePath + "/" + id.Uuid)
cfg.SetIdentity(id)
id.MessageServers = ServerStorage{DbFile: "testjobs.db"}
defer os.RemoveAll("testjobs.db")
srv, _ := CreateServerFromUrl("http://srv1.test/meow/")
id.MessageServers.StoreServer(srv)
// Create a step-1 pending peer.
_, _, err = id.InvitationStep1("Me", "Friend", []string{"http://srv1.test/meow/"}, "Hi")
assert.NoError(t, err)
jobs := id.GetRequestJobs()
// At least one job should have a lookup key (the InvitationKp).
total := 0
for _, j := range jobs {
total += len(j.LookupKeys)
}
assert.Greater(t, total, 0)
}

View File

@@ -56,8 +56,10 @@ type Peer struct {
DbIds []string `json:"db_ids,omitempty"` DbIds []string `json:"db_ids,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
PersonnaeDbId string `json:"personnae_db_id,omitempty"` PersonnaeDbId string `json:"personnae_db_id,omitempty"`
// Invitation temporary keypair (step 1 only — discarded after step 3)
InvitationKp *meowlib.KeyPair `json:"invitation_kp,omitempty"`
// Double Ratchet state // Double Ratchet state
DrKpPublic string `json:"dr_kp_public,omitempty"` DrKpPublic string `json:"dr_kp_public,omitempty"`
DrKpPrivate string `json:"dr_kp_private,omitempty"` DrKpPrivate string `json:"dr_kp_private,omitempty"`
DrRootKey string `json:"dr_root_key,omitempty"` DrRootKey string `json:"dr_root_key,omitempty"`
DrInitiator bool `json:"dr_initiator,omitempty"` DrInitiator bool `json:"dr_initiator,omitempty"`
@@ -171,9 +173,28 @@ func (p *Peer) BuildSingleFileMessage(filename string, message []byte) ([]meowli
return msgs, nil return msgs, nil
} }
// Builds an invitation answer user message. // BuildInvitationStep2Message builds the invitee's answer UserMessage (STEP_2_SEND).
// it takes as input a contactcard generated by Identity.AnswerInvitation // The ContactCard is encrypted with the initiator's temp public key via ProcessOutboundUserMessage.
func (p *Peer) BuildInvitationAnswerMessage(myContactCard *meowlib.ContactCard) (*meowlib.UserMessage, error) { func (p *Peer) BuildInvitationStep2Message(myContactCard *meowlib.ContactCard) (*meowlib.UserMessage, error) {
var msg meowlib.UserMessage
var invitation meowlib.Invitation
invitation.Step = 2
out, err := proto.Marshal(myContactCard)
if err != nil {
return nil, err
}
invitation.Uuid = p.InvitationId
invitation.Payload = out
msg.Destination = p.ContactLookupKey
msg.Invitation = &invitation
msg.From = p.MyIdentity.Public
msg.Type = "1"
return &msg, nil
}
// BuildInvitationStep3Message builds the initiator's full ContactCard UserMessage (STEP_3_SEND).
// Sent through the invitee's servers after the initiator has finalized their keypairs.
func (p *Peer) BuildInvitationStep3Message(myContactCard *meowlib.ContactCard) (*meowlib.UserMessage, error) {
var msg meowlib.UserMessage var msg meowlib.UserMessage
var invitation meowlib.Invitation var invitation meowlib.Invitation
invitation.Step = 3 invitation.Step = 3
@@ -190,6 +211,42 @@ func (p *Peer) BuildInvitationAnswerMessage(myContactCard *meowlib.ContactCard)
return &msg, nil return &msg, nil
} }
// BuildInvitationStep4Message builds the invitee's confirmation UserMessage (STEP_4).
// Sent through the initiator's servers to signal the invitation is complete.
func (p *Peer) BuildInvitationStep4Message() (*meowlib.UserMessage, error) {
var msg meowlib.UserMessage
var invitation meowlib.Invitation
invitation.Step = 4
invitation.Uuid = p.InvitationId
msg.Destination = p.ContactLookupKey
msg.Invitation = &invitation
msg.From = p.MyIdentity.Public
msg.Type = "1"
return &msg, nil
}
// ProcessInboundStep2UserMessage decrypts the invitee's step-2 answer using the
// initiator's temporary InvitationKp private key. inviteePublicKey is the sender's
// identity public key (carried in Invitation.From by the server).
func (p *Peer) ProcessInboundStep2UserMessage(packed *meowlib.PackedUserMessage, inviteePublicKey string) (*meowlib.UserMessage, error) {
dec, err := meowlib.AsymDecryptAndCheck(p.InvitationKp.Private, inviteePublicKey, packed.Payload, packed.Signature)
if err != nil {
return nil, err
}
return p.DeserializeUserMessage(dec)
}
// ProcessInboundStep3UserMessage decrypts the initiator's step-3 full ContactCard using
// the invitee's MyEncryptionKp. Signature verification is skipped because the
// initiator's identity key is not yet known — it is extracted from the decrypted payload.
func (p *Peer) ProcessInboundStep3UserMessage(packed *meowlib.PackedUserMessage) (*meowlib.UserMessage, error) {
dec, err := meowlib.AsymDecrypt(p.MyEncryptionKp.Private, packed.Payload)
if err != nil {
return nil, err
}
return p.DeserializeUserMessage(dec)
}
// //
// Messages encryption and packaging // Messages encryption and packaging
// //

View File

@@ -228,7 +228,7 @@ func TestBuildSingleFileMessage_EmptyFile(t *testing.T) {
// BuildInvitationAnswerMessage // BuildInvitationAnswerMessage
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
func TestBuildInvitationAnswerMessage(t *testing.T) { func TestBuildInvitationStep2Message(t *testing.T) {
p := &Peer{ p := &Peer{
ContactLookupKey: "dest-lookup", ContactLookupKey: "dest-lookup",
MyIdentity: &meowlib.KeyPair{Public: "my-pub"}, MyIdentity: &meowlib.KeyPair{Public: "my-pub"},
@@ -239,13 +239,13 @@ func TestBuildInvitationAnswerMessage(t *testing.T) {
ContactPublicKey: "alice-pub", ContactPublicKey: "alice-pub",
} }
msg, err := p.BuildInvitationAnswerMessage(contactCard) msg, err := p.BuildInvitationStep2Message(contactCard)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "dest-lookup", msg.Destination) assert.Equal(t, "dest-lookup", msg.Destination)
assert.Equal(t, "my-pub", msg.From) assert.Equal(t, "my-pub", msg.From)
assert.Equal(t, "1", msg.Type) assert.Equal(t, "1", msg.Type)
assert.NotNil(t, msg.Invitation) assert.NotNil(t, msg.Invitation)
assert.Equal(t, int32(3), msg.Invitation.Step) assert.Equal(t, int32(2), msg.Invitation.Step)
assert.Equal(t, "inv-uuid-123", msg.Invitation.Uuid) assert.Equal(t, "inv-uuid-123", msg.Invitation.Uuid)
// Payload is the proto-serialized contact card // Payload is the proto-serialized contact card
@@ -256,6 +256,42 @@ func TestBuildInvitationAnswerMessage(t *testing.T) {
assert.Equal(t, "alice-pub", decoded.ContactPublicKey) assert.Equal(t, "alice-pub", decoded.ContactPublicKey)
} }
func TestBuildInvitationStep3Message(t *testing.T) {
p := &Peer{
ContactLookupKey: "dest-lookup",
MyIdentity: &meowlib.KeyPair{Public: "my-pub"},
InvitationId: "inv-uuid-456",
}
contactCard := &meowlib.ContactCard{
Name: "Initiator",
ContactPublicKey: "init-pub",
}
msg, err := p.BuildInvitationStep3Message(contactCard)
assert.NoError(t, err)
assert.Equal(t, int32(3), msg.Invitation.Step)
assert.Equal(t, "inv-uuid-456", msg.Invitation.Uuid)
var decoded meowlib.ContactCard
err = proto.Unmarshal(msg.Invitation.Payload, &decoded)
assert.NoError(t, err)
assert.Equal(t, "Initiator", decoded.Name)
}
func TestBuildInvitationStep4Message(t *testing.T) {
p := &Peer{
ContactLookupKey: "dest-lookup",
MyIdentity: &meowlib.KeyPair{Public: "my-pub"},
InvitationId: "inv-uuid-789",
}
msg, err := p.BuildInvitationStep4Message()
assert.NoError(t, err)
assert.Equal(t, int32(4), msg.Invitation.Step)
assert.Equal(t, "inv-uuid-789", msg.Invitation.Uuid)
assert.Nil(t, msg.Invitation.Payload)
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Serialize / Deserialize // Serialize / Deserialize
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@@ -199,12 +199,12 @@ func (ints *Server) BuildVideoRoomRequestMessage(users []string, expiry uint64)
return &msg, nil return &msg, nil
} }
// BuildToServerMessageInvitation creates an invitation message to server and returns it as a meowlib.ToServerMessage // BuildToServerMessageInvitationStep1 sends the InvitationInitPayload to the server (STEP_1).
// it takes as input a contactcard generated by Identity.InvitePeer // The server stores it and returns a shortcode URL.
func (ints *Server) BuildToServerMessageInvitationCreation(invitation *meowlib.ContactCard, password string, timeout int, shortCodeLen int) (*meowlib.ToServerMessage, error) { func (ints *Server) BuildToServerMessageInvitationStep1(initPayload *meowlib.InvitationInitPayload, password string, timeout int, shortCodeLen int) (*meowlib.ToServerMessage, error) {
var msg meowlib.ToServerMessage var msg meowlib.ToServerMessage
var inv meowlib.Invitation var inv meowlib.Invitation
payload, err := invitation.Compress() payload, err := initPayload.Compress()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -454,16 +454,18 @@ func TestServer_BuildVideoRoomRequestMessage_SingleUser(t *testing.T) {
// BuildToServerMessageInvitationCreation // BuildToServerMessageInvitationCreation
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
func TestServer_BuildToServerMessageInvitationCreation(t *testing.T) { func TestServer_BuildToServerMessageInvitationStep1(t *testing.T) {
srv, err := CreateServerFromUrl("https://example.com/meow") srv, err := CreateServerFromUrl("https://example.com/meow")
assert.NoError(t, err) assert.NoError(t, err)
cc := &meowlib.ContactCard{ initPayload := &meowlib.InvitationInitPayload{
Name: "Alice", Uuid: "test-uuid",
ContactPublicKey: "alice-pub", Name: "Alice",
PublicKey: "alice-temp-pub",
InvitationMessage: "Hello!",
} }
msg, err := srv.BuildToServerMessageInvitationCreation(cc, "secret", 300, 8) msg, err := srv.BuildToServerMessageInvitationStep1(initPayload, "secret", 300, 8)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "1", msg.Type) assert.Equal(t, "1", msg.Type)
assert.Equal(t, srv.UserKp.Public, msg.From) assert.Equal(t, srv.UserKp.Public, msg.From)
@@ -474,18 +476,19 @@ func TestServer_BuildToServerMessageInvitationCreation(t *testing.T) {
assert.Equal(t, int32(8), msg.Invitation.ShortcodeLen) assert.Equal(t, int32(8), msg.Invitation.ShortcodeLen)
assert.NotEmpty(t, msg.Invitation.Payload) assert.NotEmpty(t, msg.Invitation.Payload)
// Payload is a compressed ContactCard — decompress and verify // Payload is a compressed InvitationInitPayload — decompress and verify
restored, err := meowlib.NewContactCardFromCompressed(msg.Invitation.Payload) restored, err := meowlib.NewInvitationInitPayloadFromCompressed(msg.Invitation.Payload)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "Alice", restored.Name) assert.Equal(t, "Alice", restored.Name)
assert.Equal(t, "alice-pub", restored.ContactPublicKey) assert.Equal(t, "alice-temp-pub", restored.PublicKey)
assert.Equal(t, "test-uuid", restored.Uuid)
} }
func TestServer_BuildToServerMessageInvitationCreation_NoPassword(t *testing.T) { func TestServer_BuildToServerMessageInvitationStep1_NoPassword(t *testing.T) {
srv, _ := CreateServerFromUrl("https://example.com/meow") srv, _ := CreateServerFromUrl("https://example.com/meow")
cc := &meowlib.ContactCard{Name: "Bob"} initPayload := &meowlib.InvitationInitPayload{Name: "Bob", Uuid: "bob-uuid", PublicKey: "bob-pub"}
msg, err := srv.BuildToServerMessageInvitationCreation(cc, "", 60, 6) msg, err := srv.BuildToServerMessageInvitationStep1(initPayload, "", 60, 6)
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, msg.Invitation.Password) assert.Empty(t, msg.Invitation.Password)
assert.Equal(t, int32(1), msg.Invitation.Step) assert.Equal(t, int32(1), msg.Invitation.Step)

44
client/test.id Normal file
View File

@@ -0,0 +1,44 @@
-----BEGIN PGP MESSAGE-----
Comment: https://gopenpgp.org
Version: GopenPGP 2.8.3
wy4ECQMIlftc5WyUrBjgI1MbXSAWh3ZqBpILi+RN79+v4HuvB/xmqoEJtZVeypwh
0uoBc2FevnicfVu4wOUlglRjhPWLcE25+gQxlKB7RzX6cQND3+Nw3qiexvK+psrm
mW7nOIHE/9EVXzAlRrCgMlPcZpPB+5q5X9t01BQ/tTV6OytcLS3J6byrMmefA7jG
ki/U9oSkdwFYPosG5PKhiHCe03AIjY++s/Wgn1OMtsLWX/8/dJ6CNkzvwnX4CVti
x8KGj7IwJefG7BGApU3eg9OcqRz8KubWI1mWfiC2uVOoFgVlnAOjP8qzUFs65LK9
cBglhUNuG/Jc2ojCa9ndWYIaDJ2pzGpvhlGsj7kU0Fyh3AMTTzrJeRwAoqcLv8P5
B6ERBv0rG16arkhpC4v6BFT3UekMzBMhpGSb8PPu3BmDayHmWG+Q3Lt7ufnm/UId
naLVfnQKD6An05KkqZNqHjPsbHPg8gFcV3N87LCtCMYGGDgxbsKBDh/ig0FQwnnq
P5Hj4VZTUcuJ25BSV/Tbbo8Z9XGKQ02OnX7h7qies+oVAan9Pq3YgjoqFB06wDTq
hBxrSMgexfB2Dj23pioC72Ege22n1I6PBwuM5p6Ja0btZQrfhL/yY/y102MvgUXh
Qh84zxtTKKR8b3sL3WeEckOPBcEOvbmLf+sTjWdIIcQMB0IGhDhzCvf0sGtk48eJ
rKNruG7RMHGjBZkZnpJVArJchxmRZkuGLjwsQTRbdRPQc6vMmvPhqCuFPMhnTaL9
nss0tnzQ2DdLOwO8JsQH41IoRi0STl6ndDT4wbGlmuh57xqMdrNjkur84zsi6G76
wQOtGQ7A+9xCz/cnAaTPlmUUe+0Fg2vHQbGPfZy3TfERAkGYg9EsQbww/nNSOQua
e+DbLNbBPp5egkfR6TDDbiTgwWXn6R673qLQ27MpHBY2eQ8IaJqz/jdm6/UPbuh3
bpBF0G7HVwxfhDAPBKPObJM8doHB67d5hoxcqfINexVXsX5Dd3OzCY1mUKgn95kF
Tzl4VGu4kIxcFRXMR49XaHC4/CQbv70c/2NiJf739fxcLkGUQ5wXA44uMKwEbzwW
x53fhFKKjGC/AWubs9jnVVJz7EfiFX9VvhEYvXp3++emM9Nbv6BaRobq9JIKmdMl
E69BcHrqZ7ahMDTENSpVZTlohs4AnaxeZesCPq7t75STAx2/jj3YtgfeYarE3d9I
rn8VofS5uI41VNO4noQtj8a18YzNW5V+aGLjD2ZxvxMYfp8NfsJpEuWpXRNE5yZq
AzeXlGlcMHc/n6+vgdTirSTbrwY2chBgxwWAdpcezimAl6VpT4gZ1pmtDxtQA5v0
yC6LRujp+p9yPfrVEB/tuduo3DpnBJjkAcBlDtGuSew98QoIDKI/UcMUqGZW+n4U
/QugOpd9aY7UhIFiWHZ14PnZwiUhdxZTEE4wo8TVVFRmP4L6oxLBjOByLPOH4ct+
eNrL5cXABE0rwm3/Ywxuxy3hV07tazm+GpxdUjX4+cjBJZCwYO/JyT0OI2sPsKIY
6WO2zkobs8fn0j3ba1ovRWGmAU0MnGCg1ZnJOiXtUn17QXoe3CnjvQu9wS15ms2F
htQtIZwnosXuHcXUzNNtv4SFdZAFsy8tj4TYtQ3qtxYKjxyLlmPZ9yT0DD2VDcFL
ra7II59iElBCyC0JS/q1JQxdgVPhD0ZU+x9F/koquS+35gtqjemVmeLb9W+nEWc0
3H4W0i0k0wkSwWX4FUmGbqHczOCoVoTuKkp+ypAfzZ8L/nHybz4eK7RdGKfWeYbG
N3zlTLaTTd2D7D1s5+df0itoM/VS0pSHPHMkNCJ/CmC8gwlIENU4cRqvvBXF2dEA
Far8qCMJLscaoKvbQVQwhqzq9nEyra5CscJzD7nq3aiS5gwOfzy6G1qvk4KFxcaX
PLBEAegTueaMj7KvTwDd+Yz7lnbk2fmNo4lJlGkUJMyEDLCjYg0sqLokOO7MMYyC
V69bnJCoQPwfaE0vETiZn6TFGXG0oQg4ki87lhNzzXlT8JiTK4RMWWGtw8QBHmsv
PWVBMuooqrPXpBEty7O7+Cxef/P0My8CxwgMOEPA2dAtWrvXrOM3wHCWoLK4FJlS
XxHNHPwyZ49vCuEWhUJArge1oXWwZUTCpGEJLd0taUI+T9GU+5VG/VrbHprBdod4
FjRAXxpO4Sx/Z7L/vccFjOjHeobNMKGmC4BDDmUSECCszWqT37/XFbGJrHdYqnht
yzdRDeI11rEIpNyF65PgJR6A5hEnZk0IsSqiTvPcIodUlPkhlSVPoc+NSrYATuJa
VviYI8AhTUxrAcZyG/unEKKQfCBB8XBn8gUTkodxOaI27GVJ/T4WgGERsPNQm/Fl
HCbvaphsM7nszn8iuoRv5PWiWiZsetl+HvXVKWUUb4jxq6xgpIpsBJw=
=BI6k
-----END PGP MESSAGE-----

View File

@@ -1,6 +1,13 @@
@startuml General Invitation Steps @startuml General Invitation Steps
InitiatingUser -> InvitedUser: STEP_1=InitiatingUser_TmpId generate a public key, invitation uid & message for InvitedUser optionnally password protected InitiatingUser -> InitiatingUser : STEP_1 = Create InivitedUser_Id generate a public key, invitation uid & message for InvitedUser optionnally password protected
InvitedUser -> InitiatingUser: STEP_2=InvitedUser_Id Create invitation for alice (Generate InvitedUser ContactCard and create InitiatingUser pending contact) InitiatingUser -> InvitedUser: STEP_1_SEND= transmit step 1 data (QR Code, Bluetooth, messaging, mail, ...) optional password being tranmitted through another channel
InitiatingUser -> InvitedUser: STEP_3=InitiatingUser_Id Accept Invitation and create answer (Generate InitiatingUser ContactCard and create finalized InvitedUser contact)
InvitedUser -> InitiatingUser: STEP_4=InvitedUser_OK Review Answer, invitation finalize (Finalize InitiatingUser contact and notify InitiatingUser that communication is possible) InvitedUser -> InvitedUser :Create InitatingUser_Id & InvitedUser ContactCard
InvitedUser -> InitiatingUser: STEP_2_SEND=transmit InvitedUser ContactCard (QR Codes, Bluetooth, messaging, mail, ...) encrypted with initiating user pub key
InitiatingUser -> InitiatingUser : STEP_3=InitiatingUser_Id Accept Invitation and create answer (Generate InitiatingUser ContactCard and create finalized InvitedUser contact)
InitiatingUser -> InvitedUser: STEP_3_SEND=Send answer through invited user's message servers from contact card
InvitedUser -> InvitedUser : Finalize InitiatingUser from its ContactCard
InvitedUser -> InitiatingUser: STEP_4= Send confirmation to InitiatingUser that communication is possible through initiating user's message servers from contact card
@enduml @enduml

View File

@@ -1,9 +0,0 @@
@startuml Detailled Invitation Steps
InitiatingUser -> MeowInitiatingUser: STEP 1 generate a public key & message for InvitedUser optionnally password protected
InitiatingUser -> InvitedUser: send public key
InvitedUser -> MeowInvitedUser: STEP 2 Create invitation for alice (Generate InvitedUser ContactCard and create InitiatingUser pending contact)
InvitedUser -> InitiatingUser: Send invitation (InvitedUser ContactCard encrypted with InitiatingUser public key)
InitiatingUser -> MeowInitiatingUser: STEP 3 Accept Invitation and create answer (Generate InitiatingUser ContactCard and create finalized InvitedUser contact)
InitiatingUser -> InvitedUser: Send answer (InitiatingUser ContactCard)
InvitedUser -> MeowInvitedUser: STEP 4 Review Answer, invitation finalize (Finalize InitiatingUser contact and notify InitiatingUser that communication is possible)
@enduml

View File

@@ -2,7 +2,7 @@
InitiatingUser -> Bastet : contact name InitiatingUser -> Bastet : contact name
InitiatingUser -> Bastet : invitationMessage InitiatingUser -> Bastet : invitationMessage
InitiatingUser -> Bastet : select invitation server InitiatingUser -> Bastet : select invitation server
InitiatingUser -> Bastet : optional password InitiatingUser -> Bastet : optional passwords ([payload], [url])
Bastet -> NativeLib : send invitation Bastet -> NativeLib : send invitation
NativeLib -> NativeLib : create contact and invtation KP NativeLib -> NativeLib : create contact and invtation KP
NativeLib -> Server : send invitation NativeLib -> Server : send invitation
@@ -12,4 +12,5 @@ Server -> NativeLib : invitation URL
NativeLib -> Bastet : invitation URL NativeLib -> Bastet : invitation URL
Bastet -> InitiatingUser : invitation URL Bastet -> InitiatingUser : invitation URL
InitiatingUser -> InvitedUser : invitation URL InitiatingUser -> InvitedUser : invitation URL
InitiatingUser -> InvitedUser : [invitation passwords] through another channel
@enduml @enduml

View File

@@ -32,58 +32,59 @@ func TestEndToEnd(t *testing.T) {
srv = client.Server{Name: "MyServer", Url: "meow://127.0.0.1"} srv = client.Server{Name: "MyServer", Url: "meow://127.0.0.1"}
Me.MessageServers.StoreServer(&srv) Me.MessageServers.StoreServer(&srv)
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Create an invitation for a friend, I want him/her to know me as Bender // // STEP_1: Create an invitation — only a temp keypair, no full ContactCard //
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
fmt.Println("Creating an invitation for the first friend...") fmt.Println("Creating an invitation for the first friend...")
peer, err := Me.InvitePeer("Bender", "myfirstfriend", []string{"http://127.0.0.1/meow/", "mqtt://127.0.0.1"}, "welcome, it's me!") initPayload, peer, err := Me.InvitationStep1("Bender", "myfirstfriend", []string{"http://127.0.0.1/meow/", "mqtt://127.0.0.1"}, "welcome, it's me!")
if err != nil { if err != nil {
println(err) println(err)
} }
println(peer.Name) println(peer.Name)
// print my invitation a, _ := json.Marshal(initPayload)
a, _ := json.Marshal(peer.GetMyContact()) fmt.Println("InvitationInitPayload:", string(a))
fmt.Println(string(a)) data, err := initPayload.Compress()
// TODO : Convert invitation to QR Code
peer.GetMyContact().WritePng("invitation.png")
data, err := peer.GetMyContact().Compress()
if err != nil { if err != nil {
println(err) println(err)
} }
peer.GetMyContact().WriteQr("qrcode.png") initPayload.WriteQr("qrcode.png")
println("Compressed contact card :", len(data)) println("Compressed init payload :", len(data))
///////////////////////////////////////
// Simulate peer invitation response //
///////////////////////////////////////
fmt.Println("Simulating first friend answer...")
var ReceivedContact meowlib.ContactCard
// Friend simulated invitation ///////////////////////////////////////////////////////
FirstFriendContactKp, err := meowlib.NewKeyPair() // STEP_2: Simulate friend receiving the payload and answering //
///////////////////////////////////////////////////////
fmt.Println("Simulating first friend answer (STEP_2)...")
friendMe, err := client.CreateIdentity("friendname")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
FirstFriendEncryptionKp, err := meowlib.NewKeyPair() friendPeer, err := friendMe.InvitationStep2("FriendNick", "Bender", []string{}, initPayload)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
FirstFriendLookupKp, err := meowlib.NewKeyPair() friendCC := friendPeer.GetMyContact()
if err != nil {
t.Fatal(err)
}
ReceivedContact.Name = "I'm the friend"
ReceivedContact.ContactPublicKey = FirstFriendContactKp.Public
ReceivedContact.EncryptionPublicKey = FirstFriendEncryptionKp.Public
ReceivedContact.LookupPublicKey = FirstFriendLookupKp.Public
ReceivedContact.InvitationId = peer.GetMyContact().InvitationId
FriendServer1KP, err := meowlib.NewKeyPair() FriendServer1KP, err := meowlib.NewKeyPair()
if err != nil {
t.Fatal(err)
}
FriendServer1 := meowlib.ServerCard{Name: "FriendServer1", Url: "http://myfriend.org/meow/", PublicKey: FriendServer1KP.Public, Description: "Fancy description"} FriendServer1 := meowlib.ServerCard{Name: "FriendServer1", Url: "http://myfriend.org/meow/", PublicKey: FriendServer1KP.Public, Description: "Fancy description"}
ReceivedContact.PullServers = append(ReceivedContact.PullServers, &FriendServer1) friendCC.PullServers = append(friendCC.PullServers, &FriendServer1)
/////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// Finalize the contact with the invitation response // // STEP_3: Initiator receives friend's CC, generates full keypairs //
/////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
Me.FinalizeInvitation(&ReceivedContact) myCC, _, err := Me.InvitationStep3(friendCC)
if err != nil {
t.Fatal(err)
}
fmt.Println("Initiator ContactCard ready:", myCC.ContactPublicKey != "")
////////////////////////////////////////////////////
// STEP_4: Friend finalizes initiator's ContactCard //
////////////////////////////////////////////////////
err = friendMe.InvitationStep4(myCC)
if err != nil {
t.Fatal(err)
}
err = Me.Save() err = Me.Save()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

80
invitationinitpayload.go Normal file
View File

@@ -0,0 +1,80 @@
package meowlib
import (
"bytes"
"compress/gzip"
"io"
"os"
"github.com/makiuchi-d/gozxing"
"github.com/makiuchi-d/gozxing/qrcode"
"google.golang.org/protobuf/proto"
"image/png"
)
func (p *InvitationInitPayload) Serialize() ([]byte, error) {
return proto.Marshal(p)
}
func (p *InvitationInitPayload) Compress() ([]byte, error) {
out, err := p.Serialize()
if err != nil {
return nil, err
}
var b bytes.Buffer
gz, err := gzip.NewWriterLevel(&b, gzip.BestCompression)
if err != nil {
return nil, err
}
if _, err := gz.Write(out); err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func NewInvitationInitPayloadFromCompressed(compressed []byte) (*InvitationInitPayload, error) {
p := &InvitationInitPayload{}
reader := bytes.NewReader(compressed)
gzreader, err := gzip.NewReader(reader)
if err != nil {
return nil, err
}
output, err := io.ReadAll(gzreader)
if err != nil {
return nil, err
}
if err := proto.Unmarshal(output, p); err != nil {
return nil, err
}
return p, nil
}
func (p *InvitationInitPayload) WriteCompressed(filename string) error {
out, err := p.Compress()
if err != nil {
return err
}
return os.WriteFile(filename, out, 0600)
}
func (p *InvitationInitPayload) WriteQr(filename string) error {
data, err := p.Compress()
if err != nil {
return err
}
qwriter := qrcode.NewQRCodeWriter()
code, err := qwriter.Encode(string(data), gozxing.BarcodeFormat_QR_CODE, 512, 512, nil)
if err != nil {
return err
}
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
return png.Encode(file, code)
}

View File

@@ -97,15 +97,15 @@ func (x *PackedServerMessage) GetSignature() []byte {
// structure to hold an invitation through a server // structure to hold an invitation through a server
type Invitation struct { type Invitation struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` // invitation payload, encrypted after step 2 Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` // invitation payload, optionaly encrypted with payload password(transmitted OOB) on step 1
Timeout int32 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` // how long do I want the invitation to remain available on the server Timeout int32 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` // how long do I want the invitation to remain available on the server
ShortcodeLen int32 `protobuf:"varint,3,opt,name=shortcodeLen,proto3" json:"shortcodeLen,omitempty"` // len of the shortcode you wish for short url transmission ShortcodeLen int32 `protobuf:"varint,3,opt,name=shortcode_len,json=shortcodeLen,proto3" json:"shortcode_len,omitempty"` // len of the shortcode you wish for short url transmission
Shortcode string `protobuf:"bytes,4,opt,name=shortcode,proto3" json:"shortcode,omitempty"` // shortcode that the friend shall request to get the invitation Shortcode string `protobuf:"bytes,4,opt,name=shortcode,proto3" json:"shortcode,omitempty"` // shortcode that the friend shall request to get the invitation
Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"` // password to set for accessing invitation (optional) Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"` // optional password(transmitted OOB) to set for accessing invitation (server check)
Uuid string `protobuf:"bytes,6,opt,name=uuid,proto3" json:"uuid,omitempty"` // id that the friend gave you, that you should include to your reply to get recognized Uuid string `protobuf:"bytes,6,opt,name=uuid,proto3" json:"uuid,omitempty"` // invitation uuid
Expiry int64 `protobuf:"varint,7,opt,name=expiry,proto3" json:"expiry,omitempty"` // the server allowed expiry date, it may be samller than the requested timeout according to server policy Expiry int64 `protobuf:"varint,7,opt,name=expiry,proto3" json:"expiry,omitempty"` // the server allowed expiry date, it may be smaller than the requested timeout according to server policy
Step int32 `protobuf:"varint,8,opt,name=step,proto3" json:"step,omitempty"` // progress in the inviattion process : 1=invite friend, 2=friend requests invitation, 3=friend's answer Step int32 `protobuf:"varint,8,opt,name=step,proto3" json:"step,omitempty"` // progress in the invitation process : 1=initiator pub key, 2=invited data enc with pub key, 3=initator data full encrypted, 4=invited All OK !
From string `protobuf:"bytes,9,opt,name=from,proto3" json:"from,omitempty"` // used in step 3 the answer public key to check the signature in user message From string `protobuf:"bytes,9,opt,name=from,proto3" json:"from,omitempty"` // still useful ?
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -203,6 +203,75 @@ func (x *Invitation) GetFrom() string {
return "" return ""
} }
// This payload migh be used for file serialization as well as Invitation/payload in case of through server invitation
type InvitationInitPayload struct {
state protoimpl.MessageState `protogen:"open.v1"`
Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` // uuid of the invitation, it is set here on init cause the payload might be encrypted
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // name of the initiator
PublicKey string `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // public key to be used for step 2 encryption
InvitationMessage string `protobuf:"bytes,4,opt,name=invitation_message,json=invitationMessage,proto3" json:"invitation_message,omitempty"` // message for the invited peer
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InvitationInitPayload) Reset() {
*x = InvitationInitPayload{}
mi := &file_messages_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *InvitationInitPayload) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*InvitationInitPayload) ProtoMessage() {}
func (x *InvitationInitPayload) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use InvitationInitPayload.ProtoReflect.Descriptor instead.
func (*InvitationInitPayload) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{2}
}
func (x *InvitationInitPayload) GetUuid() string {
if x != nil {
return x.Uuid
}
return ""
}
func (x *InvitationInitPayload) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *InvitationInitPayload) GetPublicKey() string {
if x != nil {
return x.PublicKey
}
return ""
}
func (x *InvitationInitPayload) GetInvitationMessage() string {
if x != nil {
return x.InvitationMessage
}
return ""
}
// structure for requesting incoming messages // structure for requesting incoming messages
type ConversationRequest struct { type ConversationRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
@@ -216,7 +285,7 @@ type ConversationRequest struct {
func (x *ConversationRequest) Reset() { func (x *ConversationRequest) Reset() {
*x = ConversationRequest{} *x = ConversationRequest{}
mi := &file_messages_proto_msgTypes[2] mi := &file_messages_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -228,7 +297,7 @@ func (x *ConversationRequest) String() string {
func (*ConversationRequest) ProtoMessage() {} func (*ConversationRequest) ProtoMessage() {}
func (x *ConversationRequest) ProtoReflect() protoreflect.Message { func (x *ConversationRequest) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[2] mi := &file_messages_proto_msgTypes[3]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -241,7 +310,7 @@ func (x *ConversationRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ConversationRequest.ProtoReflect.Descriptor instead. // Deprecated: Use ConversationRequest.ProtoReflect.Descriptor instead.
func (*ConversationRequest) Descriptor() ([]byte, []int) { func (*ConversationRequest) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{2} return file_messages_proto_rawDescGZIP(), []int{3}
} }
func (x *ConversationRequest) GetLookupKey() string { func (x *ConversationRequest) GetLookupKey() string {
@@ -283,7 +352,7 @@ type Meet struct {
func (x *Meet) Reset() { func (x *Meet) Reset() {
*x = Meet{} *x = Meet{}
mi := &file_messages_proto_msgTypes[3] mi := &file_messages_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -295,7 +364,7 @@ func (x *Meet) String() string {
func (*Meet) ProtoMessage() {} func (*Meet) ProtoMessage() {}
func (x *Meet) ProtoReflect() protoreflect.Message { func (x *Meet) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[3] mi := &file_messages_proto_msgTypes[4]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -308,7 +377,7 @@ func (x *Meet) ProtoReflect() protoreflect.Message {
// Deprecated: Use Meet.ProtoReflect.Descriptor instead. // Deprecated: Use Meet.ProtoReflect.Descriptor instead.
func (*Meet) Descriptor() ([]byte, []int) { func (*Meet) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{3} return file_messages_proto_rawDescGZIP(), []int{4}
} }
func (x *Meet) GetPublicStatus() string { func (x *Meet) GetPublicStatus() string {
@@ -344,7 +413,7 @@ type Credentials struct {
func (x *Credentials) Reset() { func (x *Credentials) Reset() {
*x = Credentials{} *x = Credentials{}
mi := &file_messages_proto_msgTypes[4] mi := &file_messages_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -356,7 +425,7 @@ func (x *Credentials) String() string {
func (*Credentials) ProtoMessage() {} func (*Credentials) ProtoMessage() {}
func (x *Credentials) ProtoReflect() protoreflect.Message { func (x *Credentials) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[4] mi := &file_messages_proto_msgTypes[5]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -369,7 +438,7 @@ func (x *Credentials) ProtoReflect() protoreflect.Message {
// Deprecated: Use Credentials.ProtoReflect.Descriptor instead. // Deprecated: Use Credentials.ProtoReflect.Descriptor instead.
func (*Credentials) Descriptor() ([]byte, []int) { func (*Credentials) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{4} return file_messages_proto_rawDescGZIP(), []int{5}
} }
func (x *Credentials) GetLogin() string { func (x *Credentials) GetLogin() string {
@@ -422,7 +491,7 @@ type ToServerMessage struct {
func (x *ToServerMessage) Reset() { func (x *ToServerMessage) Reset() {
*x = ToServerMessage{} *x = ToServerMessage{}
mi := &file_messages_proto_msgTypes[5] mi := &file_messages_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -434,7 +503,7 @@ func (x *ToServerMessage) String() string {
func (*ToServerMessage) ProtoMessage() {} func (*ToServerMessage) ProtoMessage() {}
func (x *ToServerMessage) ProtoReflect() protoreflect.Message { func (x *ToServerMessage) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[5] mi := &file_messages_proto_msgTypes[6]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -447,7 +516,7 @@ func (x *ToServerMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use ToServerMessage.ProtoReflect.Descriptor instead. // Deprecated: Use ToServerMessage.ProtoReflect.Descriptor instead.
func (*ToServerMessage) Descriptor() ([]byte, []int) { func (*ToServerMessage) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{5} return file_messages_proto_rawDescGZIP(), []int{6}
} }
func (x *ToServerMessage) GetType() string { func (x *ToServerMessage) GetType() string {
@@ -561,7 +630,7 @@ type FromServerMessage struct {
func (x *FromServerMessage) Reset() { func (x *FromServerMessage) Reset() {
*x = FromServerMessage{} *x = FromServerMessage{}
mi := &file_messages_proto_msgTypes[6] mi := &file_messages_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -573,7 +642,7 @@ func (x *FromServerMessage) String() string {
func (*FromServerMessage) ProtoMessage() {} func (*FromServerMessage) ProtoMessage() {}
func (x *FromServerMessage) ProtoReflect() protoreflect.Message { func (x *FromServerMessage) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[6] mi := &file_messages_proto_msgTypes[7]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -586,7 +655,7 @@ func (x *FromServerMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use FromServerMessage.ProtoReflect.Descriptor instead. // Deprecated: Use FromServerMessage.ProtoReflect.Descriptor instead.
func (*FromServerMessage) Descriptor() ([]byte, []int) { func (*FromServerMessage) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{6} return file_messages_proto_rawDescGZIP(), []int{7}
} }
func (x *FromServerMessage) GetType() string { func (x *FromServerMessage) GetType() string {
@@ -678,7 +747,7 @@ type MatriochkaServer struct {
func (x *MatriochkaServer) Reset() { func (x *MatriochkaServer) Reset() {
*x = MatriochkaServer{} *x = MatriochkaServer{}
mi := &file_messages_proto_msgTypes[7] mi := &file_messages_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -690,7 +759,7 @@ func (x *MatriochkaServer) String() string {
func (*MatriochkaServer) ProtoMessage() {} func (*MatriochkaServer) ProtoMessage() {}
func (x *MatriochkaServer) ProtoReflect() protoreflect.Message { func (x *MatriochkaServer) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[7] mi := &file_messages_proto_msgTypes[8]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -703,7 +772,7 @@ func (x *MatriochkaServer) ProtoReflect() protoreflect.Message {
// Deprecated: Use MatriochkaServer.ProtoReflect.Descriptor instead. // Deprecated: Use MatriochkaServer.ProtoReflect.Descriptor instead.
func (*MatriochkaServer) Descriptor() ([]byte, []int) { func (*MatriochkaServer) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{7} return file_messages_proto_rawDescGZIP(), []int{8}
} }
func (x *MatriochkaServer) GetUrl() string { func (x *MatriochkaServer) GetUrl() string {
@@ -746,7 +815,7 @@ type Matriochka struct {
func (x *Matriochka) Reset() { func (x *Matriochka) Reset() {
*x = Matriochka{} *x = Matriochka{}
mi := &file_messages_proto_msgTypes[8] mi := &file_messages_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -758,7 +827,7 @@ func (x *Matriochka) String() string {
func (*Matriochka) ProtoMessage() {} func (*Matriochka) ProtoMessage() {}
func (x *Matriochka) ProtoReflect() protoreflect.Message { func (x *Matriochka) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[8] mi := &file_messages_proto_msgTypes[9]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -771,7 +840,7 @@ func (x *Matriochka) ProtoReflect() protoreflect.Message {
// Deprecated: Use Matriochka.ProtoReflect.Descriptor instead. // Deprecated: Use Matriochka.ProtoReflect.Descriptor instead.
func (*Matriochka) Descriptor() ([]byte, []int) { func (*Matriochka) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{8} return file_messages_proto_rawDescGZIP(), []int{9}
} }
func (x *Matriochka) GetLookupKey() string { func (x *Matriochka) GetLookupKey() string {
@@ -818,7 +887,7 @@ type ServerCard struct {
func (x *ServerCard) Reset() { func (x *ServerCard) Reset() {
*x = ServerCard{} *x = ServerCard{}
mi := &file_messages_proto_msgTypes[9] mi := &file_messages_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -830,7 +899,7 @@ func (x *ServerCard) String() string {
func (*ServerCard) ProtoMessage() {} func (*ServerCard) ProtoMessage() {}
func (x *ServerCard) ProtoReflect() protoreflect.Message { func (x *ServerCard) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[9] mi := &file_messages_proto_msgTypes[10]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -843,7 +912,7 @@ func (x *ServerCard) ProtoReflect() protoreflect.Message {
// Deprecated: Use ServerCard.ProtoReflect.Descriptor instead. // Deprecated: Use ServerCard.ProtoReflect.Descriptor instead.
func (*ServerCard) Descriptor() ([]byte, []int) { func (*ServerCard) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{9} return file_messages_proto_rawDescGZIP(), []int{10}
} }
func (x *ServerCard) GetName() string { func (x *ServerCard) GetName() string {
@@ -915,7 +984,7 @@ type ContactCard struct {
func (x *ContactCard) Reset() { func (x *ContactCard) Reset() {
*x = ContactCard{} *x = ContactCard{}
mi := &file_messages_proto_msgTypes[10] mi := &file_messages_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -927,7 +996,7 @@ func (x *ContactCard) String() string {
func (*ContactCard) ProtoMessage() {} func (*ContactCard) ProtoMessage() {}
func (x *ContactCard) ProtoReflect() protoreflect.Message { func (x *ContactCard) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[10] mi := &file_messages_proto_msgTypes[11]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -940,7 +1009,7 @@ func (x *ContactCard) ProtoReflect() protoreflect.Message {
// Deprecated: Use ContactCard.ProtoReflect.Descriptor instead. // Deprecated: Use ContactCard.ProtoReflect.Descriptor instead.
func (*ContactCard) Descriptor() ([]byte, []int) { func (*ContactCard) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{10} return file_messages_proto_rawDescGZIP(), []int{11}
} }
func (x *ContactCard) GetName() string { func (x *ContactCard) GetName() string {
@@ -1035,7 +1104,7 @@ type PackedUserMessage struct {
func (x *PackedUserMessage) Reset() { func (x *PackedUserMessage) Reset() {
*x = PackedUserMessage{} *x = PackedUserMessage{}
mi := &file_messages_proto_msgTypes[11] mi := &file_messages_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1047,7 +1116,7 @@ func (x *PackedUserMessage) String() string {
func (*PackedUserMessage) ProtoMessage() {} func (*PackedUserMessage) ProtoMessage() {}
func (x *PackedUserMessage) ProtoReflect() protoreflect.Message { func (x *PackedUserMessage) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[11] mi := &file_messages_proto_msgTypes[12]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1060,7 +1129,7 @@ func (x *PackedUserMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use PackedUserMessage.ProtoReflect.Descriptor instead. // Deprecated: Use PackedUserMessage.ProtoReflect.Descriptor instead.
func (*PackedUserMessage) Descriptor() ([]byte, []int) { func (*PackedUserMessage) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{11} return file_messages_proto_rawDescGZIP(), []int{12}
} }
func (x *PackedUserMessage) GetDestination() string { func (x *PackedUserMessage) GetDestination() string {
@@ -1108,7 +1177,7 @@ func (x *PackedUserMessage) GetDrHeader() []byte {
type ConversationStatus struct { type ConversationStatus struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` // uuid of message, or uuid of related message if uuid_action is not empty Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` // uuid of message, or uuid of related message if uuid_action is not empty
Reactions []*Reaction `protobuf:"bytes,2,rep,name=reactions,proto3" json:"reactions,omitempty"` // empty => normal message, 1: receivedack, 2: processedack, 3:reaction Reactions []*Reaction `protobuf:"bytes,2,rep,name=reactions,proto3" json:"reactions,omitempty"` // reaction to the message per peer
ReplyToUuid string `protobuf:"bytes,3,opt,name=reply_to_uuid,json=replyToUuid,proto3" json:"reply_to_uuid,omitempty"` // this message replies to the specified uuid ReplyToUuid string `protobuf:"bytes,3,opt,name=reply_to_uuid,json=replyToUuid,proto3" json:"reply_to_uuid,omitempty"` // this message replies to the specified uuid
LocalSequence uint64 `protobuf:"varint,4,opt,name=local_sequence,json=localSequence,proto3" json:"local_sequence,omitempty"` // seq number in local conversation for custom reordering LocalSequence uint64 `protobuf:"varint,4,opt,name=local_sequence,json=localSequence,proto3" json:"local_sequence,omitempty"` // seq number in local conversation for custom reordering
Sent uint64 `protobuf:"varint,5,opt,name=sent,proto3" json:"sent,omitempty"` // timestamp of the message sent Sent uint64 `protobuf:"varint,5,opt,name=sent,proto3" json:"sent,omitempty"` // timestamp of the message sent
@@ -1122,7 +1191,7 @@ type ConversationStatus struct {
func (x *ConversationStatus) Reset() { func (x *ConversationStatus) Reset() {
*x = ConversationStatus{} *x = ConversationStatus{}
mi := &file_messages_proto_msgTypes[12] mi := &file_messages_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1134,7 +1203,7 @@ func (x *ConversationStatus) String() string {
func (*ConversationStatus) ProtoMessage() {} func (*ConversationStatus) ProtoMessage() {}
func (x *ConversationStatus) ProtoReflect() protoreflect.Message { func (x *ConversationStatus) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[12] mi := &file_messages_proto_msgTypes[13]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1147,7 +1216,7 @@ func (x *ConversationStatus) ProtoReflect() protoreflect.Message {
// Deprecated: Use ConversationStatus.ProtoReflect.Descriptor instead. // Deprecated: Use ConversationStatus.ProtoReflect.Descriptor instead.
func (*ConversationStatus) Descriptor() ([]byte, []int) { func (*ConversationStatus) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{12} return file_messages_proto_rawDescGZIP(), []int{13}
} }
func (x *ConversationStatus) GetUuid() string { func (x *ConversationStatus) GetUuid() string {
@@ -1223,7 +1292,7 @@ type Reaction struct {
func (x *Reaction) Reset() { func (x *Reaction) Reset() {
*x = Reaction{} *x = Reaction{}
mi := &file_messages_proto_msgTypes[13] mi := &file_messages_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1235,7 +1304,7 @@ func (x *Reaction) String() string {
func (*Reaction) ProtoMessage() {} func (*Reaction) ProtoMessage() {}
func (x *Reaction) ProtoReflect() protoreflect.Message { func (x *Reaction) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[13] mi := &file_messages_proto_msgTypes[14]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1248,7 +1317,7 @@ func (x *Reaction) ProtoReflect() protoreflect.Message {
// Deprecated: Use Reaction.ProtoReflect.Descriptor instead. // Deprecated: Use Reaction.ProtoReflect.Descriptor instead.
func (*Reaction) Descriptor() ([]byte, []int) { func (*Reaction) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{13} return file_messages_proto_rawDescGZIP(), []int{14}
} }
func (x *Reaction) GetReaction() string { func (x *Reaction) GetReaction() string {
@@ -1275,7 +1344,7 @@ type Group struct {
func (x *Group) Reset() { func (x *Group) Reset() {
*x = Group{} *x = Group{}
mi := &file_messages_proto_msgTypes[14] mi := &file_messages_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1287,7 +1356,7 @@ func (x *Group) String() string {
func (*Group) ProtoMessage() {} func (*Group) ProtoMessage() {}
func (x *Group) ProtoReflect() protoreflect.Message { func (x *Group) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[14] mi := &file_messages_proto_msgTypes[15]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1300,7 +1369,7 @@ func (x *Group) ProtoReflect() protoreflect.Message {
// Deprecated: Use Group.ProtoReflect.Descriptor instead. // Deprecated: Use Group.ProtoReflect.Descriptor instead.
func (*Group) Descriptor() ([]byte, []int) { func (*Group) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{14} return file_messages_proto_rawDescGZIP(), []int{15}
} }
func (x *Group) GetName() string { func (x *Group) GetName() string {
@@ -1339,7 +1408,7 @@ type UserMessage struct {
func (x *UserMessage) Reset() { func (x *UserMessage) Reset() {
*x = UserMessage{} *x = UserMessage{}
mi := &file_messages_proto_msgTypes[15] mi := &file_messages_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1351,7 +1420,7 @@ func (x *UserMessage) String() string {
func (*UserMessage) ProtoMessage() {} func (*UserMessage) ProtoMessage() {}
func (x *UserMessage) ProtoReflect() protoreflect.Message { func (x *UserMessage) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[15] mi := &file_messages_proto_msgTypes[16]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1364,7 +1433,7 @@ func (x *UserMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserMessage.ProtoReflect.Descriptor instead. // Deprecated: Use UserMessage.ProtoReflect.Descriptor instead.
func (*UserMessage) Descriptor() ([]byte, []int) { func (*UserMessage) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{15} return file_messages_proto_rawDescGZIP(), []int{16}
} }
func (x *UserMessage) GetDestination() string { func (x *UserMessage) GetDestination() string {
@@ -1470,7 +1539,7 @@ type File struct {
func (x *File) Reset() { func (x *File) Reset() {
*x = File{} *x = File{}
mi := &file_messages_proto_msgTypes[16] mi := &file_messages_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1482,7 +1551,7 @@ func (x *File) String() string {
func (*File) ProtoMessage() {} func (*File) ProtoMessage() {}
func (x *File) ProtoReflect() protoreflect.Message { func (x *File) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[16] mi := &file_messages_proto_msgTypes[17]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1495,7 +1564,7 @@ func (x *File) ProtoReflect() protoreflect.Message {
// Deprecated: Use File.ProtoReflect.Descriptor instead. // Deprecated: Use File.ProtoReflect.Descriptor instead.
func (*File) Descriptor() ([]byte, []int) { func (*File) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{16} return file_messages_proto_rawDescGZIP(), []int{17}
} }
func (x *File) GetFilename() string { func (x *File) GetFilename() string {
@@ -1538,7 +1607,7 @@ type Location struct {
func (x *Location) Reset() { func (x *Location) Reset() {
*x = Location{} *x = Location{}
mi := &file_messages_proto_msgTypes[17] mi := &file_messages_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1550,7 +1619,7 @@ func (x *Location) String() string {
func (*Location) ProtoMessage() {} func (*Location) ProtoMessage() {}
func (x *Location) ProtoReflect() protoreflect.Message { func (x *Location) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[17] mi := &file_messages_proto_msgTypes[18]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1563,7 +1632,7 @@ func (x *Location) ProtoReflect() protoreflect.Message {
// Deprecated: Use Location.ProtoReflect.Descriptor instead. // Deprecated: Use Location.ProtoReflect.Descriptor instead.
func (*Location) Descriptor() ([]byte, []int) { func (*Location) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{17} return file_messages_proto_rawDescGZIP(), []int{18}
} }
func (x *Location) GetTime() uint64 { func (x *Location) GetTime() uint64 {
@@ -1615,7 +1684,7 @@ type DbMessage struct {
func (x *DbMessage) Reset() { func (x *DbMessage) Reset() {
*x = DbMessage{} *x = DbMessage{}
mi := &file_messages_proto_msgTypes[18] mi := &file_messages_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1627,7 +1696,7 @@ func (x *DbMessage) String() string {
func (*DbMessage) ProtoMessage() {} func (*DbMessage) ProtoMessage() {}
func (x *DbMessage) ProtoReflect() protoreflect.Message { func (x *DbMessage) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[18] mi := &file_messages_proto_msgTypes[19]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1640,7 +1709,7 @@ func (x *DbMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use DbMessage.ProtoReflect.Descriptor instead. // Deprecated: Use DbMessage.ProtoReflect.Descriptor instead.
func (*DbMessage) Descriptor() ([]byte, []int) { func (*DbMessage) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{18} return file_messages_proto_rawDescGZIP(), []int{19}
} }
func (x *DbMessage) GetOutbound() bool { func (x *DbMessage) GetOutbound() bool {
@@ -1747,7 +1816,7 @@ type VideoData struct {
func (x *VideoData) Reset() { func (x *VideoData) Reset() {
*x = VideoData{} *x = VideoData{}
mi := &file_messages_proto_msgTypes[19] mi := &file_messages_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1759,7 +1828,7 @@ func (x *VideoData) String() string {
func (*VideoData) ProtoMessage() {} func (*VideoData) ProtoMessage() {}
func (x *VideoData) ProtoReflect() protoreflect.Message { func (x *VideoData) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[19] mi := &file_messages_proto_msgTypes[20]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1772,7 +1841,7 @@ func (x *VideoData) ProtoReflect() protoreflect.Message {
// Deprecated: Use VideoData.ProtoReflect.Descriptor instead. // Deprecated: Use VideoData.ProtoReflect.Descriptor instead.
func (*VideoData) Descriptor() ([]byte, []int) { func (*VideoData) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{19} return file_messages_proto_rawDescGZIP(), []int{20}
} }
func (x *VideoData) GetUrl() string { func (x *VideoData) GetUrl() string {
@@ -1821,7 +1890,7 @@ type VideoCredential struct {
func (x *VideoCredential) Reset() { func (x *VideoCredential) Reset() {
*x = VideoCredential{} *x = VideoCredential{}
mi := &file_messages_proto_msgTypes[20] mi := &file_messages_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1833,7 +1902,7 @@ func (x *VideoCredential) String() string {
func (*VideoCredential) ProtoMessage() {} func (*VideoCredential) ProtoMessage() {}
func (x *VideoCredential) ProtoReflect() protoreflect.Message { func (x *VideoCredential) ProtoReflect() protoreflect.Message {
mi := &file_messages_proto_msgTypes[20] mi := &file_messages_proto_msgTypes[21]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1846,7 +1915,7 @@ func (x *VideoCredential) ProtoReflect() protoreflect.Message {
// Deprecated: Use VideoCredential.ProtoReflect.Descriptor instead. // Deprecated: Use VideoCredential.ProtoReflect.Descriptor instead.
func (*VideoCredential) Descriptor() ([]byte, []int) { func (*VideoCredential) Descriptor() ([]byte, []int) {
return file_messages_proto_rawDescGZIP(), []int{20} return file_messages_proto_rawDescGZIP(), []int{21}
} }
func (x *VideoCredential) GetUsername() string { func (x *VideoCredential) GetUsername() string {
@@ -1878,18 +1947,24 @@ const file_messages_proto_rawDesc = "" +
"\x13PackedServerMessage\x12\x12\n" + "\x13PackedServerMessage\x12\x12\n" +
"\x04from\x18\x01 \x01(\tR\x04from\x12\x18\n" + "\x04from\x18\x01 \x01(\tR\x04from\x12\x18\n" +
"\apayload\x18\x02 \x01(\fR\apayload\x12\x1c\n" + "\apayload\x18\x02 \x01(\fR\apayload\x12\x1c\n" +
"\tsignature\x18\x03 \x01(\fR\tsignature\"\xf2\x01\n" + "\tsignature\x18\x03 \x01(\fR\tsignature\"\xf3\x01\n" +
"\n" + "\n" +
"Invitation\x12\x18\n" + "Invitation\x12\x18\n" +
"\apayload\x18\x01 \x01(\fR\apayload\x12\x18\n" + "\apayload\x18\x01 \x01(\fR\apayload\x12\x18\n" +
"\atimeout\x18\x02 \x01(\x05R\atimeout\x12\"\n" + "\atimeout\x18\x02 \x01(\x05R\atimeout\x12#\n" +
"\fshortcodeLen\x18\x03 \x01(\x05R\fshortcodeLen\x12\x1c\n" + "\rshortcode_len\x18\x03 \x01(\x05R\fshortcodeLen\x12\x1c\n" +
"\tshortcode\x18\x04 \x01(\tR\tshortcode\x12\x1a\n" + "\tshortcode\x18\x04 \x01(\tR\tshortcode\x12\x1a\n" +
"\bpassword\x18\x05 \x01(\tR\bpassword\x12\x12\n" + "\bpassword\x18\x05 \x01(\tR\bpassword\x12\x12\n" +
"\x04uuid\x18\x06 \x01(\tR\x04uuid\x12\x16\n" + "\x04uuid\x18\x06 \x01(\tR\x04uuid\x12\x16\n" +
"\x06expiry\x18\a \x01(\x03R\x06expiry\x12\x12\n" + "\x06expiry\x18\a \x01(\x03R\x06expiry\x12\x12\n" +
"\x04step\x18\b \x01(\x05R\x04step\x12\x12\n" + "\x04step\x18\b \x01(\x05R\x04step\x12\x12\n" +
"\x04from\x18\t \x01(\tR\x04from\"\xb1\x01\n" + "\x04from\x18\t \x01(\tR\x04from\"\x8d\x01\n" +
"\x15InvitationInitPayload\x12\x12\n" +
"\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12\x1d\n" +
"\n" +
"public_key\x18\x03 \x01(\tR\tpublicKey\x12-\n" +
"\x12invitation_message\x18\x04 \x01(\tR\x11invitationMessage\"\xb1\x01\n" +
"\x13ConversationRequest\x12\x1d\n" + "\x13ConversationRequest\x12\x1d\n" +
"\n" + "\n" +
"lookup_key\x18\x01 \x01(\tR\tlookupKey\x12)\n" + "lookup_key\x18\x01 \x01(\tR\tlookupKey\x12)\n" +
@@ -2072,66 +2147,67 @@ func file_messages_proto_rawDescGZIP() []byte {
return file_messages_proto_rawDescData return file_messages_proto_rawDescData
} }
var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 21) var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 22)
var file_messages_proto_goTypes = []any{ var file_messages_proto_goTypes = []any{
(*PackedServerMessage)(nil), // 0: meowlib.PackedServerMessage (*PackedServerMessage)(nil), // 0: meowlib.PackedServerMessage
(*Invitation)(nil), // 1: meowlib.Invitation (*Invitation)(nil), // 1: meowlib.Invitation
(*ConversationRequest)(nil), // 2: meowlib.ConversationRequest (*InvitationInitPayload)(nil), // 2: meowlib.InvitationInitPayload
(*Meet)(nil), // 3: meowlib.Meet (*ConversationRequest)(nil), // 3: meowlib.ConversationRequest
(*Credentials)(nil), // 4: meowlib.Credentials (*Meet)(nil), // 4: meowlib.Meet
(*ToServerMessage)(nil), // 5: meowlib.ToServerMessage (*Credentials)(nil), // 5: meowlib.Credentials
(*FromServerMessage)(nil), // 6: meowlib.FromServerMessage (*ToServerMessage)(nil), // 6: meowlib.ToServerMessage
(*MatriochkaServer)(nil), // 7: meowlib.MatriochkaServer (*FromServerMessage)(nil), // 7: meowlib.FromServerMessage
(*Matriochka)(nil), // 8: meowlib.Matriochka (*MatriochkaServer)(nil), // 8: meowlib.MatriochkaServer
(*ServerCard)(nil), // 9: meowlib.ServerCard (*Matriochka)(nil), // 9: meowlib.Matriochka
(*ContactCard)(nil), // 10: meowlib.ContactCard (*ServerCard)(nil), // 10: meowlib.ServerCard
(*PackedUserMessage)(nil), // 11: meowlib.PackedUserMessage (*ContactCard)(nil), // 11: meowlib.ContactCard
(*ConversationStatus)(nil), // 12: meowlib.ConversationStatus (*PackedUserMessage)(nil), // 12: meowlib.PackedUserMessage
(*Reaction)(nil), // 13: meowlib.Reaction (*ConversationStatus)(nil), // 13: meowlib.ConversationStatus
(*Group)(nil), // 14: meowlib.Group (*Reaction)(nil), // 14: meowlib.Reaction
(*UserMessage)(nil), // 15: meowlib.UserMessage (*Group)(nil), // 15: meowlib.Group
(*File)(nil), // 16: meowlib.File (*UserMessage)(nil), // 16: meowlib.UserMessage
(*Location)(nil), // 17: meowlib.Location (*File)(nil), // 17: meowlib.File
(*DbMessage)(nil), // 18: meowlib.DbMessage (*Location)(nil), // 18: meowlib.Location
(*VideoData)(nil), // 19: meowlib.VideoData (*DbMessage)(nil), // 19: meowlib.DbMessage
(*VideoCredential)(nil), // 20: meowlib.VideoCredential (*VideoData)(nil), // 20: meowlib.VideoData
(*VideoCredential)(nil), // 21: meowlib.VideoCredential
} }
var file_messages_proto_depIdxs = []int32{ var file_messages_proto_depIdxs = []int32{
10, // 0: meowlib.Meet.contact_card:type_name -> meowlib.ContactCard 11, // 0: meowlib.Meet.contact_card:type_name -> meowlib.ContactCard
2, // 1: meowlib.ToServerMessage.pull_request:type_name -> meowlib.ConversationRequest 3, // 1: meowlib.ToServerMessage.pull_request:type_name -> meowlib.ConversationRequest
11, // 2: meowlib.ToServerMessage.messages:type_name -> meowlib.PackedUserMessage 12, // 2: meowlib.ToServerMessage.messages:type_name -> meowlib.PackedUserMessage
9, // 3: meowlib.ToServerMessage.known_servers:type_name -> meowlib.ServerCard 10, // 3: meowlib.ToServerMessage.known_servers:type_name -> meowlib.ServerCard
8, // 4: meowlib.ToServerMessage.matriochka_message:type_name -> meowlib.Matriochka 9, // 4: meowlib.ToServerMessage.matriochka_message:type_name -> meowlib.Matriochka
1, // 5: meowlib.ToServerMessage.invitation:type_name -> meowlib.Invitation 1, // 5: meowlib.ToServerMessage.invitation:type_name -> meowlib.Invitation
11, // 6: meowlib.ToServerMessage.device_messages:type_name -> meowlib.PackedUserMessage 12, // 6: meowlib.ToServerMessage.device_messages:type_name -> meowlib.PackedUserMessage
19, // 7: meowlib.ToServerMessage.video_data:type_name -> meowlib.VideoData 20, // 7: meowlib.ToServerMessage.video_data:type_name -> meowlib.VideoData
4, // 8: meowlib.ToServerMessage.credentials:type_name -> meowlib.Credentials 5, // 8: meowlib.ToServerMessage.credentials:type_name -> meowlib.Credentials
11, // 9: meowlib.FromServerMessage.chat:type_name -> meowlib.PackedUserMessage 12, // 9: meowlib.FromServerMessage.chat:type_name -> meowlib.PackedUserMessage
9, // 10: meowlib.FromServerMessage.known_servers:type_name -> meowlib.ServerCard 10, // 10: meowlib.FromServerMessage.known_servers:type_name -> meowlib.ServerCard
1, // 11: meowlib.FromServerMessage.invitation:type_name -> meowlib.Invitation 1, // 11: meowlib.FromServerMessage.invitation:type_name -> meowlib.Invitation
11, // 12: meowlib.FromServerMessage.device_messages:type_name -> meowlib.PackedUserMessage 12, // 12: meowlib.FromServerMessage.device_messages:type_name -> meowlib.PackedUserMessage
19, // 13: meowlib.FromServerMessage.video_data:type_name -> meowlib.VideoData 20, // 13: meowlib.FromServerMessage.video_data:type_name -> meowlib.VideoData
10, // 14: meowlib.FromServerMessage.contact_card:type_name -> meowlib.ContactCard 11, // 14: meowlib.FromServerMessage.contact_card:type_name -> meowlib.ContactCard
7, // 15: meowlib.Matriochka.prev:type_name -> meowlib.MatriochkaServer 8, // 15: meowlib.Matriochka.prev:type_name -> meowlib.MatriochkaServer
7, // 16: meowlib.Matriochka.next:type_name -> meowlib.MatriochkaServer 8, // 16: meowlib.Matriochka.next:type_name -> meowlib.MatriochkaServer
9, // 17: meowlib.ContactCard.pull_servers:type_name -> meowlib.ServerCard 10, // 17: meowlib.ContactCard.pull_servers:type_name -> meowlib.ServerCard
13, // 18: meowlib.ConversationStatus.reactions:type_name -> meowlib.Reaction 14, // 18: meowlib.ConversationStatus.reactions:type_name -> meowlib.Reaction
10, // 19: meowlib.ConversationStatus.my_next_identity:type_name -> meowlib.ContactCard 11, // 19: meowlib.ConversationStatus.my_next_identity:type_name -> meowlib.ContactCard
10, // 20: meowlib.Group.members:type_name -> meowlib.ContactCard 11, // 20: meowlib.Group.members:type_name -> meowlib.ContactCard
12, // 21: meowlib.UserMessage.status:type_name -> meowlib.ConversationStatus 13, // 21: meowlib.UserMessage.status:type_name -> meowlib.ConversationStatus
10, // 22: meowlib.UserMessage.contact:type_name -> meowlib.ContactCard 11, // 22: meowlib.UserMessage.contact:type_name -> meowlib.ContactCard
9, // 23: meowlib.UserMessage.known_servers:type_name -> meowlib.ServerCard 10, // 23: meowlib.UserMessage.known_servers:type_name -> meowlib.ServerCard
14, // 24: meowlib.UserMessage.group:type_name -> meowlib.Group 15, // 24: meowlib.UserMessage.group:type_name -> meowlib.Group
16, // 25: meowlib.UserMessage.files:type_name -> meowlib.File 17, // 25: meowlib.UserMessage.files:type_name -> meowlib.File
17, // 26: meowlib.UserMessage.current_location:type_name -> meowlib.Location 18, // 26: meowlib.UserMessage.current_location:type_name -> meowlib.Location
1, // 27: meowlib.UserMessage.invitation:type_name -> meowlib.Invitation 1, // 27: meowlib.UserMessage.invitation:type_name -> meowlib.Invitation
19, // 28: meowlib.UserMessage.video_data:type_name -> meowlib.VideoData 20, // 28: meowlib.UserMessage.video_data:type_name -> meowlib.VideoData
12, // 29: meowlib.DbMessage.status:type_name -> meowlib.ConversationStatus 13, // 29: meowlib.DbMessage.status:type_name -> meowlib.ConversationStatus
10, // 30: meowlib.DbMessage.contact:type_name -> meowlib.ContactCard 11, // 30: meowlib.DbMessage.contact:type_name -> meowlib.ContactCard
14, // 31: meowlib.DbMessage.group:type_name -> meowlib.Group 15, // 31: meowlib.DbMessage.group:type_name -> meowlib.Group
17, // 32: meowlib.DbMessage.current_location:type_name -> meowlib.Location 18, // 32: meowlib.DbMessage.current_location:type_name -> meowlib.Location
1, // 33: meowlib.DbMessage.invitation:type_name -> meowlib.Invitation 1, // 33: meowlib.DbMessage.invitation:type_name -> meowlib.Invitation
20, // 34: meowlib.VideoData.credentials:type_name -> meowlib.VideoCredential 21, // 34: meowlib.VideoData.credentials:type_name -> meowlib.VideoCredential
35, // [35:35] is the sub-list for method output_type 35, // [35:35] is the sub-list for method output_type
35, // [35:35] is the sub-list for method input_type 35, // [35:35] is the sub-list for method input_type
35, // [35:35] is the sub-list for extension type_name 35, // [35:35] is the sub-list for extension type_name
@@ -2150,7 +2226,7 @@ func file_messages_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_messages_proto_rawDesc), len(file_messages_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_messages_proto_rawDesc), len(file_messages_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 21, NumMessages: 22,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View File

@@ -30,7 +30,15 @@ message Invitation {
string uuid = 6; // invitation uuid string uuid = 6; // invitation uuid
int64 expiry = 7; // the server allowed expiry date, it may be smaller than the requested timeout according to server policy int64 expiry = 7; // the server allowed expiry date, it may be smaller than the requested timeout according to server policy
int32 step = 8; // progress in the invitation process : 1=initiator pub key, 2=invited data enc with pub key, 3=initator data full encrypted, 4=invited All OK ! int32 step = 8; // progress in the invitation process : 1=initiator pub key, 2=invited data enc with pub key, 3=initator data full encrypted, 4=invited All OK !
string from = 9; // used in step 1 the public key to encrypt step 2 message string from = 9; // still useful ?
}
// This payload migh be used for file serialization as well as Invitation/payload in case of through server invitation
message InvitationInitPayload {
string uuid = 1; // uuid of the invitation, it is set here on init cause the payload might be encrypted
string name = 2; // name of the initiator
string public_key = 3; // public key to be used for step 2 encryption
string invitation_message = 4; // message for the invited peer
} }
// structure for requesting incoming messages // structure for requesting incoming messages