"Fossies" - the Fresh Open Source Software archive

Member "evolution-brutus-1.2.35/server/brutus_util.c" of archive evolution-brutus-1.2.35.tar.gz:


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

/*
 *    Implementation file for Brutus utility functions.
 *    Copyright (C) 2005-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 2 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, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA 02111-1307 USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/* system includes */
#include <glib.h>
#include <glib/gprintf.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <libedataserver/e-source-list.h>
#include <libedataserver/md5-utils.h>
#include <brutus-keyring.h>
#include <brutus-keyringd.h>

/* evolution-brutus includes */
#include <idl_output/return_codes.h>
#include <session/brutusd.h>
#include <server/brutus_conv.h>
#include <log/brutus-logd.h>
#include <servant_impl/BrutusCheck.h>

/* Lorica */
#include "ReferenceMapper.h"

#include "brutus_corba.h"
#include "brutus_mapi.h"
#include "brutus_uuid.h"
#include "brutus_util.h"

#define BRUTUS_REQ_MAJOR (2)
#define BRUTUS_REQ_MINOR (0)
#define BRUTUS_REQ_MICRO (0)
#define BRUTUS_REQ_RELEASE_TAG (0)

gboolean
brutus_server_version_OK(int major,
			 int minor,
			 int micro,
			 int release_tag)
{
	return ((BRUTUS_REQ_MAJOR == major)
		&& (BRUTUS_REQ_MINOR == minor));
}

void brutus_for_each_free(gpointer data,
			  gpointer user_data)
{
	free(data);
}

void brutus_for_each_g_free(gpointer data,
			    gpointer user_data)
{
	g_free(data);
}

int brutus_timezone_to_gmt_offset(long time_zone_offset)
{
	int tz = (-1) * time_zone_offset;

	return (((tz/60/60) * 100) + (tz/60 % 60));
}

static gboolean
brutus_get_e_source_list(ESourceList **sources,
			 const enum E_SOURCE_BRUTUS_TYPE type)
{
	gboolean retv = TRUE;
	GConfClient *gconf = gconf_client_get_default();

	if (!gconf)
		return FALSE;

	switch (type) {
	case E_SOURCE_BRUTUS_CALENDAR:
		*sources = e_source_list_new_for_gconf(gconf, "/apps/evolution/calendar/sources");
		break;
	case E_SOURCE_BRUTUS_TASKS:
		*sources = e_source_list_new_for_gconf(gconf, "/apps/evolution/tasks/sources");
		break;
	case E_SOURCE_BRUTUS_NOTES:
		*sources = e_source_list_new_for_gconf(gconf, "/apps/evolution/memos/sources");
		break;
	case E_SOURCE_BRUTUS_ADDRESSBOOK_PAB:
	case E_SOURCE_BRUTUS_ADDRESSBOOK_GAL:
		*sources = e_source_list_new_for_gconf(gconf, "/apps/evolution/addressbook/sources");
		break;
	default:
		retv = FALSE;
	}
	g_object_unref(gconf);

	return retv;
}

static const char*
brutus_get_account_sub_type(const enum E_SOURCE_BRUTUS_TYPE e_type)
{
	// create relative uri
	switch (e_type) {
	case E_SOURCE_BRUTUS_CALENDAR:
		return "EVENTS";
		break;
	case E_SOURCE_BRUTUS_TASKS:
		return "TODOS";
		break;
	case E_SOURCE_BRUTUS_NOTES:
		return "NOTES";
		break;
	case E_SOURCE_BRUTUS_ADDRESSBOOK_PAB:
	case E_SOURCE_BRUTUS_ADDRESSBOOK_GAL:
		return "ADDRESSBOOK";
		break;
	default:
		g_assert_not_reached();
	}

	return "";
}


