spec-3nweb-server 1.8.6 → 1.8.7

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 (54) hide show
  1. package/build/3nstorage/resources/store.d.ts +2 -2
  2. package/build/3nstorage/resources/store.js +1 -1
  3. package/build/3nstorage/resources/users.d.ts +3 -3
  4. package/build/3nstorage/resources/users.js +1 -1
  5. package/build/3nstorage/resources/utils.d.ts +6 -0
  6. package/build/3nstorage/resources/utils.js +41 -0
  7. package/build/3nstorage/routes/owner/archive-obj-version.js +8 -10
  8. package/build/3nstorage/routes/owner/cancel-trans.js +5 -4
  9. package/build/3nstorage/routes/owner/delete-obj.js +7 -8
  10. package/build/3nstorage/routes/owner/get-archived-obj-ver.js +11 -13
  11. package/build/3nstorage/routes/owner/get-current-obj.js +7 -6
  12. package/build/3nstorage/routes/owner/get-obj-status.js +8 -8
  13. package/build/3nstorage/routes/owner/put-current-obj.js +25 -55
  14. package/build/3nstorage/routes/owner/session-params.js +2 -0
  15. package/build/asmail/resources/utils.d.ts +6 -0
  16. package/build/asmail/resources/utils.js +36 -0
  17. package/build/asmail/routes/delivery/finalize-delivery.js +3 -2
  18. package/build/asmail/routes/delivery/pre-flight.js +9 -26
  19. package/build/asmail/routes/delivery/provide-recipient-pubkey.js +4 -3
  20. package/build/asmail/routes/delivery/put-metadata.js +13 -21
  21. package/build/asmail/routes/delivery/put-obj.js +19 -25
  22. package/build/asmail/routes/delivery/restart-session.d.ts +4 -8
  23. package/build/asmail/routes/delivery/restart-session.js +10 -23
  24. package/build/asmail/routes/delivery/sender-authorization.js +5 -5
  25. package/build/asmail/routes/delivery/start-session.js +9 -24
  26. package/build/asmail/routes/retrieval/get-message-meta.js +4 -7
  27. package/build/asmail/routes/retrieval/get-message-obj.js +10 -7
  28. package/build/asmail/routes/retrieval/list-messages.js +4 -4
  29. package/build/asmail/routes/retrieval/remove-message.js +3 -2
  30. package/package.json +1 -1
  31. package/ts-code/3nstorage/resources/store.ts +3 -3
  32. package/ts-code/3nstorage/resources/users.ts +5 -5
  33. package/ts-code/3nstorage/resources/utils.ts +45 -0
  34. package/ts-code/3nstorage/routes/owner/archive-obj-version.ts +8 -10
  35. package/ts-code/3nstorage/routes/owner/cancel-trans.ts +5 -3
  36. package/ts-code/3nstorage/routes/owner/delete-obj.ts +9 -13
  37. package/ts-code/3nstorage/routes/owner/get-archived-obj-ver.ts +13 -15
  38. package/ts-code/3nstorage/routes/owner/get-current-obj.ts +7 -5
  39. package/ts-code/3nstorage/routes/owner/get-obj-status.ts +8 -8
  40. package/ts-code/3nstorage/routes/owner/put-current-obj.ts +33 -60
  41. package/ts-code/3nstorage/routes/owner/session-params.ts +4 -3
  42. package/ts-code/asmail/resources/utils.ts +41 -0
  43. package/ts-code/asmail/routes/delivery/finalize-delivery.ts +3 -2
  44. package/ts-code/asmail/routes/delivery/pre-flight.ts +15 -29
  45. package/ts-code/asmail/routes/delivery/provide-recipient-pubkey.ts +4 -4
  46. package/ts-code/asmail/routes/delivery/put-metadata.ts +21 -30
  47. package/ts-code/asmail/routes/delivery/put-obj.ts +20 -34
  48. package/ts-code/asmail/routes/delivery/restart-session.ts +11 -24
  49. package/ts-code/asmail/routes/delivery/sender-authorization.ts +5 -5
  50. package/ts-code/asmail/routes/delivery/start-session.ts +17 -32
  51. package/ts-code/asmail/routes/retrieval/get-message-meta.ts +5 -8
  52. package/ts-code/asmail/routes/retrieval/get-message-obj.ts +12 -15
  53. package/ts-code/asmail/routes/retrieval/list-messages.ts +4 -6
  54. package/ts-code/asmail/routes/retrieval/remove-message.ts +3 -2
