emailengine-app 2.69.0 → 2.71.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/deploy.yml +6 -3
- package/.github/workflows/release.yaml +2 -0
- package/.github/workflows/test.yml +73 -12
- package/.ncurc.js +3 -3
- package/CHANGELOG.md +37 -0
- package/Gruntfile.js +21 -23
- package/bin/emailengine.js +8 -1
- package/config/default.toml +5 -0
- package/config/test.toml +5 -0
- package/data/google-crawlers.json +1 -1
- package/getswagger.sh +44 -4
- package/gettext-extract.js +163 -0
- package/lib/account.js +104 -72
- package/lib/api-routes/account-routes.js +231 -71
- package/lib/api-routes/blocklist-routes.js +25 -18
- package/lib/api-routes/chat-routes.js +32 -14
- package/lib/api-routes/delivery-test-routes.js +30 -5
- package/lib/api-routes/export-routes.js +27 -2
- package/lib/api-routes/gateway-routes.js +63 -12
- package/lib/api-routes/license-routes.js +18 -4
- package/lib/api-routes/mailbox-routes.js +33 -7
- package/lib/api-routes/message-routes.js +291 -145
- package/lib/api-routes/oauth2-app-routes.js +90 -24
- package/lib/api-routes/outbox-routes.js +16 -4
- package/lib/api-routes/pubsub-routes.js +8 -4
- package/lib/api-routes/route-helpers.js +14 -1
- package/lib/api-routes/settings-routes.js +51 -25
- package/lib/api-routes/stats-routes.js +37 -3
- package/lib/api-routes/submit-routes.js +31 -42
- package/lib/api-routes/template-routes.js +54 -21
- package/lib/api-routes/token-routes.js +67 -67
- package/lib/api-routes/webhook-route-routes.js +37 -8
- package/lib/autodetect-imap-settings.js +0 -2
- package/lib/consts.js +5 -0
- package/lib/document-store.js +22 -1
- package/lib/email-client/base-client.js +31 -8
- package/lib/email-client/gmail-client.js +119 -112
- package/lib/email-client/imap/mailbox.js +2 -2
- package/lib/email-client/imap/subconnection.js +0 -1
- package/lib/email-client/imap/sync-operations.js +1 -1
- package/lib/email-client/imap-client.js +36 -17
- package/lib/email-client/notification-handler.js +3 -6
- package/lib/email-client/outlook-client.js +49 -62
- package/lib/export.js +49 -1
- package/lib/feature-flags.js +8 -2
- package/lib/gateway.js +4 -9
- package/lib/get-raw-email.js +5 -5
- package/lib/imapproxy/imap-core/lib/imap-connection.js +0 -1
- package/lib/license-beacon.js +367 -0
- package/lib/logger.js +35 -22
- package/lib/metrics-collector.js +0 -2
- package/lib/oauth2-apps.js +13 -4
- package/lib/outbox.js +24 -40
- package/lib/redis-operations.js +1 -1
- package/lib/routes-ui.js +2 -1
- package/lib/schemas.js +403 -83
- package/lib/sentry.js +139 -0
- package/lib/settings.js +9 -3
- package/lib/stream-encrypt.js +1 -1
- package/lib/templates.js +1 -1
- package/lib/tokens.js +5 -3
- package/lib/tools.js +28 -6
- package/lib/ui-routes/account-routes.js +7 -4
- package/lib/ui-routes/admin-config-routes.js +20 -6
- package/lib/ui-routes/document-store-routes.js +7 -1
- package/lib/ui-routes/oauth-config-routes.js +0 -2
- package/lib/ui-routes/route-helpers.js +0 -2
- package/lib/ui-routes/unsubscribe-routes.js +0 -2
- package/lib/webhooks.js +8 -4
- package/package.json +23 -19
- package/sbom.json +1 -1
- package/server.js +38 -31
- package/static/licenses.html +171 -391
- package/translations/de.mo +0 -0
- package/translations/de.po +154 -142
- package/translations/et.mo +0 -0
- package/translations/et.po +129 -131
- package/translations/fr.mo +0 -0
- package/translations/fr.po +133 -136
- package/translations/ja.mo +0 -0
- package/translations/ja.po +126 -129
- package/translations/messages.pot +107 -107
- package/translations/nl.mo +0 -0
- package/translations/nl.po +128 -130
- package/translations/pl.mo +0 -0
- package/translations/pl.po +125 -128
- package/update-info.sh +19 -1
- package/views/config/logging.hbs +48 -0
- package/views/dashboard.hbs +22 -0
- package/workers/api.js +33 -37
- package/workers/documents.js +2 -22
- package/workers/export.js +73 -92
- package/workers/imap-proxy.js +3 -23
- package/workers/imap.js +2 -22
- package/workers/smtp.js +2 -22
- package/workers/submit.js +6 -24
- package/workers/webhooks.js +2 -22
|
@@ -46,7 +46,7 @@ const SYSTEM_LABELS = {
|
|
|
46
46
|
|
|
47
47
|
// User-friendly names for system labels
|
|
48
48
|
const SYSTEM_NAMES = {
|
|
49
|
-
SENT: 'Sent
|
|
49
|
+
SENT: 'Sent',
|
|
50
50
|
INBOX: 'Inbox',
|
|
51
51
|
TRASH: 'Trash',
|
|
52
52
|
DRAFT: 'Drafts',
|
|
@@ -63,6 +63,12 @@ for (let label of Object.keys(SYSTEM_LABELS)) {
|
|
|
63
63
|
SYSTEM_LABELS_REV[SYSTEM_LABELS[label]] = label;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// Convert an IMAP special-use label (e.g. '\Important') to its Gmail system label ID, pass every
|
|
67
|
+
// other value through as-is
|
|
68
|
+
function toGmailLabelId(label) {
|
|
69
|
+
return SYSTEM_LABELS_REV.hasOwnProperty(label) ? SYSTEM_LABELS_REV[label] : label;
|
|
70
|
+
}
|
|
71
|
+
|
|
66
72
|
// Timing constants for Gmail Pub/Sub watch
|
|
67
73
|
const RENEW_WATCH_TTL = 60 * 60 * 1000; // 1h - how often to check if watch needs renewal
|
|
68
74
|
const MIN_WATCH_TTL = 24 * 3600 * 1000; // 1day - minimum time before renewing watch
|
|
@@ -753,7 +759,7 @@ class GmailClient extends BaseClient {
|
|
|
753
759
|
// NB! Might throw if using unsupported search terms
|
|
754
760
|
const preparedQuery = this.prepareQuery(query.search);
|
|
755
761
|
if (preparedQuery) {
|
|
756
|
-
requestQuery.q =
|
|
762
|
+
requestQuery.q = preparedQuery;
|
|
757
763
|
}
|
|
758
764
|
}
|
|
759
765
|
|
|
@@ -875,16 +881,13 @@ class GmailClient extends BaseClient {
|
|
|
875
881
|
|
|
876
882
|
let sourceLabel = path && path !== '\\All' ? await this.getLabel(path) : null;
|
|
877
883
|
if (path && path !== '\\All' && !sourceLabel) {
|
|
878
|
-
|
|
879
|
-
error.info = {
|
|
880
|
-
response: `Mailbox doesn't exist: ${path}`
|
|
881
|
-
};
|
|
882
|
-
error.code = 'NotFound';
|
|
883
|
-
error.statusCode = 404;
|
|
884
|
-
throw error;
|
|
884
|
+
throw this.unknownPathError(path);
|
|
885
885
|
}
|
|
886
886
|
|
|
887
|
-
// Add TRASH label and remove source label
|
|
887
|
+
// Add TRASH label and remove source label. When deleting from the Trash folder itself both
|
|
888
|
+
// labels are TRASH - updateMessages resolves the add/remove conflict in favor of the add,
|
|
889
|
+
// keeping the request valid (Gmail API accounts only hold the gmail.modify scope, so a
|
|
890
|
+
// permanent batchDelete is not possible)
|
|
888
891
|
let labelsUpdate = { add: 'TRASH' };
|
|
889
892
|
if (sourceLabel) {
|
|
890
893
|
labelsUpdate.delete = sourceLabel.id;
|
|
@@ -911,51 +914,28 @@ class GmailClient extends BaseClient {
|
|
|
911
914
|
await this.prepare();
|
|
912
915
|
updates = updates || {};
|
|
913
916
|
|
|
914
|
-
let addLabelIds = new Set();
|
|
915
|
-
let removeLabelIds = new Set();
|
|
916
|
-
|
|
917
917
|
// Convert IMAP flags to Gmail labels
|
|
918
|
-
|
|
919
|
-
let labelUpdates = [];
|
|
920
|
-
|
|
921
|
-
for (let flag of [].concat(updates.flags.add || [])) {
|
|
922
|
-
labelUpdates.push(this.flagToLabel(flag));
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
for (let flag of [].concat(updates.flags.delete || [])) {
|
|
926
|
-
labelUpdates.push(this.flagToLabel(flag, true));
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
labelUpdates
|
|
930
|
-
.filter(label => label)
|
|
931
|
-
.forEach(label => {
|
|
932
|
-
if (label.add) {
|
|
933
|
-
addLabelIds.add(label.add);
|
|
934
|
-
}
|
|
935
|
-
if (label.remove) {
|
|
936
|
-
removeLabelIds.add(label.remove);
|
|
937
|
-
}
|
|
938
|
-
});
|
|
939
|
-
}
|
|
918
|
+
let { addLabelIds, removeLabelIds } = this.flagsToLabelIds(updates.flags);
|
|
940
919
|
|
|
941
920
|
// Process direct label updates
|
|
942
921
|
if (updates.labels) {
|
|
922
|
+
this.assertLabelSetSupported(updates.labels);
|
|
923
|
+
|
|
943
924
|
for (let label of [].concat(updates.labels.add || [])) {
|
|
944
|
-
|
|
945
|
-
if (SYSTEM_LABELS_REV.hasOwnProperty(label)) {
|
|
946
|
-
label = SYSTEM_LABELS_REV[label];
|
|
947
|
-
}
|
|
948
|
-
addLabelIds.add(label);
|
|
925
|
+
addLabelIds.add(toGmailLabelId(label));
|
|
949
926
|
}
|
|
950
927
|
|
|
951
928
|
for (let label of [].concat(updates.labels.delete || [])) {
|
|
952
|
-
|
|
953
|
-
label = SYSTEM_LABELS_REV[label];
|
|
954
|
-
}
|
|
955
|
-
removeLabelIds.add(label);
|
|
929
|
+
removeLabelIds.add(toGmailLabelId(label));
|
|
956
930
|
}
|
|
957
931
|
}
|
|
958
932
|
|
|
933
|
+
// Gmail rejects modify calls where the same label is both added and removed (deleting or
|
|
934
|
+
// moving a message within its current folder re-adds the source label) - the add wins
|
|
935
|
+
for (let label of addLabelIds) {
|
|
936
|
+
removeLabelIds.delete(label);
|
|
937
|
+
}
|
|
938
|
+
|
|
959
939
|
if (!addLabelIds.size && !removeLabelIds.size) {
|
|
960
940
|
return updates;
|
|
961
941
|
}
|
|
@@ -1024,6 +1004,17 @@ class GmailClient extends BaseClient {
|
|
|
1024
1004
|
await this.prepare();
|
|
1025
1005
|
updates = updates || {};
|
|
1026
1006
|
|
|
1007
|
+
// Reject unsupported operations before any API calls are made
|
|
1008
|
+
if (updates.labels) {
|
|
1009
|
+
this.assertLabelSetSupported(updates.labels);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
path = [].concat(path || []).join('/');
|
|
1013
|
+
|
|
1014
|
+
if (path && path !== '\\All' && !(await this.getLabel(path))) {
|
|
1015
|
+
throw this.unknownPathError(path);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1027
1018
|
// Step 1. Resolve matching messages
|
|
1028
1019
|
let messages = [];
|
|
1029
1020
|
let cursor;
|
|
@@ -1043,8 +1034,8 @@ class GmailClient extends BaseClient {
|
|
|
1043
1034
|
{ metadataOnly: true }
|
|
1044
1035
|
);
|
|
1045
1036
|
|
|
1046
|
-
if (messageListResult
|
|
1047
|
-
messages = messages.concat(messageListResult
|
|
1037
|
+
if (messageListResult.messages) {
|
|
1038
|
+
messages = messages.concat(messageListResult.messages);
|
|
1048
1039
|
if (messages.length >= maxMessages) {
|
|
1049
1040
|
messages = messages.slice(0, maxMessages);
|
|
1050
1041
|
break;
|
|
@@ -1064,43 +1055,25 @@ class GmailClient extends BaseClient {
|
|
|
1064
1055
|
return updates;
|
|
1065
1056
|
}
|
|
1066
1057
|
|
|
1067
|
-
let addLabelIds = new Set();
|
|
1068
|
-
let removeLabelIds = new Set();
|
|
1069
|
-
|
|
1070
1058
|
// Convert flags to label operations
|
|
1071
|
-
|
|
1072
|
-
let labelUpdates = [];
|
|
1073
|
-
|
|
1074
|
-
for (let flag of [].concat(updates.flags.add || [])) {
|
|
1075
|
-
labelUpdates.push(this.flagToLabel(flag));
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
for (let flag of [].concat(updates.flags.delete || [])) {
|
|
1079
|
-
labelUpdates.push(this.flagToLabel(flag, true));
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
labelUpdates
|
|
1083
|
-
.filter(label => label)
|
|
1084
|
-
.forEach(label => {
|
|
1085
|
-
if (label.add) {
|
|
1086
|
-
addLabelIds.add(label.add);
|
|
1087
|
-
}
|
|
1088
|
-
if (label.remove) {
|
|
1089
|
-
removeLabelIds.add(label.remove);
|
|
1090
|
-
}
|
|
1091
|
-
});
|
|
1092
|
-
}
|
|
1059
|
+
let { addLabelIds, removeLabelIds } = this.flagsToLabelIds(updates.flags);
|
|
1093
1060
|
|
|
1094
1061
|
if (updates.labels) {
|
|
1095
1062
|
for (let label of [].concat(updates.labels.add || [])) {
|
|
1096
|
-
addLabelIds.add(label);
|
|
1063
|
+
addLabelIds.add(toGmailLabelId(label));
|
|
1097
1064
|
}
|
|
1098
1065
|
|
|
1099
1066
|
for (let label of [].concat(updates.labels.delete || [])) {
|
|
1100
|
-
removeLabelIds.add(label);
|
|
1067
|
+
removeLabelIds.add(toGmailLabelId(label));
|
|
1101
1068
|
}
|
|
1102
1069
|
}
|
|
1103
1070
|
|
|
1071
|
+
// Gmail rejects batchModify calls where the same label is both added and removed (deleting
|
|
1072
|
+
// or moving messages within their current folder re-adds the source label) - the add wins
|
|
1073
|
+
for (let label of addLabelIds) {
|
|
1074
|
+
removeLabelIds.delete(label);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1104
1077
|
if (!addLabelIds.size && !removeLabelIds.size) {
|
|
1105
1078
|
return { flags: {}, labels: {} };
|
|
1106
1079
|
}
|
|
@@ -1141,13 +1114,7 @@ class GmailClient extends BaseClient {
|
|
|
1141
1114
|
|
|
1142
1115
|
let label = await this.getLabel(path);
|
|
1143
1116
|
if (!label) {
|
|
1144
|
-
|
|
1145
|
-
error.info = {
|
|
1146
|
-
response: `Mailbox doesn't exist: ${path}`
|
|
1147
|
-
};
|
|
1148
|
-
error.code = 'NotFound';
|
|
1149
|
-
error.statusCode = 404;
|
|
1150
|
-
throw error;
|
|
1117
|
+
throw this.unknownPathError(path);
|
|
1151
1118
|
}
|
|
1152
1119
|
let labelsUpdate = { add: [label.id] };
|
|
1153
1120
|
|
|
@@ -1155,13 +1122,7 @@ class GmailClient extends BaseClient {
|
|
|
1155
1122
|
|
|
1156
1123
|
let sourceLabel = sourcePath ? await this.getLabel(sourcePath) : null;
|
|
1157
1124
|
if (sourcePath && !sourceLabel) {
|
|
1158
|
-
|
|
1159
|
-
error.info = {
|
|
1160
|
-
response: `Mailbox doesn't exist: ${sourcePath}`
|
|
1161
|
-
};
|
|
1162
|
-
error.code = 'NotFound';
|
|
1163
|
-
error.statusCode = 404;
|
|
1164
|
-
throw error;
|
|
1125
|
+
throw this.unknownPathError(sourcePath);
|
|
1165
1126
|
}
|
|
1166
1127
|
|
|
1167
1128
|
if (sourceLabel) {
|
|
@@ -1190,24 +1151,12 @@ class GmailClient extends BaseClient {
|
|
|
1190
1151
|
|
|
1191
1152
|
let targetLabel = await this.getLabel(path);
|
|
1192
1153
|
if (!targetLabel) {
|
|
1193
|
-
|
|
1194
|
-
error.info = {
|
|
1195
|
-
response: `Mailbox doesn't exist: ${path}`
|
|
1196
|
-
};
|
|
1197
|
-
error.code = 'NotFound';
|
|
1198
|
-
error.statusCode = 404;
|
|
1199
|
-
throw error;
|
|
1154
|
+
throw this.unknownPathError(path);
|
|
1200
1155
|
}
|
|
1201
1156
|
|
|
1202
1157
|
let sourceLabel = source ? await this.getLabel(source) : null;
|
|
1203
1158
|
if (source && !sourceLabel) {
|
|
1204
|
-
|
|
1205
|
-
error.info = {
|
|
1206
|
-
response: `Mailbox doesn't exist: ${source}`
|
|
1207
|
-
};
|
|
1208
|
-
error.code = 'NotFound';
|
|
1209
|
-
error.statusCode = 404;
|
|
1210
|
-
throw error;
|
|
1159
|
+
throw this.unknownPathError(source);
|
|
1211
1160
|
}
|
|
1212
1161
|
|
|
1213
1162
|
let labelsUpdate = { add: targetLabel.id };
|
|
@@ -1251,7 +1200,7 @@ class GmailClient extends BaseClient {
|
|
|
1251
1200
|
|
|
1252
1201
|
const contentResponse = {
|
|
1253
1202
|
headers: {
|
|
1254
|
-
'content-type': attachmentData.
|
|
1203
|
+
'content-type': attachmentData.contentType || 'application/octet-stream',
|
|
1255
1204
|
'content-disposition': 'attachment' + filenameParam
|
|
1256
1205
|
},
|
|
1257
1206
|
contentType: attachmentData.contentType,
|
|
@@ -1421,7 +1370,7 @@ class GmailClient extends BaseClient {
|
|
|
1421
1370
|
|
|
1422
1371
|
// Map part IDs to content types
|
|
1423
1372
|
textParts[0].forEach(p => {
|
|
1424
|
-
bodyParts.set(p, '
|
|
1373
|
+
bodyParts.set(p, 'plain');
|
|
1425
1374
|
});
|
|
1426
1375
|
|
|
1427
1376
|
textParts[1].forEach(p => {
|
|
@@ -1481,13 +1430,7 @@ class GmailClient extends BaseClient {
|
|
|
1481
1430
|
|
|
1482
1431
|
let targetLabel = await this.getLabel(path);
|
|
1483
1432
|
if (!targetLabel) {
|
|
1484
|
-
|
|
1485
|
-
error.info = {
|
|
1486
|
-
response: `Mailbox doesn't exist: ${path}`
|
|
1487
|
-
};
|
|
1488
|
-
error.code = 'NotFound';
|
|
1489
|
-
error.statusCode = 404;
|
|
1490
|
-
throw error;
|
|
1433
|
+
throw this.unknownPathError(path);
|
|
1491
1434
|
}
|
|
1492
1435
|
|
|
1493
1436
|
// Generate raw message
|
|
@@ -2400,7 +2343,7 @@ class GmailClient extends BaseClient {
|
|
|
2400
2343
|
flags.push('\\Flagged');
|
|
2401
2344
|
}
|
|
2402
2345
|
|
|
2403
|
-
if (messageData.labelIds?.includes('
|
|
2346
|
+
if (messageData.labelIds?.includes('DRAFT')) {
|
|
2404
2347
|
flags.push('\\Draft');
|
|
2405
2348
|
}
|
|
2406
2349
|
|
|
@@ -2609,6 +2552,21 @@ class GmailClient extends BaseClient {
|
|
|
2609
2552
|
return term;
|
|
2610
2553
|
}
|
|
2611
2554
|
|
|
2555
|
+
/**
|
|
2556
|
+
* Builds the canonical 404 error for a mailbox path that does not match any Gmail label
|
|
2557
|
+
* @param {string} path - Requested mailbox path
|
|
2558
|
+
* @returns {Error} Error object with code and statusCode set
|
|
2559
|
+
*/
|
|
2560
|
+
unknownPathError(path) {
|
|
2561
|
+
let error = new Error('Unknown path');
|
|
2562
|
+
error.info = {
|
|
2563
|
+
response: `Mailbox doesn't exist: ${path}`
|
|
2564
|
+
};
|
|
2565
|
+
error.code = 'NotFound';
|
|
2566
|
+
error.statusCode = 404;
|
|
2567
|
+
return error;
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2612
2570
|
/**
|
|
2613
2571
|
* Converts IMAP flags to Gmail label operations
|
|
2614
2572
|
* @param {string} flag - IMAP flag
|
|
@@ -2625,6 +2583,55 @@ class GmailClient extends BaseClient {
|
|
|
2625
2583
|
}
|
|
2626
2584
|
}
|
|
2627
2585
|
|
|
2586
|
+
/**
|
|
2587
|
+
* Converts an IMAP-style flag update request (add/delete/set) into Gmail label ID sets.
|
|
2588
|
+
* Set precedence is handled by BaseClient#normalizeFlagUpdates.
|
|
2589
|
+
* @param {Object} [flagUpdates] - Flag update request ({ add, delete, set })
|
|
2590
|
+
* @returns {Object} Label ID sets ({ addLabelIds: Set, removeLabelIds: Set })
|
|
2591
|
+
*/
|
|
2592
|
+
flagsToLabelIds(flagUpdates) {
|
|
2593
|
+
let addLabelIds = new Set();
|
|
2594
|
+
let removeLabelIds = new Set();
|
|
2595
|
+
|
|
2596
|
+
let normalized = this.normalizeFlagUpdates(flagUpdates, ['\\Seen', '\\Flagged']);
|
|
2597
|
+
|
|
2598
|
+
let labelUpdates = [];
|
|
2599
|
+
|
|
2600
|
+
for (let flag of normalized.add) {
|
|
2601
|
+
labelUpdates.push(this.flagToLabel(flag));
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
for (let flag of normalized.delete) {
|
|
2605
|
+
labelUpdates.push(this.flagToLabel(flag, true));
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
labelUpdates
|
|
2609
|
+
.filter(label => label)
|
|
2610
|
+
.forEach(label => {
|
|
2611
|
+
if (label.add) {
|
|
2612
|
+
addLabelIds.add(label.add);
|
|
2613
|
+
}
|
|
2614
|
+
if (label.remove) {
|
|
2615
|
+
removeLabelIds.add(label.remove);
|
|
2616
|
+
}
|
|
2617
|
+
});
|
|
2618
|
+
|
|
2619
|
+
return { addLabelIds, removeLabelIds };
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
/**
|
|
2623
|
+
* Throws if the update request uses `labels.set`, which Gmail API accounts do not support
|
|
2624
|
+
* @param {Object} labels - Label update request
|
|
2625
|
+
*/
|
|
2626
|
+
assertLabelSetSupported(labels) {
|
|
2627
|
+
if (labels.set) {
|
|
2628
|
+
let error = new Error('Replacing the full label set is not supported for Gmail API accounts');
|
|
2629
|
+
error.code = 'UnsupportedOperation';
|
|
2630
|
+
error.statusCode = 400;
|
|
2631
|
+
throw error;
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2628
2635
|
/**
|
|
2629
2636
|
* Converts IMAP SEARCH query to Gmail API query
|
|
2630
2637
|
* @param {Object} search - IMAP search object
|
|
@@ -2805,7 +2812,7 @@ class GmailClient extends BaseClient {
|
|
|
2805
2812
|
case 'STARRED':
|
|
2806
2813
|
changes.flags[addedProp].push('\\Flagged');
|
|
2807
2814
|
break;
|
|
2808
|
-
case '
|
|
2815
|
+
case 'DRAFT':
|
|
2809
2816
|
changes.flags[addedProp].push('\\Draft');
|
|
2810
2817
|
break;
|
|
2811
2818
|
default:
|
|
@@ -15,7 +15,7 @@ const ical = require('ical.js');
|
|
|
15
15
|
const addressparser = require('nodemailer/lib/addressparser');
|
|
16
16
|
const { llmPreProcess } = require('../../llm-pre-process');
|
|
17
17
|
|
|
18
|
-
const { getESClient } = require('../../document-store');
|
|
18
|
+
const { getESClient, isDocumentStoreEnabled } = require('../../document-store');
|
|
19
19
|
|
|
20
20
|
const {
|
|
21
21
|
MESSAGE_NEW_NOTIFY,
|
|
@@ -1958,7 +1958,7 @@ class Mailbox {
|
|
|
1958
1958
|
async publishSyncedEvents(storedStatus) {
|
|
1959
1959
|
let messageFetchOptions = {};
|
|
1960
1960
|
|
|
1961
|
-
let documentStoreEnabled = await
|
|
1961
|
+
let documentStoreEnabled = await isDocumentStoreEnabled();
|
|
1962
1962
|
|
|
1963
1963
|
// Configure text fetching
|
|
1964
1964
|
let notifyText = await settings.get('notifyText');
|
|
@@ -909,7 +909,7 @@ class SyncOperations {
|
|
|
909
909
|
currentPath: currentMailbox ? currentMailbox.path : 'none',
|
|
910
910
|
loopId
|
|
911
911
|
});
|
|
912
|
-
throw new Error('Mailbox changed during sync operation');
|
|
912
|
+
throw new Error('Mailbox changed during sync operation', { cause: err });
|
|
913
913
|
}
|
|
914
914
|
|
|
915
915
|
// Refresh mailbox status in case it changed
|
|
@@ -1858,11 +1858,13 @@ class IMAPClient extends BaseClient {
|
|
|
1858
1858
|
|
|
1859
1859
|
this.checkIMAPConnection(connectionOptions);
|
|
1860
1860
|
|
|
1861
|
-
|
|
1861
|
+
path = await this.resolvePathAlias(path);
|
|
1862
|
+
|
|
1863
|
+
if (!this.mailboxes.has(path)) {
|
|
1862
1864
|
return false; //?
|
|
1863
1865
|
}
|
|
1864
1866
|
|
|
1865
|
-
let mailbox = this.mailboxes.get(
|
|
1867
|
+
let mailbox = this.mailboxes.get(path);
|
|
1866
1868
|
|
|
1867
1869
|
return await mailbox.updateMessages(search, updates, connectionOptions);
|
|
1868
1870
|
}
|
|
@@ -1924,11 +1926,13 @@ class IMAPClient extends BaseClient {
|
|
|
1924
1926
|
|
|
1925
1927
|
this.checkIMAPConnection(connectionOptions);
|
|
1926
1928
|
|
|
1927
|
-
|
|
1929
|
+
source = await this.resolvePathAlias(source);
|
|
1930
|
+
|
|
1931
|
+
if (!this.mailboxes.has(source)) {
|
|
1928
1932
|
return false; //?
|
|
1929
1933
|
}
|
|
1930
1934
|
|
|
1931
|
-
let mailbox = this.mailboxes.get(
|
|
1935
|
+
let mailbox = this.mailboxes.get(source);
|
|
1932
1936
|
|
|
1933
1937
|
let res = await mailbox.moveMessages(search, target, connectionOptions);
|
|
1934
1938
|
|
|
@@ -1981,11 +1985,13 @@ class IMAPClient extends BaseClient {
|
|
|
1981
1985
|
|
|
1982
1986
|
this.checkIMAPConnection(connectionOptions);
|
|
1983
1987
|
|
|
1984
|
-
|
|
1988
|
+
path = await this.resolvePathAlias(path);
|
|
1989
|
+
|
|
1990
|
+
if (!this.mailboxes.has(path)) {
|
|
1985
1991
|
return false; //?
|
|
1986
1992
|
}
|
|
1987
1993
|
|
|
1988
|
-
let mailbox = this.mailboxes.get(
|
|
1994
|
+
let mailbox = this.mailboxes.get(path);
|
|
1989
1995
|
let res = await mailbox.deleteMessages(search, force, connectionOptions);
|
|
1990
1996
|
|
|
1991
1997
|
// force sync target mailbox if messages were moved to trash
|
|
@@ -2112,15 +2118,8 @@ class IMAPClient extends BaseClient {
|
|
|
2112
2118
|
|
|
2113
2119
|
this.checkIMAPConnection(connectionOptions);
|
|
2114
2120
|
|
|
2115
|
-
let path = normalizePath(options.path);
|
|
2116
|
-
|
|
2117
2121
|
// Handle special-use folder aliases
|
|
2118
|
-
|
|
2119
|
-
let resolvedPath = await this.getSpecialUseMailbox(path);
|
|
2120
|
-
if (resolvedPath) {
|
|
2121
|
-
path = resolvedPath.path;
|
|
2122
|
-
}
|
|
2123
|
-
}
|
|
2122
|
+
let path = await this.resolvePathAlias(options.path);
|
|
2124
2123
|
|
|
2125
2124
|
if (!this.mailboxes.has(path)) {
|
|
2126
2125
|
return false; //?
|
|
@@ -2294,7 +2293,7 @@ class IMAPClient extends BaseClient {
|
|
|
2294
2293
|
throw error;
|
|
2295
2294
|
} else if (err.responseStatus === 'NO') {
|
|
2296
2295
|
return {
|
|
2297
|
-
path,
|
|
2296
|
+
path: [].concat(path || []).join('/'),
|
|
2298
2297
|
created: false
|
|
2299
2298
|
};
|
|
2300
2299
|
} else {
|
|
@@ -2357,7 +2356,7 @@ class IMAPClient extends BaseClient {
|
|
|
2357
2356
|
error.info = {
|
|
2358
2357
|
response: err.response && typeof err.response === 'string' && err.response.replace(/^[^\s]*\s*/, '')
|
|
2359
2358
|
};
|
|
2360
|
-
error.code = err.serverResponseCode;
|
|
2359
|
+
error.code = err.serverResponseCode || 'RenameFailed';
|
|
2361
2360
|
error.statusCode = 400;
|
|
2362
2361
|
throw error;
|
|
2363
2362
|
} else {
|
|
@@ -2413,6 +2412,26 @@ class IMAPClient extends BaseClient {
|
|
|
2413
2412
|
.find(entry => entry.specialUse === specialUse);
|
|
2414
2413
|
}
|
|
2415
2414
|
|
|
2415
|
+
/**
|
|
2416
|
+
* Resolves a special-use folder alias (e.g. "\Sent") to the real mailbox path.
|
|
2417
|
+
* Returns the normalized input path unchanged if it is not an alias or if no
|
|
2418
|
+
* mailbox with the requested special-use flag exists.
|
|
2419
|
+
* @param {string} path - Mailbox path or special-use alias
|
|
2420
|
+
* @returns {string} Normalized mailbox path
|
|
2421
|
+
*/
|
|
2422
|
+
async resolvePathAlias(path) {
|
|
2423
|
+
path = normalizePath(path);
|
|
2424
|
+
|
|
2425
|
+
if (['\\Junk', '\\Sent', '\\Trash', '\\Inbox', '\\Drafts', '\\All'].includes(path)) {
|
|
2426
|
+
let resolved = await this.getSpecialUseMailbox(path);
|
|
2427
|
+
if (resolved) {
|
|
2428
|
+
path = normalizePath(resolved.path);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
return path;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2416
2435
|
/**
|
|
2417
2436
|
* Uploads a message to a mailbox
|
|
2418
2437
|
* @param {Object} data - Message data including path, flags, content
|
|
@@ -2734,7 +2753,7 @@ class IMAPClient extends BaseClient {
|
|
|
2734
2753
|
const emptyResponse = { signatures: [], signaturesSupported: false };
|
|
2735
2754
|
let accountData = await this.accountObject.loadAccountData();
|
|
2736
2755
|
|
|
2737
|
-
if (!accountData.oauth2
|
|
2756
|
+
if (!accountData.oauth2?.provider) {
|
|
2738
2757
|
// Not an OAuth2 account
|
|
2739
2758
|
return emptyResponse;
|
|
2740
2759
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { parentPort } = require('worker_threads');
|
|
4
4
|
const logger = require('../logger');
|
|
5
5
|
const { webhooks: Webhooks } = require('../webhooks');
|
|
6
|
-
const { getESClient } = require('../document-store');
|
|
6
|
+
const { getESClient, isDocumentStoreEnabled } = require('../document-store');
|
|
7
7
|
const { getThread } = require('../threads');
|
|
8
8
|
const settings = require('../settings');
|
|
9
9
|
|
|
@@ -126,7 +126,7 @@ class NotificationHandler {
|
|
|
126
126
|
return false;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
return await
|
|
129
|
+
return await isDocumentStoreEnabled();
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
/**
|
|
@@ -157,10 +157,7 @@ class NotificationHandler {
|
|
|
157
157
|
}
|
|
158
158
|
} catch (err) {
|
|
159
159
|
if (this.logger.notifyError) {
|
|
160
|
-
this.logger.notifyError(err,
|
|
161
|
-
event.setUser(this.account);
|
|
162
|
-
event.addMetadata('ee', { index });
|
|
163
|
-
});
|
|
160
|
+
this.logger.notifyError(err, { user: this.account, meta: { index } });
|
|
164
161
|
}
|
|
165
162
|
this.logger.error({
|
|
166
163
|
msg: 'Failed to resolve thread',
|