#!/usr/local/bin/php -c/usr/local/directadmin/scripts/php_clean.ini
<?php

$version = 0.1;

/*
Restore script for the per-domain RoundCube settings.
Backup/Restore written by DirectAdmin: http://www.directadmin.com
RoundCube Webmail Client: http://roundcube.net

This script will take an XML output (generated by backup_roundcube.php)
and restore all elements for a given User, merging them into an active roundcube database.
New IDs are generated, where applicable, so they will not be any conflicts if the IDs are differnt.
Existing accounts will be respected, and only missing data is merged in.

See the DirectAdmin versions system for more info:
http://www.directadmin.com/features.php?id=1062

All variables are passed via environment, not command line options
But you can specify environmental variables... via command line options before the script (see the showHelp() function)

RETURN VALUES
0: All is well
>1: an error worthy or reporting has occured. Message on stderr.
1: an error, most likely due to not actually having RoundCube installed or no restore data, has occured.

*/

/***********************
* Environmental variables
*/
$domain = getenv("domain");				//for security reasons, it must match the XML values.
$system_username = getenv("username");	//only this account is allowed to be restored.
$xml_file = getenv("xml_file");			//Name of the file to restore.

/***********************
* Enabling debug lets you see which Email is seen,
* and what values are being restored, and shows you their respective IDs (found or set)
* Keep this set to FALSE if the dataksq is calling it, so as to not fill the stdin buffer (dataskq only reads from stderr for this script)
*/
$is_debug = FALSE;

/***********************
* this restores as da_admin instead of da_roundube. It is less secure, avoid using it if possible.
*/
$high_access_connection = FALSE;


/***********************
* is the host value set within the da_roundcube.users table.
*/
$rc_mail_host = 'localhost';

/***********************
* If $high_access_restore is false, this is used for the mysql credentials.
*/
$rc_config = "/var/www/html/roundcube/config/config.inc.php";

//****************************************************************
//****************************************************************

if (!isset($xml_file) || $xml_file == "")
	show_help();

if (!isset($domain) || $domain == "")
	show_help();

if (!file_exists($xml_file))
{
	echo_stderr("Cannot find path: $xml_file. Skipping RoundCube restore.\n");
	exit(1);
}

if (filesize($xml_file) == 0)
{
	echo_stderr("Size of $xml_file is 0. Skipping RoundCube restore.\n");
	exit(1);
}

//****************************************************************
//****************************************************************

if ($high_access_connection)
{
	$mysql_conf = @parse_ini_file("/usr/local/directadmin/conf/mysql.conf");
}

if ($high_access_connection && $mysql_conf)
{
	$mysql_conf = parse_ini_file("/usr/local/directadmin/conf/mysql.conf");
	$mysql_user = $mysql_conf['user'];
	$mysql_pass = $mysql_conf['passwd'];
	$mysql_host = 'localhost';
	$mysql_db = 'da_roundcube';

	if (isset($mysql_conf['host']) && $mysql_conf['host'] != "")
		$mysql_host = $mysql_conf['host'];
}
else
{
	if (!file_exists($rc_config))
	{
		echo_stderr("Cannot find RoundCube config at $rc_config.  Is RC installed and up to date?\n");
		exit(5);
	}

	include_once($rc_config);

	if (!isset($config) || !isset($config['db_dsnw']) || $config['db_dsnw'] == '')
	{
		echo_stderr("Cannot find \$config['db_dsnw'] variable in $rc_config\n");
		exit(6);
	}

	//$config['db_dsnw'] = 'mysql://da_roundcube:password@localhost/da_roundcube';

	$values = explode('/', $config['db_dsnw']);
	$connect = explode('@', $values[2]);
	$auth = explode(':', $connect[0]);

	$mysql_user = $auth[0];
	$mysql_pass = $auth[1];
	$mysql_host = $connect[1];
	$mysql_db = $values[3];
}

$mysqli = new mysqli($mysql_host, $mysql_user, $mysql_pass);
if ($mysqli->connect_errno) {
    echo_stderr("Failed to connect to MySQL: (".$mysqli->connect_errno.") ".$mysqli->connect_error."\n");
    exit(3);
}
$mysqli->set_charset('utf8');

if (!$mysqli->select_db($mysql_db))
{
	echo_stderr("There is no $mysql_db database. Skipping RoundCube restore.\n");
	exit(1);
}

//****************************************************************
//****************************************************************

$xml = simplexml_load_file($xml_file);

