core-3nweb-client-lib 0.49.7 → 0.50.0
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 +1 -1
- package/build/core/asmail/delivery/per-recipient-wip.js +4 -0
- package/build/core/asmail/inbox/index.d.ts +8 -31
- package/build/core/asmail/inbox/index.js +119 -164
- package/build/core/asmail/inbox/msg-indexing/index.js +8 -9
- package/build/core/asmail/inbox/msg-on-disk.d.ts +0 -5
- package/build/core/asmail/inbox/msg-on-disk.js +1 -22
- package/build/core/asmail/index.js +4 -4
- package/build/core/asmail/sending-params/index.d.ts +13 -14
- package/build/core/asmail/sending-params/index.js +15 -35
- package/build/core/asmail/sending-params/invitations-anon.d.ts +7 -31
- package/build/core/asmail/sending-params/invitations-anon.js +77 -127
- package/build/core/asmail/sending-params/own-params.d.ts +5 -25
- package/build/core/asmail/sending-params/own-params.js +144 -97
- package/build/core/asmail/sending-params/params-from-others.d.ts +6 -16
- package/build/core/asmail/sending-params/params-from-others.js +88 -58
- package/build/core/index.d.ts +2 -1
- package/build/core/index.js +4 -4
- package/build/core/keyring/index.js +2 -3
- package/build/core/keyring/introductory-keys/published-intro-key.d.ts +2 -2
- package/build/core/keyring/introductory-keys/published-intro-key.js +71 -71
- package/build/core/keyring/peer-keys/index.js +6 -7
- package/build/core/storage/index.d.ts +2 -1
- package/build/core/storage/index.js +4 -3
- package/build/core/storage/system-folders/index.d.ts +1 -2
- package/build/core/storage/system-folders/index.js +18 -22
- 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 +21 -15
- package/build/lib-client/xsp-fs/folder-node.js +13 -3
- package/build/lib-client/xsp-fs/util/file-based-json.js +1 -1
- package/build/lib-common/dataset-sync/per-device-json-array-logs.js +43 -28
- package/build/lib-common/dataset-sync/single-file.d.ts +12 -0
- package/build/lib-common/dataset-sync/single-file.js +73 -0
- package/build/lib-common/dataset-sync/utils.d.ts +16 -0
- package/build/lib-common/dataset-sync/utils.js +72 -0
- package/build/lib-common-on-node/device-fs-places.d.ts +3 -0
- package/build/{lib-client/xsp-fs/util/for-arrays.js → lib-common-on-node/device-fs-places.js} +21 -13
- package/build/tests/computer.3nweb.core/keyrings.js +2 -2
- package/build/tests/libs-for-tests/core-runner.js +2 -1
- package/package.json +1 -1
- package/build/lib-client/xsp-fs/util/for-arrays.d.ts +0 -1
|
@@ -1172,7 +1172,7 @@ declare namespace web3n.files {
|
|
|
1172
1172
|
|
|
1173
1173
|
interface FileChangeEvent extends FSChangeEvent {
|
|
1174
1174
|
type: 'file-change';
|
|
1175
|
-
newVersion
|
|
1175
|
+
newVersion: number;
|
|
1176
1176
|
}
|
|
1177
1177
|
|
|
1178
1178
|
type RemoteEvent = RemoteVersionArchivalEvent | RemoteArchVerRemovalEvent |
|
|
@@ -132,9 +132,13 @@ class WIP {
|
|
|
132
132
|
const recipient = this.state.recipient;
|
|
133
133
|
const sp = this.msg.r.correspondents.paramsForSendingTo(recipient);
|
|
134
134
|
if (sp) {
|
|
135
|
+
// DEBUG
|
|
136
|
+
// console.log(`sending params for sending to ${recipient} invitation are`, sp);
|
|
135
137
|
this.sender = await sender_1.MailSender.fresh(this.msg.r.makeNet(), this.msg.r.asmailResolver, (sp.auth ? senderAddress : undefined), recipient, sp.invitation);
|
|
136
138
|
}
|
|
137
139
|
else {
|
|
140
|
+
// DEBUG
|
|
141
|
+
// console.log(`have no sending params to use for sending to ${recipient}`);
|
|
138
142
|
this.sender = await sender_1.MailSender.fresh(this.msg.r.makeNet(), this.msg.r.asmailResolver, undefined, recipient);
|
|
139
143
|
}
|
|
140
144
|
await this.sender.startSession();
|
|
@@ -72,35 +72,12 @@ export interface ResourcesForReceiving {
|
|
|
72
72
|
* messages have not been removed via direct action or due to expiry.
|
|
73
73
|
* This object is also responsible for expiring messages on the server.
|
|
74
74
|
*/
|
|
75
|
-
export
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
private readonly index;
|
|
84
|
-
private readonly logError;
|
|
85
|
-
private readonly inboxEvents;
|
|
86
|
-
private readonly procs;
|
|
87
|
-
private readonly recentlyOpenedMsgs;
|
|
88
|
-
private constructor();
|
|
89
|
-
static makeAndStart(cachePath: string, syncedFS: WritableFS, localFS: WritableFS, r: ResourcesForReceiving): Promise<InboxOnServer>;
|
|
90
|
-
close(): Promise<void>;
|
|
91
|
-
makeCAP(): InboxService;
|
|
92
|
-
private removeMsg;
|
|
93
|
-
private removeMsgFromServerAndCache;
|
|
94
|
-
private msgFromDiskOrDownload;
|
|
95
|
-
private startCachingAndAddKeyToIndex;
|
|
96
|
-
private markOwnParams;
|
|
97
|
-
private absorbSendingParams;
|
|
98
|
-
private listMsgs;
|
|
99
|
-
private listNewMsgs;
|
|
100
|
-
private getMsg;
|
|
101
|
-
private msgToUIForm;
|
|
102
|
-
suspendNetworkActivity(): void;
|
|
103
|
-
resumeNetworkActivity(): void;
|
|
104
|
-
get connectivityEvent$(): import("rxjs").Observable<import("./inbox-events").InboxConnectionStatus>;
|
|
105
|
-
}
|
|
75
|
+
export type InboxOnServer = Awaited<ReturnType<typeof makeInboxOnServer>>;
|
|
76
|
+
export declare function makeInboxOnServer(cachePath: string, syncedFS: WritableFS, localFS: WritableFS, r: ResourcesForReceiving): Promise<{
|
|
77
|
+
connectivityEvent$: import("rxjs").Observable<import("./inbox-events").InboxConnectionStatus>;
|
|
78
|
+
resumeNetworkActivity: () => void;
|
|
79
|
+
suspendNetworkActivity: () => void;
|
|
80
|
+
close: () => Promise<void>;
|
|
81
|
+
makeCAP: () => InboxService;
|
|
82
|
+
}>;
|
|
106
83
|
export {};
|
|
@@ -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,189 +31,165 @@ 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.getOrMakeDirOnInit)(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.getOrMakeSyncedDirOnInit)(syncedFS, MSG_INDEX_FOLDER);
|
|
65
|
-
const indexLocalFS = await localFS.writableSubRoot(MSG_INDEX_FOLDER);
|
|
66
|
-
const index = await (0, msg_indexing_1.makeMsgIndex)(indexSyncedFS, indexLocalFS, r.logError);
|
|
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);
|
|
88
|
+
}
|
|
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
|
+
};
|
|
116
110
|
}
|
|
117
|
-
async startCachingAndAddKeyToIndex(msgId) {
|
|
118
|
-
const msgOnDisk = await
|
|
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);
|
|
120
|
+
// XXX we have never used this part of protocol, should it be removed at all from ASMail protocol?
|
|
144
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) {
|
|
150
|
+
// DEBUG
|
|
151
|
+
// console.log(` markOwnParams (if invite used) ->`, {sender, 'meta.invite':meta.invite});
|
|
179
152
|
if (!meta.invite) {
|
|
180
153
|
return;
|
|
181
154
|
}
|
|
182
|
-
await
|
|
155
|
+
await r.correspondents.markOwnSendingParamsAsUsed(sender, meta.invite);
|
|
183
156
|
}
|
|
184
|
-
async absorbSendingParams(openedMsg) {
|
|
157
|
+
async function absorbSendingParams(openedMsg) {
|
|
185
158
|
const sendingParams = openedMsg.nextSendingParams;
|
|
159
|
+
// DEBUG
|
|
160
|
+
// console.log(` openedMsg.nextSendingParams ->`, openedMsg.nextSendingParams);
|
|
186
161
|
if (!sendingParams) {
|
|
187
162
|
return;
|
|
188
163
|
}
|
|
189
164
|
const address = openedMsg.sender;
|
|
190
|
-
await
|
|
165
|
+
await r.correspondents.saveParamsForSendingTo(address, sendingParams);
|
|
191
166
|
}
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
if (!checkServer) {
|
|
195
|
-
return this.index.listMsgs(fromTS);
|
|
196
|
-
}
|
|
197
|
-
// check for an already started process
|
|
198
|
-
const procId = 'listing msgs';
|
|
199
|
-
const promise = this.procs.latestTaskAtThisMoment(procId);
|
|
167
|
+
function debouncingProc(procId, action) {
|
|
168
|
+
const promise = procs.latestTaskAtThisMoment(procId);
|
|
200
169
|
if (promise) {
|
|
201
170
|
return promise;
|
|
202
171
|
}
|
|
203
|
-
|
|
204
|
-
|
|
172
|
+
return procs.start(procId, action);
|
|
173
|
+
}
|
|
174
|
+
async function listMsgs(fromTS) {
|
|
175
|
+
const checkServer = true; // XXX in future this will be an option from req
|
|
176
|
+
if (!checkServer) {
|
|
177
|
+
return index.listMsgs(fromTS);
|
|
178
|
+
}
|
|
179
|
+
return debouncingProc('listing msgs', async () => {
|
|
205
180
|
// message listing info is located in index, yet, process involves
|
|
206
181
|
// getting and caching messages' metadata
|
|
207
182
|
let msgIds;
|
|
208
183
|
try {
|
|
209
|
-
msgIds = await
|
|
184
|
+
msgIds = await msgReceiver.listMsgs(fromTS);
|
|
210
185
|
}
|
|
211
186
|
catch (exc) {
|
|
212
187
|
if (exc.type !== 'connect') {
|
|
213
188
|
throw exc;
|
|
214
189
|
}
|
|
215
|
-
return
|
|
190
|
+
return index.listMsgs(fromTS);
|
|
216
191
|
}
|
|
217
|
-
const indexedMsgs = await
|
|
192
|
+
const indexedMsgs = await index.listMsgs(fromTS);
|
|
218
193
|
for (const info of indexedMsgs) {
|
|
219
194
|
const ind = msgIds.indexOf(info.msgId);
|
|
220
195
|
if (ind >= 0) {
|
|
@@ -224,17 +199,17 @@ class InboxOnServer {
|
|
|
224
199
|
if (msgIds.length === 0) {
|
|
225
200
|
return indexedMsgs;
|
|
226
201
|
}
|
|
227
|
-
await Promise.all(msgIds.map(msgId =>
|
|
202
|
+
await Promise.all(msgIds.map(msgId => startCachingAndAddKeyToIndex(msgId)
|
|
228
203
|
.catch(async (exc) => {
|
|
229
|
-
await
|
|
204
|
+
await logError(exc, `Failed to start caching message ${msgId}`);
|
|
230
205
|
})));
|
|
231
|
-
return
|
|
206
|
+
return index.listMsgs(fromTS);
|
|
232
207
|
});
|
|
233
208
|
}
|
|
234
|
-
async listNewMsgs(fromTS) {
|
|
235
|
-
const msgIds = await
|
|
209
|
+
async function listNewMsgs(fromTS) {
|
|
210
|
+
const msgIds = await msgReceiver.listMsgs(fromTS);
|
|
236
211
|
// remove from listing messages already in index
|
|
237
|
-
const indexedMsgs = await
|
|
212
|
+
const indexedMsgs = await index.listMsgs(fromTS);
|
|
238
213
|
for (const info of indexedMsgs) {
|
|
239
214
|
const ind = msgIds.indexOf(info.msgId);
|
|
240
215
|
if (ind >= 0) {
|
|
@@ -245,54 +220,39 @@ class InboxOnServer {
|
|
|
245
220
|
return [];
|
|
246
221
|
}
|
|
247
222
|
// cache and index these
|
|
248
|
-
await Promise.all(msgIds.map(msgId =>
|
|
223
|
+
await Promise.all(msgIds.map(msgId => startCachingAndAddKeyToIndex(msgId)));
|
|
249
224
|
// get info's from index, focusing on specific messages only
|
|
250
|
-
return (await
|
|
225
|
+
return (await index.listMsgs(fromTS)).filter(({ msgId }) => msgIds.includes(msgId));
|
|
251
226
|
}
|
|
252
|
-
async getMsg(msgId) {
|
|
227
|
+
async function getMsg(msgId) {
|
|
253
228
|
if (!msgId || (typeof msgId !== 'string')) {
|
|
254
229
|
throw `Given message id is not a non-empty string`;
|
|
255
230
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return promise;
|
|
260
|
-
}
|
|
261
|
-
return this.procs.start(procId, async () => {
|
|
262
|
-
const msgOnDisk = await this.msgFromDiskOrDownload(msgId);
|
|
263
|
-
let msg = this.recentlyOpenedMsgs.get(msgId);
|
|
231
|
+
return debouncingProc(`get msg #${msgId}`, async () => {
|
|
232
|
+
const msgOnDisk = await msgFromDiskOrDownload(msgId);
|
|
233
|
+
let msg = recentlyOpenedMsgs.get(msgId);
|
|
264
234
|
if (msg) {
|
|
265
|
-
return
|
|
266
|
-
}
|
|
267
|
-
if ((msgOnDisk.keyStatus === 'not-found')
|
|
268
|
-
|| (msgOnDisk.keyStatus === 'fail')) {
|
|
269
|
-
// XXX
|
|
270
|
-
// message cannot be opened, and should be removed
|
|
271
|
-
// await Promise.all([
|
|
272
|
-
// this.index.remove(msgInfo),
|
|
273
|
-
// this.removeMsgFromServerAndCache(msgId)
|
|
274
|
-
// ]);
|
|
275
|
-
throw (0, recipient_1.makeMsgNotFoundException)(msgId);
|
|
276
|
-
}
|
|
277
|
-
else if (msgOnDisk.keyStatus === 'not-checked') {
|
|
278
|
-
await this.startCachingAndAddKeyToIndex(msgId);
|
|
279
|
-
}
|
|
280
|
-
else if (msgOnDisk.keyStatus !== 'ok') {
|
|
281
|
-
throw new Error(`Unknown message key status ${msgOnDisk.keyStatus}`);
|
|
235
|
+
return msgToUIForm(msg, msgOnDisk.deliveryTS, msgOnDisk);
|
|
282
236
|
}
|
|
283
237
|
const meta = await msgOnDisk.getMsgMeta();
|
|
284
238
|
const mainObjId = meta.extMeta.objIds[0];
|
|
285
239
|
const mainObj = await msgOnDisk.getMsgObj(mainObjId);
|
|
286
|
-
|
|
240
|
+
let msgKey = await index.getKeyFor(msgId, meta.deliveryCompletion);
|
|
287
241
|
if (!msgKey) {
|
|
288
|
-
|
|
242
|
+
if (!(await startCachingAndAddKeyToIndex(msgId))) {
|
|
243
|
+
throw (0, recipient_1.makeFailToDecryptMsgException)(msgId);
|
|
244
|
+
}
|
|
245
|
+
msgKey = await index.getKeyFor(msgId, meta.deliveryCompletion);
|
|
246
|
+
if (!msgKey) {
|
|
247
|
+
throw (0, recipient_1.makeFailToDecryptMsgException)(msgId);
|
|
248
|
+
}
|
|
289
249
|
}
|
|
290
|
-
msg = await (0, opener_1.openMsg)(msgId, mainObjId, mainObj, msgKey.mainObjHeaderOfs, msgKey.msgKey,
|
|
250
|
+
msg = await (0, opener_1.openMsg)(msgId, mainObjId, mainObj, msgKey.mainObjHeaderOfs, msgKey.msgKey, cryptor);
|
|
291
251
|
msg.setMsgKeyRole(msgKey.msgKeyRole);
|
|
292
|
-
return
|
|
252
|
+
return msgToUIForm(msg, msgOnDisk.deliveryTS, msgOnDisk);
|
|
293
253
|
});
|
|
294
254
|
}
|
|
295
|
-
msgToUIForm(msg, deliveryTS, msgOnDisk) {
|
|
255
|
+
function msgToUIForm(msg, deliveryTS, msgOnDisk) {
|
|
296
256
|
const m = {
|
|
297
257
|
sender: msg.sender,
|
|
298
258
|
establishedSenderKeyChain: msg.establishedKeyChain,
|
|
@@ -317,23 +277,18 @@ class InboxOnServer {
|
|
|
317
277
|
}
|
|
318
278
|
const attachments = msg.attachmentsJSON;
|
|
319
279
|
if (attachments) {
|
|
320
|
-
m.attachments = (0, fs_1.fsForAttachments)(msgOnDisk, attachments,
|
|
280
|
+
m.attachments = (0, fs_1.fsForAttachments)(msgOnDisk, attachments, getStorages, cryptor, random, logError);
|
|
321
281
|
}
|
|
322
282
|
return m;
|
|
323
283
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
return this.inboxEvents.connectionEvent$;
|
|
332
|
-
}
|
|
284
|
+
return {
|
|
285
|
+
connectivityEvent$: inboxEvents.connectionEvent$,
|
|
286
|
+
resumeNetworkActivity: inboxEvents.resumeNetworkActivity.bind(inboxEvents),
|
|
287
|
+
suspendNetworkActivity: inboxEvents.suspendNetworkActivity.bind(inboxEvents),
|
|
288
|
+
close,
|
|
289
|
+
makeCAP
|
|
290
|
+
};
|
|
333
291
|
}
|
|
334
|
-
exports.InboxOnServer = InboxOnServer;
|
|
335
|
-
Object.freeze(InboxOnServer.prototype);
|
|
336
|
-
Object.freeze(InboxOnServer);
|
|
337
292
|
function checkServerAuthIfPresent(meta, { correspondent }) {
|
|
338
293
|
// if sender authenticated to server, check that it matches address,
|
|
339
294
|
// recovered from message decryption
|
|
@@ -27,9 +27,9 @@ const INDEX_DIR = 'index';
|
|
|
27
27
|
const NUM_OF_ENTRIES_TO_CHOP_DB = 200;
|
|
28
28
|
async function makeMsgIndex(syncedFS, localFS, logError) {
|
|
29
29
|
// file systems within dataset
|
|
30
|
-
const logsFS = await (0, fs_sync_utils_1.
|
|
30
|
+
const logsFS = await (0, fs_sync_utils_1.getOrMakeDirOnInit)(syncedFS, LOGS_DIR);
|
|
31
31
|
const localLogsFS = await localFS.writableSubRoot(LOGS_DIR);
|
|
32
|
-
const indexFS = await (0, fs_sync_utils_1.
|
|
32
|
+
const indexFS = await (0, fs_sync_utils_1.getOrMakeDirOnInit)(syncedFS, INDEX_DIR);
|
|
33
33
|
const logs = await (0, logs_1.makeRecentMsgIndexChangesLogs)(logsFS, localLogsFS, logError);
|
|
34
34
|
const indexed = await (0, sql_db_1.makeSqliteDBs)(indexFS);
|
|
35
35
|
async function add(msgInfo, decrInfo) {
|
|
@@ -45,13 +45,12 @@ async function makeMsgIndex(syncedFS, localFS, logError) {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
async function periodicCheckAndUploadOfData() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// }
|
|
48
|
+
const shardTS = Date.now() - 10 * 60 * 1000;
|
|
49
|
+
const countBefore = indexed.numberOfRecordsInLatestBefore(shardTS);
|
|
50
|
+
if (countBefore >= NUM_OF_ENTRIES_TO_CHOP_DB) {
|
|
51
|
+
const syncedIndexDbVersion = await indexed.makeNewShardWithRecordsUpToTS(shardTS);
|
|
52
|
+
await logs.cutLogOnDatasetSyncAndUpload(syncedIndexDbVersion, { shardTS });
|
|
53
|
+
}
|
|
55
54
|
}
|
|
56
55
|
function millisBeforeNextRun() {
|
|
57
56
|
return 30 * 60 * 1000 + Math.floor(10 * 60 * 1000 * Math.random());
|
|
@@ -2,14 +2,12 @@ import { ObjOnDisk } from '../../../lib-client/objs-on-disk/obj-on-disk';
|
|
|
2
2
|
import { MsgMeta } from '../../../lib-common/service-api/asmail/retrieval';
|
|
3
3
|
import { ObjSource } from 'xsp-files';
|
|
4
4
|
import { MsgDownloader } from './msg-downloader';
|
|
5
|
-
export type MsgKeyStatus = 'not-checked' | 'not-found' | 'fail' | 'ok';
|
|
6
5
|
export interface ObjSize {
|
|
7
6
|
header: number;
|
|
8
7
|
segments: number;
|
|
9
8
|
}
|
|
10
9
|
export interface MsgStatus {
|
|
11
10
|
msgId: string;
|
|
12
|
-
keyStatus: MsgKeyStatus;
|
|
13
11
|
onDisk: boolean;
|
|
14
12
|
mainObjId: string;
|
|
15
13
|
deliveryTS: number;
|
|
@@ -31,7 +29,4 @@ export declare class MsgOnDisk {
|
|
|
31
29
|
getObjFile(objId: string): Promise<ObjOnDisk>;
|
|
32
30
|
getMsgObj(objId: string): Promise<ObjSource>;
|
|
33
31
|
get deliveryTS(): number;
|
|
34
|
-
get keyStatus(): MsgKeyStatus;
|
|
35
|
-
updateMsgKeyStatus(newStatus: Exclude<MsgKeyStatus, 'not-checked'>): Promise<void>;
|
|
36
|
-
private saveStatusToDisk;
|
|
37
32
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
Copyright (C) 2016 - 2019 3NSoft Inc.
|
|
3
|
+
Copyright (C) 2016 - 2019, 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
|
|
@@ -27,7 +27,6 @@ const STATUS_FNAME = 'status.json';
|
|
|
27
27
|
function makeInitMsgStatus(msgId, meta) {
|
|
28
28
|
return {
|
|
29
29
|
msgId,
|
|
30
|
-
keyStatus: 'not-checked',
|
|
31
30
|
onDisk: false,
|
|
32
31
|
mainObjId: meta.extMeta.objIds[0],
|
|
33
32
|
deliveryTS: meta.deliveryStart
|
|
@@ -94,26 +93,6 @@ class MsgOnDisk {
|
|
|
94
93
|
get deliveryTS() {
|
|
95
94
|
return this.status.deliveryTS;
|
|
96
95
|
}
|
|
97
|
-
get keyStatus() {
|
|
98
|
-
return this.status.keyStatus;
|
|
99
|
-
}
|
|
100
|
-
updateMsgKeyStatus(newStatus) {
|
|
101
|
-
return this.syncProc.startOrChain(async () => {
|
|
102
|
-
if (this.status.keyStatus === 'not-checked') {
|
|
103
|
-
this.status.keyStatus = newStatus;
|
|
104
|
-
}
|
|
105
|
-
else if (this.status.keyStatus === newStatus) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
throw Error(`Message has key status ${this.status.keyStatus}, and can't be updated to ${newStatus}`);
|
|
110
|
-
}
|
|
111
|
-
await this.saveStatusToDisk();
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
async saveStatusToDisk() {
|
|
115
|
-
await writeJSON(this.msgFolderPath, STATUS_FNAME, this.status);
|
|
116
|
-
}
|
|
117
96
|
}
|
|
118
97
|
exports.MsgOnDisk = MsgOnDisk;
|
|
119
98
|
Object.freeze(MsgOnDisk.prototype);
|
|
@@ -61,8 +61,8 @@ class ASMail {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
async setupSendingParams(syncedFS) {
|
|
64
|
-
const fs = await (0, fs_sync_utils_1.
|
|
65
|
-
this.sendingParams = await sending_params_1.
|
|
64
|
+
const fs = await (0, fs_sync_utils_1.getOrMakeDirOnInit)(syncedFS, SEND_PARAMS_DATA_FOLDER);
|
|
65
|
+
this.sendingParams = await (0, sending_params_1.makeSendingParamsHolder)(fs, this.config.makeParamSetterAndGetter('anon-sender/invites'), this.random);
|
|
66
66
|
}
|
|
67
67
|
async setupDelivery(localFS, getSigner, makeResolver) {
|
|
68
68
|
const fs = await localFS.writableSubRoot(DELIVERY_DATA_FOLDER);
|
|
@@ -88,9 +88,9 @@ class ASMail {
|
|
|
88
88
|
}
|
|
89
89
|
async setupInbox(syncedFS, localFS, getSigner, getStorages, makeResolver) {
|
|
90
90
|
const cachePath = this.inboxPathForUser(this.address);
|
|
91
|
-
const inboxSyncedFS = await (0, fs_sync_utils_1.
|
|
91
|
+
const inboxSyncedFS = await (0, fs_sync_utils_1.getOrMakeDirOnInit)(syncedFS, INBOX_DATA_FOLDER);
|
|
92
92
|
const inboxLocalFS = await localFS.writableSubRoot(INBOX_DATA_FOLDER);
|
|
93
|
-
this.inbox = await inbox_1.
|
|
93
|
+
this.inbox = await (0, inbox_1.makeInboxOnServer)(cachePath, inboxSyncedFS, inboxLocalFS, {
|
|
94
94
|
address: this.address,
|
|
95
95
|
cryptor: this.cryptor,
|
|
96
96
|
random: this.random,
|
|
@@ -6,18 +6,17 @@ export { SendingParams } from './params-from-others';
|
|
|
6
6
|
type WritableFS = web3n.files.WritableFS;
|
|
7
7
|
type SendingResources = ResourcesForSending['correspondents'];
|
|
8
8
|
type ReceptionResources = ResourcesForReceiving['correspondents'];
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
readonly thisSide: {
|
|
13
|
-
getUpdated: SendingResources['newParamsForSendingReplies'];
|
|
14
|
-
setAsUsed: ReceptionResources['markOwnSendingParamsAsUsed'];
|
|
15
|
-
};
|
|
16
|
-
readonly otherSides: {
|
|
17
|
-
get: SendingResources['paramsForSendingTo'];
|
|
18
|
-
set: ReceptionResources['saveParamsForSendingTo'];
|
|
19
|
-
};
|
|
20
|
-
private constructor();
|
|
21
|
-
static makeAndInit(fs: WritableFS, anonInvitesOnServer: ParamOnServer<'anon-sender/invites'>, random: AsyncRNG): Promise<SendingParamsHolder>;
|
|
22
|
-
close(): Promise<void>;
|
|
9
|
+
export interface SendingParamsThisSide {
|
|
10
|
+
getUpdated: SendingResources['newParamsForSendingReplies'];
|
|
11
|
+
setAsUsed: ReceptionResources['markOwnSendingParamsAsUsed'];
|
|
23
12
|
}
|
|
13
|
+
export interface SendingParamsOtherSides {
|
|
14
|
+
get: SendingResources['paramsForSendingTo'];
|
|
15
|
+
set: ReceptionResources['saveParamsForSendingTo'];
|
|
16
|
+
}
|
|
17
|
+
export interface SendingParamsHolder {
|
|
18
|
+
thisSide: SendingParamsThisSide;
|
|
19
|
+
otherSides: SendingParamsOtherSides;
|
|
20
|
+
close: () => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare function makeSendingParamsHolder(fs: WritableFS, anonInvitesOnServer: ParamOnServer<'anon-sender/invites'>, random: AsyncRNG): Promise<SendingParamsHolder>;
|