/**
 * ZmCustomizeMsi.js - customize Zimbra COnnector for Outlook MSI
 * Sets the default host port and connection method in the MSI
 ***/


/****************************************************
 *
 *
 *
 ****************************************************/
function PrintUsage( msg )
{
	WScript.Echo( 
	"Error: " + msg + "\n" + 
	"Usage: cscript " + WScript.ScriptName + " <msi-filename> <options>\n" +
	"    msi-filename                    : name of the Zimbra Connector for Outlook MSI\n" + 
	"    options 	\n" + 
	"     Server options 	\n" + 
    "      --server-name,-sn <name>|\"\"        : default Zimbra server value to set in the MSI\n" + 
	"      --server-port,-sp <port>           : default port to set in the MSI\n" + 
	"      --use-ssl,-ssl     <1|0>           : 1 means use HTTPS connection, 0 means HTTP connection\n" +
	"     ZDB file options 	\n" + 
	"      --zdb-folder,-zdb <path>|\"\"        : folder to store ZDB files if roaming profiles are used. \"\" - empty path means default location\n" +
	"     Logging options 	\n" + 
	"      --log-enabled,-le  <1|0>           : 1 means logging enabled, 0 means logging disabled\n" +
	"      --log-filename,-lf <path>          : path to the file where log will be written. Should end with 'zco.log'. F.e. c:\\somedir\\zco.log \n" +
	"      --log-max-size,-lms <size>         : maximum size of log file in megabytes\n" +
	"     Timeout options 	\n" + 
	"      --connect-timeout,-tc <ms>         : connect timeout in miliseconds. Should be greater than 60000. if less 60000 is used\n" +
	"      --send-timeout,-ts <ms>            : send timeout in miliseconds. Should be greater than 60000. if less 60000 is used\n" +
	"      --receive-timeout,-tr <ms>         : receive timeout in miliseconds. Should be greater than 60000. if less 60000 is used\n" +
	"      --option-receive-timeout,-tor <ms> : option receive timeout in miliseconds. Should be greater than 60000. if less 60000 is used\n"+
	"     Sync failure options 	\n" + 	
	"      --inbox-failures-off,-ifo <1|0>    : 1 means create sync failure messages only in 'Sync Issues' folders, 0 means create in Sync Issues and Inbox\n" +
	"     GAL options 	\n" + 	
	"      --galsync-mode,-gsm  <2|1|0>       : 2 means GAL sync disabled, 1 means manual GAL sync only, 0 means manual and automatic GAL sync enabled\n" +
	"      --galsync-delta,-gsd  <interval>   : interval in minutes to attempt a GAL delta sync\n" +
	"      --galsync-resync,-gsr  <interval>  : interval in days to attempt a full GAL sync\n" +
	"      --galsync-sleep,-gss  <interval>   : interval in milliseconds to sleep for GAL throttling.  Should be between 0 and 1000\n" +
	"      --ldap-enabled,-lde  <1|0>         : 1 means LDAP enabled, 0 means LDAP disabled\n" +
	"      --ldapserver-name,-lsn <name>|\"\" : default LDAP server name value to set in the MSI\n" +	
	"Samples: \n"+
	"  Cscript ZmCustomizeMsi.js ZimbraOlkConnector.msi: shows current settings of MSI file \n"+
	"  Cscript ZmCustomizeMsi.js ZimbraOlkConnector.msi -sn server.zimbra.com: sets default server to be 'server.zimbra.com'\n"+
	"  Cscript ZmCustomizeMsi.js ZimbraOlkConnector.msi -sn server.zimbra.com -sp 443 -ssl 1: sets default server to be 'server.zimbra.com' and sets SSL connection method to be used\n"
	);
	
}




/****************************************************
 *
 *
 *
 ****************************************************/
function IsNumber( str )
{
	var allNums = "0123456789";
	for( var i = 0; i < str.length; i++ )
	{
		if( allNums.indexOf(str.charAt(i)) == -1 )
			return false;
	}
	return true;
}