if ($xml === FALSE)
{
	echo_stderr("Error reading in XML file with with simplexml_load_file('$xml_file')\n");
	exit(4);
}

foreach($xml->children() as $email)
{
	$username = urldecode($email->USERNAME);

	if ($username != $system_username)
	{
		$data = explode('@', $username);

		if ($data[1] != $domain)
		{
			echo_stderr($username. " is not part of domain '".$domain."': Skipping.\n");
			continue;
		}
	}

	$user_id = ensure_user($email);
	if ($user_id == -1)
		continue;

	echo_debug("username $username : $user_id\n");

	foreach($email->INDENTITIES->children() as $identity)
	{
		$id_id = ensure_identity($user_id, $identity);

		if ($id_id == -1)
			continue;

		$id_email = urldecode($identity->EMAIL);
		echo_debug("   identity $id_email :  $id_id\n");
	}


	$groups = Array();

	foreach($email->CONTACTS->children() as $contact)
	{
		//first, ensure all groups exisrt for this user_id.
		foreach($contact->GROUPS->children() as $group)
		{
			$group_id = ensure_group($user_id, $group);
			$group_name = urldecode($group->NAME);

			//save it for later.
			$groups[$group_name] = $group_id;
		}

		//next, ensure the contact exists, and add to contactgroupsmembers.
		$contact_id = ensure_contact($user_id, $contact);

		$contact_email = urldecode($contact->EMAIL);
		echo_debug("   contact $contact_email : $contact_id\n");

		//link contact to their groups.
		link_contact_to_group($contact, $contact_id, $groups);

	}
}

$mysqli->close();

exit(0);

//**********************************************************************
//**********************************************************************

/**********************************************************************
* ensure that the contact has been assigned to their groups.
*/
function link_contact_to_group($contact, $contact_id, $groups)
{
	global $mysqli;

	foreach($contact->GROUPS->children() as $group)
	{
		$group_name = urldecode($group->NAME);
		$group_created = mes(urldecode($group->CREATED));
		$query = "REPLACE INTO `contactgroupmembers` (contactgroup_id, contact_id, created) VALUES (".mes($groups[$group_name]).", $contact_id, '$group_created')";

		if (!$mysqli->query($query))
		{
			echo_stderr("Query error:\n".$query."\n".$mysqli->error."\n");
			return -1;
		}

		echo_debug("      group $group_name : ".$groups[$group_name]."\n");
	}
	return 1;
}

/**********************************************************************
* ensure that the Group exists for this user_id.
*/
function ensure_group($user_id, $group)
{
	global $mysqli;

	$group_name = mes(urldecode($group->NAME));

	$query = "SELECT contactgroup_id FROM `contactgroups` WHERE user_id=$user_id AND name='$group_name'";
	if (!$group_ids = $mysqli->query($query))
	{
		echo_stderr("Query error:\n".$query."\n".$mysqli->error."\n");
		return -1;
	}

	if ($group_ids->num_rows > 0)
	{
		$data = $group_ids->fetch_array();
		return $data['contactgroup_id'];
	}

	//No group, must add it.

	$group_changed = mes(urldecode($group->CHANGED));

	$query = "INSERT INTO `contactgroups` (user_id, changed, name) VALUES ($user_id, '$group_changed', '$group_name')";

	if (!$groups = $mysqli->query($query))
	{
		echo_stderr("Query error:\n".$query."\n".$mysqli->error."\n");
		return -1;
	}

	return $mysqli->insert_id;
}



/**********************************************************************
* ensure that the Contact exists for this user_id.
*/
function ensure_contact($user_id, $contact)
{
	global $mysqli;

	$contact_email = mes(urldecode($contact->EMAIL));
	$contact_name = mes(urldecode($contact->NAME));

	$query = "SELECT contact_id FROM `contacts` WHERE user_id=$user_id AND name='$contact_name' AND email='$contact_email' LIMIT 1";
	if (!$contact_ids = $mysqli->query($query))
	{
		echo_stderr("Query error:\n".$query."\n".$mysqli->error."\n");
		return -1;
	}

	if ($contact_ids->num_rows > 0)
	{
		$data = $contact_ids->fetch_array();
		return $data['contact_id'];
	}

	//No contact, must add it.

	$contact_changed =		mes(urldecode($contact->CHANGED));
	$contact_firstname =	mes(urldecode($contact->FIRSTNAME));
	$contact_surname =		mes(urldecode($contact->SURNAME));
	$contact_vcard =		mes(urldecode($contact->VCARD));
	$contact_words =		mes(urldecode($contact->WORDS));

	$query = "INSERT INTO `contacts` (changed, name, email, firstname, surname, vcard, words, user_id) VALUES ('$contact_changed', '$contact_name', '$contact_email', '$contact_firstname', '$contact_surname', '$contact_vcard', '$contact_words', $user_id)";
	if (!$contact_ids = $mysqli->query($query))
	{
		echo_stderr("Query error:\n".$query."\n".$mysqli->error."\n");
		return -1;
	}

	return $mysqli->insert_id;
}

