<?php
/**
 * @category 	Atmail
 * @package 	Models
 * @subpackage	Config
 * @copyright 	Copyright (c) 2009-2011 ATMAIL. All rights reserved
 * @license 	See http://atmail.com/license.php for license agreement
 * @author		Allan Wrethman allan@staff.atmail.com
 * @author		Atmail (http://atmail.com)
 */
class config
{

	public static function save($sectionNode, $newConfig)
	{
		//get existing db records.			 
		$dbConfig = Zend_Registry::get('dbConfig');
		$dbAdapter = Zend_Registry::get('dbAdapter');
		$select = $dbAdapter->select()
							->from($dbConfig->database->params->configtable)
							->where("section = " . $dbAdapter->quote($sectionNode));
		$query = $select->query();
		$existingConfig = $query->fetchAll();
		foreach($newConfig as $newKey => $newValue) 
		{
			//go through each responce field
			$responseMatchFoundInDb = false;
			foreach($existingConfig as $existingRow) 
			{

				//go thorugh each db row looking for a match (only update exsting)
				if( $existingRow['keyName'] == $newKey )
				{

					//update $row then update db  
					//if array remove all and all new
					if( $existingRow['keyType'] == 'Array') 
					{

						$where = $dbAdapter->quoteinto('`section` = ?', $sectionNode) . ' AND ' . $dbAdapter->quoteinto(' `keyName` = ?', $existingRow['keyName']);
						$result = $dbAdapter->delete($dbConfig->database->params->configtable,$where);
						$newValueArray = explode("\n", $newValue);
						unset($existingRow['configId']);
						foreach( $newValueArray as $v ) 
						{

							$existingRow['keyValue'] = trim($v);
							// Skip array values with no data ( e.g local domains with a return/\n )
							if( !empty($existingRow['keyValue']) )
							{
								
								$result = $dbAdapter->insert($dbConfig->database->params->configtable,$existingRow);
								
							}

						}

					} 
					else if( $existingRow['keyType'] == 'Boolean') 
					{

						$existingRow['keyValue'] = (in_array( $newValue, array('yes','Yes', 'YES', 1, '1', true, 'true') )?'1':'0');
						$result = $dbAdapter->update($dbConfig->database->params->configtable,$existingRow, $dbAdapter->quoteinto('configId = ?',  $existingRow['configId']) );
                        
					}
					else
					{

						$existingRow['keyValue'] = trim($newValue);
						$result = $dbAdapter->update($dbConfig->database->params->configtable,$existingRow, $dbAdapter->quoteinto('configId = ?',  $existingRow['configId']) );
						
					}
					$responseMatchFoundInDb = true;
					break;

				}   

			}
			if( $responseMatchFoundInDb == false ) 
			{

				if( is_null($newValue) ) 
				{

					$newValue = '';

				}

				$newKeyType = "String";

				//$dbAdapter->insert( $dbConfig->database->params->configtable, $row );
				$result = $dbAdapter->insert($dbConfig->database->params->configtable,array('keyName' => $newKey, 'keyType' => $newKeyType, 'keyValue' => $newValue, 'section' => $sectionNode));

				//Zend_Registry::get('log')->info('Config field not found in db so added: ' . $newKeyType . ":" . $newKey . ':' . $newValue);
				
			}
		}

		$dbConfig = new Zend_Config_Ini( APP_ROOT . 'config/dbconfig.ini', 'production');

                // Recreate the config object from the new settings in the DB
                // and save back to Zend Registry
		$configObj = new Atmail_Config_Mysql($dbConfig, true);
		Zend_Registry::set('config', $configObj);
	}

