/*
 * Implementation file for a Brutus sample and test application.
 * 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/>.
 */

// system includes
#include <signal.h>

// Test system includes
#include <config/config.h>
#include <bresult/bresult.h>
#include <init_orb/init_orb.h>
#include <brutus_utils/brutus_utils.h>
#include <servant_impl/BrutusCheckS_impl.h>
#include <servant_impl/IMAPIAdviseSinkS_impl.h>

// Brutus includes
#include <idl_output/BrutusLogOnC.h>
#include <idl_output/IMAPISessionC.h>
#include <idl_output/IMAPIFolderC.h>
#include <idl_output/typesC.h>

// Lorica
#include <idl_output/ReferenceMapperC.h>

// ACE includes
#include <ace/OS.h>
#include <ace/Task.h>

#define CONFIG_FILE "../../config/client.conf"
#define FLUSH() do { fflush(NULL); } while (0)

static volatile int global_notice = 0;

void signal_handler(int signal)
{
        global_notice = 1;
}


// thread entry class
class ORB_Thread_Base : public ACE_Task_Base {
public:
        ORB_Thread_Base(void)
                {
                };

        void set_orb(CORBA::ORB_ptr Broker)
                {
                        orb_ = CORBA::ORB::_duplicate(Broker);
                };

        virtual int svc(void)
                {
                        if (CORBA::is_nil(orb_))
                                throw 1;

                        orb_->run();

                        return 0;
                };

private:
        CORBA::ORB_var orb_;
};

// thread entry class
class NOTIFY_Thread_Base : public ACE_Task_Base {
public:
        NOTIFY_Thread_Base(void)
                : notice_(0),
                  sink_(NULL),
                  sink_obj_(::BRUTUS::IMAPIAdviseSink::_nil())
                {
                };

        // must be invoked *after* Unadvise()
        void destroy_sink(void)
                {
                        PortableServer::ObjectId_var oid = poa_->servant_to_id(sink_);
                        poa_->deactivate_object(oid);
                };

        void set_poa(PortableServer::POA_ptr Poa)
                {
                        poa_ = PortableServer::POA::_duplicate(Poa);
                };

        template <class T> ::BRUTUS::BRESULT advise(T Entity,
                                                    BRUTUS::BDEFINE Mask,
                                                    CORBA::ULong & Connection)
                {
                        // Create sink object
                        sink_ = new BRUTUS_IMAPIAdviseSink_i(&notice_);
                        PortableServer::ObjectId_var oid = poa_->activate_object(sink_);
                        CORBA::Object_var obj = poa_->id_to_reference(oid);
                        sink_obj_ = ::BRUTUS::IMAPIAdviseSink::_narrow(obj);

                        // notify on all contained objects
                        ::BRUTUS::ENTRYID eid = { 0 };

                        return Entity->Advise(eid,
                                              Mask,
                                              sink_obj_.in(),
                                              Connection);
                }

        // Will exit when something happens (notice != 0).
        // That something is a notification, a destruction
        // of the sink on the server side or SIGINT locally.
        virtual int svc(void)
                {
                        while (!notice_) {
                                printf(".");
                                FLUSH();
                                ACE_OS::sleep(3);
                                if (global_notice)
                                        notice_ = 1;
                        }
                        return 0;
                };

private:
        unsigned long notice_;
        BRUTUS_IMAPIAdviseSink_i *sink_;
        ::BRUTUS::IMAPIAdviseSink_var sink_obj_;
        PortableServer::POA_var poa_;
};


// will realloc and strcat src to *dest, freeing *dest if
// not successfull. Returns a pointer to the longer string.
static inline char *append_string(char **dest, const char *src)
{
        char *retv;
        size_t size;


        if (!*dest) {
                size = sizeof(char) * (strlen(src) + 1);
                retv = (char*)calloc(size, sizeof(char));
        } else {
                size = sizeof(char) * (strlen(*dest) + strlen(src) + 1);
                retv = (char*)realloc((void*)*dest, size);
        }
        if (!retv) {
                free(*dest);
                return NULL;
        }

        strcat(retv, src);
        *dest = retv;

        return *dest;
}