@@ -0,0 +1,41 @@
1
+ /*
2
+ Copyright (C) 2026 3NSoft Inc.
3
+
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU General Public License as published by the Free Software
6
+ Foundation, either version 3 of the License, or (at your option) any later
7
+ version.
8
+
9
+ This program is distributed in the hope that it will be useful, but
10
+ WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
+ See the GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License along with
15
+ this program. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+
18
+ import { Response, Request } from 'express';
19
+ import { base64urlSafe } from '../../lib-common/buffer-utils';
20
+ import { attachByteDrainToRequest } from '../../lib-server/middleware/body-parsers';
21
+ import { ErrorReply } from '../../lib-common/service-api/asmail/delivery';
22
+
23
+ export function getObjIdFromParams(req: Request): {
24
+ objId: string; objIdParseErr?: string;
25
+ } {
26
+ const objId = req.params.objId;
27
+ return ((!!objId && base64urlSafe.allCharsFromAlphabet(objId)) ?
28
+ { objId } :
29
+ { objId: undefined as any, objIdParseErr: "Bad object id" }
30
+ );
31
+ }
32
+
33
+ export function replyWithErr(status: number, msg: string, res: Response, reqToDrainBytesFrom?: Request): void {
34
+ if (reqToDrainBytesFrom) {
35
+ attachByteDrainToRequest(reqToDrainBytesFrom);
36
+ }
37
+ res.status(status).json({ error: msg } as ErrorReply);
38
+ }
39
+
40
+
41
+ Object.freeze(exports);
@@ -19,6 +19,7 @@ import { RequestHandler } from 'express';
19
19
  import { SC as recipSC, MsgDelivery } from '../../resources/recipients';
20
20
  import { Request } from '../../resources/delivery-sessions';
21
21
  import { completion as api, ERR_SC } from '../../../lib-common/service-api/asmail/delivery';
22
+ import { replyWithErr } from '../../resources/utils';
22
23
 
23
24
  export function finalizeDelivery(finDelivFunc: MsgDelivery['finalizeDelivery']): RequestHandler {
24
25
  return async (req: Request, res, next) => {
@@ -35,10 +36,10 @@ export function finalizeDelivery(finDelivFunc: MsgDelivery['finalizeDelivery']):
35
36
  next(err);
36
37
  } else if (err === recipSC.USER_UNKNOWN) {
37
38
  session.close();
38
- res.status(ERR_SC.server).send("Recipient disappeared from the system.");
39
+ replyWithErr(ERR_SC.server, "Recipient disappeared from the system.", res);
39
40
  } else if (err === recipSC.MSG_UNKNOWN) {
40
41
  session.close();
41
- res.status(ERR_SC.server).send("Message disappeared from the system.");
42
+ replyWithErr(ERR_SC.server, "Message disappeared from the system.", res);
42
43
  } else {
43
44
  next(new Error("Unhandled storage error code: "+err));
44
45
  }
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright (C) 2015 - 2017, 2025 3NSoft Inc.
2
+ Copyright (C) 2015 - 2017, 2025 - 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
@@ -17,10 +17,11 @@
17
17
 
18
18
  import { RequestHandler, Response } from 'express';
19
19
  import { SC as recipSC, MsgDelivery } from '../../resources/recipients';
20
- import { preFlight as api, ERR_SC, ErrorReply } from '../../../lib-common/service-api/asmail/delivery';
20
+ import { preFlight as api, ERR_SC } from '../../../lib-common/service-api/asmail/delivery';
21
21
  import { Request } from '../../resources/delivery-sessions';
22
22
  import { Redirect } from './start-session';
23
23
  import { checkAndTransformAddress } from '../../../lib-common/canonical-address';
24
+ import { replyWithErr } from '../../resources/utils';
24
25
 
25
26
  /**
26
27
  * This creates a pre-flight route handler.
@@ -44,17 +45,16 @@ export function preFlight(
44
45
  ): Promise<void> {
45
46
  const msgSize = await allowedMsgSizeFunc(recipient, sender, invitation);
46
47
  if (msgSize > 0) {
47
- res.status(api.SC.ok).json( <api.Reply> {
48
+ res.status(api.SC.ok).json({
48
49
  maxMsgLength: msgSize
49
- });
50
+ } as api.Reply);
50
51
  } else if (msgSize === 0) {
51
- res.status(api.SC.senderNotAllowed).json( <ErrorReply> {
52
- error: `${sender ? sender : "Anonymous sender "} is not allowed to leave mail for ${recipient}`
53
- });
52
+ replyWithErr(
53
+ api.SC.senderNotAllowed,
54
+ `${sender ? sender : "Anonymous sender "} is not allowed to leave mail for ${recipient}`, res
55
+ );
54
56
  } else if (msgSize === -1) {
55
- res.status(api.SC.inboxFull).json( <ErrorReply> {
56
- error: `Mail box for ${recipient} is full.`
57
- });
57
+ replyWithErr(api.SC.inboxFull, `Mail box for ${recipient} is full.`, res);
58
58
  } else {
59
59
  throw new Error(`Unrecognized code ${msgSize} for message size limits.`);
60
60
  }
@@ -70,37 +70,25 @@ export function preFlight(
70
70
 
71
71
  // already existing session indicates repeated call, which should be bounced off
72
72
  if (session) {
73
- res.status(ERR_SC.duplicateReq).json( <ErrorReply> {
74
- error: "This protocol request has already been served."
75
- });
76
- return;
73
+ return replyWithErr(ERR_SC.duplicateReq, "This protocol request has already been served.", res);
77
74
  }
78
75
 
79
76
  // missing recipient makes a bad request
80
77
  if (!recipient) {
81
- res.status(ERR_SC.malformed).json( <ErrorReply> {
82
- error: "Recipient is either missing in the request, or is malformed"
83
- });
84
- return;
78
+ return replyWithErr(ERR_SC.malformed, "Recipient is either missing in the request, or is malformed", res);
85
79
  }
86
80
 
87
81
  // if sender is given, we canonicalize the address
88
82
  if (sender) {
89
83
  sender = checkAndTransformAddress(sender);
90
84
  if (!sender) {
91
- res.status(ERR_SC.malformed).json( <ErrorReply> {
92
- error: "Sender field is malformed"
93
- });
94
- return;
85
+ return replyWithErr(ERR_SC.malformed, "Sender field is malformed", res);
95
86
  }
96
87
  }
97
88
 
98
89
  // check invitation field
99
90
  if ((invitation !== undefined) && (typeof invitation !== 'string')) {
100
- res.status(ERR_SC.malformed).json( <ErrorReply> {
101
- error: "Inviation field is malformed"
102
- });
103
- return;
91
+ return replyWithErr(ERR_SC.malformed, "Inviation field is malformed", res);
104
92
  }
105
93
 
106
94
  try {
@@ -118,9 +106,7 @@ export function preFlight(
118
106
  }
119
107
  } catch (err) {
120
108
  if (err === recipSC.USER_UNKNOWN) {
121
- res.status(api.SC.unknownRecipient).json( <ErrorReply> {
122
- error: `Recipient ${recipient} is unknown.`
123
- });
109
+ replyWithErr(api.SC.unknownRecipient, `Recipient ${recipient} is unknown.`, res);
124
110
  } else {
125
111
  next(err);
126
112
  }
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright (C) 2015 - 2016, 2025 3NSoft Inc.
2
+ Copyright (C) 2015 - 2016, 2025 - 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
@@ -19,6 +19,7 @@ import { RequestHandler } from 'express';
19
19
  import { SC as recipSC, MsgDelivery } from '../../resources/recipients';
20
20
  import { initPubKey as api, ERR_SC } from '../../../lib-common/service-api/asmail/delivery';
21
21
  import { Request } from '../../resources/delivery-sessions';
22
+ import { replyWithErr } from '../../resources/utils';
22
23
 
23
24
  /**
24
25
  * This creates a get-init-pub-key route handler.
@@ -37,15 +38,14 @@ export function getRecipientPubKey(
37
38
  if (certs) {
38
39
  res.status(api.SC.ok).json(certs);
39
40
  } else {
40
- res.status(api.SC.pkeyNotRegistered).send('No public key registered for the recipient.');
41
+ replyWithErr(api.SC.pkeyNotRegistered, 'No public key registered for the recipient.', res);
41
42
  session.close();
42
43
  }
43
44
  } catch (err) {
44
45
  if (typeof err !== "string") {
45
46
  next(err);
46
47
  } else if (err === recipSC.USER_UNKNOWN) {
47
- res.status(ERR_SC.server).send(
48
- "Recipient disappeared from the system.");
48
+ replyWithErr(ERR_SC.server, "Recipient disappeared from the system.", res);
49
49
  session.close();
50
50
  } else {
51
51
  next(new Error("Unhandled storage error code: "+err));
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright (C) 2015 - 2016, 2025 3NSoft Inc.
2
+ Copyright (C) 2015 - 2016, 2025 - 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
@@ -17,28 +17,23 @@
17
17
 
18
18
  import { RequestHandler } from 'express';
19
19
  import { SC as recipSC, MsgDelivery } from '../../resources/recipients';
20
- import { msgMeta as api, ERR_SC, ErrorReply } from '../../../lib-common/service-api/asmail/delivery';
20
+ import { msgMeta as api, ERR_SC } from '../../../lib-common/service-api/asmail/delivery';
21
21
  import * as confUtil from '../../../lib-server/conf-util';
22
22
  import { Request } from '../../resources/delivery-sessions';
23
23
  import { base64urlSafe } from '../../../lib-common/buffer-utils';
24
+ import { replyWithErr } from '../../resources/utils';
24
25
 
25
- function findProblemWithObjIds(ids: string[]): ErrorReply|undefined {
26
+ function findProblemWithObjIds(ids: string[]): string|undefined {
26
27
  if (!Array.isArray(ids)) {
27
- return {
28
- error: "Object ids are missing."
29
- };
28
+ return "Object ids are missing.";
30
29
  }
31
30
  const checkedIds = new Set<string>();
32
31
  for (const objId of ids) {
33
32
  if (checkedIds.has(objId)) {
34
- return {
35
- error: "Duplication of object ids."
36
- };
33
+ return "Duplication of object ids.";
37
34
  }
38
35
  if (!base64urlSafe.allCharsFromAlphabet(objId!)) {
39
- return {
40
- error: "Object id is invalid."
41
- };
36
+ return "Object id is invalid.";
42
37
  }
43
38
  checkedIds.add(objId);
44
39
  }
@@ -53,37 +48,33 @@ export function saveMetadata(
53
48
 
54
49
  return async (req: Request, res, next) => {
55
50
  const session = req.session;
56
- const msgMeta: api.Request = req.body;
57
- const recipient = session.params.recipient;
58
- const objIds = msgMeta.objIds;
59
-
60
51
  if (session.params.msgId) {
61
- res.status(ERR_SC.duplicateReq).json( <ErrorReply> {
62
- error: "This protocol request has already been served."
63
- });
64
- return;
52
+ return replyWithErr(ERR_SC.duplicateReq, "This protocol request has already been served.", res);
65
53
  }
66
54
 
67
- if (findProblemWithObjIds(objIds)) {
68
- res.status(ERR_SC.malformed).json(findProblemWithObjIds(objIds));
69
- return;
55
+ const recipient = session.params.recipient;
56
+ const msgMeta: api.Request = req.body;
57
+
58
+ const objIds = msgMeta.objIds;
59
+ const errWithWithObjIds = findProblemWithObjIds(objIds);
60
+ if (errWithWithObjIds) {
61
+ return replyWithErr(ERR_SC.malformed, errWithWithObjIds, res);
70
62
  }
71
63
 
72
64
  try {
73
- const msgId = await setMsgStorageFunc(recipient, msgMeta,
74
- session.params.sender, session.params.invite,
75
- session.params.maxMsgLength);
65
+ const msgId = await setMsgStorageFunc(
66
+ recipient, msgMeta, session.params.sender, session.params.invite, session.params.maxMsgLength
67
+ );
76
68
  session.params.msgId = msgId;
77
- res.status(api.SC.ok).json( <api.Reply> {
69
+ res.status(api.SC.ok).json({
78
70
  msgId,
79
71
  maxChunkSize
80
- });
72
+ } as api.Reply);
81
73
  } catch (err) {
82
74
  if ("string" !== typeof err) {
83
75
  next(err);
84
76
  } else if (err === recipSC.USER_UNKNOWN) {
85
- res.status(ERR_SC.server).send(
86
- "Recipient disappeared from the system.");
77
+ replyWithErr(ERR_SC.server, "Recipient disappeared from the system.", res);
87
78
  session.close();
88
79
  }
89
80
  }
@@ -20,7 +20,7 @@ import { SC as recipSC, MsgDelivery, BIN_TYPE } from '../../resources/recipients
20
20
  import { msgObj as api, ERR_SC, HTTP_HEADER, PutObjFirstQueryOpts, PutObjSecondQueryOpts } from '../../../lib-common/service-api/asmail/delivery';
21
21
  import * as confUtil from '../../../lib-server/conf-util';
22
22
  import { Request } from '../../resources/delivery-sessions';
23
- import { attachByteDrainToRequest } from '../../../lib-server/middleware/body-parsers';
23
+ import { getObjIdFromParams, replyWithErr } from '../../resources/utils';
24
24
 
25
25
  export function saveMsgObj(saveObjFunc: MsgDelivery['saveObj'], chunkLimit: string|number): RequestHandler {
26
26
 
@@ -29,11 +29,7 @@ export function saveMsgObj(saveObjFunc: MsgDelivery['saveObj'], chunkLimit: stri
29
29
  return async (req: Request, res, next) => {
30
30
 
31
31
  if (!req.is(BIN_TYPE)) {
32
- attachByteDrainToRequest(req);
33
- res.status(ERR_SC.wrongContentType).send(
34
- `Content-Type must be ${BIN_TYPE} for this call.`
35
- );
36
- return;
32
+ return replyWithErr(ERR_SC.wrongContentType, `Content-Type must be ${BIN_TYPE} for this call.`, res, req);
37
33
  }
38
34
 
39
35
  const session = req.session;
@@ -41,51 +37,46 @@ export function saveMsgObj(saveObjFunc: MsgDelivery['saveObj'], chunkLimit: stri
41
37
  const msgId = session.params.msgId;
42
38
 
43
39
  if (!msgId) {
44
- res.status(ERR_SC.earlyReq).send(
45
- "Metadata has not been sent, yet."
46
- );
47
- return;
40
+ return replyWithErr(ERR_SC.earlyReq, "Metadata has not been sent, yet.", res, req);
48
41
  }
49
42
 
50
- const objId: string = req.params.objId;
43
+ const { objId, objIdParseErr } = getObjIdFromParams(req);
44
+ if (objIdParseErr) {
45
+ return replyWithErr(ERR_SC.malformed, objIdParseErr, res);
46
+ }
51
47
 
52
48
  // get and check Content-Length, implicitly sending replies for bad length
53
49
  let len = getContentLenOrSendError(req, res, maxChunkSize);
54
50
  if (len === undefined) {
55
- attachByteDrainToRequest(req);
56
51
  return;
57
52
  }
58
53
 
59
54
  // extract and check query parameters
60
55
  const opts = extractQueryOptions(req);
61
56
  if (!opts) {
62
- return replyToMalformed(req, "Bad query parameters", res);
57
+ return replyWithErr(ERR_SC.malformed, "Bad query parameters", res, req);
63
58
  }
64
59
 
65
60
  if (opts.fstReq) {
66
61
  // check options versus chunk length
67
62
  if (opts.fstReq.header > len) {
68
- return replyToMalformed(req, "Bad query parameters", res);
63
+ return replyWithErr(ERR_SC.malformed, "Bad query parameters", res, req);
69
64
  }
70
65
  } else if (opts.sndReq) {
71
66
  // check options versus chunk length
72
67
  if ((len === 0) && !opts.sndReq.last) {
73
- return replyToMalformed(req, "No segment bytes", res);
68
+ return replyWithErr(ERR_SC.malformed, "No segment bytes", res, req);
74
69
  }
75
70
  } else {
76
71
  return next(new Error(`This place should not be reachable`));
77
72
  }
78
73
 
79
74
  // ensure that it is ok space-wise to save additional bytes
80
- const allowedSpace =
81
- (session.params.maxMsgLength - session.params.currentMsgLength);
75
+ const allowedSpace = (session.params.maxMsgLength - session.params.currentMsgLength);
82
76
  if (len <= allowedSpace) {
83
77
  session.params.currentMsgLength += len;
84
78
  } else {
85
- res.status(ERR_SC.contentTooLong).send(
86
- "This request goes over the message limit."
87
- );
88
- return;
79
+ return replyWithErr(ERR_SC.contentTooLong, "This request goes over the message limit.", res, req);
89
80
  }
90
81
 
91
82
  try {
@@ -96,19 +87,19 @@ export function saveMsgObj(saveObjFunc: MsgDelivery['saveObj'], chunkLimit: stri
96
87
  if ("string" !== typeof err) {
97
88
  next(err);
98
89
  } else if (err === recipSC.USER_UNKNOWN) {
99
- res.status(ERR_SC.server).send("Recipient disappeared from the system.");
90
+ replyWithErr(ERR_SC.server, "Recipient disappeared from the system.", res);
100
91
  session.close();
101
92
  } else if (err === recipSC.OBJ_EXIST) {
102
- res.status(api.SC.objAlreadyExists).send(`Object ${objId} already exists.`);
93
+ replyWithErr(api.SC.objAlreadyExists, `Object ${objId} already exists.`, res);
103
94
  } else if (err === recipSC.MSG_UNKNOWN) {
104
- res.status(ERR_SC.server).send("Message disappeared from the system.");
95
+ replyWithErr(ERR_SC.server, "Message disappeared from the system.", res);
105
96
  session.close();
106
97
  } else if (err === recipSC.OBJ_UNKNOWN) {
107
- res.status(api.SC.unknownObj).send(`Object ${objId} is unknown.`);
98
+ replyWithErr(api.SC.unknownObj, `Object ${objId} is unknown.`, res);
108
99
  } else if (err === recipSC.WRONG_OBJ_STATE) {
109
- res.status(ERR_SC.malformed).send(`Object ${objId} is not in a different state.`);
100
+ replyWithErr(ERR_SC.malformed, `Object ${objId} is not in a different state.`, res);
110
101
  } else if (err === recipSC.OBJ_FILE_INCOMPLETE) {
111
- res.status(ERR_SC.objIncomplete).send(`Object ${objId} is not complete.`);
102
+ replyWithErr(ERR_SC.objIncomplete, `Object ${objId} is not complete.`, res);
112
103
  } else {
113
104
  next(new Error(`Unhandled storage error code: ${err}`));
114
105
  }
@@ -149,18 +140,13 @@ function extractQueryOptions(req: Request): undefined|{
149
140
  function getContentLenOrSendError(req: Request, res: Response, maxChunkSize: number): number|undefined {
150
141
  const contentLength = parseInt(req.get(HTTP_HEADER.contentLength)!);
151
142
  if (isNaN(contentLength)) {
152
- res.status(ERR_SC.contentLenMissing).send("Content-Length header is required with proper number.");
143
+ replyWithErr(ERR_SC.contentLenMissing, "Content-Length header is required with proper number.", res, req);
153
144
  } else if (contentLength > maxChunkSize) {
154
- res.status(ERR_SC.contentTooLong).send("Request body is too long.");
145
+ replyWithErr(ERR_SC.contentTooLong, "Request body is too long.", res, req);
155
146
  } else {
156
147
  return contentLength;
157
148
  }
158
149
  return;
159
150
  }
160
151
 
161
- function replyToMalformed(req: Request, msg: string, res: Response): void {
162
- attachByteDrainToRequest(req);
163
- res.status(ERR_SC.malformed).send(msg);
164
- }
165
-
166
152
  Object.freeze(exports);
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright (C) 2017, 2025 3NSoft Inc.
2
+ Copyright (C) 2017, 2025 - 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
@@ -17,22 +17,19 @@
17
17
 
18
18
  import { RequestHandler, Response } from 'express';
19
19
  import { SC as recipSC, MsgDelivery } from '../../resources/recipients';
20
- import { sessionRestart as api, ERR_SC, ErrorReply } from '../../../lib-common/service-api/asmail/delivery';
20
+ import { sessionRestart as api, ERR_SC } from '../../../lib-common/service-api/asmail/delivery';
21
21
  import { Request, GenerateSession, GetSessionForMsg } from '../../resources/delivery-sessions';
22
22
  import { Redirect } from './start-session';
23
23
  import { checkAndTransformAddress } from '../../../lib-common/canonical-address';
24
24
  import * as confUtil from '../../../lib-server/conf-util';
25
+ import { replyWithErr } from '../../resources/utils';
25
26
 
26
27
  /**
27
28
  * This creates a session restart route handler.
28
- * @param sessionGenFunc is a promise returning function that generates new
29
- * session objects.
30
- * @param sessionForMsgFunc is a function that promises existing session for
31
- * a known message in delivery.
32
- * @param incompleteMsgFunc is a function that promises parameters of a message
33
- * that is still in delivery.
34
- * @param redirectFunc is an optional function that returns a promise,
35
- * resolvable to
29
+ * @param sessionGenFunc is a promise returning function that generates new session objects.
30
+ * @param sessionForMsgFunc is a function that promises existing session for a known message in delivery.
31
+ * @param incompleteMsgFunc is a function that promises parameters of a message that is still in delivery.
32
+ * @param redirectFunc is an optional function that returns a promise, resolvable to
36
33
  * (1) string with URI for ASMail service, which is serving given recipient,
37
34
  * (2) undefined, if it is this server should service given recipient.
38
35
  */