gboolean
brutus_add_account(const enum E_SOURCE_BRUTUS_TYPE e_type,
		   const char *account_name,
		   const char *mapi_profile,
		   const char *exchange_server,
		   const char *windows_user,
		   const char *effective_mailbox,
		   const char *exchange_mailbox,
		   const char *exchange_mailbox_dn,
		   const char *exchange_mailbox_email,
		   const char *windows_domain,
		   const char *brutus_server,
		   const guint16 brutus_port,
		   const gboolean expect_other_clients,
		   int auto_check_timeout)
{
	gboolean retv = FALSE;
	gboolean unref_group = FALSE;
	ESourceList *source_list = NULL;
	ESourceGroup *group = NULL;
	ESource *source = NULL;
	char *relative_uri = NULL;
	char *timeout = NULL;
	const char *sub_type = NULL;
	char brutus_kind[5] = { '\0' };
	char port[8] = { '\0' };

	if (0 > snprintf(port, sizeof(port), "%hu", brutus_port)) {
		d("Invalid Brutus port number");
		goto out;
	}

	if (-1 == auto_check_timeout)
		auto_check_timeout = 10;
	timeout = g_strdup_printf("%d", auto_check_timeout);
	if (!timeout) {
		d("No memory");
		goto out;
	}

	// get list of groups
	if (!brutus_get_e_source_list(&source_list, e_type)) {
		d("Could not get source list from GConf");
		goto out;
	}

	// create group
	group = e_source_list_peek_group_by_name(source_list, "Brutus");
	if (!group) {
		group = e_source_group_new("Brutus", "brutus://");
		if (!group) {
			d("No memory");
			goto out;
		}
		unref_group = TRUE;

		if (!e_source_list_add_group(source_list, group, -1)) {
			d("Could not add Brutus source group");
			goto out;
		} else {
			e_source_list_sync(source_list, NULL);
		}
	}

	// create relative uri
	sub_type = brutus_get_account_sub_type(e_type);
	relative_uri = g_strdup_printf("%s\\%s@%s:%s:__%s",
				       windows_domain,
				       windows_user,
				       exchange_server,
				       effective_mailbox,
				       sub_type);
	if (!relative_uri) {
		d("No memory");
		goto out;
	}

	// check for presence of source
	source = e_source_group_peek_source_by_name(group, account_name);
	if (source) {
		if (!g_ascii_strcasecmp(mapi_profile, e_source_get_property(source, "mapi_profile"))) {
			retv = TRUE;
			goto out;
		}
	} else { // create source
		source = e_source_new(account_name, relative_uri);
		if (!source) {
			d("Could not create new source");
			goto out;
		}
	}

	// get the stringified account type
	g_sprintf(brutus_kind, "%u", brutus_e_type_to_unsigned_int(e_type));

	// set properties
	e_source_set_color(source, 0x3F8D46); // 42tools green
	e_source_set_property(source, "mapi_profile", mapi_profile);
	e_source_set_property(source, "exchange_server", exchange_server);
	e_source_set_property(source, "windows_user", windows_user);
	e_source_set_property(source, "effective_mailbox", effective_mailbox);
	e_source_set_property(source, "exchange_mailbox", (exchange_mailbox ? exchange_mailbox : ""));
	e_source_set_property(source, "exchange_mailbox_dn", (exchange_mailbox_dn ? exchange_mailbox_dn : ""));
	e_source_set_property(source, "exchange_mailbox_email", (exchange_mailbox_email ? exchange_mailbox_email : ""));
	e_source_set_property(source, "windows_domain", windows_domain);
	e_source_set_property(source, "brutus_server", brutus_server);
	e_source_set_property(source, "brutus_port", port);
	e_source_set_property(source, "brutus_kind", brutus_kind);
	e_source_set_property(source, "expect_other_clients", expect_other_clients ? "1" : "0");
	e_source_set_property(source, "auto_check_timeout", timeout);

	if (!e_source_group_add_source(group, source, -1)) {
		d("Could not add source to group");
		goto out;
	}

	if (!e_source_list_sync(source_list, NULL)) {
		d("Could not sync source list");
		goto out;
	}

	retv = TRUE;

out:
	if (timeout)
		g_free(timeout);

	if (relative_uri)
		g_free(relative_uri);

	if (source)
		g_object_unref(source);

	if (group && unref_group)
		g_object_unref(group);

	if (source_list)
		g_object_unref(source_list);

	return retv;
}

void
brutus_remove_account(const enum E_SOURCE_BRUTUS_TYPE e_type,
		      const char *account_name,
		      const char *mapi_profile)
{
	ESourceList *source_list = NULL;
	ESourceGroup *group = NULL;
	ESource *source = NULL;

	// get list of groups
	if (!brutus_get_e_source_list(&source_list, e_type)) {
		d("Could not get source list from GConf");
		goto out;
	}

	// get group
	group = e_source_list_peek_group_by_name(source_list, "Brutus");
	if (!group)
		goto out;

	// check for presence of source
	source = e_source_group_peek_source_by_name(group, account_name);
	if (source) {
		if (!g_ascii_strcasecmp(mapi_profile, e_source_get_property(source, "mapi_profile")))
			e_source_group_remove_source(group, source);
	}
	if (!e_source_list_sync(source_list, NULL))
		d("Could not sync source list");

out:
	if (source)
		g_object_unref(source);

	if (source_list)
		g_object_unref(source_list);
}

static CORBA_ORB
create_orb(CORBA_Environment *ev)
{
	CORBA_ORB orb = CORBA_OBJECT_NIL;
	int init_argc = 4;
	char **init_argv = (char**)malloc(sizeof(char*) * (init_argc));
	if (!init_argv)
		return CORBA_OBJECT_NIL;

	init_argv[0] = strdup("DUMMY_ARGV0");

	// IPv4 - needed to interoperate with clients
	init_argv[1] = "--ORBIIOPIPv4=1";

	// IPv6 - better to switch it off if we do not use it
	init_argv[2] = "--ORBIIOPIPv6=0";

	// Explicitly force ORBit2 to be local only
	init_argv[3] = "--ORBLocalOnly=1";

	// initialize the ORB
	orb = CORBA_ORB_init(&init_argc, init_argv, "brutus-util_orb_orbit-io-thread", ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		orb = CORBA_OBJECT_NIL;
	}

	free(init_argv[0]);
	free(init_argv);

	return orb;
}

