/*
 * Implementation file for Brutus test/sample utility functions.
 * Copyright (C) 2004-2007 OMC Denmark ApS.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "brutus_utils.h"
#include <idl_output/definesC.h>
#include <servant_impl/typesS_impl.h>
#include <proptags/proptag.h>

#include <tao/PortableServer/PortableServer.h>
#include <tao/PortableServer/ServantActivatorC.h>
#include <tao/PortableServer/Servant_Base.h>

#define FLUSH() do { fflush(NULL); } while (0)

// Servant Activator
class BrutusActivator : public virtual PortableServer::ServantActivator {
public:
        BrutusActivator()
		{
		};

        ~BrutusActivator()
		{
		};
        
	PortableServer::Servant incarnate(const PortableServer::ObjectId &oid,
					  PortableServer::POA_ptr poa
					  ACE_ENV_ARG_DECL)
		ACE_THROW_SPEC((CORBA::SystemException,
				PortableServer::ForwardRequest))
		{
			throw CORBA::OBJECT_NOT_EXIST();
		};

	void etherealize(const PortableServer::ObjectId &oid,
			 PortableServer::POA_ptr adapter,
			 PortableServer::Servant servant,
			 CORBA::Boolean cleanup_in_progress,
			 CORBA::Boolean remaining_activations
			 ACE_ENV_ARG_DECL)
		ACE_THROW_SPEC((CORBA::SystemException))
		{
			servant->_remove_ref(ACE_ENV_SINGLE_ARG_PARAMETER);
		};
};


PortableServer::POA_ptr get_root_poa(CORBA::ORB_ptr ORB)
{
	PortableServer::POA_var root_poa = PortableServer::POA::_nil();
	if (CORBA::is_nil(ORB))
		return root_poa;

	try {
		CORBA::Object_var obj = ORB->resolve_initial_references ("RootPOA");
		root_poa = PortableServer::POA::_narrow(obj.in());
	}
	catch (...) {
		return PortableServer::POA::_nil();
	}

	return root_poa._retn();
}

PortableServer::POA_ptr create_poa(const char *Name,
				   PortableServer::POAManager_ptr PoaManager,
				   PortableServer::POA_ptr ParentPOA)
{
	PortableServer::POA_var retv = PortableServer::POA::_nil();
	if (CORBA::is_nil(ParentPOA))
		return retv;

	try {
                CORBA::PolicyList pl(7);
                pl.length(7);

                pl[0] = ParentPOA->create_lifespan_policy(PortableServer::TRANSIENT);
                pl[1] = ParentPOA->create_id_assignment_policy(PortableServer::SYSTEM_ID);
                pl[2] = ParentPOA->create_id_uniqueness_policy(PortableServer::UNIQUE_ID);
                pl[3] = ParentPOA->create_implicit_activation_policy(PortableServer::NO_IMPLICIT_ACTIVATION);
                pl[4] = ParentPOA->create_request_processing_policy(PortableServer::USE_SERVANT_MANAGER);
                pl[5] = ParentPOA->create_servant_retention_policy(PortableServer::RETAIN);
                pl[6] = ParentPOA->create_thread_policy(PortableServer::ORB_CTRL_MODEL);

		retv = ParentPOA->create_POA(Name, PoaManager, pl);

                for (int n = 0; n < 7; n++)
                        pl[n]->destroy();

	}
	catch (...) {
		return PortableServer::POA::_nil();
	}

	return retv._retn();
}

void sbinary_to_entryid(::BRUTUS::SBinary & Bin,
			::BRUTUS::ENTRYID & Eid)
{
	for (CORBA::ULong n=0; n<4; n++) 
		Eid.abFlags[n] = Bin[n];

	CORBA::ULong len = Bin.length();
	Eid.ab.length(len-4);

	for (CORBA::ULong n=4; n<len; n++)
		Eid.ab[n-4] = Bin[n];
}

void print_spropvalue(::BRUTUS::SPropValue & PropValue)
{
	CORBA::ULong n;
	CORBA::ULong i;

        switch (PropValue.Value._d()) {
	case ::BRUTUS::BRUTUS_PT_UNSPECIFIED :
		break;
	case ::BRUTUS::BRUTUS_PT_NULL :
		printf("%ld", (long)PropValue.Value.x());
		break;
	case ::BRUTUS::BRUTUS_PT_SHORT :
		printf("%hd (%#.4hx)", (int16_t)PropValue.Value.i(), (uint16_t)PropValue.Value.i());
		break;
	case ::BRUTUS::BRUTUS_PT_USHORT :
		printf("%#.4hx", PropValue.Value.ui());
		break;
        case ::BRUTUS::BRUTUS_PT_LONG :
		if (!(long)PropValue.Value.l())
			printf("%ld (0x%#.8lx)", (long)PropValue.Value.l(), (unsigned long)PropValue.Value.l());
		else
			printf("%ld (%#.8lx)", (long)PropValue.Value.l(), (unsigned long)PropValue.Value.l());
		break;
        case ::BRUTUS::BRUTUS_PT_ULONG :
		printf("%#.8lx", (unsigned long)PropValue.Value.ul());
		break;
	case ::BRUTUS::BRUTUS_PT_FLOAT :
		printf("%f", PropValue.Value.flt());
		break;
	case ::BRUTUS::BRUTUS_PT_DOUBLE :
		printf("%f", PropValue.Value.dbl());
		break;
	case ::BRUTUS::BRUTUS_PT_CURRENCY :
		printf("Lo:%lu Hi:%ld", 
		       (unsigned long)PropValue.Value.cur().Lo, 
		       (long)PropValue.Value.cur().Hi);
		break;
	case ::BRUTUS::BRUTUS_PT_APPTIME :
		printf("%f", PropValue.Value.at());
		break;
	case ::BRUTUS::BRUTUS_PT_ERROR :
		printf("%lu", (unsigned long)PropValue.Value.err());
		break;
	case ::BRUTUS::BRUTUS_PT_BOOLEAN :
		printf("%s", PropValue.Value.b() ? "true" : "false");
		break;
	case ::BRUTUS::BRUTUS_PT_OBJECT :
		printf("Unknown interface");
		break;
	case ::BRUTUS::BRUTUS_PT_LONGLONG : 
		printf("%lld (%#.8llx)", PropValue.Value.li(), PropValue.Value.li());
		break;
	case ::BRUTUS::BRUTUS_PT_STRING8 :
		printf("%s", PropValue.Value.lpszA());
		break;
	case ::BRUTUS::BRUTUS_PT_UNICODE :
		printf("%s", (const char*)PropValue.Value.lpszW());
		break;
	case ::BRUTUS::BRUTUS_PT_SYSTIME :
		printf("dwLowDateTime:%lu dwHighDateTime:%lu",
		       (unsigned long)PropValue.Value.ft().dwLowDateTime,
		       (unsigned long)PropValue.Value.ft().dwHighDateTime);
		break;
	case ::BRUTUS::BRUTUS_PT_CLSID :
		printf("%s", PropValue.Value.lpguid());
		break;
	case ::BRUTUS::BRUTUS_PT_BINARY : 
		if (PropValue.Value.bin().length()) {
			if (!PropValue.Value.bin()[0])
				printf("0x00");
			else
				printf("%#.2hhx", PropValue.Value.bin()[0]);
			for (n=1; n<PropValue.Value.bin().length(); n++) {
				if (!PropValue.Value.bin()[n])
					printf(" 0x00");
				else
					printf(" %#.2hhx", PropValue.Value.bin()[n]);
			}
		}
		break;
	case ::BRUTUS::BRUTUS_PT_MV_SHORT :
		if (PropValue.Value.MVi().length()) {
			if (!PropValue.Value.MVi()[0])
				printf("0x0000");
			else
				printf("%hd", PropValue.Value.MVi()[0]);
			for (n=1; n<PropValue.Value.MVi().length(); n++) {
				if (!PropValue.Value.MVi()[n])
					printf(" 0x0000");
				else
					printf(" %hd", PropValue.Value.MVi()[n]);
			}
		}
		break;
	case ::BRUTUS::BRUTUS_PT_MV_LONG :
		if (PropValue.Value.MVl().length())
			printf("%d", PropValue.Value.MVl()[0]);
		for (n=1; n<PropValue.Value.MVl().length(); n++)
			printf(" %d", PropValue.Value.MVl()[n]);
		break;
	case ::BRUTUS::BRUTUS_PT_MV_FLOAT :
		if (PropValue.Value.MVflt().length())
			printf("%f", PropValue.Value.MVflt()[0]);
		for (n=1; n<PropValue.Value.MVflt().length(); n++)
			printf(" %f", PropValue.Value.MVflt()[n]);
		break;
	case ::BRUTUS::BRUTUS_PT_MV_DOUBLE :
		if (PropValue.Value.MVdbl().length())
			printf("%f", PropValue.Value.MVdbl()[0]);
		for (n=1; n<PropValue.Value.MVdbl().length(); n++)
			printf(" %f", PropValue.Value.MVdbl()[n]);
		break;
	case ::BRUTUS::BRUTUS_PT_MV_CURRENCY :
		if (PropValue.Value.MVcur().length())
			printf("Lo:%lu Hi:%ld", 
			       (unsigned long)PropValue.Value.MVcur()[0].Lo, 
			       (long)PropValue.Value.MVcur()[0].Hi);
		for (n=1; n<PropValue.Value.MVcur().length(); n++)
			printf(" Lo:%lu Hi:%ld",
			       (unsigned long)PropValue.Value.MVcur()[n].Lo,
			       (long)PropValue.Value.MVcur()[n].Hi);
		break;
	case ::BRUTUS::BRUTUS_PT_MV_APPTIME :
		if (PropValue.Value.MVat().length())
			printf("%f", PropValue.Value.MVat()[0]);
		for (n=1; n<PropValue.Value.MVat().length(); n++)
			printf(" %f", PropValue.Value.MVat()[n]);
		break;
	case ::BRUTUS::BRUTUS_PT_MV_SYSTIME :
		if (PropValue.Value.MVft().length())
			printf("dwLowDateTime:%lu dwHighDateTime:%lu", 
			       (unsigned long)PropValue.Value.MVft()[0].dwLowDateTime, 
			       (unsigned long)PropValue.Value.MVft()[0].dwHighDateTime);
		for (n=1; n<PropValue.Value.MVft().length(); n++)
			printf(" dwLowDateTime:%lu dwHighDateTime:%lu", 
			       (unsigned long)PropValue.Value.MVft()[n].dwLowDateTime,
			       (unsigned long)PropValue.Value.MVft()[n].dwHighDateTime);
		break;
	case ::BRUTUS::BRUTUS_PT_MV_STRING8 :
		if (PropValue.Value.MVszA().length())
			printf("%s", (const char*)PropValue.Value.MVszA()[0]);
		for (n=1; n<PropValue.Value.MVszA().length(); n++)
			printf("\n%s", (const char*)PropValue.Value.MVszA()[n]);
		break;
	case ::BRUTUS::BRUTUS_PT_MV_BINARY :
		for (n=0; n<PropValue.Value.MVbin().length(); n++) {
			if (PropValue.Value.MVbin()[n].length())
				printf("%#.2hhx", PropValue.Value.MVbin()[n][0]);
			for (i=1; i<PropValue.Value.MVbin()[n].length(); i++)
				printf(" %#.2hhx", PropValue.Value.MVbin()[n][i]);
			printf("\n");
		}
		break;
	case ::BRUTUS::BRUTUS_PT_MV_UNICODE :
		if (PropValue.Value.MVszA().length())
			printf("%s", (const char*)PropValue.Value.MVszW()[0]);
		for (n=1; n<PropValue.Value.MVszW().length(); n++)
			printf("\n%s", (const char*)PropValue.Value.MVszW()[n]);
		break;
	case ::BRUTUS::BRUTUS_PT_MV_CLSID :
		for (n=0; n<PropValue.Value.MVguid().length(); n++) {
			printf("%s", (const char*)PropValue.Value.MVguid()[n]);
			printf("\n");
		}
		break;
	case ::BRUTUS::BRUTUS_PT_MV_LONGLONG :
		if (PropValue.Value.MVli().length())
			printf("%lld", PropValue.Value.MVli()[0]);
		for (n=1; n<PropValue.Value.MVl().length(); n++)
			printf(" %lld", PropValue.Value.MVli()[n]);
		break;
	default : ;
        }
}

/* You should use stack allocated variables as much as possible for performance
 * reasons. The only reason for the following parameter definitions are that I 
 * want to promote the use of _var types as much as possible.
 */