/****************************************************
 *
 *
 *
 ****************************************************/
function MsiExists( filename )
{
	var fso = new ActiveXObject("Scripting.FileSystemObject");
	return (fso.FileExists(filename));
}

/****************************************************
 *
 *
 *
 ****************************************************/
function MsiFolderExists( filename )
{
	var fso = new ActiveXObject("Scripting.FileSystemObject");
	return (fso.FolderExists(filename));
}

/****************************************************
 *
 *
 *
 ****************************************************/
function UnEscapePath( filename )
{
	var Unescaped = filename.replace("^%","%");
	while(Unescaped != filename)
	{
		filename = Unescaped+"";
		Unescaped = filename.replace("^%","%");
	}
	return Unescaped;
}


/****************************************************
 *
 *
 *
 ****************************************************/
var msiOpenDatabaseModeReadOnly = 0;
var msiColumnInfoNames = 0;
var msiColumnInfoTypes = 1;
var msiDatabaseNullInteger = -2147483648;

function ShowCurrentParameters(ZimbraMsiDb)
{
	WScript.Echo("Current settings:");
	var installer = new ActiveXObject("WindowsInstaller.Installer");
	var database = installer.OpenDatabase( ZimbraMsiDb, msiOpenDatabaseModeReadOnly );
	var query = database.OpenView("select Registry, Value from Registry");
	if(null != query)
	{
		query.Execute();
		
		var record = query.Fetch();
		while(null != record)
		{
			var iValue = record.IntegerData(2);
			if(msiDatabaseNullInteger == iValue)
			{
				iValue = record.StringData(2);
				if('#' == iValue.charAt(0))
				{
					//we have numeric value
					iValue = iValue.substr(1);
				}
				else
				{
					iValue = "\""+iValue+"\"";
				}
			}
			WScript.Echo("  "+record.StringData(1)+": "+iValue);
			record = query.Fetch();
		}
	}
}


/****************************************************
 *
 *
 *
 ****************************************************/
var msiOpenDatabaseModeTransact = 1;
var nArgs = WScript.Arguments.length;
if( nArgs < 1 )
{
	PrintUsage("Not enough parameters");
	WScript.Quit(1);
}

var ZmMsiDb = WScript.Arguments(0);
if( !MsiExists(ZmMsiDb) )
{
	PrintUsage("Cannot find file: " + ZmMsiDb);
	WScript.Quit(1);
}

var updates = new Array();
var updatesCount = 0;

//WScript.Sleep(30000);