static BRUTUS_BrutusKeyring
get_keyring_ref(CORBA_ORB orb)
{
	gchar *ref_path = NULL;
	BRUTUS_BrutusKeyring keyring = CORBA_OBJECT_NIL;
	CORBA_boolean cb = CORBA_FALSE;
	CORBA_Environment ev[1];

	CORBA_exception_init(ev);

	cb = CORBA_Object_is_nil((CORBA_Object)orb, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto out;
	}
	if (cb) {
		d("ORB is NIL");
		goto out;
	}

	ref_path = g_strconcat(getenv("HOME"), "/", BRUTUS_KEYRINGD_REF_FILE_NAME, NULL);
	if (!ref_path)
		goto out;

	d("Before getting keyring ref from file");
	keyring = brutus_object_ref_from_file(orb, ref_path);
	g_free(ref_path);
	cb = CORBA_Object_is_nil((CORBA_Object)keyring, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto out;
	}
	if (cb) {
		d("Could not get the Brutus Keyring reference");
		goto out;
	}

out:
	CORBA_exception_free(ev);

	return keyring;

}

static void
brutus_forget_account_password(const char *mapi_profile)
{
	BRUTUS_BrutusKeyring keyring = CORBA_OBJECT_NIL;
	CORBA_boolean cb = CORBA_TRUE;
	CORBA_ORB orb = CORBA_OBJECT_NIL;
	CORBA_Environment ev[1];

	CORBA_exception_init(ev);
	orb = create_orb(ev);
	keyring = get_keyring_ref(orb);
	cb = CORBA_Object_is_nil((CORBA_Object)keyring, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto out;
	}
	if (cb)
		goto out;

	BRUTUS_BrutusKeyring_delSecret(keyring, mapi_profile, ev);

out:
	CORBA_Object_release((CORBA_Object)keyring, ev);
	CORBA_Object_release((CORBA_Object)orb, ev);
	CORBA_exception_free(ev);

	return;
}

static void
brutus_set_account_password(const char *mapi_profile,
			    const char *password)
{
	BRUTUS_BrutusKeyring_KeyringResult keyring_retv = BRUTUS_BrutusKeyring_failed;
	BRUTUS_BrutusKeyring_KeyringData secret;
	BRUTUS_BrutusKeyring keyring = CORBA_OBJECT_NIL;
	CORBA_boolean cb = CORBA_TRUE;
	CORBA_ORB orb = CORBA_OBJECT_NIL;
	CORBA_Environment ev[1];

	CORBA_exception_init(ev);

	orb = create_orb(ev);
	keyring = get_keyring_ref(orb);
	cb = CORBA_Object_is_nil((CORBA_Object)keyring, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto out;
	}
	if (cb)
		goto out;

	if (!password)
		goto out;
	else {
		secret._maximum = strlen(password)+1;
		secret._length = secret._maximum;
		secret._buffer = BRUTUS_BrutusKeyring_KeyringData_allocbuf(secret._maximum);
		if (!secret._buffer)
			goto out;
	}

	keyring_retv = BRUTUS_BrutusKeyring_setSecret(keyring, mapi_profile, &secret, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto out;
	}
	if (BRUTUS_BrutusKeyring_success == keyring_retv)

	out:
		if (secret._buffer)
			CORBA_free(secret._buffer);

	CORBA_Object_release((CORBA_Object)keyring, ev);
	CORBA_Object_release((CORBA_Object)orb, ev);
	CORBA_exception_free(ev);

	return;
}

/*
 * This will retrieve the Brutus account password for a specified MAPI profile.
 */
static char*
brutus_get_account_password(const char *mapi_profile)
{
	BRUTUS_BrutusKeyring_KeyringResult keyring_retv = BRUTUS_BrutusKeyring_failed;
	BRUTUS_BrutusKeyring_KeyringData *secret = NULL;
	BRUTUS_BrutusKeyring keyring = CORBA_OBJECT_NIL;
	CORBA_boolean cb = CORBA_TRUE;
	CORBA_ORB orb = CORBA_OBJECT_NIL;
	CORBA_Environment ev[1];
	char *retv = NULL;
	int status;
	char *argv[] = {
		LIBEXECDIR "/brutus-query-password",
		"-set_passphrase",
		NULL,
	};

	CORBA_exception_init(ev);
	orb = create_orb(ev);
	keyring = get_keyring_ref(orb);
	cb = CORBA_Object_is_nil((CORBA_Object)keyring, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto out;
	}
	if (cb)
		goto out;

	keyring_retv = BRUTUS_BrutusKeyring_getSecret(keyring, mapi_profile, &secret, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto out;
	}
	if (BRUTUS_BrutusKeyring_set_passphrase == keyring_retv) {
		g_spawn_sync(NULL,
			     argv,
			     NULL,
			     G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     &status,
			     NULL);
		if (EXIT_SUCCESS != status)
			goto out;
	}
	keyring_retv = BRUTUS_BrutusKeyring_getSecret(keyring, mapi_profile, &secret, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto out;
	}
	if (BRUTUS_BrutusKeyring_success == keyring_retv)
		retv = strdup((const char*)secret->_buffer);

out:
	if (secret)
		CORBA_free(secret);

	CORBA_Object_release((CORBA_Object)keyring, ev);
	CORBA_Object_release((CORBA_Object)orb, ev);
	CORBA_exception_free(ev);

	return retv;
}

