diff --git a/lib/msoffice.js b/lib/msoffice.js index 46905fe..f39568a 100644 --- a/lib/msoffice.js +++ b/lib/msoffice.js @@ -239,7 +239,6 @@ function Outlook() { this.namespace.Logon("", "", false, false); console.info("Outlook MAPI session established"); } catch (e) { - // Already logged on / interactive session not required console.warn("Outlook MAPI session already active or logon skipped"); } @@ -257,7 +256,6 @@ function Outlook() { this.namespace.Logoff(); console.info("Outlook MAPI session closed"); } catch (e) { - // Logoff may fail if Outlook manages the session console.warn("Outlook MAPI session logoff skipped"); } @@ -265,7 +263,7 @@ function Outlook() { this.application = null; console.info("Outlook automation released"); }; - + this.selectFolder = function (folderIdOrPath) { if (typeof folderIdOrPath === "number") { this.currentFolder = this.namespace.GetDefaultFolder(folderIdOrPath); @@ -276,7 +274,7 @@ function Outlook() { } this.items = this.currentFolder.Items; - this.items.Sort("[ReceivedTime]", true); // newest first + this.items.Sort("[ReceivedTime]", true); return this; }; @@ -291,12 +289,14 @@ function Outlook() { }; this.restrict = function (filter) { + // Accept both Jet filter and DASL filter (@SQL=...) + console.log(filter); var restricted = this.items.Restrict(filter); return new Outlook.Items(restricted); }; this.createMail = function () { - var mail = this.application.CreateItem(0); // 0 = olMailItem + var mail = this.application.CreateItem(0); return new Outlook.MailItem(mail); }; @@ -309,12 +309,20 @@ function Outlook() { }; this.searchByRecipientContains = function (keyword) { - return this.restrict(Outlook.Search.filters.recipientContains(keyword)); + // DASL (@SQL=...) + return this.restrict(Outlook.Search.filters.recipientContains_DASL(keyword)); }; this.searchBySenderOrRecipientContains = function (keyword) { - var pre = this.restrict(Outlook.Search.filters.senderOrToCcBccContains(keyword)); - return new Outlook.ItemsFiltered(pre, function (mailItem) { + // IMPORTANT: cannot mix Jet and DASL in a single Restrict filter + // We run two Restrict calls and then merge the candidates in JS. + + var bySender = this.restrict(Outlook.Search.filters.senderContains_Jet(keyword)); + var byRecipients = this.restrict(Outlook.Search.filters.recipientContains_DASL(keyword)); + + var merged = new Outlook.ItemsMerged(bySender, byRecipients); + + return new Outlook.ItemsFiltered(merged, function (mailItem) { return Outlook.Search.match.senderOrRecipientObjectContains(mailItem, keyword); }); }; @@ -348,13 +356,9 @@ Outlook.Folders = { Outlook.MailItemClass = 43; Outlook.resolveFolderPath = function (mapiNamespace, path) { - // path examples: - // - "Inbox\\SubFolder" - // - "Mailbox - Name\\Inbox\\SubFolder" (store root name) var parts = path.split("\\"); var cur = null; - // If first segment matches a store root, start there; else start at default store root. var stores = mapiNamespace.Folders; for (var i = 1; i <= stores.Count; i++) { var f = stores.Item(i); @@ -397,12 +401,10 @@ Outlook.Items = function (items) { }; Outlook.ItemsFiltered = function (items, predicate) { - // items: Outlook.Items this.base = (items instanceof Outlook.Items) ? items : new Outlook.Items(items); this.predicate = predicate; this.count = function () { - // filtered count is expensive; do not compute return this.base.count(); }; @@ -429,6 +431,51 @@ Outlook.ItemsFiltered = function (items, predicate) { }; }; +Outlook.ItemsMerged = function (a, b) { + this.a = (a instanceof Outlook.Items) ? a : new Outlook.Items(a); + this.b = (b instanceof Outlook.Items) ? b : new Outlook.Items(b); + + this.count = function () { + return this.a.count() + this.b.count(); + }; + + this.get = function (idx) { + return null; + }; + + this.forEach = function (fn, maxCount) { + var seen = {}; + var emitted = 0; + + var emit = function (it, idx) { + if (!it) return false; + if (!(it instanceof Outlook.MailItem)) return false; + + var entryId = it.mail.EntryID; + if (!entryId) entryId = String(it.getSubject()) + "|" + String(it.getReceivedTime()); + + if (seen[entryId]) return false; + seen[entryId] = true; + + fn(it, idx); + emitted++; + + if (typeof maxCount === "number" && maxCount > 0 && emitted >= maxCount) return true; + return false; + }; + + var na = this.a.count(); + for (var i = 1; i <= na; i++) { + if (emit(this.a.get(i), i)) return; + } + + var nb = this.b.count(); + for (var j = 1; j <= nb; j++) { + if (emit(this.b.get(j), j)) return; + } + }; +}; + Outlook.Item = function (item) { this.item = item; @@ -519,52 +566,40 @@ Outlook.MailItem = function (mail) { }; Outlook.Search = {}; - Outlook.Search.filters = {}; -// Jet: escape for single-quoted literals Outlook.Search.filters._escape = function (s) { return (s + "").replace(/'/g, "''"); }; -// DASL: escape for single-quoted literals (same rule) Outlook.Search.filters._escapeDASL = function (s) { return (s + "").replace(/'/g, "''"); }; -// 1) Subject contains (Jet, wildcard = *) Outlook.Search.filters.subjectContains = function (keyword) { var k = Outlook.Search.filters._escape(keyword); return "([Subject] Like '*" + k + "*')"; }; -// 2) Sender contains (Jet, wildcard = *) Outlook.Search.filters.senderContains = function (keyword) { var k = Outlook.Search.filters._escape(keyword); return "([SenderEmailAddress] Like '*" + k + "*') OR ([SenderName] Like '*" + k + "*')"; }; -// 3) Sender email equals (Jet) Outlook.Search.filters.senderEmailEquals = function (email) { var e = Outlook.Search.filters._escape(email); return "([SenderEmailAddress] = '" + e + "')"; }; -// 4) Recipient contains (DASL; use @SQL= with % wildcard) Outlook.Search.filters.recipientContains = function (keyword) { + // Legacy name kept for compatibility: this returns DASL now var k = Outlook.Search.filters._escapeDASL(keyword); - - // Note: do NOT wrap property names in [] in DASL. - // Use httpmail display fields for recipient display strings. return '@SQL=' + '"urn:schemas:httpmail:displayto" LIKE \'%' + k + '%\' OR ' + '"urn:schemas:httpmail:displaycc" LIKE \'%' + k + '%\' OR ' + '"urn:schemas:httpmail:displaybcc" LIKE \'%' + k + '%\''; }; -// 5) Sender OR To/CC/BCC contains -// IMPORTANT: cannot mix Jet and DASL in a single Restrict filter. -// Provide TWO filters: Jet (sender) + DASL (recipients), and let caller intersect/union in script. Outlook.Search.filters.senderContains_Jet = function (keyword) { return Outlook.Search.filters.senderContains(keyword); }; @@ -573,15 +608,11 @@ Outlook.Search.filters.recipientContains_DASL = function (keyword) { return Outlook.Search.filters.recipientContains(keyword); }; -// Optional helper: receivedSince in Jet (often OK), but keep separate to avoid mixing with DASL Outlook.Search.filters.receivedSince = function (dateObj) { return "([ReceivedTime] >= '" + dateObj + "')"; }; -// Optional helper: receivedSince in DASL (if you want to apply with other DASL filters) Outlook.Search.filters.receivedSince_DASL = function (dateObj) { - // Use the same date string format you pass elsewhere; Outlook parses locale-dependent strings. - // This uses DAV:date-received. If your environment is picky, keep date restriction in Jet step. var d = Outlook.Search.filters._escapeDASL(dateObj); return '@SQL="DAV:date-received" >= \'' + d + '\''; };