fix: omit empty cc/bcc from Email/set so server does not emit bare Cc: header #301

This commit is contained in:
Linus Rath
2026-05-18 16:36:45 +02:00
parent 40f36baf94
commit fa261fecfd
3 changed files with 30 additions and 8 deletions
+19
View File
@@ -127,6 +127,25 @@ describe('JMAPClient.sendEmail threading headers', () => {
expect(draft.references).toBeUndefined();
});
it('omits cc/bcc when arrays are empty so the server does not emit a bare Cc: header', async () => {
const client = createClient();
const captured = mockSendEmailFlow();
await client.sendEmail(
['recipient@example.com'],
'No copies',
'body',
[], [], 'identity-1', 'user@example.com',
);
const setCall = captured[2].methodCalls[0];
const create = setCall[1].create as Record<string, Record<string, unknown>>;
const draft = Object.values(create)[0];
expect(draft.cc).toBeUndefined();
expect(draft.bcc).toBeUndefined();
});
it('drops empty / whitespace-only ids rather than sending blank entries', async () => {
const client = createClient();
const captured = mockSendEmailFlow();
+4 -4
View File
@@ -409,8 +409,8 @@ export class DemoJMAPClient implements IJMAPClient {
receivedAt: new Date().toISOString(),
from: [{ name: 'Demo User', email: 'demo@example.com' }],
to: to.map(e => ({ email: e })),
cc: cc?.map(e => ({ email: e })),
bcc: bcc?.map(e => ({ email: e })),
cc: cc?.length ? cc.map(e => ({ email: e })) : undefined,
bcc: bcc?.length ? bcc.map(e => ({ email: e })) : undefined,
subject,
sentAt: new Date().toISOString(),
preview: body.substring(0, 200),
@@ -463,8 +463,8 @@ export class DemoJMAPClient implements IJMAPClient {
receivedAt: new Date().toISOString(),
from: [{ name: 'Demo User', email: 'demo@example.com' }],
to: to.map(e => ({ email: e })),
cc: cc?.map(e => ({ email: e })),
bcc: bcc?.map(e => ({ email: e })),
cc: cc?.length ? cc.map(e => ({ email: e })) : undefined,
bcc: bcc?.length ? bcc.map(e => ({ email: e })) : undefined,
subject,
sentAt: new Date().toISOString(),
preview: body.substring(0, 200),
+7 -4
View File
@@ -2014,8 +2014,8 @@ export class JMAPClient implements IJMAPClient {
const emailData: EmailDraft = {
from: [{ ...(sanitizedFromName ? { name: sanitizedFromName } : {}), email: fromEmail || this.username }],
to: to.map(email => ({ email })),
cc: cc?.map(email => ({ email })),
bcc: bcc?.map(email => ({ email })),
cc: cc?.length ? cc.map(email => ({ email })) : undefined,
bcc: bcc?.length ? bcc.map(email => ({ email })) : undefined,
subject,
keywords: { "$draft": true },
mailboxIds: { [draftsMailbox.id]: true },
@@ -2141,8 +2141,11 @@ export class JMAPClient implements IJMAPClient {
from: [{ ...(sanitizedFromName ? { name: sanitizedFromName } : {}), email: fromEmail || this.username }],
replyTo: identityReplyTo?.length ? identityReplyTo : undefined,
to: to.map(email => ({ email })),
cc: cc?.map(email => ({ email })),
bcc: bcc?.map(email => ({ email })),
// RFC 5322 §3.6.3: To/Cc carry an address-list (non-empty). Sending
// cc:[] makes the server emit a literal `Cc:` header with no addresses,
// which is malformed and a spam signal. Omit the field when empty.
cc: cc?.length ? cc.map(email => ({ email })) : undefined,
bcc: bcc?.length ? bcc.map(email => ({ email })) : undefined,
subject,
inReplyTo: normalizedInReplyTo?.length ? normalizedInReplyTo : undefined,
references: normalizedReferences?.length ? normalizedReferences : undefined,