/* -*- 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 "IAttachS_impl.h"

#include "obj_utils.h"
#include "conv_utils.h"
#include "progress_utils.h"

#ifdef MAPI_PREFIX
#undef MAPI_PREFIX
#endif
#define MAPI_PREFIX 0x2A,0x86,0x48,0x86,0xf7,0x14,0x03

#ifdef DEFINE_OID_1
#undef DEFINE_OID_1
#endif
#define DEFINE_OID_1(name, b0, b1) const BYTE name[] = { MAPI_PREFIX, b0, b1 }

#ifdef DEFINE_OID_2
#undef DEFINE_OID_2
#endif
#define DEFINE_OID_2(name, b0, b1, b2) const BYTE name[] = { MAPI_PREFIX, b0, b1, b2 }

#ifdef DEFINE_OID_3
#undef DEFINE_OID_3
#endif
#define DEFINE_OID_3(name, b0, b1, b2, b3) const BYTE name[] = { MAPI_PREFIX, b0, b1, b2, b3 }

#ifdef DEFINE_OID_4
#undef DEFINE_OID_4
#endif
#define DEFINE_OID_4(name, b0, b1, b2, b3, b4) const BYTE name[] = { MAPI_PREFIX, b0, b1, b2, b3, b4 }

#define CB_OID_1        (9)
#define CB_OID_2        (10)
#define CB_OID_3        (11)
#define CB_OID_4        (12)

#define OID_TAG         (0x0A)
#define OID_ENCODING    (0x0B)

DEFINE_OID_1(OID_TNEF, OID_TAG, 0x01);
#define CB_OID_TNEF CB_OID_1

DEFINE_OID_1(OID_OLE, OID_TAG, 0x03);
#define CB_OID_OLE CB_OID_1

DEFINE_OID_2(OID_OLE1, OID_TAG, 0x03, 0x01);
#define CB_OID_OLE1 CB_OID_2

DEFINE_OID_3(OID_OLE1_STORAGE, OID_TAG, 0x03, 0x01, 0x01);
#define CB_OID_OLE1_STORAGE CB_OID_3

DEFINE_OID_2(OID_OLE2, OID_TAG, 0x03, 0x02);
#define CB_OID_OLE2 CB_OID_2

DEFINE_OID_3(OID_OLE2_STORAGE, OID_TAG, 0x03, 0x02, 0x01);
#define CB_OID_OLE2_STORAGE CB_OID_3

DEFINE_OID_1(OID_MAC_BINARY, OID_ENCODING, 0x01);
#define CB_OID_MAC_BINARY CB_OID_1

DEFINE_OID_1(OID_MIMETAG, OID_TAG, 0x04);
#define CB_OID_MIMETAG CB_OID_1

static const LPCSTR default_attach_rendering_base64_encoded =
	"AQAJAAAD3AYAAAAAIQYAAAAABQAAAAkCAAAAAAUAAAABAv///wClAAAAQQvGAIgAIAAgAAAAA\
AAgACAAAAAAACgAAAAgAAAAQAAAAAEAAQAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///\
/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAA8AAAA/AAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8\
AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAH\
wAAAB8AAAAfAAAAHwAAAB8AAAA/AAAAPwAAAH8AAAD/AAAf8hBgAAQQtGAGYAIAAgAAAAAAAgA\
CAAAAAAACgAAAAgAAAAIAAAAAEAGAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAADAQEHAwIKBQMMBgMMBgMMBgMMBgMMBgMMBgMMBgMMBgMMBgMMBgMMBgMMBgMMBgMMBgMMB\
gMMBgMMBgMMBgMLBgMKBQIHAwIDAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAHAwIQCAQXDAYbDgc\
cDgccDgccDgccDgccDgccDgccDgccDgccDgccDgccDgccDgccDgccDgccDgccDgccDgcbDgcXD\
AYPCAQHAwICAQAAAAAAAAAAAAAAAAAAAAAAAADOnYvapprXpJfWopfUoZbUoZbSn5XRnpXPnJT\
Pm5TPm5TPm5TPm5TPm5TOm5TOmpTOmpTOmpTOmpTOmpTOmpTOmpPZlIgXDAYKBQMDAQEAAAAAA\
AAAAAAAAAAAAAAAAADLr6b639X93s793c393Mv928n92sf92cb92cT81cH81cH81cH81cH81cH\
81cH81cH81cH81cH81cH81cH81cH9ypfalIkbDQcLBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADHs\
Kb36tn96NP95tD95c3948r94sf94cX94MP938D93b393Lr927j92bT92LL91q/91az906n90qf\
90aX9z6L70L3alYkcDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADKs6f36tv96dX96NP95tD95\
c3948r94sf94cX94MP938D93b393Lr927j92bT92LL91q/91az906n90qf90aX70L3alYkcDgc\
MBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADMtan3697969n96dX96NP95tD95c3948r94sf94cX94\
MP938D93b393Lr927j92bT92LL91q/91az906n90qf70L3alYkcDgcMBgMDAQEAAAAAAAAAAAA\
AAAAAAAAAAADOtqr47OD97NvMwce/hIDCeWnAemu/em29e2+7e3C6e3GrhoSmi4uRoauDr793u\
tJ1t9KMoK6gjpHyv43906n70L3alYocDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADQuKz47+T\
97d7Hlpi2f4a1gIixgIuwgIuugI6rgJCqgJGhh52bjqaSnbmIq8pxzvd0x/CIqcm4f4TOiGL91\
az70L3alYocDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADRuq348Ob97+G4kp6pgJKngJSvf4q\
zfoelgJOegpuZiaWSlrN+yup53v153v2AwOCKrMqSnrmYla3FhGX91q/70L3blYocDgcMBgMDA\
QEAAAAAAAAAAAAAAAAAAAAAAADUvK748un98OOslKibgp6ZgZ6xaW+4ZGSoc4CUgqGOhaqGo8a\
GqsyCz+mB3/Z/4/2AzeuXiKWegpy+hmv92LL70L3blYocDgcMBgMDAQEAAAAAAAAAAAAAAAAAA\
AAAAADWvrD58+v98uagl7OOhaqLhq2TdZCsTEyfX26KgKSAiLaAiLeButuF2/CH5veG3vKAzeq\
AsdWMia63iHL92bT70L3blYocDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADYwLH59e/98+iVm\
b6AiLZ/iLl+ibo0RWBHWX1hdaF0isN4otKE3/SJ5fSJ5fSE4vaAyed8pdCAiLawiXn927j70L3\
bloocDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADawrL59vH99OuKm8h0isNxi8VMYYcuQFtFV\
IBlhMFnjc5mjc9qn9mC1u+E3PKG4fR5u+Nymc91isKoi4D93Lr70L3bloocDgcMBgMDAQEAAAA\
AAAAAAAAAAAAAAAAAAADcxLT5+PX99u6AntNnjc9kjdEsQFwmN1A5VoFdj9haj9pZkNtfoONpt\
Op70vOA3vdsseVkktRojM2ijIT93b370L3bloocDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAAD\
dxbX6+fb99/F3oNxZkNtUiNAnNk4uTHFAdrNPkuVMkuZLkuhZqu931Pd52vphtvFerexTkeBZj\
9qbjor938D70L3bloocDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADgyLf6+/v9+fRso+dLkuh\
EgMYqOFMsX4tCj+ZBlPE/lPI9lfRGovdcu/ly1fxAmfRBlPBFk+xKkuiTj5L94MP70L3cloocD\
gcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADiyrf6/Pz9+vc0PrIKGaYcKoAtO1gaUJYQJ7QVMr8\
aPcgdR9IhT9onWeEtYeUnXOUnWOEkUdohSNJ9aIX94cX70L3cloocDgcMBgMDAQEAAAAAAAAAA\
AAAAAAAAAAAAADky7n6/f39/PoqKqcAAJcmMGssP2cSOqwRJrcUK70WL8McPc8hSdonVOQqXOw\
rXe0oVucjTN0dQNJ7Y4X94sf70L3cloocDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADmzrv7/\
f39/PxVhPw0b/0zSoMpWos2dfo+gf1EjPs2b+U5d+hDiO9Zsfl2wftatftHkPUtX+YfRNaAbYv\
948r70L3cl4ocDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADoz7z7/f39/f1Vhfw0b/01WLEnb\
cM3dv0/gv1Hkf1Pn/1Wrf1evP1+0f39/f1/0v1gv/1Ysf1Rov2Sk6D95c370L3cl4ocDgcMBgM\
DAQEAAAAAAAAAAAAAAAAAAAAAAADq0r77/f39/f1Vhf00b/01ZdwtcvA3dv0+gf1GkP1Onv1Vr\
f1duv1kx/1/0v1lyf1fvf1XsP1Qof2Rk6D95tD70L3cl4ocDgcMBgMDAQEAAAAAAAAAAAAAAAA\
AAAAAAADs1MD7/f39/f1hjf00b/00bfk0b/02cv08f/1EjP1Mmf1Spv1Zs/1fvf1iw/1gv/1at\
v1Uqv1NnP2QkaH96NP70L3cl4ocDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADu1sH8/f39/f3\
g6f1Vhf1Vhf1VhfxWhfxbj/xhmftmo/trrPpwtfp0vfl2wPl1vvhxt/hsrPiGosv438b96dX70\
L3cl4ocDgcMBgMDAQEAAAAAAAAAAAAAAAAAAAAAAADw18P8/f39/f39/f39/f39/f39/f39/Pz\
9/Pr9+vf9+fT99/H99u799Ov98+j98ub98OP96t79y8j9wb79urf70L3dk4YdEAkMBwQDAgEAA\
AAAAAAAAAAAAAAAAAAAAADy2sX8/f39/f39/f39/f39/f39/f39/fz9+/n9+vb9+PT9+PH99u7\
99ez98+n98uX98uT32Mz1sbH1qqr0n6D2mJjQi30eEQsNCAUDAgEAAAAAAAAAAAAAAAAAAAAAA\
AD028X8/f39/f39/f39/f39/f39/f39/f39/Pz9+/n9+fb9+PT99/H99e799Oz98+n78ObjtI/\
sn0flkzjciTbTgDWldVkcEg0LCAYCAgEAAAAAAAAAAAAAAAAAAAAAAAD23sj8/f39/f39/f39/\
f39/f39/f39/f39/f39/fz9+/n9+vb9+PT99/H99u799ez68Ofpvpz9u2D9pzH6oCK3ekEvIRk\
TDQoHBQQBAQEAAAAAAAAAAAAAAAAAAAAAAAD44Mj9/f39/f39/f39/f39/f39/f39/f39/f39/\
f39/Pz9+/n9+fb9+PT99/H99e768Ojpxab9zIP8s1S/i1UxIhsVDgsJBgUCAgEAAAAAAAAAAAA\
AAAAAAAAAAAAAAAD64sr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/Pv9+/f9+\
fX9+fP88evrzq391pS8imExIhsVDwsJBwUDAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD85dD\
9/f38/f37/f37/f36/f36/f35/f34/P34/P33+/32+v32+fz19/n09fbz9PTx7+3sz67AooE6K\
B4aEg4LCAYDAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD806n72Lf41bX31LP00rLyz7H\
vza/tyq7ryazqx6vnxankwqjiwKbhv6XfvaPcuqLZuKGsh28RDQoAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAADAAAAAAA=";

/*
 * Base64 decoding functions from the libb64 project which has been
 * placed in the public domain.
 *
 * For details, see http://sourceforge.net/projects/libb64
 */