//WScript.Echo("WScript.Arguments = "+WScript.Arguments.length);
var i=1;
while(i<WScript.Arguments.length)
{
	var iParameter = WScript.Arguments(i);
	//WScript.Echo(""+i+": " +iParameter);
	var iValue;
	switch(iParameter)
	{
		case "--server-name":
		case "-sn":
			iValue = WScript.Arguments(i+1);
			iValue = iValue + "";
			updates[updatesCount] = new Object();
			updates[updatesCount].col = "ServerName";
			updates[updatesCount].val = iValue;
			updates[updatesCount].tbl = "Registry"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		
		case "--server-port": 
		case "-sp": 
			iValue = WScript.Arguments(i+1);
			//WScript.Echo(iValue);
			if( !IsNumber(iValue) )
			{
				PrintUsage("Invalid port specified: " + iValue);
				WScript.Quit(1);
			}
			updates[updatesCount] = new Object();
			updates[updatesCount].col = "ServerPort";
			updates[updatesCount].val = "#" + iValue;
			updates[updatesCount].tbl = "Registry"
			updatesCount++;
			//skip next parameter
		//WScript.Echo(""+i+": " +iParameter);
			++i;
		//WScript.Echo(""+i+": " +iParameter);
		break;
		
		case "--use-ssl":
		case "-ssl":
			iValue = WScript.Arguments(i+1);
			if( iValue != "0" && iValue != "1" )
			{
				PrintUsage("Invalid connection method." + iValue + " Specify 0 or 1.");
				WScript.Quit(1);
			}
			updates[updatesCount] = new Object();
			updates[updatesCount].col = "ConnectionMethod";
			updates[updatesCount].val = "#" + iValue;
			updates[updatesCount].tbl = "Registry"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		
		case "--zdb-folder":
		case "-zdb":
			iValue = WScript.Arguments(i+1);
			iValue = iValue + "";
			if(iValue != "" && !MsiFolderExists(iValue))
			{
   				var UnEscaped = UnEscapePath(iValue);

              	var WshShell = WScript.CreateObject("WScript.Shell");
              	var Expanded = WshShell.ExpandEnvironmentStrings(UnEscaped);
              	//we check that at least directory exists for expanded variables
              	if( !MsiFolderExists(Expanded) )
              	{
              		PrintUsage("Cannot find folder: " + UnEscaped);
              		WScript.Quit(1);
              	}
              	iValue = UnEscaped+"";
            }
			updates[updatesCount] = new Object();
			updates[updatesCount].col = "ZDBFolder";
			updates[updatesCount].val = iValue;
			updates[updatesCount].tbl = "Registry"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		
		case "--log-enabled":
		case "-le":
			iValue = WScript.Arguments(i+1);
			if( iValue != "0" && iValue != "1" )
			{
				PrintUsage("Invalid parameter value." + iValue + " Specify 0 or 1.");
				WScript.Quit(1);
			}
			updates[updatesCount] = new Object();
			updates[updatesCount].col = "LoggingEnabled";
			updates[updatesCount].val = /*"#" + */iValue;
			updates[updatesCount].tbl = "Property"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		
		case "--log-filename":
		case "-lf":
			iValue = WScript.Arguments(i+1);
			iValue = iValue + "";
			if("" == iValue)
				iValue = "?";
			updates[updatesCount] = new Object();
			updates[updatesCount].col = "LoggingFileName";
			updates[updatesCount].val = iValue;
			updates[updatesCount].tbl = "Property"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		
		case "--log-max-size":
		case "-lms":
			iValue = WScript.Arguments(i+1);
			if( !IsNumber(iValue) )
			{
				PrintUsage("Invalid parameter value: " + iValue);
				WScript.Quit(1);
			}
			updates[updatesCount] = new Object();
			updates[updatesCount].col = "LoggingMaxMB";
			updates[updatesCount].val = /*"#" + */iValue;
			updates[updatesCount].tbl = "Property"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		
		case "--connect-timeout":
		case "-tc":
			iValue = WScript.Arguments(i+1);
			if( !IsNumber(iValue) )
			{
				PrintUsage("Invalid parameter value: " + iValue);
				WScript.Quit(1);
			}
			iValue = iValue + 0; //getting numeric value
			if(iValue < 60000)
				iValue = 60000;

			updates[updatesCount] = new Object();
			updates[updatesCount].col = "ConnectTimeout";
			updates[updatesCount].val = "#" + iValue;
			updates[updatesCount].tbl = "Registry"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		case "--send-timeout":
		case "-ts":
			iValue = WScript.Arguments(i+1);
			if( !IsNumber(iValue) )
			{
				PrintUsage("Invalid parameter value: " + iValue);
				WScript.Quit(1);
			}
			iValue = iValue + 0; //getting numeric value
			if(iValue < 60000)
				iValue = 60000;

			updates[updatesCount] = new Object();
			updates[updatesCount].col = "SendTimeout";
			updates[updatesCount].val = "#" + iValue;
			updates[updatesCount].tbl = "Registry"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		case "--receive-timeout":
		case "-tr":
			iValue = WScript.Arguments(i+1);
			if( !IsNumber(iValue) )
			{
				PrintUsage("Invalid parameter value: " + iValue);
				WScript.Quit(1);
			}
			iValue = iValue + 0; //getting numeric value
			if(iValue < 60000)
				iValue = 60000;

			updates[updatesCount] = new Object();
			updates[updatesCount].col = "ReceiveTimeout";
			updates[updatesCount].val = "#" + iValue;
			updates[updatesCount].tbl = "Registry"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		case "--option-receive-timeout":
		case "-tor":
			iValue = WScript.Arguments(i+1);
			if( !IsNumber(iValue) )
			{
				PrintUsage("Invalid parameter value: " + iValue);
				WScript.Quit(1);
			}
			iValue = iValue + 0; //getting numeric value
			if(iValue < 60000)
				iValue = 60000;

			updates[updatesCount] = new Object();
			updates[updatesCount].col = "OptionReceiveTimeout";
			updates[updatesCount].val = "#" + iValue;
			updates[updatesCount].tbl = "Registry"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		case "--inbox-failures-off":
		case "-ifo":
		iValue = WScript.Arguments(i+1);
		if( iValue != "0" && iValue != "1" )
		{
			PrintUsage("Invalid parameter value." + iValue + " Specify 0 or 1.");
			WScript.Quit(1);
		}		
		updates[updatesCount] = new Object();
		updates[updatesCount].col = "turnOffInboxFailures";
		updates[updatesCount].val = "#" + iValue;
		updates[updatesCount].tbl = "Registry"
		updatesCount++;
		//skip next parameter
		i++;		
		break;
		case "--galsync-mode":
		case "-gsm":
		iValue = WScript.Arguments(i+1);
		if( iValue != "0" && iValue != "1"  && iValue != "2")
		{
			PrintUsage("Invalid parameter value." + iValue + " Specify 0, 1, or 2.");
			WScript.Quit(1);
		}		
		updates[updatesCount] = new Object();
		updates[updatesCount].col = "GalSyncMode";
		updates[updatesCount].val = "#" + iValue;
		updates[updatesCount].tbl = "Registry"
		updatesCount++;
		//skip next parameter
		i++;		
		break;
		case "--galsync-delta":
		case "-gsd":
		iValue = WScript.Arguments(i+1);
		if( !IsNumber(iValue) )
		{
			PrintUsage("Invalid parameter value: " + iValue);
			WScript.Quit(1);
		}
		updates[updatesCount] = new Object();
		updates[updatesCount].col = "GalDeltaSync";
		updates[updatesCount].val = "#" + iValue;
		updates[updatesCount].tbl = "Registry"
		updatesCount++;
		//skip next parameter
		i++;		
		break;
		case "--galsync-resync":
		case "-gsr":
		iValue = WScript.Arguments(i+1);
		if( !IsNumber(iValue) )
		{
			PrintUsage("Invalid parameter value: " + iValue);
			WScript.Quit(1);
		}
		updates[updatesCount] = new Object();
		updates[updatesCount].col = "GalResync";
		updates[updatesCount].val = "#" + iValue;
		updates[updatesCount].tbl = "Registry"
		updatesCount++;
		//skip next parameter
		i++;		
		break;
		case "--galsync-sleep":
		case "-gss":
		iValue = WScript.Arguments(i+1);
		if( !IsNumber(iValue) )
		{
			PrintUsage("Invalid parameter value: " + iValue);
			WScript.Quit(1);
		}
		if(iValue > 1000)
			iValue = 1000;	
		updates[updatesCount] = new Object();
		updates[updatesCount].col = "GalFullSyncSleep";
		updates[updatesCount].val = "#" + iValue;
		updates[updatesCount].tbl = "Registry"
		updatesCount++;
		//skip next parameter
		i++;		
		break;
		case "--ldap-enabled":
		case "-lde":
		iValue = WScript.Arguments(i+1);
		if( iValue != "0" && iValue != "1" )
		{
			PrintUsage("Invalid parameter value." + iValue + " Specify 0 or 1.");
			WScript.Quit(1);
		}		
		updates[updatesCount] = new Object();
		updates[updatesCount].col = "EnableLDAP";
		updates[updatesCount].val = "#" + iValue;
		updates[updatesCount].tbl = "Registry"
		updatesCount++;
		//skip next parameter
		i++;		
		break;
		case "--ldapserver-name":
		case "-lsn":
			iValue = WScript.Arguments(i+1);
			iValue = iValue + "";
			updates[updatesCount] = new Object();
			updates[updatesCount].col = "LDAPServerName";
			updates[updatesCount].val = iValue;
			updates[updatesCount].tbl = "Registry"
			updatesCount++;
			//skip next parameter
			i++;
		break;
		
		default:
			PrintUsage("Invalid parameter: " + iParameter);
	}	
	//WScript.Echo(""+i+": " +iParameter);
	i++;
}


