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.
Files changed (47) hide show
  1. package/build/api-defs/asmail.d.ts +27 -4
  2. package/build/api-defs/files.d.ts +6 -3
  3. package/build/core/asmail/asmail-cap-ipc.js +22 -3
  4. package/build/core/asmail/delivery/common.d.ts +3 -0
  5. package/build/core/asmail/delivery/common.js +5 -0
  6. package/build/core/asmail/delivery/index.js +6 -6
  7. package/build/core/asmail/delivery/msg.js +1 -6
  8. package/build/core/asmail/delivery/per-recipient-wip.js +3 -5
  9. package/build/core/asmail/filtering.d.ts +11 -0
  10. package/build/core/asmail/filtering.js +219 -0
  11. package/build/core/asmail/inbox/inbox-events.d.ts +1 -0
  12. package/build/core/asmail/inbox/inbox-events.js +3 -0
  13. package/build/core/asmail/inbox/index.d.ts +1 -0
  14. package/build/core/asmail/inbox/index.js +8 -0
  15. package/build/core/asmail/index.d.ts +2 -0
  16. package/build/core/asmail/index.js +29 -4
  17. package/build/core/asmail/sending-params/index.d.ts +2 -0
  18. package/build/core/asmail/sending-params/index.js +11 -1
  19. package/build/core/asmail/sending-params/own-params.d.ts +2 -0
  20. package/build/core/asmail/sending-params/own-params.js +27 -5
  21. package/build/core/asmail/sending-params/params-from-others.d.ts +2 -0
  22. package/build/core/asmail/sending-params/params-from-others.js +28 -3
  23. package/build/core/keyring/index.d.ts +2 -0
  24. package/build/core/keyring/index.js +4 -2
  25. package/build/core/keyring/peer-keys/dataset/v2/logs.d.ts +13 -1
  26. package/build/core/keyring/peer-keys/dataset/v2/logs.js +17 -1
  27. package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.d.ts +2 -0
  28. package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.js +31 -1
  29. package/build/core/keyring/peer-keys/index.d.ts +2 -0
  30. package/build/core/keyring/peer-keys/index.js +11 -1
  31. package/build/core/storage/synced/remote-events.d.ts +1 -0
  32. package/build/core/storage/synced/remote-events.js +3 -0
  33. package/build/core/storage/synced/storage.d.ts +2 -0
  34. package/build/core/storage/synced/storage.js +13 -0
  35. package/build/core/storage/synced/upsyncer.js +2 -1
  36. package/build/lib-client/3nstorage/storage-owner.js +1 -2
  37. package/build/lib-client/asmail/sender.d.ts +0 -6
  38. package/build/lib-client/asmail/sender.js +20 -64
  39. package/build/lib-client/service-locator.d.ts +1 -2
  40. package/build/lib-client/service-locator.js +0 -1
  41. package/build/lib-common/awaitable-state.d.ts +1 -0
  42. package/build/lib-common/awaitable-state.js +3 -0
  43. package/build/lib-common/canonical-address.js +3 -0
  44. package/build/lib-common/periodic-trigger.d.ts +1 -0
  45. package/build/lib-common/periodic-trigger.js +44 -0
  46. package/build/tests/caps-api/asmail.js +34 -4
  47. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright (C) 2016, 2020, 2022, 2024 - 2025 3NSoft Inc.
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
- const exc = {
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
- const exc = {
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
- const exc = {
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')) {
@@ -62,6 +62,7 @@ export interface ResourcesForReceiving {
62
62
  */
63
63
  saveParamsForSendingTo: (address: string, params: SendingParams) => Promise<void>;
64
64
  midResolver: ServiceLocator;
65
+ isAddressBlocked(address: string): boolean;
65
66
  };
66
67
  logError: LogError;
67
68
  }
@@ -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>;