	public static function saveIniChanges($array, $delimiter = '.')
	{
        $configurationNode = 'production';
		$sectionNode = 'database.params';
		$dbConfig = Zend_Registry::get('dbConfig');

		$dbArray = 	array_merge(
			(isset($array['master_key']) ? array('masterkey' => $array['master_key']) : array()), 
			array(
				'host'	 => $array['sql_host'],
				'username' => $array['sql_user'],
				'password' => $array['sql_pass'],
				'dbname'   => $array['sql_table']
			)
		);
		
		$dbConfig = new Zend_Config_Ini( APP_ROOT . 'config/dbconfig.ini', NULL, array('allowModifications' => true));
        $dbConfig = $dbConfig->toArray();

              // add masterkey during first active section found
        if(isset($array['master_key']) && $array['master_key'] != '')
        {
            $dbConfig[$configurationNode]['database']['params']['masterkey']= $array['master_key'];
        }

        $dbHosts = explode(',',$array['sql_host'],6);
        $totalHosts = count($dbHosts);
        if($totalHosts > 5) $totalHosts = 5;

        if(count($dbConfig[$configurationNode]) > $totalHosts)
        {
            $dbConfig[$configurationNode] = array_splice($dbConfig[$configurationNode], 0, $totalHosts);
        }
        for( $i = 0 ; $i < $totalHosts ; $i ++ )
        {
            $dbNodeName = 'database' . ( $i == 0 ? '' : ($i + 1));
            if(!array_key_exists($dbNodeName, $dbConfig[$configurationNode]))
            {
                $dbConfig[$configurationNode][$dbNodeName] = array_merge(array(),$dbConfig[$configurationNode]['database']);
            }

            $dbConfig[$configurationNode][$dbNodeName]['params']['host'] = $dbHosts[$i];
            $dbConfig[$configurationNode][$dbNodeName]['params']['username'] = $array['sql_user'];
            $dbConfig[$configurationNode][$dbNodeName]['params']['password'] = $array['sql_pass'];
            $dbConfig[$configurationNode][$dbNodeName]['params']['dbname'] = $array['sql_table'];
        }

        $newConfig = new Zend_Config($dbConfig);
        # write new configuration
        $writer = new Zend_Config_Writer_Ini(
            array(
                'config'   => $newConfig,
                'filename' => APP_ROOT . "config/newdbconfig.ini"));
        $writer->write();
        if( !@copy(APP_ROOT . "config/dbconfig.ini", APP_ROOT . 'config/dbconfig.ini.bak') )
            throw new Exception('Unable to save backup of config, possibly from insufficient web server permissions. Continuing anyway.');
        if( !@copy(APP_ROOT . "config/newdbconfig.ini", APP_ROOT . 'config/dbconfig.ini') )
            throw new Exception('Unable to save changes to config, possibly from insufficient web server permissions.');
        unlink(APP_ROOT . "config/newdbconfig.ini");
        return true;


	}

