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}`);