<?php
    /**
     * Utility functions
     *
     * @package core
     */

	/**
	 * Function which reads the data stream. This data is send by the WebClient.
	 * @return string data
	 */
	function readData() {
		$data = "";
		$putData = fopen("php://input", "r");
		
		while($block = fread($putData, 1024)) 
		{
			$data .= $block;
		}
		
		fclose($putData);
		return $data;
	}	
	
	/**
	 * Function which is called every time the "session_start" method is called.
	 * It unserializes the objects in the session. This function called by PHP.
	 * @param string @className the className of the object in the session
	 */	 	
	function sessionModuleLoader($className)
	{
		$className = strtolower($className); // for PHP5 set className to lower case to find the file (see ticket #839 for more information)

		switch($className)
		{
			case "bus":
				require_once("core/class.bus.php");
				break;
				
			default:
				$path = BASE_PATH . 'server/modules/class.' . $className . '.php';
				if (is_file($path)) {
					require_once($path);
				} else {
					$path = $GLOBALS['PluginManager']->getModuleFilePath($className);
					if (is_file($path)) {
						require_once($path);
					}
				}
				break;
		}
		if (!class_exists($className)){
			trigger_error("Can't load ".$className." while unserializing the session.", E_USER_WARNING);
		}
	}

	/**
	 * Function which replaces some characters to correct XML values.
	 * @param string @string string which should be converted
	 * @return string correct XML	 
	 */	 	
	function xmlentities($string) {
		$string = str_replace("\x00", "", $string);
		$string = preg_replace("/[\x01-\x08\x0b\x0c\x0e-\x1f]+/", "", $string); // remove control chars, they would break the xml, and aren't needed anyway
		return str_replace ( array ( '&', '"', "'", '<', '>'), array ( '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;'), $string );
	}
	
	/**
	 * Function which checks if an array is an associative array.
	 * @param array $data array which should be verified
	 * @return boolean true if the given array is an associative array, false if not
	 */	 	
	function is_assoc_array($data) {
		return is_array($data) && !empty($data) && !preg_match('/^\d+$/', implode('', array_keys($data)));
	}
	
	/**
	 * Function which compares two users on there full name. Used for sorting the user list.
	 * @param array $a user
	 * @param array $b user
	 * @return integer -1 - $a < $b || 0 - equal || 1 - $a > $b
	 */	 	
	function cmpUserList($a, $b) {
		if (strtolower($a["fullname"]) == strtolower($b["fullname"])) {
			return 0;
		}
		
		return (strtolower($a["fullname"]) < strtolower($b["fullname"])) ? -1 : 1;
	}
	
	/**
	 * Function which compares two groups on there group name. Used for sorting the group list.
	 * @param array $a group
	 * @param array $b group
	 * @return integer -1 - $a < $b || 0 - equal || 1 - $a > $b
	 */	 	
	function cmpGroupList($a, $b) {
		if (strtolower($a["groupname"]) == strtolower($b["groupname"])) {
			return 0;
		}
		
		return (strtolower($a["groupname"]) < strtolower($b["groupname"])) ? -1 : 1;
	}
	
	/**
	 * Function which is simular to the php function array_merge_recursive, but this 
	 * one overwrites duplicates.
	 * @param array $array1 array
	 * @param array $array1 array
	 * @return array merged array	 	 	 
	 */
	function array_merge_recursive_overwrite($array1, $array2)
	{
		if (!is_array($array1) || !is_array($array2)) {
			return $array2;
		}
		
		foreach ($array2 as $key=>$value) {
			if (isset($array1[$key])){
				$array1[$key] = array_merge_recursive_overwrite($array1[$key], $value);
			}else{
				$array1[$key] = $value;
			}
		}
		return $array1;
	}
	
	/**
	 * Function which adds a new mail popup on links which are mail addresses in mail body's.
	 * This function is called in the filter class (it is a preg_replace_callback function)
	 * @param array $match the information which part of the body is found.
	 * @return string new mail popup string	 	 	 
	 */
	function mailto_newmail($match)
	{
		$js_options = "'toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=780,height=560,top='+((screen.height/2)-280)+',left='+((screen.width/2)-390)";
		$newMail = array("TO"=>$match[4]);

		/**
		 * $match[5] will contain whole string of parameters passed in mailto link, we can't actually
		 * use reg expr to check different parts, so we have to split parameters
		 */
		if (!empty($match[5])) {
			$parameters = explode('&amp;', $match[5]);
			for($index = 0, $len = count($parameters); $index < $len; $index++) {
				$param = explode('=', $parameters[$index]);
				$newMail[strtoupper($param[0])] = $param[1];
			}
		}

		// build a string of parameters which will be added in URL
		$parameterString = "";
		if(isset($newMail["TO"])) $parameterString .= '&to=' . $newMail['TO'];
		if(isset($newMail["CC"])) $parameterString .= '&cc=' . $newMail['CC'];
		if(isset($newMail["BCC"])) $parameterString .= '&bcc=' . $newMail['BCC'];
		if(isset($newMail["BODY"])) $parameterString .= '&body=' . $newMail['BODY'];
		if(isset($newMail["SUBJECT"])) $parameterString .= '&subject=' . $newMail['SUBJECT'];

		// 'encode' newMail array
		$newMailString = bin2hex(serialize($newMail)); 
		return '<'.$match[1].$match[2].'='.$match[3].'mailto:'.$match[4].(!empty($match[5])?'?'.$match[5]:'').$match[6].' onclick='."\"parent.webclient.openWindow(this, 'createmail', 'index.php?load=dialog&task=createmail_standard" . $parameterString . "'); return false;\"".$match[7].'>';
	}


	function microtime_float() {
		list($usec, $sec) = explode(" ", microtime());
		return ((float)$usec + (float)$sec);
	}

	/**
	 * gets maximum upload size of attachment from php ini settings
	 * important settings are upload_max_filesize and post_max_size
	 * upload_max_filesize specifies maximum upload size for attachments
	 * post_max_size specifies maximum size of a post request, we are uploading attachment using post method
	 * that's why we need to check this also
	 * these values are overwritten in .htaccess file of WA
	 */
	function getMaxUploadSize($as_string = false)
	{
		$upload_max_value = strtoupper(ini_get('upload_max_filesize'));
		$post_max_value = strtoupper(ini_get('post_max_size'));

		/**
		 * if POST_MAX_SIZE is lower then UPLOAD_MAX_FILESIZE, then we have to check based on that value
		 * as we will not be able to upload attachment larger then POST_MAX_SIZE (file size + header data)
		 * so set POST_MAX_SIZE value to higher then UPLOAD_MAX_FILESIZE
		 */

		// calculate upload_max_value value to bytes
		if (strpos($upload_max_value, "K")!== false){
			$upload_max_value = ((int) $upload_max_value) * 1024;
		} else if (strpos($upload_max_value, "M")!== false){
			$upload_max_value = ((int) $upload_max_value) * 1024 * 1024;
		} else if (strpos($upload_max_value, "G")!== false){
			$upload_max_value = ((int) $upload_max_value) * 1024 * 1024 * 1024; 
		}

		// calculate post_max_value value to bytes
		if (strpos($post_max_value, "K")!== false){
			$post_max_value = ((int) $post_max_value) * 1024;
		} else if (strpos($post_max_value, "M")!== false){
			$post_max_value = ((int) $post_max_value) * 1024 * 1024;
		} else if (strpos($post_max_value, "G")!== false){
			$post_max_value = ((int) $post_max_value) * 1024 * 1024 * 1024; 
		}

		// check which one is larger
		$value = $upload_max_value;
		if($upload_max_value > $post_max_value) {
			$value = $post_max_value;
		}

		if ($as_string){
			// make user readable string
			if ($value > (1024 * 1024 * 1024)){
				$value = round($value / (1024 * 1024 * 1024), 1) ." ". _("GB");
			}else if ($value > (1024 * 1024)){
				$value = round($value / (1024 * 1024), 1) ." ". _("MB");
			}else if ($value > 1024){
				$value = round($value / 1024, 1) ." ". _("KB");
			}else{
				$value = $value ." ". _("B");
			}
		}

		return $value;
	}

	/**
	 * cleanTemp
	 * 
	 * Cleans up the temp directory.
	 * @param String $directory The path to the temp dir or sessions dir.
	 * @param Integer $maxLifeTime The maximum allowed age of files in seconds.
	 */
	function cleanTemp($directory = TMP_PATH, $maxLifeTime = false)
	{
		if($maxLifeTime===false)$maxLifeTime = ini_get("session.gc_maxlifetime");

		if (!is_dir($directory)){
			return;
		}

		$dir = opendir($directory);

		while($file = readdir($dir)) {
			if (!is_dir($directory . "/" . $file)){
				$fileinfo = stat($directory . "/" . $file);

				if($fileinfo && $fileinfo["atime"] < time() - $maxLifeTime) {
					unlink($directory . "/" . $file);
				}
			}
		}
	}

	function cleanSearchFolders()
	{
		$store = $GLOBALS["mapisession"]->getDefaultMessageStore();

		$storeProps = mapi_getprops($store, array(PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID));
		if (($storeProps[PR_STORE_SUPPORT_MASK] & STORE_SEARCH_OK) !== STORE_SEARCH_OK) {
			return;
		}

		$finderfolder = mapi_msgstore_openentry($store, $storeProps[PR_FINDER_ENTRYID]);

		$hierarchytable = mapi_folder_gethierarchytable($finderfolder, MAPI_DEFERRED_ERRORS);
		mapi_table_restrict($hierarchytable, array(RES_AND, 
												array(
													array(RES_CONTENT, 
														array(
															FUZZYLEVEL	=> FL_PREFIX,
															ULPROPTAG	=> PR_DISPLAY_NAME,
															VALUE		=> array(PR_DISPLAY_NAME=>"WebApp Search Folder")
														)
													),
													array(RES_PROPERTY,
														array(
															RELOP		=> RELOP_LT,
															ULPROPTAG	=> PR_LAST_MODIFICATION_TIME,
															VALUE		=> array(PR_LAST_MODIFICATION_TIME=>(time()-ini_get("session.gc_maxlifetime")))
														)
													)
												)
											), TBL_BATCH
		);

		$folders = mapi_table_queryallrows($hierarchytable, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_LAST_MODIFICATION_TIME));
		foreach($folders as $folder){
			mapi_folder_deletefolder($finderfolder, $folder[PR_ENTRYID]);
		}
	}

	function dechex_32($dec){
		// Because on 64bit systems PHP handles integers as 64bit, 
		// we need to convert these 64bit integers to 32bit when we 
		// want the hex value 
		$result = unpack("H*",pack("N", $dec));
		return $result[1];
	}

	/**
	 * Replace control characters in xml to '?'
	 * so xml parser will not break on control characters
	 * http://en.wikipedia.org/wiki/Windows-1252
	 * @param	string	$xml The XML string
	 * @return	string	$xml The valid utf-8 xml string
	 */
	function replaceControlCharactersInXML($xml) {
		/**
		 * don't remove CR => \x0D, LF => \x0A, HT => \x09, VT => \x0B
		 */
		$search = array("\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06",
			"\x07", "\x08", "\x0C", "\x0E", "\x0F", "\x10", "\x11", "\x12", "\x13",
			"\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1A", "\x1B", "\x1C",
			"\x1D", "\x1E", "\x1F", "\x7F");

		$replace = "?";

		$xml = str_replace($search, $replace, $xml);

		return $xml;
	}

	/**
	 * checkTrialVersion
	 * Checks whether the zarafa-server we are connected to has a trial license or not, based on the
	 * capabilities list.
	 * @return Boolean returns true on a trial license, false when otherwise.
	 */
	function checkTrialVersion(){
		$capabilities = mapi_zarafa_getcapabilities($GLOBALS['mapisession']->getDefaultMessageStore());
		return (is_array($capabilities)&&array_search('TRIAL', $capabilities)!==false?true:false);
	}

	/**
	 * getDaysLeftOnTrialPeriod
	 * Returns the number of days left on the trial of the connected zarafa-server. This number is 
	 * based on the remaining seconds left in the trial and rounded up to whole days.
	 * @return Integer Number of days remaining on trial. Returns 0 when not on a trial license.
	 */
	function getDaysLeftOnTrialPeriod(){
		$secondsLeft = 0;
		$capabilities = mapi_zarafa_getcapabilities($GLOBALS['mapisession']->getDefaultMessageStore());
		if(is_array($capabilities)){
			for($i = 0, $len = count($capabilities); $i < $len; $i++){
				if(substr($capabilities[$i], 0, 6) == 'TRIAL.'){
					$secondsLeft = substr($capabilities[$i], 6);
					break;
				}
			}
		}

		return ceil($secondsLeft/60/60/24);
	}

	/**
	 * This function will encode the input string for the header based on the browser that makes the
	 * HTTP request. MSIE has an issue with unicode filenames. All browsers do not seem to follow 
	 * the RFC specification. Firefox requires an unencoded string in the HTTP header. MSIE will 
	 * break on this and requires encoding. 
	 * @param String $input Unencoded string
	 * @return String Encoded string
	 */
	function browserDependingHTTPHeaderEncode($input){
		if(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') === false){
			return $input;
		}else{
			return urlencode($input);
		}
	}

	// Constants for regular expressions which are used in get method to verify the input string
	define("ID_REGEX", "/^[a-z0-9_]+$/im");
	define("STRING_REGEX", "/^[a-z0-9_\s()@]+$/im");
	define("ALLOWED_EMAIL_CHARS_REGEX", "/^[-a-z0-9_\.@!#\$%&'\*\+/=\?\^_`\{\|\}~]+$/im");
	define("TIMESTAMP_REGEX", "/^[0-9]+$/im");
	define("NUMERIC_REGEX", "/^[0-9]+$/im");
	// Don't allow "\/:*?"<>|" characters in filename.
	define("FILENAME_REGEX", "/^[^\/\:\*\?\"\<\>\|\\\]+$/im");

	/**
	* Function to retrieve a $_GET variable to prevent XSS
	*
	* $var = varibale requested
	* $default = default result when $var doesn't exist
	* $usequote = if $var must be surrounded by quotes, note that $default isn't surrounded by quotes even if this is set here!
	* $regex = To prevent unusual hackers attack / validate the values send from client.
	*/
	function get($var, $default="", $usequote=false, $regex = false){
		$result = $default;
		if (isset($_GET[$var])){
			$result = addslashes($_GET[$var]);
			if($regex) {
				$match = preg_match_all($regex, $result, $matches);
				if(!$match){
					$result = false;
					$usequote = false;
				}
			}
			if ($usequote===true) 
				$usequote = "'";
			if ($usequote!==false)
				$result = $usequote.$result.$usequote;
		}
		return $result;
	}
?>