	public static function modifyDbConfigInExistingServerConfigFiles( $newConfigArray = array() )
	{
		$dbConfig = Zend_Registry::get('dbConfig');
		if( count($newConfigArray) == 0 )
		{
			$newConfigArray['sql_host'] = $dbConfig->database->params->host; //this section is old and quite likely redundant, if not it needs to be upgraded to handle multiple sql servers
			$newConfigArray['sql_user'] = $dbConfig->database->params->username;
			$newConfigArray['sql_pass'] = $dbConfig->database->params->password;
			$newConfigArray['sql_table'] = $dbConfig->database->params->dbname;
		}
		// Find the mode from the current DB (previous upgrade attempts failed here, expected the argument)
		if(empty($newConfigArray['install_type'])) {
		$newConfigArray['install_type'] = Zend_registry::get('config')->global['install_type'];
		}
	
		if(empty($newConfigArray['serverDir'])) {
			$newConfigArray['serverDir'] = Zend_registry::get('config')->global['serverDir'];
		}
		
		// If we are the Webmail client, do not execute this function ( server mode files only )
		if( $newConfigArray['install_type'] != 'server')
		{
			return;
		}
		
		if( !isset($newConfigArray['serverDir']) )
		{
			
			$newConfigArray['serverDir'] = '/usr/local/atmail/';
		
		}

		// Some servers seem to prefer we use 127.0.0.1 rather than localhost
		if( strtolower($newConfigArray['sql_host']) == 'localhost' )
		{
			
			$newConfigArray['sql_host'] = '127.0.0.1';
			
		}

		$dbTables = new dbTables();

		// Call the SA save function, to store the new mysql pass in all the SA config files (bayes, awl, etc)
		config::publishServerConfigFiles('spamassassin');
		/*
		////update to support multi SQL servers if required or revived
		if( !$fh = fopen("{$newConfigArray['serverDir']}mailserver/configure", 'r'))
		{
			
			throw new Atmail_Config_Exception("Cannot read {$newConfigArray['serverDir']}mailserver/configure");
			
		}
		else
		{
		
			if( !$fh2 = fopen("{$newConfigArray['serverDir']}mailserver/configure.new", 'a'))
			{
				
				throw new Atmail_Config_Exception("Cannot write {$newConfigArray['serverDir']}mailserver/configure.new");
				
			}
			else
			{
				
				$host = $newConfigArray['sql_host']? $newConfigArray['sql_host'] : '127.0.0.1';

				while (!feof($fh))
				{
					$line = fgets($fh);
					
					$line = preg_replace('|hide mysql_servers = .*|', "hide mysql_servers = $host/{$newConfigArray['sql_table']}/{$newConfigArray['sql_user']}/{$newConfigArray['sql_pass']}", $line); 
					fwrite($fh2, $line);
				}

				fclose($fh);
				fclose($fh2);
				rename("{$newConfigArray['serverDir']}mailserver/configure.new", "{$newConfigArray['serverDir']}mailserver/configure");
			
			}
			
		}
		*/

		foreach(array('dovecot-sql.conf', 'dovecot-users-sql.conf') as $conf)
		{

			if( !$fh = @fopen("{$newConfigArray['serverDir']}mailserver/etc/$conf", 'r'))
			{
				
				throw new Atmail_Config_Exception("Cannot read {$newConfigArray['serverDir']}mailserver/etc/$conf");
				
			}
			else
			{
				
				if( !$fh2 = @fopen("{$newConfigArray['serverDir']}mailserver/etc/$conf.new", 'w'))
				{
					
					throw new Atmail_Config_Exception("Cannot write {$newConfigArray['serverDir']}mailserver/etc/$conf.new");
					
				}
				else
				{
					
					$host = ($newConfigArray['sql_host'])? $newConfigArray['sql_host'] : '127.0.0.1';
					$hosts = explode(',',$host,6);
					$hostsString = '';
					foreach( $hosts as $k => $v )
					{
						if( $k > 4 )
							break;
						$hostsString .= " host=" . $v;
							
					}

					while (!feof($fh))
					{
					
						$line = fgets($fh);
						$line = preg_replace('/^connect = .*/', "connect ={$hostsString} dbname={$newConfigArray['sql_table']} user={$newConfigArray['sql_user']} password={$newConfigArray['sql_pass']}", $line);
						fwrite($fh2, $line);
					
					}

					fclose($fh);
					fclose($fh2);
					rename("{$newConfigArray['serverDir']}mailserver/etc/$conf.new", "{$newConfigArray['serverDir']}mailserver/etc/$conf");

				}

			}

		}

		return;

	}