::BRUTUS::BRESULT BrQueryAllRows(::BRUTUS::IMAPITable_ptr ptable, 
				 ::BRUTUS::SPropTagArray_var & ptags, 
				 ::BRUTUS::SRestrictionContainer_ptr pres,
				 ::BRUTUS::SSortOrderSet_var & psort, 
				 CORBA::Long crowsMax, 
				 ::BRUTUS::SRowSet_out pprows)
{
	::BRUTUS::BRESULT br;

	if (CORBA::is_nil(ptable))
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;

	if (ptags->length()) {
		br = ptable->SetColumns(ptags.in(), 0);
		if (::BRUTUS::BRUTUS_S_OK != br)
			return br;
	}

	if (!CORBA::is_nil(pres)) {
		br = ptable->Restrict(pres, 0);
		if (::BRUTUS::BRUTUS_S_OK != br)
			return br;
	}

	if (psort->aSort.length()) {
		br = ptable->SortTable(psort.in(), 0);
		if (::BRUTUS::BRUTUS_S_OK != br)
			return br;
	}

	if (!crowsMax)
		crowsMax = 0x0fffffff;

	CORBA::ULong status;
 	br = ::BRUTUS::BRUTUS_MAPI_E_BUSY;
 	do { 
 		br = ptable->QueryRows(crowsMax, ::BRUTUS::BRUTUS_TBL_NOADVANCE, pprows);
		if (br == ::BRUTUS::BRUTUS_MAPI_E_BUSY) { 
 			br = ptable->WaitForCompletion(0, 1000, status);
			switch (br) {
			case ::BRUTUS::BRUTUS_MAPI_E_TIMEOUT :
			case ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT :
			case ::BRUTUS::BRUTUS_S_OK :
				br = ::BRUTUS::BRUTUS_MAPI_E_BUSY;
				break;
			default :
				goto out;
			}
 		}
 	} while (br == ::BRUTUS::BRUTUS_MAPI_E_BUSY);

out:
	return br;
}