/**********************************************************************
* ensure that the Identity exists for this user_id.
* no need to worry about the return value
*/
function ensure_identity($user_id, $identity)
{
	global $mysqli;

	$id_email = mes(urldecode($identity->EMAIL));
	$id_name = mes(urldecode($identity->NAME));

	$query = "SELECT identity_id FROM `identities` WHERE user_id=$user_id AND name='$id_name' AND email='$id_email' LIMIT 1";
	if (!$ids = $mysqli->query($query))
	{
		echo_stderr("Query error:\n".$query."\n".$mysqli->error."\n");
		return -1;
	}

	if ($ids->num_rows > 0)
	{
		$data = $ids->fetch_array();
		return $data['identity_id'];
	}

	//No identity, must add it.
	$id_changed =		mes(urldecode($identity->CHANGED));
	$id_standard =		mes(urldecode($identity->STANDARD));
	$id_organization =	mes(urldecode($identity->ORGANIZATION));
	$id_reply_to =		mes(urldecode($identity['REPLY-TO']));
	$id_bcc =			mes(urldecode($identity->BCC));
	$id_signature =		mes(urldecode($identity->SIGNATURE));
	$id_html_signature =mes(urldecode($identity->HTML_SIGNATURE));

	$query = "INSERT INTO `identities` (user_id, changed, standard, name, organization, email, `reply-to`, bcc, signature, html_signature) VALUES ($user_id, '$id_changed', $id_standard, '$id_name', '$id_organization', '$id_email', '$id_reply_to', '$id_bcc', '$id_signature', $id_html_signature)";
	if (!$ids = $mysqli->query($query))
	{
		echo_stderr("Query error:\n".$query."\n".$mysqli->error."\n");
		return -1;
	}

	return $mysqli->insert_id;
}

/**********************************************************************
* ensure that the User exists
* yes: return user_id
* no: add user, set data, resturn user_id
*/
function ensure_user($email)
{
	global $mysqli, $rc_mail_host;
	$username = mes(urldecode($email->USERNAME));
	$query = "SELECT user_id FROM `users` WHERE username='$username' LIMIT 1";
	if (!$users = $mysqli->query($query))
	{
		echo_stderr("Query error:\n".$query."\n".$mysqli->error."\n");
		return -1;
	}

	if ($users->num_rows > 0)
	{
		$data = $users->fetch_array();
		return $data['user_id'];
	}

	//No User, must add it.
	$created=mes(urldecode($email->CREATED));
	$last_login=mes(urldecode($email->LAST_LOGIN));
	$language=mes(urldecode($email->LANGUAGE));
	$preferences=mes(urldecode($email->PREFERENCES));

	$query = "INSERT INTO `users` (username, mail_host, created, last_login, language, preferences) VALUES ('$username', '$rc_mail_host', '$created', '$last_login', '$language', '$preferences')";
	if (!$users = $mysqli->query($query))
	{
		echo_stderr("Query error:\n".$query."\n".$mysqli->error."\n");
		return -1;
	}

	return $mysqli->insert_id;
}

//**********************************************************************

function show_help()
{
	global $mysql_db, $version;

	echo_stderr("Roundcube $version restore script to restore Users.\n\n");
	echo_stderr("Usage:\n");
	echo_stderr("  username=username domain=domain.com xml_file=/path/to/rc.xml ".__FILE__."\n\n");

	echo_stderr("The script will read in the XML specified by xml_file.\n");
	echo_stderr("It will insert the data into the $mysql_db database.\n");
	exit(2);
}

function die_stderr($str)
{
	echo_stderr($str);
	die();
}

function echo_stderr($str)
{
	$fd = fopen('php://stderr', 'w');
	fwrite($fd, $str);
	fclose($fd);
}

function echo_debug($str)
{
	global $is_debug;

	if ($is_debug)
		echo $str;
}

function mes($str)
{
	global $mysqli;
	return $mysqli->real_escape_string($str);
}

?>
