/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2008 OMC Denmark ApS.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma warning( disable : 6211 )

#include "IMessageS_impl.h"

#include "recur_utils.h"
#include "obj_utils.h"
#include "conv_utils.h"
#include "progress_utils.h"
#include "IMAPITableS_impl.h"
#include "uidC.h"

#include <mapiutil.h>

#include "templates.i"

// see: http://support.microsoft.com/?kbid=834496
#define MDB_ONLINE ((ULONG) 0x00000100)

static inline ULONG native_flags(const ::BRUTUS::BDEFINE Flags)
{
	::BRUTUS::BDEFINE flags = Flags;
	ULONG retval = 0;

	if (!Flags)
		return 0;

	if (flags & ::BRUTUS::BRUTUS_MODRECIP_ADD) {
		retval |= MODRECIP_ADD;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MODRECIP_ADD);
	}
	if (flags & ::BRUTUS::BRUTUS_MODRECIP_MODIFY) {
		retval |= MODRECIP_MODIFY;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MODRECIP_MODIFY);
	}
	if (flags & ::BRUTUS::BRUTUS_MODRECIP_REMOVE) {
		retval |= MODRECIP_REMOVE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MODRECIP_REMOVE);
	}
	if (flags & ::BRUTUS::BRUTUS_FORCE_SUBMIT) {
		retval |= FORCE_SUBMIT;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_FORCE_SUBMIT);
	}
	if (flags & ::BRUTUS::BRUTUS_ATTACH_DIALOG) {
		retval |= ATTACH_DIALOG;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_ATTACH_DIALOG);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_BEST_ACCESS) {
		retval |= MAPI_BEST_ACCESS;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_BEST_ACCESS);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_DIALOG) {
		retval |= MAPI_DIALOG;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_DIALOG);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_DECLINE_OK) {
		retval |= MAPI_DECLINE_OK;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_DECLINE_OK);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_MOVE) {
		retval |= MAPI_MOVE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_MOVE);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_NOREPLACE) {
		retval |= MAPI_NOREPLACE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_NOREPLACE);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_MODIFY) {
		retval |= MAPI_MODIFY;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_MODIFY);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_CREATE) {
		retval |= MAPI_CREATE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_CREATE);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_UNICODE) {
		retval |= MAPI_UNICODE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_UNICODE);
	}
	if (flags & ::BRUTUS::BRUTUS_FORCE_SAVE) {
		retval |= FORCE_SAVE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_FORCE_SAVE);
	}
	if (flags & ::BRUTUS::BRUTUS_KEEP_OPEN_READONLY) {
		retval |= KEEP_OPEN_READONLY;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_KEEP_OPEN_READONLY);
	}
	if (flags & ::BRUTUS::BRUTUS_KEEP_OPEN_READWRITE) {
		retval |= KEEP_OPEN_READWRITE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_KEEP_OPEN_READWRITE);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_DEFERRED_ERRORS) {
		retval |= MAPI_DEFERRED_ERRORS;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_DEFERRED_ERRORS);
	}

	if (flags) {
		char msg[128] = {0};
		sprintf_s(msg, sizeof(msg), "Unknown flag(s) from BRUTUS : %X", flags);
		BRUTUS_LOG_BUG(msg);
	}

	return retval;
}

static inline ULONG native_read_flags(const ::BRUTUS::BDEFINE Flags)
{
	::BRUTUS::BDEFINE flags = Flags;
	ULONG retval = 0;

	if (!Flags)
		return 0;

	if (flags & ::BRUTUS::BRUTUS_CLEAR_READ_FLAG) {
		retval |= CLEAR_READ_FLAG;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_CLEAR_READ_FLAG);
	}
	if (flags & ::BRUTUS::BRUTUS_CLEAR_NRN_PENDING) {
		retval |= CLEAR_NRN_PENDING;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_CLEAR_NRN_PENDING);
	}
	if (flags & ::BRUTUS::BRUTUS_CLEAR_RN_PENDING) {
		retval |= CLEAR_RN_PENDING;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_CLEAR_RN_PENDING);
	}
	if (flags & ::BRUTUS::BRUTUS_GENERATE_RECEIPT_ONLY) {
		retval |= GENERATE_RECEIPT_ONLY;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_GENERATE_RECEIPT_ONLY);
	}
	if (flags & ::BRUTUS::BRUTUS_SUPPRESS_RECEIPT) {
		retval |= SUPPRESS_RECEIPT;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_SUPPRESS_RECEIPT);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_DEFERRED_ERRORS) {
		retval |= MAPI_DEFERRED_ERRORS;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_DEFERRED_ERRORS);
	}

	if (flags) {
		char msg[128] = {0};
		sprintf_s(msg, sizeof(msg), "Unknown read flag(s) from BRUTUS : %X", flags);
		BRUTUS_LOG_BUG(msg);
	}

	return retval;
}

/*
 * Will retrieve the property tag for the recurrence BLOB.
 * No locking is performed.
 */
static ULONG
get_recurrence_proptag(bool is_event,
		       LPMESSAGE message)
{
	GUID mapi_propset_guid;
	if (!guid_brutus_to_mapi_no_alloc(is_event ? ::BRUTUS::BRUTUS_PS_PROPSET_APPOINTMENT : ::BRUTUS::BRUTUS_PS_PROPSET_TASK,
					  &mapi_propset_guid,
					  false))
		return 0;

	::LPMAPINAMEID *mapi_names = NULL;
	if (S_OK != MAPIAllocateBuffer(sizeof(::MAPINAMEID*),
				       (void**)&mapi_names))
		return 0;

	if (S_OK != MAPIAllocateMore(sizeof(::MAPINAMEID),
				     mapi_names,
				     (void**)&(mapi_names[0]))) {
		MAPIFreeBuffer(mapi_names);
		return 0;
	}
	mapi_names[0]->lpguid = (LPGUID)&mapi_propset_guid;
	mapi_names[0]->ulKind = MNID_ID;
	mapi_names[0]->Kind.lID = ((is_event ? ::BRUTUS::BRUTUS_PR_OUTLOOK_EVENT_RECURRENCE_STATE : ::BRUTUS::BRUTUS_PR_OUTLOOK_TASK_RECURRENCE_STATE) >> 16);

	SPropTagArray *tags = NULL;
	HRESULT hr = message->GetIDsFromNames(1,
					      mapi_names,
					      MAPI_CREATE,
					      &tags);

	MAPIFreeBuffer(mapi_names);
	if ((S_OK != hr) || !tags)
		return 0;
	if (1 != tags->cValues) {
		MAPIFreeBuffer(tags);
		return 0;
	}

	ULONG retv = 0;
	if (S_OK == hr)
		retv = PROP_TAG(PT_BINARY, tags->aulPropTag[0]);
	MAPIFreeBuffer(tags);

	return retv;
}