::BRUTUS::BRESULT BrGetOneProp(::BRUTUS::IMAPIProp_ptr MapiProp, 
			       ::BRUTUS::BDEFINE PropTag, 
			       ::BRUTUS::SPropValue_out PropValue)
{ 
	if (CORBA::is_nil(MapiProp))
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;

	::BRUTUS::SPropTagArray proptag_array;
	proptag_array.length(1);
	proptag_array[0] = PropTag;

	::BRUTUS::seq_SPropValue_var props;
	::BRUTUS::BRESULT br = MapiProp->GetProps(proptag_array,
						  0,
						  props.out());
	if (::BRUTUS::BRUTUS_S_OK == br)
		PropValue = new ::BRUTUS::SPropValue(props[(CORBA::ULong)0]);

	return br;
}

/* I do generally prefer to postpone declaration of variables 
 * until they are needed, but my use of goto makes this approach
 * sub-optimal here.
 */
::BRUTUS::BRESULT get_default_message_store(::BRUTUS::IMAPISession_ptr Session,
					    PortableServer::POA_ptr ParentPoa,
					    ::BRUTUS::IMsgStore_out MsgStore)
{
	// Initiate out value asap
	MsgStore = ::BRUTUS::IMsgStore::_nil();

	// sanity check
	if (CORBA::is_nil(Session) || CORBA::is_nil(ParentPoa))
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	
	::BRUTUS::BRESULT br = ::BRUTUS::BRUTUS_UNKNOWN_ERROR;
        ::BRUTUS::ENTRYID eid;
        CORBA::String_var iid = CORBA::string_dup(""); // use default interface (IMsgStore)
	::BRUTUS::IMsgStore_var msg_store = ::BRUTUS::IMsgStore::_nil();
	::BRUTUS::IMAPITable_var mapi_table = ::BRUTUS::IMAPITable::_nil();;
	::BRUTUS::SRestrictionContainer_var restrict_container = ::BRUTUS::SRestrictionContainer::_nil();
	::BRUTUS::SPropValue_var pv;
	::BRUTUS::SPropertyRestriction_var prop_res;
	::BRUTUS::SRestriction_var res;
	::BRUTUS::SPropTagArray_var proptag_array;
	::BRUTUS::SSortOrderSet_var sort_order;
	::BRUTUS::SRowSet_var row_set; 
	BRUTUS_SPropertyRestriction_i *prop_res_impl;
	BRUTUS_SRestrictionContainer_i *restrict_container_impl;
	PortableServer::ObjectId_var oid;
	CORBA::Object_var obj;
	PortableServer::ServantManager_var activator = PortableServer::ServantManager::_nil();
		
	// create containter poa for restriction container
	PortableServer::POA_var container_poa = create_poa("", PortableServer::POAManager::_nil(), ParentPoa);
	if (CORBA::is_nil(container_poa.in())) 
		return ::BRUTUS::BRUTUS_UNKNOWN_ERROR;

	// activate the container poa
	PortableServer::POAManager_var poa_manager = container_poa->the_POAManager();
	poa_manager->activate();

        // create and register the servant activator in the container poa
	activator = new BrutusActivator();
	container_poa->set_servant_manager(activator.in());

	// create content poa for restriction container
	PortableServer::POA_var content_poa = create_poa("", PortableServer::POAManager::_nil(), container_poa);
	if (CORBA::is_nil(content_poa.in())) 
		return ::BRUTUS::BRUTUS_UNKNOWN_ERROR;

	// activate the content poa
	poa_manager = content_poa->the_POAManager();
	poa_manager->activate();

	// get message store table
	br = Session->GetMsgStoresTable(0, mapi_table.out());
	if (::BRUTUS::BRUTUS_S_OK != br)
		goto exit;

	// initiate SPropValue
	pv = new ::BRUTUS::SPropValue;
	pv->dwAlignPad = 0;
	pv->ulPropTag = ::BRUTUS::BRUTUS_PR_DEFAULT_STORE;
	pv->Value.b(true);

	// create SPropertyRestriction and activate under content POA
	prop_res_impl = new BRUTUS_SPropertyRestriction_i(::BRUTUS::BRUTUS_RELOP_EQ,
							  ::BRUTUS::BRUTUS_PR_DEFAULT_STORE,
							  pv._retn());
	oid = content_poa->activate_object(prop_res_impl);
	obj = content_poa->id_to_reference(oid);
	prop_res = ::BRUTUS::SPropertyRestriction::_narrow(obj);

	// create restriction union
	res = new ::BRUTUS::SRestriction;
	res->resProperty(prop_res._retn());

	// Create restriction container.
	// It is important that the container itself is *not* created
	// under the content POA. The content POA is for resource management 
	// of the content of the restriction container, not the container 
	// itself. All content is registered under the content POA which is
	// destroyed when the container poa is being destroyed.
	restrict_container_impl =  new BRUTUS_SRestrictionContainer_i(res._retn(), content_poa._retn());
	oid = container_poa->activate_object(restrict_container_impl);
	obj = container_poa->id_to_reference(oid);
	restrict_container = ::BRUTUS::SRestrictionContainer::_narrow(obj);

	// only retrieve entry id
	proptag_array = new ::BRUTUS::SPropTagArray;
	proptag_array->length(1);
	proptag_array[0] = ::BRUTUS::BRUTUS_PR_ENTRYID;

	// empty sort order
	sort_order = new ::BRUTUS::SSortOrderSet;
	sort_order->aSort.length(0);

	// get the single row
	br = BrQueryAllRows(mapi_table.in(),
			    proptag_array,
			    restrict_container.in(),
			    sort_order,
			    0,
			    row_set.out());
	if (::BRUTUS::BRUTUS_S_OK != br) 
		goto exit;

	// get entry id from BLOB
	sbinary_to_entryid(row_set[(CORBA::ULong)0][(CORBA::ULong)0].Value.bin(), eid);
	
	br = Session->OpenMsgStore(eid,
				   iid.in(),
				   ::BRUTUS::BRUTUS_MAPI_BEST_ACCESS, 
				   msg_store.out());
	if (::BRUTUS::BRUTUS_S_OK != br) 
		goto exit;

	MsgStore = msg_store._retn();

exit:
	if (::BRUTUS::BRUTUS_S_OK != br) {
		::BRUTUS::MAPIERROR_var mapi_error;
		if (::BRUTUS::BRUTUS_S_OK == Session->GetLastError(br, 0, mapi_error.out()))
			printf("\n\tlpszError     : %s\n\tlpszComponent : %s\n\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
	}

	if (!CORBA::is_nil(mapi_table.in()))
		mapi_table->Destroy(0);

	// destroy the restriction container and its content 
	try { 
		// get the POA manager
		poa_manager = container_poa->the_POAManager();

		// makes sure that all objects are closed from the world
		poa_manager->deactivate(0, 1);

		// initiate etheralize() by calling _remove_ref() on
		// all objects in this poa.
		container_poa->destroy(1, 0);
	}
	catch (const CORBA::Exception &e) {
		e._tao_print_exception("Exception from restrict_container->Destroy()\n");
		FLUSH();
	}

	return br;
}

::BRUTUS::BRESULT get_root_folder(::BRUTUS::IMsgStore_ptr MsgStore,
				  ::BRUTUS::IMAPIFolder_out RootFolder)
{
	// initiate out value asap
	RootFolder = ::BRUTUS::IMAPIFolder::_nil();

	// sanity check
	if (CORBA::is_nil(MsgStore))
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;

	::BRUTUS::BRESULT br;
        ::BRUTUS::ENTRYID eid = { 0 }; // open root folder
        CORBA::String_var iid = CORBA::string_dup(""); // use default interface (IMAPIFolder)
	::BRUTUS::BDEFINE obj_type;
	::BRUTUS::IUnknown_var unk = ::BRUTUS::IUnknown::_nil();
	::BRUTUS::IMAPIFolder_var root_folder;
	
	br = MsgStore->OpenEntry(eid, 
				 iid.in(), 
				 ::BRUTUS::BRUTUS_MAPI_BEST_ACCESS, 
				 obj_type,
				 unk.out());
	if (::BRUTUS::BRUTUS_S_OK != br) 
		goto exit;

	// narrow to folder interface
	root_folder = ::BRUTUS::IMAPIFolder::_narrow(unk.in());
	if (CORBA::is_nil(root_folder)) {
		unk->Destroy(0);
		return ::BRUTUS::BRUTUS_MAPI_E_INTERFACE_NOT_SUPPORTED;
	}

	RootFolder = root_folder._retn();

exit:
	if (::BRUTUS::BRUTUS_S_OK != br) {
		::BRUTUS::MAPIERROR_var mapi_error;
		if (::BRUTUS::BRUTUS_S_OK == MsgStore->GetLastError(br, 0, mapi_error.out()))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
	}

	return br;
}

::BRUTUS::BRESULT get_outlook_folder(::BRUTUS::IMsgStore_ptr MsgStore,
				     ::BRUTUS::BDEFINE PropTag,
				     ::BRUTUS::IMAPIFolder_out Folder)
{
	// initiate out value asap
	Folder = ::BRUTUS::IMAPIFolder::_nil();

	// sanity check
	if (CORBA::is_nil(MsgStore))
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;

	::BRUTUS::BRESULT br;
        ::BRUTUS::ENTRYID eid;
        CORBA::String_var iid = CORBA::string_dup(""); // use default interface (IMAPIFolder)
	::BRUTUS::BDEFINE obj_type;
	::BRUTUS::IUnknown_var unk = ::BRUTUS::IUnknown::_nil();
	::BRUTUS::IMAPIFolder_var root_folder;
	::BRUTUS::SPropValue_var pv;
	
	br = get_root_folder(MsgStore, 
			     root_folder.out());
	if (::BRUTUS::BRUTUS_S_OK != br) 
		goto exit;

	br = BrGetOneProp(root_folder.in(), 
			  PropTag, 
			  pv.out());
	if (::BRUTUS::BRUTUS_S_OK != br) 
		goto exit;

	// get entry id from BLOB
	sbinary_to_entryid(pv->Value.bin(), eid);

	// open the outlook folder
	br = root_folder->OpenEntry(eid, 
				    iid.in(), 
				    ::BRUTUS::BRUTUS_MAPI_BEST_ACCESS, 
				    obj_type,
				    unk.out());
	if (::BRUTUS::BRUTUS_S_OK != br) 
		goto exit;

	// narrow to folder interface
	Folder = ::BRUTUS::IMAPIFolder::_narrow(unk.in());
	if (CORBA::is_nil(Folder)) {
		br = ::BRUTUS::BRUTUS_MAPI_E_INTERFACE_NOT_SUPPORTED;
		goto exit;
	}

exit:
	if (::BRUTUS::BRUTUS_S_OK != br) {
		::BRUTUS::MAPIERROR_var mapi_error;
		if (::BRUTUS::BRUTUS_S_OK == MsgStore->GetLastError(br, 0, mapi_error.out()))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
	}

	// release server resources
	if (!CORBA::is_nil(root_folder.in()))
		root_folder->Destroy(0);

	return br;
}

::BRUTUS::BRESULT list_folder(::BRUTUS::IMAPIFolder_ptr Folder,
			      ::BRUTUS::BDEFINE ListBy)
{
	// sanity check
	if (CORBA::is_nil(Folder))
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;

	::BRUTUS::BRESULT br;
	::BRUTUS::IMAPITable_var contents_table;
	::BRUTUS::SPropTagArray_var proptag_array;
	::BRUTUS::SRowSet_var row_set; 
	::BRUTUS::SSortOrderSet_var sort_order;

	br = Folder->GetContentsTable(0,
				      contents_table.out()); 
	if (::BRUTUS::BRUTUS_S_OK != br)
		goto exit;

	// we are only interested in the ListBy property
	proptag_array = new ::BRUTUS::SPropTagArray;
	proptag_array->length(1);
	proptag_array[0] = ListBy;

	// empty sort order
	sort_order = new ::BRUTUS::SSortOrderSet;
	sort_order->aSort.length(0);

	// get the single row
	br = BrQueryAllRows(contents_table.in(),
			    proptag_array,
			    ::BRUTUS::SRestrictionContainer::_nil(),
			    sort_order,
			    0,
			    row_set.out());
	if (::BRUTUS::BRUTUS_S_OK != br) 
		goto exit;

	// write the object values to stdout
	if (row_set->length()) {
		for (CORBA::ULong n=0; n<row_set->length(); n++) {
			printf("        %d: ", n);
			print_spropvalue(row_set[n][(CORBA::ULong)0]);
			printf("\n");
		}
	}

exit:
	if (::BRUTUS::BRUTUS_S_OK != br) {
		::BRUTUS::MAPIERROR_var mapi_error;
		if (::BRUTUS::BRUTUS_S_OK == Folder->GetLastError(br, 0, mapi_error.out()))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
	}

	// release server resources
	if (!CORBA::is_nil(contents_table.in()))
		contents_table->Destroy(0);

	return br;
}

::BRUTUS::BRESULT print_properties(::BRUTUS::IMAPIProp_ptr PropObject)
{
	::BRUTUS::BRESULT br;
	::BRUTUS::seq_SPropValue_var props;
	::BRUTUS::SPropTagArray_var proptag_array = new ::BRUTUS::SPropTagArray;
	CPropTag prop_cnv;

	proptag_array->length(0);
	br = PropObject->GetProps(proptag_array.in(),
				  0,
				  props.out());
	if ((::BRUTUS::BRUTUS_S_OK != br) && (::BRUTUS::BRUTUS_MAPI_W_ERRORS_RETURNED != br))
		goto exit;

	for (CORBA::ULong n=0; n<props->length(); n++) {
		printf("%#.8lx (%s/%s) :\n", 
		       (unsigned long)props[n].ulPropTag, 
		       prop_cnv.proptag_to_str(props[n].ulPropTag), 
		       prop_cnv.proptype_to_str(props[n].ulPropTag & 0x0000ffff));
		print_spropvalue(props[n]);
		printf("\n\n");
	}

exit:
	if (::BRUTUS::BRUTUS_S_OK != br) {
		::BRUTUS::MAPIERROR_var mapi_error;
		if (::BRUTUS::BRUTUS_S_OK == PropObject->GetLastError(br, 0, mapi_error.out()))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
	}


	return br;
}
