core-3nweb-client-lib 0.49.6 → 0.49.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api-defs/asmail.d.ts +1 -0
- package/build/api-defs/files.d.ts +11 -5
- package/build/core/asmail/delivery/common.d.ts +8 -28
- package/build/core/asmail/delivery/common.js +1 -47
- package/build/core/asmail/delivery/index.js +3 -3
- package/build/core/asmail/delivery/per-recipient-wip.js +3 -2
- package/build/core/asmail/inbox/index.d.ts +8 -32
- package/build/core/asmail/inbox/index.js +120 -170
- package/build/core/asmail/inbox/msg-indexing/dataset/v0/sql-db-v-none.d.ts +11 -0
- package/build/{lib-client/xsp-fs/util/for-arrays.js → core/asmail/inbox/msg-indexing/dataset/v0/sql-db-v-none.js} +7 -13
- package/build/core/asmail/inbox/msg-indexing/dataset/v1/logs.d.ts +33 -0
- package/build/core/asmail/inbox/msg-indexing/dataset/v1/logs.js +72 -0
- package/build/core/asmail/inbox/msg-indexing/dataset/v1/sql-db.d.ts +28 -0
- package/build/core/asmail/inbox/msg-indexing/dataset/v1/sql-db.js +477 -0
- package/build/core/asmail/inbox/msg-indexing/index.d.ts +12 -26
- package/build/core/asmail/inbox/msg-indexing/index.js +47 -68
- package/build/core/asmail/inbox/msg-on-disk.d.ts +0 -5
- package/build/core/asmail/inbox/msg-on-disk.js +2 -24
- package/build/core/asmail/index.js +8 -7
- package/build/core/asmail/msg/common.d.ts +0 -1
- package/build/core/asmail/msg/common.js +1 -1
- package/build/core/index.js +7 -4
- package/build/core/keyring/common.d.ts +1 -0
- package/build/core/keyring/common.js +2 -1
- package/build/core/keyring/index.d.ts +7 -44
- package/build/core/keyring/index.js +88 -222
- package/build/core/keyring/introductory-keys/published-intro-key.d.ts +17 -0
- package/build/core/keyring/introductory-keys/published-intro-key.js +190 -0
- package/build/core/keyring/peer-keys/dataset/v1/json-files.d.ts +105 -0
- package/build/core/keyring/peer-keys/dataset/v1/json-files.js +89 -0
- package/build/core/keyring/peer-keys/dataset/v2/logs.d.ts +43 -0
- package/build/core/keyring/peer-keys/dataset/v2/logs.js +116 -0
- package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.d.ts +73 -0
- package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.js +543 -0
- package/build/core/keyring/peer-keys/index.d.ts +24 -0
- package/build/core/keyring/peer-keys/index.js +208 -0
- package/build/core/storage/synced/obj-files.js +10 -3
- package/build/core/storage/synced/obj-status.d.ts +1 -1
- package/build/core/storage/synced/obj-status.js +8 -2
- package/build/core/storage/synced/storage.d.ts +4 -4
- package/build/core/storage/system-folders/apps-data.d.ts +0 -1
- package/build/core/storage/system-folders/apps-data.js +2 -6
- package/build/lib-client/asmail/recipient.d.ts +1 -0
- package/build/lib-client/asmail/recipient.js +11 -0
- package/build/lib-client/fs-utils/fs-sync-utils.d.ts +1 -1
- package/build/lib-client/fs-utils/fs-sync-utils.js +70 -13
- package/build/lib-client/xsp-fs/folder-node.js +13 -3
- package/build/lib-client/xsp-fs/fs.js +3 -1
- package/build/lib-client/xsp-fs/node-in-fs.d.ts +1 -0
- package/build/lib-client/xsp-fs/node-in-fs.js +7 -2
- package/build/lib-client/xsp-fs/node-persistence.js +1 -3
- package/build/lib-client/xsp-fs/xsp-payload-v2.js +1 -2
- package/build/lib-common/dataset-sync/per-device-json-array-logs.d.ts +58 -0
- package/build/lib-common/dataset-sync/per-device-json-array-logs.js +432 -0
- package/build/lib-common/dataset-sync/single-file.d.ts +10 -0
- package/build/lib-common/dataset-sync/single-file.js +77 -0
- package/build/lib-common/number-line.d.ts +10 -0
- package/build/lib-common/number-line.js +66 -0
- package/build/lib-common/objs-on-disk/obj-version-file.d.ts +1 -1
- package/build/lib-common/objs-on-disk/obj-version-file.js +3 -3
- package/build/lib-common/rng-def.d.ts +1 -0
- package/build/lib-common/rng-def.js +8 -0
- package/build/lib-sqlite-on-3nstorage/for-sqlite.d.ts +38 -0
- package/build/lib-sqlite-on-3nstorage/for-sqlite.js +162 -0
- package/build/lib-sqlite-on-3nstorage/index.d.ts +18 -5
- package/build/lib-sqlite-on-3nstorage/index.js +49 -26
- package/build/tests/caps-api/file-sink-checks/splice.js +72 -1
- package/build/tests/caps-api/fs-checks/sync-on-one-dev/upload.js +27 -1
- package/build/tests/caps-api/fs-checks/sync-with-two-devs/update-propagation.js +12 -3
- package/build/tests/computer.3nweb.core/inbox/msg-indexing.js +16 -12
- package/build/tests/computer.3nweb.core/keyrings.js +113 -28
- package/build/tests/jasmine.js +2 -2
- package/build/tests/units/number-line.js +9 -9
- package/package.json +2 -1
- package/build/core/asmail/inbox/msg-indexing/logs-n-entries.d.ts +0 -52
- package/build/core/asmail/inbox/msg-indexing/logs-n-entries.js +0 -333
- package/build/core/asmail/inbox/msg-indexing/sql-indexing.d.ts +0 -4
- package/build/core/asmail/inbox/msg-indexing/sql-indexing.js +0 -319
- package/build/core/keyring/correspondent-keys.d.ts +0 -149
- package/build/core/keyring/correspondent-keys.js +0 -403
- package/build/core/keyring/id-to-email-map.d.ts +0 -38
- package/build/core/keyring/id-to-email-map.js +0 -105
- package/build/core/keyring/keyring-storage.d.ts +0 -9
- package/build/core/keyring/keyring-storage.js +0 -54
- package/build/core/keyring/published-intro-key.d.ts +0 -45
- package/build/core/keyring/published-intro-key.js +0 -200
- package/build/lib-client/xsp-fs/util/for-arrays.d.ts +0 -1
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.
|
|
20
|
-
const error_1 = require("../../../lib-common/exceptions/error");
|
|
19
|
+
exports.makeInboxOnServer = makeInboxOnServer;
|
|
21
20
|
const synced_1 = require("../../../lib-common/processes/synced");
|
|
22
21
|
const recipient_1 = require("../../../lib-client/asmail/recipient");
|
|
23
22
|
const opener_1 = require("../msg/opener");
|
|
@@ -32,197 +31,161 @@ const cached_msgs_1 = require("./cached-msgs");
|
|
|
32
31
|
const timed_cache_1 = require("../../../lib-common/timed-cache");
|
|
33
32
|
const fs_sync_utils_1 = require("../../../lib-client/fs-utils/fs-sync-utils");
|
|
34
33
|
const MSG_INDEX_FOLDER = 'msg-index';
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.cache = cache;
|
|
51
|
-
this.index = index;
|
|
52
|
-
this.logError = logError;
|
|
53
|
-
this.procs = new synced_1.NamedProcs();
|
|
54
|
-
this.recentlyOpenedMsgs = (0, timed_cache_1.makeTimedCache)(60 * 1000);
|
|
55
|
-
this.inboxEvents = new inbox_events_1.InboxEvents(this.msgReceiver, this.getMsg.bind(this), this.listNewMsgs.bind(this), this.removeMsg.bind(this), this.logError);
|
|
56
|
-
Object.seal(this);
|
|
34
|
+
async function makeInboxOnServer(cachePath, syncedFS, localFS, r) {
|
|
35
|
+
const procs = new synced_1.NamedProcs();
|
|
36
|
+
const recentlyOpenedMsgs = (0, timed_cache_1.makeTimedCache)(60 * 1000);
|
|
37
|
+
const { getStorages, cryptor, random, logError, } = r;
|
|
38
|
+
(0, file_1.ensureCorrectFS)(syncedFS, 'synced', true);
|
|
39
|
+
const indexSyncedFS = await (0, fs_sync_utils_1.getOrMakeSyncedDirOnInit)(syncedFS, MSG_INDEX_FOLDER);
|
|
40
|
+
const indexLocalFS = await localFS.writableSubRoot(MSG_INDEX_FOLDER);
|
|
41
|
+
const msgReceiver = new recipient_1.MailRecipient(r.address, r.getSigner, () => r.asmailResolver(r.address), r.makeNet());
|
|
42
|
+
const downloader = new msg_downloader_1.MsgDownloader(msgReceiver);
|
|
43
|
+
const cache = await cached_msgs_1.CachedMessages.makeFor(cachePath, downloader, r.logError);
|
|
44
|
+
const index = await (0, msg_indexing_1.makeMsgIndex)(indexSyncedFS, indexLocalFS, r.logError);
|
|
45
|
+
const inboxEvents = new inbox_events_1.InboxEvents(msgReceiver, getMsg, listNewMsgs, removeMsg, r.logError);
|
|
46
|
+
async function close() {
|
|
47
|
+
index.stopSyncing();
|
|
48
|
+
inboxEvents.close();
|
|
57
49
|
}
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
(0, file_1.ensureCorrectFS)(syncedFS, 'synced', true);
|
|
61
|
-
const msgReceiver = new recipient_1.MailRecipient(r.address, r.getSigner, () => r.asmailResolver(r.address), r.makeNet());
|
|
62
|
-
const downloader = new msg_downloader_1.MsgDownloader(msgReceiver);
|
|
63
|
-
const cache = await cached_msgs_1.CachedMessages.makeFor(cachePath, downloader, r.logError);
|
|
64
|
-
const indexSyncedFS = await (0, fs_sync_utils_1.getOrMakeAndUploadFolderIn)(syncedFS, MSG_INDEX_FOLDER);
|
|
65
|
-
const index = await msg_indexing_1.MsgIndex.make(indexSyncedFS);
|
|
66
|
-
await (0, fs_sync_utils_1.uploadFolderChangesIfAny)(syncedFS);
|
|
67
|
-
const inbox = new InboxOnServer(r.correspondents, msgReceiver, r.getStorages, r.cryptor, r.random, downloader, cache, index, r.logError);
|
|
68
|
-
return inbox;
|
|
69
|
-
}
|
|
70
|
-
catch (err) {
|
|
71
|
-
throw (0, error_1.errWithCause)(err, 'Failed to initialize Inbox');
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
async close() {
|
|
75
|
-
this.index.stopSyncing();
|
|
76
|
-
this.inboxEvents.close();
|
|
77
|
-
}
|
|
78
|
-
makeCAP() {
|
|
50
|
+
function makeCAP() {
|
|
79
51
|
const service = {
|
|
80
|
-
getMsg
|
|
81
|
-
listMsgs
|
|
82
|
-
removeMsg
|
|
83
|
-
subscribe:
|
|
52
|
+
getMsg,
|
|
53
|
+
listMsgs,
|
|
54
|
+
removeMsg,
|
|
55
|
+
subscribe: inboxEvents.subscribe.bind(inboxEvents)
|
|
84
56
|
};
|
|
85
57
|
return Object.freeze(service);
|
|
86
58
|
}
|
|
87
|
-
async removeMsg(msgId) {
|
|
59
|
+
async function removeMsg(msgId) {
|
|
88
60
|
// check for an already started process
|
|
89
61
|
const procId = 'removal of ' + msgId;
|
|
90
|
-
const promise =
|
|
62
|
+
const promise = procs.latestTaskAtThisMoment(procId);
|
|
91
63
|
if (promise) {
|
|
92
64
|
return promise;
|
|
93
65
|
}
|
|
94
66
|
// start removal process
|
|
95
|
-
return
|
|
67
|
+
return procs.start(procId, (async () => {
|
|
96
68
|
await Promise.all([
|
|
97
|
-
|
|
98
|
-
|
|
69
|
+
index.remove(msgId),
|
|
70
|
+
removeMsgFromServerAndCache(msgId)
|
|
99
71
|
]);
|
|
100
72
|
}));
|
|
101
73
|
}
|
|
102
|
-
async removeMsgFromServerAndCache(msgId) {
|
|
74
|
+
async function removeMsgFromServerAndCache(msgId) {
|
|
103
75
|
await Promise.all([
|
|
104
|
-
|
|
76
|
+
cache.deleteMsg(msgId),
|
|
105
77
|
// XXX the following is suspicious, as it swallows other exceptions too
|
|
106
|
-
|
|
78
|
+
msgReceiver.removeMsg(msgId).catch(() => { })
|
|
107
79
|
]);
|
|
108
80
|
}
|
|
109
|
-
async msgFromDiskOrDownload(msgId) {
|
|
110
|
-
const msgOnDisk = await
|
|
81
|
+
async function msgFromDiskOrDownload(msgId) {
|
|
82
|
+
const msgOnDisk = await cache.findMsg(msgId);
|
|
111
83
|
if (msgOnDisk) {
|
|
112
84
|
return msgOnDisk;
|
|
113
85
|
}
|
|
114
|
-
const meta = await
|
|
115
|
-
return await
|
|
86
|
+
const meta = await downloader.getMsgMeta(msgId);
|
|
87
|
+
return await cache.addMsg(msgId, meta);
|
|
116
88
|
}
|
|
117
|
-
|
|
118
|
-
|
|
89
|
+
function checkerOfMidKeyCerts(deliveryTS) {
|
|
90
|
+
return (certs) => (0, key_verification_1.checkAndExtractPKeyWithAddress)(msgReceiver.getNet(), r.correspondents.midResolver, certs, Math.round(deliveryTS / 1000));
|
|
91
|
+
}
|
|
92
|
+
function msgReadingAndOpening(msgId, mainObjId, msgOnDisk) {
|
|
93
|
+
// lazy main obj source
|
|
94
|
+
let mainObjSrc = undefined;
|
|
95
|
+
async function getMainObjHeader() {
|
|
96
|
+
if (!mainObjSrc) {
|
|
97
|
+
mainObjSrc = await msgOnDisk.getMsgObj(mainObjId);
|
|
98
|
+
}
|
|
99
|
+
return mainObjSrc.readHeader();
|
|
100
|
+
}
|
|
101
|
+
async function getOpenedMsg(mainObjFileKey, msgKeyPackLen) {
|
|
102
|
+
if (!mainObjSrc) {
|
|
103
|
+
mainObjSrc = await msgOnDisk.getMsgObj(mainObjId);
|
|
104
|
+
}
|
|
105
|
+
return await (0, opener_1.openMsg)(msgId, mainObjId, mainObjSrc, msgKeyPackLen, mainObjFileKey, cryptor);
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
getMainObjHeader, getOpenedMsg
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
async function startCachingAndAddKeyToIndex(msgId) {
|
|
112
|
+
const msgOnDisk = await msgFromDiskOrDownload(msgId);
|
|
119
113
|
const meta = await msgOnDisk.getMsgMeta();
|
|
114
|
+
const { getMainObjHeader, getOpenedMsg } = msgReadingAndOpening(msgId, meta.extMeta.objIds[0], msgOnDisk);
|
|
120
115
|
try {
|
|
121
|
-
|
|
122
|
-
let mainObjSrc = undefined;
|
|
123
|
-
const mainObjId = meta.extMeta.objIds[0];
|
|
124
|
-
const getMainObjHeader = async () => {
|
|
125
|
-
if (!mainObjSrc) {
|
|
126
|
-
mainObjSrc = await msgOnDisk.getMsgObj(mainObjId);
|
|
127
|
-
}
|
|
128
|
-
return mainObjSrc.readHeader();
|
|
129
|
-
};
|
|
130
|
-
const getOpenedMsg = async (mainObjFileKey, msgKeyPackLen) => {
|
|
131
|
-
if (!mainObjSrc) {
|
|
132
|
-
mainObjSrc = await msgOnDisk.getMsgObj(mainObjId);
|
|
133
|
-
}
|
|
134
|
-
const openedMsg = await (0, opener_1.openMsg)(msgId, mainObjId, mainObjSrc, msgKeyPackLen, mainObjFileKey, this.cryptor);
|
|
135
|
-
return openedMsg;
|
|
136
|
-
};
|
|
137
|
-
const checkMidKeyCerts = (certs) => {
|
|
138
|
-
return (0, key_verification_1.checkAndExtractPKeyWithAddress)(this.msgReceiver.getNet(), this.r.midResolver, certs, Math.round(msgOnDisk.deliveryTS / 1000));
|
|
139
|
-
};
|
|
140
|
-
const decrOut = await this.r.msgDecryptor(meta.extMeta, getMainObjHeader, getOpenedMsg, checkMidKeyCerts);
|
|
116
|
+
const decrOut = await r.correspondents.msgDecryptor(meta.extMeta, getMainObjHeader, getOpenedMsg, checkerOfMidKeyCerts(msgOnDisk.deliveryTS));
|
|
141
117
|
if (decrOut) {
|
|
142
118
|
const { decrInfo, openedMsg } = decrOut;
|
|
143
119
|
openedMsg.setMsgKeyRole(decrInfo.keyStatus);
|
|
144
|
-
this
|
|
120
|
+
// XXX we have never used this part of protocol, should it be removed at all from ASMail protocol?
|
|
121
|
+
checkServerAuthIfPresent(meta, decrInfo);
|
|
145
122
|
// add records cache and to index
|
|
146
|
-
|
|
123
|
+
recentlyOpenedMsgs.set(msgId, openedMsg);
|
|
147
124
|
const msgInfo = {
|
|
148
125
|
msgType: openedMsg.getSection('Msg Type'),
|
|
149
126
|
msgId,
|
|
150
127
|
deliveryTS: msgOnDisk.deliveryTS
|
|
151
128
|
};
|
|
152
|
-
await
|
|
129
|
+
await index.add(msgInfo, decrInfo);
|
|
153
130
|
await Promise.all([
|
|
154
|
-
|
|
155
|
-
|
|
131
|
+
absorbSendingParams(openedMsg),
|
|
132
|
+
markOwnParams(meta, openedMsg.sender)
|
|
156
133
|
]);
|
|
157
134
|
}
|
|
158
135
|
else {
|
|
159
136
|
// check, if msg has already been indexed
|
|
160
|
-
const knownDecr = await
|
|
137
|
+
const knownDecr = await index.getKeyFor(msgId, msgOnDisk.deliveryTS);
|
|
161
138
|
if (!knownDecr) {
|
|
162
|
-
await msgOnDisk.updateMsgKeyStatus('not-found');
|
|
163
139
|
return false;
|
|
164
140
|
}
|
|
165
|
-
// TODO try to open main message, just as a check
|
|
166
141
|
}
|
|
167
|
-
await msgOnDisk.updateMsgKeyStatus('ok');
|
|
168
142
|
return true;
|
|
169
143
|
}
|
|
170
144
|
catch (exc) {
|
|
171
|
-
await
|
|
172
|
-
if (msgOnDisk.keyStatus !== 'ok') {
|
|
173
|
-
await msgOnDisk.updateMsgKeyStatus('fail');
|
|
174
|
-
}
|
|
145
|
+
await logError(exc, `Problem with opening message ${msgId}`);
|
|
175
146
|
return false;
|
|
176
147
|
}
|
|
177
148
|
}
|
|
178
|
-
async markOwnParams(meta, sender) {
|
|
149
|
+
async function markOwnParams(meta, sender) {
|
|
179
150
|
if (!meta.invite) {
|
|
180
151
|
return;
|
|
181
152
|
}
|
|
182
|
-
await
|
|
153
|
+
await r.correspondents.markOwnSendingParamsAsUsed(sender, meta.invite);
|
|
183
154
|
}
|
|
184
|
-
async absorbSendingParams(openedMsg) {
|
|
155
|
+
async function absorbSendingParams(openedMsg) {
|
|
185
156
|
const sendingParams = openedMsg.nextSendingParams;
|
|
186
157
|
if (!sendingParams) {
|
|
187
158
|
return;
|
|
188
159
|
}
|
|
189
160
|
const address = openedMsg.sender;
|
|
190
|
-
await
|
|
161
|
+
await r.correspondents.saveParamsForSendingTo(address, sendingParams);
|
|
191
162
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
&& !(0, canonical_address_1.areAddressesEqual)(meta.authSender, decrInfo.correspondent)) {
|
|
197
|
-
throw new Error(`Sender authenticated to server as ${meta.authSender}, while decrypting key is associated with ${decrInfo.correspondent}`);
|
|
163
|
+
function debouncingProc(procId, action) {
|
|
164
|
+
const promise = procs.latestTaskAtThisMoment(procId);
|
|
165
|
+
if (promise) {
|
|
166
|
+
return promise;
|
|
198
167
|
}
|
|
168
|
+
return procs.start(procId, action);
|
|
199
169
|
}
|
|
200
|
-
async listMsgs(fromTS) {
|
|
170
|
+
async function listMsgs(fromTS) {
|
|
201
171
|
const checkServer = true; // XXX in future this will be an option from req
|
|
202
172
|
if (!checkServer) {
|
|
203
|
-
return
|
|
204
|
-
}
|
|
205
|
-
// check for an already started process
|
|
206
|
-
const procId = 'listing msgs';
|
|
207
|
-
const promise = this.procs.latestTaskAtThisMoment(procId);
|
|
208
|
-
if (promise) {
|
|
209
|
-
return promise;
|
|
173
|
+
return index.listMsgs(fromTS);
|
|
210
174
|
}
|
|
211
|
-
|
|
212
|
-
return this.procs.start(procId, async () => {
|
|
175
|
+
return debouncingProc('listing msgs', async () => {
|
|
213
176
|
// message listing info is located in index, yet, process involves
|
|
214
177
|
// getting and caching messages' metadata
|
|
215
178
|
let msgIds;
|
|
216
179
|
try {
|
|
217
|
-
msgIds = await
|
|
180
|
+
msgIds = await msgReceiver.listMsgs(fromTS);
|
|
218
181
|
}
|
|
219
182
|
catch (exc) {
|
|
220
183
|
if (exc.type !== 'connect') {
|
|
221
184
|
throw exc;
|
|
222
185
|
}
|
|
223
|
-
return
|
|
186
|
+
return index.listMsgs(fromTS);
|
|
224
187
|
}
|
|
225
|
-
const indexedMsgs = await
|
|
188
|
+
const indexedMsgs = await index.listMsgs(fromTS);
|
|
226
189
|
for (const info of indexedMsgs) {
|
|
227
190
|
const ind = msgIds.indexOf(info.msgId);
|
|
228
191
|
if (ind >= 0) {
|
|
@@ -232,17 +195,17 @@ class InboxOnServer {
|
|
|
232
195
|
if (msgIds.length === 0) {
|
|
233
196
|
return indexedMsgs;
|
|
234
197
|
}
|
|
235
|
-
await Promise.all(msgIds.map(msgId =>
|
|
198
|
+
await Promise.all(msgIds.map(msgId => startCachingAndAddKeyToIndex(msgId)
|
|
236
199
|
.catch(async (exc) => {
|
|
237
|
-
await
|
|
200
|
+
await logError(exc, `Failed to start caching message ${msgId}`);
|
|
238
201
|
})));
|
|
239
|
-
return
|
|
202
|
+
return index.listMsgs(fromTS);
|
|
240
203
|
});
|
|
241
204
|
}
|
|
242
|
-
async listNewMsgs(fromTS) {
|
|
243
|
-
const msgIds = await
|
|
205
|
+
async function listNewMsgs(fromTS) {
|
|
206
|
+
const msgIds = await msgReceiver.listMsgs(fromTS);
|
|
244
207
|
// remove from listing messages already in index
|
|
245
|
-
const indexedMsgs = await
|
|
208
|
+
const indexedMsgs = await index.listMsgs(fromTS);
|
|
246
209
|
for (const info of indexedMsgs) {
|
|
247
210
|
const ind = msgIds.indexOf(info.msgId);
|
|
248
211
|
if (ind >= 0) {
|
|
@@ -253,54 +216,39 @@ class InboxOnServer {
|
|
|
253
216
|
return [];
|
|
254
217
|
}
|
|
255
218
|
// cache and index these
|
|
256
|
-
await Promise.all(msgIds.map(msgId =>
|
|
219
|
+
await Promise.all(msgIds.map(msgId => startCachingAndAddKeyToIndex(msgId)));
|
|
257
220
|
// get info's from index, focusing on specific messages only
|
|
258
|
-
return (await
|
|
221
|
+
return (await index.listMsgs(fromTS)).filter(({ msgId }) => msgIds.includes(msgId));
|
|
259
222
|
}
|
|
260
|
-
async getMsg(msgId) {
|
|
223
|
+
async function getMsg(msgId) {
|
|
261
224
|
if (!msgId || (typeof msgId !== 'string')) {
|
|
262
225
|
throw `Given message id is not a non-empty string`;
|
|
263
226
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return promise;
|
|
268
|
-
}
|
|
269
|
-
return this.procs.start(procId, async () => {
|
|
270
|
-
const msgOnDisk = await this.msgFromDiskOrDownload(msgId);
|
|
271
|
-
let msg = this.recentlyOpenedMsgs.get(msgId);
|
|
227
|
+
return debouncingProc(`get msg #${msgId}`, async () => {
|
|
228
|
+
const msgOnDisk = await msgFromDiskOrDownload(msgId);
|
|
229
|
+
let msg = recentlyOpenedMsgs.get(msgId);
|
|
272
230
|
if (msg) {
|
|
273
|
-
return
|
|
274
|
-
}
|
|
275
|
-
if ((msgOnDisk.keyStatus === 'not-found') ||
|
|
276
|
-
(msgOnDisk.keyStatus === 'fail')) {
|
|
277
|
-
// XXX
|
|
278
|
-
// message cannot be opened, and should be removed
|
|
279
|
-
// await Promise.all([
|
|
280
|
-
// this.index.remove(msgInfo),
|
|
281
|
-
// this.removeMsgFromServerAndCache(msgId)
|
|
282
|
-
// ]);
|
|
283
|
-
throw (0, recipient_1.makeMsgNotFoundException)(msgId);
|
|
284
|
-
}
|
|
285
|
-
else if (msgOnDisk.keyStatus === 'not-checked') {
|
|
286
|
-
await this.startCachingAndAddKeyToIndex(msgId);
|
|
287
|
-
}
|
|
288
|
-
else if (msgOnDisk.keyStatus !== 'ok') {
|
|
289
|
-
throw new Error(`Unknown message key status ${msgOnDisk.keyStatus}`);
|
|
231
|
+
return msgToUIForm(msg, msgOnDisk.deliveryTS, msgOnDisk);
|
|
290
232
|
}
|
|
291
233
|
const meta = await msgOnDisk.getMsgMeta();
|
|
292
234
|
const mainObjId = meta.extMeta.objIds[0];
|
|
293
235
|
const mainObj = await msgOnDisk.getMsgObj(mainObjId);
|
|
294
|
-
|
|
236
|
+
let msgKey = await index.getKeyFor(msgId, meta.deliveryCompletion);
|
|
295
237
|
if (!msgKey) {
|
|
296
|
-
|
|
238
|
+
if (!(await startCachingAndAddKeyToIndex(msgId))) {
|
|
239
|
+
throw (0, recipient_1.makeFailToDecryptMsgException)(msgId);
|
|
240
|
+
}
|
|
241
|
+
msgKey = await index.getKeyFor(msgId, meta.deliveryCompletion);
|
|
242
|
+
if (!msgKey) {
|
|
243
|
+
throw (0, recipient_1.makeFailToDecryptMsgException)(msgId);
|
|
244
|
+
}
|
|
297
245
|
}
|
|
298
|
-
msg = await (0, opener_1.openMsg)(msgId, mainObjId, mainObj, msgKey.mainObjHeaderOfs, msgKey.msgKey,
|
|
246
|
+
msg = await (0, opener_1.openMsg)(msgId, mainObjId, mainObj, msgKey.mainObjHeaderOfs, msgKey.msgKey, cryptor);
|
|
299
247
|
msg.setMsgKeyRole(msgKey.msgKeyRole);
|
|
300
|
-
return
|
|
248
|
+
return msgToUIForm(msg, msgOnDisk.deliveryTS, msgOnDisk);
|
|
301
249
|
});
|
|
302
250
|
}
|
|
303
|
-
msgToUIForm(msg, deliveryTS, msgOnDisk) {
|
|
251
|
+
function msgToUIForm(msg, deliveryTS, msgOnDisk) {
|
|
304
252
|
const m = {
|
|
305
253
|
sender: msg.sender,
|
|
306
254
|
establishedSenderKeyChain: msg.establishedKeyChain,
|
|
@@ -325,21 +273,23 @@ class InboxOnServer {
|
|
|
325
273
|
}
|
|
326
274
|
const attachments = msg.attachmentsJSON;
|
|
327
275
|
if (attachments) {
|
|
328
|
-
m.attachments = (0, fs_1.fsForAttachments)(msgOnDisk, attachments,
|
|
276
|
+
m.attachments = (0, fs_1.fsForAttachments)(msgOnDisk, attachments, getStorages, cryptor, random, logError);
|
|
329
277
|
}
|
|
330
278
|
return m;
|
|
331
279
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
280
|
+
return {
|
|
281
|
+
connectivityEvent$: inboxEvents.connectionEvent$,
|
|
282
|
+
resumeNetworkActivity: inboxEvents.resumeNetworkActivity.bind(inboxEvents),
|
|
283
|
+
suspendNetworkActivity: inboxEvents.suspendNetworkActivity.bind(inboxEvents),
|
|
284
|
+
close,
|
|
285
|
+
makeCAP
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function checkServerAuthIfPresent(meta, { correspondent }) {
|
|
289
|
+
// if sender authenticated to server, check that it matches address,
|
|
290
|
+
// recovered from message decryption
|
|
291
|
+
if (meta.authSender && !(0, canonical_address_1.areAddressesEqual)(meta.authSender, correspondent)) {
|
|
292
|
+
throw new Error(`Sender authenticated to server as ${meta.authSender}, while decrypting key is associated with ${correspondent}`);
|
|
340
293
|
}
|
|
341
294
|
}
|
|
342
|
-
exports.InboxOnServer = InboxOnServer;
|
|
343
|
-
Object.freeze(InboxOnServer.prototype);
|
|
344
|
-
Object.freeze(InboxOnServer);
|
|
345
295
|
Object.freeze(exports);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Database } from '../../../../../../lib-sqlite-on-3nstorage/sqljs';
|
|
2
|
+
export interface MsgIndexDbVNoneEntry {
|
|
3
|
+
msg_id: string;
|
|
4
|
+
msg_type: string;
|
|
5
|
+
delivery_ts: number;
|
|
6
|
+
key: Uint8Array;
|
|
7
|
+
key_status: string;
|
|
8
|
+
main_obj_header_ofs: number;
|
|
9
|
+
remove_after: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function getAllRecordsFromVersionNone(db: Database): MsgIndexDbVNoneEntry[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
Copyright (C)
|
|
3
|
+
Copyright (C) 2026 3NSoft Inc.
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under
|
|
6
6
|
the terms of the GNU General Public License as published by the Free Software
|
|
@@ -16,16 +16,10 @@
|
|
|
16
16
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
else {
|
|
26
|
-
arr.push(elems);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return arr;
|
|
19
|
+
exports.getAllRecordsFromVersionNone = getAllRecordsFromVersionNone;
|
|
20
|
+
const lib_sqlite_on_3nstorage_1 = require("../../../../../../lib-sqlite-on-3nstorage");
|
|
21
|
+
function getAllRecordsFromVersionNone(db) {
|
|
22
|
+
const result = db.exec(`--sql
|
|
23
|
+
SELECT * FROM inbox_index`)[0];
|
|
24
|
+
return (result ? (0, lib_sqlite_on_3nstorage_1.objectFromQueryExecResult)(result) : []);
|
|
30
25
|
}
|
|
31
|
-
Object.freeze(exports);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SingleProc } from '../../../../../../lib-common/processes/synced';
|
|
2
|
+
import { MsgKeyInfo, MsgKeyRole } from '../../../../../keyring';
|
|
3
|
+
import { LogError } from '../../../../../../lib-client/logging/log-to-file';
|
|
4
|
+
type WritableFS = web3n.files.WritableFS;
|
|
5
|
+
type MsgInfo = web3n.asmail.MsgInfo;
|
|
6
|
+
interface MsgOpenedLog {
|
|
7
|
+
eventType: 'msg-opened';
|
|
8
|
+
eventTS: number;
|
|
9
|
+
msgId: string;
|
|
10
|
+
msgType: string;
|
|
11
|
+
deliveryTS: number;
|
|
12
|
+
keyB64: string;
|
|
13
|
+
keyStatus: MsgKeyRole;
|
|
14
|
+
mainObjHeaderOfs: number;
|
|
15
|
+
}
|
|
16
|
+
interface MsgRemovedLog {
|
|
17
|
+
eventType: 'msg-removed';
|
|
18
|
+
eventTS: number;
|
|
19
|
+
msgId: string;
|
|
20
|
+
deliveryTS: number;
|
|
21
|
+
}
|
|
22
|
+
export type MsgOp = MsgOpenedLog | MsgRemovedLog;
|
|
23
|
+
export declare function msgOpenedEventToRecordParams(op: MsgOpenedLog): {
|
|
24
|
+
msgInfo: MsgInfo;
|
|
25
|
+
decrInfo: MsgKeyInfo;
|
|
26
|
+
};
|
|
27
|
+
export declare function makeRecentMsgIndexChangesLogs(logsFS: WritableFS, localLogsFS: WritableFS, logError: LogError): Promise<{
|
|
28
|
+
recordMsgAddition: (msgInfo: MsgInfo, decrInfo: MsgKeyInfo) => Promise<void>;
|
|
29
|
+
recordMsgRemoval: (msgId: string, deliveryTS: number) => Promise<void>;
|
|
30
|
+
watchAndApplyOpsFromOtherDevices: (changeProc: SingleProc, sink: (ops: import("../../../../../../lib-common/dataset-sync/per-device-json-array-logs").LogEntry<MsgOp>[]) => Promise<void>) => () => void;
|
|
31
|
+
cutLogOnDatasetSyncAndUpload: (syncPointV: number, params?: any) => Promise<void> | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (C) 2016 - 2020, 2023, 2026 3NSoft Inc.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
7
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
8
|
+
version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful, but
|
|
11
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
13
|
+
See the GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License along with
|
|
16
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.msgOpenedEventToRecordParams = msgOpenedEventToRecordParams;
|
|
20
|
+
exports.makeRecentMsgIndexChangesLogs = makeRecentMsgIndexChangesLogs;
|
|
21
|
+
const buffer_utils_1 = require("../../../../../../lib-common/buffer-utils");
|
|
22
|
+
const per_device_json_array_logs_1 = require("../../../../../../lib-common/dataset-sync/per-device-json-array-logs");
|
|
23
|
+
function msgOpenedEventToRecordParams(op) {
|
|
24
|
+
const { msgType, msgId, deliveryTS, keyB64, keyStatus, mainObjHeaderOfs } = op;
|
|
25
|
+
return {
|
|
26
|
+
msgInfo: {
|
|
27
|
+
msgId, msgType, deliveryTS
|
|
28
|
+
},
|
|
29
|
+
decrInfo: {
|
|
30
|
+
correspondent: '',
|
|
31
|
+
keyStatus,
|
|
32
|
+
msgKeyPackLen: mainObjHeaderOfs,
|
|
33
|
+
key: buffer_utils_1.base64.open(keyB64)
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function areOpsEqual(a, b) {
|
|
38
|
+
return ((a.eventType === b.eventType) && (a.msgId === b.msgId) && (a.deliveryTS === b.deliveryTS));
|
|
39
|
+
}
|
|
40
|
+
async function makeRecentMsgIndexChangesLogs(logsFS, localLogsFS, logError) {
|
|
41
|
+
const { appendLogsAndUpload, cutLogOnDatasetSyncAndUpload, watchAndApplyOpsFromOtherDevices } = await (0, per_device_json_array_logs_1.makeRecentChangesLogs)(logsFS, localLogsFS, areOpsEqual, logError);
|
|
42
|
+
function recordMsgAddition(msgInfo, decrInfo) {
|
|
43
|
+
if (!decrInfo.key) {
|
|
44
|
+
throw new Error(`Given message decryption info doesn't have a key for message ${msgInfo.msgId}`);
|
|
45
|
+
}
|
|
46
|
+
return appendLogsAndUpload({
|
|
47
|
+
eventType: 'msg-opened',
|
|
48
|
+
eventTS: Date.now(),
|
|
49
|
+
msgType: msgInfo.msgType,
|
|
50
|
+
msgId: msgInfo.msgId,
|
|
51
|
+
deliveryTS: msgInfo.deliveryTS,
|
|
52
|
+
keyB64: buffer_utils_1.base64.pack(decrInfo.key),
|
|
53
|
+
keyStatus: decrInfo.keyStatus,
|
|
54
|
+
mainObjHeaderOfs: decrInfo.msgKeyPackLen,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function recordMsgRemoval(msgId, deliveryTS) {
|
|
58
|
+
return appendLogsAndUpload({
|
|
59
|
+
eventType: 'msg-removed',
|
|
60
|
+
eventTS: Date.now(),
|
|
61
|
+
msgId,
|
|
62
|
+
deliveryTS
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
recordMsgAddition,
|
|
67
|
+
recordMsgRemoval,
|
|
68
|
+
watchAndApplyOpsFromOtherDevices,
|
|
69
|
+
cutLogOnDatasetSyncAndUpload
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
Object.freeze(exports);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { MsgKeyInfo, MsgKeyRole } from '../../../../../keyring';
|
|
2
|
+
import { MsgOp } from './logs';
|
|
3
|
+
import { SingleProc } from '../../../../../../lib-common/processes/synced';
|
|
4
|
+
import { LogEntry } from '../../../../../../lib-common/dataset-sync/per-device-json-array-logs';
|
|
5
|
+
type WritableFS = web3n.files.WritableFS;
|
|
6
|
+
type WritableFile = web3n.files.WritableFile;
|
|
7
|
+
type MsgInfo = web3n.asmail.MsgInfo;
|
|
8
|
+
export interface MsgKey {
|
|
9
|
+
msgKey: Uint8Array;
|
|
10
|
+
msgKeyRole: MsgKeyRole;
|
|
11
|
+
mainObjHeaderOfs: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function makeSqliteDBs(dbsFS: WritableFS): Promise<{
|
|
14
|
+
absorbOpsFromOtherDevices: (changeProc: SingleProc, ops: LogEntry<MsgOp>[]) => Promise<void>;
|
|
15
|
+
addToLatest: (msgInfo: MsgInfo, decrInfo: MsgKeyInfo) => Promise<boolean>;
|
|
16
|
+
getDBFile: (fileTS: number) => Promise<WritableFile>;
|
|
17
|
+
getKeyFor: (msgId: string, deliveryTS: number) => Promise<MsgKey | undefined>;
|
|
18
|
+
isForLatestDb: (deliveryTS: number) => boolean;
|
|
19
|
+
latestDbHasNewRemoteVersion: () => Promise<boolean>;
|
|
20
|
+
listMsgs: (fromTS: number | undefined) => Promise<MsgInfo[]>;
|
|
21
|
+
msgExists: ({ msgId, deliveryTS }: MsgInfo) => Promise<boolean>;
|
|
22
|
+
makeNewShardWithRecordsUpToTS: (shardTS: number) => Promise<number>;
|
|
23
|
+
syncPastShards: () => Promise<void>;
|
|
24
|
+
numberOfRecordsInLatestBefore: (ts: number) => number;
|
|
25
|
+
remove: (msgId: string) => Promise<number | undefined>;
|
|
26
|
+
watchAndApplyOpsFromOtherDevices: (changeProc: SingleProc, resetSyncInterval: () => void) => () => void;
|
|
27
|
+
}>;
|
|
28
|
+
export {};
|