BRUTUS_IMessage_i::BRUTUS_IMessage_i(LPMESSAGE Message,
				     LPMAPISESSION MAPISession,
				     ::PortableServer::POA_ptr Poa)
	: message_(Message),
	  mapi_session_(MAPISession),
	  poa_(::PortableServer::POA::_duplicate(Poa)),
	  recurrence_proptag_(0)
{
	mapi_session_->AddRef();
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::GetAttachmentTable(::BRUTUS::BDEFINE ulFlags,
							::BRUTUS::IMAPITable_out lppTable)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetAttachmentTable()");

	lppTable = ::BRUTUS::IMAPITable::_nil();
	LPMAPITABLE mapi_table = NULL;

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetAttachmentTable(flags, &mapi_table);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	lppTable = create_object<BRUTUS_IMAPITable_i, ::BRUTUS::IMAPITable, LPMAPITABLE>
		(mapi_table, poa_.in(), mapi_session_);

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetAttachmentTable()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::OpenAttach(::CORBA::ULong ulAttachmentNum,
						const char * lpInterface,
						::BRUTUS::BDEFINE ulFlags,
						::BRUTUS::IUnknown_out lppAttach)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::OpenAttach()");

	lppAttach = ::BRUTUS::IUnknown::_nil();

	unsigned long flags = native_flags(ulFlags);

	GUID *i_id;
	if (!strlen(lpInterface))
		i_id = NULL;
	else if (!guid_brutus_to_mapi(lpInterface, NULL, i_id)) {
		BRUTUS_LOG_BUG("Could not convert ::BRUTUS::GUID into MAPI GUID");
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	LPATTACH mapi_attach_object = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->OpenAttach(ulAttachmentNum,
					  i_id ? i_id : &IID_IAttachment,
					  flags,
					  &mapi_attach_object);
	}
	MAPIFreeBuffer(i_id);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	if (S_OK != hr) {
		BRUTUS_LOG_ERR("BRUTUS_IMessage_i::OpenAttach() failed");
		return br;
	}

	::BRUTUS::IUnknown_var obj;
	if (!create_brutus_object(strlen(lpInterface) ? lpInterface : ::BRUTUS::BRUTUS_IID_IAttachment, mapi_attach_object, poa_.in(), obj.out(), mapi_session_)) {
		BRUTUS_LOG_BUG("Could not create brutus object");
		mapi_attach_object->Release();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppAttach = obj._retn();

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::OpenAttach()");
	return br;
}


// Verify
::BRUTUS::BRESULT BRUTUS_IMessage_i::CreateAttach(const char * lpInterface,
						  ::BRUTUS::BDEFINE ulFlags,
						  ::CORBA::ULong_out lpulAttachmentNum,
						  ::BRUTUS::IUnknown_out lppAttach)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::CreateAttach()");

	lpulAttachmentNum = 0;
	lppAttach = ::BRUTUS::IUnknown::_nil();

	unsigned long flags = native_flags(ulFlags);

	GUID *i_id;
	if (!guid_brutus_to_mapi(lpInterface, NULL, i_id)) {
		BRUTUS_LOG_BUG("Could not convert ::BRUTUS::GUID into MAPI GUID");
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	unsigned long att_num;
	LPATTACH mapi_attach_object = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->CreateAttach(i_id,
					    flags,
					    &att_num,
					    &mapi_attach_object);
	}
	MAPIFreeBuffer(i_id);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	if (S_OK != hr) {
		BRUTUS_LOG_ERR("BRUTUS_IMessage_i::CreateAttach() failed");
		return br;
	}

	::BRUTUS::IUnknown_var obj;
	if (!create_brutus_object(lpInterface, mapi_attach_object, poa_.in(), obj.out(), mapi_session_)) {
		BRUTUS_LOG_BUG("Could not create brutus object");
		mapi_attach_object->Release();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppAttach = obj._retn();
	lpulAttachmentNum = att_num;

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::CreateAttach()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::DeleteAttach(::CORBA::ULong ulAttachmentNum,
						  ::BRUTUS::IMAPIProgress_ptr lpProgress,
						  ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::DeleteAttach()");

	ULONG flags = native_flags(ulFlags);

	CMAPIProgress *progress = NULL;
	if ((flags & ATTACH_DIALOG) || ::CORBA::is_nil(lpProgress)) {
		try {
			progress = new CMAPIProgress(lpProgress);
		}
		catch (std::bad_alloc &) {
			BRUTUS_LOG_ERR("No memory");
			throw ::CORBA::NO_MEMORY();
		}
	} else
		FLAGS_OFF(ULONG, flags, ATTACH_DIALOG);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->DeleteAttach(ulAttachmentNum,
					    0,
					    progress,
					    flags);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::DeleteAttach()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::GetRecipientTable(::BRUTUS::BDEFINE ulFlags,
						       ::BRUTUS::IMAPITable_out lppTable)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetRecipientTable()");

	lppTable = ::BRUTUS::IMAPITable::_nil();
	LPMAPITABLE mapi_table = NULL;

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetRecipientTable(flags, &mapi_table);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	lppTable = create_object<BRUTUS_IMAPITable_i, ::BRUTUS::IMAPITable, LPMAPITABLE>
		(mapi_table, poa_.in(), mapi_session_);

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetRecipientTable()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::ModifyRecipients(::BRUTUS::BDEFINE ulFlags,
						      const ::BRUTUS::ADRLIST & lpMods)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::ModifyRecipients()");

	unsigned long flags = native_flags(ulFlags);
	FLAGS_OFF(unsigned long , flags, MAPI_DIALOG);
	BRUTUS_LOG_INF("");

	ADRLIST *mapi_adr_list = NULL;
	if (!adr_list_brutus_to_mapi(&lpMods, mapi_adr_list)) {
		BRUTUS_LOG_ERR("Could not convert Brutus ADRLIST to MAPI ADRLIST");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->ModifyRecipients(flags, mapi_adr_list);
	}
	FreePadrlist(mapi_adr_list);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::ModifyRecipients()");
	return br;
}

static ::HRESULT get_default_message_store(LPMAPISESSION Session,
					   LPMDB *DefMsgStore)
{
	ULONG cRows = 0;
	LPSRowSet lpRows = NULL;
	LPENTRYID entry_id = NULL;
	ULONG entry_id_size = 0;
	ULONG i;

	// Columns to return from the table
	SizedSPropTagArray(2, prop_tags) = {2, {PR_DEFAULT_STORE, PR_ENTRYID} };

	// Get list of available message stores
	HRESULT hr;
	LPMAPITABLE mapi_table = NULL;
	hr = Session->GetMsgStoresTable(0, &mapi_table);
	if (S_OK != hr)
		goto exit;

	// Get row count
	hr = mapi_table->GetRowCount(0, &cRows);
	if ((S_OK != hr) || !cRows)
		goto exit;

	// Select columns to return from the table
	hr = mapi_table->SetColumns((LPSPropTagArray)&prop_tags, 0);
	if (S_OK != hr)
		goto exit;

	// Go to the beginning of the recipient table for the envelope
	hr = mapi_table->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
	if (S_OK != hr)
		goto exit;

	// Read all the rows of the table
	hr = mapi_table->QueryRows(cRows, 0, &lpRows);
	if ((S_OK == hr) && (lpRows != NULL) && (lpRows->cRows == 0))
		goto exit;

	mapi_table->Release();
	mapi_table = NULL;

	for (i = 0; i < cRows; i++) {
		// check PR_DEFAULT_STORE boolean
		if (lpRows && (TRUE == lpRows->aRow[i].lpProps[0].Value.b)) {
			entry_id = (LPENTRYID)lpRows->aRow[i].lpProps[1].Value.bin.lpb;
			entry_id_size = lpRows->aRow[i].lpProps[1].Value.bin.cb;
			break;
		}
	}

	ULONG flags = MAPI_BEST_ACCESS | MDB_NO_DIALOG | MDB_ONLINE;
	do {
		hr = Session->OpenMsgStore(0,
					   entry_id_size,
					   entry_id,
					   NULL,
					   flags,
					   DefMsgStore);
		if (S_OK == hr)
			break;

		if (MAPI_E_UNKNOWN_FLAGS != hr)
			break;

		flags &= ~MDB_ONLINE; // Exchange 5.5/2000 does not understand this flag
		hr = Session->OpenMsgStore(0,
					   entry_id_size,
					   entry_id,
					   NULL,
					   flags,
					   DefMsgStore);
	} while (false);

exit:
	FreeProws(lpRows);

	if (mapi_table)
		mapi_table->Release();

	return hr;
}

static ::HRESULT get_outlook_folder_entryid(::BRUTUS::OUTLOOK_FOLDER Which,
					    LPMDB msg_store,
					    LPSPropValue pv)
{
	BRUTUS_LOG_INF("Entering get_outlook_folder_entryid()");

	HRESULT hr;
	ULONG prop_tag = 0;
	ULONG obj_type;
	LPUNKNOWN unk = NULL;
	LPSPropValue pv_tmp = NULL;
	LPMAPIFOLDER root_folder = NULL;
	char *explicit_class = NULL;

	// create property tag
	switch (Which) {
	case ::BRUTUS::OUTLOOK_INBOX :
		break;
	case ::BRUTUS::OUTLOOK_FAVORITES :
		prop_tag = PR_IPM_FAVORITES_ENTRYID; // on all message stores
		break;
	case ::BRUTUS::OUTLOOK_ROOT_FOLDER :
		prop_tag = PR_IPM_SUBTREE_ENTRYID;   // on all message stores
		break;
	case ::BRUTUS::OUTLOOK_OUTBOX :
		prop_tag = PT_BINARY | (0x35E2<<16); // on private message store
		break;
	case ::BRUTUS::OUTLOOK_WASTEBASKET :
		prop_tag = PT_BINARY | (0x35E3<<16); // on private message store
		break;
	case ::BRUTUS::OUTLOOK_SENTMAIL :
		prop_tag = PT_BINARY | (0x35E4<<16); // on private message store
		break;
	case ::BRUTUS::OUTLOOK_VIEWS :
		prop_tag = PT_BINARY | (0x35E5<<16); // on private message store
		break;
	case ::BRUTUS::OUTLOOK_COMMON_VIEWS :
		prop_tag = PT_BINARY | (0x35E6<<16); // on private message store
		break;
	case ::BRUTUS::OUTLOOK_FINDER :
		prop_tag = PT_BINARY | (0x35E7<<16); // on private message store
		break;
	case ::BRUTUS::OUTLOOK_CALENDAR :
		prop_tag = PT_BINARY | (0x36D0<<16); // on root folder in private message store
		break;
	case ::BRUTUS::OUTLOOK_CONTACTS :
		prop_tag = PT_BINARY | (0x36D1<<16); // on root folder in private message store
		break;
	case ::BRUTUS::OUTLOOK_JOURNAL :
		prop_tag = PT_BINARY | (0x36D2<<16); // on root folder in private message store
		break;
	case ::BRUTUS::OUTLOOK_NOTES :
		prop_tag = PT_BINARY | (0x36D3<<16); // on root folder in private message store
		break;
	case ::BRUTUS::OUTLOOK_TASKS :
		prop_tag = PT_BINARY | (0x36D4<<16); // on root folder in private message store
		break;
	case ::BRUTUS::OUTLOOK_REMINDERS :
		prop_tag = PT_BINARY | (0x36D5<<16); // on root folder in private message store
		break;
	case ::BRUTUS::OUTLOOK_DRAFTS :
		prop_tag = PT_BINARY | (0x36D7<<16); // on root folder in private message store
		break;
	default :
		return MAPI_E_INVALID_PARAMETER;
	}

	// open outlook folder
	switch (Which) {
	case ::BRUTUS::OUTLOOK_INBOX :
	{
		hr = msg_store->GetReceiveFolder("IPM.Note",
						 0,
						 &pv->Value.bin.cb,
						 (LPENTRYID*)&pv->Value.bin.lpb,
						 &explicit_class);
		MAPIFreeBuffer(explicit_class);
		return hr;
	}
	break;
	case ::BRUTUS::OUTLOOK_FAVORITES :
	case ::BRUTUS::OUTLOOK_ROOT_FOLDER :
	case ::BRUTUS::OUTLOOK_OUTBOX :
	case ::BRUTUS::OUTLOOK_WASTEBASKET :
	case ::BRUTUS::OUTLOOK_SENTMAIL :
	case ::BRUTUS::OUTLOOK_VIEWS :
	case ::BRUTUS::OUTLOOK_COMMON_VIEWS :
	case ::BRUTUS::OUTLOOK_FINDER :
	{
		hr = HrGetOneProp(msg_store, prop_tag, &pv_tmp);
		if (S_OK != hr)
			return hr;

		pv->Value.bin.cb = pv_tmp->Value.bin.cb;
		hr = MAPIAllocateBuffer(pv->Value.bin.cb, (void**)&pv->Value.bin.lpb);
		if (S_OK != hr) {
			MAPIFreeBuffer(pv_tmp);
			return hr;
		}
		memcpy((void*)pv->Value.bin.lpb, pv_tmp->Value.bin.lpb, pv->Value.bin.cb);
		MAPIFreeBuffer(pv_tmp);

		return S_OK;
	}
	break;
	case ::BRUTUS::OUTLOOK_CALENDAR :
	case ::BRUTUS::OUTLOOK_CONTACTS :
	case ::BRUTUS::OUTLOOK_JOURNAL :
	case ::BRUTUS::OUTLOOK_NOTES :
	case ::BRUTUS::OUTLOOK_TASKS :
	case ::BRUTUS::OUTLOOK_REMINDERS :
	case ::BRUTUS::OUTLOOK_DRAFTS :
	{
		hr = msg_store->OpenEntry(0, 0, NULL, MAPI_BEST_ACCESS, &obj_type, &unk);
		if (S_OK != hr)
			return hr;

		root_folder = (LPMAPIFOLDER)unk;
		hr = HrGetOneProp(root_folder, prop_tag, &pv_tmp);
		unk->Release();
		if (S_OK != hr)
			return hr;

		pv->Value.bin.cb = pv_tmp->Value.bin.cb;
		hr = MAPIAllocateBuffer(pv->Value.bin.cb, (void**)&pv->Value.bin.lpb);
		if (S_OK != hr) {
			MAPIFreeBuffer(pv_tmp);
			return hr;
		}
		memcpy((void*)pv->Value.bin.lpb, pv_tmp->Value.bin.lpb, pv->Value.bin.cb);
		MAPIFreeBuffer(pv_tmp);

		return S_OK;
	}
	break;
	default :
		return E_FAIL;
	}

	return E_FAIL;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::SubmitMessage(::BRUTUS::BDEFINE ulFlags,
						   ::BRUTUS::OUTLOOK_FOLDER Fate)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::SubmitMessage()");

	::HRESULT hr = E_FAIL;
	LPMDB msg_store = NULL;

	if (::BRUTUS::OUTLOOK_NONE != Fate) {
		SPropValue *mapi_props = NULL;

		if (S_OK != MAPIAllocateBuffer(sizeof(::SPropValue),
					       (void**)&mapi_props)) {
			BRUTUS_LOG_ERR("No memory");
			throw ::CORBA::NO_MEMORY();
		}

		if (::BRUTUS::OUTLOOK_DEVNULL == Fate) {
			mapi_props[0].ulPropTag = PR_DELETE_AFTER_SUBMIT;
			mapi_props[0].Value.b = true;
		} else {
			mapi_props[0].ulPropTag = PR_SENTMAIL_ENTRYID;

			hr = get_default_message_store(mapi_session_, &msg_store);
			if ((S_OK != hr) && (MAPI_W_ERRORS_RETURNED != hr)) {
				MAPIFreeBuffer(mapi_props);
				BRUTUS_EXIT;
			}

			hr = get_outlook_folder_entryid(Fate, msg_store, &mapi_props[0]);
			if (msg_store) {
				msg_store->Release();
				msg_store = NULL;
			}
			if (S_OK != hr) {
				MAPIFreeBuffer(mapi_props);
				BRUTUS_EXIT;
			}
		}

		{
			ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
			hr = message_->SetProps(1, mapi_props, NULL);
			MAPIFreeBuffer(mapi_props);

			hr = message_->SaveChanges(KEEP_OPEN_READWRITE);
			if (S_OK != hr)
				BRUTUS_EXIT;
		}
	}

	unsigned long flags = native_flags(ulFlags);

	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->SubmitMessage(flags);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

exit:
	if (msg_store)
		msg_store->Release();

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::SubmitMessage()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::SetReadFlag(::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::SetReadFlag()");

	unsigned long flags = native_read_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->SetReadFlag(flags);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::SetReadFlag()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::GetLastError(::BRUTUS::BRESULT ReturnCode,
						  ::BRUTUS::BDEFINE ulFlags,
						  ::BRUTUS::MAPIERROR_out lppMAPIError)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetLastError()");

	::BRUTUS::MAPIERROR_var error;
	try {
		error = new ::BRUTUS::MAPIERROR;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	error->ulVersion = (::CORBA::ULong)0;
	error->ulLowLevelError = (::CORBA::ULong)0;
	error->ulContext = (::CORBA::ULong)0;

	HRESULT hr;
	if (!bresult_to_hresult(ReturnCode, hr)) {
		lppMAPIError = error._retn();
		BRUTUS_LOG_BUG("Could not convert BRESULT into HRESULT");
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	LPMAPIERROR mapi_error = NULL;
	unsigned long flags = native_flags(ulFlags);

	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetLastError(hr, flags, &mapi_error);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		MAPIFreeBuffer(mapi_error);
		lppMAPIError = error._retn();
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	if (!mapi_error)
		BRUTUS_LOG_INF("No applicable error information from MAPI");

	mapierror_mapi_to_brutus(mapi_error, error, true);

	lppMAPIError = error._retn();

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetLastError()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::SaveChanges(::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::SaveChanges()");

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->SaveChanges(flags);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::SaveChanges()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::GetProps(const ::BRUTUS::SPropTagArray& lpPropTagArray,
					      ::BRUTUS::BDEFINE ulFlags,
					      ::BRUTUS::seq_SPropValue_out lppPropArray)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetProps()");

	SPropTagArray *tags = NULL;
	proptag_array_brutus_to_mapi(&lpPropTagArray, 0, tags);
	unsigned long flags = native_flags(ulFlags);
	SPropValue *props = NULL;
	unsigned long count = 0;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetProps(tags, flags, &count, &props);
	}
	MAPIFreeBuffer(tags);

	::BRUTUS::seq_SPropValue_var brutus_props;
	try {
		brutus_props = new ::BRUTUS::seq_SPropValue;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_props->length(0);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		lppPropArray = brutus_props._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	spropvalue_array_mapi_to_brutus(count, props, brutus_props, mapi_session_, true, poa_);
	lppPropArray = brutus_props._retn();

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetProps()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::GetPropList(::BRUTUS::BDEFINE ulFlags,
						 ::BRUTUS::SPropTagArray_out lppPropTagArray)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetPropList()");

	SPropTagArray *tags = NULL;
	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetPropList(flags, &tags);
	}
	::BRUTUS::SPropTagArray_var brutus_tags;
	if (!proptag_array_mapi_to_brutus(tags, brutus_tags.out(), true)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	lppPropTagArray = brutus_tags._retn();

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetPropList()");
	return br;
}

// FIXME. Support for all possible interfaces are misssing
::BRUTUS::BRESULT BRUTUS_IMessage_i::OpenProperty(::CORBA::ULong ulPropTag,
						  const char * lpiid,
						  ::CORBA::ULong ulInterfaceOptions,
						  ::BRUTUS::BDEFINE ulFlags,
						  ::BRUTUS::IUnknown_out lppUnk)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::OpenProperty()");

	lppUnk = ::BRUTUS::IUnknown::_nil();

	unsigned long tag;
	proptag_brutus_to_mapi(ulPropTag, tag);

	GUID id;
	if (!guid_brutus_to_mapi_no_alloc(lpiid, &id)) {
		BRUTUS_LOG_BUG("Could not convert ::BRUTUS::GUID into MAPI GUID");
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	unsigned long options = (unsigned long)ulInterfaceOptions;
	unsigned long flags = native_flags(ulFlags);

	LPUNKNOWN unknown = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->OpenProperty(tag, &id, options, flags, &unknown);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	if (S_OK != hr)
		return br;

	bool create_result = false;
	try {
		if (S_OK == hr)
			create_result = create_brutus_object(lpiid, unknown, poa_, lppUnk, mapi_session_);
		else if (unknown) {
			unknown->Release();
			unknown = NULL;
		}
	}
	catch (...) {
		BRUTUS_LOG_BUG("Exception caught");
	}

	if (!create_result) {
		BRUTUS_LOG_BUG("Could not convert MAPI object into BRUTUS object");
		if (unknown)
			unknown->Release();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::OpenProperty()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::SetProps(const ::BRUTUS::seq_SPropValue & lpPropArray,
					      ::CORBA::Boolean ProblemInfo,
					      ::BRUTUS::SPropProblemArray_out lppProblems)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::SetProps()");

	unsigned long count = 0;
	SPropValue *mapi_props = NULL;
	::BRUTUS::SPropProblemArray_var brutus_problems;
	try {
		brutus_problems = new ::BRUTUS::SPropProblemArray;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_problems->length(0);

	if (!spropvalue_array_brutus_to_mapi(&lpPropArray, 0, count, mapi_props)) {
		MAPIFreeBuffer(mapi_props);
		BRUTUS_LOG_ERR("Could not convert Brutus SPropValue to MAPI SPropValue");
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	HRESULT hr;
	LPSPropProblemArray mapi_problems = NULL;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->SetProps(count, mapi_props, (ProblemInfo ? &mapi_problems : NULL));
	}
	MAPIFreeBuffer(mapi_props);

	if (!sPropProblem_array_mapi_to_brutus(mapi_problems, brutus_problems.inout(), true)) {
		BRUTUS_LOG_ERR("Could not convert MAPI SPropProblemArray to Brutus SPropProblemArray");
		brutus_problems->length(0);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppProblems = brutus_problems._retn();

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::SetProps()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::DeleteProps(const ::BRUTUS::SPropTagArray & lpPropTagArray,
						 ::CORBA::Boolean ProblemInfo,
						 ::BRUTUS::SPropProblemArray_out lppProblems)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::DeleteProps()");

	::BRUTUS::SPropProblemArray_var brutus_problems;
	try {
		brutus_problems = new ::BRUTUS::SPropProblemArray;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_problems->length(0);

	SPropTagArray *tags = NULL;
	if (!proptag_array_brutus_to_mapi(&lpPropTagArray, 0, tags)) {
		BRUTUS_LOG_ERR("Could not convert Brutus SPropTagArray to MAPI SPropTagArray");
		MAPIFreeBuffer(tags);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	HRESULT hr;
	LPSPropProblemArray mapi_problems = NULL;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->DeleteProps(tags, (ProblemInfo ? &mapi_problems : NULL));
	}
	MAPIFreeBuffer(tags);

	if (!sPropProblem_array_mapi_to_brutus(mapi_problems, brutus_problems.inout(), true)) {
		BRUTUS_LOG_ERR("Could not convert MAPI SPropProblemArray to Brutus SPropProblemArray");
		brutus_problems->length(0);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppProblems = brutus_problems._retn();

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::DeleteProps()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::CopyTo(const ::BRUTUS::seq_GUID & rgiidExclude,
					    const ::BRUTUS::SPropTagArray & lpExcludeProps,
					    ::BRUTUS::IMAPIProgress_ptr lpProgress,
					    const char * lpInterface,
					    const ::BRUTUS::ENTRYID & lpDestObj,
					    ::BRUTUS::BDEFINE ulFlags,
					    ::CORBA::Boolean ProblemInfo,
					    ::BRUTUS::SPropProblemArray_out lppProblems)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::CopyTo()");

	::BRUTUS::SPropProblemArray_var brutus_problems;
	try {
		brutus_problems = new ::BRUTUS::SPropProblemArray;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_problems->length(0);

	GUID *guids = NULL;
	unsigned long count = rgiidExclude.length();
	if (count) {
		if (!guid_array_brutus_to_mapi(&rgiidExclude, 0, guids)) {
			BRUTUS_LOG_ERR("Could not convert Brutus GUID to MAPI GUID");
			lppProblems = brutus_problems._retn();
			return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
	}

	SPropTagArray *exc_tags = NULL;
	if (lpExcludeProps.length()) {
		if (!proptag_array_brutus_to_mapi(&lpExcludeProps, 0, exc_tags)) {
			BRUTUS_LOG_ERR("Could not convert Brutus SPropTagArray to MAPI SPropTagArray");
			MAPIFreeBuffer(guids);
			lppProblems = brutus_problems._retn();
			return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
	}
	ULONG flags = native_flags(ulFlags);

	CMAPIProgress *progress = NULL;
	if ((flags & MAPI_DIALOG) && !::CORBA::is_nil(lpProgress)) {
		try {
			progress = new CMAPIProgress(lpProgress);
		}
		catch (std::bad_alloc &) {
			BRUTUS_LOG_ERR("No memory");
			MAPIFreeBuffer(exc_tags);
			MAPIFreeBuffer(guids);
			lppProblems = brutus_problems._retn();
			return ::BRUTUS::BRUTUS_MAPI_E_NOT_ENOUGH_MEMORY;
		}
	} else
		FLAGS_OFF(ULONG, flags, MAPI_DIALOG);

	GUID i_id;
	if (!guid_brutus_to_mapi_no_alloc(lpInterface, &i_id)) {
		BRUTUS_LOG_BUG("Could not convert ::BRUTUS::GUID into GUID");
		if (progress)
			progress->Release();
		MAPIFreeBuffer(exc_tags);
		MAPIFreeBuffer(guids);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	ENTRYID *entry_id = NULL;
	unsigned long entry_id_size;
	if (!entryid_brutus_to_mapi(&lpDestObj, 0, entry_id_size, entry_id)) {
		BRUTUS_LOG_ERR("No memory");
		if (progress)
			progress->Release();
		MAPIFreeBuffer(exc_tags);
		MAPIFreeBuffer(guids);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_MAPI_E_NOT_ENOUGH_MEMORY;
	}

	LPUNKNOWN unk_dest_object = NULL;
	HRESULT hr;
	{
		unsigned long obj_type;
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_session_->OpenEntry(entry_id_size,
					      entry_id,
					      &i_id,
					      MAPI_MODIFY,
					      &obj_type,
					      &unk_dest_object);
	}
	::BRUTUS::BRESULT br;
	if (hr != S_OK) {
		if (!hresult_to_bresult(hr, br)) {
			BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
			br = ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
		BRUTUS_LOG_BUG("Error opening object");
		if (progress)
			progress->Release();
		if (unk_dest_object)
			unk_dest_object->Release();

		MAPIFreeBuffer(exc_tags);
		MAPIFreeBuffer(guids);
		lppProblems = brutus_problems._retn();
		return br;
	}

	LPSPropProblemArray mapi_problems = NULL;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->CopyTo(count,
				      guids,
				      exc_tags,
				      0, // UI handle
				      progress,
				      &i_id,
				      unk_dest_object,
				      flags,
				      (ProblemInfo ? &mapi_problems : NULL));
	}
	if (progress)
		progress->Release();
	if (unk_dest_object)
		unk_dest_object->Release();
	MAPIFreeBuffer(exc_tags);
	MAPIFreeBuffer(guids);

	if (!sPropProblem_array_mapi_to_brutus(mapi_problems, brutus_problems.inout(), true)) {
		BRUTUS_LOG_ERR("Could not convert MAPI SPropProblemArray to Brutus SPropProblemArray");
		brutus_problems->length(0);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppProblems = brutus_problems._retn();

	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::CopyTo()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::CopyProps(const ::BRUTUS::SPropTagArray & lpIncludeProps,
					       ::BRUTUS::IMAPIProgress_ptr lpProgress,
					       const char * lpInterface,
					       const ::BRUTUS::ENTRYID &lpDestObj,
					       ::BRUTUS::BDEFINE ulFlags,
					       ::CORBA::Boolean ProblemInfo,
					       ::BRUTUS::SPropProblemArray_out lppProblems)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::CopyProps()");

	::BRUTUS::SPropProblemArray_var brutus_problems;
	try {
		brutus_problems = new ::BRUTUS::SPropProblemArray;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_problems->length(0);

	GUID i_id;
	if (!guid_brutus_to_mapi_no_alloc(lpInterface, &i_id)) {
		BRUTUS_LOG_BUG("Could not convert ::BRUTUS::GUID into GUID");
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	SPropTagArray *inc_tags = NULL;
	if (lpIncludeProps.length()) {
		if (!proptag_array_brutus_to_mapi(&lpIncludeProps, 0, inc_tags)) {
			BRUTUS_LOG_ERR("Could not convert Brutus SPropTagArray to MAPI SPropTagArray");
			lppProblems = brutus_problems._retn();
			return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
	}

	ULONG flags = native_flags(ulFlags);

	CMAPIProgress *progress = NULL;
	if ((flags & MAPI_DIALOG) && !::CORBA::is_nil(lpProgress)) {
		try {
			progress = new CMAPIProgress(lpProgress);
		}
		catch (...) {
			BRUTUS_LOG_ERR("No memory");
			MAPIFreeBuffer(inc_tags);
			lppProblems = brutus_problems._retn();
			return ::BRUTUS::BRUTUS_MAPI_E_NOT_ENOUGH_MEMORY;
		}
	} else
		FLAGS_OFF(ULONG, flags, MAPI_DIALOG);

	ENTRYID *entry_id = NULL;
	unsigned long entry_id_size;
	if (!entryid_brutus_to_mapi(&lpDestObj, 0, entry_id_size, entry_id)) {
		BRUTUS_LOG_ERR("No memory");
		MAPIFreeBuffer(inc_tags);
		if (progress)
			progress->Release();
		throw ::CORBA::NO_MEMORY();
	}

	LPUNKNOWN unk_dest_object = NULL;
	HRESULT hr;
	{
		unsigned long obj_type;
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_session_->OpenEntry(entry_id_size,
					      entry_id,
					      &i_id,
					      MAPI_MODIFY,
					      &obj_type,
					      &unk_dest_object);
	}
	::BRUTUS::BRESULT br;
	if (hr != S_OK) {
		if (!hresult_to_bresult(hr, br)) {
			BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
			br = ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
		BRUTUS_LOG_BUG("Error opening object");
		if (progress)
			progress->Release();
		if (unk_dest_object)
			unk_dest_object->Release();

		MAPIFreeBuffer(inc_tags);
		lppProblems = brutus_problems._retn();

		return br;
	}

	LPSPropProblemArray mapi_problems = NULL;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->CopyProps(inc_tags,
					 0, // UI handle
					 progress,
					 &i_id,
					 unk_dest_object,
					 flags,
					 (ProblemInfo ? &mapi_problems : NULL));
	}
	MAPIFreeBuffer(inc_tags);
	if (progress)
		progress->Release();
	if (unk_dest_object)
		unk_dest_object->Release();

	if (!sPropProblem_array_mapi_to_brutus(mapi_problems, brutus_problems.inout(), true)) {
		BRUTUS_LOG_ERR("Could not convert MAPI SPropProblemArray to Brutus SPropProblemArray");
		brutus_problems->length(0);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppProblems = brutus_problems._retn();

	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::CopyProps()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::GetNamesFromIDs(::BRUTUS::SPropTagArray & lppPropTags,
						     const char * lpPropSetGuid,
						     ::BRUTUS::BDEFINE ulFlags,
						     ::BRUTUS::seq_MAPINAMEID_out lpppPropNames)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetNamesFromIDs()");

	::BRUTUS::seq_MAPINAMEID_var brutus_names;
	try {
		brutus_names = new ::BRUTUS::seq_MAPINAMEID;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_names->length(0);

	SPropTagArray *tags = NULL;
	if (lppPropTags.length())
		proptag_array_brutus_to_mapi(&lppPropTags, 0, tags);

	GUID *i_id;
	if (!guid_brutus_to_mapi(lpPropSetGuid, NULL, i_id)) {
		BRUTUS_LOG_BUG("Could not convert ::BRUTUS::GUID into GUID");
		MAPIFreeBuffer(tags);
		lpppPropNames = brutus_names._retn();
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	unsigned long flags = native_flags(ulFlags);

	unsigned long count = 0;
	MAPINAMEID **mapi_names = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetNamesFromIDs(&tags, i_id, flags, &count, &mapi_names);
	}
	MAPIFreeBuffer(i_id);

	if (!proptag_array_mapi_to_brutus(tags, lppPropTags, true)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	nameid_array_mapi_to_brutus(count, mapi_names, brutus_names, true);
	lpppPropNames = brutus_names._retn();

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetNamesFromIDs()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMessage_i::GetIDsFromNames(const ::BRUTUS::seq_MAPINAMEID & lppPropNames,
						     ::BRUTUS::BDEFINE ulFlags,
						     ::BRUTUS::SPropTagArray_out lppPropTags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetIDsFromNames()");

	unsigned long count = lppPropNames.length();
	::LPMAPINAMEID *mapi_names = NULL;
	nameid_array_brutus_to_mapi(&lppPropNames, 0, mapi_names);

	unsigned long flags = native_flags(ulFlags);

	SPropTagArray *tags = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetIDsFromNames(count,
					       mapi_names,
					       flags,
					       &tags);
	}
	MAPIFreeBuffer(mapi_names);

	::BRUTUS::SPropTagArray_var brutus_tags;
	if (!proptag_array_mapi_to_brutus(tags, brutus_tags.out(), true)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	lppPropTags = brutus_tags._retn();

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetIDsFromNames()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IMessage_i::GetNamedProps(const char *PropSetGuid,
				 const ::BRUTUS::seq_NamedPropertyTag & lpPropTagArray,
				 ::BRUTUS::BDEFINE ulFlags,
				 ::BRUTUS::seq_SPropValue_out lppPropArray)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetNamedProps()");

	unsigned long flags = 0;
	SPropValue *props = NULL;
	unsigned long count = 0;

	::BRUTUS::seq_SPropValue_var brutus_props;
	try {
		brutus_props = new ::BRUTUS::seq_SPropValue;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_props->length(0);

	GUID mapi_propset_guid;
	if (!guid_brutus_to_mapi_no_alloc(PropSetGuid, &mapi_propset_guid, false)) {
		lppPropArray = brutus_props._retn();
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	::LPMAPINAMEID *mapi_names = NULL;
	if (S_OK != MAPIAllocateBuffer(lpPropTagArray.length()*sizeof(::MAPINAMEID*),
				       (void**)&mapi_names)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	ULONG n = 0;
	for (n = 0; n < lpPropTagArray.length(); n++) {
		if (S_OK != MAPIAllocateMore(sizeof(::MAPINAMEID),
					     mapi_names,
					     (void**)&(mapi_names[n]))) {
			BRUTUS_LOG_ERR("No memory");
			MAPIFreeBuffer(mapi_names);
			throw ::CORBA::NO_MEMORY();
		}
	}

	for (n = 0; n < lpPropTagArray.length(); n++) {
		mapi_names[n]->lpguid = (LPGUID)&mapi_propset_guid;
		switch (lpPropTagArray[n].name._d()) {
		case ::BRUTUS::BRUTUS_ID_INTEGER :
			mapi_names[n]->ulKind = MNID_ID;
			mapi_names[n]->Kind.lID = lpPropTagArray[n].name.int_name();
			break;
		case ::BRUTUS::BRUTUS_ID_STRING :
			mapi_names[n]->ulKind = MNID_STRING;
			mapi_names[n]->Kind.lpwstrName = ascii_to_unicode((void*)mapi_names, (const char*)lpPropTagArray[n].name.str_name());
			if (!mapi_names[n]->Kind.lpwstrName) {
				BRUTUS_LOG_ERR("No memory");
				MAPIFreeBuffer(mapi_names);
				throw ::CORBA::NO_MEMORY();
			}
			break;
		default :
			BRUTUS_LOG_BUG("Unknown name kind");
			lppPropArray = brutus_props._retn();
			MAPIFreeBuffer(mapi_names);
			return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
		}
	}

	SPropTagArray *tags = NULL;
	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetIDsFromNames(lpPropTagArray.length(),
					       mapi_names,
					       MAPI_CREATE,
					       &tags);
	}
	MAPIFreeBuffer(mapi_names);

	if (!tags || (tags->cValues != lpPropTagArray.length())) {
		BRUTUS_LOG_ERR("Could not get PropTag ID from name");
		BRUTUS_EXIT;
	}
	if ((MAPI_W_ERRORS_RETURNED != hr) && (S_OK != hr))
		BRUTUS_EXIT;

	ULONG prop_id = 0;
	ULONG prop_type = 0;
	for (n = 0; n < tags->cValues; n++) {
		prop_id = PROP_ID(tags->aulPropTag[n]);
		prop_type = proptype_brutus_to_mapi(lpPropTagArray[n].type);
		tags->aulPropTag[n] = PROP_TAG(prop_type, prop_id);
	}

	flags = native_flags(ulFlags);
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetProps(tags, flags, &count, &props);
	}

exit:
	MAPIFreeBuffer(tags);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		lppPropArray = brutus_props._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	spropvalue_array_mapi_to_brutus(count, props, brutus_props, mapi_session_, true, poa_);
	lppPropArray = brutus_props._retn();

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetNamedProps()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IMessage_i::SetNamedProps(const char *PropSetGuid,
				 const ::BRUTUS::seq_NamedPropertyTag & lpPropTagArray,
				 const ::BRUTUS::seq_SPropValue & lpPropArray,
				 ::CORBA::Boolean ProblemInfo,
				 ::BRUTUS::SPropProblemArray_out lppProblems)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::SetNamedProps()");

	unsigned long count = 0;
	SPropValue *mapi_props = NULL;
	::BRUTUS::SPropProblemArray_var brutus_problems;
	try {
		brutus_problems = new ::BRUTUS::SPropProblemArray;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_problems->length(0);

	if (!lpPropTagArray.length() || !lpPropArray.length()) {
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	if (!spropvalue_array_brutus_to_mapi(&lpPropArray, 0, count, mapi_props)) {
		MAPIFreeBuffer(mapi_props);
		BRUTUS_LOG_ERR("Could not convert Brutus SPropValue to MAPI SPropValue");
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	GUID mapi_propset_guid;
	if (!guid_brutus_to_mapi_no_alloc(PropSetGuid, &mapi_propset_guid, false)) {
		MAPIFreeBuffer(mapi_props);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	::LPMAPINAMEID *mapi_names = NULL;
	if (S_OK != MAPIAllocateBuffer(lpPropArray.length() * sizeof(::LPMAPINAMEID),
				       (void**)&mapi_names)) {
		MAPIFreeBuffer(mapi_props);
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	HRESULT hr;
	ULONG n = 0;
	void *buf = NULL;
	for (n = 0; n < lpPropArray.length(); n++) {
		BRUTUS_LOG_INF("");
		try {
			hr = MAPIAllocateMore(sizeof(::MAPINAMEID), (void*)mapi_names, &buf);
		}
		catch (...) {
			BRUTUS_LOG_ERR("*** EXCEPTION ***");
			MAPIFreeBuffer(mapi_names);
			throw ::CORBA::NO_MEMORY();
		}
		if (S_OK == hr) {
			mapi_names[n] = (::MAPINAMEID*)buf;
		} else {
			BRUTUS_LOG_ERR(hresult_to_str(hr));
			MAPIFreeBuffer(mapi_names);
			return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
	}

	{
		char msg[128] = {0};
		sprintf_s(msg, sizeof(msg), "lpPropTagArray.length() = %d", lpPropTagArray.length());
		BRUTUS_LOG_INF(msg);
	}
	for (n = 0; n < lpPropTagArray.length(); n++) {
		mapi_names[n]->lpguid = (LPGUID)&mapi_propset_guid;
		switch (lpPropTagArray[n].name._d()) {
		case ::BRUTUS::BRUTUS_ID_INTEGER :
			mapi_names[n]->ulKind = MNID_ID;
			mapi_names[n]->Kind.lID = lpPropTagArray[n].name.int_name();
			break;
		case ::BRUTUS::BRUTUS_ID_STRING :
			mapi_names[n]->ulKind = MNID_STRING;
			mapi_names[n]->Kind.lpwstrName = ascii_to_unicode((void*)mapi_names, (const char*)lpPropTagArray[n].name.str_name());
			if (!mapi_names[n]->Kind.lpwstrName) {
				BRUTUS_LOG_ERR("No memory");
				MAPIFreeBuffer(mapi_names);
				throw ::CORBA::NO_MEMORY();
			}
			break;
		default :
			BRUTUS_LOG_BUG("Unknown name kind");
			MAPIFreeBuffer(mapi_props);
			MAPIFreeBuffer(mapi_names);

			lppProblems = brutus_problems._retn();
			return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
		}
	}

	SPropTagArray *tags = NULL;
	LPSPropProblemArray mapi_problems = NULL;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetIDsFromNames(lpPropTagArray.length(),
					       mapi_names,
					       MAPI_CREATE,
					       &tags);
	}
	MAPIFreeBuffer(mapi_names);

	if (!tags || (tags->cValues != lpPropArray.length())) {
		BRUTUS_LOG_ERR("Could not get PropTag ID from name");
		BRUTUS_EXIT;
	}
	if ((MAPI_W_ERRORS_RETURNED != hr) && (S_OK != hr))
		BRUTUS_EXIT;

	ULONG prop_id = 0;
	ULONG prop_type = 0;
	for (n = 0; n < tags->cValues; n++) {
		prop_id = PROP_ID(tags->aulPropTag[n]);
		prop_type = proptype_brutus_to_mapi(lpPropTagArray[n].type);
		mapi_props[n].ulPropTag = PROP_TAG(prop_type, prop_id);
	}

	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->SetProps(lpPropArray.length(), mapi_props, (ProblemInfo ? &mapi_problems : NULL));
	}
exit:
	MAPIFreeBuffer(mapi_props);
	MAPIFreeBuffer(tags);

	if (!sPropProblem_array_mapi_to_brutus(mapi_problems, brutus_problems.inout(), true)) {
		BRUTUS_LOG_ERR("Could not convert MAPI SPropProblemArray to Brutus SPropProblemArray");
		brutus_problems->length(0);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppProblems = brutus_problems._retn();

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::SetNamedProps()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::SetRFC822Headers(const ::BRUTUS::seq_string & FieldNames,
						      const ::BRUTUS::seq_string & FieldBodies)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::SetRFC822Header()");

	static const GUID PS_RFC822_HEADER = {0x00020386, 0x0000, 0x0000, {0xC0, 0x00, 0x0, 0x00, 0x00, 0x00, 0x00, 0x046}};

	if (FieldNames.length() != FieldBodies.length())
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;

	if (!FieldNames.length())
		return ::BRUTUS::BRUTUS_S_OK;

	::SPropValue *mapi_props = NULL;
	if (S_OK != MAPIAllocateBuffer(FieldNames.length()*sizeof(::SPropValue),
				       (void**)&mapi_props)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	::LPMAPINAMEID *mapi_names = NULL;
	if (S_OK != MAPIAllocateBuffer(FieldNames.length()*sizeof(::MAPINAMEID*),
				       (void**)&mapi_names)) {
		BRUTUS_LOG_ERR("No memory");
		MAPIFreeBuffer(mapi_props);
		throw ::CORBA::NO_MEMORY();
	}

	ULONG n = 0;
	for (n = 0; n < FieldNames.length(); n++) {
		if (S_OK != MAPIAllocateMore(sizeof(::MAPINAMEID),
					     mapi_names,
					     (void**)&(mapi_names[n]))) {
			BRUTUS_LOG_ERR("No memory");
			MAPIFreeBuffer(mapi_props);
			MAPIFreeBuffer(mapi_names);
			throw ::CORBA::NO_MEMORY();
		}
	}
	for (n = 0; n < FieldNames.length(); n++) {
		mapi_names[n]->lpguid = (LPGUID)&PS_RFC822_HEADER;
		mapi_names[n]->ulKind = MNID_STRING;
		mapi_names[n]->Kind.lpwstrName = ascii_to_unicode((void*)mapi_names, (const char*)FieldNames[n]);
		if (!mapi_names[n]->Kind.lpwstrName) {
			BRUTUS_LOG_ERR("No memory");
			MAPIFreeBuffer(mapi_props);
			MAPIFreeBuffer(mapi_names);
			throw ::CORBA::NO_MEMORY();
		}
	}

	SPropTagArray *tags = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetIDsFromNames(FieldNames.length(),
					       mapi_names,
					       MAPI_CREATE,
					       &tags);
	}
	MAPIFreeBuffer(mapi_names);

	if ((MAPI_W_ERRORS_RETURNED != hr) && (S_OK != hr))
		BRUTUS_EXIT;

	ULONG prop_id = 0;
	for (n = 0; n < FieldNames.length(); n++) {
		prop_id = (tags->aulPropTag[n]>>16);
		if (!prop_id) {
			BRUTUS_LOG_BUG("Could not get property ID");
			mapi_props[n].ulPropTag = 0;
			mapi_props[n].Value.lpszA = NULL;
			continue;
		}
		mapi_props[n].ulPropTag = PROP_TAG(PT_STRING8, prop_id);
		mapi_props[n].Value.lpszA = (LPSTR)((const char*)FieldBodies[n]);
	}

	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->SetProps(FieldNames.length(), mapi_props, NULL);
	}

exit:
	MAPIFreeBuffer(mapi_props);
	MAPIFreeBuffer(tags);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::SetRFC822Header()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::GetLocalPointer(::CORBA::ULongLong_out Pointer)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetLocalPointer()");

	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		message_->AddRef();
		Pointer = (::CORBA::ULongLong)message_;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetLocalPointer()");
	return ::BRUTUS::BRUTUS_S_OK;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::GetRecurrenceState(::CORBA::Boolean is_event,
							::BRUTUS::RecurrenceState_out state)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetRecurrenceState()");

	::BRUTUS::RecurrenceState_var recur_state;
	try {
		recur_state = new ::BRUTUS::RecurrenceState;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	if (!recurrence_proptag_) {
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		recurrence_proptag_ = get_recurrence_proptag((is_event ? true : false), message_);
	}
	if (!recurrence_proptag_) {
		BRUTUS_LOG_ERR("Could not get recurrence property tag");
		state = recur_state._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	SizedSPropTagArray(1, tags) = {1, {recurrence_proptag_}};

	LPSPropValue props = NULL;
	unsigned long count = 0;
	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetProps((LPSPropTagArray)&tags, 0, &count, &props);
	}
	if (S_OK != hr)
		BRUTUS_EXIT;
	else {
		try {
			MAPIRecurrence mapi_recurrence;
			::BRUTUS::RecurrencePattern_var pattern;

			if (!mapi_recurrence.read(&props[0].Value.bin)) {
				BRUTUS_LOG_INF("Error decoding recurrence binary data");
				recur_state->pattern._d(::BRUTUS::recur_none);
				goto exit;
			}

			recur_state->occurrence_count = mapi_recurrence.get_occurrence_count();
			recur_state->exception_count = mapi_recurrence.get_exception_count();
			recur_state->modification_count = mapi_recurrence.get_modification_count();
			recur_state->how_to_end = mapi_recurrence.get_termination_type();
			recur_state->first_day_of_week = mapi_recurrence.get_first_day_of_week();

			try {
				pattern = new ::BRUTUS::RecurrencePattern;
			}
			catch (std::bad_alloc &) {
				throw ::CORBA::NO_MEMORY();
			}
			mapi_recurrence.get_pattern(pattern.out());
			recur_state->pattern = pattern._retn();
		}
		catch (invalid_index &) {
			BRUTUS_LOG_INF("*** INVALID INDEX EXCEPTION ***");
		}
		catch (invalid_operation &) {
			BRUTUS_LOG_INF("*** INVALID OPERATION EXCEPTION ***");
		}
		catch (...) {
			BRUTUS_LOG_INF("*** EXCEPTION ***");
		}
	}

exit:
	state = recur_state._retn();

	MAPIFreeBuffer(props);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetRecurrenceState()");

	return br;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::GetRecurrenceExceptions(::CORBA::Boolean is_event,
							     const ::BRUTUS::FILETIME & start,
							     ::BRUTUS::OccurrenceTimeType time_format,
							     ::BRUTUS::seq_RecurrenceExceptionData_out exception_data_set)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::GetRecurrenceExceptions()");

	::BRUTUS::seq_RecurrenceExceptionData_var data_set;
	try {
		data_set = new ::BRUTUS::seq_RecurrenceExceptionData;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	data_set->length(0);

	if (!recurrence_proptag_) {
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		recurrence_proptag_ = get_recurrence_proptag((is_event ? true : false), message_);
	}
	if (!recurrence_proptag_) {
		BRUTUS_LOG_ERR("Could not get recurrence property tag");
		exception_data_set = data_set._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	SizedSPropTagArray(1, tags) = {1, {recurrence_proptag_}};

	LPSPropValue props = NULL;
	unsigned long count = 0;
	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->GetProps((LPSPropTagArray)&tags, 0, &count, &props);
	}
	if (S_OK != hr)
		BRUTUS_EXIT;
	else {
		try {
			FILETIME ft;
			filetime_brutus_to_mapi_no_alloc(&start, ft);

			MAPIRecurrence mapi_recurrence;
			if (!mapi_recurrence.read(&(props[0].Value.bin))) {
				BRUTUS_LOG_ERR("Could not decode recurrence data");
				exception_data_set = data_set._retn();
				MAPIFreeBuffer(props);
				return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
			}
			mapi_recurrence.get_exception_set((is_event ? true :  false), time_format, &ft, data_set.out());
		}
		catch (invalid_index &) {
			BRUTUS_LOG_INF("*** INVALID INDEX EXCEPTION ***");
		}
		catch (invalid_operation &) {
			BRUTUS_LOG_INF("*** INVALID OPERATION EXCEPTION ***");
		}
		catch (...) {
			BRUTUS_LOG_INF("*** EXCEPTION ***");
		}
	}

exit:
	exception_data_set = data_set._retn();

	MAPIFreeBuffer(props);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::GetRecurrenceExceptions()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::SetRecurrencePattern(::CORBA::Boolean is_event,
							  const ::BRUTUS::FILETIME & start,
							  const ::BRUTUS::OccurrenceTime & end,
							  ::CORBA::ULong duration,
							  ::BRUTUS::DayOfWeek first_day_of_week,
							  ::BRUTUS::RecurrenceTerminationType how_to_end,
							  const ::BRUTUS::RecurrencePattern & pattern)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::SetRecurrencePattern()");

	if (!recurrence_proptag_) {
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		recurrence_proptag_ = get_recurrence_proptag((is_event ? true : false), message_);
	}
	if (!recurrence_proptag_) {
		BRUTUS_LOG_ERR("Could not get recurrence property tag");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	MAPIRecurrence mapi_recurrence;
	if (!mapi_recurrence.set_pattern(is_event,
					 start,
					 end,
					 duration,
					 first_day_of_week,
					 how_to_end,
					 pattern)) {
		BRUTUS_LOG_ERR("Could not encode pattern data");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	SPropValue *mapi_props = NULL;
	if (S_OK != MAPIAllocateBuffer(sizeof(::SPropValue),
				       (void**)&mapi_props)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	mapi_props[0].ulPropTag = recurrence_proptag_;
	mapi_recurrence.write((void*)mapi_props, &(mapi_props[0].Value.bin));

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);

		hr = message_->SetProps(1, mapi_props, NULL);
		MAPIFreeBuffer(mapi_props);

		if (S_OK == hr)
			hr = message_->SaveChanges(KEEP_OPEN_READWRITE);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::SetRecurrencePattern()");

	return br;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::SetRecurrenceException(::CORBA::Boolean is_event,
							    const ::BRUTUS::FILETIME & start,
							    ::CORBA::ULong duration,
							    const ::BRUTUS::seq_RecurrenceExceptionData & exception_data_set)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::SetRecurrenceException()");

	if (!recurrence_proptag_) {
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		recurrence_proptag_ = get_recurrence_proptag(is_event ? true : false, message_);
	}
	if (!recurrence_proptag_) {
		BRUTUS_LOG_ERR("Could not get recurrence property tag");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	::BRUTUS::RecurrenceState_var recur_state;
	GetRecurrenceState(is_event, recur_state.out());

	::BRUTUS::seq_RecurrenceExceptionData_var data_set;
	GetRecurrenceExceptions(is_event, start, ::BRUTUS::tt_BaseDate, data_set.out());

	::BRUTUS::OccurrenceTime ot_end;
	ot_end._d(::BRUTUS::tt_oID);
	if (::BRUTUS::term_never == recur_state->how_to_end)
		ot_end.oID(10);
	else
		ot_end.oID(recur_state->occurrence_count);

	MAPIRecurrence mapi_recurrence;
	if (!mapi_recurrence.set_pattern(is_event,
					 start,
					 ot_end,
					 duration,
					 recur_state->first_day_of_week,
					 recur_state->how_to_end,
					 recur_state->pattern)) {
		BRUTUS_LOG_ERR("Could not encode pattern data");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	::CORBA::ULong n;
	for (n = 0; n < data_set->length(); n++)
		mapi_recurrence.set_exception(data_set[n]);
	for (n = 0; n < exception_data_set.length(); n++)
		mapi_recurrence.set_exception(exception_data_set[n]);

	SPropValue *mapi_props = NULL;
	if (S_OK != MAPIAllocateBuffer(sizeof(::SPropValue),
				       (void**)&mapi_props)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	mapi_props[0].ulPropTag = recurrence_proptag_;
	mapi_recurrence.write((void*)mapi_props, &(mapi_props[0].Value.bin));

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);

		hr = message_->SetProps(1, mapi_props, NULL);
		MAPIFreeBuffer(mapi_props);

		if (S_OK == hr)
			hr = message_->SaveChanges(KEEP_OPEN_READWRITE);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::SetRecurrenceException()");

	return ::BRUTUS::BRUTUS_S_OK;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::RemoveRecurrenceException(::CORBA::Boolean is_event,
							       const ::BRUTUS::FILETIME & start,
							       ::CORBA::ULong duration,
							       const ::BRUTUS::OccurrenceTime & oc_time)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::RemoveRecurrenceException()");

	if (!recurrence_proptag_) {
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		recurrence_proptag_ = get_recurrence_proptag(is_event ? true : false, message_);
	}
	if (!recurrence_proptag_) {
		BRUTUS_LOG_ERR("Could not get recurrence property tag");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}


	::BRUTUS::RecurrenceState_var recur_state;
	GetRecurrenceState(is_event, recur_state.out());

	::BRUTUS::seq_RecurrenceExceptionData_var data_set;
	GetRecurrenceExceptions(is_event, start, ::BRUTUS::tt_BaseDate, data_set.out());

	::BRUTUS::OccurrenceTime ot_end;
	ot_end._d(::BRUTUS::tt_oID);
	if (::BRUTUS::term_never == recur_state->how_to_end)
		ot_end.oID(10);
	else
		ot_end.oID(recur_state->occurrence_count);

	MAPIRecurrence mapi_recurrence;
	if (!mapi_recurrence.set_pattern(is_event,
					 start,
					 ot_end,
					 duration,
					 recur_state->first_day_of_week,
					 recur_state->how_to_end,
					 recur_state->pattern)) {
		BRUTUS_LOG_ERR("Could not encode pattern data");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	::CORBA::ULong n;
	for (n = 0; n < data_set->length(); n++)
		mapi_recurrence.set_exception_cond(data_set[n], oc_time);

	SPropValue *mapi_props = NULL;
	if (S_OK != MAPIAllocateBuffer(sizeof(::SPropValue),
				       (void**)&mapi_props)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	mapi_props[0].ulPropTag = recurrence_proptag_;
	mapi_recurrence.write((void*)mapi_props, &(mapi_props[0].Value.bin));

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);

		hr = message_->SetProps(1, mapi_props, NULL);
		MAPIFreeBuffer(mapi_props);

		if (S_OK == hr)
			hr = message_->SaveChanges(KEEP_OPEN_READWRITE);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::RemoveRecurrenceException()");

	return ::BRUTUS::BRUTUS_S_OK;
}

::BRUTUS::BRESULT BRUTUS_IMessage_i::QueryInterface(const char *iid,
						    ::BRUTUS::IUnknown_out ppvObject)
{
	ppvObject = ::BRUTUS::IUnknown::_nil();

	GUID mapi_iid;
	if (!guid_brutus_to_mapi_no_alloc(iid, &mapi_iid)) {
		BRUTUS_LOG_BUG("Could not convert ::BRUTUS::GUID into MAPI GUID");
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	HRESULT hr;
	void *object = NULL;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = message_->QueryInterface(mapi_iid, &object);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	if (hr == S_OK) {
		if (!create_brutus_object(iid, (LPUNKNOWN)object, poa_, ppvObject, mapi_session_)) {
			BRUTUS_LOG_BUG("Could not create brutus object.");
			((LPUNKNOWN)object)->Release();
			return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
	}

	return br;
}

void BRUTUS_IMessage_i::Destroy(::CORBA::ULong InstanceID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMessage_i::Destroy()");

	::PortableServer::ObjectId_var oid;
	oid = poa_->servant_to_id(this);

	poa_->deactivate_object(oid);

	BRUTUS_LOG_INF("Leaving BRUTUS_IMessage_i::Destroy()");
}
