/* -*- 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 "IAddrBookS_impl.h"
#include "obj_utils.h"
#include "conv_utils.h"
#include "progress_utils.h"
#include "IABContainerS_impl.h"
#include "templates.i"

#include <mapiutil.h>

static inline ULONG native_eventmask(const ::BRUTUS::BDEFINE EventMask)
{
	::BRUTUS::BDEFINE event_mask = EventMask;
	ULONG retval = 0;

	if (!EventMask)
		return 0;

	if (event_mask & ::BRUTUS::BRUTUS_fnevCriticalError) {
		retval |= fnevCriticalError;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevCriticalError);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevNewMail) {
		retval |= fnevNewMail;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevNewMail);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevObjectCreated) {
		retval |= fnevObjectCreated;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevObjectCreated);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevObjectDeleted) {
		retval |= fnevObjectDeleted;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevObjectDeleted);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevObjectModified) {
		retval |= fnevObjectModified;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevObjectModified);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevObjectMoved) {
		retval |= fnevObjectMoved;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevObjectMoved);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevObjectCopied) {
		retval |= fnevObjectCopied;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevObjectCopied);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevSearchComplete) {
		retval |= fnevSearchComplete;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevSearchComplete);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevTableModified) {
		retval |= fnevTableModified;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevTableModified);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevStatusObjectModified) {
		retval |= fnevStatusObjectModified;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevStatusObjectModified);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevReservedForMapi) {
		retval |= fnevReservedForMapi;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevReservedForMapi);
	}

	if (event_mask & ::BRUTUS::BRUTUS_fnevExtended) {
		retval |= fnevExtended;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevExtended);
	}

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


	return retval;
}

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

	if (!Flags)
		return 0;

	if (flags & ::BRUTUS::BRUTUS_MAPI_DIALOG ) { // ignore dialog flag
		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 & ::BRUTUS::BRUTUS_MAPI_BEST_ACCESS) {
		retval |= MAPI_BEST_ACCESS;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_BEST_ACCESS);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_SEND_NO_RICH_INFO) {
		retval |= MAPI_SEND_NO_RICH_INFO;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_SEND_NO_RICH_INFO);
	}

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

	return retval;
}


BRUTUS_IAddrBook_i::BRUTUS_IAddrBook_i(LPADRBOOK AddrBook,
				       LPMAPISESSION MAPISession,
				       ::PortableServer::POA_ptr Poa)
	: addr_book_(AddrBook),
	  mapi_session_(MAPISession),
	  poa_(::PortableServer::POA::_duplicate(Poa))
{
	mapi_session_->AddRef();
	advise_sinks_.Clear();
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::OpenEntry(const ::BRUTUS::ENTRYID & lpEntryID,
			      const char * lpInterface,
			      ::BRUTUS::BDEFINE ulFlags,
			      ::BRUTUS::BDEFINE_out lpulObjType,
			      ::BRUTUS::IUnknown_out lppUnk)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::OpenEntry()");

	lpulObjType = 0;
	lppUnk = ::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;
	}

	ENTRYID *entry_id = NULL;
	unsigned long entry_id_size;
	if (!entryid_brutus_to_mapi(&lpEntryID, 0, entry_id_size, entry_id)) {
		MAPIFreeBuffer(i_id);
		BRUTUS_LOG_ERR("No memory");
		return ::BRUTUS::BRUTUS_MAPI_E_NOT_ENOUGH_MEMORY;
	}

	unsigned long obj_type;
	LPUNKNOWN unk_object = NULL;
	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->OpenEntry(entry_id_size,
					   entry_id,
					   i_id,
					   flags,
					   &obj_type,
					   &unk_object);
	}
	MAPIFreeBuffer(entry_id);
	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_IABContainer_i::OpenEntry() failed");
		return br;
	}

	::BRUTUS::IUnknown_var obj = ::BRUTUS::IUnknown::_nil();
	if (object_type_mapi_to_brutus(obj_type, lpulObjType)) {
		if (!create_brutus_object(obj_type, unk_object, poa_.in(), obj.out(), mapi_session_)) {
			if (unk_object)
				unk_object->Release();
			BRUTUS_LOG_BUG("Could not create brutus object");
			return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
	} else {
		if (!create_brutus_object(lpInterface, unk_object, poa_, obj.out())) {
			if (unk_object)
				unk_object->Release();
			BRUTUS_LOG_BUG("Could not create brutus object");
			return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
	}
	lppUnk = obj._retn();

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::OpenEntry()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::CompareEntryIDs(const ::BRUTUS::ENTRYID & lpEntryID1,
				    const ::BRUTUS::ENTRYID & lpEntryID2,
				    ::BRUTUS::BDEFINE ulFlags,
				    ::CORBA::ULong_out lpulResult)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::CompareEntryIDs()");

	ENTRYID *entry_id_1;
	ENTRYID *entry_id_2;

	unsigned long result = 0;
	unsigned long flags = native_flags(ulFlags);

	unsigned long entry_id_1_size;
	if (!entryid_brutus_to_mapi(&lpEntryID1, 0, entry_id_1_size, entry_id_1)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	unsigned long entry_id_2_size;
	if (!entryid_brutus_to_mapi(&lpEntryID2, 0, entry_id_2_size, entry_id_2)) {
		MAPIFreeBuffer(entry_id_1);
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->CompareEntryIDs(entry_id_1_size,
						 entry_id_1,
						 entry_id_2_size,
						 entry_id_2,
						 flags,
						 &result);
	}
	MAPIFreeBuffer(entry_id_1);
	MAPIFreeBuffer(entry_id_2);

	if (result == TRUE)
		lpulResult = (::CORBA::Boolean)1;
	else
		lpulResult = (::CORBA::Boolean)0;

	::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_IAddrBook_i::CompareEntryIDs()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::Advise(const ::BRUTUS::ENTRYID & lpEntryID,
			   ::BRUTUS::BDEFINE ulEventMask,
			   ::BRUTUS::IMAPIAdviseSink_ptr lpAdviseSink,
			   ::CORBA::ULong_out lpulConnection)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::Advise()");

	lpulConnection = 0;

	if (::CORBA::is_nil(lpAdviseSink)) {
		BRUTUS_LOG_BUG("lpAdviseSink is NIL");
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	ENTRYID *entry_id;
	unsigned long entry_id_size;
	if (!entryid_brutus_to_mapi(&lpEntryID, 0, entry_id_size, entry_id)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	CMAPIAdviseSink *sink;
	try {
		sink = new CMAPIAdviseSink(lpAdviseSink, mapi_session_, poa_.in());
	}
	catch (std::bad_alloc &) {
		MAPIFreeBuffer(entry_id);
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	unsigned long event_mask = native_eventmask(ulEventMask);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		unsigned long adv_con; // the compiler complains otherwise..
		hr = addr_book_->Advise(entry_id_size,
					entry_id,
					event_mask,
					sink,
					&adv_con);

		lpulConnection = (::CORBA::ULong)adv_con; // see above..
	}
	MAPIFreeBuffer(entry_id);
	sink->Release(); // MAPI::UnAdvise will call Release()

	if (hr == S_OK)
		advise_sinks_.AddSink(lpulConnection);

	::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_IAddrBook_i::Advise()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::Unadvise(::CORBA::ULong ulConnection)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::Unadvise()");

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->Unadvise(ulConnection);
	}
	advise_sinks_.RemoveSink(ulConnection);

	::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_IAddrBook_i::Unadvise()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::CreateOneOff(const char *lpszName,
				 const char *lpszAdrType,
				 const char *lpszAddress,
				 ::BRUTUS::BDEFINE ulFlags,
				 ::BRUTUS::ENTRYID_out lppEntryID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::CreateOneOff()");

	unsigned long flags = native_flags(ulFlags);

	unsigned long count = 0;
	ENTRYID *entry_id = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->CreateOneOff((char*)lpszName,
					      (char*)lpszAdrType,
					      (char*)lpszAddress,
					      flags,
					      &count,
					      &entry_id);
	}

	::BRUTUS::ENTRYID_var out_entry_id;
	if (!entryid_mapi_to_brutus(count, entry_id, out_entry_id.out(), true)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	lppEntryID = out_entry_id._retn();

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

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::CreateOneOff()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::NewEntry(void)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::NewEntry()");

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::NewEntry()");

	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::ResolveName(::BRUTUS::BDEFINE ulFlags,
				::BRUTUS::ADRLIST & lpAdrList)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::ResolveName()");

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

	ADRLIST *mapi_adr_list = NULL;
	if (!adr_list_brutus_to_mapi(&lpAdrList, 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 = addr_book_->ResolveName(0,
					     flags,
					     0,
					     mapi_adr_list);
	}
	if (!adr_list_mapi_to_brutus(mapi_adr_list, lpAdrList, mapi_session_, true, poa_)) {
		BRUTUS_LOG_ERR("Could not convert MAPI ADRLIST to Brutus ADRLIST");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	::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_IAddrBook_i::ResolveName()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::Address(void)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::Address()");

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::Address()");

	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::Details(void)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::Details()");

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::Details()");

	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::RecipOptions(void)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::RecipOptions()");

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::RecipOptions()");

	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::QueryDefaultRecipOpt(const char *lpszAdrType,
					 ::BRUTUS::BDEFINE ulFlags,
					 ::BRUTUS::seq_SPropValue_out lppOptions)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::QueryDefaultRecipOpt()");

	unsigned long flags = native_flags(ulFlags);

	::BRUTUS::seq_SPropValue_var out_values;
	try {
		out_values = new ::BRUTUS::seq_SPropValue;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	out_values->length(0);
	unsigned long count = 0;
	LPSPropValue values = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->QueryDefaultRecipOpt((char*)lpszAdrType,
						      flags,
						      &count,
						      &values);
	}

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

	if (count && (hr == S_OK))
		spropvalue_array_mapi_to_brutus(count, values, out_values, mapi_session_, true, poa_);
	else if (values)
		MAPIFreeBuffer(values);

	lppOptions = out_values._retn();

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::QueryDefaultRecipOpt()");

	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::GetPAB(::BRUTUS::ENTRYID_out lppEntryID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::GetPAB()");

	unsigned long count = 0;
	ENTRYID *entry_id = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->GetPAB(&count,
					&entry_id);
	}

	::BRUTUS::ENTRYID_var out_entry_id;
	if (!entryid_mapi_to_brutus(count, entry_id, out_entry_id.out(), true)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	lppEntryID = out_entry_id._retn();

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

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::GetPAB()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::SetPAB(const ::BRUTUS::ENTRYID & lpEntryID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::SetPAB()");

	ENTRYID *entry_id = NULL;
	unsigned long entry_id_size;
	if (!entryid_brutus_to_mapi(&lpEntryID, 0, entry_id_size, entry_id)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->SetPAB(entry_id_size, entry_id);
	}
	MAPIFreeBuffer(entry_id);

	::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_IAddrBook_i::SetPAB()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::GetDefaultDir(::BRUTUS::ENTRYID_out lppEntryID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::GetDefaultDir()");

	unsigned long count = 0;
	ENTRYID *entry_id = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->GetDefaultDir(&count,
					       &entry_id);
	}

	::BRUTUS::ENTRYID_var out_entry_id;
	if (!entryid_mapi_to_brutus(count, entry_id, out_entry_id.out(), true)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	lppEntryID = out_entry_id._retn();

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

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::GetDefaultDir()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::SetDefaultDir(const ::BRUTUS::ENTRYID & lpEntryID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::SetDefaultDir()");

	ENTRYID *entry_id = NULL;
	unsigned long entry_id_size;
	if (!entryid_brutus_to_mapi(&lpEntryID, 0, entry_id_size, entry_id)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->SetDefaultDir(entry_id_size, entry_id);
	}
	MAPIFreeBuffer(entry_id);

	::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_IAddrBook_i::SetDefaultDir()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::GetSearchPath(::BRUTUS::BDEFINE ulFlags,
				  ::BRUTUS::SRowSet_out lppSearchPath)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::GetSearchPath()");

	unsigned long flags = native_flags(ulFlags);

	SRowSet *search_path = NULL;
	::BRUTUS::SRowSet_var brutus_set;
	try {
		brutus_set = new ::BRUTUS::SRowSet;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_set->length(0);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->GetSearchPath(flags, &search_path);
	}

	if (!srow_set_mapi_to_brutus(search_path, brutus_set, mapi_session_, true, poa_)) {
		lppSearchPath = brutus_set._retn();
		BRUTUS_LOG_BUG("Conversion error");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppSearchPath = brutus_set._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_IAddrBook_i::GetSearchPath()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::SetSearchPath(::BRUTUS::BDEFINE ulFlags,
				  const ::BRUTUS::SRowSet & lpSearchPath)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::SetSearchPath()");

	unsigned long flags = native_flags(ulFlags);

	SRowSet *search_path = NULL;
	if (!srow_set_brutus_to_mapi(&lpSearchPath, search_path)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->SetSearchPath(flags, search_path);
	}
	MAPIFreeBuffer(search_path);

	::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_IAddrBook_i::SetSearchPath()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::PrepareRecips(::BRUTUS::BDEFINE ulFlags,
				  const ::BRUTUS::SPropTagArray & lpPropTagArray,
				  const ::BRUTUS::ADRLIST & lpRecipList)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::PrepareRecips()");

	unsigned long flags = native_flags(ulFlags);

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

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

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->PrepareRecips(flags, tags, mapi_adr_list);
	}
	MAPIFreeBuffer(tags);
	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_IAddrBook_i::PrepareRecips()");
	return br;
}

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

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

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

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::PAB(::BRUTUS::IABContainer_out lppPAB)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::PAB()");

	::BRUTUS::BRESULT br;
	unsigned long entry_id_size = 0;
	ENTRYID *entry_id = NULL;
	unsigned long obj_type;
	LPABCONT ab_cont = NULL;

	lppPAB = ::BRUTUS::IABContainer::_nil();

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->GetPAB(&entry_id_size,
					&entry_id);
	}
	if (S_OK != hr)
		goto exit;

	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->OpenEntry(entry_id_size,
					   entry_id,
					   &IID_IABContainer, // or NULL
					   MAPI_BEST_ACCESS,
					   &obj_type,
					   (LPUNKNOWN*)&ab_cont);
	}
	if (S_OK == hr) {
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		lppPAB = create_object<BRUTUS_IABContainer_i, ::BRUTUS::IABContainer, LPABCONT>
			(ab_cont, poa_.in(), mapi_session_);
		ab_cont = NULL;
	}
exit:
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		br = ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	if (ab_cont)
		ab_cont->Release();
	if (entry_id)
		MAPIFreeBuffer(entry_id);

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::PAB()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::GAL(::BRUTUS::IABContainer_out lppGAL)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::GAL()");

	::BRUTUS::BRESULT br;
	ULONG obj_type = 0;
	unsigned long entry_id_size = 0;
	LPENTRYID entry_id = NULL;
	LPMAPICONTAINER root_cont = NULL;
	LPMAPITABLE mapi_table = NULL;
	LPABCONT gal_cont = NULL;
	SRestriction restriction;
	LPSRowSet rows = NULL;
	SPropValue pv;
	SizedSPropTagArray(2, prop_tag_array) = {2, PR_DISPLAY_TYPE,PR_ENTRYID};

	lppGAL = ::BRUTUS::IABContainer::_nil();
	memset((void*)&restriction, 0, sizeof(SRestriction));

	HRESULT hr = S_OK;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->OpenEntry(NULL,
					   0,
					   &IID_IABContainer,
					   MAPI_BEST_ACCESS,
					   &obj_type,
					   (LPUNKNOWN*)&root_cont);
	}
	if ((S_OK != hr) || (MAPI_ABCONT != obj_type)) {
		BRUTUS_LOG_BUG("OpenEntry() failed");
		goto exit;
	}

	// get root container
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = root_cont->GetHierarchyTable(0,
						  &mapi_table);
	}
	if (S_OK != hr) {
		BRUTUS_LOG_BUG("GetHierarchyTable() failed");
		goto exit;
	}

	// get the only DT_GLOBAL container
	pv.ulPropTag = PR_DISPLAY_TYPE;
	pv.Value.l = DT_GLOBAL;
	restriction.rt = RES_PROPERTY;
	restriction.res.resProperty.relop = RELOP_EQ;
	restriction.res.resProperty.ulPropTag = PR_DISPLAY_TYPE;
	restriction.res.resProperty.lpProp = &pv;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = HrQueryAllRows(mapi_table,
				    (LPSPropTagArray)&prop_tag_array,
				    &restriction,
				    NULL,
				    0,
				    &rows);
	}
	if (hr != S_OK) {
		BRUTUS_LOG_BUG("HrQueryAllRows() failed");
		goto exit;
	}
	entry_id_size = rows->aRow->lpProps[1].Value.bin.cb;
	entry_id = (LPENTRYID)rows->aRow->lpProps[1].Value.bin.lpb;

	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->OpenEntry(entry_id_size,
					   entry_id,
					   &IID_IABContainer,
					   MAPI_BEST_ACCESS,
					   &obj_type,
					   (LPUNKNOWN*)&gal_cont);
	}
	if ((S_OK != hr) || (MAPI_ABCONT != obj_type)) {
		BRUTUS_LOG_BUG("OpenEntry() failed");
		goto exit;
	} else {
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		lppGAL = create_object<BRUTUS_IABContainer_i, ::BRUTUS::IABContainer, LPABCONT>
			(gal_cont, poa_.in(), mapi_session_);
		gal_cont = NULL;
	}

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

	if (root_cont)
		root_cont->Release();
	if (gal_cont)
		gal_cont->Release();
	if (rows)
		FreeProws(rows);

	BRUTUS_LOG_INF("Leaving BRUTUS_IAddrBook_i::GAL()");
	return br;
}


::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::GetLastError(::BRUTUS::BRESULT ReturnCode,
				 ::BRUTUS::BDEFINE ulFlags,
				 ::BRUTUS::MAPIERROR_out lppMAPIError)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_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 = addr_book_->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_IAddrBook_i::GetLastError()");
	return br;
}

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

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->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_IAddrBook_i::SaveChanges()");
	return br;

	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::GetProps(const ::BRUTUS::SPropTagArray & lpPropTagArray,
			     ::BRUTUS::BDEFINE ulFlags,
			     ::BRUTUS::seq_SPropValue_out lppPropArray)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_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 = addr_book_->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_IAddrBook_i::GetProps()");
	return br;
}

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

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

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->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_IAddrBook_i::GetPropList()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::OpenProperty(::CORBA::ULong ulPropTag,
				 const char * lpiid,
				 ::CORBA::ULong ulInterfaceOptions,
				 ::BRUTUS::BDEFINE ulFlags,
				 ::BRUTUS::IUnknown_out lppUnk)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_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 = addr_book_->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_IAddrBook_i::OpenProperty()");
	return br;
}


::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::SetProps(const ::BRUTUS::seq_SPropValue & lpPropArray,
			     ::CORBA::Boolean ProblemInfo,
			     ::BRUTUS::SPropProblemArray_out lppProblems)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_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 = addr_book_->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_IAddrBook_i::SetProps()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::DeleteProps(const ::BRUTUS::SPropTagArray & lpPropTagArray,
				::CORBA::Boolean ProblemInfo,
				::BRUTUS::SPropProblemArray_out lppProblems)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_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 = addr_book_->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_IAddrBook_i::DeleteProps()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_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_IAddrBook_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 = addr_book_->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_IAddrBook_i::CopyTo()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_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_IAddrBook_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 = addr_book_->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_IAddrBook_i::CopyProps()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::GetNamesFromIDs(::BRUTUS::SPropTagArray & lppPropTags,
				    const char * lpPropSetGuid,
				    ::BRUTUS::BDEFINE ulFlags,
				    ::BRUTUS::seq_MAPINAMEID_out lpppPropNames)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_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 = addr_book_->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_IAddrBook_i::GetNamesFromIDs()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::GetIDsFromNames(const ::BRUTUS::seq_MAPINAMEID & lppPropNames,
				    ::BRUTUS::BDEFINE ulFlags,
				    ::BRUTUS::SPropTagArray_out lppPropTags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_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 = addr_book_->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_IAddrBook_i::GetIDsFromNames()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_i::GetNamedProps(const char *PropSetGuid,
				  const ::BRUTUS::seq_NamedPropertyTag & lpPropTagArray,
				  ::BRUTUS::BDEFINE ulFlags,
				  ::BRUTUS::seq_SPropValue_out lppPropArray)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_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 = addr_book_->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 = addr_book_->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_IAddrBook_i::GetNamedProps()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_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_IAddrBook_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 (!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;

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

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

	ULONG n = 0;
	for (n = 0; n < lpPropArray.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");
			MAPIFreeBuffer(mapi_props);
			MAPIFreeBuffer(mapi_problems);
			MAPIFreeBuffer(mapi_names);
			lppProblems = brutus_problems._retn();
			return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
		}
	}

	SPropTagArray *tags = NULL;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = addr_book_->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 = addr_book_->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_IAddrBook_i::SetNamedProps()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAddrBook_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 = addr_book_->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_IAddrBook_i::Destroy(::CORBA::ULong InstanceID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAddrBook_i::Destroy()");

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

	poa_->deactivate_object(oid);

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