typedef enum {
	step_a,
	step_b,
	step_c,
	step_d
} base64_decodestep;

typedef struct {
	base64_decodestep step;
	char plainchar;
} base64_decodestate;

static inline int
base64_decode_value(char value_in)
{
	static const char decoding[] = {
		62, -1, -1, -1, 63, 52, 53, 54, 55, 56,
		57, 58, 59, 60, 61, -1, -1, -1, -2, -1,
		-1, -1,  0,  1,  2,  3,  4,  5,  6,  7,
		8,   9, 10, 11, 12, 13, 14, 15, 16, 17,
		18, 19, 20, 21, 22, 23, 24, 25, -1, -1,
		-1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
		32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
		42, 43, 44, 45, 46, 47, 48, 49, 50, 51
	};
	static const char decoding_size = sizeof(decoding);

	value_in -= 43;
	if ((value_in < 0) || (value_in > decoding_size))
		return -1;

	return decoding[(int)value_in];
}

static inline void
base64_init_decodestate(base64_decodestate* state_in)
{
	state_in->step = step_a;
	state_in->plainchar = 0;
}

// Returns the actual size of the decoded data which is always
// smaller than the encoded content. You must therefore allocate
// 'length_in' bytes of memory for plaintext_out. The number of
// leading bytes of the resulting plaintext_out that actually
// consists of the decoded content is the return value of this
// function
static int
base64_decode_block(const char *code_in,
		    const int length_in,
		    char *plaintext_out,
		    base64_decodestate *state_in)
{
	const char* codechar = code_in;
	char* plainchar = plaintext_out;
	char fragment;

	*plainchar = state_in->plainchar;

	switch (state_in->step) {
		while (1) {
		case step_a:
			do {
				if (codechar == code_in+length_in) {
					state_in->step = step_a;
					state_in->plainchar = *plainchar;
					return plainchar - plaintext_out;
				}
				fragment = (char)base64_decode_value(*codechar++);
			} while (fragment < 0);
			*plainchar = (fragment & 0x03f) << 2;
		case step_b:
			do {
				if (codechar == code_in+length_in) {
					state_in->step = step_b;
					state_in->plainchar = *plainchar;
					return plainchar - plaintext_out;
				}
				fragment = (char)base64_decode_value(*codechar++);
			} while (fragment < 0);
			*plainchar++ |= (fragment & 0x030) >> 4;
			*plainchar = (fragment & 0x00f) << 4;
		case step_c:
			do {
				if (codechar == code_in+length_in) {
					state_in->step = step_c;
					state_in->plainchar = *plainchar;
					return plainchar - plaintext_out;
				}
				fragment = (char)base64_decode_value(*codechar++);
			} while (fragment < 0);
			*plainchar++ |= (fragment & 0x03c) >> 2;
			*plainchar    = (fragment & 0x003) << 6;
		case step_d:
			do {
				if (codechar == code_in+length_in) {
					state_in->step = step_d;
					state_in->plainchar = *plainchar;
					return plainchar - plaintext_out;
				}
				fragment = (char)base64_decode_value(*codechar++);
			} while (fragment < 0);
			*plainchar++ |= (fragment & 0x03f);
		}
	}

	/* control should not reach here */
	return plainchar - plaintext_out;
}

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 ) {
		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;
}


