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
2024-03-31 19:04:37 +02:00
cfg := GetConfig ( )
identity := cfg . GetIdentity ( )
2023-01-07 00:39:05 +01:00
// 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 }
2024-03-29 18:07:06 +01:00
2024-03-31 19:04:37 +02:00
identity . Save ( )
identity . CreateFolder ( )
file , err := os . Create ( filepath . Join ( cfg . StoragePath , identity . Uuid , dbid + GetConfig ( ) . DbSuffix ) )
2023-01-07 00:39:05 +01:00
if err != nil {
return err
}
file . Close ( )
peer . DbIds = append ( peer . DbIds , dbid )
2024-03-31 19:04:37 +02:00
sqliteDatabase , _ := sql . Open ( "sqlite3" , filepath . Join ( cfg . StoragePath , identity . Uuid , 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
2024-03-31 19:04:37 +02:00
db , _ := sql . Open ( "sqlite3" , filepath . Join ( cfg . StoragePath , identity . Uuid , 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
}
2024-03-31 19:39:50 +02:00
if _ , err := os . Stat ( filepath . Join ( cfg . StoragePath , identity . Uuid , "securefiles" ) ) ; os . IsNotExist ( err ) {
err = os . MkdirAll ( filepath . Join ( cfg . StoragePath , identity . Uuid , "securefiles" ) , 0755 )
if err != nil {
return err
}
}
os . WriteFile ( filepath . Join ( cfg . StoragePath , identity . Uuid , "securefiles" , hiddenFilename ) , encData , 0600 )
2023-01-07 00:39:05 +01:00
// replace f.Data by uuid filename
2024-03-31 19:39:50 +02:00
f . Data = [ ] byte ( filepath . Join ( cfg . StoragePath , identity . Uuid , "securefiles" , hiddenFilename ) )
2023-01-07 00:39:05 +01:00
}
}
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
2024-03-31 19:04:37 +02:00
identity . 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-31 19:04:37 +02:00
cfg := GetConfig ( )
identity := cfg . GetIdentity ( )
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)
2024-03-31 19:04:37 +02:00
db , _ := sql . Open ( "sqlite3" , filepath . Join ( cfg . StoragePath , identity . Uuid , peer . DbIds [ fileidx ] + GetConfig ( ) . DbSuffix ) ) // Open the created SQLite File
2024-02-17 19:30:25 +01:00
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)
2024-03-29 18:07:06 +01:00
db , _ := sql . Open ( "sqlite3" , filepath . Join ( GetConfig ( ) . StoragePath , GetConfig ( ) . GetIdentity ( ) . Uuid , peer . DbIds [ fileidx ] + GetConfig ( ) . DbSuffix ) ) // Open the created SQLite File
2023-02-15 22:08:17 +01:00
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 ) {
2024-03-29 18:07:06 +01:00
db , _ := sql . Open ( "sqlite3" , filepath . Join ( GetConfig ( ) . StoragePath , GetConfig ( ) . GetIdentity ( ) . Uuid , dbid + GetConfig ( ) . DbSuffix ) ) // Open the created SQLite File
2023-02-15 22:08:17 +01:00
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
}