2023-01-07 00:39:05 +01:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
2023-02-15 22:08:17 +01:00
|
|
|
"math"
|
2023-01-07 00:39:05 +01:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"forge.redroom.link/yves/meowlib"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
2023-01-08 22:57:17 +01:00
|
|
|
"google.golang.org/protobuf/proto"
|
2023-01-07 00:39:05 +01:00
|
|
|
)
|
|
|
|
|
2024-02-29 21:03:15 +01:00
|
|
|
func StoreMessage(peer *Peer, usermessage *meowlib.UserMessage, filenames []string, password string) error {
|
2023-01-07 00:39:05 +01:00
|
|
|
var dbid string
|
|
|
|
// If no db/no ID create DB + Tablz
|
2023-02-15 22:08:17 +01:00
|
|
|
// TODO : if file size > X new db
|
2023-01-07 00:39:05 +01:00
|
|
|
if len(peer.DbIds) == 0 {
|
|
|
|
dbid = uuid.NewString()
|
2024-02-25 20:15:35 +01:00
|
|
|
peer.DbIds = []string{dbid}
|
|
|
|
GetConfig().me.Save()
|
2023-02-15 22:08:17 +01:00
|
|
|
file, err := os.Create(filepath.Join(GetConfig().StoragePath, dbid+GetConfig().DbSuffix))
|
2023-01-07 00:39:05 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
file.Close()
|
|
|
|
peer.DbIds = append(peer.DbIds, dbid)
|
2023-02-15 22:08:17 +01:00
|
|
|
sqliteDatabase, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
2023-07-27 10:44:09 +02:00
|
|
|
err = createMessageTable(sqliteDatabase)
|
2023-01-07 00:39:05 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sqliteDatabase.Close()
|
|
|
|
} else {
|
|
|
|
dbid = peer.DbIds[len(peer.DbIds)-1]
|
|
|
|
}
|
|
|
|
// Open Db
|
2023-02-15 22:08:17 +01:00
|
|
|
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
2023-01-07 00:39:05 +01:00
|
|
|
defer db.Close()
|
2023-01-08 22:57:17 +01:00
|
|
|
// Detach Files
|
2023-01-07 00:39:05 +01:00
|
|
|
if len(usermessage.Files) > 0 {
|
|
|
|
for _, f := range usermessage.Files {
|
|
|
|
hiddenFilename := uuid.NewString()
|
2023-01-08 22:57:17 +01:00
|
|
|
// Cypher file
|
2023-01-07 00:39:05 +01:00
|
|
|
encData, err := meowlib.SymEncrypt(password, f.Data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
os.WriteFile(hiddenFilename, encData, 0600)
|
|
|
|
// replace f.Data by uuid filename
|
|
|
|
f.Data = []byte(hiddenFilename)
|
|
|
|
}
|
|
|
|
}
|
2024-02-29 21:03:15 +01:00
|
|
|
outbound := true
|
|
|
|
if usermessage.From == peer.ContactPublicKey {
|
|
|
|
outbound = false
|
|
|
|
}
|
|
|
|
// Convert UserMessage to DbMessage
|
|
|
|
dbm := UserMessageToDbMessage(outbound, usermessage, filenames)
|
2023-01-08 22:57:17 +01:00
|
|
|
// Encrypt message
|
2024-02-29 21:03:15 +01:00
|
|
|
out, err := proto.Marshal(dbm)
|
2023-01-08 22:57:17 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
encData, err := meowlib.SymEncrypt(password, out)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Insert message
|
2023-02-15 22:08:17 +01:00
|
|
|
insertMessageSQL := `INSERT INTO message(m) VALUES (?) RETURNING ID`
|
2023-01-07 00:39:05 +01:00
|
|
|
statement, err := db.Prepare(insertMessageSQL) // Prepare statement.
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-03-05 20:15:48 +01:00
|
|
|
result, err := statement.Exec(encData)
|
2023-01-07 00:39:05 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-03-05 20:15:48 +01:00
|
|
|
id, err := result.LastInsertId()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ium := DbMessageToInternalUserMessage(id, dbid, dbm)
|
|
|
|
peer.LastMessage = ium
|
|
|
|
GetConfig().me.Save()
|
2023-01-07 00:39:05 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-02-17 19:30:25 +01:00
|
|
|
// Get new messages from a peer
|
2024-02-18 13:46:11 +01:00
|
|
|
func GetNewMessages(peer *Peer, lastDbId int, password string) ([]*InternalUserMessage, error) {
|
|
|
|
var messages []*InternalUserMessage
|
2024-03-05 23:15:19 +01:00
|
|
|
// handle no db yet
|
|
|
|
if len(peer.DbIds) == 0 {
|
|
|
|
return messages, nil
|
|
|
|
}
|
2024-02-17 19:30:25 +01:00
|
|
|
fileidx := len(peer.DbIds) - 1
|
|
|
|
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
|
|
|
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File
|
|
|
|
defer db.Close()
|
|
|
|
// if it's first app query, it won't hold a lastIndex, so let's start from end
|
|
|
|
if lastDbId == 0 {
|
|
|
|
lastDbId = math.MaxInt64
|
|
|
|
}
|
|
|
|
stm, err := db.Prepare("SELECT id, m FROM message WHERE id > ? ORDER BY id DESC")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer stm.Close()
|
|
|
|
rows, err := stm.Query(lastDbId)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
for rows.Next() {
|
2024-02-18 13:46:11 +01:00
|
|
|
var ium *InternalUserMessage
|
2024-02-29 21:03:15 +01:00
|
|
|
var dbm meowlib.DbMessage
|
2024-02-17 19:30:25 +01:00
|
|
|
var id int64
|
|
|
|
var m []byte
|
|
|
|
err = rows.Scan(&id, &m)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
decdata, err := meowlib.SymDecrypt(password, m)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-02-29 21:03:15 +01:00
|
|
|
err = proto.Unmarshal(decdata, &dbm)
|
2024-02-17 19:30:25 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-02-18 13:46:11 +01:00
|
|
|
|
2024-02-29 21:03:15 +01:00
|
|
|
ium = DbMessageToInternalUserMessage(id, peer.DbIds[fileidx], &dbm)
|
2024-02-17 19:30:25 +01:00
|
|
|
ium.Dbid = id
|
|
|
|
ium.Dbfile = peer.DbIds[fileidx]
|
|
|
|
messages = append(messages, ium)
|
|
|
|
}
|
|
|
|
// TODO DB overlap
|
|
|
|
return messages, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get old messages from a peer
|
2024-02-20 20:30:55 +01:00
|
|
|
func GetMessagesHistory(peer *Peer, inAppMsgCount int, lastDbId int, wantMore int, password string) ([]InternalUserMessage, error) {
|
|
|
|
var messages []InternalUserMessage
|
2024-03-05 23:15:19 +01:00
|
|
|
// handle no db yet
|
|
|
|
if len(peer.DbIds) == 0 {
|
|
|
|
return messages, nil
|
|
|
|
}
|
2023-02-15 22:08:17 +01:00
|
|
|
fileidx := len(peer.DbIds) - 1
|
|
|
|
// initialize count with last db message count
|
|
|
|
countStack, err := getMessageCount(peer.DbIds[fileidx])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// while the db message count < what we already have in app, step to next db file
|
|
|
|
for inAppMsgCount > countStack {
|
|
|
|
fileidx--
|
|
|
|
if fileidx < 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
newCount, err := getMessageCount(peer.DbIds[fileidx])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
countStack += newCount
|
|
|
|
}
|
|
|
|
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
|
|
|
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File
|
|
|
|
defer db.Close()
|
|
|
|
// if it's first app query, it won't hold a lastIndex, so let's start from end
|
|
|
|
if lastDbId == 0 {
|
|
|
|
lastDbId = math.MaxInt64
|
|
|
|
}
|
|
|
|
stm, err := db.Prepare("SELECT id, m FROM message WHERE id < ? ORDER BY id DESC LIMIT ?")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer stm.Close()
|
|
|
|
rows, err := stm.Query(lastDbId, wantMore)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
for rows.Next() {
|
2024-02-20 20:30:55 +01:00
|
|
|
var ium *InternalUserMessage
|
2024-02-29 21:03:15 +01:00
|
|
|
var dbm meowlib.DbMessage
|
2023-02-15 22:08:17 +01:00
|
|
|
var id int64
|
|
|
|
var m []byte
|
|
|
|
err = rows.Scan(&id, &m)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
decdata, err := meowlib.SymDecrypt(password, m)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-02-29 21:03:15 +01:00
|
|
|
err = proto.Unmarshal(decdata, &dbm)
|
2023-02-15 22:08:17 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-02-18 13:46:11 +01:00
|
|
|
|
2024-02-29 21:03:15 +01:00
|
|
|
ium = DbMessageToInternalUserMessage(id, peer.DbIds[fileidx], &dbm)
|
2024-02-17 19:30:25 +01:00
|
|
|
ium.Dbid = id
|
|
|
|
ium.Dbfile = peer.DbIds[fileidx]
|
2024-02-20 20:30:55 +01:00
|
|
|
messages = append(messages, *ium)
|
2023-02-15 22:08:17 +01:00
|
|
|
}
|
|
|
|
// TODO DB overlap
|
|
|
|
return messages, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getMessageCount(dbid string) (int, error) {
|
|
|
|
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
|
|
|
defer db.Close()
|
|
|
|
var count int
|
|
|
|
query := "SELECT COUNT(*) FROM message"
|
|
|
|
err := db.QueryRow(query).Scan(&count)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return count, nil
|
2023-01-07 00:39:05 +01:00
|
|
|
}
|
|
|
|
|
2023-07-27 10:44:09 +02:00
|
|
|
func createMessageTable(db *sql.DB) error {
|
2023-01-07 00:39:05 +01:00
|
|
|
createMessageTableSQL := `CREATE TABLE message (
|
|
|
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
2023-02-15 22:08:17 +01:00
|
|
|
"m" BLOB);` // SQL Statement for Create Table
|
2023-01-07 00:39:05 +01:00
|
|
|
statement, err := db.Prepare(createMessageTableSQL) // Prepare SQL Statement
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
statement.Exec() // Execute SQL Statements
|
|
|
|
return nil
|
|
|
|
}
|
2023-07-27 10:44:09 +02:00
|
|
|
|
|
|
|
func createServerTable(db *sql.DB) error {
|
|
|
|
createServerTableSQL := `CREATE TABLE servers (
|
|
|
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
|
|
"country" varchar(2),
|
|
|
|
"public" bool,
|
|
|
|
"uptime" int,
|
|
|
|
"bandwith" float,
|
|
|
|
"load" float,
|
|
|
|
"url" varchar(2000)
|
|
|
|
"name" varchar(255);
|
|
|
|
"description" varchar(5000)
|
|
|
|
"publickey" varchar(10000)
|
|
|
|
)` // SQL Statement for Create Table
|
|
|
|
statement, err := db.Prepare(createServerTableSQL) // Prepare SQL Statement
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
statement.Exec() // Execute SQL Statements
|
|
|
|
return nil
|
|
|
|
}
|