BRUTUS_IAttach_i::BRUTUS_IAttach_i(LPATTACH Attach,
				   LPMAPISESSION MAPISession,
				   ::PortableServer::POA_ptr Poa)
	: attach_(Attach),
	  mapi_session_(MAPISession),
	  poa_(::PortableServer::POA::_duplicate(Poa))
{
	mapi_session_->AddRef();
}

::BRUTUS::BRESULT
BRUTUS_IAttach_i::GetLastError(::BRUTUS::BRESULT ReturnCode,
			       ::BRUTUS::BDEFINE ulFlags,
			       ::BRUTUS::MAPIERROR_out lppMAPIError)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAttach_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 = attach_->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_IAttach_i::GetLastError()");
	return br;
}


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

	unsigned long flags = native_flags(ulFlags);

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


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


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

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

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

// FIXME. Support for all possible interfaces are misssing
::BRUTUS::BRESULT
BRUTUS_IAttach_i::OpenProperty(::CORBA::ULong ulPropTag,
			       const char * lpiid,
			       ::CORBA::ULong ulInterfaceOptions,
			       ::BRUTUS::BDEFINE ulFlags,
			       ::BRUTUS::IUnknown_out lppUnk)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAttach_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 = attach_->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_IAttach_i::OpenProperty()");
	return br;
}


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

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


