diff --git a/apps/api/src/dkv/providers/exchange-inbox.provider.ts b/apps/api/src/dkv/providers/exchange-inbox.provider.ts index 7dd4e99..f8b9cb7 100644 --- a/apps/api/src/dkv/providers/exchange-inbox.provider.ts +++ b/apps/api/src/dkv/providers/exchange-inbox.provider.ts @@ -23,7 +23,8 @@ function soapEnvelope(body: string): string { `; } -function findItemSoap(folderId: string, maxResults: number, senderFilter?: string): string { +/** folderElement: either or */ +function findItemSoap(folderElement: string, maxResults: number, senderFilter?: string): string { const isReadFilter = ` @@ -56,7 +57,7 @@ function findItemSoap(folderId: string, maxResults: number, senderFilter?: strin ${restriction} - + ${folderElement} `); } @@ -157,24 +158,46 @@ function extractAttrs(xml: string, tag: string, attr: string): string[] { return results; } -/** Map configured folder name to EWS DistinguishedFolderId. */ -function resolveDistinguishedFolder(folder?: string): string { +const DISTINGUISHED_FOLDER_MAP: Record = { + inbox: 'inbox', + posteingang: 'inbox', + deleteditems: 'deleteditems', + gelöschteelemente: 'deleteditems', + trash: 'deleteditems', + sentitems: 'sentitems', + gesendet: 'sentitems', + drafts: 'drafts', + entwürfe: 'drafts', + junk: 'junkemail', + junkemail: 'junkemail', + spam: 'junkemail', +}; + +/** Returns the DistinguishedFolderId if folder is a well-known name, else null (→ needs FindFolder). */ +function resolveDistinguishedFolder(folder?: string): string | null { const name = (folder ?? 'INBOX').toLowerCase().replace(/[\s_-]/g, ''); - const map: Record = { - inbox: 'inbox', - posteingang: 'inbox', - deleteditems: 'deleteditems', - gelöschteelemente: 'deleteditems', - trash: 'deleteditems', - sentitems: 'sentitems', - gesendet: 'sentitems', - drafts: 'drafts', - entwürfe: 'drafts', - junk: 'junkemail', - junkemail: 'junkemail', - spam: 'junkemail', - }; - return map[name] ?? 'inbox'; + return DISTINGUISHED_FOLDER_MAP[name] ?? null; +} + +/** Build FindFolder SOAP to resolve a subfolder by display name under a parent DistinguishedFolderId. */ +function findFolderSoap(parentDistinguishedId: string, displayName: string): string { + return soapEnvelope(` + + + IdOnly + + + + + + + + + + + + + `); } // ─── NTLM HTTP helper ──────────────────────────────────────────────────────── @@ -227,8 +250,8 @@ export class ExchangeInboxProvider implements InboxProvider { async testConnection(config: InboxConfig): Promise<{ success: boolean; message?: string }> { try { - const folder = resolveDistinguishedFolder(config.folder); - const soap = findItemSoap(folder, 1); + const folderElement = await this.resolveFolderElement(config); + const soap = findItemSoap(folderElement, 1); const res = await this.ewsPost(config, soap, 'FindItem'); if (res.statusCode === 401) { @@ -251,11 +274,34 @@ export class ExchangeInboxProvider implements InboxProvider { // ─── Private ─────────────────────────────────────────────────────────────── + /** Resolve folder config to a EWS ParentFolderIds XML element. + * Well-known names → . + * Custom names (e.g. "DKV" or "INBOX/DKV") → FindFolder deep search under inbox → . + */ + private async resolveFolderElement(config: InboxConfig): Promise { + const folderCfg = config.folder ?? 'INBOX'; + // Strip leading "INBOX/" prefix — EWS FindFolder searches deep, name alone suffices + const displayName = folderCfg.replace(/^INBOX\//i, '').trim(); + const distinguished = resolveDistinguishedFolder(displayName); + if (distinguished) { + return ``; + } + // Custom subfolder: resolve via FindFolder + const ffSoap = findFolderSoap('inbox', displayName); + const ffRes = await this.ewsPost(config, ffSoap, 'FindFolder'); + const folderId = extractAttr(ffRes.body, 't:FolderId', 'Id'); + if (!folderId) { + this.logger.warn(`EWS FindFolder: subfolder "${displayName}" not found — falling back to inbox`); + return ``; + } + return ``; + } + private async fetchViaEws(config: InboxConfig): Promise { - const folder = resolveDistinguishedFolder(config.folder); + const folderElement = await this.resolveFolderElement(config); // 1. FindItem — get IDs of emails with attachments - const findSoap = findItemSoap(folder, 50, config.senderFilter); + const findSoap = findItemSoap(folderElement, 50, config.senderFilter); const findRes = await this.ewsPost(config, findSoap, 'FindItem'); if (findRes.statusCode !== 200) { this.logger.warn(`EWS FindItem returned HTTP ${findRes.statusCode}`);