if(0 == updatesCount)
{
	ShowCurrentParameters(ZmMsiDb);
	WScript.Quit(0);
}

/*
var ZmServer = WScript.Arguments(1);
var ZmPort = WScript.Arguments(2);
var ZmUseSSL = WScript.Arguments(3);
var ZDBFolder = WScript.Arguments(4);

if( ZDBFolder != "" && !MsiFolderExists(ZDBFolder) )
{
	var UnEscaped = UnEscapePath(ZDBFolder);

	var WshShell = WScript.CreateObject("WScript.Shell");
	var Expanded = WshShell.ExpandEnvironmentStrings(UnEscaped);
	//we check that at least directory exists for expanded variables
	if( !MsiFolderExists(Expanded) )
	{
		PrintUsage("Cannot find folder: " + UnEscaped);
		WScript.Quit(1);
	}
	ZDBFolder = UnEscaped+"";
}


if( !IsNumber(ZmPort) )
{
	PrintUsage("Invalid port specified");
	WScript.Quit(1);
}

if( ZmUseSSL != "0" && ZmUseSSL != "1"  )
{
	PrintUsage("Invalid connection method.  Specify 0 or 1.");
	WScript.Quit(1);
}

var updates = new Array(3);
updates[0] = new Object();
updates[0].col = "ServerName";
updates[0].val = ZmServer;
updates[1] = new Object();
updates[1].col = "ServerPort";
updates[1].val = "#" + ZmPort;
updates[2] = new Object();
updates[2].col = "ConnectionMethod";
updates[2].val = "#" + ZmUseSSL;
updates[3] = new Object();
updates[3].col = "ZDBFolder";
updates[3].val = ZDBFolder;
*/