static char*
brutus_get_password(const char *prompt,
		    const char *mapi_profile)
{
	static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
	char *password = NULL;
	int status;
	char *argv[] = {
		LIBEXECDIR "/brutus-query-password",
		"-set_password",
		(char*)prompt,
		(char*)mapi_profile,
		NULL,
	};

	// only one password query dialog at any one time
	pthread_mutex_lock(&mutex);

	d("libexecdir = " LIBEXECDIR);
	password = brutus_get_account_password(mapi_profile); // from keyring
	if (!password) { // not present, query user
		d("Could not retrieve password from keyring - querying user");
		g_spawn_sync(NULL,
			     argv,
			     NULL,
			     G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
			     NULL,
			     NULL,
			     NULL,
			     NULL,
			     &status,
			     NULL);
		if (EXIT_SUCCESS != status)
			goto out;

		password = brutus_get_account_password(mapi_profile); // try the keyring again
	}

out:
	pthread_mutex_unlock(&mutex);
	return password;
}

static Lorica_ReferenceMapper
get_lorica(void)
{
	CORBA_char *corbaloc_str[512] = { '\0' };
	CORBA_boolean cb = CORBA_TRUE;
	CORBA_ORB orb = CORBA_OBJECT_NIL;
	Lorica_ReferenceMapper mapper = CORBA_OBJECT_NIL;
	CORBA_Environment ev[1];

	CORBA_exception_init(ev);

	orb = create_orb(ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto no_lorica;
	}

	snprintf((char*)corbaloc_str, sizeof(corbaloc_str), "corbaloc:iiop:localhost:%u/%s", Lorica_ReferenceMapper_lorica_in_port, Lorica_ReferenceMapper_IOR_TABLE_KEY);
	mapper = (Lorica_ReferenceMapper)CORBA_ORB_string_to_object(orb, (CORBA_char*)corbaloc_str, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto no_lorica;
	}
	cb = CORBA_Object_is_nil((CORBA_Object)mapper, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto no_lorica;
	}
	if (cb)
		goto no_lorica;

	cb = CORBA_Object_non_existent((CORBA_Object)mapper, ev);
	if (ORBIT2_EX(ev)) {
		PRINT_ORBIT2_EX(ev);
		goto no_lorica;
	}
	if (cb)
		goto no_lorica;

	goto lorica_ok;

no_lorica:
	d("no_lorica");
	CORBA_Object_release((CORBA_Object)orb, ev);
	CORBA_exception_free(ev);

	return CORBA_OBJECT_NIL;

lorica_ok:
	d("lorica_ok");
	CORBA_Object_release((CORBA_Object)orb, ev);
	CORBA_exception_free(ev);

	return mapper;
}