/*
 * Will return a pointer to a properly initialized
 * Config object or NULL on error.
 */
Config *get_config(const char *config_file)
{
        int load_err = 0;
        Config *retv = NULL;

        try {
                retv = new Config();
        }
        catch (std::bad_alloc &) {
                printf("No memory\n");
                FLUSH();
                goto err_out;
        }
        if (!retv) {
                printf("No memory\n");
                goto err_out;
        }

        // load configuration
        load_err = retv->load(config_file);
        if (load_err) {
                char msg[128] = {'\0'};
                if (load_err > 0)
                        snprintf(msg, 128, "Configuration error: Error in config file at line %d\n", load_err);
                else
                        snprintf(msg, 128, "Configuration error: Could not open %s\n", config_file);

                printf(msg);
                goto err_out;
        }

        return retv;

err_out:
        if (retv)
                delete retv;

        return NULL;
}


/*
 * Will check a string for conversion into an "bits" bit wide
 * integer number. Returns non-zero on error.
 */
int get_xbit_number(const unsigned char bits, const char *num_str, unsigned long *ul)
{
        unsigned long max;

        switch (bits) {
        case 8 :
                max = 0xff;
                break;
        case 16 :
                max = 0xffff;
                break;
        case 32 :
                max = 0xffffffff;
                break;
        default :
                return -1;
        }

        if (!num_str)
                return -1;

        if (*num_str == '-')
                return -1;

        char *tmp;
        unsigned long num = strtoul(num_str, &tmp, 10);
        if (errno == ERANGE)
                return -1;

        if (*tmp != '\0')
                return -1;

        if (num > max)
                return -1;

        *ul = (unsigned long)num;

        return 0;
}

static ::Lorica::ReferenceMapper_ptr
get_lorica(CORBA::ORB_ptr orb)
{
	CORBA::Object_var obj = CORBA::Object::_nil();
        char *corbaloc_str[512] = { '\0' };
        ::Lorica::ReferenceMapper_var mapper = Lorica::ReferenceMapper::_nil();

        snprintf((char*)corbaloc_str,
                 sizeof(corbaloc_str), 
                 "corbaloc:iiop:1.2@localhost:%u/%s",
                 ::Lorica::ReferenceMapper::lorica_in_port, 
                 ::Lorica::ReferenceMapper::IOR_TABLE_KEY);
        try {
                obj = orb->string_to_object((const char*)corbaloc_str);
        }
        catch (...) {
                goto no_lorica;
        }
	try {
		mapper = ::Lorica::ReferenceMapper::_narrow(obj.in());
	}
	catch (const CORBA::Exception &e) {
		e._tao_print_exception("could not narrow to ::BRUTUS::BrutusLogOn\n");
		FLUSH();
		goto no_lorica;
	}
	catch (...) {
		printf("ERROR - Unknown exception caught in _narrow()\n");
		FLUSH();
		goto no_lorica;
	}
	if (CORBA::is_nil(mapper.in())) 
		goto no_lorica;

        try {
                if (mapper->_non_existent())
                        goto no_lorica;
        }
        catch (...) {
                goto no_lorica;
        }

        goto lorica_ok;
        
no_lorica:
        return ::Lorica::ReferenceMapper::_nil();
        
lorica_ok:
        return mapper._retn();
}

/*
 * Brutus source file for implementing a value to string helper class.
 * 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/>.
 */
