<?php
	/**
	 * ResolveNames Module
	 */
	class ResolveNamesModule extends Module
	{
		/**
		 * Constructor
		 */
		function ResolveNamesModule($id, $data)
		{
			parent::Module($id, $data);
		}

		/**
		 * Executes all the actions in the $data variable.
		 * @return boolean true on success of false on fialure.
		 */
		function execute()
		{
			foreach($this->data as $actionType => $action)
			{
				if(isset($actionType)) {
					try {
						$store = $this->getActionStore($action);

						switch($actionType)
						{
							case "checknames":
								$this->checkNames($store, $action);
								break;
							default:
								$this->handleUnknownActionType($actionType);
						}
					} catch (MAPIException $e) {
						$this->processException($e, $actionType);
					}
				}
			}
		}
		
		/**
		 * Function which checks the names, sent by the client. This function is used
		 * when a user wants to sent an email and want to check the names filled in
		 * by the user in the to, cc and bcc field. This function uses the global
		 * user list of Zarafa to check if the names are correct.		 		 		 
		 * @param object $store MAPI Message Store Object
		 * @param array $action the action data, sent by the client
		 * @return boolean true on success or false on failure		 		 
		 */
		function checkNames($store, $action)
		{
			if(isset($action["resolverequests"])) {
				$data = array();
				$excludeLocalContacts = !empty($action["exclude_local_contacts"]) ? $action["exclude_local_contacts"] : false;
				$excludeGABGroups = !empty($action["exclude_gab_groups"]) ? $action["exclude_gab_groups"] : false;

				$resolveRequest = $action["resolverequests"];
				if(!is_array($resolveRequest)) {
					$resolveRequest = array($resolveRequest);
				}

				// open addressbook
				$ab = $GLOBALS["mapisession"]->getAddressbook();
				$ab_entryid = mapi_ab_getdefaultdir($ab);
				$ab_dir = mapi_ab_openentry($ab, $ab_entryid);
				$resolveResponse = Array();

				// check names
				foreach($resolveRequest as $query) {
					if (is_array($query) && isset($query["id"]) && isset($query["resolvename"]) && !empty($query["resolvename"])) {
						$responseEntry = Array();
						$responseEntry['id'] = $query["id"];
						$responseEntry['result'] = Array();

						$resolvename = $query["resolvename"];

						// first resolve in the global addressbook
						$result = $this->searchAddressBook($ab, $ab_dir, $resolvename, $excludeGABGroups);

						// if no matching entry found in addressbook then search in local contact folders
						if(empty($result) && !$excludeLocalContacts) {
							$result = $this->searchContactFolders($resolvename);
						}

						$responseEntry['result'] = $result;
						$resolveResponse[] = $responseEntry;
					}
				}

				$data['resolveresponse'] = $resolveResponse;
				$this->addActionData("checknames", $data);
				$GLOBALS["bus"]->addData($this->getResponseData());
			}
		}

		/**
		 * This function searches the addressbook specified for users and returns an array with data
		 * Please note that the returning array must be UTF8
		 * 
		 * @param {MAPIAddressbook} $ab The addressbook
		 * @param {MAPIAbContainer} $ab_dir The addressbook container
		 * @param {String} $query The search query, case is ignored
		 * @param {Boolean} $excludeGABGroups flag to exclude groups from resolving
		 */
		function searchAddressBook($ab, $ab_dir, $query, $excludeGABGroups)
		{
			try {
				// First, try an addressbook lookup
				$rows = mapi_ab_resolvename($ab, array ( array(PR_DISPLAY_NAME => $query) ) , 0 );
			} catch (MAPIException $e) {
				if ($e->getCode() == MAPI_E_AMBIGUOUS_RECIP) {
					// Ambiguous, show possiblities:
	   				$table = mapi_folder_getcontentstable($ab_dir, MAPI_DEFERRED_ERRORS);

					// only return users from who the displayName or the username starts with $name
					// TODO: use PR_ANR for this restriction instead of PR_DISPLAY_NAME and PR_ACCOUNT
					$resAnd = array(
						array(RES_OR, 
							array(
								array(RES_CONTENT,
									array(
										FUZZYLEVEL => FL_PREFIX|FL_IGNORECASE,
										ULPROPTAG => PR_DISPLAY_NAME,
										VALUE => $query
									)
								),
								array(RES_CONTENT,
									array(
										FUZZYLEVEL => FL_PREFIX|FL_IGNORECASE,
										ULPROPTAG => PR_ACCOUNT,
										VALUE => $query
									)
								),
							), // RES_OR
						)
					);

					// create restrictions based on excludeGABGroups flag
					if($excludeGABGroups) {
						array_push($resAnd, array(
							RES_PROPERTY,
							array(
								RELOP => RELOP_EQ,
								ULPROPTAG => PR_OBJECT_TYPE,
								VALUE => MAPI_MAILUSER
							)
						));
					} else {
						array_push($resAnd, array(RES_OR,
							array(
								array(RES_PROPERTY,
									array(
										RELOP => RELOP_EQ,
										ULPROPTAG => PR_OBJECT_TYPE,
										VALUE => MAPI_MAILUSER
									)
								),
								array(RES_PROPERTY,
									array(
										RELOP => RELOP_EQ,
										ULPROPTAG => PR_OBJECT_TYPE,
										VALUE => MAPI_DISTLIST
									)
								)
							)
						));
					}

					$restriction = array(RES_AND, $resAnd);

					mapi_table_restrict($table, $restriction, TBL_BATCH);
					mapi_table_sort($table, array(PR_DISPLAY_NAME => TABLE_SORT_ASCEND), TBL_BATCH);

					$rows = mapi_table_queryrows($table, array(PR_ACCOUNT, PR_DISPLAY_NAME, PR_ENTRYID, PR_OBJECT_TYPE, PR_SMTP_ADDRESS, PR_DISPLAY_TYPE_EX, PR_EMAIL_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE), 0, 0x7fffffff);
				} else if ($e->getCode() == MAPI_E_NOT_FOUND) {
					$rows = array();
				} else {
					// all other errors should be propagated to higher level exception handlers
					throw $e;
				}
			}

			if (!$rows) {
				$items = array(); // Nothing found
			} else {
				$items = array();
				foreach($rows as $user_data) {
					$item = array();

					if (!isset($user_data[PR_ACCOUNT])) {
						$abitem = mapi_ab_openentry($ab, $user_data[PR_ENTRYID]);
						$user_data = mapi_getprops($abitem, array(PR_ACCOUNT, PR_DISPLAY_NAME, PR_DISPLAY_TYPE_EX, PR_ENTRYID, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE));
					}

					$item["entryid"] = bin2hex($user_data[PR_ENTRYID]);
					$item["user_name"] = $user_data[PR_ACCOUNT];
					$item["display_name"] = $user_data[PR_DISPLAY_NAME];
					$item["display_type"] = $user_data[PR_DISPLAY_TYPE];
					$item["display_type_ex"] = $user_data[PR_DISPLAY_TYPE_EX];
					$item["smtp_address"] = isset($user_data[PR_SMTP_ADDRESS]) ? $user_data[PR_SMTP_ADDRESS] : '';
					$item["email_address"] = isset($user_data[PR_EMAIL_ADDRESS]) ? $user_data[PR_EMAIL_ADDRESS] : '';
					$item["object_type"] = $user_data[PR_OBJECT_TYPE];
					$item["address_type"] = 'ZARAFA';

					array_push($items, $item);
				}
			}
			
			return $items;
		}

		/**
		 * Function will work as a wrapper function for searchContactFolder function.
		 * This function will get list of folders which will be used in the resolving.
		 * 
		 * @param {String} $query The search query, case is ignored
		 */
		function searchContactFolders($query)
		{
			// get all contact folders from own store, exclude subfolders
			$store = $GLOBALS["mapisession"]->getDefaultMessageStore();
			$subtreeEntryid = mapi_getprops($store, array(PR_IPM_SUBTREE_ENTRYID));
			$ipmSubtree = mapi_msgstore_openentry($store, $subtreeEntryid[PR_IPM_SUBTREE_ENTRYID]);
			$hierarchyTable = mapi_folder_gethierarchytable($ipmSubtree, MAPI_DEFERRED_ERRORS);

			$restriction = array(RES_CONTENT,
				array(
					FUZZYLEVEL => FL_FULLSTRING,
					ULPROPTAG => PR_CONTAINER_CLASS,
					VALUE => array(
						PR_CONTAINER_CLASS => "IPF.Contact"
					)
				)
			);

			mapi_table_restrict($hierarchyTable, $restriction, TBL_BATCH);
			$contactFolderEntryIds = mapi_table_queryrows($hierarchyTable, array(PR_ENTRYID), 0, 0x7fffffff);

			$contactFolders = array();
			for($index = 0, $len = count($contactFolderEntryIds); $index < $len; $index++) {
				array_push($contactFolders, mapi_msgstore_openentry($store, $contactFolderEntryIds[$index][PR_ENTRYID]));
			}

			$response = array();
			if(!empty($contactFolders)) {
				for($index = 0, $len = count($contactFolders); $index < $len; $index++) {
					$result = $this->searchContactFolder($contactFolders[$index], $query, $store);
					$response = array_merge($response, $result);
				}
			}

			return $response;
		}

		/**
		 * This function searches the contact folder specified for users and returns an array with data
		 * Please note that the returning array must be UTF8
		 *
		 * @param {MAPIFolder} $folder The opened folder to search, normaly this is a contactsfolder
		 * @param {String} $query The search query, case is ignored
		 * @param {MAPIStore} $store The store in which contact folders are present
		 */
		function searchContactFolder($folder, $query, $store=false)
		{
			$table = mapi_folder_getcontentstable($folder, MAPI_DEFERRED_ERRORS);
			
			$properties = $GLOBALS["properties"]->getContactProperties();

			// only return users from who the displayName or the username starts with $name
			// TODO: use PR_ANR for this restriction instead of PR_DISPLAY_NAME and PR_ACCOUNT
			$restriction = array(RES_AND, 
								array(
									array(RES_OR, 
										array(
											array(
												RES_CONTENT,
													array(FUZZYLEVEL => FL_PREFIX|FL_IGNORECASE,
														ULPROPTAG=>$properties["display_name"],
														VALUE=>$query
													)
												),
											array(
												RES_CONTENT,
													array(FUZZYLEVEL => FL_PREFIX|FL_IGNORECASE,
														ULPROPTAG=>$properties["fileas"],
														VALUE=>$query
													)
												),
											array(
												RES_CONTENT,
													array(FUZZYLEVEL => FL_PREFIX|FL_IGNORECASE,
														ULPROPTAG=>$properties["email_address_display_name_1"],
														VALUE=>$query
													)
												),
											array(
												RES_CONTENT,
													array(FUZZYLEVEL => FL_PREFIX|FL_IGNORECASE,
														ULPROPTAG=>$properties["email_address_display_name_2"],
														VALUE=>$query
													)
												),
											array(
												RES_CONTENT,
													array(FUZZYLEVEL => FL_PREFIX|FL_IGNORECASE,
														ULPROPTAG=>$properties["email_address_display_name_3"],
														VALUE=>$query
													)
												),
										), // RES_OR
									),
									array(RES_OR,
										array(
											array(
												RES_PROPERTY,
													array(RELOP=>RELOP_EQ,
														ULPROPTAG=>$properties["message_class"],
														VALUE=>"IPM.Contact"
													)
												),
											// @TODO we currently don't support sending mails to distlist
											/*array(
												RES_PROPERTY,
													array(
														RELOP=>RELOP_EQ,
														ULPROPTAG=>$properties["message_class"],
														VALUE=>"IPM.DistList"
													)
												)*/
											)
										)
								) // RES_AND
							);
			mapi_table_restrict($table, $restriction, TBL_BATCH);
			mapi_table_sort($table, array($properties["fileas"] => TABLE_SORT_ASCEND), TBL_BATCH);

			// get all matching contacts
			$rows =  mapi_table_queryrows($table, $properties, 0, 0x7fffffff);

			$items = array();
			foreach ($rows as $row) {
				if(stripos($row[$properties["message_class"]], "IPM.DISTLIST") !== false) {
					$item = array();
					$item["user_name"] = $row[$properties["fileas"]];
					$item["display_name"] = $row[$properties["display_name"]];
					$item["message_class"] = $row[$properties["message_class"]];
					$members = $GLOBALS["operations"]->getMembersFromDistributionList($store, $row[$properties["entryid"]], $properties, true);
					$item["members"] = array("member"=>$members);

					array_push($items, $item);
				}else{
					for ($email=1; $email<=3; $email++){
						if (isset($row[$properties["email_address_".$email]]) && !empty($row[$properties["email_address_".$email]])){
							$item = array();

							$item["user_name"] = $row[$properties["fileas"]];
							if (isset($row["email_address_display_name_".$email])){
								$item["display_name"] = $row[$properties["email_address_display_name_".$email]];
							}else{
								$item["display_name"] = $row[$properties["display_name"]];
							}
							$item["smtp_address"] = $row[$properties["email_address_".$email]];
							$item["message_class"] = $row[$properties["message_class"]];
							$item["entryid"] = bin2hex($row[$properties["entryid"]]). "_" .$email;
							$item["parent_entryid"] = bin2hex($row[$properties["parent_entryid"]]);
							$item["store_entryid"] = bin2hex($row[$properties["store_entryid"]]);
							$item["object_type"] = MAPI_MAILUSER;

							array_push($items, $item);
						}
					}
				}
			}
			return $items;
		}
	}
?>