gboolean
brutus_logon_loop(BRUTUS_BrutusLogOn brutus_logon,
		  BRUTUS_BrutusCheck lifeline,
		  BRUTUS_IMAPISession *mapi_session,
		  CORBA_unsigned_long *instance_id,
		  const char *brutus_logon_corbaloc,
		  const char *prompt,
		  const char *err_prompt,
		  const char *windows_user,
		  const char *windows_domain,
		  const char *exchange_mailbox,
		  const char *exchange_mailbox_dn,
		  const char *exchange_mailbox_email,
		  const char *exchange_server,
		  char **mapi_profile,
		  char **effective_mailbox)
{
	BRUTUS_BrutusLogOn brutus_logon_mapped = CORBA_OBJECT_NIL;
	Lorica_ReferenceMapper lorica = CORBA_OBJECT_NIL;
	CORBA_boolean cb = CORBA_TRUE;
	gboolean retv = FALSE;
	gboolean ref_mapped = FALSE;
	BRUTUS_BRESULT br = BRUTUS_BRUTUS_MAPI_E_LOGON_FAILED;
	char *password = NULL;
	uuid_str_t uuid = { '\0' };
	const size_t mailbox_opts_len = 5;
	char *mailbox_opts[mailbox_opts_len];
	size_t idx = 0;
	CORBA_Environment ev[1];

	CORBA_exception_init(ev);

	cb = CORBA_Object_is_nil((CORBA_Object)brutus_logon, ev);
	if (ORBIT2_EX(ev) || cb) {
		d("No valid logon reference");
		goto out;
	}

	mailbox_opts[0] = *effective_mailbox;
	mailbox_opts[1] = (exchange_mailbox_email && strlen(exchange_mailbox_email)) ? g_strdup_printf("SMTP:%s", exchange_mailbox_email) : g_strdup("");
	mailbox_opts[2] = (exchange_mailbox && strlen(exchange_mailbox))             ? g_strdup(exchange_mailbox)                         : g_strdup("");
	mailbox_opts[3] = (strlen(mailbox_opts[1]))                                  ? g_strdup_printf("=%s", exchange_mailbox)           : g_strdup("");
	mailbox_opts[4] = (exchange_mailbox_dn && strlen(exchange_mailbox_dn))       ? g_strdup(exchange_mailbox_dn)                      : g_strdup("");

	d(mailbox_opts[0]);
	d(mailbox_opts[1]);
	d(mailbox_opts[2]);
	d(mailbox_opts[3]);
	d(mailbox_opts[4]);
	d(windows_user);
	d(windows_domain);
	d(exchange_server);

	/* get the password */
	password = brutus_get_password(prompt, (const char*)(*mapi_profile));
	if (!password) {
		d("User canceled or password was all whitespace");
		goto out;
	} else
		d("Recieved password from keyring");

	// get a client mapped ref if possible
	do {
		lorica = get_lorica();
		cb = CORBA_Object_is_nil((CORBA_Object)lorica, ev);
		if (ORBIT2_EX(ev)) {
			PRINT_ORBIT2_EX(ev);
			goto no_map;
		}
		if (cb)
			goto no_map;

		brutus_logon_mapped = Lorica_ReferenceMapper_as_client_with_corbaloc(lorica, 
										     brutus_logon_corbaloc, 
										     "IDL:omc.brutus/BRUTUS/BrutusLogOn:1.0",
										     ev);
		if (ORBIT2_EX(ev)) {
			PRINT_ORBIT2_EX(ev);
			goto no_map;
		}
		cb = CORBA_Object_is_nil((CORBA_Object)brutus_logon_mapped, ev);
		if (ORBIT2_EX(ev)) {
			PRINT_ORBIT2_EX(ev);
			goto no_map;
		}
		if (cb)
			goto no_map;

		ref_mapped = TRUE;
		break;

	no_map:
		brutus_logon_mapped = brutus_logon;
	} while (FALSE);

	do { // loop indefinitely if password is bad, break for other errors
		idx = 0;
		do { // loop while resolving the mailbox
			if (idx >= mailbox_opts_len)
				break;

			if (!mailbox_opts[idx] || !strlen(mailbox_opts[idx])) {
				idx++;
				continue;
			}

			d((const char*)(*mapi_profile));
			d(windows_user);
			d(windows_domain);
			dfmt2("mailbox_opts[%d] = %s", idx, mailbox_opts[idx]);
			br = BRUTUS_BrutusLogOn_Logon(brutus_logon_mapped,
						      lifeline,
						      (const char*)(*mapi_profile),
						      "",
						      windows_user,
						      windows_domain,
						      password,
						      mailbox_opts[idx],
						      exchange_server,
						      BRUTUS_BRUTUS_MAPI_EXPLICIT_PROFILE,
						      0,
						      BRUTUS_SERVER_TYPE_EXCHANGE, // or BRUTUS_SERVER_TYPE_DOMINO
						      instance_id,
						      mapi_session,
						      ev);
			if (ORBIT2_EX(ev)) {
				PRINT_ORBIT2_EX(ev);
				goto out;
			}
			d(brutus_bresult_to_str(br));
			switch (br) {
			case BRUTUS_BRUTUS_MAPI_E_UNKNOWN_MAILBOX : // could not resolve mailbox
			case BRUTUS_BRUTUS_MAPI_E_AMBIGUOUS_RECIP : // could not resolve mailbox
			case BRUTUS_BRUTUS_MAPI_E_VERSION :         // no support
				idx++;
				continue;
			default :
				break;
			}
			break;
		} while (TRUE);
		switch (br) {
		case BRUTUS_BRUTUS_LIFELINE_TIMEOUT :
			d("Lifeline timeout");
			goto out;
		case BRUTUS_BRUTUS_EXCEPTION_FROM_CLIENT :
			d("Possible exception in lifeline servant - exiting from logon loop");
			goto out;
		case BRUTUS_BRUTUS_INTERNAL_ERROR :
			d("Brutus Server internal error");
			goto out;
		default :
			d(brutus_bresult_to_str(br));
			break;
		}
		if (BRUTUS_BRUTUS_MAPI_E_LOGON_FAILED == br) {
			g_free(password);
			password = NULL;
			brutus_forget_account_password((const char*)(*mapi_profile)); // forget bad password

			password = brutus_get_password(err_prompt, (const char*)(*mapi_profile));
			if (!password) { // user canceled
				d("User canceled or password was all whitespace");
				goto out;
			}
			continue;
		}
		if (BRUTUS_BRUTUS_MAPI_E_NO_ACCESS == br) {
			brutus_forget_account_password((const char*)(*mapi_profile)); // forget bad profile
			goto create_profile;
		}

		break;
	} while (TRUE);
	if (BRUTUS_BRUTUS_S_OK == br) {
		retv = TRUE;
		goto out;
	}

	dfmt("BrutusLogOn->Logon() returned %s", brutus_bresult_to_str(br));
	if (BRUTUS_BRUTUS_MAPI_E_NOT_INITIALIZED == br)
		goto out;

create_profile:
	if (BRUTUS_BRUTUS_MAPI_E_NO_ACCESS == br) { // should never happen...
		d("MAPI profile name collision, creating a new one...");
		brutus_uuid(uuid);
		CORBA_free(*mapi_profile);
		*mapi_profile = CORBA_string_dup(uuid);

		brutus_forget_account_password((const char*)(*mapi_profile)); // forget bad password
		brutus_set_account_password((const char*)(*mapi_profile),
					    password);

		dfmt("The new MAPI profile is \"%s\"", *mapi_profile);
	}

	dfmt("Attempting logon with MAPI profile \"%s\"", *mapi_profile);
	do {
		br = BRUTUS_BrutusLogOn_Logon(brutus_logon_mapped,
					      lifeline,
					      (const char*)(*mapi_profile),
					      "",
					      windows_user,
					      windows_domain,
					      password,
					      mailbox_opts[idx],
					      exchange_server,
					      BRUTUS_BRUTUS_MAPI_EXPLICIT_PROFILE,
					      0,
					      BRUTUS_SERVER_TYPE_EXCHANGE, // or BRUTUS_SERVER_TYPE_DOMINO
					      instance_id,
					      mapi_session,
					      ev);
		if (ORBIT2_EX(ev)) {
			PRINT_ORBIT2_EX(ev);
			goto out;
		}
		if (BRUTUS_BRUTUS_S_OK != br) {
			d(brutus_bresult_to_str(br));

			g_free(password);
			password = NULL;
			brutus_forget_account_password((const char*)(*mapi_profile)); // forget bad password

			password = brutus_get_password(err_prompt, (const char*)(*mapi_profile));
			if (!password) { // user canceled
				d("User canceled or password was all whitespace");
				goto out;
			}
		} else
			break;
	} while (TRUE);

out:
	if (ref_mapped) {
		Lorica_ReferenceMapper_remove_client(lorica, (CORBA_Object)brutus_logon_mapped, ev);
		CORBA_Object_release((CORBA_Object)lorica, ev);
		CORBA_Object_release((CORBA_Object)brutus_logon_mapped, ev);
	}

	if (BRUTUS_BRUTUS_S_OK == br) {
		if (idx) { // new effective mailbox name
			if (*effective_mailbox)
				CORBA_free(*effective_mailbox);
			*effective_mailbox = CORBA_string_dup(mailbox_opts[idx]);
		}
		dfmt("\"%s\" has logged on", windows_user);
		retv = TRUE;
	}

	CORBA_exception_free(ev);

	g_free(password);

	// never free mailbox_opts[0]
	g_free(mailbox_opts[1]);
	g_free(mailbox_opts[2]);
	g_free(mailbox_opts[3]);
	g_free(mailbox_opts[4]);

	return retv;
}