@@ -83,18 +80,12 @@ export function restartSession(
83
80
 
84
81
  // missing recipient makes a bad request
85
82
  if (!recipient) {
86
- res.status(ERR_SC.malformed).json( <ErrorReply> {
87
- error: "Recipient is either missing in the request, or is malformed"
88
- });
89
- return;
83
+ return replyWithErr(ERR_SC.malformed, "Recipient is either missing in the request, or is malformed", res);
90
84
  }
91
85
 
92
86
  // check message id
93
87
  if (typeof msgId !== 'string') {
94
- res.status(ERR_SC.malformed).json( <ErrorReply> {
95
- error: "Message id is either missing, or is malformed"
96
- });
97
- return;
88
+ return replyWithErr(ERR_SC.malformed, "Message id is either missing, or is malformed", res);
98
89
  }
99
90
 
100
91
  try {
@@ -112,13 +103,9 @@ export function restartSession(
112
103
  }
113
104
  } catch (err) {
114
105
  if (err === recipSC.USER_UNKNOWN) {
115
- res.status(api.SC.unknownRecipient).json( <ErrorReply> {
116
- error: `Recipient ${recipient} is unknown.`
117
- });
106
+ replyWithErr(api.SC.unknownRecipient, `Recipient ${recipient} is unknown.`, res);
118
107
  } else if (err === recipSC.MSG_UNKNOWN) {
119
- res.status(api.SC.unknownMsg).json( <ErrorReply> {
120
- error: `Recipient ${recipient} is unknown.`
121
- });
108
+ replyWithErr(api.SC.unknownMsg, `Recipient ${recipient} is unknown.`, res);
122
109
  } else {
123
110
  next(err);
124
111
  }
@@ -19,6 +19,7 @@ import { RequestHandler } from 'express';
19
19
  import { SignedLoad } from '../../../lib-common/jwkeys';
20
20
  import { authSender as api, ERR_SC } from '../../../lib-common/service-api/asmail/delivery';
21
21
  import { Request } from '../../resources/delivery-sessions';
22
+ import { replyWithErr } from '../../resources/utils';
22
23
 
23
24
  export type IMidAuthorizer = (
24
25
  rpDomain: string, sessionId: string, userId: string,
@@ -38,8 +39,7 @@ export function authorize(relyingPartyDomain: string, midAuthorizingFunc: IMidAu
38
39
  return async (req: Request, res, next) => {
39
40
 
40
41
  if (req.session.isAuthorized) {
41
- res.status(ERR_SC.duplicateReq).send("This protocol request has already been served.");
42
- return;
42
+ return replyWithErr(ERR_SC.duplicateReq, "This protocol request has already been served.", res);
43
43
  }
44
44
 
45
45
  const rb: api.Request = req.body;
@@ -49,13 +49,13 @@ export function authorize(relyingPartyDomain: string, midAuthorizingFunc: IMidAu
49
49
  if (!sender) {
50
50
  // This case must be rejected, because place for authorizing
51
51
  // anonymous connection is at the session start.
52
- res.status(api.SC.authFailed).send("Server is not accepting provided credentials.");
52
+ replyWithErr(api.SC.authFailed, "Server is not accepting provided credentials.", res);
53
53
  req.session.close();
54
54
  return;
55
55
  }
56
56
 
57
57
  if (!rb.assertion || !rb.userCert || !rb.provCert) {
58
- res.status(ERR_SC.malformed).send("No credentials given.");
58
+ replyWithErr(ERR_SC.malformed, "No credentials given.", res);
59
59
  req.session.close();
60
60
  return;
61
61
  }
@@ -68,7 +68,7 @@ export function authorize(relyingPartyDomain: string, midAuthorizingFunc: IMidAu
68
68
  req.session.isAuthorized = true;
69
69
  res.status(api.SC.ok).send();
70
70
  } else {
71
- res.status(api.SC.authFailed).send("Server is not accepting provided credentials.");
71
+ replyWithErr(api.SC.authFailed, "Server is not accepting provided credentials.", res);
72
72
  req.session.close();
73
73
  }
74
74
  } catch (err) {
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright (C) 2015 - 2016, 2025 3NSoft Inc.
2
+ Copyright (C) 2015 - 2016, 2025 - 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
@@ -18,8 +18,9 @@
18
18
  import { RequestHandler, Response } from 'express';
19
19
  import { GenerateSession, Request } from '../../resources/delivery-sessions';
20
20
  import { SC as recipSC, MsgDelivery } from '../../resources/recipients';
21
- import { sessionStart as api, ERR_SC, ErrorReply } from '../../../lib-common/service-api/asmail/delivery';
21
+ import { sessionStart as api, ERR_SC } from '../../../lib-common/service-api/asmail/delivery';
22
22
  import { checkAndTransformAddress } from '../../../lib-common/canonical-address';
23
+ import { replyWithErr } from '../../resources/utils';
23
24
 
24
25
  export type Redirect = (userId: string) => Promise<string>;
25
26
 
@@ -64,17 +65,14 @@ export function startSession(
64
65
  maxMsgLength: msgSize
65
66
  });
66
67
  } else if (msgSize === 0) {
67
- res.status(api.SC.senderNotAllowed).json( <ErrorReply> {
68
- error: `${sender ? sender : "Anonymous sender "} is not allowed to leave mail for ${recipient}`
69
- });
68
+ replyWithErr(
69
+ api.SC.senderNotAllowed,
70
+ `${sender ? sender : "Anonymous sender "} is not allowed to leave mail for ${recipient}`, res
71
+ );
70
72
  } else if (msgSize === -1) {
71
- res.status(api.SC.inboxFull).json( <ErrorReply> {
72
- error: `Mail box for ${recipient} is full.`
73
- });
73
+ replyWithErr(api.SC.inboxFull, `Mail box for ${recipient} is full.`, res);
74
74
  } else {
75
- throw new Error(
76
- `Unrecognized code ${msgSize} for message size limits.`
77
- );
75
+ throw new Error(`Unrecognized code ${msgSize} for message size limits.`);
78
76
  }
79
77
  }
80
78
 
@@ -88,17 +86,13 @@ export function startSession(
88
86
 
89
87
  // already existing session indicates repeated call, which should be bounced off
90
88
  if (session) {
91
- res.status(ERR_SC.duplicateReq).json( <ErrorReply> {
92
- error: "This protocol request has already been served."
93
- });
89
+ replyWithErr(ERR_SC.duplicateReq, "This protocol request has already been served.", res);
94
90
  return;
95
91
  }
96
92
 
97
93
  // missing recipient makes a bad request
98
94
  if (!recipient) {
99
- res.status(ERR_SC.malformed).json( <ErrorReply> {
100
- error: "Recipient is either missing in the request, or is malformed"
101
- });
95
+ replyWithErr(ERR_SC.malformed, "Recipient is either missing in the request, or is malformed", res);
102
96
  return;
103
97
  }
104
98
 
@@ -106,29 +100,22 @@ export function startSession(
106
100
  if (sender) {
107
101
  sender = checkAndTransformAddress(sender);
108
102
  if (!sender) {
109
- res.status(ERR_SC.malformed).json( <ErrorReply> {
110
- error: "Sender field is malformed"
111
- });
112
- return;
103
+ return replyWithErr(ERR_SC.malformed, "Sender field is malformed", res);
113
104
  }
114
105
  }
115
106
 
116
107
  // check invitation field
117
108
  if ((invitation !== undefined) && (typeof invitation !== 'string')) {
118
- res.status(ERR_SC.malformed).json( <ErrorReply> {
119
- error: "Inviation field is malformed"
120
- });
121
- return;
109
+ return replyWithErr(ERR_SC.malformed, "Inviation field is malformed", res);
122
110
  }
123
111
 
124
112
  try {
125
113
  if (redirectFunc) {
126
114
  const redirectTo = await redirectFunc(recipient)
127
115
  if (redirectTo) {
128
- res.status(api.SC.redirect).json(
129
- <api.RedirectReply> {
130
- redirect: redirectTo
131
- });
116
+ res.status(api.SC.redirect).json({
117
+ redirect: redirectTo
118
+ } as api.RedirectReply);
132
119
  } else {
133
120
  await serveRequestHere(recipient, sender, invitation, res);
134
121
  }
@@ -137,9 +124,7 @@ export function startSession(
137
124
  }
138
125
  } catch (err) {
139
126
  if (err === recipSC.USER_UNKNOWN) {
140
- res.status(api.SC.unknownRecipient).json( <ErrorReply> {
141
- error: `Recipient ${recipient} is unknown.`
142
- });
127
+ replyWithErr(api.SC.unknownRecipient, `Recipient ${recipient} is unknown.`, res);
143
128
  } else {
144
129
  next(err);
145
130
  }