Skip to content

Commit f448d39

Browse files
committed
fix: Special envelope handling for LarkSuite accounts
1 parent f7f9593 commit f448d39

File tree

2 files changed

+65
-5
lines changed

2 files changed

+65
-5
lines changed

lib/email-client/imap-client.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ class IMAPClient extends BaseClient {
543543
// User might have disabled All Mail folder access and in that case we should treat it as a regular mailbox
544544
this.isGmail = imapClient.capabilities.has('X-GM-EXT-1') && listing.some(entry => entry.specialUse === '\\All');
545545
this.isOutlook = /\boffice365\.com$/i.test(imapClient.host); // || /The Microsoft Exchange IMAP4 service is ready/.test(imapClient.greeting);
546+
this.isLarkSuite = /\blarksuite\.com$/i.test(imapClient.host);
546547

547548
const accountPaths = [].concat(accountData.path || '*');
548549
if (!accountPaths.length) {

lib/email-client/imap/mailbox.js

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const appendList = require('../../append-list');
2323
const { mimeHtml } = require('@postalsys/email-text-tools');
2424
const simpleParser = require('mailparser').simpleParser;
2525
const ical = require('ical.js');
26+
const addressparser = require('nodemailer/lib/addressparser');
2627
const { llmPreProcess } = require('../../llm-pre-process');
2728

2829
const { getESClient } = require('../../document-store');
@@ -84,6 +85,8 @@ class Mailbox {
8485
this.isGmail = connection.isGmail;
8586
this.isAllMail = this.isGmail && this.listingEntry.specialUse === '\\All';
8687

88+
this.isLarkSuite = connection.isLarkSuite;
89+
8790
this.selected = false;
8891
// does the mailbox open happen before or after initial syncing
8992
this.previouslyConnected = false;
@@ -1343,7 +1346,7 @@ class Mailbox {
13431346
isDraft = true;
13441347
}
13451348

1346-
// do not expose the \Recent flag as it is session specific
1349+
// Do not expose the \Recent flag as it is session specific
13471350
if (messageData.flags && messageData.flags.has('\\Recent')) {
13481351
messageData.flags.delete('\\Recent');
13491352
}
@@ -1352,6 +1355,41 @@ class Mailbox {
13521355
isDraft = true;
13531356
}
13541357

1358+
let headers;
1359+
1360+
// This section is needed for Lark Mail as some address fields might be missing
1361+
// from the ENVELOPE section, so fall back to the header instead.
1362+
// Normally, these headers are not fetched from the server and only ENVELOPE is used
1363+
let parsedAddresses = {};
1364+
if (messageData.headers) {
1365+
headers = libmime.decodeHeaders(messageData.headers.toString().trim());
1366+
for (let key of ['from', 'to', 'cc', 'bcc']) {
1367+
if (headers[key]?.length) {
1368+
try {
1369+
const addressList = addressparser(headers[key])
1370+
.filter(address => !!address.address)
1371+
.map(address => {
1372+
let name = address.name;
1373+
try {
1374+
name = libmime.decodeWords(name);
1375+
} catch (err) {
1376+
// ignore
1377+
}
1378+
return {
1379+
name,
1380+
address: address.address
1381+
};
1382+
});
1383+
if (addressList?.length) {
1384+
parsedAddresses[key] = addressList;
1385+
}
1386+
} catch (err) {
1387+
// just ignore
1388+
}
1389+
}
1390+
}
1391+
}
1392+
13551393
const result = {
13561394
id: packedUid,
13571395
uid: messageData.uid,
@@ -1374,21 +1412,21 @@ class Mailbox {
13741412

13751413
size: messageData.size || undefined,
13761414
subject: envelope.subject || undefined,
1377-
from: envelope.from && envelope.from[0] ? envelope.from[0] : undefined,
1415+
from: envelope.from?.[0] ? envelope.from[0] : parsedAddresses.from?.[0] || undefined,
13781416

13791417
replyTo: envelope.replyTo && envelope.replyTo.length ? envelope.replyTo : undefined,
13801418
sender: extended && envelope.sender && envelope.sender[0] ? envelope.sender[0] : undefined,
13811419

1382-
to: envelope.to && envelope.to.length ? envelope.to : undefined,
1383-
cc: envelope.cc && envelope.cc.length ? envelope.cc : undefined,
1420+
to: envelope.to?.length ? envelope.to : parsedAddresses.to || undefined,
1421+
cc: envelope.cc?.length ? envelope.cc : parsedAddresses.cc || undefined,
13841422

13851423
bcc: extended && envelope.bcc && envelope.bcc.length ? envelope.bcc : undefined,
13861424

13871425
attachments: attachments && attachments.length ? attachments : undefined,
13881426
messageId: (envelope.messageId && envelope.messageId.toString().trim()) || undefined,
13891427
inReplyTo: envelope.inReplyTo || undefined,
13901428

1391-
headers: (extended && messageData.headers && libmime.decodeHeaders(messageData.headers.toString().trim())) || undefined,
1429+
headers: (extended && headers) || undefined,
13921430
text: textId
13931431
? {
13941432
id: textId,
@@ -2072,6 +2110,13 @@ class Mailbox {
20722110

20732111
fetchHeaders.add('content-type');
20742112

2113+
if (this.isLarkSuite) {
2114+
// add address headers as a fallback to use if ENVELOPE does not include a specific address field (no idea why it happens)
2115+
fetchHeaders.add('from');
2116+
fetchHeaders.add('to');
2117+
fetchHeaders.add('cc');
2118+
}
2119+
20752120
messageFetchOptions.fetchHeaders = Array.from(fetchHeaders);
20762121
}
20772122

@@ -2904,6 +2949,20 @@ class Mailbox {
29042949
labels: true
29052950
};
29062951

2952+
if (this.isLarkSuite) {
2953+
if (!fields.headers && fields.headers !== true) {
2954+
fields.headers = [];
2955+
}
2956+
if (Array.isArray(fields.headers)) {
2957+
// ensure that the response includes header fiels because Lark Mail ENVELOPE response is unreliable
2958+
for (let key of ['from', 'to', 'cc']) {
2959+
if (!fields.headers.includes(key)) {
2960+
fields.headers.push(key);
2961+
}
2962+
}
2963+
}
2964+
}
2965+
29072966
for await (let messageData of connectionClient.fetch(range, fields, opts)) {
29082967
if (!messageData || !messageData.uid) {
29092968
//TODO: support partial responses

0 commit comments

Comments
 (0)