static inline gboolean
brutus_digest_str_is_null(const digest_str_t digest_str)
{
	return (('\0' == digest_str[0]) ? TRUE : FALSE);
}

static inline gboolean
brutus_digest_is_null(const digest_t digest)
{
	static const digest_t null_digest = { 0 };

	return (memcmp((const void*)null_digest, (const void*)digest, sizeof(digest_t)) ? FALSE : TRUE);
}

static inline void
brutus_digest_to_str(const digest_t digest,
		     digest_str_t digest_str)
{
	int i;

	memset((void*)digest_str, '\0', sizeof(digest_str_t));

	if (brutus_digest_is_null(digest))
		return;

	for (i = 0; i < sizeof(digest_t); i++)
		snprintf(&digest_str[2*i], 3, "%02x", (guint8)digest[i]);
}

void
brutus_get_digest_str(const char *str,
		      digest_str_t digest_str)
{
	digest_t digest = { 0 };

	md5_get_digest(str, strlen(str), digest);
	brutus_digest_to_str(digest, digest_str);
}

gchar *
brutus_strip_trailing_and_leading_whitespace(const char *str)
{
	gchar *retv = NULL;
	int pos = 0;

	if (!str)
		return NULL;

	// fast-forward to first non-space character
	while (isspace(*str))
		str++;

	retv = g_strdup(str);
	pos = strlen(str) - 1; // last character

	while (0 <= pos) {
		if (isspace(retv[pos]))
			retv[pos] = '\0';
		else
			break;
		pos--;
	}

	return retv;
}

gboolean
brutus_get_random_uuid(uuid_str_t uuid_str)
{
	return (brutus_uuid(uuid_str) ? FALSE : TRUE);
}

void
brutus_free_lifeline(BRUTUS_BrutusProxy proxy,
		     PortableServer_POA poa,
		     struct BrutusCheckProxy *lifeline)
{
	CORBA_boolean cb = CORBA_TRUE;
	PortableServer_ObjectId *objid = NULL;
	CORBA_Environment ev[1];

	CORBA_exception_init(ev);

	cb = CORBA_Object_is_nil((CORBA_Object)lifeline->wrapper, ev);
	if (!ORBIT2_EX(ev) && !cb) {
		if (lifeline->key)
			BRUTUS_BrutusProxy_killWrapper(proxy, lifeline->key, ev);
	}
	CORBA_Object_release((CORBA_Object)lifeline->wrapper, ev);

	cb = CORBA_Object_is_nil((CORBA_Object)lifeline->filling, ev);
	if (ORBIT2_EX(ev) || cb)
		goto out;

	objid = PortableServer_POA_reference_to_id (poa, (CORBA_Object)lifeline->filling, ev);
	if (ORBIT2_EX(ev)) {
		if (objid)
			CORBA_free(objid);
		goto out;
	}
	if (objid) {
		PortableServer_POA_deactivate_object (poa, objid, ev);
		CORBA_free(objid);
		if (ORBIT2_EX(ev))
			goto out;
	}
	CORBA_Object_release((CORBA_Object)lifeline->filling, ev);

out:
	CORBA_exception_free(ev);

	lifeline->key = 0;
	lifeline->wrapper = CORBA_OBJECT_NIL;
	lifeline->filling = CORBA_OBJECT_NIL;
}