	public static function publishServerConfigFiles($conffile = 'exim', $newConfigArray = null) 
	{
		
		// Function which re-writes the configuration files of Atmail
		// Read it again, for our new settings. Do not use require_once!!
		require('library/Atmail/Exim_Config.php');
		
		// Create an array for the filename, requires for config settings with more then 1 config file ( e.g SA 3.3.2+ )
		$filename = array();
			
		// If using the Exim or Courier config, change the array
		if( $conffile == 'exim')
		{
			
			$filename[] = "/usr/local/atmail/mailserver/configure";
			$conf = $exim_conf;

		}
		elseif( $conffile == 'dovecot')
		{
		
			$filename[] = "/usr/local/atmail/mailserver/etc/dovecot.conf";
			$conf = $dovecot_conf;

		}
		elseif( $conffile == 'dovecotsql' )
		{
			
			$filename[] = "/usr/local/atmail/mailserver/etc/dovecot-sql.conf";
			$conf = $dovecotsql_conf;

		}
		elseif( $conffile == 'spamassassin')
		{
			// We have multiple locations for the SA 3.3.2+ config files, make an array and loop
			$baseDir = "/usr/local/atmail/spamassassin/etc/mail/spamassassin/";
			$filename[] = $baseDir . "init.pre";
			$filename[] = $baseDir . "local.cf";
			$filename[] = $baseDir . "v310.pre";
			$filename[] = $baseDir . "v312.pre";
			$filename[] = $baseDir . "v320.pre";
			$filename[] = $baseDir . "v330.pre";

			$conf = $sa_conf; // Slurp down the values from the Exim_Config.php array of #CONF changes

		}
		elseif( $conffile == 'dovecotldap')
		{
			
			$filename[] = "/usr/local/atmail/mailserver/etc/dovecot-ldap.conf";
			$conf = $dovecotldap_conf;

		}
		else
		{
			// No such config/file, return
			return;
		
		}

		// Loop, for each file on the configuration
		foreach($filename as $file) {
		
		
			if( !$read = @fopen("$file", 'r') )
			{

				return Zend_Registry::get('log')->info("Cannot read $file");

			}

			if( !$write = @fopen("$file.new", 'w') )
			{

				return Zend_Registry::get('log')->info("Cannot write $file.new");

			}

			$status = array();

			$value = "";
			$config = "";

			while(!feof($read))
			{

				$line = fgets($read);

				// Check for our configuration line
				if (preg_match('/^#\s?<(\w+)>/', $line, $m))
				{

					$value = $m[1];

				}

				if (preg_match("/^#\s?<$value>/", $line))
				{

					$status[$value] = 2; //beginning tag found

				}

				if (preg_match("/^#\s?<\/$value>/", $line))
				{

					$status[$value] = 0; // end tag found
					$value = '';

				}

				if( $value)
				{

					if (isset($conf[$value]))
					{
						// Configuration if statements
						if( $status[$value] == 2)
						{ //inside tag

							if( $status[$value] == 3) // already written new config now carry on reading lines until old end tag reached
							{

								continue;

							}

							$config .= $conf[$value];

							$status[$value] = 3; // wrote new config now carry on reading lines until old end tag reached

						}
					}   

				}   


				// Print all other configuration values
				else
				{

					$config .= $line;

				}

			}

			fwrite($write, $config);
			fclose($read);
			fclose($write);

			if( !rename("$file.new", "$file") )
			{

				return Zend_Registry::get('log')->info("Cannot rename $file.new to $file");

			}

			// If Exim restart
			if( $conf == 'exim')
			{

				//$this->restart_exim();

			}
	
		}
		
	}

	private static function composeLinearConfig($array, $delimiter = '.')
	{
		//if is array recurse intil find value then pass back string (e.g. for ini files)
		$thisArray = array();
		foreach($array as $key => $value)
		{
			if( is_array($value) )
			{
				$childArray = self::composeLinearConfig($value);
				foreach($childArray as $node)
					$thisArray[] = array($key . $delimiter . $node[0],$node[1]);
			} else
				$thisArray[] = array($key,$value);
		}
		return $thisArray;
	}

	/* Save the new admin password
	*/
	public static function changePassword($oldpass, $password)
	{
		//get existing db records.			 
		$dbConfig = Zend_Registry::get('dbConfig');
		$dbAdapter = Zend_Registry::get('dbAdapter');
		$dbTables = new dbTables();

		// Query for the old password
		$var = array(
		'password' => new Zend_Db_Expr( $dbAdapter->quoteinto('MD5(?)',  $oldpass) )
		);

		$select = $dbAdapter->select()
							->from(array('p' => $dbTables->authUsers), array('password'))
							->where($dbAdapter->quoteinto('username = ?',  'admin'))
							->where("password = " . $dbAdapter->quote($var['password']));

		$query = $select->query();
		$rows = $query->fetchAll();

		// Existing passsword does not match, return
		if(empty($rows[0]['password']))
		return 0;

		// Update query
		$where = array(
			$dbAdapter->quoteinto('username = ?',  'admin'),
			new Zend_Db_Expr( $dbAdapter->quoteinto('password = MD5(?)',  $oldpass))
		);

		$var = array(
		'password' => new Zend_Db_Expr( $dbAdapter->quoteinto('MD5(?)',  $password) )
		);

		$dbAdapter->update($dbTables->authUsers, $var, $where );

		// Write the MD5/pass to the Exim configure file for SMTP authentication
		$write = @fopen("/usr/local/atmail-archive/mailserver/.pass", 'w');

		fwrite($write, "admin: " . md5($password));
		fclose($write);
 
		return 1;
	}

