Bug 547027 - Create a framework for MAPI tests. r=standard8,jorgk

This commit is contained in:
Joshua Cranmer 2019-02-10 21:16:26 +01:00
Родитель 72159e289e
Коммит 4b16fef452
9 изменённых файлов: 341 добавлений и 68 удалений

Просмотреть файл

@ -167,12 +167,7 @@ ULONG FAR PASCAL MAPISendMail (LHANDLE lhSession, ULONG ulUIParam, nsMapiMessage
if (!lhSession || pNsMapi->IsValidSession(lhSession) != S_OK)
{
FLAGS LoginFlag = 0;
if ( (flFlags & MAPI_LOGON_UI) && (flFlags & MAPI_NEW_SESSION) )
LoginFlag = MAPI_LOGON_UI | MAPI_NEW_SESSION ;
else if (flFlags & MAPI_LOGON_UI)
LoginFlag = MAPI_LOGON_UI ;
FLAGS LoginFlag = flFlags & (MAPI_LOGON_UI | MAPI_NEW_SESSION);
hr = MAPILogon(ulUIParam, nullptr, nullptr, LoginFlag, 0, &lhSession);
if (hr != SUCCESS_SUCCESS)
return MAPI_E_LOGIN_FAILURE ;
@ -213,12 +208,7 @@ ULONG FAR PASCAL MAPISendMailW(LHANDLE lhSession, ULONG ulUIParam, nsMapiMessage
if (!lhSession || pNsMapi->IsValidSession(lhSession) != S_OK)
{
FLAGS LoginFlag = 0;
if ((flFlags & MAPI_LOGON_UI) && (flFlags & MAPI_NEW_SESSION))
LoginFlag = MAPI_LOGON_UI | MAPI_NEW_SESSION;
else if (flFlags & MAPI_LOGON_UI)
LoginFlag = MAPI_LOGON_UI;
FLAGS LoginFlag = flFlags & (MAPI_LOGON_UI | MAPI_NEW_SESSION);
hr = MAPILogon(ulUIParam, nullptr, nullptr, LoginFlag, 0, &lhSession);
if (hr != SUCCESS_SUCCESS)
return MAPI_E_LOGIN_FAILURE;

Просмотреть файл

@ -36,6 +36,7 @@
#include "nsThreadUtils.h"
#include "nsMsgUtils.h"
#include "nsNetUtil.h"
#include "mozilla/Monitor.h"
#include "mozilla/Services.h"
#include "nsIArray.h"
#include "nsArrayUtils.h"
@ -44,9 +45,13 @@
extern mozilla::LazyLogModule MAPI; // defined in msgMapiImp.cpp
class nsMAPISendListener : public nsIMsgSendListener
class MAPISendListener : public nsIMsgSendListener,
public mozilla::Monitor
{
public:
MAPISendListener()
: Monitor("MAPISendListener monitor"),
m_done(false) {}
// nsISupports interface
NS_DECL_THREADSAFE_ISUPPORTS
@ -63,11 +68,9 @@ public:
/* void OnStopSending (in string aMsgID, in nsresult aStatus, in wstring aMsg, in nsIFile returnFile); */
NS_IMETHOD OnStopSending(const char *aMsgID, nsresult aStatus, const char16_t *aMsg,
nsIFile *returnFile) {
PR_CEnterMonitor(this);
PR_CNotifyAll(this);
m_done = true;
PR_CExitMonitor(this);
return NS_OK ;
NotifyAll();
return NS_OK;
}
/* void OnSendNotPerformed */
@ -79,30 +82,52 @@ public:
/* void OnGetDraftFolderURI (); */
NS_IMETHOD OnGetDraftFolderURI(const char *aFolderURI) {return NS_OK;}
static nsresult CreateMAPISendListener( nsIMsgSendListener **ppListener);
bool IsDone() { return m_done ; }
protected :
nsMAPISendListener() {
m_done = false;
}
bool m_done;
private:
virtual ~nsMAPISendListener() { }
bool m_done;
virtual ~MAPISendListener() { }
};
/// Helper for setting up the hidden window for blind MAPI.
class MOZ_STACK_CLASS AutoHiddenWindow {
public:
explicit AutoHiddenWindow(nsresult &rv)
: mAppService(do_GetService("@mozilla.org/appshell/appShellService;1"))
{
mCreatedHiddenWindow = false;
rv = mAppService->GetHiddenDOMWindow(getter_AddRefs(mHiddenWindow));
if (rv == NS_ERROR_FAILURE)
{
// Try to get a hidden window. If it doesn't exist, create a hidden
// window for us to use.
rv = mAppService->CreateHiddenWindow();
NS_ENSURE_SUCCESS_VOID(rv);
mCreatedHiddenWindow = true;
rv = mAppService->GetHiddenDOMWindow(getter_AddRefs(mHiddenWindow));
}
NS_ENSURE_SUCCESS_VOID(rv);
}
~AutoHiddenWindow()
{
if (mCreatedHiddenWindow)
mAppService->DestroyHiddenWindow();
}
mozIDOMWindowProxy *operator->()
{
return mHiddenWindow;
}
operator mozIDOMWindowProxy *()
{
return mHiddenWindow;
}
private:
nsCOMPtr<nsIAppShellService> mAppService;
nsCOMPtr<mozIDOMWindowProxy> mHiddenWindow;
bool mCreatedHiddenWindow;
};
NS_IMPL_ISUPPORTS(nsMAPISendListener, nsIMsgSendListener)
nsresult nsMAPISendListener::CreateMAPISendListener( nsIMsgSendListener **ppListener)
{
NS_ENSURE_ARG_POINTER(ppListener) ;
NS_ADDREF(*ppListener = new nsMAPISendListener());
return NS_OK;
}
NS_IMPL_ISUPPORTS(MAPISendListener, nsIMsgSendListener)
bool nsMapiHook::isMapiService = false;
@ -262,20 +287,15 @@ nsMapiHook::IsBlindSendAllowed()
// this is used for Send without UI
nsresult nsMapiHook::BlindSendMail (unsigned long aSession, nsIMsgCompFields * aCompFields)
{
nsresult rv = NS_OK ;
nsresult rv = NS_OK;
if (!IsBlindSendAllowed())
return NS_ERROR_FAILURE;
/** create nsIMsgComposeParams obj and other fields to populate it **/
// Get a hidden window to use for compose.
AutoHiddenWindow hiddenWindow(rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIDOMWindowProxy> hiddenWindow;
// get parent window
nsCOMPtr<nsIAppShellService> appService = do_GetService( "@mozilla.org/appshell/appShellService;1", &rv);
if (NS_FAILED(rv)|| (!appService) ) return rv ;
rv = appService->GetHiddenDOMWindow(getter_AddRefs(hiddenWindow));
if ( NS_FAILED(rv) ) return rv ;
// smtp password and Logged in used IdKey from MapiConfig (session obj)
nsMAPIConfiguration * pMapiConfig = nsMAPIConfiguration::GetMAPIConfiguration() ;
if (!pMapiConfig) return NS_ERROR_FAILURE ; // get the singelton obj
@ -294,9 +314,7 @@ nsresult nsMapiHook::BlindSendMail (unsigned long aSession, nsIMsgCompFields * a
if (NS_FAILED(rv) ) return rv ;
// create a send listener to get back the send status
nsCOMPtr <nsIMsgSendListener> sendListener ;
rv = nsMAPISendListener::CreateMAPISendListener(getter_AddRefs(sendListener)) ;
if (NS_FAILED(rv) || (!sendListener) ) return rv;
RefPtr<MAPISendListener> sendListener = new MAPISendListener;
// create the compose params object
nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv));
@ -315,33 +333,31 @@ nsresult nsMapiHook::BlindSendMail (unsigned long aSession, nsIMsgCompFields * a
// create the nsIMsgCompose object to send the object
nsCOMPtr<nsIMsgCompose> pMsgCompose (do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv));
if (NS_FAILED(rv) || (!pMsgCompose) ) return rv ;
/** initialize nsIMsgCompose, Send the message, wait for send completion response **/
NS_ENSURE_SUCCESS(rv, rv);
rv = pMsgCompose->Initialize(pMsgComposeParams, hiddenWindow, nullptr);
if (NS_FAILED(rv)) return rv ;
NS_ENSURE_SUCCESS(rv, rv);
// If we're in offline mode, we'll need to queue it for later. No point in trying to send it.
return pMsgCompose->SendMsg(WeAreOffline() ? nsIMsgSend::nsMsgQueueForLater : nsIMsgSend::nsMsgDeliverNow,
pMsgId, nullptr, nullptr, nullptr);
if (NS_FAILED(rv)) return rv ;
// If we're in offline mode, we'll need to queue it for later.
rv = pMsgCompose->SendMsg(WeAreOffline() ? nsIMsgSend::nsMsgQueueForLater
: nsIMsgSend::nsMsgDeliverNow,
pMsgId, nullptr, nullptr, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
// assign to interface pointer from nsCOMPtr to facilitate typecast below
nsIMsgSendListener * pSendListener = sendListener ;
// We need to wait to make sure that we only return when the send is
// completed. If we're offline, we're not sending yet, so don't bother
// waiting.
if (WeAreOffline())
return NS_OK;
// we need to wait here to make sure that we return only after send is completed
// so we will have a event loop here which will process the events till the Send IsDone.
nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
while ( !((nsMAPISendListener *) pSendListener)->IsDone() )
while (!sendListener->IsDone())
{
PR_CEnterMonitor(pSendListener);
PR_CWait(pSendListener, PR_MicrosecondsToInterval(1000UL));
PR_CExitMonitor(pSendListener);
mozilla::MonitorAutoLock mal(*sendListener);
sendListener->Wait(mozilla::TimeDuration::FromMilliseconds(1000UL));
NS_ProcessPendingEvents(thread);
}
return rv ;
return rv;
}
nsresult nsMapiHook::HandleAttachments (nsIMsgCompFields * aCompFields, int32_t aFileCount,
@ -907,9 +923,7 @@ nsresult nsMapiHook::ShowComposerWindow (unsigned long aSession, nsIMsgCompField
nsresult rv = NS_OK ;
// create a send listener to get back the send status
nsCOMPtr <nsIMsgSendListener> sendListener ;
rv = nsMAPISendListener::CreateMAPISendListener(getter_AddRefs(sendListener)) ;
if (NS_FAILED(rv) || (!sendListener) ) return rv ;
RefPtr<MAPISendListener> sendListener = new MAPISendListener;
// create the compose params object
nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv));

Просмотреть файл

@ -0,0 +1,7 @@
# 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']

Просмотреть файл

@ -0,0 +1,14 @@
"use strict";
module.exports = {
"extends": "plugin:mozilla/xpcshell-test",
"rules": {
"func-names": "off",
"mozilla/import-headjs-globals": "error",
"no-unused-vars": ["error", {
"args": "none",
"vars": "all",
}],
},
};

Просмотреть файл

@ -0,0 +1,199 @@
/* 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 {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
var {MailServices} = ChromeUtils.import("resource:///modules/MailServices.jsm");
var {ctypes} = ChromeUtils.import("resource:///modules/ctypes.jsm");
var {localAccountUtils} = ChromeUtils.import("resource://testing-common/mailnews/localAccountUtils.js");
// Ensure the profile directory is set up.
do_get_profile();
// Import fakeserver
var {nsMailServer} = ChromeUtils.import("resource://testing-common/mailnews/maild.js");
var {
smtpDaemon,
SMTP_RFC2821_handler,
} = ChromeUtils.import("resource://testing-common/mailnews/smtpd.js");
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;
}

Просмотреть файл

@ -0,0 +1 @@
load("../../../resources/mailShutdown.js");

Просмотреть файл

@ -0,0 +1,40 @@
/* 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.import("resource:///modules/ctypes.jsm");
var {MimeParser} = ChromeUtils.import("resource:///modules/mimeParser.jsm");
function run_test() {
// Set up an SMTP server and the MAPI daemon.
getBasicSmtpServer();
let [daemon, server] = setupServerDaemon();
server.start(SMTP_PORT);
let mapi = loadMAPILibrary();
// Build a message using the MAPI interface.
let message = new mapi.MapiMessage();
message.lpszSubject = ctypes.char.array()("Hello, MAPI!");
message.lpszNoteText = ctypes.char.array()("I successfully sent a message!");
message.lpszMessageType = ctypes.char.array()("");
message.lpFiles = null;
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);
// 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!");
Assert.equal(body.trim(), "I successfully sent a message!");
server.stop();
}

Просмотреть файл

@ -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.

Просмотреть файл

@ -51,6 +51,7 @@ if CONFIG['MOZ_MAPI_SUPPORT']:
'mapi/mapiDLL',
'mapi/mapihook',
]
TEST_DIRS += ['mapi/test']
DIRS += [
'build',