void
brutus_base_class_free(BrutusBaseClass *base_class)
{
	CORBA_boolean cb = CORBA_TRUE;
	CORBA_Environment ev[1];

	if (!base_class->connect_mutex)
		return;

	g_mutex_lock(base_class->connect_mutex);

	CORBA_exception_init(ev);

	if (brutus_is_connected(base_class->brutus_server,
				base_class->mapi_session))
		BRUTUS_IMAPISession_Destroy(base_class->mapi_session, base_class->instance_id, ev);
	cb = CORBA_Object_is_nil(base_class->mapi_session, ev);
	if (!ORBIT2_EX(ev) && !cb)
		CORBA_Object_release(base_class->mapi_session, ev);
	base_class->mapi_session = CORBA_OBJECT_NIL;

	if (base_class->brutus_server)
		g_free(base_class->brutus_server);
	base_class->brutus_server = NULL;

	if (base_class->exchange_server)
		g_free(base_class->exchange_server);
	base_class->exchange_server = NULL;

	if (base_class->brutus_logon_corbaloc)
		g_free(base_class->brutus_logon_corbaloc);
	base_class->brutus_logon_corbaloc = NULL;

	if (base_class->cache_dir)
		g_free(base_class->cache_dir);
	base_class->cache_dir = NULL;

	if (base_class->mapi_profile)
		CORBA_free(base_class->mapi_profile);
	base_class->mapi_profile = NULL;

	if (base_class->effective_mailbox)
		CORBA_free(base_class->effective_mailbox);
	base_class->effective_mailbox = NULL;

	brutus_free_lifeline(base_class->brutus_proxy,
			     base_class->root_poa,
			     &base_class->lifeline);

	cb = CORBA_Object_is_nil((CORBA_Object)base_class->brutus_proxy, ev);
	if (!ORBIT2_EX(ev) && !cb) {
		CORBA_Object_release((CORBA_Object)base_class->brutus_proxy, ev);
		PRINT_IF_ORBIT2_EX(ev);
	}
	base_class->brutus_proxy = CORBA_OBJECT_NIL;

	cb = CORBA_Object_is_nil((CORBA_Object)base_class->root_poa, ev);
	if (!ORBIT2_EX(ev) && !cb) {
		CORBA_Object_release((CORBA_Object)base_class->root_poa, ev);
		PRINT_IF_ORBIT2_EX(ev);
	}
	base_class->root_poa = CORBA_OBJECT_NIL;

	cb = CORBA_Object_is_nil((CORBA_Object)base_class->orb, ev);
	if (!ORBIT2_EX(ev) && !cb) {
		CORBA_Object_release((CORBA_Object)base_class->orb, ev);
		PRINT_IF_ORBIT2_EX(ev);
	}
	base_class->orb = CORBA_OBJECT_NIL;

	if (base_class->likely_connected_mutex)
		g_mutex_free(base_class->likely_connected_mutex);

	g_mutex_unlock(base_class->connect_mutex);
	g_mutex_free(base_class->connect_mutex);
	base_class->connect_mutex = NULL;

	CORBA_exception_free(ev);
}

void
brutus_base_class_init(BrutusBaseClass *base_class)
{
	base_class->orb = CORBA_OBJECT_NIL;
	base_class->root_poa = CORBA_OBJECT_NIL;

	base_class->mapi_session = CORBA_OBJECT_NIL;
	base_class->brutus_proxy = CORBA_OBJECT_NIL;
	base_class->expect_other_clients = TRUE;
	base_class->likely_connected = FALSE;
	base_class->likely_connected_mutex = g_mutex_new();

	base_class->lifeline.key = 0;
	base_class->lifeline.wrapper = CORBA_OBJECT_NIL;
	base_class->lifeline.filling = CORBA_OBJECT_NIL;

	base_class->instance_id = 0;
	base_class->exchange_time_zone = 0;
	base_class->client_time_zone = 0;

	base_class->connect_mutex = g_mutex_new();

	base_class->mapi_profile = NULL;
	base_class->effective_mailbox = NULL;
	base_class->brutus_logon_corbaloc = NULL;
	base_class->cache_dir = NULL;
	base_class->brutus_server = NULL;
	base_class->exchange_server = NULL;

	base_class->brutus_port = 0;
}

int
brutus_start_daemon(const enum E_BRUTUS_DAEMON which)
{
	switch (which) {
	case E_BRUTUS_SESSION_DAEMON :
		return system("brutusd");
	case E_BRUTUS_LOG_DAEMON :
		return system("brutus-logd");
	case E_BRUTUS_KEYRING_DAEMON :
		return system("brutus-keyringd");
	default:
		g_assert_not_reached();
	}

	return -1;
}

pid_t
brutus_get_daemon_pid(const enum E_BRUTUS_DAEMON which)
{
	int items = 0;
	pid_t pid = -1;
	gchar *path = NULL;
	const gchar *pid_file_name = NULL;
	FILE *pid_file = NULL;

	switch (which) {
	case E_BRUTUS_SESSION_DAEMON :
		pid_file_name = BRUTUSD_PID_FILE_NAME;
		break;
	case E_BRUTUS_LOG_DAEMON :
		pid_file_name = BRUTUS_LOGD_PID_FILE_NAME;
		break;
	case E_BRUTUS_KEYRING_DAEMON :
		pid_file_name = BRUTUS_KEYRINGD_PID_FILE_NAME;
		break;
	default:
		g_assert_not_reached();
	}

	path = g_strconcat(getenv("HOME"), "/", pid_file_name, NULL);
	if (!path)
		goto out;
	pid_file = fopen(path, "r");
	g_free(path);
	if (!pid_file)
		goto out;

	items = fscanf(pid_file,"%d", &pid);
	fclose(pid_file);
	if (!items || (items > 1))
		pid = -1;
out:
	return pid;
}


