diff --git a/lib/types/sqlite-types.js b/lib/types/sqlite-types.js new file mode 100644 --- /dev/null +++ b/lib/types/sqlite-types.js @@ -0,0 +1,8 @@ +// @flow + +export type ReceivedMessageToDevice = { + +messageID: string, + +senderDeviceID: string, + +plaintext: string, + +status: string, +}; diff --git a/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h --- a/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h +++ b/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h @@ -12,6 +12,7 @@ #include "entities/OlmPersistAccount.h" #include "entities/OlmPersistSession.h" #include "entities/PersistItem.h" +#include "entities/ReceivedMessageToDevice.h" #include "entities/Report.h" #include "entities/SyncedMetadataEntry.h" #include "entities/Thread.h" @@ -142,6 +143,12 @@ const ClientMessageToDevice &lastConfirmedMessage) const = 0; virtual void removeAllMessagesForDevice(const std::string &deviceID) const = 0; + virtual void + addReceivedMessageToDevice(ReceivedMessageToDevice message) const = 0; + virtual std::vector + getAllReceivedMessageToDevice() const = 0; + virtual void + removeReceivedMessagesToDevice(const std::vector &ids) const = 0; #ifdef EMSCRIPTEN virtual std::vector getAllThreadsWeb() const = 0; diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h @@ -152,6 +152,12 @@ void removeMessagesToDeviceOlderThan( const ClientMessageToDevice &lastConfirmedMessage) const override; void removeAllMessagesForDevice(const std::string &deviceID) const override; + void + addReceivedMessageToDevice(ReceivedMessageToDevice message) const override; + std::vector + getAllReceivedMessageToDevice() const override; + void removeReceivedMessagesToDevice( + const std::vector &ids) const override; #ifdef EMSCRIPTEN std::vector getAllThreadsWeb() const override; diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp @@ -2155,6 +2155,41 @@ SQLiteQueryExecutor::getConnection(), removeMessagesSQL, keys); } +void SQLiteQueryExecutor::addReceivedMessageToDevice( + ReceivedMessageToDevice message) const { + static std::string addMessage = + "REPLACE INTO received_messages_to_device (" + " message_id, sender_device_id, plaintext, status)" + "VALUES (?, ?, ?, ?);"; + + replaceEntity( + SQLiteQueryExecutor::getConnection(), addMessage, message); +} + +std::vector +SQLiteQueryExecutor::getAllReceivedMessageToDevice() const { + static std::string query = + "SELECT message_id, sender_device_id, plaintext, status " + "FROM received_messages_to_device;"; + return getAllEntities( + SQLiteQueryExecutor::getConnection(), query); +} + +void SQLiteQueryExecutor::removeReceivedMessagesToDevice( + const std::vector &ids) const { + if (!ids.size()) { + return; + } + + std::stringstream removeMessagesSQLStream; + removeMessagesSQLStream << "DELETE FROM received_messages_to_device " + "WHERE message_id IN " + << getSQLStatementArray(ids.size()) << ";"; + + removeEntitiesByKeys( + SQLiteQueryExecutor::getConnection(), removeMessagesSQLStream.str(), ids); +} + #ifdef EMSCRIPTEN std::vector SQLiteQueryExecutor::getAllThreadsWeb() const { auto threads = this->getAllThreads(); diff --git a/native/cpp/CommonCpp/DatabaseManagers/entities/ReceivedMessageToDevice.h b/native/cpp/CommonCpp/DatabaseManagers/entities/ReceivedMessageToDevice.h new file mode 100644 --- /dev/null +++ b/native/cpp/CommonCpp/DatabaseManagers/entities/ReceivedMessageToDevice.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +#include "SQLiteDataConverters.h" + +namespace comm { + +struct ReceivedMessageToDevice { + std::string message_id; + std::string sender_device_id; + std::string plaintext; + std::string status; + + static ReceivedMessageToDevice fromSQLResult(sqlite3_stmt *sqlRow, int idx) { + return ReceivedMessageToDevice{ + getStringFromSQLRow(sqlRow, idx), + getStringFromSQLRow(sqlRow, idx + 1), + getStringFromSQLRow(sqlRow, idx + 2), + getStringFromSQLRow(sqlRow, idx + 3), + }; + } + + int bindToSQL(sqlite3_stmt *sql, int idx) const { + bindStringToSQL(message_id, sql, idx); + bindStringToSQL(sender_device_id, sql, idx + 1); + bindStringToSQL(plaintext, sql, idx + 2); + return bindStringToSQL(status, sql, idx + 3); + } +}; + +} // namespace comm diff --git a/web/cpp/SQLiteQueryExecutorBindings.cpp b/web/cpp/SQLiteQueryExecutorBindings.cpp --- a/web/cpp/SQLiteQueryExecutorBindings.cpp +++ b/web/cpp/SQLiteQueryExecutorBindings.cpp @@ -1,6 +1,7 @@ #include "SQLiteQueryExecutor.cpp" #include "entities/MessageToDevice.h" #include "entities/Nullable.h" +#include "entities/ReceivedMessageToDevice.h" #include #include @@ -121,6 +122,12 @@ .field("plaintext", &ClientMessageToDevice::plaintext) .field("ciphertext", &ClientMessageToDevice::ciphertext); + value_object("ReceivedMessageToDevice") + .field("messageID", &ReceivedMessageToDevice::message_id) + .field("senderDeviceID", &ReceivedMessageToDevice::sender_device_id) + .field("plaintext", &ReceivedMessageToDevice::plaintext) + .field("status", &ReceivedMessageToDevice::status); + class_("SQLiteQueryExecutor") .constructor() .function("updateDraft", &SQLiteQueryExecutor::updateDraft) @@ -264,7 +271,16 @@ &SQLiteQueryExecutor::removeAllMessagesForDevice) .function( "getAllMessagesToDevice", - &SQLiteQueryExecutor::getAllMessagesToDevice); + &SQLiteQueryExecutor::getAllMessagesToDevice) + .function( + "addReceivedMessageToDevice", + &SQLiteQueryExecutor::addReceivedMessageToDevice) + .function( + "getAllReceivedMessageToDevice", + &SQLiteQueryExecutor::getAllReceivedMessageToDevice) + .function( + "removeReceivedMessagesToDevice", + &SQLiteQueryExecutor::removeReceivedMessagesToDevice); } } // namespace comm diff --git a/web/shared-worker/_generated/comm-query-executor.js b/web/shared-worker/_generated/comm-query-executor.js --- a/web/shared-worker/_generated/comm-query-executor.js +++ b/web/shared-worker/_generated/comm-query-executor.js @@ -117,7 +117,7 @@ V.prototype.fromWireType=function(a){function b(){return this.Vb?Jb(this.Ta.Eb,{Xa:this.Md,Sa:c,fb:this,ab:a}):Jb(this.Ta.Eb,{Xa:this,Sa:a})}var c=this.xd(a);if(!c)return this.Hc(a),null;var d=Ib(this.Ta,c);if(void 0!==d){if(0===d.Na.count.value)return d.Na.Sa=c,d.Na.ab=a,d.clone();d=d.clone();this.Hc(a);return d}d=this.Ta.vd(c);d=Db[d];if(!d)return b.call(this);d=this.Tb?d.jd:d.pointerType;var f=Cb(c,this.Ta,d.Ta);return null===f?b.call(this):this.Vb?Jb(d.Ta.Eb,{Xa:d,Sa:f,fb:this,ab:a}):Jb(d.Ta.Eb, {Xa:d,Sa:f})};Wb=e.UnboundTypeError=rb("UnboundTypeError");e.count_emval_handles=function(){for(var a=0,b=5;bf?-28:N.Fc(d,f).fd;case 1:case 2:return 0;case 3:return d.flags;case 4:return f=hb(),d.flags|=f,0;case 5:return f=hb(),D[f+0>>1]=2,0;case 6:case 7:return 0;case 16:case 8:return-28;case 9:return E[Kc()>>2]=28,-1;default:return-28}}catch(g){if("undefined"== +b),0}catch(c){if("undefined"==typeof N||!(c instanceof N.Ma))throw c;return-c.Ra}},Z:function(a,b,c){try{return N.fchown(a,b,c),0}catch(d){if("undefined"==typeof N||!(d instanceof N.Ma))throw d;return-d.Ra}},i:function(a,b,c){gb=c;try{var d=Q(a);switch(b){case 0:var f=hb();return 0>f?-28:N.Fc(d,f).fd;case 1:case 2:return 0;case 3:return d.flags;case 4:return f=hb(),d.flags|=f,0;case 5:return f=hb(),D[f+0>>1]=2,0;case 6:case 7:return 0;case 16:case 8:return-28;case 9:return E[Kc()>>2]=28,-1;default:return-28}}catch(g){if("undefined"== typeof N||!(g instanceof N.Ma))throw g;return-g.Ra}},X:function(a,b){try{var c=Q(a);return fb(N.stat,c.path,b)}catch(d){if("undefined"==typeof N||!(d instanceof N.Ma))throw d;return-d.Ra}},E:function(a,b,c){try{b=c+2097152>>>0<4194305-!!b?(b>>>0)+4294967296*c:NaN;if(isNaN(b))return-61;N.ud(a,b);return 0}catch(d){if("undefined"==typeof N||!(d instanceof N.Ma))throw d;return-d.Ra}},S:function(a,b){try{if(0===b)return-28;var c=N.cwd(),d=ta(c)+1;if(b>>0,(G=h,1<=+Math.abs(G)?0>>0:~~+Math.ceil((G-+(~~G>>>0))/4294967296)>>> 0:0)];E[b+a>>2]=J[0];E[b+a+4>>2]=J[1];J=[280*(g+1)>>>0,(G=280*(g+1),1<=+Math.abs(G)?0>>0:~~+Math.ceil((G-+(~~G>>>0))/4294967296)>>>0:0)];E[b+a+8>>2]=J[0];E[b+a+12>>2]=J[1];D[b+a+16>>1]=280;B[b+a+18>>0]=n;A(k,z,b+a+19,256);a+=280;g+=1}N.bb(d,280*g,0);return a}catch(p){if("undefined"==typeof N||!(p instanceof N.Ma))throw p;return-p.Ra}},Y:function(a,b,c){gb=c;try{var d=Q(a);switch(b){case 21509:case 21505:return d.tty?0:-59;case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:return d.tty? @@ -138,7 +138,7 @@ F[n>>2],p=k(),t,x=n+4,l=0;l<=q;++l){var u=n+4+l*b;if(l==q||0==p[u>>h])x=d(x,u-x),void 0===t?t=x:(t+=String.fromCharCode(0),t+=x),x=u+b}X(n);return t},toWireType:function(n,q){"string"!=typeof q&&T("Cannot pass non-string to C++ string type "+c);var p=g(q),t=wc(4+p+b);F[t>>2]=p>>h;f(q,t+4,p+b);null!==n&&n.push(X,t);return t},argPackAdvance:8,readValueFromPointer:lb,ib:function(n){X(n)}})},f:function(a,b,c,d,f,g){jb[a]={name:S(b),rc:W(c,d),pb:W(f,g),Lc:[]}},d:function(a,b,c,d,f,g,k,h,n,q){jb[a].Lc.push({rd:S(b), zd:c,Sb:W(d,f),yd:g,Sd:k,Rd:W(h,n),Td:q})},ga:function(a,b){b=S(b);R(a,{Dd:!0,name:b,argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},o:function(){return Date.now()},aa:function(){return!0},k:function(a,b,c){a=ec(a);b=oc(b,"emval::as");var d=[],f=Sb(d);F[c>>2]=f;return b.toWireType(d,a)},A:function(a,b,c,d){a=rc[a];b=ec(b);c=qc(c);a(b,c,null,d)},na:dc,oa:function(a,b){var c=tc(a,b),d=c[0];b=d.name+"_$"+c.slice(1).map(function(p){return p.name}).join("_")+"$";var f=uc[b];if(void 0!== f)return f;f=["retType"];for(var g=[d],k="",h=0;h>2]=a.getSeconds();E[b+4>>2]=a.getMinutes();E[b+8>>2]=a.getHours();E[b+12>>2]=a.getDate();E[b+16>>2]=a.getMonth();E[b+20>>2]=a.getFullYear()- +f.push(n+"};\n");a=ac(f).apply(null,g);f=sc(a);return uc[b]=f},z:function(a,b){a=ec(a);b=ec(b);return Sb(a[b])},C:function(a){4>2]=a.getSeconds();E[b+4>>2]=a.getMinutes();E[b+8>>2]=a.getHours();E[b+12>>2]=a.getDate();E[b+16>>2]=a.getMonth();E[b+20>>2]=a.getFullYear()- 1900;E[b+24>>2]=a.getDay();var c=new Date(a.getFullYear(),0,1);E[b+28>>2]=(a.getTime()-c.getTime())/864E5|0;E[b+36>>2]=-(60*a.getTimezoneOffset());var d=(new Date(a.getFullYear(),6,1)).getTimezoneOffset();c=c.getTimezoneOffset();E[b+32>>2]=(d!=c&&a.getTimezoneOffset()==Math.min(c,d))|0},N:function(a,b,c,d,f,g){try{var k=N.rb(d);if(!k)return-8;var h=N.yb(k,a,f,b,c),n=h.Sa;E[g>>2]=h.zc;return n}catch(q){if("undefined"==typeof N||!(q instanceof N.Ma))throw q;return-q.Ra}},O:function(a,b,c,d,f,g){try{var k= N.rb(f);if(k&&c&2){var h=z.slice(a,a+b);N.Fb(k,h,g,b,d)}}catch(n){if("undefined"==typeof N||!(n instanceof N.Ma))throw n;return-n.Ra}},ca:yc,m:function(){v("")},J:function(){return 2147483648},w:zc,n:function(a){var b=z.length;a>>>=0;if(2147483648=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);var f=Math;d=Math.max(a,d);f=f.min.call(f,2147483648,d+(65536-d%65536)%65536);a:{try{pa.grow(f-ua.byteLength+65535>>>16);ya();var g=1;break a}catch(k){}g=void 0}if(g)return!0}return!1}, Q:function(a,b){var c=0;Bc().forEach(function(d,f){var g=b+c;f=F[a+4*f>>2]=g;for(g=0;g>0]=d.charCodeAt(g);B[f>>0]=0;c+=d.length+1});return 0},R:function(a,b){var c=Bc();F[a>>2]=c.length;var d=0;c.forEach(function(f){d+=f.length+1});F[b>>2]=d;return 0},l:function(a){try{var b=Q(a);N.close(b);return 0}catch(c){if("undefined"==typeof N||!(c instanceof N.Ma))throw c;return c.Ra}},H:function(a,b){try{var c=Q(a);B[b>>0]=c.tty?2:N.Ya(c.mode)?3:N.vb(c.mode)?7:4;return 0}catch(d){if("undefined"== diff --git a/web/shared-worker/_generated/comm_query_executor.wasm b/web/shared-worker/_generated/comm_query_executor.wasm index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ { + let queryExecutor: ?SQLiteQueryExecutor = null; + let dbModule: ?EmscriptenModule = null; + + beforeAll(async () => { + dbModule = getDatabaseModule(); + }); + + beforeEach(() => { + if (!dbModule) { + throw new Error('Database module is missing'); + } + queryExecutor = new dbModule.SQLiteQueryExecutor(FILE_PATH); + if (!queryExecutor) { + throw new Error('SQLiteQueryExecutor is missing'); + } + queryExecutor?.addReceivedMessageToDevice(TEST_MSG_1); + queryExecutor?.addReceivedMessageToDevice(TEST_MSG_2); + queryExecutor?.addReceivedMessageToDevice(TEST_MSG_3); + }); + + afterEach(() => { + if (!dbModule || !queryExecutor) { + return; + } + clearSensitiveData(dbModule, FILE_PATH, queryExecutor); + }); + + it('should return all messages', () => { + const messages = queryExecutor?.getAllReceivedMessageToDevice() ?? []; + expect(messages.length).toBe(3); + expect(messages).toStrictEqual(messagesOrdered); + }); + it('should remove messages', () => { + queryExecutor?.removeReceivedMessagesToDevice([TEST_MSG_2.messageID]); + const messages = queryExecutor?.getAllReceivedMessageToDevice() ?? []; + expect(messages.length).toBe(2); + expect(messages).toStrictEqual([TEST_MSG_1, TEST_MSG_3]); + }); +}); diff --git a/web/shared-worker/types/sqlite-query-executor.js b/web/shared-worker/types/sqlite-query-executor.js --- a/web/shared-worker/types/sqlite-query-executor.js +++ b/web/shared-worker/types/sqlite-query-executor.js @@ -9,6 +9,7 @@ import type { ClientDBThreadActivityEntry } from 'lib/ops/thread-activity-store-ops.js'; import type { ClientDBUserInfo } from 'lib/ops/user-store-ops.js'; import type { ClientDBDraftInfo } from 'lib/types/draft-types.js'; +import type { ReceivedMessageToDevice } from 'lib/types/sqlite-types.js'; import { type WebClientDBThreadInfo, @@ -171,6 +172,10 @@ deviceID: string, ): $ReadOnlyArray; + addReceivedMessageToDevice(message: ReceivedMessageToDevice): void; + getAllReceivedMessageToDevice(): $ReadOnlyArray; + removeReceivedMessagesToDevice(ids: $ReadOnlyArray): void; + // method is provided to manually signal that a C++ object // is no longer needed and can be deleted delete(): void;