<?php

	/**
     * MAPI session handling
	 *
	 * This class handles MAPI authentication and stores
	 * 
	 * @package core
	 */
	class MAPISession
	{
		/**
		 * @var resource This holds the MAPI Session
		 */
		var $session;

		/**
		 * @var resource This can hold the addressbook resource
		 */
		var $ab;

		/**
		 * @var array List with all the currently opened stores
		 */
		var $stores;

		/**
		 * @var string The entryid (binary) of the default store
		 */
		var $defaultstore;

		/**
		 * @var array Information about the current session (username/email/password/etc)
		 */
		var $session_info;
		
		/**
		 * @var array Mapping username -> entryid for other stores
		 */
		 
		var $userstores;
		 
		 
		/**
		 * @var int Makes sure retrieveUserData is called only once
		 */
		var $userDataRetrieved;
		
		/**
		 * Default constructor
		 */		 		
		function MAPISession()
		{
			$this->session_info = array("auth"=>false);
			$this->stores = array();
			$this->defaultstore = 0;
			$this->session = false;
			$this->ab = false;
			$this->userstores = array();
			$this->userDataRetrieved = false;
		} 
	
		/**
		 * Logon to Zarafa's MAPI system via php MAPI extension
		 *
		 * Logs on to Zarafa with the specified username and password. If the server is not specified,
		 * it will logon to the local server.
		 *
		 * @param string $username the username of the user
		 * @param string $password the password of the user 
		 * @param string $server the server address
		 * @param string $sslcert_file the optional ssl certificate file
		 * @param string $sslcert_pass the optional ssl certificate password
		 * @result int 0 on no error, otherwise a MAPI error code
		 */		 		
		function logon($username = NULL, $password = NULL, $server = DEFAULT_SERVER, $sslcert_file = NULL, $sslcert_pass = NULL)
		{
			$result = NOERROR;
			$username = (string) $username;
			$password = (string) $password;

			try {
				// logon
				$this->session = mapi_logon_zarafa($username, $password, $server, $sslcert_file, $sslcert_pass);

				if ($this->session !== false){
					$this->session_info["username"] = $username;
					$this->session_info["password"] = $password;
					$this->session_info["server"] = $server;
					
					// we are authenticated
					$this->session_info["auth"] = true;
				}
			} catch (MAPIException $e) {
				$result = $e->getCode();
			}

			return $result;
		}

		/**
		* Get logged-in user information
		*
		* This function populates the 'session_info' property of this class with the following information:
		* - userentryid: the MAPI entryid of the current user
		* - fullname: the fullname of the current user
		* - emailaddress: the email address of the current user
		*
		* The function only populates the information once, subsequent calls will return without error and without
		* doing anything.
		*
		* @return array Array of information about the currently logged-on user
		* @access private
		*/
		function retrieveUserData()
		{
			if($this->userDataRetrieved)
				return;
				
			$result = NOERROR;

			try {
				// get user entryid
				$store_props = mapi_getprops($this->getDefaultMessageStore(), array(PR_USER_ENTRYID));

				// open the user entry
				$user = mapi_ab_openentry($this->getAddressbook(), $store_props[PR_USER_ENTRYID]);

				// receive userdata
				$user_props = mapi_getprops($user, array(PR_DISPLAY_NAME, PR_SMTP_ADDRESS, PR_EMAIL_ADDRESS, PR_SEARCH_KEY));

				if (is_array($user_props) && isset($user_props[PR_DISPLAY_NAME]) && isset($user_props[PR_SMTP_ADDRESS])){
					$this->session_info["userentryid"] = $store_props[PR_USER_ENTRYID];
					$this->session_info["fullname"] = $user_props[PR_DISPLAY_NAME];
					$this->session_info["smtpaddress"] = $user_props[PR_SMTP_ADDRESS];
					$this->session_info["emailaddress"] = $user_props[PR_EMAIL_ADDRESS];
					$this->session_info["searchkey"] = $user_props[PR_SEARCH_KEY];
				}

				$this->userDataRetrieved = true;

			} catch (MAPIException $e) {
				$result = $e->getCode();
			}

			return $result;
		}

		/**
		 * Get MAPI session object
		 *
		 * @return mapisession Current MAPI session
		 */
		function getSession()
		{
			return $this->session;
		}

		/**
		 * Get MAPI addressbook object
		 *
		 * @return mapiaddressbook An addressbook object to be used with mapi_ab_*
		 */
		function getAddressbook()
		{
			$result = NOERROR;

			try {
				if ($this->ab === false){
					$this->ab = mapi_openaddressbook($this->session);
				}
				
				if ($this->ab !== false){
					$result = $this->ab;
				}
			} catch (MAPIException $e) {
				$result = $e->getCode();
			}

			return $result;
		}


		/**
		 * Get logon status
		 *
		 * @return boolean true on logged on, false on not logged on
		 */		 		
		function isLoggedOn()
		{
			return array_key_exists("auth",$this->session_info)?$this->session_info["auth"]:false;
		}
		
		/**
		 * Get current user entryid
		 * @return string Current user's entryid
		 */
		function getUserEntryID()
		{
			$result = $this->retrieveUserData(); 
			
			return array_key_exists("userentryid",$this->session_info)?$this->session_info["userentryid"]:false;
		}

		/**
		 * Get current username
		 * @return string Current user's username (equal to username passed in logon() )
		 */
		function getUserName()
		{
			$result = $this->retrieveUserData(); 
			
			return array_key_exists("username",$this->session_info)?$this->session_info["username"]:false;
		}

		/**
		 * Get current user's full name
		 * @return string User's full name
		 */
		function getFullName()
		{
			$result = $this->retrieveUserData(); 
			
			return array_key_exists("fullname",$this->session_info)?$this->session_info["fullname"]:false;
		}

		/**
		 * Get current user's smtp address
		 * @return string User's smtp address
		 */
		function getSMTPAddress()
		{
			$result = $this->retrieveUserData(); 
			
			return array_key_exists("smtpaddress",$this->session_info)?$this->session_info["smtpaddress"]:false;
		}

		/**
		 * Get current user's email address
		 * @return string User's email address
		 */
		function getEmailAddress()
		{
			$result = $this->retrieveUserData(); 
			
			return array_key_exists("emailaddress",$this->session_info)?$this->session_info["emailaddress"]:false;
		}

		/**
		 * Get current user's search key
		 * @return string Current user's searchkey
		 */
		function getSearchKey()
		{
			$result = $this->retrieveUserData();

			return array_key_exists("searchkey",$this->session_info)?$this->session_info["searchkey"]:false;
		}

		/**
		 * Get the current user's default message store
		 *
		 * The store is opened only once, subsequent calls will return the previous store object
		 * @return mapistore User's default message store object
		 */
		function getDefaultMessageStore()
		{
			// Return cached default store if we have one
			if(isset($this->defaultstore) && isset($this->stores[$this->defaultstore])) {
				return $this->stores[$this->defaultstore];
			}
			
			// Find the default store
			try {
				$storestables = mapi_getmsgstorestable($this->session);
				$entryid = false;

				$rows = mapi_table_queryallrows($storestables, array(PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER));

				foreach($rows as $row) {
					if(isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE] == true) {
						$entryid = $row[PR_ENTRYID];
					}
				}

				if($entryid) {
					$this->defaultstore = $entryid;
					return $this->openMessageStore($entryid);
				}
			} catch (MAPIException $e) {
				return false;
			}

			return false;
		}
	
		/**
		 * Get all message stores currently open in the session
		 *
		 * @return array Associative array with entryid -> mapistore of all open stores (private, public, delegate)
		 */
		function getAllMessageStores()
		{
			try {
				// Loop through all stores
				$storestables = mapi_getmsgstorestable($this->session);
				$entryid = false;

				$rows = mapi_table_queryallrows($storestables, array(PR_ENTRYID));
				foreach($rows as $row) {
					$this->openMessageStore($row[PR_ENTRYID]);
				}

				// The cache now contains all the stores in our profile. Next, add the stores
				// for other users.

				$otherusers = $this->retrieveOtherUsersFromSettings();
				if(is_array($otherusers)) {
					foreach($otherusers as $username=>$folder) {
						if(is_array($folder) && !empty($folder)) {
							$user_entryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username);

							$this->openMessageStore($user_entryid);
							$this->userstores[$username] = $user_entryid;
						}
					}
				}
			} catch (MAPIException $e) {}

			// Just return all the stores in our cache, even if we have some error in mapi
			return $this->stores;
		}

		/**
		 * Open the message store with entryid $entryid
		 *
		 * @return mapistore The opened store on success, false otherwise
		 */
		function openMessageStore($entryid)
		{
			// Check the cache before opening
			foreach($this->stores as $storeEntryId => $storeObj) {
				if($GLOBALS["entryid"]->compareStoreEntryIds(bin2hex($entryid), bin2hex($storeEntryId))) {
					return $storeObj;
				}
			}

			try {
				$store = mapi_openmsgstore($this->session, $entryid);

				// Cache the store for later use
				$this->stores[$entryid] = $store;
			} catch (MAPIException $e) {
				return $e->getCode();
			}

			return $store;
		}
	
		/**
		 * Resolve a username to its entryid
		 *
		 * @return string Entryid of the user on success, false otherwise
		 */
		function resolveUserName($username)
		{
			$request = array(array(PR_DISPLAY_NAME => $username));

			try {
				$ret = mapi_ab_resolvename($this->getAddressbook(), $request);
				$result = $ret[0][PR_ENTRYID];
			} catch (MAPIException $e) {
				return $e->getCode();
			}

			return $result;
		}

		/**
		 * Get other users from settings
		 *
		 * @return array Array of usernames of delegate stores
		 */
		function retrieveOtherUsersFromSettings()
		{
			$result = false;
			$other_users = $GLOBALS["settings"]->get("zarafa/v1/contexts/hierarchy/shared_stores",null);

			if (is_array($other_users)){
				$result = Array();
				// Due to a previous bug you were able to open folders from both user_a and USER_A
				// so we have to filter that here. We do that by making everything lower-case
				foreach($other_users as $username=>$folders) {
					// No folders are being shared, the store has probably been closed by the user,
					// but the username is still lingering in the settings...
					if (!isset($folders) || empty($folders)) {
						continue;
					}

					$username = strtolower($username);
					if(!isset($result[$username])) {
						$result[$username] = Array();
					}

					foreach($folders as $type => $folder) {
						if(is_array($folder)) {
							$result[$username][$folder["folder_type"]] = Array();
							$result[$username][$folder["folder_type"]]["folder_type"] = $folder["folder_type"];
							$result[$username][$folder["folder_type"]]["show_subfolders"] = $folder["show_subfolders"];
						}
					}
				}

				// Delete first so that we do a full replace instead of update
				$GLOBALS["settings"]->delete("zarafa/v1/contexts/hierarchy/shared_stores");
				$GLOBALS["settings"]->set("zarafa/v1/contexts/hierarchy/shared_stores", $result);
			}
			return $result;
		}

		/**
		 * Add the store of another user to the list of other user stores
		 *
		 * @param string $username The username whose store should be added to the list of other users' stores
		 * @return mapistore The store of the user or false on error;
		 */
		function addUserStore($username)
		{
			$user_entryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username);

			if($user_entryid) {
				$this->userstores[$username] = $user_entryid;
			
				return $this->openMessageStore($user_entryid);
			}
		}
		
		/**
		 * Remove the store of another user from the list of other user stores
		 *
		 * @param string $username The username whose store should be deleted from the list of other users' stores
		 * @return string The entryid of the store which was removed
		 */
		function removeUserStore($username)
		{
			// Remove the reference to the store if we had one
			if (isset($this->userstores[$username])){
				$entryid = $this->userstores[$username];
				unset($this->userstores[$username]);
				unset($this->stores[$entryid]);
				return $entryid;
			}
		}	

		/**
		 * Get the store entryid of the specified user
		 *
		 * The store must have been previously added via addUserStores.
		 *
		 * @param string $username The username whose store is being looked up
		 * @return string The entryid of the store of the user
		 */
		function getStoreEntryIdOfUser($username)
		{
			return $this->userstores[$username];
		}

		/**
		 * Get the username of the owner of the specified store
		 *
		 * The store must have been previously added via addUserStores.
		 *
		 * @param string $entryid EntryID of the store
		 * @return string Username of the specified store or false if it is not found
		 */
		function getUserNameOfStore($entryid)
		{
			foreach($this->userstores as $username => $storeentryid) {
				if($GLOBALS["entryid"]->compareStoreEntryIds(bin2hex($storeentryid), bin2hex($entryid)))
					return $username;
			}
			
			return false;
		}

		/**
		 * Open a MAPI message using session object.
		 * The function is used to open message when we dont' know
		 * the specific store and we want to open message using entryid.
		 *
		 * @param string $entryid entryid of the message
		 * @return object MAPI Message
		 */
		function openMessage($entryid)
		{
			return mapi_openentry($this->session, $entryid); 
		}
	}
?>