/* sends SIGUSR2 to the specified daemon - no error if the daemon is dead or zombie */
void
brutus_restart_daemon(const enum E_BRUTUS_DAEMON which)
{
	pid_t pid = brutus_get_daemon_pid(which);

	if (0 < pid)
		kill(pid, SIGUSR2);
}

/* sends SIGKILL to the specified daemon - no error if the daemon is dead or zombie */
void
brutus_kill_daemon(const enum E_BRUTUS_DAEMON which)
{
	pid_t pid = brutus_get_daemon_pid(which);

	if (0 < pid)
		kill(pid, SIGKILL);
}

enum E_SOURCE_BRUTUS_TYPE
brutus_int_to_e_type(int i_rep)
{
	switch (i_rep) {
	case 1 :
		return E_SOURCE_BRUTUS_CALENDAR;
	case 2 :
		return E_SOURCE_BRUTUS_TASKS;
	case 3 :
		return E_SOURCE_BRUTUS_NOTES;
	case 4 :
		return E_SOURCE_BRUTUS_ADDRESSBOOK_PAB;
	case 5 :
		return E_SOURCE_BRUTUS_ADDRESSBOOK_GAL;
	default:
		g_assert_not_reached();
	}

	return E_SOURCE_BRUTUS_UNKNOWN;
}

unsigned int
brutus_e_type_to_unsigned_int(enum E_SOURCE_BRUTUS_TYPE e_rep)
{
	switch (e_rep) {
	case E_SOURCE_BRUTUS_CALENDAR :
		return 1;
	case E_SOURCE_BRUTUS_TASKS :
		return 2;
	case E_SOURCE_BRUTUS_NOTES :
		return 3;
	case E_SOURCE_BRUTUS_ADDRESSBOOK_PAB :
		return 4;
	case E_SOURCE_BRUTUS_ADDRESSBOOK_GAL :
		return 5;
	default:
		g_assert_not_reached();
	}

	return 0;
}

gchar*
brutus_mangle_uri(const gchar *uri)
{
	int n;
	gchar *mangled_uri = NULL;

	if (!uri)
		return NULL;
	mangled_uri = g_strdup(uri);

	for (n = 0; n < strlen(mangled_uri); n++) {
		switch (mangled_uri[n]) {
		case ':' :
		case '/' :
			mangled_uri[n] = '_';
		}
	}

	return mangled_uri;
}

gboolean
brutus_is_likely_connected(BrutusBaseClass *base_class)
{
	gboolean retv;

	if (!g_mutex_trylock(base_class->likely_connected_mutex))
		return FALSE;

	retv = base_class->likely_connected;

	g_mutex_unlock(base_class->likely_connected_mutex);

	return retv;
}

struct st_likely_data {
	BrutusBaseClass *base_class;
	gboolean likely_connected;
};
static gpointer
set_likely_connected_thread_func(gpointer data)
{
	struct st_likely_data *likely_data = (struct st_likely_data*)data;

	g_mutex_lock(likely_data->base_class->likely_connected_mutex);
	likely_data->base_class->likely_connected = likely_data->likely_connected;
	g_mutex_unlock(likely_data->base_class->likely_connected_mutex);

	return NULL;
}

void
brutus_set_likely_connected(BrutusBaseClass *base_class,
			    gboolean likely_connected)
{
	GThread *thr = NULL;
	struct st_likely_data likely_data = {
		.base_class = base_class,
		.likely_connected = likely_connected,
	};

	thr = g_thread_create(set_likely_connected_thread_func,
			      (gpointer)&likely_data,
			      TRUE,
			      NULL);
	g_thread_join(thr);
}

time_t
brutus_get_server_utc_time(const char *brutus_server,
			   BRUTUS_IMAPISession mapi_session,
			   const char *exchange_server)
{
	static gboolean timing_out = FALSE;
	BRUTUS_TIME_OF_DAY_INFO exchange_tod;
	char *tmp_exchange_server = NULL;
	time_t utc_time = 0;
	CORBA_boolean cb = CORBA_TRUE;
	CORBA_Environment ev[1];

	if (timing_out)
		return 0;

	if (!brutus_is_connected(brutus_server, mapi_session))
		return 0;

	if (!exchange_server)
		return 0;

	tmp_exchange_server = g_strjoin(NULL, "\\\\", exchange_server, NULL);
	if (!tmp_exchange_server)
		return 0;

	CORBA_exception_init(ev);
	cb = BRUTUS_IMAPISession_GetServerTime(mapi_session, tmp_exchange_server, &exchange_tod, ev);
	g_free(tmp_exchange_server);
	if (!ORBIT2_EX(ev) && cb)
		utc_time = (time_t)exchange_tod.tod_elapsedt;
	if (ORBIT2_EX(ev) && !strcmp(CORBA_exception_id(ev), ex_CORBA_TIMEOUT))
		timing_out = TRUE;

	CORBA_exception_free(ev);

	return utc_time;
}

void
brutus_g_ptr_array_find_and_delete_string(GPtrArray *arr,
					  char *str)
{
	guint n = 0;

	for (n = 0; n < arr->len; n++) {
		if (!strcmp(str, g_ptr_array_index(arr, n))) {
			g_free(g_ptr_array_index(arr, n));
			g_ptr_array_remove_index_fast(arr, n);
			break;
		}
	}
}