core-3nweb-client-lib 0.51.0 → 0.52.1
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 +27 -4
- package/build/api-defs/files.d.ts +6 -3
- package/build/core/asmail/asmail-cap-ipc.js +22 -3
- package/build/core/asmail/delivery/common.d.ts +3 -0
- package/build/core/asmail/delivery/common.js +5 -0
- package/build/core/asmail/delivery/index.js +6 -6
- package/build/core/asmail/delivery/msg.js +1 -6
- package/build/core/asmail/delivery/per-recipient-wip.js +3 -5
- package/build/core/asmail/filtering.d.ts +11 -0
- package/build/core/asmail/filtering.js +219 -0
- package/build/core/asmail/inbox/inbox-events.d.ts +1 -0
- package/build/core/asmail/inbox/inbox-events.js +3 -0
- package/build/core/asmail/inbox/index.d.ts +1 -0
- package/build/core/asmail/inbox/index.js +8 -0
- package/build/core/asmail/index.d.ts +2 -0
- package/build/core/asmail/index.js +29 -4
- package/build/core/asmail/sending-params/index.d.ts +2 -0
- package/build/core/asmail/sending-params/index.js +11 -1
- package/build/core/asmail/sending-params/own-params.d.ts +2 -0
- package/build/core/asmail/sending-params/own-params.js +27 -5
- package/build/core/asmail/sending-params/params-from-others.d.ts +2 -0
- package/build/core/asmail/sending-params/params-from-others.js +28 -3
- package/build/core/keyring/index.d.ts +2 -0
- package/build/core/keyring/index.js +4 -2
- package/build/core/keyring/peer-keys/dataset/v2/logs.d.ts +13 -1
- package/build/core/keyring/peer-keys/dataset/v2/logs.js +17 -1
- package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.d.ts +2 -0
- package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.js +31 -1
- package/build/core/keyring/peer-keys/index.d.ts +2 -0
- package/build/core/keyring/peer-keys/index.js +11 -1
- package/build/core/storage/synced/remote-events.d.ts +1 -0
- package/build/core/storage/synced/remote-events.js +3 -0
- package/build/core/storage/synced/storage.d.ts +2 -0
- package/build/core/storage/synced/storage.js +13 -0
- package/build/core/storage/synced/upsyncer.js +2 -1
- package/build/lib-client/3nstorage/storage-owner.js +1 -2
- package/build/lib-client/asmail/sender.d.ts +0 -6
- package/build/lib-client/asmail/sender.js +20 -64
- package/build/lib-client/service-locator.d.ts +1 -2
- package/build/lib-client/service-locator.js +0 -1
- package/build/lib-common/awaitable-state.d.ts +1 -0
- package/build/lib-common/awaitable-state.js +3 -0
- package/build/lib-common/canonical-address.js +3 -0
- package/build/lib-common/periodic-trigger.d.ts +1 -0
- package/build/lib-common/periodic-trigger.js +44 -0
- package/build/tests/caps-api/asmail.js +34 -4
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright (C) 2016, 2020, 2022, 2024 -
|
|
2
|
+
Copyright (C) 2016, 2020, 2022, 2024 - 2026 3NSoft Inc.
|
|
3
3
|
|
|
4
4
|
This program is free software: you can redistribute it and/or modify it under
|
|
5
5
|
the terms of the GNU General Public License as published by the Free Software
|
|
@@ -36,6 +36,8 @@ declare namespace web3n.asmail {
|
|
|
36
36
|
|
|
37
37
|
getReportAddressForDomain(domain: string): Promise<string>;
|
|
38
38
|
|
|
39
|
+
filter: ASMailFilter;
|
|
40
|
+
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
interface PreFlightOnlyService {
|
|
@@ -309,6 +311,7 @@ declare namespace web3n.asmail {
|
|
|
309
311
|
interface ASMailSendException extends RuntimeException {
|
|
310
312
|
type: 'asmail-delivery';
|
|
311
313
|
address?: string;
|
|
314
|
+
id?: string;
|
|
312
315
|
|
|
313
316
|
// errors that are due to remote side,
|
|
314
317
|
// these will be placed into ProgressDelivery object
|
|
@@ -321,10 +324,11 @@ declare namespace web3n.asmail {
|
|
|
321
324
|
allowedSize?: number;
|
|
322
325
|
recipientHasNoPubKey?: true;
|
|
323
326
|
recipientPubKeyFailsValidation?: true;
|
|
324
|
-
msgNotFound?: true;
|
|
325
327
|
|
|
326
328
|
// errors that are due to this side
|
|
327
329
|
msgCancelled?: true;
|
|
330
|
+
recipientBlocked?: true;
|
|
331
|
+
msgNotFound?: true;
|
|
328
332
|
}
|
|
329
333
|
|
|
330
334
|
interface ASMailConfigService {
|
|
@@ -380,7 +384,7 @@ declare namespace web3n.asmail {
|
|
|
380
384
|
*/
|
|
381
385
|
interface AddressesList {
|
|
382
386
|
[address: string]: number;
|
|
383
|
-
}
|
|
387
|
+
}
|
|
384
388
|
|
|
385
389
|
/**
|
|
386
390
|
* Invites list is a map from invites to respective maximum message size in
|
|
@@ -388,6 +392,25 @@ declare namespace web3n.asmail {
|
|
|
388
392
|
*/
|
|
389
393
|
interface InvitesList {
|
|
390
394
|
[invite: string]: number;
|
|
391
|
-
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
interface ASMailFilter {
|
|
398
|
+
listRules(): Promise<FilteringRule[]>;
|
|
399
|
+
addRule(rule: FilteringRule): Promise<{ ruleIndex: number; rule: FilteringRule; }>;
|
|
400
|
+
removeRule(ruleIndex: number, rule: FilteringRule): Promise<boolean>;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
interface BlockAddressRule {
|
|
404
|
+
ruleType: 'block-address';
|
|
405
|
+
domain: string;
|
|
406
|
+
username: string;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
interface BlockDomainRule {
|
|
410
|
+
ruleType: 'block-domain';
|
|
411
|
+
domain: string;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
type FilteringRule = BlockAddressRule | BlockDomainRule;
|
|
392
415
|
|
|
393
416
|
}
|
|
@@ -70,6 +70,7 @@ declare namespace web3n.files {
|
|
|
70
70
|
type: 'fs-sync';
|
|
71
71
|
path: string;
|
|
72
72
|
localVersion?: number;
|
|
73
|
+
uploadVersion?: number;
|
|
73
74
|
remoteVersion?: number;
|
|
74
75
|
alreadyUploading?: true;
|
|
75
76
|
uploadTaskId?: number;
|
|
@@ -1681,8 +1682,8 @@ declare namespace web3n.files {
|
|
|
1681
1682
|
upload(path: string, opts?: OptionsToUploadLocal): Promise<number|undefined>;
|
|
1682
1683
|
|
|
1683
1684
|
/**
|
|
1684
|
-
* This method is for resolving conflicts on folders.
|
|
1685
|
-
* It adopts given folder item, that is present in remote version and is missing in local version.
|
|
1685
|
+
* This method is for resolving conflicts on folders, an action item by item.
|
|
1686
|
+
* It adopts only given folder item, that is present in remote version and is missing in local version.
|
|
1686
1687
|
* Returns new local version.
|
|
1687
1688
|
* @param path
|
|
1688
1689
|
* @param remoteItemName
|
|
@@ -1694,7 +1695,9 @@ declare namespace web3n.files {
|
|
|
1694
1695
|
|
|
1695
1696
|
/**
|
|
1696
1697
|
* This method is for resolving conflicts on folders.
|
|
1697
|
-
* It absorbs folder changes done in remote version.
|
|
1698
|
+
* It completely absorbs folder changes done in remote version.
|
|
1699
|
+
* Such wholesome may or may not be appriate in all circumstances, so use `adoptRemoteFolderItem`
|
|
1700
|
+
* for a more nuanced item-by-item operations.
|
|
1698
1701
|
* Returns new local version, if there were remote items to adopt and their were added to local state.
|
|
1699
1702
|
* @param path
|
|
1700
1703
|
* @param opts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
Copyright (C) 2020 - 2023, 2025 3NSoft Inc.
|
|
3
|
+
Copyright (C) 2020 - 2023, 2025 - 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
|
|
@@ -59,7 +59,8 @@ function exposeASMailCAP(cap, expServices) {
|
|
|
59
59
|
subscribe: inboxSubscribe.wrapService(box.subscribe, expServices)
|
|
60
60
|
},
|
|
61
61
|
config: exposeConfigCAP(cap.config),
|
|
62
|
-
getReportAddressForDomain: (0, service_side_wrap_1.wrapReqReplyFunc)(cap, cap.getReportAddressForDomain)
|
|
62
|
+
getReportAddressForDomain: (0, service_side_wrap_1.wrapReqReplyFunc)(cap, cap.getReportAddressForDomain),
|
|
63
|
+
filter: exposeFilterCAP(cap.filter)
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
}
|
|
@@ -85,7 +86,8 @@ function makeASMailBasedOnListing(caller, objPath, capLists) {
|
|
|
85
86
|
subscribe: inboxSubscribe.makeCaller(caller, inboxPath)
|
|
86
87
|
},
|
|
87
88
|
config: makeConfigCaller(caller, objPath.concat('config')),
|
|
88
|
-
getReportAddressForDomain: (0, caller_side_wrap_1.makeReqRepFuncCaller)(caller, objPath.concat('getReportAddressForDomain'))
|
|
89
|
+
getReportAddressForDomain: (0, caller_side_wrap_1.makeReqRepFuncCaller)(caller, objPath.concat('getReportAddressForDomain')),
|
|
90
|
+
filter: makeFilterCaller(caller, objPath.concat('filter'))
|
|
89
91
|
};
|
|
90
92
|
}
|
|
91
93
|
else if (capLists === 'preflight') {
|
|
@@ -614,4 +616,21 @@ function makeConfigCaller(caller, objPath) {
|
|
|
614
616
|
setOnServer: callConfig(caller, objPath, 'setOnServer')
|
|
615
617
|
};
|
|
616
618
|
}
|
|
619
|
+
function exposeFilterCAP(cap) {
|
|
620
|
+
return {
|
|
621
|
+
addRule: (0, service_side_wrap_1.wrapReqReplySrvMethod)(cap, 'addRule'),
|
|
622
|
+
listRules: (0, service_side_wrap_1.wrapReqReplySrvMethod)(cap, 'listRules'),
|
|
623
|
+
removeRule: (0, service_side_wrap_1.wrapReqReplySrvMethod)(cap, 'removeRule')
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
function callFilder(caller, objPath, method) {
|
|
627
|
+
return (0, caller_side_wrap_1.makeReqRepObjCaller)(caller, objPath, method);
|
|
628
|
+
}
|
|
629
|
+
function makeFilterCaller(caller, objPath) {
|
|
630
|
+
return {
|
|
631
|
+
addRule: callFilder(caller, objPath, 'addRule'),
|
|
632
|
+
listRules: callFilder(caller, objPath, 'listRules'),
|
|
633
|
+
removeRule: callFilder(caller, objPath, 'removeRule')
|
|
634
|
+
};
|
|
635
|
+
}
|
|
617
636
|
Object.freeze(exports);
|
|
@@ -14,6 +14,7 @@ type AttachmentsContainer = web3n.asmail.AttachmentsContainer;
|
|
|
14
14
|
type FS = web3n.files.FS;
|
|
15
15
|
type WritableFS = web3n.files.WritableFS;
|
|
16
16
|
type JsonKey = web3n.keys.JsonKey;
|
|
17
|
+
type ASMailSendException = web3n.asmail.ASMailSendException;
|
|
17
18
|
export declare class Attachments {
|
|
18
19
|
container: AttachmentsContainer | undefined;
|
|
19
20
|
fs: FS | undefined;
|
|
@@ -66,6 +67,7 @@ export interface ResourcesForSending {
|
|
|
66
67
|
* @param address
|
|
67
68
|
*/
|
|
68
69
|
newParamsForSendingReplies: (address: string) => Promise<SendingParams | undefined>;
|
|
70
|
+
isAddressBlocked(address: string): boolean;
|
|
69
71
|
};
|
|
70
72
|
cryptor: AsyncSBoxCryptor;
|
|
71
73
|
random: AsyncRNG;
|
|
@@ -79,4 +81,5 @@ export interface SavedMsgToSend {
|
|
|
79
81
|
recipients: string[];
|
|
80
82
|
retryOpts: DeliveryOptions['retryRecipient'];
|
|
81
83
|
}
|
|
84
|
+
export declare function makeDeliveryException(params: Partial<ASMailSendException>): ASMailSendException;
|
|
82
85
|
export {};
|
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.SEG_SIZE_IN_K_QUATS = exports.Attachments = void 0;
|
|
20
20
|
exports.estimatePackedSizeOf = estimatePackedSizeOf;
|
|
21
|
+
exports.makeDeliveryException = makeDeliveryException;
|
|
21
22
|
const attachments_container_1 = require("../msg/attachments-container");
|
|
22
23
|
const buffer_utils_1 = require("../../../lib-common/buffer-utils");
|
|
24
|
+
const runtime_1 = require("../../../lib-common/exceptions/runtime");
|
|
23
25
|
const ATTACHMENTS_NAME = 'attachments';
|
|
24
26
|
class Attachments {
|
|
25
27
|
constructor(container, fs) {
|
|
@@ -138,4 +140,7 @@ function estimatePackedSizeOf(size) {
|
|
|
138
140
|
}
|
|
139
141
|
return 148 + numOfCompleteSegs * exports.SEG_SIZE_IN_K_QUATS * 256 + lastSegSize;
|
|
140
142
|
}
|
|
143
|
+
function makeDeliveryException(params) {
|
|
144
|
+
return (0, runtime_1.makeRuntimeException)('asmail-delivery', params, {});
|
|
145
|
+
}
|
|
141
146
|
Object.freeze(exports);
|
|
@@ -104,12 +104,7 @@ class Delivery {
|
|
|
104
104
|
const msg = this.msgs.get(id);
|
|
105
105
|
if (!msg) {
|
|
106
106
|
if (observer.error) {
|
|
107
|
-
|
|
108
|
-
runtimeException: true,
|
|
109
|
-
type: 'asmail-delivery',
|
|
110
|
-
msgNotFound: true
|
|
111
|
-
};
|
|
112
|
-
observer.error(exc);
|
|
107
|
+
observer.error((0, common_1.makeDeliveryException)({ id, msgNotFound: true }));
|
|
113
108
|
}
|
|
114
109
|
return () => { };
|
|
115
110
|
}
|
|
@@ -149,6 +144,11 @@ class Delivery {
|
|
|
149
144
|
if (!Array.isArray(recipients) || (recipients.length === 0)) {
|
|
150
145
|
throw new Error(`Given invalid recipients: ${recipients} for message ${id}`);
|
|
151
146
|
}
|
|
147
|
+
for (const recipient of recipients) {
|
|
148
|
+
if (this.r.correspondents.isAddressBlocked(recipient)) {
|
|
149
|
+
throw (0, common_1.makeDeliveryException)({ address: recipient, recipientBlocked: true });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
152
|
if (this.msgs.has(id)) {
|
|
153
153
|
throw new Error(`Message with id ${id} has already been added for delivery`);
|
|
154
154
|
}
|
|
@@ -235,12 +235,7 @@ class Msg {
|
|
|
235
235
|
return;
|
|
236
236
|
}
|
|
237
237
|
await filesProc.catch(() => { });
|
|
238
|
-
|
|
239
|
-
runtimeException: true,
|
|
240
|
-
type: 'asmail-delivery',
|
|
241
|
-
msgCancelled: true
|
|
242
|
-
};
|
|
243
|
-
this.progressPublisher.error(exc);
|
|
238
|
+
this.progressPublisher.error((0, common_1.makeDeliveryException)({ id: this.id, msgCancelled: true }));
|
|
244
239
|
}
|
|
245
240
|
/**
|
|
246
241
|
* This starts sending a message to all recipients in parallel, and should be
|
|
@@ -21,6 +21,7 @@ const buffer_utils_1 = require("../../../lib-common/buffer-utils");
|
|
|
21
21
|
const sender_1 = require("../../../lib-client/asmail/sender");
|
|
22
22
|
const synced_1 = require("../../../lib-common/processes/synced");
|
|
23
23
|
const key_verification_1 = require("../key-verification");
|
|
24
|
+
const common_1 = require("./common");
|
|
24
25
|
const MSG_SIZE_FOR_STATE_RECORDING = 1024 * 1024;
|
|
25
26
|
const EMPTY_BYTE_ARR = new Uint8Array(0);
|
|
26
27
|
/**
|
|
@@ -209,14 +210,11 @@ class WIP {
|
|
|
209
210
|
}
|
|
210
211
|
const certs = await this.sender.getRecipientsInitPubKey();
|
|
211
212
|
return (0, key_verification_1.checkAndExtractPKey)(this.sender.net, this.msg.r.midResolver, recipient, certs).catch(err => {
|
|
212
|
-
|
|
213
|
-
runtimeException: true,
|
|
214
|
-
type: 'asmail-delivery',
|
|
213
|
+
throw (0, common_1.makeDeliveryException)({
|
|
215
214
|
address: recipient,
|
|
216
215
|
recipientPubKeyFailsValidation: true,
|
|
217
216
|
cause: err
|
|
218
|
-
};
|
|
219
|
-
throw exc;
|
|
217
|
+
});
|
|
220
218
|
});
|
|
221
219
|
}
|
|
222
220
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { LogError } from "../../lib-client/logging/log-to-file";
|
|
2
|
+
type WritableFS = web3n.files.WritableFS;
|
|
3
|
+
type ASMailFilter = web3n.asmail.ASMailFilter;
|
|
4
|
+
type FilteringRule = web3n.asmail.FilteringRule;
|
|
5
|
+
export declare function makeAddressFilter(fs: WritableFS, logError: LogError, onRuleAddition: (r: FilteringRule) => Promise<void>): Promise<{
|
|
6
|
+
makeCAP: () => ASMailFilter;
|
|
7
|
+
isAddressBlocked: (address: string) => boolean;
|
|
8
|
+
close: () => void;
|
|
9
|
+
}>;
|
|
10
|
+
export type AddressFilter = Awaited<ReturnType<typeof makeAddressFilter>>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (C) 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.makeAddressFilter = makeAddressFilter;
|
|
20
|
+
const canonical_address_1 = require("../../lib-common/canonical-address");
|
|
21
|
+
const single_file_1 = require("../../lib-common/dataset-sync/single-file");
|
|
22
|
+
const json_utils_1 = require("../../lib-common/json-utils");
|
|
23
|
+
const RULES_FNAME = 'rules.json';
|
|
24
|
+
async function makeAddressFilter(fs, logError, onRuleAddition) {
|
|
25
|
+
const { file: rulesFile, changeProc } = await (0, single_file_1.initSyncedFile)(fs, RULES_FNAME, newFile => newFile.writeJSON([]));
|
|
26
|
+
let rules = await prepareRulesReadFromFile(await rulesFile.readJSON());
|
|
27
|
+
async function saveRulesAndTriggerSync() {
|
|
28
|
+
await changeProc.startOrChain(() => rulesFile.writeJSON(rules));
|
|
29
|
+
triggerUpload();
|
|
30
|
+
}
|
|
31
|
+
const { stopFileWatching, triggerUpload } = (0, single_file_1.watchAndApplyChangesFromOtherDevices)(rulesFile, changeProc, onChangeFromOtherDevices, doOnConflict);
|
|
32
|
+
async function onChangeFromOtherDevices(newVersion) {
|
|
33
|
+
changeProc.startOrChain(async () => {
|
|
34
|
+
const { version, json } = await rulesFile.v.readJSON();
|
|
35
|
+
if (version === newVersion) {
|
|
36
|
+
rules = await prepareRulesReadFromFile(json);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function doOnConflict() {
|
|
41
|
+
return changeProc.startOrChain(async () => {
|
|
42
|
+
const { remote, synced } = await rulesFile.v.sync.status(true);
|
|
43
|
+
const syncedRules = await prepareRulesReadFromFile((await rulesFile.v.readJSON({ remoteVersion: synced === null || synced === void 0 ? void 0 : synced.latest })).json);
|
|
44
|
+
const remoteRules = await prepareRulesReadFromFile((await rulesFile.v.readJSON({ remoteVersion: remote === null || remote === void 0 ? void 0 : remote.latest })).json);
|
|
45
|
+
const localRules = rules.concat();
|
|
46
|
+
const mergedRules = [];
|
|
47
|
+
while (remoteRules.length > 0) {
|
|
48
|
+
const r = remoteRules.shift();
|
|
49
|
+
const si = syncedRules.findIndex(sr => (0, json_utils_1.deepEqual)(sr, r));
|
|
50
|
+
const li = localRules.findIndex(lr => (0, json_utils_1.deepEqual)(lr, r));
|
|
51
|
+
if (si >= 0) {
|
|
52
|
+
if (li >= 0) {
|
|
53
|
+
// rule stays as is
|
|
54
|
+
mergedRules.push(r);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// rule removed in local
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
if (li >= 0) {
|
|
62
|
+
// rule added in local and remote
|
|
63
|
+
mergedRules.push(r);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// rule added in remote
|
|
67
|
+
mergedRules.push(r);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (si > 0) {
|
|
71
|
+
syncedRules.splice(si, 1);
|
|
72
|
+
}
|
|
73
|
+
if (li > 0) {
|
|
74
|
+
localRules.splice(li, 1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
while (localRules.length > 0) {
|
|
78
|
+
const l = localRules.shift();
|
|
79
|
+
const si = syncedRules.findIndex(sr => (0, json_utils_1.deepEqual)(sr, l));
|
|
80
|
+
if (si >= 0) {
|
|
81
|
+
// rule was removed in remote
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// rule was added in local
|
|
85
|
+
mergedRules.push(l);
|
|
86
|
+
}
|
|
87
|
+
if (si > 0) {
|
|
88
|
+
syncedRules.splice(si, 1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
rules = mergedRules;
|
|
92
|
+
await rulesFile.writeJSON(rules);
|
|
93
|
+
await rulesFile.v.sync.upload({ uploadVersion: remote.latest + 1 });
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async function prepareRulesReadFromFile(rulesFromFile) {
|
|
97
|
+
if (!Array.isArray(rulesFromFile)) {
|
|
98
|
+
await logError(`Expected array with rules`, `Reading of ASMail filter rules produced unexpected data`);
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
const rules = [];
|
|
102
|
+
const errors = [];
|
|
103
|
+
for (const r of rulesFromFile) {
|
|
104
|
+
try {
|
|
105
|
+
rules.push(prepareRuleFrom(r));
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
errors.push(err);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (errors.length > 0) {
|
|
112
|
+
await logError(errors[0], `Reading of ASMail filter rules produced produced ${errors.length} errors, the first one is here`);
|
|
113
|
+
}
|
|
114
|
+
return rules;
|
|
115
|
+
}
|
|
116
|
+
function groupRulesByDomains(rules) {
|
|
117
|
+
const m = new Map();
|
|
118
|
+
for (const rule of rules) {
|
|
119
|
+
const domain = rule.domain;
|
|
120
|
+
let domainRules = m.get(domain);
|
|
121
|
+
if (!domainRules) {
|
|
122
|
+
domainRules = [];
|
|
123
|
+
m.set(domain, domainRules);
|
|
124
|
+
}
|
|
125
|
+
domainRules.push(rule);
|
|
126
|
+
}
|
|
127
|
+
return m;
|
|
128
|
+
}
|
|
129
|
+
let rulesByDomain = groupRulesByDomains(rules);
|
|
130
|
+
async function addRule(ruleToAdd) {
|
|
131
|
+
const rule = prepareRuleFrom(ruleToAdd);
|
|
132
|
+
const ruleIndex = rules.push(rule) - 1;
|
|
133
|
+
rulesByDomain = groupRulesByDomains(rules);
|
|
134
|
+
await saveRulesAndTriggerSync();
|
|
135
|
+
await onRuleAddition(rule);
|
|
136
|
+
return { rule, ruleIndex };
|
|
137
|
+
}
|
|
138
|
+
async function removeRule(ruleIndex, rule) {
|
|
139
|
+
if ((ruleIndex < 0) || (ruleIndex >= rules.length) || !(0, json_utils_1.deepEqual)(rules[ruleIndex], rule)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
rules.splice(ruleIndex, 1);
|
|
143
|
+
await saveRulesAndTriggerSync();
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
async function listRules() {
|
|
147
|
+
return rules;
|
|
148
|
+
}
|
|
149
|
+
function isAddressBlocked(address) {
|
|
150
|
+
const cAddr = (0, canonical_address_1.checkAndTransformAddress)(address);
|
|
151
|
+
if (!cAddr) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
const { uName, dName } = cAddrIntoParts(cAddr);
|
|
155
|
+
const domainRules = rulesByDomain.get(dName);
|
|
156
|
+
if (!domainRules) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
for (const rule of domainRules) {
|
|
160
|
+
if (rule.ruleType === 'block-domain') {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
else if (rule.ruleType === 'block-address') {
|
|
164
|
+
if (uName == rule.username) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
function makeCAP() {
|
|
172
|
+
return {
|
|
173
|
+
addRule,
|
|
174
|
+
removeRule,
|
|
175
|
+
listRules
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function close() {
|
|
179
|
+
stopFileWatching();
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
makeCAP,
|
|
183
|
+
isAddressBlocked,
|
|
184
|
+
close
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function cAddrIntoParts(cAddr) {
|
|
188
|
+
const atInd = cAddr.lastIndexOf('@');
|
|
189
|
+
if (atInd < 0) {
|
|
190
|
+
throw TypeError(`No @ in given canonical address: ${cAddr}`);
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
uName: cAddr.substring(0, atInd),
|
|
194
|
+
dName: cAddr.substring(atInd + 1)
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function prepareRuleFrom(r) {
|
|
198
|
+
switch (r.ruleType) {
|
|
199
|
+
case 'block-address': {
|
|
200
|
+
const cAddr = (0, canonical_address_1.toCanonicalAddress)(`${r.username}@${r.domain}`);
|
|
201
|
+
const { uName, dName } = cAddrIntoParts(cAddr);
|
|
202
|
+
return {
|
|
203
|
+
ruleType: 'block-address',
|
|
204
|
+
domain: dName, username: uName
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
case 'block-domain': {
|
|
208
|
+
const cAddr = (0, canonical_address_1.toCanonicalAddress)(`@${r.domain}`);
|
|
209
|
+
const { dName } = cAddrIntoParts(cAddr);
|
|
210
|
+
return {
|
|
211
|
+
ruleType: 'block-domain',
|
|
212
|
+
domain: dName
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
default:
|
|
216
|
+
throw TypeError(`Unknown rule type: ${r.ruleType}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
Object.freeze(exports);
|
|
@@ -34,6 +34,7 @@ export declare class InboxEvents {
|
|
|
34
34
|
constructor(msgReceiver: MailRecipient, getMsg: (msgId: string) => Promise<IncomingMessage>, listNewMsgs: (fromTS: number) => Promise<MsgInfo[]>, rmMsg: (msgId: string) => Promise<void>, logError: LogError);
|
|
35
35
|
private makeProc;
|
|
36
36
|
whenConnected(): Promise<void>;
|
|
37
|
+
isConnected(): boolean;
|
|
37
38
|
private getMessage;
|
|
38
39
|
subscribe<T>(event: InboxEventType, observer: Observer<T>): () => void;
|
|
39
40
|
close(): void;
|
|
@@ -87,6 +87,9 @@ class InboxEvents {
|
|
|
87
87
|
async whenConnected() {
|
|
88
88
|
return this.msgReceiver.connectedState.whenStateIsSet();
|
|
89
89
|
}
|
|
90
|
+
isConnected() {
|
|
91
|
+
return this.msgReceiver.connectedState.isSet();
|
|
92
|
+
}
|
|
90
93
|
getMessage(msgId) {
|
|
91
94
|
return this.getMsg(msgId).catch(async (exc) => {
|
|
92
95
|
if ((exc.type === 'connect') || (exc.type === 'inbox')) {
|
|
@@ -30,6 +30,8 @@ const msg_downloader_1 = require("./msg-downloader");
|
|
|
30
30
|
const cached_msgs_1 = require("./cached-msgs");
|
|
31
31
|
const timed_cache_1 = require("../../../lib-common/timed-cache");
|
|
32
32
|
const fs_sync_utils_1 = require("../../../lib-client/fs-utils/fs-sync-utils");
|
|
33
|
+
const periodic_trigger_1 = require("../../../lib-common/periodic-trigger");
|
|
34
|
+
const CONNECTION_CHECK_WAIT_SECS = 30;
|
|
33
35
|
const MSG_INDEX_FOLDER = 'msg-index';
|
|
34
36
|
async function makeInboxOnServer(cachePath, syncedFS, localFS, r) {
|
|
35
37
|
const procs = new synced_1.NamedProcs();
|
|
@@ -43,7 +45,10 @@ async function makeInboxOnServer(cachePath, syncedFS, localFS, r) {
|
|
|
43
45
|
const cache = await cached_msgs_1.CachedMessages.makeFor(cachePath, downloader, r.logError);
|
|
44
46
|
const index = await (0, msg_indexing_1.makeMsgIndex)(indexSyncedFS, indexLocalFS, r.logError);
|
|
45
47
|
const inboxEvents = new inbox_events_1.InboxEvents(msgReceiver, getMsg, listNewMsgs, removeMsg, r.logError);
|
|
48
|
+
let stopConnectionCheckingProc = (0, periodic_trigger_1.startPeriodicConditionalTriggerProc)(() => !inboxEvents.isConnected(), () => inboxEvents.resumeNetworkActivity(), CONNECTION_CHECK_WAIT_SECS);
|
|
46
49
|
async function close() {
|
|
50
|
+
stopConnectionCheckingProc === null || stopConnectionCheckingProc === void 0 ? void 0 : stopConnectionCheckingProc();
|
|
51
|
+
stopConnectionCheckingProc = undefined;
|
|
47
52
|
index.stopSyncing();
|
|
48
53
|
inboxEvents.close();
|
|
49
54
|
}
|
|
@@ -116,6 +121,9 @@ async function makeInboxOnServer(cachePath, syncedFS, localFS, r) {
|
|
|
116
121
|
const decrOut = await r.correspondents.msgDecryptor(meta.extMeta, getMainObjHeader, getOpenedMsg, checkerOfMidKeyCerts(msgOnDisk.deliveryTS));
|
|
117
122
|
if (decrOut) {
|
|
118
123
|
const { decrInfo, openedMsg } = decrOut;
|
|
124
|
+
if (r.correspondents.isAddressBlocked(decrInfo.correspondent)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
119
127
|
openedMsg.setMsgKeyRole(decrInfo.keyStatus);
|
|
120
128
|
// XXX we have never used this part of protocol, should it be removed at all from ASMail protocol?
|
|
121
129
|
checkServerAuthIfPresent(meta, decrInfo);
|
|
@@ -24,6 +24,7 @@ export declare class ASMail {
|
|
|
24
24
|
private config;
|
|
25
25
|
private sendingParams;
|
|
26
26
|
private getReportAddressForDomain;
|
|
27
|
+
private filter;
|
|
27
28
|
constructor(cryptor: AsyncSBoxCryptor, random: AsyncRNG, makeNet: MakeNet, inboxPathForUser: InboxPathForUser, logger: Logger);
|
|
28
29
|
init(address: string, getSigner: GetSigner, syncedFS: WritableFS, localFS: WritableFS, getStorages: StorageGetter, makeResolver: ServiceLocatorMaker, config: ConfigOfASMailServer, keyring: KeyringForASMail): Promise<void>;
|
|
29
30
|
private setupSendingParams;
|
|
@@ -35,5 +36,6 @@ export declare class ASMail {
|
|
|
35
36
|
suspendNetworkActivity(): void;
|
|
36
37
|
resumeNetworkActivity(): void;
|
|
37
38
|
get connectivityEvent$(): import("rxjs").Observable<import("./inbox/inbox-events").InboxConnectionStatus>;
|
|
39
|
+
private applyNewFilteringRuleToPeersInfo;
|
|
38
40
|
}
|
|
39
41
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
Copyright (C) 2015 - 2018, 2020 - 2022, 2025 3NSoft Inc.
|
|
3
|
+
Copyright (C) 2015 - 2018, 2020 - 2022, 2025 - 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
|
|
@@ -22,9 +22,11 @@ const error_1 = require("../../lib-common/exceptions/error");
|
|
|
22
22
|
const delivery_1 = require("./delivery");
|
|
23
23
|
const sending_params_1 = require("./sending-params");
|
|
24
24
|
const fs_sync_utils_1 = require("../../lib-client/fs-utils/fs-sync-utils");
|
|
25
|
+
const filtering_1 = require("./filtering");
|
|
25
26
|
const INBOX_DATA_FOLDER = 'inbox';
|
|
26
27
|
const DELIVERY_DATA_FOLDER = 'delivery';
|
|
27
28
|
const SEND_PARAMS_DATA_FOLDER = 'sending-params';
|
|
29
|
+
const FILTERS_DATA_FOLDER = 'filtering';
|
|
28
30
|
class ASMail {
|
|
29
31
|
constructor(cryptor, random, makeNet, inboxPathForUser, logger) {
|
|
30
32
|
this.cryptor = cryptor;
|
|
@@ -39,6 +41,7 @@ class ASMail {
|
|
|
39
41
|
this.config = undefined;
|
|
40
42
|
this.sendingParams = undefined;
|
|
41
43
|
this.getReportAddressForDomain = undefined;
|
|
44
|
+
this.filter = undefined;
|
|
42
45
|
Object.seal(this);
|
|
43
46
|
}
|
|
44
47
|
async init(address, getSigner, syncedFS, localFS, getStorages, makeResolver, config, keyring) {
|
|
@@ -50,6 +53,7 @@ class ASMail {
|
|
|
50
53
|
this.config = config;
|
|
51
54
|
this.keyring = keyring;
|
|
52
55
|
await this.setupSendingParams(syncedFS);
|
|
56
|
+
this.filter = await (0, filtering_1.makeAddressFilter)(await (0, fs_sync_utils_1.getOrMakeDirOnInit)(syncedFS, FILTERS_DATA_FOLDER), this.logger.logError, this.applyNewFilteringRuleToPeersInfo.bind(this));
|
|
53
57
|
await Promise.all([
|
|
54
58
|
this.setupInbox(syncedFS, localFS, getSigner, getStorages, makeResolver),
|
|
55
59
|
this.setupDelivery(localFS, getSigner, makeResolver)
|
|
@@ -80,7 +84,8 @@ class ASMail {
|
|
|
80
84
|
generateIntroKeysToSendMsg: this.keyring.generateIntroKeysToSendMsg,
|
|
81
85
|
getEstablishedKeysToSendMsg: this.keyring.getEstablishedKeysToSendMsg,
|
|
82
86
|
paramsForSendingTo: this.sendingParams.otherSides.get,
|
|
83
|
-
newParamsForSendingReplies: this.sendingParams.thisSide.getUpdated
|
|
87
|
+
newParamsForSendingReplies: this.sendingParams.thisSide.getUpdated,
|
|
88
|
+
isAddressBlocked: this.filter.isAddressBlocked
|
|
84
89
|
},
|
|
85
90
|
notifyMsgProgress: () => { throw new Error(`Method not set`); },
|
|
86
91
|
makeNet: this.makeNet,
|
|
@@ -103,7 +108,8 @@ class ASMail {
|
|
|
103
108
|
msgDecryptor: this.keyring.decrypt,
|
|
104
109
|
markOwnSendingParamsAsUsed: this.sendingParams.thisSide.setAsUsed,
|
|
105
110
|
saveParamsForSendingTo: this.sendingParams.otherSides.set,
|
|
106
|
-
midResolver: makeResolver('mailerid', this.logger.logError)
|
|
111
|
+
midResolver: makeResolver('mailerid', this.logger.logError),
|
|
112
|
+
isAddressBlocked: this.filter.isAddressBlocked
|
|
107
113
|
},
|
|
108
114
|
makeNet: this.makeNet,
|
|
109
115
|
logError: this.logger.logError
|
|
@@ -115,7 +121,8 @@ class ASMail {
|
|
|
115
121
|
delivery: this.delivery.makeCAP(),
|
|
116
122
|
inbox: this.inbox.makeCAP(),
|
|
117
123
|
config: this.config.makeCAP(),
|
|
118
|
-
getReportAddressForDomain: this.getReportAddressForDomain
|
|
124
|
+
getReportAddressForDomain: this.getReportAddressForDomain,
|
|
125
|
+
filter: this.filter.makeCAP()
|
|
119
126
|
};
|
|
120
127
|
return Object.freeze(w);
|
|
121
128
|
}
|
|
@@ -136,6 +143,7 @@ class ASMail {
|
|
|
136
143
|
await this.delivery.close();
|
|
137
144
|
await this.keyring.close();
|
|
138
145
|
await this.sendingParams.close();
|
|
146
|
+
this.filter.close();
|
|
139
147
|
}
|
|
140
148
|
suspendNetworkActivity() {
|
|
141
149
|
this.inbox.suspendNetworkActivity();
|
|
@@ -146,6 +154,23 @@ class ASMail {
|
|
|
146
154
|
get connectivityEvent$() {
|
|
147
155
|
return this.inbox.connectivityEvent$;
|
|
148
156
|
}
|
|
157
|
+
async applyNewFilteringRuleToPeersInfo(r) {
|
|
158
|
+
switch (r.ruleType) {
|
|
159
|
+
case 'block-address': {
|
|
160
|
+
const { username, domain } = r;
|
|
161
|
+
const cAddr = `${username}@${domain}`;
|
|
162
|
+
await this.keyring.forgetCorrespondentAt(cAddr);
|
|
163
|
+
await this.sendingParams.forgetCorrespondentAt(cAddr);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case 'block-domain': {
|
|
167
|
+
const { domain } = r;
|
|
168
|
+
await this.keyring.forgetCorrespondentAt(domain);
|
|
169
|
+
await this.sendingParams.forgetCorrespondentAt(domain);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
149
174
|
}
|
|
150
175
|
exports.ASMail = ASMail;
|
|
151
176
|
Object.freeze(ASMail.prototype);
|
|
@@ -18,5 +18,7 @@ export interface SendingParamsHolder {
|
|
|
18
18
|
thisSide: SendingParamsThisSide;
|
|
19
19
|
otherSides: SendingParamsOtherSides;
|
|
20
20
|
close: () => Promise<void>;
|
|
21
|
+
forgetCorrespondentAt(cAddr: string): Promise<void>;
|
|
22
|
+
forgetAllCorrespondentsAtDomain(domain: string): Promise<void>;
|
|
21
23
|
}
|
|
22
24
|
export declare function makeSendingParamsHolder(fs: WritableFS, anonInvitesOnServer: ParamOnServer<'anon-sender/invites'>, random: AsyncRNG): Promise<SendingParamsHolder>;
|