WScript.Echo("Setting parameters: \n");
for(var i = 0; i< updates.length; i++)
{
	var iValue = updates[i].val;
	if('#' == iValue.charAt(0))
		iValue = iValue.substr(1);
	else
		iValue = "\""+iValue+"\"";
	WScript.Echo(updates[i].col+": "+iValue);
}

//for debug only
//WScript.Quit(0);

try
{
	var installer = new ActiveXObject("WindowsInstaller.Installer");
	var database = installer.OpenDatabase( ZmMsiDb, msiOpenDatabaseModeTransact );
	
	for( var i = 0; i < updates.length; i++ )
	{
		var query = 
			"update " + updates[i].tbl + " "/*"Registry "*/ + 
			"Set Value='" + updates[i].val + "' " + 
			"Where " + updates[i].tbl + "='" /*"Registry='"*/ + updates[i].col + "'";
//for debug only
//		WScript.Echo("Updating database query: '"+query+"'");		
		var view = database.OpenView(query);
		view.Execute();
	}
	
	database.Commit();
	WScript.Echo("Installer db updated successfully");
}
catch(e)
{
	WScript.Echo("Error opening or updating MSI" );
	WScript.Echo("Error Number     : " + (e.number & 0xFFFF) );
	WScript.Echo("Error Description: " + e.description);
}

WScript.Quit(0);
