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.
- package/build/3nstorage/resources/store.d.ts +2 -2
- package/build/3nstorage/resources/store.js +1 -1
- package/build/3nstorage/resources/users.d.ts +3 -3
- package/build/3nstorage/resources/users.js +1 -1
- package/build/3nstorage/resources/utils.d.ts +6 -0
- package/build/3nstorage/resources/utils.js +41 -0
- package/build/3nstorage/routes/owner/archive-obj-version.js +8 -10
- package/build/3nstorage/routes/owner/cancel-trans.js +5 -4
- package/build/3nstorage/routes/owner/delete-obj.js +7 -8
- package/build/3nstorage/routes/owner/get-archived-obj-ver.js +11 -13
- package/build/3nstorage/routes/owner/get-current-obj.js +7 -6
- package/build/3nstorage/routes/owner/get-obj-status.js +8 -8
- package/build/3nstorage/routes/owner/put-current-obj.js +25 -55
- package/build/3nstorage/routes/owner/session-params.js +2 -0
- package/build/asmail/resources/utils.d.ts +6 -0
- package/build/asmail/resources/utils.js +36 -0
- package/build/asmail/routes/delivery/finalize-delivery.js +3 -2
- package/build/asmail/routes/delivery/pre-flight.js +9 -26
- package/build/asmail/routes/delivery/provide-recipient-pubkey.js +4 -3
- package/build/asmail/routes/delivery/put-metadata.js +13 -21
- package/build/asmail/routes/delivery/put-obj.js +19 -25
- package/build/asmail/routes/delivery/restart-session.d.ts +4 -8
- package/build/asmail/routes/delivery/restart-session.js +10 -23
- package/build/asmail/routes/delivery/sender-authorization.js +5 -5
- package/build/asmail/routes/delivery/start-session.js +9 -24
- package/build/asmail/routes/retrieval/get-message-meta.js +4 -7
- package/build/asmail/routes/retrieval/get-message-obj.js +10 -7
- package/build/asmail/routes/retrieval/list-messages.js +4 -4
- package/build/asmail/routes/retrieval/remove-message.js +3 -2
- package/package.json +1 -1
- package/ts-code/3nstorage/resources/store.ts +3 -3
- package/ts-code/3nstorage/resources/users.ts +5 -5
- package/ts-code/3nstorage/resources/utils.ts +45 -0
- package/ts-code/3nstorage/routes/owner/archive-obj-version.ts +8 -10
- package/ts-code/3nstorage/routes/owner/cancel-trans.ts +5 -3
- package/ts-code/3nstorage/routes/owner/delete-obj.ts +9 -13
- package/ts-code/3nstorage/routes/owner/get-archived-obj-ver.ts +13 -15
- package/ts-code/3nstorage/routes/owner/get-current-obj.ts +7 -5
- package/ts-code/3nstorage/routes/owner/get-obj-status.ts +8 -8
- package/ts-code/3nstorage/routes/owner/put-current-obj.ts +33 -60
- package/ts-code/3nstorage/routes/owner/session-params.ts +4 -3
- package/ts-code/asmail/resources/utils.ts +41 -0
- package/ts-code/asmail/routes/delivery/finalize-delivery.ts +3 -2
- package/ts-code/asmail/routes/delivery/pre-flight.ts +15 -29
- package/ts-code/asmail/routes/delivery/provide-recipient-pubkey.ts +4 -4
- package/ts-code/asmail/routes/delivery/put-metadata.ts +21 -30
- package/ts-code/asmail/routes/delivery/put-obj.ts +20 -34
- package/ts-code/asmail/routes/delivery/restart-session.ts +11 -24
- package/ts-code/asmail/routes/delivery/sender-authorization.ts +5 -5
- package/ts-code/asmail/routes/delivery/start-session.ts +17 -32
- package/ts-code/asmail/routes/retrieval/get-message-meta.ts +5 -8
- package/ts-code/asmail/routes/retrieval/get-message-obj.ts +12 -15
- package/ts-code/asmail/routes/retrieval/list-messages.ts +4 -6
- 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
|
-
|
|
39
|
+
replyWithErr(ERR_SC.server, "Recipient disappeared from the system.", res);
|
|
39
40
|
} else if (err === recipSC.MSG_UNKNOWN) {
|
|
40
41
|
session.close();
|
|
41
|
-
|
|
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
|
|
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(
|
|
48
|
+
res.status(api.SC.ok).json({
|
|
48
49
|
maxMsgLength: msgSize
|
|
49
|
-
});
|
|
50
|
+
} as api.Reply);
|
|
50
51
|
} else if (msgSize === 0) {
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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[]):
|
|
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
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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(
|
|
74
|
-
session.params.sender, session.params.invite,
|
|
75
|
-
|
|
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(
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
90
|
+
replyWithErr(ERR_SC.server, "Recipient disappeared from the system.", res);
|
|
100
91
|
session.close();
|
|
101
92
|
} else if (err === recipSC.OBJ_EXIST) {
|
|
102
|
-
|
|
93
|
+
replyWithErr(api.SC.objAlreadyExists, `Object ${objId} already exists.`, res);
|
|
103
94
|
} else if (err === recipSC.MSG_UNKNOWN) {
|
|
104
|
-
|
|
95
|
+
replyWithErr(ERR_SC.server, "Message disappeared from the system.", res);
|
|
105
96
|
session.close();
|
|
106
97
|
} else if (err === recipSC.OBJ_UNKNOWN) {
|
|
107
|
-
|
|
98
|
+
replyWithErr(api.SC.unknownObj, `Object ${objId} is unknown.`, res);
|
|
108
99
|
} else if (err === recipSC.WRONG_OBJ_STATE) {
|
|
109
|
-
|
|
100
|
+
replyWithErr(ERR_SC.malformed, `Object ${objId} is not in a different state.`, res);
|
|
110
101
|
} else if (err === recipSC.OBJ_FILE_INCOMPLETE) {
|
|
111
|
-
|
|
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
|
-
|
|
143
|
+
replyWithErr(ERR_SC.contentLenMissing, "Content-Length header is required with proper number.", res, req);
|
|
153
144
|
} else if (contentLength > maxChunkSize) {
|
|
154
|
-
|
|
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
|
|
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
|
|
30
|
-
* @param
|
|
31
|
-
* a
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
130
|
-
|
|
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
|
-
|
|
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
|
}
|