summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/mapi/test
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/mapi/test')
-rw-r--r--comm/mailnews/mapi/test/moz.build6
-rw-r--r--comm/mailnews/mapi/test/unit/head_mapi.js243
-rw-r--r--comm/mailnews/mapi/test/unit/tail_mapi.js1
-rw-r--r--comm/mailnews/mapi/test/unit/test_mapisendmail.js97
-rw-r--r--comm/mailnews/mapi/test/unit/xpcshell.ini7
5 files changed, 354 insertions, 0 deletions
diff --git a/comm/mailnews/mapi/test/moz.build b/comm/mailnews/mapi/test/moz.build
new file mode 100644
index 0000000000..6b37fdbe09
--- /dev/null
+++ b/comm/mailnews/mapi/test/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell.ini"]
diff --git a/comm/mailnews/mapi/test/unit/head_mapi.js b/comm/mailnews/mapi/test/unit/head_mapi.js
new file mode 100644
index 0000000000..d80b7da173
--- /dev/null
+++ b/comm/mailnews/mapi/test/unit/head_mapi.js
@@ -0,0 +1,243 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+var { ctypes } = ChromeUtils.importESModule(
+ "resource://gre/modules/ctypes.sys.mjs"
+);
+var { localAccountUtils } = ChromeUtils.import(
+ "resource://testing-common/mailnews/LocalAccountUtils.jsm"
+);
+
+// Ensure the profile directory is set up.
+do_get_profile();
+
+// Import fakeserver
+var { nsMailServer } = ChromeUtils.import(
+ "resource://testing-common/mailnews/Maild.jsm"
+);
+var { SmtpDaemon, SMTP_RFC2821_handler } = ChromeUtils.import(
+ "resource://testing-common/mailnews/Smtpd.jsm"
+);
+
+var SMTP_PORT = 1024 + 120;
+var POP3_PORT = 1024 + 121;
+
+// Setup the daemon and server
+function setupServerDaemon(handler) {
+ if (!handler) {
+ handler = function (d) {
+ return new SMTP_RFC2821_handler(d);
+ };
+ }
+ let daemon = new SmtpDaemon();
+ let server = new nsMailServer(handler, daemon);
+ return [daemon, server];
+}
+
+function getBasicSmtpServer() {
+ // We need to have a default account for MAPI.
+ localAccountUtils.loadLocalMailAccount();
+ let incoming = localAccountUtils.create_incoming_server(
+ "pop3",
+ POP3_PORT,
+ "user",
+ "password"
+ );
+ let server = localAccountUtils.create_outgoing_server(
+ SMTP_PORT,
+ "user",
+ "password"
+ );
+ // We also need to have a working identity, including an email address.
+ let account = MailServices.accounts.FindAccountForServer(incoming);
+ localAccountUtils.associate_servers(account, server, true);
+ let identity = account.defaultIdentity;
+ identity.email = "tinderbox@tinderbox.invalid";
+ MailServices.accounts.defaultAccount = account;
+
+ return server;
+}
+
+/**
+ * Returns a structure allowing access to all of the Simple MAPI functions.
+ * The functions do not have the MAPI prefix on the variables. Also added are
+ * the three structures needed for MAPI.
+ */
+function loadMAPILibrary() {
+ // This is a hack to load the MAPI support in the current environment, as the
+ // profile-after-change event is never sent out.
+ var gMapiSupport = Cc["@mozilla.org/mapisupport;1"].getService(
+ Ci.nsIObserver
+ );
+ gMapiSupport.observe(null, "profile-after-change", null);
+ // Set some preferences to make MAPI (particularly blind MAPI, aka work
+ // without a dialog box) work properly.
+ Services.prefs.setBoolPref("mapi.blind-send.enabled", true);
+ Services.prefs.setBoolPref("mapi.blind-send.warn", false);
+
+ // The macros that are used in the definitions
+ let WINAPI = ctypes.winapi_abi;
+ let ULONG = ctypes.unsigned_long;
+ let LHANDLE = ULONG.ptr;
+ let LPSTR = ctypes.char.ptr;
+ let LPVOID = ctypes.voidptr_t;
+ let FLAGS = ctypes.unsigned_long;
+
+ // Define all of the MAPI structs we need to use.
+ let functionData = {};
+ functionData.MapiRecipDesc = new ctypes.StructType("gMapi.MapiRecipDesc", [
+ { ulReserved: ULONG },
+ { ulRecipClass: ULONG },
+ { lpszName: LPSTR },
+ { lpszAddress: LPSTR },
+ { ulEIDSize: ULONG },
+ { lpEntryID: LPVOID },
+ ]);
+ let lpMapiRecipDesc = functionData.MapiRecipDesc.ptr;
+
+ functionData.MapiFileDesc = new ctypes.StructType("gMapi.MapiFileDesc", [
+ { ulReserved: ULONG },
+ { flFlags: ULONG },
+ { nPosition: ULONG },
+ { lpszPathName: LPSTR },
+ { lpszFileName: LPSTR },
+ { lpFileType: LPVOID },
+ ]);
+ let lpMapiFileDesc = functionData.MapiFileDesc.ptr;
+
+ functionData.MapiMessage = new ctypes.StructType("gMapi.MapiMessage", [
+ { ulReserved: ULONG },
+ { lpszSubject: LPSTR },
+ { lpszNoteText: LPSTR },
+ { lpszMessageType: LPSTR },
+ { lpszDateReceived: LPSTR },
+ { lpszConversationID: LPSTR },
+ { flFlags: FLAGS },
+ { lpOriginator: lpMapiRecipDesc },
+ { nRecipCount: ULONG },
+ { lpRecips: lpMapiRecipDesc },
+ { nFileCount: ULONG },
+ { lpFiles: lpMapiFileDesc },
+ ]);
+ let lpMapiMessage = functionData.MapiMessage.ptr;
+
+ // Load the MAPI library. We're using our definition instead of the global
+ // MAPI definition.
+ let mapi = ctypes.open("mozMapi32.dll");
+
+ // Load the MAPI functions,
+ // see https://developer.mozilla.org/en-US/docs/Mozilla/js-ctypes/Using_js-ctypes/Declaring_types
+ // for details. The first three parameters of the declaration are name, API flag and output value.
+ // This is followed by input parameters.
+
+ // MAPIAddress is not supported.
+
+ functionData.DeleteMail = mapi.declare(
+ "MAPIDeleteMail",
+ WINAPI,
+ ULONG,
+ LHANDLE, // lhSession
+ ULONG.ptr, // ulUIParam
+ LPSTR, // lpszMessageID
+ FLAGS, // flFlags
+ ULONG
+ ); // ulReserved
+
+ // MAPIDetails is not supported.
+
+ functionData.FindNext = mapi.declare(
+ "MAPIFindNext",
+ WINAPI,
+ ULONG,
+ LHANDLE, // lhSession
+ ULONG.ptr, // ulUIParam
+ LPSTR, // lpszMessageType
+ LPSTR, // lpszSeedMessageID
+ FLAGS, // flFlags
+ ULONG, // ulReserved
+ LPSTR
+ ); // lpszMessageID
+
+ functionData.FreeBuffer = mapi.declare(
+ "MAPIFreeBuffer",
+ WINAPI,
+ ULONG,
+ LPVOID
+ ); // pv
+
+ functionData.Logoff = mapi.declare(
+ "MAPILogoff",
+ WINAPI,
+ ULONG,
+ LHANDLE, // lhSession
+ ULONG.ptr, // ulUIParam
+ FLAGS, // flFlags
+ ULONG
+ ); // ulReserved
+
+ functionData.Logon = mapi.declare(
+ "MAPILogon",
+ WINAPI,
+ ULONG,
+ ULONG.ptr, // ulUIParam
+ LPSTR, // lpszProfileName
+ LPSTR, // lpszPassword
+ FLAGS, // flFlags
+ ULONG, // ulReserved
+ LHANDLE.ptr
+ ); // lplhSession
+
+ functionData.ReadMail = mapi.declare(
+ "MAPIReadMail",
+ WINAPI,
+ ULONG,
+ LHANDLE, // lhSession
+ ULONG.ptr, // ulUIParam
+ LPSTR, // lpszMessageID
+ FLAGS, // flFlags
+ ULONG, // ulReserved
+ lpMapiMessage.ptr
+ ); // *lppMessage
+
+ functionData.ResolveName = mapi.declare(
+ "MAPIResolveName",
+ WINAPI,
+ ULONG,
+ LHANDLE, // lhSession
+ ULONG.ptr, // ulUIParam
+ LPSTR, // lpszName
+ FLAGS, // flFlags
+ ULONG, // ulReserved
+ lpMapiRecipDesc.ptr
+ ); // *lppRecip
+
+ // MAPISaveMail is not supported.
+
+ functionData.SendDocuments = mapi.declare(
+ "MAPISendDocuments",
+ WINAPI,
+ ULONG,
+ ULONG.ptr, // ulUIParam
+ LPSTR, // lpszDelimChar
+ LPSTR, // lpszFilePaths
+ LPSTR, // lpszFileNames
+ ULONG
+ ); // ulReserved
+
+ functionData.SendMail = mapi.declare(
+ "MAPISendMail",
+ WINAPI,
+ ULONG,
+ LHANDLE, // lhSession
+ ULONG.ptr, // ulUIParam
+ lpMapiMessage, // lpMessage
+ FLAGS, // flFlags
+ ULONG
+ ); // ulReserved
+
+ return functionData;
+}
diff --git a/comm/mailnews/mapi/test/unit/tail_mapi.js b/comm/mailnews/mapi/test/unit/tail_mapi.js
new file mode 100644
index 0000000000..f64ae61317
--- /dev/null
+++ b/comm/mailnews/mapi/test/unit/tail_mapi.js
@@ -0,0 +1 @@
+load("../../../resources/mailShutdown.js");
diff --git a/comm/mailnews/mapi/test/unit/test_mapisendmail.js b/comm/mailnews/mapi/test/unit/test_mapisendmail.js
new file mode 100644
index 0000000000..5dc480af9e
--- /dev/null
+++ b/comm/mailnews/mapi/test/unit/test_mapisendmail.js
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { ctypes } = ChromeUtils.importESModule(
+ "resource://gre/modules/ctypes.sys.mjs"
+);
+var { MimeParser } = ChromeUtils.import("resource:///modules/mimeParser.jsm");
+var { mailTestUtils } = ChromeUtils.import(
+ "resource://testing-common/mailnews/MailTestUtils.jsm"
+);
+
+// Set up an SMTP server and the MAPI daemon.
+getBasicSmtpServer();
+let [daemon, server] = setupServerDaemon();
+server.start(SMTP_PORT);
+let mapi = loadMAPILibrary();
+
+registerCleanupFunction(() => {
+ server.stop();
+});
+
+/**
+ * Construct a MapiMessage, then send through MAPI.
+ *
+ * @param {boolean} offline - Switch to offline mode if true.
+ */
+function testMapiSendMail(offline) {
+ Services.io.offline = offline;
+
+ // Build a message using the MAPI interface.
+ let message = new mapi.MapiMessage();
+ message.lpszSubject = ctypes.char.array()(`Hello, MAPI offline=${offline}!`);
+ message.lpszNoteText = ctypes.char.array()("I successfully sent a message!");
+ message.lpszMessageType = ctypes.char.array()("");
+
+ let file = do_get_file("../../../compose/test/unit/data/message1.eml");
+ let attachment = new mapi.MapiFileDesc();
+ attachment.lpszFileName = ctypes.char.array()(file.leafName);
+ attachment.lpszPathName = ctypes.char.array()(file.path);
+ message.nFileCount = 1;
+ message.lpFiles = attachment.address();
+
+ let recipient = new mapi.MapiRecipDesc();
+ recipient.ulRecipClass = 1; /* MAPI_TO */
+ recipient.lpszName = ctypes.char.array()("John Doe");
+ recipient.lpszAddress = ctypes.char.array()("SMTP:john.doe@example.com");
+ message.nRecipCount = 1;
+ message.lpRecips = recipient.address();
+
+ // Use MAPISendMail to send this message.
+ mapi.SendMail(
+ null /* No session */,
+ null /* No HWND */,
+ message.address(),
+ 0x2 /* MAPI_NEW_SESSION */,
+ 0
+ );
+}
+
+/**
+ * Test that when we're online, the message can be sent correctly to the SMTP
+ * server.
+ */
+add_task(function mapiSendMailOnline() {
+ server.resetTest();
+ testMapiSendMail(false);
+
+ // Check that the post has the correct information.
+ let [headers, body] = MimeParser.extractHeadersAndBody(daemon.post);
+ Assert.equal(headers.get("from")[0].email, "tinderbox@tinderbox.invalid");
+ Assert.equal(headers.get("to")[0].email, "john.doe@example.com");
+ Assert.equal(headers.get("subject"), "Hello, MAPI offline=false!");
+ Assert.ok(body.includes("I successfully sent a message!"));
+ Assert.ok(body.includes("this email is in dos format"));
+});
+
+/**
+ * Test that when we're offline, the message can be saved correctly to the Outbox.
+ */
+add_task(function mapiSendMailOffline() {
+ server.resetTest();
+ testMapiSendMail(true);
+
+ let outbox = localAccountUtils.rootFolder.getChildNamed("Outbox");
+ let msgData = mailTestUtils.loadMessageToString(
+ outbox,
+ mailTestUtils.firstMsgHdr(outbox)
+ );
+ // Check that the post has the correct information.
+ let [headers, body] = MimeParser.extractHeadersAndBody(msgData);
+ Assert.equal(headers.get("from")[0].email, "tinderbox@tinderbox.invalid");
+ Assert.equal(headers.get("to")[0].email, "john.doe@example.com");
+ Assert.equal(headers.get("subject"), "Hello, MAPI offline=true!");
+ Assert.ok(body.includes("I successfully sent a message!"));
+ Assert.ok(body.includes("this email is in dos format"));
+});
diff --git a/comm/mailnews/mapi/test/unit/xpcshell.ini b/comm/mailnews/mapi/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..8d4fe504ab
--- /dev/null
+++ b/comm/mailnews/mapi/test/unit/xpcshell.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+head = head_mapi.js
+tail = tail_mapi.js
+run-sequentially = Need to use well-known port for SMTP.
+
+[test_mapisendmail.js]
+skip-if = true # Not yet enabled, see bug 1526807.