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.
Files changed (87) hide show
  1. package/build/api-defs/asmail.d.ts +1 -0
  2. package/build/api-defs/files.d.ts +11 -5
  3. package/build/core/asmail/delivery/common.d.ts +8 -28
  4. package/build/core/asmail/delivery/common.js +1 -47
  5. package/build/core/asmail/delivery/index.js +3 -3
  6. package/build/core/asmail/delivery/per-recipient-wip.js +3 -2
  7. package/build/core/asmail/inbox/index.d.ts +8 -32
  8. package/build/core/asmail/inbox/index.js +120 -170
  9. package/build/core/asmail/inbox/msg-indexing/dataset/v0/sql-db-v-none.d.ts +11 -0
  10. package/build/{lib-client/xsp-fs/util/for-arrays.js → core/asmail/inbox/msg-indexing/dataset/v0/sql-db-v-none.js} +7 -13
  11. package/build/core/asmail/inbox/msg-indexing/dataset/v1/logs.d.ts +33 -0
  12. package/build/core/asmail/inbox/msg-indexing/dataset/v1/logs.js +72 -0
  13. package/build/core/asmail/inbox/msg-indexing/dataset/v1/sql-db.d.ts +28 -0
  14. package/build/core/asmail/inbox/msg-indexing/dataset/v1/sql-db.js +477 -0
  15. package/build/core/asmail/inbox/msg-indexing/index.d.ts +12 -26
  16. package/build/core/asmail/inbox/msg-indexing/index.js +47 -68
  17. package/build/core/asmail/inbox/msg-on-disk.d.ts +0 -5
  18. package/build/core/asmail/inbox/msg-on-disk.js +2 -24
  19. package/build/core/asmail/index.js +8 -7
  20. package/build/core/asmail/msg/common.d.ts +0 -1
  21. package/build/core/asmail/msg/common.js +1 -1
  22. package/build/core/index.js +7 -4
  23. package/build/core/keyring/common.d.ts +1 -0
  24. package/build/core/keyring/common.js +2 -1
  25. package/build/core/keyring/index.d.ts +7 -44
  26. package/build/core/keyring/index.js +88 -222
  27. package/build/core/keyring/introductory-keys/published-intro-key.d.ts +17 -0
  28. package/build/core/keyring/introductory-keys/published-intro-key.js +190 -0
  29. package/build/core/keyring/peer-keys/dataset/v1/json-files.d.ts +105 -0
  30. package/build/core/keyring/peer-keys/dataset/v1/json-files.js +89 -0
  31. package/build/core/keyring/peer-keys/dataset/v2/logs.d.ts +43 -0
  32. package/build/core/keyring/peer-keys/dataset/v2/logs.js +116 -0
  33. package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.d.ts +73 -0
  34. package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.js +543 -0
  35. package/build/core/keyring/peer-keys/index.d.ts +24 -0
  36. package/build/core/keyring/peer-keys/index.js +208 -0
  37. package/build/core/storage/synced/obj-files.js +10 -3
  38. package/build/core/storage/synced/obj-status.d.ts +1 -1
  39. package/build/core/storage/synced/obj-status.js +8 -2
  40. package/build/core/storage/synced/storage.d.ts +4 -4
  41. package/build/core/storage/system-folders/apps-data.d.ts +0 -1
  42. package/build/core/storage/system-folders/apps-data.js +2 -6
  43. package/build/lib-client/asmail/recipient.d.ts +1 -0
  44. package/build/lib-client/asmail/recipient.js +11 -0
  45. package/build/lib-client/fs-utils/fs-sync-utils.d.ts +1 -1
  46. package/build/lib-client/fs-utils/fs-sync-utils.js +70 -13
  47. package/build/lib-client/xsp-fs/folder-node.js +13 -3
  48. package/build/lib-client/xsp-fs/fs.js +3 -1
  49. package/build/lib-client/xsp-fs/node-in-fs.d.ts +1 -0
  50. package/build/lib-client/xsp-fs/node-in-fs.js +7 -2
  51. package/build/lib-client/xsp-fs/node-persistence.js +1 -3
  52. package/build/lib-client/xsp-fs/xsp-payload-v2.js +1 -2
  53. package/build/lib-common/dataset-sync/per-device-json-array-logs.d.ts +58 -0
  54. package/build/lib-common/dataset-sync/per-device-json-array-logs.js +432 -0
  55. package/build/lib-common/dataset-sync/single-file.d.ts +10 -0
  56. package/build/lib-common/dataset-sync/single-file.js +77 -0
  57. package/build/lib-common/number-line.d.ts +10 -0
  58. package/build/lib-common/number-line.js +66 -0
  59. package/build/lib-common/objs-on-disk/obj-version-file.d.ts +1 -1
  60. package/build/lib-common/objs-on-disk/obj-version-file.js +3 -3
  61. package/build/lib-common/rng-def.d.ts +1 -0
  62. package/build/lib-common/rng-def.js +8 -0
  63. package/build/lib-sqlite-on-3nstorage/for-sqlite.d.ts +38 -0
  64. package/build/lib-sqlite-on-3nstorage/for-sqlite.js +162 -0
  65. package/build/lib-sqlite-on-3nstorage/index.d.ts +18 -5
  66. package/build/lib-sqlite-on-3nstorage/index.js +49 -26
  67. package/build/tests/caps-api/file-sink-checks/splice.js +72 -1
  68. package/build/tests/caps-api/fs-checks/sync-on-one-dev/upload.js +27 -1
  69. package/build/tests/caps-api/fs-checks/sync-with-two-devs/update-propagation.js +12 -3
  70. package/build/tests/computer.3nweb.core/inbox/msg-indexing.js +16 -12
  71. package/build/tests/computer.3nweb.core/keyrings.js +113 -28
  72. package/build/tests/jasmine.js +2 -2
  73. package/build/tests/units/number-line.js +9 -9
  74. package/package.json +2 -1
  75. package/build/core/asmail/inbox/msg-indexing/logs-n-entries.d.ts +0 -52
  76. package/build/core/asmail/inbox/msg-indexing/logs-n-entries.js +0 -333
  77. package/build/core/asmail/inbox/msg-indexing/sql-indexing.d.ts +0 -4
  78. package/build/core/asmail/inbox/msg-indexing/sql-indexing.js +0 -319
  79. package/build/core/keyring/correspondent-keys.d.ts +0 -149
  80. package/build/core/keyring/correspondent-keys.js +0 -403
  81. package/build/core/keyring/id-to-email-map.d.ts +0 -38
  82. package/build/core/keyring/id-to-email-map.js +0 -105
  83. package/build/core/keyring/keyring-storage.d.ts +0 -9
  84. package/build/core/keyring/keyring-storage.js +0 -54
  85. package/build/core/keyring/published-intro-key.d.ts +0 -45
  86. package/build/core/keyring/published-intro-key.js +0 -200
  87. 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.InboxOnServer = void 0;
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
- * Instance of this class represents inbox-on-mail-server.
37
- * It uses api to manage messages on a ASMail server, caching and recording
38
- * some information to allow faster response, and to keep message keys, while
39
- * messages have not been removed via direct action or due to expiry.
40
- * This object is also responsible for expiring messages on the server.
41
- */
42
- class InboxOnServer {
43
- constructor(r, msgReceiver, storages, cryptor, random, downloader, cache, index, logError) {
44
- this.r = r;
45
- this.msgReceiver = msgReceiver;
46
- this.storages = storages;
47
- this.cryptor = cryptor;
48
- this.random = random;
49
- this.downloader = downloader;
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
- static async makeAndStart(cachePath, syncedFS, r) {
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: this.getMsg.bind(this),
81
- listMsgs: this.listMsgs.bind(this),
82
- removeMsg: this.removeMsg.bind(this),
83
- subscribe: this.inboxEvents.subscribe.bind(this.inboxEvents)
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 = this.procs.latestTaskAtThisMoment(procId);
62
+ const promise = procs.latestTaskAtThisMoment(procId);
91
63
  if (promise) {
92
64
  return promise;
93
65
  }
94
66
  // start removal process
95
- return this.procs.start(procId, (async () => {
67
+ return procs.start(procId, (async () => {
96
68
  await Promise.all([
97
- this.index.remove(msgId),
98
- this.removeMsgFromServerAndCache(msgId)
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
- this.cache.deleteMsg(msgId),
76
+ cache.deleteMsg(msgId),
105
77
  // XXX the following is suspicious, as it swallows other exceptions too
106
- this.msgReceiver.removeMsg(msgId).catch(() => { })
78
+ msgReceiver.removeMsg(msgId).catch(() => { })
107
79
  ]);
108
80
  }
109
- async msgFromDiskOrDownload(msgId) {
110
- const msgOnDisk = await this.cache.findMsg(msgId);
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 this.downloader.getMsgMeta(msgId);
115
- return await this.cache.addMsg(msgId, meta);
86
+ const meta = await downloader.getMsgMeta(msgId);
87
+ return await cache.addMsg(msgId, meta);
116
88
  }
117
- async startCachingAndAddKeyToIndex(msgId) {
118
- const msgOnDisk = await this.msgFromDiskOrDownload(msgId);
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
- // setup closures, some memoized, for use by keyring
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.checkServerAuthIfPresent(meta, decrInfo);
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
- this.recentlyOpenedMsgs.set(msgId, openedMsg);
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 this.index.add(msgInfo, decrInfo);
129
+ await index.add(msgInfo, decrInfo);
153
130
  await Promise.all([
154
- this.absorbSendingParams(openedMsg),
155
- this.markOwnParams(meta, openedMsg.sender)
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 this.index.getKeyFor(msgId, msgOnDisk.deliveryTS);
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 this.logError(exc, `Problem with opening message ${msgId}`);
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 this.r.markOwnSendingParamsAsUsed(sender, meta.invite);
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 this.r.saveParamsForSendingTo(address, sendingParams);
161
+ await r.correspondents.saveParamsForSendingTo(address, sendingParams);
191
162
  }
192
- checkServerAuthIfPresent(meta, decrInfo) {
193
- // if sender authenticated to server, check that it matches address,
194
- // recovered from message decryption
195
- if (meta.authSender
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 this.index.listMsgs(fromTS);
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
- // start message listing process
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 this.msgReceiver.listMsgs(fromTS);
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 this.index.listMsgs(fromTS);
186
+ return index.listMsgs(fromTS);
224
187
  }
225
- const indexedMsgs = await this.index.listMsgs(fromTS);
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 => this.startCachingAndAddKeyToIndex(msgId)
198
+ await Promise.all(msgIds.map(msgId => startCachingAndAddKeyToIndex(msgId)
236
199
  .catch(async (exc) => {
237
- await this.logError(exc, `Failed to start caching message ${msgId}`);
200
+ await logError(exc, `Failed to start caching message ${msgId}`);
238
201
  })));
239
- return this.index.listMsgs(fromTS);
202
+ return index.listMsgs(fromTS);
240
203
  });
241
204
  }
242
- async listNewMsgs(fromTS) {
243
- const msgIds = await this.msgReceiver.listMsgs(fromTS);
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 this.index.listMsgs(fromTS);
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 => this.startCachingAndAddKeyToIndex(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 this.index.listMsgs(fromTS)).filter(({ msgId }) => msgIds.includes(msgId));
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
- const procId = `get msg #${msgId}`;
265
- const promise = this.procs.latestTaskAtThisMoment(procId);
266
- if (promise) {
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 this.msgToUIForm(msg, msgOnDisk.deliveryTS, msgOnDisk);
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
- const msgKey = await this.index.getKeyFor(msgId, meta.deliveryCompletion);
236
+ let msgKey = await index.getKeyFor(msgId, meta.deliveryCompletion);
295
237
  if (!msgKey) {
296
- throw (0, recipient_1.makeMsgNotFoundException)(msgId);
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, this.cryptor);
246
+ msg = await (0, opener_1.openMsg)(msgId, mainObjId, mainObj, msgKey.mainObjHeaderOfs, msgKey.msgKey, cryptor);
299
247
  msg.setMsgKeyRole(msgKey.msgKeyRole);
300
- return this.msgToUIForm(msg, msgOnDisk.deliveryTS, msgOnDisk);
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, this.storages, this.cryptor, this.random, this.logError);
276
+ m.attachments = (0, fs_1.fsForAttachments)(msgOnDisk, attachments, getStorages, cryptor, random, logError);
329
277
  }
330
278
  return m;
331
279
  }
332
- suspendNetworkActivity() {
333
- this.inboxEvents.suspendNetworkActivity();
334
- }
335
- resumeNetworkActivity() {
336
- this.inboxEvents.resumeNetworkActivity();
337
- }
338
- get connectivityEvent$() {
339
- return this.inboxEvents.connectionEvent$;
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) 2022 3NSoft Inc.
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.appendArray = appendArray;
20
- function appendArray(arr, elems) {
21
- if (elems !== undefined) {
22
- if (Array.isArray(elems)) {
23
- arr.push(...elems);
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 {};