	public static function changePasswordSSH($password)
	{

	 system("echo '" . escapeshellarg($password) . "' | /usr/local/atmail-archive/mailserver/bin/atmail-passwd-change 2>&1 > /dev/null");

	}

	/*
	Load the system IP, hostname and network configuration directly. Don't trust the vales in the Config
	array/DB, just in case an admin has changed the settings via the terminal/SSH
	*/
	public static function loadNetworkSettings($array)
	{

	// Find current IP's
	$eth0 = `/sbin/ifconfig eth0`;
	if (preg_match('/inet addr:(.*?) /', $eth0, $m))
		$array['ipeth0'] = $m[1];

	if (preg_match('/Mask:(.*)/', $eth0, $m))
		$array['networketh0'] = $m[1];

	$eth1 = `/sbin/ifconfig eth1`;
	if (preg_match('/inet addr:(.*?) /', $eth1, $m))
		$array['ipeth1'] = $m[1];

	if (preg_match('/Mask:(.*)/', $eth1, $m))
		$array['networketh1'] = $m[1];

	// Find the default route
	$droute = `/bin/netstat -nr`;

	if (preg_match('/^0\.0\.0\.0\s+(.*?)\s.*?eth0/m', $droute, $m))	{
		$array['defaultgatewayeth0'] = $m[1];
	}

	if (preg_match('/^0\.0\.0\.0\s+(.*?)\s.*?eth1/m', $droute, $m))
	{
		$array['defaultgatewayeth1'] = $m[1];
	}

	// Find the default nameserver
	if( $fh = fopen('/etc/resolv.conf', 'r'))
	{

		$nameservers = array();

		while(false !== $line = fgets($fh))	{
			$line = trim($line);
			if (preg_match('/nameserver (.*)/', $line, $m))
			{

				array_push($nameservers, $m[1]);

			}
		}

		$array['nameserver1'] = $nameservers[0];

		if(isset($nameservers[1]))
		$array['nameserver2'] = $nameservers[1];

		if(isset($nameservers[2]))
		$array['nameserver3'] = $nameservers[2];

		fclose($fh);

	}
	else
	{

		print "Cannot open /etc/resolv.conf\n";
	}

	$array['ipconfeth0'] = '';
	$array['ipconfeth1'] = '';

	// Check if a device is DHCP
	if( $fh = fopen('/etc/sysconfig/network-scripts/ifcfg-eth0', 'r'))
	{
		while (false !== $line = fgets($fh))	{
		   if (strpos(strtoupper($line), 'BOOTPROTO=DHCP') !== false)
	{
			   $array['ipconfeth0'] = 'DHCP';
			   break;
		   }
		}

		fclose($fh);
	}

	if( $fh = fopen('/etc/sysconfig/network-scripts/ifcfg-eth1', 'r'))
	{
		while (false !== $line = fgets($fh))
	{
			if (strpos(strtoupper($line), 'BOOTPROTO=DHCP') !== false)
	{
				$array['ipconfeth1'] = 'DHCP';
				break;
			}
		}

		fclose($fh);
	}

	// Find the servers hostname
	$array['hostname'] = trim(`/bin/hostname`);
	return $array;
}

	/* Restart the network services using the setuid root script */
	public static function restartNetworkSettings()
	{

		system('/usr/local/atmail-archive/mailserver/bin/atmail-restart-network > /dev/null 2>&1');

		return;

	}