::BRUTUS::BRESULT
BRUTUS_IAttach_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_IAttach_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 = attach_->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_IAttach_i::CopyTo()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAttach_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_IAttach_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 = attach_->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_IAttach_i::CopyProps()");
	return br;
}


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

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

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

::BRUTUS::BRESULT
BRUTUS_IAttach_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_IAttach_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 = attach_->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 = attach_->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_IAttach_i::SetNamedProps()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAttach_i::SetDefaultAttachRendering(void)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAttach_i::SetDefaultAttachRendering()");

	const char *base64_content = default_attach_rendering_base64_encoded;
	char *decoded_content = NULL;
	int decoded_len = 0;
	base64_decodestate state;
	LPSTREAM mapi_stream = NULL;
	ULONG mapi_written = 0;

	if (S_OK != MAPIAllocateBuffer(sizeof(char)*strlen(base64_content),
				       (void**)&decoded_content)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	base64_init_decodestate(&state);
	decoded_len = base64_decode_block(base64_content,
					  strlen(base64_content),
					  decoded_content,
					  &state);
	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);

		// open stream for attachment data
		hr = attach_->OpenProperty(PR_ATTACH_RENDERING,
					   &IID_IStream,
					   STGM_WRITE,
					   MAPI_MODIFY | MAPI_CREATE,
					   (LPUNKNOWN*)&mapi_stream);
		if (S_OK != hr)
			goto exit;

		hr = mapi_stream->Write((LPBYTE)decoded_content,
					decoded_len,
					&mapi_written);
		if (S_OK != hr)
			goto exit;
		if (mapi_written != decoded_len) {
			BRUTUS_LOG_ERR("Incomplete write of data to attachment object");
			goto exit;
		}

		hr = mapi_stream->Commit(STGC_DEFAULT);
		if (S_OK != hr)
			goto exit;
	}