void print_legalese(const char *prog_name)
{
        printf("\n");
        printf("                      -*- DISCLAIMER START -*-\n");
        printf("\n");
        printf("  * %s - A sample program that connects to a Brutus server.\n", prog_name);
        printf("  * Copyright (C) 2004-2007 OMC Denmark ApS\n");
        printf("  * \n");
        printf("  * This program is free software; you can redistribute it and/or modify\n");
        printf("  * it under the terms of the GNU General Public License as published by\n");
        printf("  * the Free Software Foundation; either version 3 of the License, or\n");
        printf("  * (at your option) any later version.\n");
        printf("  * \n");
        printf("  * This program is distributed in the hope that it will be useful,\n");
        printf("  * but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
        printf("  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
        printf("  * GNU General Public License for more details.\n");
        printf("  * \n");
        printf("  * You should have received a copy of the GNU General Public License\n");
        printf("  * along with this program;  If not, see <http://www.gnu.org/licenses/>\n");
        printf("\n");
        printf("                       -*- DISCLAIMER END -*-\n");
        printf("\n");
}

/*
 * All local variables has been defined at the start of the
 * function. I would normally prefer to postpone the declarations
 * until they are actually needed, but my use of goto's (which would
 * predate some declarations) is making that approach sub-optimal.
 */
int main(int argc, char **argv)
{
        // initiate signal handling
        signal(SIGINT, signal_handler);

        int retv = 1;
        bool corba_started = 0;

        // Configuration
        bool use_ssl; // if false use iiop
        bool use_corbaloc; // if false use resolve_initial_references()
        bool notifications; // establish an advise sink if true
        char *logon_obj_server_key = NULL;
        char *brutus_logon_corbaloc_str = NULL;
        char *brutus_server = NULL;
        char *profile = NULL;
        char *password = NULL;
        char *exch_server = NULL;
        char *exch_mailbox = NULL;
        char *windows_user = NULL;
        char *windows_user_domain = NULL;
        char *windows_user_password = NULL;
        uint16_t brutus_iiop_port = 0;
        uint16_t brutus_ssl_port = 0;

        // Configuration file helper class
        Config *conf = NULL;

        // "BRESULT to const char*" helper class
        CBresult brcnv;
        ::BRUTUS::BRESULT br;

        // Corba
        CORBA::ORB_var orb = CORBA::ORB::_nil();
        PortableServer::POA_var root_poa = PortableServer::POA::_nil();
        PortableServer::POAManager_var root_poa_manager = PortableServer::POAManager::_nil();

        // Pure Brutus objects with no MAPI counterpart
        ::BRUTUS::BrutusLogOn_var brutus_logon = ::BRUTUS::BrutusLogOn::_nil();
        ::BRUTUS::BrutusCheck_var brutus_check = ::BRUTUS::BrutusCheck::_nil();
        BRUTUS_BrutusCheck_i * brutus_check_impl = NULL;
        ::CORBA::ULong instance_id = 0;

        // Brutus objects with a MAPI counterpart
        ::BRUTUS::IMAPISession_var mapi_session = ::BRUTUS::IMAPISession::_nil();
        ::BRUTUS::IMsgStore_var msg_store = ::BRUTUS::IMsgStore::_nil();
        ::BRUTUS::IMAPIFolder_var mapi_folder = ::BRUTUS::IMAPIFolder::_nil();
        ::BRUTUS::IMAPITable_var mapi_table = ::BRUTUS::IMAPITable::_nil();
        CORBA::ULong advise_connection = 0;

        // ACE
        ORB_Thread_Base orb_thread;
        NOTIFY_Thread_Base notify_thread;


        // Print license information
        print_legalese(argv[0]);

        printf("\nConfiguration file is \"%s\"\n", CONFIG_FILE);
        printf("    Testing load of configuration file.. ");
        {
                conf = get_config(CONFIG_FILE);
                if (!conf) {
                        printf("ERROR\n");
                        goto out;
                }
                printf("OK\n");
        }

        printf("\nParsing configuration file.. \n");
        printf("    Use SSL.. ");
        {
                char *use_ssl_str = conf->get_value("USE_SSL");
                if ((use_ssl_str) && (!strcmp(use_ssl_str, "yes"))) {
                        printf("YES\n");
                        use_ssl = true;
                } else {
                        printf("NO\n");
                        use_ssl = false;
                }
                free(use_ssl_str);
        }

        printf("    Getting BrutusLogOn server object key.. ");
        {
                logon_obj_server_key = conf->get_value("BRUTUS_LOGON_OBJECT_SERVER_KEY");
                if (logon_obj_server_key) {
                        printf("OK, \"%s\"\n", logon_obj_server_key);
                } else {
                        printf("ERROR\n");
                        goto out;
                }
        }

        printf("    Getting Brutus server address.. ");
        {
                brutus_server = conf->get_value("BRUTUS_SERVER");
                if (brutus_server) {
                        printf("OK, \"%s\"\n", brutus_server);
                } else {
                        printf("ERROR\n");
                        goto out;
                }
        }

        printf("    Getting Brutus server SSL port.. ");
        {
                char *ssl_port = conf->get_value("BRUTUS_SERVER_SSL_PORT");
                unsigned long n;
                if (ssl_port) {
                        if (!get_xbit_number(16, ssl_port, &n)) {
                                brutus_ssl_port = (uint16_t)n;
                                printf("OK, \"%hu\"\n", brutus_ssl_port);
                        } else {
                                free(ssl_port);
                                printf("conversion error\n");
                                goto out;
                        }
                        free(ssl_port);
                } else {
                        printf("not defined\n");
                }
        }

        printf("    Getting Brutus server IIOP port.. ");
        {
                char *iiop_port = conf->get_value("BRUTUS_SERVER_IIOP_PORT");
                unsigned long n;
                if (iiop_port) {
                        if (!get_xbit_number(16, iiop_port, &n)) {
                                brutus_iiop_port = (uint16_t)n;
                                printf("OK, \"%hu\"\n", brutus_iiop_port);
                        } else {
                                free(iiop_port);
                                printf("conversion error\n");
                                goto out;
                        }
                        free(iiop_port);
                } else {
                        printf("not defined\n");
                }
        }

        printf("        Sanity checking Brutus server ports.. ");
        {
                if (use_ssl) {
                        if (!brutus_ssl_port) {
                                printf("ERROR - No SSL port defined\n");
                                goto out;
                        }
                } else {
                        if (!brutus_iiop_port) {
                                printf("ERROR - No IIOP port defined\n");
                                goto out;
                        }
                }
                if (!brutus_ssl_port && !brutus_iiop_port) {
                        printf("ERROR - No ports defined\n");
                        goto out;
                }
                printf("ok, using port %hu\n", use_ssl ? brutus_ssl_port : brutus_iiop_port);
        }

        printf("    Getting MAPI Profile name.. ");
        {
                profile = conf->get_value("MAPI_PROFILE");
                if (profile) {
                        printf("OK, \"%s\"\n", profile);
                } else {
                        printf("ERROR\n");
                        goto out;
                }
        }

        printf("    Getting MAPI Profile password.. ");
        {
                password = conf->get_value("MAPI_PROFILE_PASSWORD");
                if (password) {
                        printf("OK, \"%s\"\n", password);
                } else {
                        printf("ERROR\n");
                        goto out;
                }
        }

        printf("    Getting Exchange server.. ");
        {
                exch_server = conf->get_value("EXCHANGE_SERVER");
                if (exch_server) {
                        printf("OK, \"%s\"\n", exch_server);
                } else {
                        printf("ERROR\n");
                        goto out;
                }
        }

        printf("    Getting Exchange mailbox.. ");
        {
                exch_mailbox = conf->get_value("EXCHANGE_MAILBOX");
                if (exch_mailbox) {
                        printf("OK, \"%s\"\n", exch_mailbox);
                } else {
                        printf("ERROR\n");
                        goto out;
                }
        }

        printf("    Getting Windows user.. ");
        {
                windows_user = conf->get_value("WINDOWS_USER");
                if (windows_user) {
                        printf("OK, \"%s\"\n", windows_user);
                } else {
                        printf("ERROR\n");
                        goto out;
                }
        }

        printf("    Getting Windows user domain.. ");
        {
                windows_user_domain = conf->get_value("WINDOWS_USER_DOMAIN");
                if (windows_user_domain) {
                        printf("OK, \"%s\"\n", windows_user_domain);
                } else {
                        printf("ERROR\n");
                        goto out;
                }
        }

        printf("    Getting Windows user password.. ");
        {
                windows_user_password = conf->get_value("WINDOWS_USER_PASSWORD");
                if (windows_user_password) {
                        printf("OK, \"%s\"\n", windows_user_password);
                } else {
                        printf("ERROR\n");
                        goto out;
                }
        }

        printf("    Choosing initial bootstrap method.. ");
        {
                char *use_corbaloc_str = conf->get_value("USE_CORBALOC_BOOTSTRAP");
                if ((use_corbaloc_str) && (!strcmp(use_corbaloc_str, "yes"))) {
                        printf("corbaloc\n");
                        use_corbaloc = true;
                } else {
                        printf("resolve_initial_references()\n");
                        use_corbaloc = false;
                }
                free(use_corbaloc_str);
        }
        if (use_corbaloc) {
                printf("    Composing corbaloc strings.. ");

                char brutus_port[32] = { '\0' };
                snprintf(brutus_port, sizeof(brutus_port), "%hu", use_ssl ? brutus_ssl_port : brutus_iiop_port);

                append_string(&brutus_logon_corbaloc_str, use_ssl ? "corbaloc:ssliop:" : "corbaloc:iiop:1.2@");
                append_string(&brutus_logon_corbaloc_str, (const char*)brutus_server);
                append_string(&brutus_logon_corbaloc_str, ":");
                append_string(&brutus_logon_corbaloc_str, (const char*)brutus_port);
                append_string(&brutus_logon_corbaloc_str, "/");
                append_string(&brutus_logon_corbaloc_str, (const char*)logon_obj_server_key);

                if (brutus_logon_corbaloc_str) {
                        printf("OK.\n");
                        printf("        BrutusLogOn corbaloc string   : \"%s\"\n", brutus_logon_corbaloc_str);

                } else {
                        printf("ERROR\n");
                        goto out;
                }
        }

        printf("    Test notifications.. ");
        {
                char *notify_str = conf->get_value("ADVISE_SINKS");
                if ((notify_str) && (!strcmp(notify_str, "yes"))) {
                        printf("YES\n");
                        notifications = true;
                } else {
                        printf("NO\n");
                        notifications = false;
                }
                free(notify_str);
        }

        printf("\nInitializing CORBA.. \n");
        printf("    Invoking ORB_Init().. ");
        {
                Init_ORB orb_factory;

                orb = orb_factory.create(argc,
                                         argv,
                                         use_ssl,
                                         "BrutusLogOn",
                                         logon_obj_server_key,
                                         brutus_server,
                                         use_ssl ? brutus_ssl_port : brutus_iiop_port,
                                         "Brutus_Client_ORB");
                if (CORBA::is_nil(orb.in())) {
                        printf("ERROR - could not initialize the ORB\n");
                        goto out;
                }
                printf("OK\n");
        }

        printf("    Getting root POA.. ");
        {
                root_poa = get_root_poa(orb.in());
                if (CORBA::is_nil(root_poa.in())) {
                        printf("ERROR - could not get the root POA\n");
                        goto out;
                }
                printf("OK\n");
        }

        printf("    Getting root POAManager.. ");
        {
                root_poa_manager = root_poa->the_POAManager();
                if (CORBA::is_nil(root_poa_manager.in())) {
                        printf("ERROR - could not get the root POAManager\n");
                        goto out;
                }
                root_poa_manager->activate();
                printf("OK\n");
        }

        printf("    Starting ORB main loop.. ");
        {
                orb_thread.set_orb(orb.in());
                orb_thread.activate();
                ACE_OS::sleep(2);
                corba_started = 1;
                printf("OK\n");
        }

        printf("\nBootstrapping Brutus.. \n");
        printf("    Getting reference to BrutusLogOn object.. ");
        {
                /*
                 * Get a reference to the BrutusLogOn object.
                 *
                 * Use can use the resolve_initial_references() method
                 * or a corbaloc stringified object reference.
                 *
                 * I'll really recommend the former method as it is the
                 * standard way of bootstrapping a corba server, but you
                 * may be unfortunate enough to be using an ORB where
                 * this method is unavailable.
                 */
                CORBA::Object_var obj = CORBA::Object::_nil();
                try {
                        printf("\n        resolving.. ");
                        if (use_corbaloc)
                                obj = orb->string_to_object(brutus_logon_corbaloc_str);
                        else
                                obj = orb->resolve_initial_references("BrutusLogOn");
                        printf("OK\n");
                }
                catch (const CORBA::ORB::InvalidName &e) {
                        e._tao_print_exception("Could not resolve BrutusLogOn\n");
                        FLUSH();
                        goto out;
                }
                catch (const CORBA::Exception &e) {
                        e._tao_print_exception("CORBA exception caught\n");
                        FLUSH();
                        goto out;
                }
                catch (...) {
                        printf("ERROR - Unknown exception caught in resolve_initial_references()\n");
                        FLUSH();
                        goto out;
                }
                if (CORBA::is_nil(obj.in())) {
                        printf("ERROR - Could not resolve BrutusLogOn, NIL object returned\n");
                        goto out;
                }

                try {
                        printf("        narrowing.. ");
                        brutus_logon = ::BRUTUS::BrutusLogOn::_narrow(obj.in());
                        printf("OK\n");
                }
                catch (const CORBA::Exception &e) {
                        e._tao_print_exception("could not narrow to ::BRUTUS::BrutusLogOn\n");
                        FLUSH();
                        goto out;
                }
                catch (...) {
                        printf("ERROR - Unknown exception caught in _narrow()\n");
                        FLUSH();
                }
                if (CORBA::is_nil(brutus_logon.in())) {
                        printf("ERROR - could not narrow to ::BRUTUS::BrutusLogOn, NIL object returned\n");
                        goto out;
                }
        }

	printf("\nRegistering in Lorica.. ");
	{
                CORBA::Object_var obj = CORBA::Object::_nil();
		BRUTUS::BrutusLogOn_var brutus_logon_mapped = ::BRUTUS::BrutusLogOn::_nil();
		::Lorica::ReferenceMapper_var lorica = get_lorica(orb.in());
		if (CORBA::is_nil(lorica)) {
			printf("Not using Lorica\n");
			goto proceed;
		}

		if (use_corbaloc) {
			try {
				obj = lorica->as_client_with_corbaloc(brutus_logon_corbaloc_str, 
								      "IDL:omc.brutus/BRUTUS/BrutusLogOn:1.0");
			}
			catch (const CORBA::Exception &e) {
				e._tao_print_exception("Exception from as_client_with_corbaloc()\n");
				FLUSH();
				goto proceed;
			}
			catch (...) {
				printf("ERROR - Unknown exception caught from as_client_with_corbaloc()\n");
				FLUSH();
				goto proceed;
			}
		} else {
			try {
				obj = lorica->as_client(brutus_logon);
			}
			catch (const CORBA::Exception &e) {
				e._tao_print_exception("Exception from as_client()\n");
				FLUSH();
				goto proceed;
			}
			catch (...) {
				printf("ERROR - Unknown exception caught from as_client()\n");
				FLUSH();
				goto proceed;
			}
		}
		try {
			brutus_logon_mapped = ::BRUTUS::BrutusLogOn::_narrow(obj.in());
		}
                catch (const CORBA::Exception &e) {
                        e._tao_print_exception("could not narrow to ::BRUTUS::BrutusLogOn\n");
                        FLUSH();
                        goto out;
                }
                catch (...) {
                        printf("ERROR - Unknown exception caught in _narrow()\n");
                        FLUSH();
                }

		if (CORBA::is_nil(brutus_logon_mapped)) {
			printf("Could not map BrutusLogOn as client\n");
			goto proceed;
		}

		brutus_logon = brutus_logon_mapped._retn();

		printf("OK\n");
	}
	proceed:

        printf("\nStarting Brutus operations.. \n");
	printf("    Getting Brutus Server version.. ");
	{
		::BRUTUS::BrutusLogOn::BrutusVersion_var brutus_server_version = new ::BRUTUS::BrutusLogOn::BrutusVersion;
		try {
			brutus_logon->GetVersion(brutus_server_version.out());
                }
                catch (const CORBA::Exception &e) {
                        e._tao_print_exception("could not get Brutus Server version\n");
                        FLUSH();
                        goto out;
                }
                catch (...) {
                        printf("ERROR - Unknown exception caught in GetVersion()\n");
                        FLUSH();
			goto out;
                }
		printf("%d.%d.%d-%d ", brutus_server_version->major, brutus_server_version->minor, brutus_server_version->micro, brutus_server_version->release_tag);
		printf("OK\n");
	}


        printf("    Logging on using the MAPI profile.. ");
        {
                brutus_check_impl = new BRUTUS_BrutusCheck_i();
                {
                        PortableServer::ObjectId_var oid = root_poa->activate_object(brutus_check_impl);
                        CORBA::Object_var obj = root_poa->id_to_reference(oid);
                        brutus_check = ::BRUTUS::BrutusCheck::_narrow(obj);
                }

                br = brutus_logon->Logon(brutus_check.in(),
                                         profile,
                                         "",
                                         windows_user,
                                         windows_user_domain,
                                         windows_user_password,
                                         exch_mailbox,
                                         exch_server,
                                         ::BRUTUS::BRUTUS_MAPI_EXPLICIT_PROFILE,
                                         0,
                                         ::BRUTUS::SERVER_TYPE_EXCHANGE,
                                         instance_id,
                                         mapi_session.out());
                if (::BRUTUS::BRUTUS_S_OK == br)
                        printf("OK\n");
                else {
                        printf("ERROR - BrutusLogOn->Logon() returned %s\n", brcnv.bresult_to_str(br));
                        goto out;
                }
        }

	printf("    Pinging mailbox.. ");
	{
		try {
			mapi_session->ping();
                }
                catch (const CORBA::Exception &e) {
                        e._tao_print_exception("Exception from IMAPISession->ping()\n");
                        FLUSH();
                }
                catch (...) {
                        printf("ERROR - Unknown exception caught from IMAPISession->ping()\n");
                        FLUSH();
                }
	}

        printf("    Getting default message store.. ");
        {
                try {
                        br = get_default_message_store(mapi_session.in(),
                                                       root_poa.in(),
                                                       msg_store.out());
                }
                catch (const CORBA::Exception &e) {
                        e._tao_print_exception("Exception from get_default_message_store()\n");
                        FLUSH();
                        goto out;
                }
                catch (...) {
                        printf("ERROR - Unknown exception caught from get_default_message_store()\n");
                        FLUSH();
                }

                if (::BRUTUS::BRUTUS_S_OK == br)
                        printf("OK\n");
                else {
                        printf("ERROR - get_default_message_store() returned %s\n", brcnv.bresult_to_str(br));
                        goto out;
                }
        }

        printf("    Printing default message store properties.. \n");
        {
                br = print_properties(msg_store.in());
                if (::BRUTUS::BRUTUS_S_OK == br)
                        printf("OK\n");
                else {
                        printf("ERROR - print_properties() returned %s\n", brcnv.bresult_to_str(br));
                        goto out;
                }
        }

        printf("    Getting Outlook Calendar folder.. ");
        {
                br = get_outlook_folder(msg_store.in(),
                                        ::BRUTUS::BRUTUS_PR_OUTLOOK_CALENDAR,
                                        mapi_folder.out());
                if (::BRUTUS::BRUTUS_S_OK == br)
                        printf("OK\n");
                else {
                        printf("ERROR - get_outlook_folder() returned %s\n", brcnv.bresult_to_str(br));
                        goto out;
                }
        }

        printf("    Printing Outlook folder properties.. \n");
        {
                br = print_properties(mapi_folder.in());
                if (::BRUTUS::BRUTUS_S_OK == br)
                        printf("OK\n");
                else {
                        printf("ERROR - print_properties() returned %s\n", brcnv.bresult_to_str(br));
                        goto out;
                }
        }

        printf("    Listing Outlook folder content by PR_SUBJECT.. \n");
        {
                br = list_folder(mapi_folder.in(),
                                 ::BRUTUS::BRUTUS_PR_SUBJECT);
                if (::BRUTUS::BRUTUS_S_OK == br)
                        printf("    ok\n");
                else {
                        printf("ERROR - list_folder() returned %s\n", brcnv.bresult_to_str(br));
                        goto out;
                }
        }

        printf("    Establishing a notification sink on the default message store.. ");
        {
                if (notifications) {
                        BRUTUS::BDEFINE mask = ::BRUTUS::BRUTUS_fnevCriticalError
                                | ::BRUTUS::BRUTUS_fnevNewMail
                                | ::BRUTUS::BRUTUS_fnevObjectCreated
                                | ::BRUTUS::BRUTUS_fnevObjectDeleted
                                | ::BRUTUS::BRUTUS_fnevObjectModified
                                | ::BRUTUS::BRUTUS_fnevObjectMoved
                                | ::BRUTUS::BRUTUS_fnevObjectCopied
                                | ::BRUTUS::BRUTUS_fnevSearchComplete
                                | ::BRUTUS::BRUTUS_fnevExtended;
                        notify_thread.set_poa(root_poa.in());
                        br = notify_thread.advise(msg_store.in(),
                                                  mask,
                                                  advise_connection);
                        if (::BRUTUS::BRUTUS_S_OK != br) {
                                printf("ERROR - advise() returned %s\n", brcnv.bresult_to_str(br));
                                goto out;
                        }

                        notify_thread.activate();
                        printf("OK\n");
                } else
                        printf("skipped\n");
        }

        printf("    Waiting for event.. ");
        {
                if (notifications) {
                        notify_thread.wait();
                        if (advise_connection) {
                                msg_store->Unadvise(advise_connection);
                                notify_thread.destroy_sink();
                                printf("\n    Unadvised ");
                        }
                        printf("OK\n");
                } else
                        printf("skipped\n");
        }

        // No errors it seems...
        retv = 0;

out:

        // All Brutus objects must be Destroy()'ed to prevent leaks
        // on the server side except for the BrutusLogOn object, as
        // it doesn't have or need a Destroy() method. The session
        // object *must* be destroyed last.
        if (!CORBA::is_nil(mapi_folder.in()))
                mapi_folder->Destroy(0);
        if (!CORBA::is_nil(msg_store.in()))
                msg_store->Destroy(0);

        // This is crucial. You *must* Destroy() the session object
        // or you will not be able to logon until the server is
        // restarted. The reason is that a session specific POA is
        // created in the server thus preventing multiple simultaneous
        // sessions for the same profile. This is a bug which I must
        // fix soonish. It shouldn't be to hard though...
        if (!CORBA::is_nil(mapi_session.in()))
                mapi_session->Destroy(instance_id);

        if (conf)
                delete conf;

        free(brutus_logon_corbaloc_str);
        free(logon_obj_server_key);
        free(brutus_server);
        free(exch_server);
        free(exch_mailbox);
        free(profile);
        free(password);
        free(windows_user);
        free(windows_user_domain);
        free(windows_user_password);

        printf("\n\nShutting down CORBA.. ");
        {
                if (corba_started) {
                        root_poa_manager->deactivate(1, 1);
                        root_poa->destroy(1, 1);
                        orb->shutdown(1);
                        orb_thread.wait();
                        printf("OK\n");
                } else
                        printf("not started\n");
        }

        if (retv) {
                printf("\n\nFAILURE\n\n");
        } else
                printf("\nSUCCESS, all tests passed\n\n");

        return 0;
}
