This commit is contained in:
+91
-108
@@ -15,8 +15,8 @@ import (
|
|||||||
"forge.redroom.link/yves/meowlib/client"
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
invmsgs "forge.redroom.link/yves/meowlib/client/invitation/messages"
|
invmsgs "forge.redroom.link/yves/meowlib/client/invitation/messages"
|
||||||
invsrv "forge.redroom.link/yves/meowlib/client/invitation/server"
|
invsrv "forge.redroom.link/yves/meowlib/client/invitation/server"
|
||||||
doubleratchet "github.com/status-im/doubleratchet"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
doubleratchet "github.com/status-im/doubleratchet"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ func PollServer(storage_path string, job *client.RequestsJob, timeout int, longP
|
|||||||
// SaveCheckJobs
|
// SaveCheckJobs
|
||||||
func SaveCheckJobs() (string, error) {
|
func SaveCheckJobs() (string, error) {
|
||||||
me := client.GetConfig().GetIdentity()
|
me := client.GetConfig().GetIdentity()
|
||||||
err := me.SaveBackgroundJob()
|
err := me.SaveCheckJobs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
return "CheckMessages: json.Marshal", err
|
return "CheckMessages: json.Marshal", err
|
||||||
@@ -174,7 +174,8 @@ func ConsumeInboxFile(messageFilename string) ([]string, []string, string, error
|
|||||||
// find the peer with that lookup key
|
// find the peer with that lookup key
|
||||||
peer := identity.Peers.GetFromMyLookupKey(packedUserMessage.Destination)
|
peer := identity.Peers.GetFromMyLookupKey(packedUserMessage.Destination)
|
||||||
if peer == nil {
|
if peer == nil {
|
||||||
return nil, nil, "ReadMessage: GetFromMyLookupKey", errors.New("no visible peer for that message")
|
logger.Error().Str("destination", packedUserMessage.Destination).Msg("ConsumeInboxFile: no visible peer for that message, skipping")
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
// Unpack the message — step-3 messages arrive before the initiator's identity
|
// Unpack the message — step-3 messages arrive before the initiator's identity
|
||||||
// key is known, so skip signature verification for pending peers.
|
// key is known, so skip signature verification for pending peers.
|
||||||
@@ -185,100 +186,103 @@ func ConsumeInboxFile(messageFilename string) ([]string, []string, string, error
|
|||||||
usermsg, err = peer.ProcessInboundUserMessage(packedUserMessage)
|
usermsg, err = peer.ProcessInboundUserMessage(packedUserMessage)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "ReadMessage: ProcessInboundUserMessage", err
|
//return nil, nil, "ReadMessage: ProcessInboundUserMessage", err
|
||||||
}
|
logger.Error().Msg("ReadMessage: ProcessInboundUserMessage" + err.Error())
|
||||||
|
} else {
|
||||||
|
|
||||||
// Handle invitation step 3: initiator's full ContactCard arriving at the invitee.
|
// Handle invitation step 3: initiator's full ContactCard arriving at the invitee.
|
||||||
if usermsg.Invitation != nil && usermsg.Invitation.Step == 3 {
|
if usermsg.Invitation != nil && usermsg.Invitation.Step == 3 {
|
||||||
invBytes, marshalErr := proto.Marshal(usermsg.Invitation)
|
invBytes, marshalErr := proto.Marshal(usermsg.Invitation)
|
||||||
if marshalErr == nil {
|
if marshalErr == nil {
|
||||||
finalizedPeer, finalErr := invmsgs.Step4InviteeFinalizesInitiator(invBytes)
|
finalizedPeer, finalErr := invmsgs.Step4InviteeFinalizesInitiator(invBytes)
|
||||||
if finalErr == nil && finalizedPeer != nil {
|
if finalErr == nil && finalizedPeer != nil {
|
||||||
// Auto-send step-4 confirmation to initiator's servers.
|
// Auto-send step-4 confirmation to initiator's servers.
|
||||||
step4msgs, sendErr := invsrv.Step4PostConfirmation(finalizedPeer.InvitationId)
|
step4msgs, sendErr := invsrv.Step4PostConfirmation(finalizedPeer.InvitationId)
|
||||||
if sendErr == nil {
|
if sendErr == nil {
|
||||||
for i, bytemsg := range step4msgs {
|
for i, bytemsg := range step4msgs {
|
||||||
if i < len(finalizedPeer.ContactPullServers) {
|
if i < len(finalizedPeer.ContactPullServers) {
|
||||||
meowlib.HttpPostMessage(finalizedPeer.ContactPullServers[i], bytemsg, client.GetConfig().HttpTimeOut)
|
meowlib.HttpPostMessage(finalizedPeer.ContactPullServers[i], bytemsg, client.GetConfig().HttpTimeOut)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle invitation step 4: invitee's confirmation arriving at the initiator.
|
// Handle invitation step 4: invitee's confirmation arriving at the initiator.
|
||||||
if usermsg.Invitation != nil && usermsg.Invitation.Step == 4 {
|
if usermsg.Invitation != nil && usermsg.Invitation.Step == 4 {
|
||||||
// Contact is fully active — nothing more to do on the initiator side.
|
// Contact is fully active — nothing more to do on the initiator side.
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
|
|
||||||
// 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 != "" &&
|
|
||||||
(usermsg.Status.Received != 0 || usermsg.Status.Processed != 0) {
|
|
||||||
password, _ := client.GetConfig().GetMemPass()
|
|
||||||
if ackErr := client.UpdateMessageAck(peer, usermsg.Status.Uuid, usermsg.Status.Received, usermsg.Status.Processed, password); ackErr != nil {
|
|
||||||
logger.Warn().Err(ackErr).Str("uuid", usermsg.Status.Uuid).Msg("ConsumeInboxFile: UpdateMessageAck")
|
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//fmt.Println("From:", usermsg.From)
|
// Check for received or processed already filled => it's an ack for one of our sent messages
|
||||||
//jsonUserMessage, _ := json.Marshal(usermsg)
|
if len(usermsg.Data) == 0 && usermsg.Status != nil && usermsg.Status.Uuid != "" &&
|
||||||
//fmt.Println(string(jsonUserMessage))
|
(usermsg.Status.Received != 0 || usermsg.Status.Processed != 0) {
|
||||||
//peer = client.GetConfig().GetIdentity().Peers.GetFromPublicKey(usermsg.From)
|
password, _ := client.GetConfig().GetMemPass()
|
||||||
|
if ackErr := client.UpdateMessageAck(peer, usermsg.Status.Uuid, usermsg.Status.Received, usermsg.Status.Processed, password); ackErr != nil {
|
||||||
|
logger.Warn().Err(ackErr).Str("uuid", usermsg.Status.Uuid).Msg("ConsumeInboxFile: UpdateMessageAck")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// detach files
|
//fmt.Println("From:", usermsg.From)
|
||||||
if usermsg.Files != nil {
|
//jsonUserMessage, _ := json.Marshal(usermsg)
|
||||||
// create files folder
|
//fmt.Println(string(jsonUserMessage))
|
||||||
if _, err := os.Stat(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files")); os.IsNotExist(err) {
|
//peer = client.GetConfig().GetIdentity().Peers.GetFromPublicKey(usermsg.From)
|
||||||
err = os.MkdirAll(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files"), 0700)
|
|
||||||
if err != nil {
|
// detach files
|
||||||
return nil, nil, "ReadMessage: MkdirAll", err
|
if usermsg.Files != nil {
|
||||||
|
// create files folder
|
||||||
|
if _, err := os.Stat(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files")); os.IsNotExist(err) {
|
||||||
|
err = os.MkdirAll(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files"), 0700)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "ReadMessage: MkdirAll", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, file := range usermsg.Files {
|
||||||
|
filename := uuid.New().String() + "_" + file.Filename
|
||||||
|
filenames = append(filenames, peer.Name+" sent: "+filename)
|
||||||
|
// detach file
|
||||||
|
os.WriteFile(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files", filename), file.Data, 0600)
|
||||||
|
}
|
||||||
|
//? result["invitation finalized"] = peer.Name
|
||||||
|
}
|
||||||
|
// user message
|
||||||
|
|
||||||
|
messagesOverview = append(messagesOverview, peer.Name+" > "+string(usermsg.Data))
|
||||||
|
|
||||||
|
// stamp the received time before storing
|
||||||
|
receivedAt := time.Now().UTC().Unix()
|
||||||
|
if usermsg.Status == nil {
|
||||||
|
usermsg.Status = &meowlib.ConversationStatus{}
|
||||||
|
}
|
||||||
|
usermsg.Status.Received = uint64(receivedAt)
|
||||||
|
|
||||||
|
// add message to storage
|
||||||
|
err = peer.StoreMessage(usermsg, filenames)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "ReadMessage: StoreMessage", err
|
||||||
|
}
|
||||||
|
filenames = []string{}
|
||||||
|
|
||||||
|
// Persist peer to save updated DR state (DrStateJson)
|
||||||
|
if peer.DrRootKey != "" {
|
||||||
|
if storeErr := identity.Peers.StorePeer(peer); storeErr != nil {
|
||||||
|
logger.Warn().Err(storeErr).Str("peer", peer.Uid).Msg("ConsumeInboxFile: StorePeer (DR state)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, file := range usermsg.Files {
|
|
||||||
filename := uuid.New().String() + "_" + file.Filename
|
|
||||||
filenames = append(filenames, peer.Name+" sent: "+filename)
|
|
||||||
// detach file
|
|
||||||
os.WriteFile(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files", filename), file.Data, 0600)
|
|
||||||
}
|
|
||||||
//? result["invitation finalized"] = peer.Name
|
|
||||||
}
|
|
||||||
// user message
|
|
||||||
|
|
||||||
messagesOverview = append(messagesOverview, peer.Name+" > "+string(usermsg.Data))
|
// Send delivery ack if the peer requested it
|
||||||
|
if peer.SendDeliveryAck && usermsg.Status.Uuid != "" {
|
||||||
// stamp the received time before storing
|
storagePath := filepath.Join(client.GetConfig().StoragePath, identity.Uuid)
|
||||||
receivedAt := time.Now().UTC().Unix()
|
if ackErr := sendDeliveryAck(storagePath, peer, usermsg.Status.Uuid, receivedAt); ackErr != nil {
|
||||||
if usermsg.Status == nil {
|
logger.Warn().Err(ackErr).Str("peer", peer.Uid).Msg("ConsumeInboxFile: sendDeliveryAck")
|
||||||
usermsg.Status = &meowlib.ConversationStatus{}
|
}
|
||||||
}
|
|
||||||
usermsg.Status.Received = uint64(receivedAt)
|
|
||||||
|
|
||||||
// add message to storage
|
|
||||||
err = peer.StoreMessage(usermsg, filenames)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, "ReadMessage: StoreMessage", err
|
|
||||||
}
|
|
||||||
filenames = []string{}
|
|
||||||
|
|
||||||
// Persist peer to save updated DR state (DrStateJson)
|
|
||||||
if peer.DrRootKey != "" {
|
|
||||||
if storeErr := identity.Peers.StorePeer(peer); storeErr != nil {
|
|
||||||
logger.Warn().Err(storeErr).Str("peer", peer.Uid).Msg("ConsumeInboxFile: StorePeer (DR state)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send delivery ack if the peer requested it
|
|
||||||
if peer.SendDeliveryAck && usermsg.Status.Uuid != "" {
|
|
||||||
storagePath := filepath.Join(client.GetConfig().StoragePath, identity.Uuid)
|
|
||||||
if ackErr := sendDeliveryAck(storagePath, peer, usermsg.Status.Uuid, receivedAt); ackErr != nil {
|
|
||||||
logger.Warn().Err(ackErr).Str("peer", peer.Uid).Msg("ConsumeInboxFile: sendDeliveryAck")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.Remove(messageFilename)
|
err = os.Remove(messageFilename)
|
||||||
@@ -290,54 +294,33 @@ func ConsumeInboxFile(messageFilename string) ([]string, []string, string, error
|
|||||||
return messagesOverview, filenames, "", nil
|
return messagesOverview, filenames, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LongPollAllSerevrJobs checks for messages on a all servers defived in job file
|
// LongPollAllServerJobs checks for messages on all servers defined in job file.
|
||||||
|
// It returns as soon as any server delivers at least one message, or 0 when all
|
||||||
|
// polls time out. resultChan is buffered so goroutines never block on write.
|
||||||
func LongPollAllServerJobs(storage_path string, jobs []client.RequestsJob, timeout int, longPoll bool) (int, string, error) {
|
func LongPollAllServerJobs(storage_path string, jobs []client.RequestsJob, timeout int, longPoll bool) (int, string, error) {
|
||||||
|
|
||||||
// Channel to collect results
|
|
||||||
resultChan := make(chan int, len(jobs))
|
resultChan := make(chan int, len(jobs))
|
||||||
errChan := make(chan error, len(jobs))
|
|
||||||
|
|
||||||
// WaitGroup to sync goroutines
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
// Loop through each job (server)
|
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
go func(job client.RequestsJob) {
|
go func(job client.RequestsJob) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
// Long-polling call to the server
|
|
||||||
cnt, _, err := PollServer(storage_path, &job, timeout, true)
|
cnt, _, err := PollServer(storage_path, &job, timeout, true)
|
||||||
|
|
||||||
if err == nil && cnt > 0 {
|
if err == nil && cnt > 0 {
|
||||||
select {
|
resultChan <- cnt
|
||||||
case resultChan <- cnt:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the error channel to notify all goroutines
|
|
||||||
close(errChan)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}(job)
|
}(job)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the result channel when all workers are done
|
|
||||||
go func() {
|
go func() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(resultChan)
|
close(resultChan)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Wait for the first message or all timeouts
|
if cnt, ok := <-resultChan; ok {
|
||||||
select {
|
|
||||||
case cnt := <-resultChan:
|
|
||||||
return cnt, "", nil
|
return cnt, "", nil
|
||||||
case <-errChan:
|
|
||||||
// If one fails and exitOnMessage is true
|
|
||||||
return 0, "", nil
|
|
||||||
}
|
}
|
||||||
|
return 0, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendDeliveryAck builds a delivery acknowledgment for messageUuid and enqueues
|
// sendDeliveryAck builds a delivery acknowledgment for messageUuid and enqueues
|
||||||
|
|||||||
+1
-1
@@ -425,7 +425,7 @@ func (id *Identity) GetRequestJobs() []RequestsJob {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
func (id *Identity) SaveBackgroundJob() error {
|
func (id *Identity) SaveCheckJobs() error {
|
||||||
if id.RootKp == nil {
|
if id.RootKp == nil {
|
||||||
return errors.New("identity not fully initialized: RootKp is nil")
|
return errors.New("identity not fully initialized: RootKp is nil")
|
||||||
}
|
}
|
||||||
|
|||||||
+223
-245
@@ -6,6 +6,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"forge.redroom.link/yves/meowlib"
|
"forge.redroom.link/yves/meowlib"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -13,71 +14,82 @@ import (
|
|||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func storeMessage(peer *Peer, usermessage *meowlib.UserMessage, filenames []string, password string) error {
|
// One RWMutex per SQLite file path. Entries are never deleted (bounded by
|
||||||
var dbid string
|
// peer count, which is small). RLock for reads, Lock for writes.
|
||||||
cfg := GetConfig()
|
var dbFileMu sync.Map
|
||||||
identity := cfg.GetIdentity()
|
|
||||||
// If no db/no ID create DB + Tablz
|
|
||||||
// TODO : if file size > X new db
|
|
||||||
if len(peer.DbIds) == 0 {
|
|
||||||
dbid = uuid.NewString()
|
|
||||||
peer.DbIds = []string{dbid}
|
|
||||||
|
|
||||||
identity.Peers.StorePeer(peer)
|
func getDbFileMutex(path string) *sync.RWMutex {
|
||||||
identity.CreateFolder()
|
v, _ := dbFileMu.LoadOrStore(path, &sync.RWMutex{})
|
||||||
file, err := os.Create(filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix))
|
return v.(*sync.RWMutex)
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
func withDbWrite(path string, fn func(*sql.DB) error) error {
|
||||||
file.Close()
|
mu := getDbFileMutex(path)
|
||||||
sqliteDatabase, err := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix))
|
mu.Lock()
|
||||||
if err != nil {
|
defer mu.Unlock()
|
||||||
return err
|
db, err := sql.Open("sqlite3", path)
|
||||||
}
|
|
||||||
defer sqliteDatabase.Close()
|
|
||||||
err = createMessageTable(sqliteDatabase)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sqliteDatabase.Close()
|
|
||||||
} else {
|
|
||||||
dbid = peer.DbIds[len(peer.DbIds)-1]
|
|
||||||
}
|
|
||||||
// Open Db
|
|
||||||
db, err := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
// Detach Files
|
return fn(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withDbRead(path string, fn func(*sql.DB) error) error {
|
||||||
|
mu := getDbFileMutex(path)
|
||||||
|
mu.RLock()
|
||||||
|
defer mu.RUnlock()
|
||||||
|
db, err := sql.Open("sqlite3", path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
return fn(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbPath(cfg *Config, identity *Identity, dbid string) string {
|
||||||
|
return filepath.Join(cfg.StoragePath, identity.Uuid, dbid+cfg.DbSuffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMessage(peer *Peer, usermessage *meowlib.UserMessage, filenames []string, password string) error {
|
||||||
|
cfg := GetConfig()
|
||||||
|
identity := cfg.GetIdentity()
|
||||||
|
|
||||||
|
isNew := len(peer.DbIds) == 0
|
||||||
|
var dbid string
|
||||||
|
if isNew {
|
||||||
|
dbid = uuid.NewString()
|
||||||
|
peer.DbIds = []string{dbid}
|
||||||
|
identity.Peers.StorePeer(peer)
|
||||||
|
identity.CreateFolder()
|
||||||
|
} else {
|
||||||
|
dbid = peer.DbIds[len(peer.DbIds)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach file attachments — no DB lock needed for file I/O.
|
||||||
hiddenFilenames := []string{}
|
hiddenFilenames := []string{}
|
||||||
if len(usermessage.Files) > 0 {
|
if len(usermessage.Files) > 0 {
|
||||||
|
secureDir := filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles")
|
||||||
|
if _, err := os.Stat(secureDir); os.IsNotExist(err) {
|
||||||
|
if err = os.MkdirAll(secureDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, f := range usermessage.Files {
|
for _, f := range usermessage.Files {
|
||||||
hiddenFilename := uuid.NewString()
|
hiddenFilename := uuid.NewString()
|
||||||
// Cypher file
|
|
||||||
encData, err := meowlib.SymEncrypt(password, f.Data)
|
encData, err := meowlib.SymEncrypt(password, f.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles")); os.IsNotExist(err) {
|
hidden := filepath.Join(secureDir, hiddenFilename)
|
||||||
err = os.MkdirAll(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles"), 0755)
|
os.WriteFile(hidden, encData, 0600)
|
||||||
if err != nil {
|
hiddenFilenames = append(hiddenFilenames, hidden)
|
||||||
return err
|
f.Data = []byte(hidden)
|
||||||
}
|
|
||||||
}
|
|
||||||
os.WriteFile(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles", hiddenFilename), encData, 0600)
|
|
||||||
hiddenFilenames = append(hiddenFilenames, filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles", hiddenFilename))
|
|
||||||
// replace f.Data by uuid filename
|
|
||||||
f.Data = []byte(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles", hiddenFilename))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
outbound := true
|
|
||||||
if usermessage.From == peer.ContactPublicKey {
|
outbound := usermessage.From != peer.ContactPublicKey
|
||||||
outbound = false
|
|
||||||
}
|
|
||||||
// Convert UserMessage to DbMessage
|
|
||||||
dbm := UserMessageToDbMessage(outbound, usermessage, hiddenFilenames)
|
dbm := UserMessageToDbMessage(outbound, usermessage, hiddenFilenames)
|
||||||
// Encrypt message
|
|
||||||
out, err := proto.Marshal(dbm)
|
out, err := proto.Marshal(dbm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -86,98 +98,94 @@ func storeMessage(peer *Peer, usermessage *meowlib.UserMessage, filenames []stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Insert message
|
|
||||||
insertMessageSQL := `INSERT INTO message(m) VALUES (?) RETURNING ID`
|
var id int64
|
||||||
statement, err := db.Prepare(insertMessageSQL) // Prepare statement.
|
path := dbPath(cfg, identity, dbid)
|
||||||
|
err = withDbWrite(path, func(db *sql.DB) error {
|
||||||
|
// SQLite creates the file on first Open; create the table if new DB.
|
||||||
|
if isNew {
|
||||||
|
if err := createMessageTable(db); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stmt, err := db.Prepare(`INSERT INTO message(m) VALUES (?) RETURNING ID`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result, err := stmt.Exec(encData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
id, err = result.LastInsertId()
|
||||||
|
return err
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
result, err := statement.Exec(encData)
|
|
||||||
if err != nil {
|
peer.LastMessage = DbMessageToInternalUserMessage(id, dbid, dbm)
|
||||||
return err
|
|
||||||
}
|
|
||||||
id, err := result.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ium := DbMessageToInternalUserMessage(id, dbid, dbm)
|
|
||||||
peer.LastMessage = ium
|
|
||||||
identity.Peers.StorePeer(peer)
|
identity.Peers.StorePeer(peer)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get new messages from a peer
|
|
||||||
func loadNewMessages(peer *Peer, lastDbId int, password string) ([]*InternalUserMessage, error) {
|
func loadNewMessages(peer *Peer, lastDbId int, password string) ([]*InternalUserMessage, error) {
|
||||||
var messages []*InternalUserMessage
|
var messages []*InternalUserMessage
|
||||||
cfg := GetConfig()
|
cfg := GetConfig()
|
||||||
identity := cfg.GetIdentity()
|
identity := cfg.GetIdentity()
|
||||||
// handle no db yet
|
|
||||||
if len(peer.DbIds) == 0 {
|
if len(peer.DbIds) == 0 {
|
||||||
return messages, nil
|
return messages, nil
|
||||||
}
|
}
|
||||||
fileidx := len(peer.DbIds) - 1
|
fileidx := len(peer.DbIds) - 1
|
||||||
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
|
||||||
db, err := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
// if it's first app query, it won't hold a lastIndex, so let's start from end
|
|
||||||
if lastDbId == 0 {
|
if lastDbId == 0 {
|
||||||
lastDbId = math.MaxInt64
|
lastDbId = math.MaxInt64
|
||||||
}
|
}
|
||||||
stm, err := db.Prepare("SELECT id, m FROM message WHERE id > ? ORDER BY id DESC")
|
err := withDbRead(dbPath(cfg, identity, peer.DbIds[fileidx]), func(db *sql.DB) error {
|
||||||
if err != nil {
|
stm, err := db.Prepare("SELECT id, m FROM message WHERE id > ? ORDER BY id DESC")
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer stm.Close()
|
|
||||||
rows, err := stm.Query(lastDbId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var ium *InternalUserMessage
|
|
||||||
var dbm meowlib.DbMessage
|
|
||||||
var id int64
|
|
||||||
var m []byte
|
|
||||||
err = rows.Scan(&id, &m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
decdata, err := meowlib.SymDecrypt(password, m)
|
defer stm.Close()
|
||||||
|
rows, err := stm.Query(lastDbId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
err = proto.Unmarshal(decdata, &dbm)
|
defer rows.Close()
|
||||||
if err != nil {
|
for rows.Next() {
|
||||||
return nil, err
|
var id int64
|
||||||
|
var m []byte
|
||||||
|
if err = rows.Scan(&id, &m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decdata, err := meowlib.SymDecrypt(password, m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var dbm meowlib.DbMessage
|
||||||
|
if err = proto.Unmarshal(decdata, &dbm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ium := DbMessageToInternalUserMessage(id, peer.DbIds[fileidx], &dbm)
|
||||||
|
ium.Dbid = id
|
||||||
|
ium.Dbfile = peer.DbIds[fileidx]
|
||||||
|
messages = append(messages, ium)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
ium = DbMessageToInternalUserMessage(id, peer.DbIds[fileidx], &dbm)
|
})
|
||||||
ium.Dbid = id
|
|
||||||
ium.Dbfile = peer.DbIds[fileidx]
|
|
||||||
messages = append(messages, ium)
|
|
||||||
}
|
|
||||||
// TODO DB overlap
|
// TODO DB overlap
|
||||||
return messages, nil
|
return messages, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get old messages from a peer
|
|
||||||
func loadMessagesHistory(peer *Peer, inAppMsgCount int, lastDbId int, wantMore int, password string) ([]InternalUserMessage, error) {
|
func loadMessagesHistory(peer *Peer, inAppMsgCount int, lastDbId int, wantMore int, password string) ([]InternalUserMessage, error) {
|
||||||
var messages []InternalUserMessage
|
var messages []InternalUserMessage
|
||||||
// handle no db yet
|
cfg := GetConfig()
|
||||||
if len(peer.DbIds) == 0 {
|
if len(peer.DbIds) == 0 {
|
||||||
return messages, nil
|
return messages, nil
|
||||||
}
|
}
|
||||||
fileidx := len(peer.DbIds) - 1
|
fileidx := len(peer.DbIds) - 1
|
||||||
// initialize count with last db message count
|
|
||||||
countStack, err := getMessageCount(peer.DbIds[fileidx])
|
countStack, err := getMessageCount(peer.DbIds[fileidx])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// while the db message count < what we already have in app, step to next db file
|
|
||||||
for inAppMsgCount > countStack {
|
for inAppMsgCount > countStack {
|
||||||
fileidx--
|
fileidx--
|
||||||
if fileidx < 0 {
|
if fileidx < 0 {
|
||||||
@@ -189,91 +197,80 @@ func loadMessagesHistory(peer *Peer, inAppMsgCount int, lastDbId int, wantMore i
|
|||||||
}
|
}
|
||||||
countStack += newCount
|
countStack += newCount
|
||||||
}
|
}
|
||||||
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
|
||||||
db, err := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
// if it's first app query, it won't hold a lastIndex, so let's start from end
|
|
||||||
if lastDbId == 0 {
|
if lastDbId == 0 {
|
||||||
lastDbId = math.MaxInt64
|
lastDbId = math.MaxInt64
|
||||||
}
|
}
|
||||||
stm, err := db.Prepare("SELECT id, m FROM message WHERE id < ? ORDER BY id DESC LIMIT ?")
|
err = withDbRead(filepath.Join(cfg.StoragePath, cfg.GetIdentity().Uuid, peer.DbIds[fileidx]+cfg.DbSuffix), func(db *sql.DB) error {
|
||||||
if err != nil {
|
stm, err := db.Prepare("SELECT id, m FROM message WHERE id < ? ORDER BY id DESC LIMIT ?")
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer stm.Close()
|
|
||||||
rows, err := stm.Query(lastDbId, wantMore)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var ium *InternalUserMessage
|
|
||||||
var dbm meowlib.DbMessage
|
|
||||||
var id int64
|
|
||||||
var m []byte
|
|
||||||
err = rows.Scan(&id, &m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
decdata, err := meowlib.SymDecrypt(password, m)
|
defer stm.Close()
|
||||||
|
rows, err := stm.Query(lastDbId, wantMore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
err = proto.Unmarshal(decdata, &dbm)
|
defer rows.Close()
|
||||||
if err != nil {
|
for rows.Next() {
|
||||||
return nil, err
|
var id int64
|
||||||
|
var m []byte
|
||||||
|
if err = rows.Scan(&id, &m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decdata, err := meowlib.SymDecrypt(password, m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var dbm meowlib.DbMessage
|
||||||
|
if err = proto.Unmarshal(decdata, &dbm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ium := DbMessageToInternalUserMessage(id, peer.DbIds[fileidx], &dbm)
|
||||||
|
ium.Dbid = id
|
||||||
|
ium.Dbfile = peer.DbIds[fileidx]
|
||||||
|
messages = append(messages, *ium)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
ium = DbMessageToInternalUserMessage(id, peer.DbIds[fileidx], &dbm)
|
})
|
||||||
ium.Dbid = id
|
|
||||||
ium.Dbfile = peer.DbIds[fileidx]
|
|
||||||
|
|
||||||
messages = append(messages, *ium)
|
|
||||||
}
|
|
||||||
// TODO DB overlap
|
// TODO DB overlap
|
||||||
return messages, nil
|
return messages, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDbMessage(dbFile string, dbId int64, password string) (*meowlib.DbMessage, error) {
|
func GetDbMessage(dbFile string, dbId int64, password string) (*meowlib.DbMessage, error) {
|
||||||
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
cfg := GetConfig()
|
||||||
db, err := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbFile+GetConfig().DbSuffix)) // Open the created SQLite dbFile
|
path := filepath.Join(cfg.StoragePath, cfg.GetIdentity().Uuid, dbFile+cfg.DbSuffix)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
stm, err := db.Prepare("SELECT id, m FROM message WHERE id=?")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer stm.Close()
|
|
||||||
rows, err := stm.Query(dbId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
var dbm meowlib.DbMessage
|
var dbm meowlib.DbMessage
|
||||||
found := false
|
found := false
|
||||||
for rows.Next() {
|
err := withDbRead(path, func(db *sql.DB) error {
|
||||||
found = true
|
stm, err := db.Prepare("SELECT id, m FROM message WHERE id=?")
|
||||||
var id int64
|
|
||||||
var m []byte
|
|
||||||
err = rows.Scan(&id, &m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
decdata, err := meowlib.SymDecrypt(password, m)
|
defer stm.Close()
|
||||||
|
rows, err := stm.Query(dbId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
err = proto.Unmarshal(decdata, &dbm)
|
defer rows.Close()
|
||||||
if err != nil {
|
for rows.Next() {
|
||||||
return nil, err
|
found = true
|
||||||
|
var id int64
|
||||||
|
var m []byte
|
||||||
|
if err = rows.Scan(&id, &m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decdata, err := meowlib.SymDecrypt(password, m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = proto.Unmarshal(decdata, &dbm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return nil, fmt.Errorf("message row %d not found in %s", dbId, dbFile)
|
return nil, fmt.Errorf("message row %d not found in %s", dbId, dbFile)
|
||||||
@@ -282,12 +279,8 @@ func GetDbMessage(dbFile string, dbId int64, password string) (*meowlib.DbMessag
|
|||||||
}
|
}
|
||||||
|
|
||||||
func UpdateDbMessage(dbm *meowlib.DbMessage, dbFile string, dbId int64, password string) error {
|
func UpdateDbMessage(dbm *meowlib.DbMessage, dbFile string, dbId int64, password string) error {
|
||||||
db, err := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbFile+GetConfig().DbSuffix)) // Open the created SQLite dbFile
|
cfg := GetConfig()
|
||||||
if err != nil {
|
path := filepath.Join(cfg.StoragePath, cfg.GetIdentity().Uuid, dbFile+cfg.DbSuffix)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
// Encrypt message
|
|
||||||
out, err := proto.Marshal(dbm)
|
out, err := proto.Marshal(dbm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -296,20 +289,16 @@ func UpdateDbMessage(dbm *meowlib.DbMessage, dbFile string, dbId int64, password
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Insert message
|
return withDbWrite(path, func(db *sql.DB) error {
|
||||||
updateMessageSQL := `UPDATE message SET m=? WHERE id=?`
|
stmt, err := db.Prepare(`UPDATE message SET m=? WHERE id=?`)
|
||||||
statement, err := db.Prepare(updateMessageSQL) // Prepare statement.
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
|
}
|
||||||
|
_, err = stmt.Exec(encData, dbId)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
_, err = statement.Exec(encData, dbId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get old messages from a peer
|
|
||||||
func GetMessagePreview(dbFile string, dbId int64, password string) ([]byte, error) {
|
func GetMessagePreview(dbFile string, dbId int64, password string) ([]byte, error) {
|
||||||
dbm, err := GetDbMessage(dbFile, dbId, password)
|
dbm, err := GetDbMessage(dbFile, dbId, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -318,24 +307,15 @@ func GetMessagePreview(dbFile string, dbId int64, password string) ([]byte, erro
|
|||||||
return FilePreview(dbm.FilePaths[0], password)
|
return FilePreview(dbm.FilePaths[0], password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrypt the a file and returns the raw content
|
|
||||||
func FilePreview(filename string, password string) ([]byte, error) {
|
func FilePreview(filename string, password string) ([]byte, error) {
|
||||||
// get the hidden file
|
|
||||||
encData, err := os.ReadFile(filename)
|
encData, err := os.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// decrypt the file
|
return meowlib.SymDecrypt(password, encData)
|
||||||
data, err := meowlib.SymDecrypt(password, encData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the raw content from the files content (loads the first image, or build a more complex view)
|
|
||||||
func InternalUserMessagePreview(msg *InternalUserMessage, password string) ([]byte, error) {
|
func InternalUserMessagePreview(msg *InternalUserMessage, password string) ([]byte, error) {
|
||||||
// get the hidden file name
|
|
||||||
if len(msg.FilePaths) == 0 {
|
if len(msg.FilePaths) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -343,21 +323,16 @@ func InternalUserMessagePreview(msg *InternalUserMessage, password string) ([]by
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getMessageCount(dbid string) (int, error) {
|
func getMessageCount(dbid string) (int, error) {
|
||||||
db, err := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
cfg := GetConfig()
|
||||||
if err != nil {
|
path := filepath.Join(cfg.StoragePath, cfg.GetIdentity().Uuid, dbid+cfg.DbSuffix)
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
var count int
|
var count int
|
||||||
query := "SELECT COUNT(*) FROM message"
|
err := withDbRead(path, func(db *sql.DB) error {
|
||||||
err = db.QueryRow(query).Scan(&count)
|
return db.QueryRow("SELECT COUNT(*) FROM message").Scan(&count)
|
||||||
if err != nil {
|
})
|
||||||
return 0, err
|
return count, err
|
||||||
}
|
|
||||||
return count, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMessageServerDelivery updates the server delivery UUID and timestamp for an existing stored message.
|
// SetMessageServerDelivery updates the server delivery UUID and timestamp for a stored message.
|
||||||
func SetMessageServerDelivery(dbFile string, dbId int64, serverUid string, receiveTime uint64, password string) error {
|
func SetMessageServerDelivery(dbFile string, dbId int64, serverUid string, receiveTime uint64, password string) error {
|
||||||
dbm, err := GetDbMessage(dbFile, dbId, password)
|
dbm, err := GetDbMessage(dbFile, dbId, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -375,37 +350,42 @@ func FindMessageByUuid(peer *Peer, messageUuid string, password string) (string,
|
|||||||
identity := cfg.GetIdentity()
|
identity := cfg.GetIdentity()
|
||||||
for i := len(peer.DbIds) - 1; i >= 0; i-- {
|
for i := len(peer.DbIds) - 1; i >= 0; i-- {
|
||||||
dbid := peer.DbIds[i]
|
dbid := peer.DbIds[i]
|
||||||
db, err := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix))
|
path := filepath.Join(cfg.StoragePath, identity.Uuid, dbid+cfg.DbSuffix)
|
||||||
if err != nil {
|
var foundFile string
|
||||||
continue
|
var foundId int64
|
||||||
}
|
var foundMsg meowlib.DbMessage
|
||||||
rows, err := db.Query("SELECT id, m FROM message ORDER BY id DESC")
|
err := withDbRead(path, func(db *sql.DB) error {
|
||||||
if err != nil {
|
rows, err := db.Query("SELECT id, m FROM message ORDER BY id DESC")
|
||||||
db.Close()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var id int64
|
|
||||||
var m []byte
|
|
||||||
if err := rows.Scan(&id, &m); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
decdata, err := meowlib.SymDecrypt(password, m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
return err
|
||||||
}
|
}
|
||||||
var dbm meowlib.DbMessage
|
defer rows.Close()
|
||||||
if err := proto.Unmarshal(decdata, &dbm); err != nil {
|
for rows.Next() {
|
||||||
continue
|
var id int64
|
||||||
}
|
var m []byte
|
||||||
if dbm.Status != nil && dbm.Status.Uuid == messageUuid {
|
if err := rows.Scan(&id, &m); err != nil {
|
||||||
rows.Close()
|
continue
|
||||||
db.Close()
|
}
|
||||||
return dbid, id, &dbm, nil
|
decdata, err := meowlib.SymDecrypt(password, m)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var dbm meowlib.DbMessage
|
||||||
|
if err := proto.Unmarshal(decdata, &dbm); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dbm.Status != nil && dbm.Status.Uuid == messageUuid {
|
||||||
|
foundFile = dbid
|
||||||
|
foundId = id
|
||||||
|
foundMsg = dbm
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err == nil && foundFile != "" {
|
||||||
|
return foundFile, foundId, &foundMsg, nil
|
||||||
}
|
}
|
||||||
rows.Close()
|
|
||||||
db.Close()
|
|
||||||
}
|
}
|
||||||
return "", 0, nil, fmt.Errorf("message with UUID %s not found", messageUuid)
|
return "", 0, nil, fmt.Errorf("message with UUID %s not found", messageUuid)
|
||||||
}
|
}
|
||||||
@@ -430,19 +410,18 @@ func UpdateMessageAck(peer *Peer, messageUuid string, receivedAt uint64, process
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createMessageTable(db *sql.DB) error {
|
func createMessageTable(db *sql.DB) error {
|
||||||
createMessageTableSQL := `CREATE TABLE message (
|
stmt, err := db.Prepare(`CREATE TABLE message (
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
"m" BLOB);` // SQL Statement for Create Table
|
"m" BLOB)`)
|
||||||
statement, err := db.Prepare(createMessageTableSQL) // Prepare SQL Statement
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
statement.Exec() // Execute SQL Statements
|
stmt.Exec()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createServerTable(db *sql.DB) error {
|
func createServerTable(db *sql.DB) error {
|
||||||
createServerTableSQL := `CREATE TABLE servers (
|
stmt, err := db.Prepare(`CREATE TABLE servers (
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
"country" varchar(2),
|
"country" varchar(2),
|
||||||
"public" bool,
|
"public" bool,
|
||||||
@@ -453,11 +432,10 @@ func createServerTable(db *sql.DB) error {
|
|||||||
"name" varchar(255);
|
"name" varchar(255);
|
||||||
"description" varchar(5000)
|
"description" varchar(5000)
|
||||||
"publickey" varchar(10000)
|
"publickey" varchar(10000)
|
||||||
)` // SQL Statement for Create Table
|
)`)
|
||||||
statement, err := db.Prepare(createServerTableSQL) // Prepare SQL Statement
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
statement.Exec() // Execute SQL Statements
|
stmt.Exec()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-39
@@ -9,6 +9,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"forge.redroom.link/yves/meowlib"
|
"forge.redroom.link/yves/meowlib"
|
||||||
"github.com/dgraph-io/badger"
|
"github.com/dgraph-io/badger"
|
||||||
@@ -17,11 +18,12 @@ import (
|
|||||||
|
|
||||||
type PeerStorage struct {
|
type PeerStorage struct {
|
||||||
DbFile string `json:"db_file,omitempty"`
|
DbFile string `json:"db_file,omitempty"`
|
||||||
|
mu sync.RWMutex
|
||||||
db *badger.DB
|
db *badger.DB
|
||||||
cache map[string]*Peer
|
cache map[string]*Peer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the badger database from struct PeerStorage
|
// open opens the Badger database. Caller must hold mu (write).
|
||||||
func (ps *PeerStorage) open() error {
|
func (ps *PeerStorage) open() error {
|
||||||
if ps.DbFile == "" {
|
if ps.DbFile == "" {
|
||||||
ps.DbFile = uuid.New().String()
|
ps.DbFile = uuid.New().String()
|
||||||
@@ -34,20 +36,27 @@ func (ps *PeerStorage) open() error {
|
|||||||
opts.Logger = nil
|
opts.Logger = nil
|
||||||
var err error
|
var err error
|
||||||
ps.db, err = badger.Open(opts)
|
ps.db, err = badger.Open(opts)
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store function StorePeer stores a peer in the badger database with Peer.Uid as key
|
// close closes the Badger database. Caller must hold mu (write).
|
||||||
|
func (ps *PeerStorage) close() {
|
||||||
|
ps.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorePeer stores a peer in the Badger database with Peer.Uid as key.
|
||||||
func (ps *PeerStorage) StorePeer(peer *Peer) error {
|
func (ps *PeerStorage) StorePeer(peer *Peer) error {
|
||||||
err := ps.open()
|
ps.mu.Lock()
|
||||||
if err != nil {
|
defer ps.mu.Unlock()
|
||||||
|
return ps.storePeerLocked(peer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// storePeerLocked is StorePeer without acquiring the lock. Caller must hold mu (write).
|
||||||
|
func (ps *PeerStorage) storePeerLocked(peer *Peer) error {
|
||||||
|
if err := ps.open(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer ps.close()
|
defer ps.close()
|
||||||
// first marshal the Peer to bytes with protobuf
|
|
||||||
jsonsrv, err := json.Marshal(peer)
|
jsonsrv, err := json.Marshal(peer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -65,26 +74,24 @@ func (ps *PeerStorage) StorePeer(peer *Peer) error {
|
|||||||
}
|
}
|
||||||
shakey := sha256.Sum256([]byte(peer.Uid))
|
shakey := sha256.Sum256([]byte(peer.Uid))
|
||||||
key := shakey[:]
|
key := shakey[:]
|
||||||
// add it to cache
|
|
||||||
ps.cache[peer.Uid] = peer
|
ps.cache[peer.Uid] = peer
|
||||||
// then store it in the database
|
|
||||||
return ps.db.Update(func(txn *badger.Txn) error {
|
return ps.db.Update(func(txn *badger.Txn) error {
|
||||||
return txn.Set(key, data)
|
return txn.Set(key, data)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadPeer function loads a Peer from the badger database with Peer.GetUid() as key
|
// LoadPeer loads a Peer from the Badger database with Peer.GetUid() as key.
|
||||||
func (ps *PeerStorage) LoadPeer(uid string, password string) (*Peer, error) {
|
func (ps *PeerStorage) LoadPeer(uid string, password string) (*Peer, error) {
|
||||||
|
ps.mu.Lock()
|
||||||
|
defer ps.mu.Unlock()
|
||||||
var peer Peer
|
var peer Peer
|
||||||
err := ps.open()
|
if err := ps.open(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer ps.close()
|
defer ps.close()
|
||||||
shakey := sha256.Sum256([]byte(uid))
|
shakey := sha256.Sum256([]byte(uid))
|
||||||
key := shakey[:]
|
key := shakey[:]
|
||||||
err = ps.db.View(func(txn *badger.Txn) error {
|
err := ps.db.View(func(txn *badger.Txn) error {
|
||||||
item, err := txn.Get(key)
|
item, err := txn.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -100,16 +107,17 @@ func (ps *PeerStorage) LoadPeer(uid string, password string) (*Peer, error) {
|
|||||||
return &peer, err
|
return &peer, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeletePeer function deletes a Peer from the badger database with Peer.GetUid() as key
|
// DeletePeer deletes a Peer from the Badger database with Peer.GetUid() as key.
|
||||||
func (ps *PeerStorage) DeletePeer(uid string) error {
|
func (ps *PeerStorage) DeletePeer(uid string) error {
|
||||||
err := ps.open()
|
ps.mu.Lock()
|
||||||
if err != nil {
|
defer ps.mu.Unlock()
|
||||||
|
if err := ps.open(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer ps.close()
|
defer ps.close()
|
||||||
shakey := sha256.Sum256([]byte(uid))
|
shakey := sha256.Sum256([]byte(uid))
|
||||||
key := shakey[:]
|
key := shakey[:]
|
||||||
err = ps.db.Update(func(txn *badger.Txn) error {
|
err := ps.db.Update(func(txn *badger.Txn) error {
|
||||||
return txn.Delete(key)
|
return txn.Delete(key)
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -118,15 +126,16 @@ func (ps *PeerStorage) DeletePeer(uid string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadPeers function loads Peers from the badger database with a specific password
|
// LoadPeers loads all Peers from the Badger database and populates the cache.
|
||||||
func (ps *PeerStorage) LoadPeers(password string) ([]*Peer, error) {
|
func (ps *PeerStorage) LoadPeers(password string) ([]*Peer, error) {
|
||||||
|
ps.mu.Lock()
|
||||||
|
defer ps.mu.Unlock()
|
||||||
var peers []*Peer
|
var peers []*Peer
|
||||||
err := ps.open()
|
if err := ps.open(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer ps.close()
|
defer ps.close()
|
||||||
err = ps.db.View(func(txn *badger.Txn) error {
|
err := ps.db.View(func(txn *badger.Txn) error {
|
||||||
opts := badger.DefaultIteratorOptions
|
opts := badger.DefaultIteratorOptions
|
||||||
opts.PrefetchSize = 10
|
opts.PrefetchSize = 10
|
||||||
it := txn.NewIterator(opts)
|
it := txn.NewIterator(opts)
|
||||||
@@ -148,32 +157,29 @@ func (ps *PeerStorage) LoadPeers(password string) ([]*Peer, error) {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
// Sort peers based on peer.Name
|
|
||||||
sort.Slice(peers, func(i, j int) bool {
|
sort.Slice(peers, func(i, j int) bool {
|
||||||
return peers[i].Name < peers[j].Name
|
return peers[i].Name < peers[j].Name
|
||||||
})
|
})
|
||||||
return peers, err
|
return peers, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPeers function returns all peers from the cache as a sorted array
|
// GetPeers returns all peers from the cache as a sorted slice.
|
||||||
func (ps *PeerStorage) GetPeers() ([]*Peer, error) {
|
func (ps *PeerStorage) GetPeers() ([]*Peer, error) {
|
||||||
|
ps.mu.RLock()
|
||||||
|
defer ps.mu.RUnlock()
|
||||||
peers := make([]*Peer, 0, len(ps.cache))
|
peers := make([]*Peer, 0, len(ps.cache))
|
||||||
for _, peer := range ps.cache {
|
for _, peer := range ps.cache {
|
||||||
peers = append(peers, peer)
|
peers = append(peers, peer)
|
||||||
}
|
}
|
||||||
// Sort peers based on peer.Name
|
|
||||||
sort.Slice(peers, func(i, j int) bool {
|
sort.Slice(peers, func(i, j int) bool {
|
||||||
return peers[i].Name < peers[j].Name
|
return peers[i].Name < peers[j].Name
|
||||||
})
|
})
|
||||||
return peers, nil
|
return peers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// close the badger database
|
|
||||||
func (ps *PeerStorage) close() {
|
|
||||||
ps.db.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *PeerStorage) GetFromPublicKey(publickey string) *Peer {
|
func (ps *PeerStorage) GetFromPublicKey(publickey string) *Peer {
|
||||||
|
ps.mu.RLock()
|
||||||
|
defer ps.mu.RUnlock()
|
||||||
for _, peer := range ps.cache {
|
for _, peer := range ps.cache {
|
||||||
if peer.ContactPublicKey == publickey {
|
if peer.ContactPublicKey == publickey {
|
||||||
return peer
|
return peer
|
||||||
@@ -183,6 +189,8 @@ func (ps *PeerStorage) GetFromPublicKey(publickey string) *Peer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PeerStorage) GetFromInvitationId(invitationId string) *Peer {
|
func (ps *PeerStorage) GetFromInvitationId(invitationId string) *Peer {
|
||||||
|
ps.mu.RLock()
|
||||||
|
defer ps.mu.RUnlock()
|
||||||
for _, peer := range ps.cache {
|
for _, peer := range ps.cache {
|
||||||
if peer.InvitationId == invitationId {
|
if peer.InvitationId == invitationId {
|
||||||
return peer
|
return peer
|
||||||
@@ -192,6 +200,8 @@ func (ps *PeerStorage) GetFromInvitationId(invitationId string) *Peer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PeerStorage) GetFromMyLookupKey(publickey string) *Peer {
|
func (ps *PeerStorage) GetFromMyLookupKey(publickey string) *Peer {
|
||||||
|
ps.mu.RLock()
|
||||||
|
defer ps.mu.RUnlock()
|
||||||
for _, peer := range ps.cache {
|
for _, peer := range ps.cache {
|
||||||
if peer.MyLookupKp.Public == publickey {
|
if peer.MyLookupKp.Public == publickey {
|
||||||
return peer
|
return peer
|
||||||
@@ -201,6 +211,8 @@ func (ps *PeerStorage) GetFromMyLookupKey(publickey string) *Peer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PeerStorage) NameExists(name string) bool {
|
func (ps *PeerStorage) NameExists(name string) bool {
|
||||||
|
ps.mu.RLock()
|
||||||
|
defer ps.mu.RUnlock()
|
||||||
for _, peer := range ps.cache {
|
for _, peer := range ps.cache {
|
||||||
if peer.Name == name {
|
if peer.Name == name {
|
||||||
return true
|
return true
|
||||||
@@ -210,6 +222,8 @@ func (ps *PeerStorage) NameExists(name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PeerStorage) GetFromName(name string) *Peer {
|
func (ps *PeerStorage) GetFromName(name string) *Peer {
|
||||||
|
ps.mu.RLock()
|
||||||
|
defer ps.mu.RUnlock()
|
||||||
for _, peer := range ps.cache {
|
for _, peer := range ps.cache {
|
||||||
if peer.Name == name {
|
if peer.Name == name {
|
||||||
return peer
|
return peer
|
||||||
@@ -219,26 +233,29 @@ func (ps *PeerStorage) GetFromName(name string) *Peer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PeerStorage) GetFromUid(uid string) *Peer {
|
func (ps *PeerStorage) GetFromUid(uid string) *Peer {
|
||||||
|
ps.mu.RLock()
|
||||||
|
defer ps.mu.RUnlock()
|
||||||
return ps.cache[uid]
|
return ps.cache[uid]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the received contact card is an answer to an invitation, returns true if it is, and the proposed and received nicknames
|
// CheckInvitation checks if the received contact card is an answer to an invitation.
|
||||||
func (ps *PeerStorage) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string, invitationMessage string) {
|
func (ps *PeerStorage) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string, invitationMessage string) {
|
||||||
// invitation Id found, this is an answer to an invitation
|
ps.mu.RLock()
|
||||||
|
defer ps.mu.RUnlock()
|
||||||
for _, p := range ps.cache {
|
for _, p := range ps.cache {
|
||||||
if p.InvitationId == ReceivedContact.InvitationId {
|
if p.InvitationId == ReceivedContact.InvitationId {
|
||||||
return true, p.Name, ReceivedContact.Name, ReceivedContact.InvitationMessage
|
return true, p.Name, ReceivedContact.Name, ReceivedContact.InvitationMessage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// it's an invitation
|
|
||||||
return false, "", ReceivedContact.Name, ReceivedContact.InvitationMessage
|
return false, "", ReceivedContact.Name, ReceivedContact.InvitationMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalizes an invitation, returns nil if successful
|
// FinalizeInvitation completes an invitation handshake and persists the updated peer.
|
||||||
func (ps *PeerStorage) FinalizeInvitation(ReceivedContact *meowlib.ContactCard) error {
|
func (ps *PeerStorage) FinalizeInvitation(ReceivedContact *meowlib.ContactCard) error {
|
||||||
|
ps.mu.Lock()
|
||||||
|
defer ps.mu.Unlock()
|
||||||
for i, p := range ps.cache {
|
for i, p := range ps.cache {
|
||||||
if p.InvitationId == ReceivedContact.InvitationId {
|
if p.InvitationId == ReceivedContact.InvitationId {
|
||||||
//id.Peers[i].Name = ReceivedContact.Name
|
|
||||||
ps.cache[i].ContactEncryption = ReceivedContact.EncryptionPublicKey
|
ps.cache[i].ContactEncryption = ReceivedContact.EncryptionPublicKey
|
||||||
ps.cache[i].ContactLookupKey = ReceivedContact.LookupPublicKey
|
ps.cache[i].ContactLookupKey = ReceivedContact.LookupPublicKey
|
||||||
ps.cache[i].ContactPublicKey = ReceivedContact.ContactPublicKey
|
ps.cache[i].ContactPublicKey = ReceivedContact.ContactPublicKey
|
||||||
@@ -250,8 +267,7 @@ func (ps *PeerStorage) FinalizeInvitation(ReceivedContact *meowlib.ContactCard)
|
|||||||
srvs = append(srvs, ReceivedContact.PullServers[srv].GetUid())
|
srvs = append(srvs, ReceivedContact.PullServers[srv].GetUid())
|
||||||
}
|
}
|
||||||
ps.cache[i].ContactPullServers = srvs
|
ps.cache[i].ContactPullServers = srvs
|
||||||
ps.StorePeer(ps.cache[i])
|
return ps.storePeerLocked(ps.cache[i])
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errors.New("no matching contact found for invitationId " + ReceivedContact.InvitationId)
|
return errors.New("no matching contact found for invitationId " + ReceivedContact.InvitationId)
|
||||||
|
|||||||
+59
-46
@@ -7,6 +7,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"forge.redroom.link/yves/meowlib"
|
"forge.redroom.link/yves/meowlib"
|
||||||
"github.com/dgraph-io/badger"
|
"github.com/dgraph-io/badger"
|
||||||
@@ -14,30 +15,37 @@ import (
|
|||||||
|
|
||||||
type ServerStorage struct {
|
type ServerStorage struct {
|
||||||
DbFile string `json:"db_file,omitempty"`
|
DbFile string `json:"db_file,omitempty"`
|
||||||
|
mu sync.Mutex
|
||||||
db *badger.DB
|
db *badger.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open a badger database from struct ServerStorage
|
// open opens the Badger database. Caller must hold mu.
|
||||||
func (ss *ServerStorage) open() error {
|
func (ss *ServerStorage) open() error {
|
||||||
|
|
||||||
opts := badger.DefaultOptions(filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, ss.DbFile))
|
opts := badger.DefaultOptions(filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, ss.DbFile))
|
||||||
opts.Logger = nil
|
opts.Logger = nil
|
||||||
var err error
|
var err error
|
||||||
ss.db, err = badger.Open(opts)
|
ss.db, err = badger.Open(opts)
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store function StoreServer stores a server in a badger database with Server.GetUid() as key
|
// close closes the Badger database. Caller must hold mu.
|
||||||
|
func (ss *ServerStorage) close() {
|
||||||
|
ss.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreServer stores a server in the Badger database with Server.GetUid() as key.
|
||||||
func (ss *ServerStorage) StoreServer(sc *Server) error {
|
func (ss *ServerStorage) StoreServer(sc *Server) error {
|
||||||
err := ss.open()
|
ss.mu.Lock()
|
||||||
if err != nil {
|
defer ss.mu.Unlock()
|
||||||
|
return ss.storeServerLocked(sc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// storeServerLocked is StoreServer without acquiring the lock. Caller must hold mu.
|
||||||
|
func (ss *ServerStorage) storeServerLocked(sc *Server) error {
|
||||||
|
if err := ss.open(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer ss.close()
|
defer ss.close()
|
||||||
// first marshal the Server to bytes with protobuf
|
|
||||||
jsonsrv, err := json.Marshal(sc)
|
jsonsrv, err := json.Marshal(sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -52,51 +60,56 @@ func (ss *ServerStorage) StoreServer(sc *Server) error {
|
|||||||
}
|
}
|
||||||
shakey := sha256.Sum256([]byte(sc.GetServerCard().GetUid()))
|
shakey := sha256.Sum256([]byte(sc.GetServerCard().GetUid()))
|
||||||
key := shakey[:]
|
key := shakey[:]
|
||||||
// then store it in the database
|
|
||||||
return ss.db.Update(func(txn *badger.Txn) error {
|
return ss.db.Update(func(txn *badger.Txn) error {
|
||||||
return txn.Set(key, data)
|
return txn.Set(key, data)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a server exists in a badger database with Server.GetUid() as key
|
// ServerExists checks if a server exists in the Badger database.
|
||||||
func (ss *ServerStorage) ServerExists(sc *Server) (bool, error) {
|
func (ss *ServerStorage) ServerExists(sc *Server) (bool, error) {
|
||||||
err := ss.open()
|
ss.mu.Lock()
|
||||||
if err != nil {
|
defer ss.mu.Unlock()
|
||||||
|
return ss.serverExistsLocked(sc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverExistsLocked is ServerExists without acquiring the lock. Caller must hold mu.
|
||||||
|
func (ss *ServerStorage) serverExistsLocked(sc *Server) (bool, error) {
|
||||||
|
if err := ss.open(); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
defer ss.close()
|
defer ss.close()
|
||||||
|
|
||||||
shakey := sha256.Sum256([]byte(sc.GetServerCard().GetUid()))
|
shakey := sha256.Sum256([]byte(sc.GetServerCard().GetUid()))
|
||||||
key := shakey[:]
|
key := shakey[:]
|
||||||
// check if key exists in badger database
|
err := ss.db.View(func(txn *badger.Txn) error {
|
||||||
err = ss.db.View(func(txn *badger.Txn) error {
|
|
||||||
_, err := txn.Get(key)
|
_, err := txn.Get(key)
|
||||||
return err
|
return err
|
||||||
}) // Add a comma here
|
})
|
||||||
if err != nil { // key does not exist
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store a server in a badger database with Server.GetUid() as key if it is not already there
|
// StoreServerIfNotExists stores a server only if it is not already present.
|
||||||
func (ss *ServerStorage) StoreServerIfNotExists(sc *Server) error {
|
func (ss *ServerStorage) StoreServerIfNotExists(sc *Server) error {
|
||||||
exists, err := ss.ServerExists(sc)
|
ss.mu.Lock()
|
||||||
|
defer ss.mu.Unlock()
|
||||||
|
exists, err := ss.serverExistsLocked(sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
return ss.StoreServer(sc)
|
return ss.storeServerLocked(sc)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadServer function loads a Server from a badger database with Server.GetUid() as key
|
// LoadServer loads a Server from the Badger database by uid.
|
||||||
func (ss *ServerStorage) LoadServer(uid string) (*Server, error) {
|
func (ss *ServerStorage) LoadServer(uid string) (*Server, error) {
|
||||||
|
ss.mu.Lock()
|
||||||
|
defer ss.mu.Unlock()
|
||||||
var sc Server
|
var sc Server
|
||||||
err := ss.open()
|
if err := ss.open(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer ss.close()
|
defer ss.close()
|
||||||
@@ -122,10 +135,11 @@ func (ss *ServerStorage) LoadServer(uid string) (*Server, error) {
|
|||||||
return &sc, err
|
return &sc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteServer function deletes a Server from a badger database with Server.GetUid() as key
|
// DeleteServer deletes a Server from the Badger database by uid.
|
||||||
func (ss *ServerStorage) DeleteServer(uid string) error {
|
func (ss *ServerStorage) DeleteServer(uid string) error {
|
||||||
err := ss.open()
|
ss.mu.Lock()
|
||||||
if err != nil {
|
defer ss.mu.Unlock()
|
||||||
|
if err := ss.open(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer ss.close()
|
defer ss.close()
|
||||||
@@ -136,11 +150,12 @@ func (ss *ServerStorage) DeleteServer(uid string) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadAllServers function loads all Servers from a badger database
|
// LoadAllServers loads all Servers from the Badger database.
|
||||||
func (ss *ServerStorage) LoadAllServers() ([]*Server, error) {
|
func (ss *ServerStorage) LoadAllServers() ([]*Server, error) {
|
||||||
|
ss.mu.Lock()
|
||||||
|
defer ss.mu.Unlock()
|
||||||
var scs []*Server
|
var scs []*Server
|
||||||
err := ss.open()
|
if err := ss.open(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer ss.close()
|
defer ss.close()
|
||||||
@@ -173,11 +188,12 @@ func (ss *ServerStorage) LoadAllServers() ([]*Server, error) {
|
|||||||
return scs, err
|
return scs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadAllServers function loads all ServersCards from a badger database
|
// LoadAllServerCards loads all ServerCards from the Badger database.
|
||||||
func (ss *ServerStorage) LoadAllServerCards() ([]*meowlib.ServerCard, error) {
|
func (ss *ServerStorage) LoadAllServerCards() ([]*meowlib.ServerCard, error) {
|
||||||
|
ss.mu.Lock()
|
||||||
|
defer ss.mu.Unlock()
|
||||||
var scs []*meowlib.ServerCard
|
var scs []*meowlib.ServerCard
|
||||||
err := ss.open()
|
if err := ss.open(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer ss.close()
|
defer ss.close()
|
||||||
@@ -210,11 +226,12 @@ func (ss *ServerStorage) LoadAllServerCards() ([]*meowlib.ServerCard, error) {
|
|||||||
return scs, err
|
return scs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadServersFromUids function loads Servers with id in []Uid parameter from a badger database
|
// LoadServersFromUids loads Servers whose UIDs are in the provided slice.
|
||||||
func (ss *ServerStorage) LoadServersFromUids(uids []string) ([]*Server, error) {
|
func (ss *ServerStorage) LoadServersFromUids(uids []string) ([]*Server, error) {
|
||||||
|
ss.mu.Lock()
|
||||||
|
defer ss.mu.Unlock()
|
||||||
var scs []*Server
|
var scs []*Server
|
||||||
err := ss.open()
|
if err := ss.open(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer ss.close()
|
defer ss.close()
|
||||||
@@ -248,11 +265,12 @@ func (ss *ServerStorage) LoadServersFromUids(uids []string) ([]*Server, error) {
|
|||||||
return scs, err
|
return scs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadServersFromUids function loads Servers with id in []Uid parameter from a badger database
|
// LoadServerCardsFromUids loads ServerCards whose UIDs are in the provided slice.
|
||||||
func (ss *ServerStorage) LoadServerCardsFromUids(uids []string) ([]*meowlib.ServerCard, error) {
|
func (ss *ServerStorage) LoadServerCardsFromUids(uids []string) ([]*meowlib.ServerCard, error) {
|
||||||
|
ss.mu.Lock()
|
||||||
|
defer ss.mu.Unlock()
|
||||||
var scs []*meowlib.ServerCard
|
var scs []*meowlib.ServerCard
|
||||||
err := ss.open()
|
if err := ss.open(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer ss.close()
|
defer ss.close()
|
||||||
@@ -285,8 +303,3 @@ func (ss *ServerStorage) LoadServerCardsFromUids(uids []string) ([]*meowlib.Serv
|
|||||||
})
|
})
|
||||||
return scs, err
|
return scs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// close a badger database
|
|
||||||
func (ss *ServerStorage) close() {
|
|
||||||
ss.db.Close()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ func HttpPostMessage(url string, msg []byte, timeout int) (response []byte, err
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Server already accepted the request (2xx) — body truncation on our
|
||||||
|
// side doesn't mean the message wasn't stored. Return what we have so
|
||||||
|
// the caller doesn't retry and produce a duplicate.
|
||||||
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return body, nil
|
return body, nil
|
||||||
|
|||||||
Reference in New Issue
Block a user