exit:
	if (decoded_content)
		MAPIFreeBuffer(decoded_content);

	if (mapi_stream)
		mapi_stream->Release();

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

::BRUTUS::BRESULT
BRUTUS_IAttach_i::SetAttachTag(::BRUTUS::BRUTUS_ATTACH_TAG AttachTag)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAttach_i::SetAttachTag()");

	::BRUTUS::BRESULT br = ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	SPropValue pv;

	pv.ulPropTag = PR_ATTACH_TAG;
	switch (AttachTag) {
	case ::BRUTUS::BRUTUS_OID_NONE :
		br = ::BRUTUS::BRUTUS_S_OK;
		goto exit;
	case ::BRUTUS::BRUTUS_OID_TNEF :
		pv.Value.bin.cb = CB_OID_TNEF;
		pv.Value.bin.lpb = (LPBYTE)OID_TNEF;
		break;
	case ::BRUTUS::BRUTUS_OID_OLE :
		pv.Value.bin.cb = CB_OID_OLE;
		pv.Value.bin.lpb = (LPBYTE)OID_OLE;
		break;
	case ::BRUTUS::BRUTUS_OID_OLE1 :
		pv.Value.bin.cb = CB_OID_OLE1;
		pv.Value.bin.lpb = (LPBYTE)OID_OLE1;
		break;
	case ::BRUTUS::BRUTUS_OID_OLE1_STORAGE :
		pv.Value.bin.cb = CB_OID_OLE1_STORAGE;
		pv.Value.bin.lpb = (LPBYTE)OID_OLE1_STORAGE;
		break;
	case ::BRUTUS::BRUTUS_OID_OLE2:
		pv.Value.bin.cb = CB_OID_OLE2;
		pv.Value.bin.lpb = (LPBYTE)OID_OLE2;
		break;
	case ::BRUTUS::BRUTUS_OID_OLE2_STORAGE :
		pv.Value.bin.cb = CB_OID_OLE2_STORAGE;
		pv.Value.bin.lpb = (LPBYTE)OID_OLE2_STORAGE;
		break;
	case ::BRUTUS::BRUTUS_OID_MAC_BINARY :
		pv.Value.bin.cb = CB_OID_MAC_BINARY;
		pv.Value.bin.lpb = (LPBYTE)OID_MAC_BINARY;
		break;
	case ::BRUTUS::BRUTUS_OID_MIMETAG :
		pv.Value.bin.cb = CB_OID_MIMETAG;
		pv.Value.bin.lpb = (LPBYTE)OID_MIMETAG;
		break;
	default :
	{
		char msg[128] = {0};
		sprintf_s(msg, sizeof(msg), "Unknown AttachTag from BRUTUS : %d", AttachTag);
		BRUTUS_LOG_BUG(msg);
		goto exit;
	}
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = attach_->SetProps(1, (LPSPropValue)&pv, NULL);
	}

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

exit:
	BRUTUS_LOG_INF("Leaving BRUTUS_IAttach_i::SetAttachTag()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAttach_i::SetRFC822Headers(const ::BRUTUS::seq_string & FieldNames,
				   const ::BRUTUS::seq_string & FieldBodies)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAttach_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 = attach_->GetIDsFromNames(FieldNames.length(),
					      mapi_names,
					      MAPI_CREATE,
					      &tags);
	}
	MAPIFreeBuffer(mapi_names);

	if (!tags || ((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 = attach_->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_IAttach_i::SetRFC822Header()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IAttach_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 = attach_->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_IAttach_i::Destroy(::CORBA::ULong InstanceID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IAttach_i::Destroy()");

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

	poa_->deactivate_object(oid);

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