	public static function saveNetworkSettings($array)
	{
		$config = '';

		// Save the settings
		if( $array['save'])	{

			system('/bin/hostname ' . escapeshellarg($array['hostname']));
			//$pref['hostname'] = $var['hostname'];
			//writeconf();

			// Write the /etc/init.d/network with the new hostname
			if (!$read = @fopen("/etc/sysconfig/network", 'r'))
				catcherror("Cannot read /etc/init.d/network");

			while(!feof($read))
			{
				$line = fgets($read);
				$line = preg_replace('/HOSTNAME=.*/', "HOSTNAME={$array['hostname']}", $line);
				$config .= $line;
			}

			fclose($read);

			if (!$write = @fopen("/etc/sysconfig/network", 'w'))
				catcherror("Cannot write /etc/sysconfig/network");

			fwrite($write, $config);
			fclose($write);

			foreach(array('eth0', 'eth1') as $int)
			{

				// If we are DHCP
				if ( $array["ipconf$int"] == 'DHCP' )
				{

					if( $fh = fopen("/etc/sysconfig/network-scripts/ifcfg-$int", 'w'))
					{

						fwrite($fh, "BOOTPROTO=dhcp\nTYPE=Ethernet\nDEVICE=$int\nONBOOT=yes\n");
						fclose($fh);

	}
	else
	{

						print "Cannot write: /etc/sysconfig/network-scripts/ifcfg-$int\n";
					}

				} else	{
					// Manually configure interface
					if( $fh = fopen("/etc/sysconfig/network-scripts/ifcfg-$int", 'w'))
	{
						fwrite($fh, "BOOTPROTO=none\nTYPE=Ethernet\nDEVICE=$int\nNETMASK=" . $array["network$int"] . "\nIPADDR=" . $array["ip$int"] . "\nGATEWAY=" . $array["defaultgateway$int"] . "\nONBOOT=yes\n");
						fclose($fh);

	}
	else
	{

						print "Cannot write: /etc/sysconfig/network-scripts/ifcfg-$int\n";
					}
				}
			}

			// Change the nameservers
			if( $fh = fopen("/etc/resolv.conf", 'w'))
	{
				if( $array['nameserver1'])
					fwrite($fh, "nameserver {$array['nameserver1']}\n");
				if( $array['nameserver2'])
					fwrite($fh, "nameserver {$array['nameserver2']}\n");
				if( $array['nameserver3'])
					fwrite($fh, "nameserver {$array['nameserver3']}\n");

				fclose($fh);

	}
	else
	{

				print "Cannot write /etc/resolv.conf\n";
			}

			// Sleep after config write, ifconfig/hostname seem to have a lag
			sleep(2);
		}

	}

	// Specify the custom CSS width/height for the logo used in the WebUI
	public static function generateLogo($type, $imageRef=0)
	{

		// Return the CSS/path for the custom branding/logo
		$siteBaseUrl = Zend_Registry::get('siteBaseUrl');

		$dbAdapter = Zend_Registry::get('dbAdapter');

		//$serverName = config::serverBrand();

		//$stats = $dbAdapter->fetchAll("select ImageHeight, ImageWidth, ImageType from ImageBranding where Account=? and Type=?", array($serverName, $type));
		$stats = $dbAdapter->fetchAll("select ImageHeight, ImageWidth, ImageType from ImageBranding where Type=?", array($type));

		if(empty($stats[0]['ImageHeight']))
			return;

		// Return the login page CSS, for the top logo
		if($type == 'login' && $imageRef == 1) {
			$logoCss = "<img src='{$siteBaseUrl}index.php/default/index/viewlogo/type/login/' height='{$stats[0]['ImageHeight']}' width='{$stats[0]['ImageWidth']}'/>";

		} else if($type == 'login') {

			$rand = rand(0, 99999);

			$logoCss = <<<_EOF
DIV#logo {
background: url("{$siteBaseUrl}index.php/default/index/logo/type/login?$rand") no-repeat scroll 0 0 transparent !important;
height: {$stats[0]['ImageHeight']}px !important;
width: {$stats[0]['ImageWidth']}px !important;
}

DIV#header {
        height: {$stats[0]['ImageHeight']}px !important;
}
_EOF;

		} else if($type == 'webmail') {
			$logoCss = <<<_EOF
#header h3.branding a {
background: url("{$siteBaseUrl}index.php/default/index/viewlogo/type/webmail/?$rand") no-repeat scroll 0 0 transparent !important;
height: 51px;
width: 216px;
}
_EOF;

		}

		return $logoCss;

	}
	
}

