305 lines
10 KiB
Go
305 lines
10 KiB
Go
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"forge.redroom.link/yves/meowlib"
|
|
"github.com/google/uuid"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// Server manages server related operations
|
|
// - Sending messages for server usage
|
|
// - Two first steps of an invitation
|
|
// - User message sending with UserKp identification
|
|
// - Messages lookup requests
|
|
// - Utility functions for packing/unpacking, encrypting/decrypting messages for server communication
|
|
// - Server remote management if ManagerKp is available for that server
|
|
type Server struct {
|
|
//ServerCard meowlib.ServerCard `json:"server_data,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
PublicKey string `json:"public_key,omitempty"`
|
|
Url string `json:"url,omitempty"`
|
|
Login string `json:"login,omitempty"`
|
|
Password string `json:"password,omitempty"`
|
|
Presence bool `json:"presence,omitempty"`
|
|
LastCheck time.Time `json:"last_check,omitempty"`
|
|
Uptime time.Duration `json:"uptime,omitempty"`
|
|
UserKp meowlib.KeyPair `json:"user_kp,omitempty"`
|
|
ManagerKp meowlib.KeyPair `json:"manager_kp,omitempty"`
|
|
Country string `json:"country,omitempty"`
|
|
AllowedDelay int `json:"allowed_delay,omitempty"`
|
|
Backup bool `json:"backup,omitempty"`
|
|
WebRTC bool `json:"webrtc,omitempty"`
|
|
}
|
|
|
|
// CreateServerFromUrl creates a server from a basic url, ex : https://my.meowserver.example:8443/meow/
|
|
func CreateServerFromUrl(url string) *Server {
|
|
var is Server
|
|
is.Url = url
|
|
return &is
|
|
}
|
|
|
|
// CreateServerFromUid creates a server from a uid string, ex : mylogin:mypassword@https://my.meowserver.example:8443/meow/
|
|
func CreateServerFromUid(uid string) *Server {
|
|
var is Server
|
|
uidTable := strings.Split(uid, "@") //! Weak test, use regexp
|
|
if len(uidTable) == 2 {
|
|
loginpw := strings.Split(uidTable[0], ":")
|
|
is.Url = uidTable[1]
|
|
is.Login = loginpw[0]
|
|
is.Password = loginpw[1]
|
|
} else {
|
|
is.Url = uidTable[0]
|
|
}
|
|
return &is
|
|
}
|
|
|
|
// CreateServerFromMeowUrl creates a server from a meow url, ex : meow://mylogin:mypassword@https://my.meowserver.example:8443/meow/
|
|
func CreateServerFromMeowUrl(meowurl string) *Server {
|
|
uid := strings.Replace(meowurl[7:], "//", "://", 1)
|
|
return CreateServerFromUid(uid)
|
|
}
|
|
|
|
// CreateServerFromInvitationLink creates a server from a meow url, ex : meow://mylogin:mypassword@https://my.meowserver.example:8443/meow?invitationCode
|
|
func CreateServerFromInvitationLink(meowurl string) *Server {
|
|
// remove the invitation code, last token after a /
|
|
meowurlTable := strings.Split(meowurl, "?")
|
|
// join all elements with / except the last one
|
|
meowSrvUrl := meowurlTable[0]
|
|
return CreateServerFromMeowUrl(meowSrvUrl)
|
|
}
|
|
|
|
// GetServerCard returns a server card from a server
|
|
func (ints *Server) GetServerCard() *meowlib.ServerCard {
|
|
var sc meowlib.ServerCard
|
|
sc.Name = ints.Name
|
|
sc.PublicKey = ints.PublicKey
|
|
sc.Description = ints.Description
|
|
sc.Url = ints.Url
|
|
sc.Login = ints.Login
|
|
sc.Password = ints.Password
|
|
return &sc
|
|
}
|
|
|
|
func (sc *Server) GetUid() string {
|
|
if len(sc.Login) > 0 || len(sc.Password) > 0 {
|
|
return sc.Login + ":" + sc.Password + "@" + sc.Url
|
|
}
|
|
return sc.Url
|
|
}
|
|
|
|
func (sc *Server) GetMeowUrl() string {
|
|
if len(sc.Login) > 0 || len(sc.Password) > 0 {
|
|
return sc.Login + ":" + sc.Password + "@" + sc.Url
|
|
}
|
|
return "meow://" + sc.Url
|
|
}
|
|
|
|
// Create a server from a server card
|
|
func CreateServerFromServerCard(server *meowlib.ServerCard) *Server {
|
|
var is Server
|
|
is.Name = server.Name
|
|
is.PublicKey = server.PublicKey
|
|
is.Description = server.Description
|
|
is.Url = server.Url
|
|
is.Login = server.Login
|
|
is.Password = server.Password
|
|
is.UserKp = meowlib.NewKeyPair()
|
|
return &is
|
|
}
|
|
|
|
// AsymEncryptMessage prepares a message to send to a specific internal server
|
|
func (ints *Server) AsymEncryptMessage(Message []byte) (*meowlib.EncryptedMessage, error) {
|
|
var enc *meowlib.EncryptedMessage
|
|
enc, err := meowlib.AsymEncryptAndSign(ints.PublicKey, ints.UserKp.Private, Message)
|
|
if err != nil {
|
|
fmt.Println(err.Error())
|
|
return nil, err
|
|
}
|
|
return enc, err
|
|
}
|
|
|
|
// AsymDecryptMessage reads a message from a specific internal server
|
|
func (ints *Server) AsymDecryptMessage(Message []byte, Signature []byte) (DecryptedMessage []byte, err error) {
|
|
DecryptedMessage, err = meowlib.AsymDecryptAndCheck(ints.UserKp.Private, ints.PublicKey, Message, Signature)
|
|
if err != nil {
|
|
fmt.Println(err.Error())
|
|
return nil, err
|
|
}
|
|
return DecryptedMessage, err
|
|
}
|
|
|
|
// BuildToServerMessageFromUserMessage creates a basic message to server from a single packed user message and returns a meowlib.ToServerMessage
|
|
func (ints *Server) BuildToServerMessageFromUserMessage(usermsg *meowlib.PackedUserMessage) *meowlib.ToServerMessage {
|
|
var msg meowlib.ToServerMessage
|
|
msg.Uuid = uuid.New().String()
|
|
msg.Type = "1"
|
|
msg.From = ints.UserKp.Public
|
|
msg.Messages = append(msg.Messages, usermsg)
|
|
return &msg
|
|
}
|
|
|
|
// BuildMessageSendingMessage creates a basic message to server from a single packed user message and returns it as protobuf serialized byte array
|
|
func (ints *Server) BuildMessageSendingMessage(usermsg *meowlib.PackedUserMessage) ([]byte, error) {
|
|
msg := ints.BuildToServerMessageFromUserMessage(usermsg)
|
|
out, err := proto.Marshal(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// ! Unfinished unused ?
|
|
// BuildMessageRequestMessage creates a message lookup message to server and returns it as protobuf serialized byte array
|
|
func (ints *Server) BuildMessageRequestMessage(lookupKeys []string) ([]byte, error) {
|
|
var msg meowlib.ToServerMessage
|
|
msg.Uuid = uuid.New().String()
|
|
msg.Type = "1"
|
|
msg.From = ints.UserKp.Public
|
|
out, err := proto.Marshal(&msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// BuildVideoRoomRequestMessage creates a video room request to server and returns it as protobuf serialized byte array
|
|
func (ints *Server) BuildVideoRoomRequestMessage(users []string, expiry uint64) (*meowlib.ToServerMessage, error) {
|
|
var msg meowlib.ToServerMessage
|
|
msg.Uuid = uuid.New().String()
|
|
msg.Type = "1"
|
|
msg.From = ints.UserKp.Public
|
|
// declare an array of meow.VideoCredential
|
|
videocreds := make([]*meowlib.VideoCredential, len(users))
|
|
for idx := range users {
|
|
videocreds[idx] = &meowlib.VideoCredential{
|
|
Username: users[idx],
|
|
}
|
|
}
|
|
msg.VideoData.Credentials = videocreds
|
|
return &msg, nil
|
|
}
|
|
|
|
// BuildToServerMessageInvitation creates an invitation message to server and returns it as a meowlib.ToServerMessage
|
|
// it takes as input a contactcard generated by Identity.InvitePeer
|
|
func (ints *Server) BuildToServerMessageInvitationCreation(invitation *meowlib.ContactCard, password string, timeout int, shortCodeLen int) (*meowlib.ToServerMessage, error) {
|
|
var msg meowlib.ToServerMessage
|
|
var inv meowlib.Invitation
|
|
payload, err := invitation.Compress()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
msg.Type = "1"
|
|
msg.From = ints.UserKp.Public
|
|
inv.Step = 1
|
|
inv.Password = password
|
|
inv.Timeout = int32(timeout)
|
|
inv.ShortcodeLen = int32(shortCodeLen)
|
|
inv.Payload = payload
|
|
msg.Invitation = &inv
|
|
return &msg, nil
|
|
}
|
|
|
|
// BuildToServerMessageInvitationRequest requests invitation with provided id from server and returns it as a meowlib.ToServerMessage
|
|
func (ints *Server) BuildToServerMessageInvitationRequest(shortcode string, password string) (*meowlib.ToServerMessage, error) {
|
|
var msg meowlib.ToServerMessage
|
|
var inv meowlib.Invitation
|
|
msg.Type = "1"
|
|
msg.From = ints.UserKp.Public
|
|
inv.Step = 2
|
|
inv.Password = password
|
|
inv.Shortcode = shortcode
|
|
msg.Invitation = &inv
|
|
return &msg, nil
|
|
}
|
|
|
|
// BuildToServerMessageInvitationAnswer creates an invitation answer to server and returns it as a meowlib.ToServerMessage
|
|
// it takes as input a contactcard generated by Identity.InvitePeer
|
|
func (ints *Server) BuildToServerMessageInvitationAnswer(invitationAnswer *meowlib.PackedUserMessage, myPublicKeyForThatPeer string, invitation_id string, timeout int) (*meowlib.ToServerMessage, error) {
|
|
var msg meowlib.ToServerMessage
|
|
var inv meowlib.Invitation
|
|
invitationPayload, err := proto.Marshal(invitationAnswer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
inv.Step = 3
|
|
inv.Uuid = invitation_id
|
|
msg.Type = "1"
|
|
msg.From = ints.UserKp.Public
|
|
inv.From = myPublicKeyForThatPeer
|
|
inv.Payload = invitationPayload
|
|
msg.Invitation = &inv
|
|
return &msg, nil
|
|
}
|
|
|
|
// BuildToServerMessageInvitationAnswerRequest requests invitation answer with provided id from server and returns it as a meowlib.ToServerMessage
|
|
func (ints *Server) BuildToServerMessageInvitationAnswerRequest(invitationId string) (*meowlib.ToServerMessage, error) {
|
|
var msg meowlib.ToServerMessage
|
|
var inv meowlib.Invitation
|
|
msg.Type = "1"
|
|
msg.From = ints.UserKp.Public
|
|
inv.Step = 4
|
|
inv.Uuid = invitationId
|
|
msg.Invitation = &inv
|
|
return &msg, nil
|
|
}
|
|
|
|
// PackServerMessage
|
|
func (ints *Server) PackServerMessage(payload []byte, signature []byte) (protoPackedMessage []byte, err error) {
|
|
var msg meowlib.PackedServerMessage
|
|
msg.From = ints.UserKp.Public
|
|
msg.Payload = payload
|
|
msg.Signature = signature
|
|
out, err := proto.Marshal(&msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (ints *Server) UnPackServerMessage(protoPackedMessage []byte) (payload []byte, signature []byte, err error) {
|
|
msg := &meowlib.PackedServerMessage{}
|
|
if err := proto.Unmarshal(protoPackedMessage, msg); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return msg.Payload, msg.Signature, nil
|
|
}
|
|
|
|
func (srv *Server) ProcessOutboundMessage(toServerMessage *meowlib.ToServerMessage) ([]byte, error) {
|
|
byteToServerMessage, err := proto.Marshal(toServerMessage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Encrypting it
|
|
encToServer, err := srv.AsymEncryptMessage(byteToServerMessage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Packing it
|
|
protoPackedServerMsg, err := srv.PackServerMessage(encToServer.Data, encToServer.Signature)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return protoPackedServerMsg, nil
|
|
}
|
|
|
|
func (srv *Server) ProcessInboundServerResponse(msg []byte) (*meowlib.FromServerMessage, error) {
|
|
fsmsg := &meowlib.FromServerMessage{}
|
|
payload, signature, err := srv.UnPackServerMessage(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
decrypted, err := srv.AsymDecryptMessage(payload, signature)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := proto.Unmarshal(decrypted, fsmsg); err != nil {
|
|
return nil, err
|
|
}
|
|
return fsmsg, nil
|
|
}
|