<?php
/***********************************************
* File      :   atmailabook.php
* Descr     :   SQL push library for contacts
*
* Original by Zarafa Deutschland GmbH released under GPLv2.
* Modifications by Atmail published under GPLv2
* Consult LICENSE file for details
************************************************/

require_once( dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'bootloader.php');
//require_once( dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR. '..' . DIRECTORY_SEPARATOR . 'application' . DIRECTORY_SEPARATOR . 'models' . DIRECTORY_SEPARATOR . 'users.php');
require_once('diffbackend.php');
Zend_Registry::set('InDbAsArray', false); //true = Array, false = Serialized
Zend_Registry::set('totalCycleTime', 0);
Zend_Registry::set('totalCyclesNumber', 1);

class BackendAtmailAbook extends BackendDiff 
{

	var $_user;
	var $_devid;
	var $_protocolversion;

	public function Logon($user, $domain, $password)
	{

		//debugLog('Logon(' . $user . ', ' . $domain . ')');
		
		//local user must exist in the db to have an abook
		$authSuccess = users::auth( $user, $password );
		if( $authSuccess == true )
		{
			$this->_Account = $user;
			Zend_Registry::set('Account', $user);			
			return true;
			
		}
		else
		{
			
			if( strpos($user, '@') === false )
			{
				
				$authSuccess = users::auth( $user.'@'.$domain, $password );
				if( $authSuccess )
				{
					
					$this->_Account = $user.'@'.$domain;
					Zend_Registry::set('Account', $this->_Account);
					return true;
					
				}
				
			}
			return false;
			
		}

	}
	
	function Setup($user, $devid, $protocolversion)
	{
		$this->_user = $user;
		$this->_devid = $devid;
		$this->_protocolversion = $protocolversion;
		
		$this->_contacts = new contacts( array('Account' => $this->_Account) );
		//debugLog('AtmailAbook::Setup as ' . $user . ', ' . $devid . ', ' . $protocolversion);
		return true;
	}

	function GetFolderList() 
	{
		
		//debugLog('AtmailAbook::GetFolderList()');
		$contacts = array();
		$folder = $this->StatFolder("root");
		$contacts[] = $folder;
        return $contacts;

	}

	function StatFolder($id)
	{

		//debugLog('AtmailAbook::StatFolder('.$id.')');
		$folder = $this->GetFolder($id);
		$stat = array();
		$stat["id"] = $id;
		$stat["parent"] = $folder->parentid;
		$stat["mod"] = $folder->displayname;
        return $stat;

	}

	function GetFolder($id)
	{
		
		//debugLog('AtmailAbook::GetFolder('.$id.')');
		if($id == "root")
		{
		
			$folder = new SyncFolder();
			$folder->serverid = $id;
			$folder->parentid = "0";
			$folder->displayname = "Contacts";
			$folder->type = SYNC_FOLDER_TYPE_CONTACT;
			return $folder;
		
		}
		else return false;
		
	}

	function GetMessageList($folderid, $cutoffdate) 
	{
		$contacts = $this->_contacts->getContacts();
		$messages = array();

		foreach ($contacts as $contact)
		{
			$message = array();
			$message["id"] = $contact['id'];
			$message["mod"] = $this->convert_from_outlook_date($contact['DateModified']);
			$message["flags"] = 1;
			$messages[] = $message;  
		}
		return $messages;
	}

	function StatMessage($folderid, $id) 
	{
	
		//debugLog('AtmailAbook::StatMessage('.$folderid.', '.$id.')');
		if($folderid != "root")
			return false;
		
		// Optimize, we just want the date modified, nothing else
		$contact = $this->_contacts->getContact( $id );
		
		if( $contact )
		{
		
			$message = array();
			$message["id"] = $id;
			$message["mod"] = $this->convert_from_outlook_date($contact['DateModified']);
			$message["flags"] = 1;		    
			return $message;
		
		}
		//debugLog( 'message not found with id: ' . $id );
		return false;
		
	}

	function GetMessage($folderid, $id, $truncsize, $mimesupport = 0) 
	{

        global $protocolversion;

		if($folderid != "root")
			return;

		// Parse the vcard
		$message = new SyncContact();
		
		$var = $this->_contacts->getContact( $id );
		
		// Get email objects from Atmail
		if( !empty($var['UserEmail']) )
			$message->email1address = $var['UserEmail'];
		
		if( !empty($var['UserEmail2']) )
			$message->email2address = $var['UserEmail2'];
		
		if( !empty($var['UserEmail3']) )
			$message->email3address = $var['UserEmail3'];
		
		// Get the phone records
		// Missing support in Atmail for fields
		// $message->carphonenumber = $tel['val'][0];
		// $message->radiophonenumber  ( Pager )
		
		if( !empty($var['UserHomePhone']))
			$message->homephonenumber = $var['UserHomePhone'];
		
		if( !empty($var['UserHomeMobile']))
			$message->home2phonenumber = $var['UserHomeMobile'];
		
		if( !empty($var['UserHomeFax']))
			$message->homefaxnumber = $var['UserHomeFax'];
		
		if( !empty($var['UserWorkFax']))
			$message->businessfaxnumber = $var['UserWorkFax'];
		
		if( !empty($var['UserWorkPhone']))
			$message->businessphonenumber = $var['UserWorkPhone'];
		
		if( !empty($var['UserWorkMobile']))
			$message->business2phonenumber = $var['UserWorkMobile'];
		
		if( !empty($var['UserWorkMobile']))
			$message->mobilephonenumber = $var['UserWorkMobile'];
		
		else if( !empty($var['UserHomeMobile']))
			$message->mobilephonenumber = $var['UserHomeMobile'];
		
		// User address details
		// Missing support in Atmail for fields ( other address )
		if( !empty($var['UserHomeAddress']))
			$message->homestreet = $var['UserHomeAddress'];
		
		if( !empty($var['UserHomeCity']))
			$message->homecity = $var['UserHomeCity'];

		if( !empty($var['UserHomeState']))
			$message->homestate = $var['UserHomeState'];

		if( !empty($var['UserHomeZip']))
			$message->homepostalcode = $var['UserHomeZip'];
		
		if( !empty($var['UserHomeCountry']))
			$message->homecountry = $var['UserHomeCountry'];

		if( !empty($var['UserWorkAddress']))
			$message->businessstreet = $var['UserWorkAddress'];
		
		if( !empty($var['UserWorkCity']))
			$message->businesscity = $var['UserWorkCity'];
		
		if( !empty($var['UserWorkState']))
			$message->businessstate = $var['UserWorkState'];
		
		if( !empty($var['UserWorkZip']))
			$message->businesspostalcode = $var['UserWorkZip'];
		
		if( !empty($var['UserWorkCountry']))
			$message->businesscountry = $var['UserWorkCountry'];
		
		
		// Users name and details
		if( !empty($var['UserFirstName']))
			$message->firstname = $var['UserFirstName'];
		
		if( !empty($var['UserMiddleName']))
			$message->middlename = $var['UserMiddleName'];
		
		if( !empty($var['UserLastName']))
			$message->lastname = $var['UserLastName'];
		
		if( !empty($var['UserTitle']))
			$message->title = $var['UserTitle'];
		//$message->suffix = $var['UserGender']; // Check suffix return requirements

		// Check timezone DOB conversions
		if( !empty($var['UserDOB'])) 
		{
		
			$tz = date_default_timezone_get();
			date_default_timezone_set('UTC');
			$message->birthday = strtotime($var['UserDOB']);
			date_default_timezone_set($tz);			
		
		}
		
		// Misc fields
		if( !empty($var['UserWorkCompany']))
			$message->companyname = $var['UserWorkCompany'];
		
		if( !empty($var['UserInfo']))	
		{

            if($protocolversion >= 12)
            {
                $body = $var['UserInfo'];
                $syncHtmlBody = new SyncHtmlBody();
                $syncHtmlBody->bodydata = $body;
                $syncHtmlBody->bodyestimateddatasize = strlen($body);
                $syncHtmlBody->bodytype = SYNC_AIRSYNCBASE_BODYPREFERENCE_TYPE_PLAIN;
                $syncHtmlBody->bodytruncated = 0;
                $message->body = $syncHtmlBody;
            }
            else
            {
    			$message->body = $var['UserInfo'];
    			$message->bodysize = strlen($var['UserInfo']);
    			$message->bodytruncated = 0;
            }
		
		}
		
		if( !empty($var['UserWorkTitle']))
			$message->jobtitle = $var['UserWorkTitle'];
		
		if( !empty($var['UserURL']))
			$message->webpage = $var['UserURL'];
		
		if( !empty($var['UserPhoto']))
			$message->picture = $var['UserPhoto'];
		
		// Build the UserFileAs ( Used in Windows mobile for displaying the users name/formatting )
		if( !empty($var['UserFirstName']) || !empty($var['UserLastName']) || !empty($var['UserMiddleName']))	
		{
		
			if( !empty($var['UserFirstName']) && !empty($var['UserLastName']) && !empty($var['UserMiddleName']) )
				$message->fileas = $var['UserLastName'] . ', ' . $var['UserFirstName'] . ' ' . $var['UserMiddleName'];
			else if( !empty($var['UserFirstName']) && !empty($var['UserLastName']) )
				$message->fileas = $var['UserLastName'] . ', ' . $var['UserFirstName'];
			else if( !empty($var['UserFirstName']) )
				$message->fileas = $var['UserFirstName'];
			else if( !empty($var['UserLastName']) )
				$message->fileas = $var['UserLastName'];
			else if( !empty($var['UserMiddleName']) )
				$message->fileas = $var['UserMiddleName'];

		} 
		else
		$message->fileas = "";
		return $message;
		
	}

	function ChangeMessage($folderid, $id, $message) 
	{
		
		//debugLog($folderid . ' ' . $id );

		// Missing support for
		// otherstreet;othercity;otherstate;otherpostalcode;othercountry
		// carphonenumber, pager, fileas
		
		$mapping = array(
			'fileas'		=> 'UserFileAs',
			'email1address' => 'UserEmail',
			'email2address' => 'UserEmail2',
			'email3address' => 'UserEmail3',
			'firstname' => 'UserFirstName',
			'middlename' => 'UserMiddleName',
			'lastname' => 'UserLastName',
			'title' => 'UserTitle',
			'suffix' => 'UserGender',
			'businessphonenumber' => 'UserWorkPhone',
			'business2phonenumber' => 'UserWorkMobile',
			'businessfaxnumber' => 'UserWorkFax',
			'homephonenumber' => 'UserHomePhone',
			'home2phonenumber' => 'UserHomeMobile',
			'homefaxnumber' => 'UserHomeFax',
			'mobilephonenumber' => 'UserHomeMobile',
			'carphonenumber' => 'UserWorkMobile',
			'businessstreet' => 'UserWorkAddress',
			'businesscity' => 'UserWorkCity',
			'businessstate' => 'UserWorkState',
			'businesspostalcode' => 'UserWorkZip',
			'businesscountry' => 'UserWorkCountry',
			'homestreet' => 'UserHomeAddress',
			'homecity' => 'UserHomeCity',
			'homestate' => 'UserHomeState',
			'homepostalcode' => 'UserHomeZip',
			'homecountry' => 'UserHomeCountry',
			'companyname' => 'UserWorkCompany',
			'body' => 'UserInfo',
			'jobtitle' => 'UserWorkTitle',
			'webpage' => 'UserURL'
		);
		
		$db = array();
		
		$db['id'] = $id;
		
		foreach($mapping as $k => $v)
		{
			
			if( !empty($message->$k))
            {
                if(is_object($message->$k) && get_class($message->$k) == "SyncHtmlBody")
                {
                    $message->$k = $message->$k->bodydata;
                }
				$db["$v"] = $message->$k;
            }
		}
		
		// Misc fields that require more processing
		if( !empty($message->picture))
			$db['UserPhoto'] = $message->picture;
		if(isset($message->birthday))
			$db['UserDOB'] = date('Y-m-d', $message->birthday);
			
		// If a new record, insert, and return the new ID
		if(!$id)
		{
			
			$id = $this->_contacts->addContact($db);
			//debugLog('Post DB');
			//debugLog(' just created new record in db then will next stat message: ' . $id );
			
		}
		else
		{
			
			$this->_contacts->updateContact($db);      
			//debugLog('Post DB');
			//debugLog(' just updated record in db then will next stat message: ' . $id );
			
		}
		$stat = $this->StatMessage($folderid, $id);
		return $stat;
		
	}

	function DeleteMessage($folderid, $id) 
	{

		//debugLog('Delete message ' . $folderid . ' ' . $id);
		
		try
		{
			$this->_contacts->deleteContact( array('id' => $id) );		
		}
		catch( Exception $e )
		{
			
			//contact may not exist on server any more so ignore delete failure
			
		}
		return true;
		//return unlink($this->getPath() . '/' . $id);
	}
	
	function getSearchResults($searchquery, $searchrange = '0')
	{
		$rangestart = 0;
		$rangeend = 50;
		
		if ( $searchrange != '0' ) 
		{
		
			$pos = strpos($searchrange, '-');
			$rangestart = substr($searchrange, 0, $pos);
			$rangeend = substr($searchrange, ($pos + 1));
		
		}
		$searchFields = array(
			'UserEmail' => $searchquery,
			'UserEmail2' => $searchquery,
			'UserEmail3' => $searchquery,
			'UserEmail4' => $searchquery,
			'UserEmail5' => $searchquery,
			'UserFirstName' => $searchquery,
			'UserMiddleName' => $searchquery,
			'UserLastName' => $searchquery,
			'UserTitle' => $searchquery,
			'UserHomeAddress' => $searchquery,
			'UserHomeCity' => $searchquery,
			'UserHomeState' => $searchquery,
			'UserHomeZip' => $searchquery,
			'UserHomeCountry' => $searchquery,
			'UserHomePhone' => $searchquery,
			'UserHomeMobile' => $searchquery,
			'UserWorkCompany' => $searchquery,
			'UserWorkAddress' => $searchquery,
			'UserWorkCity' => $searchquery,
			'UserWorkState' => $searchquery,
			'UserWorkZip' => $searchquery,
			'UserWorkCountry' => $searchquery,
			'UserWorkPhone' => $searchquery,
			'UserWorkMobile' => $searchquery
		);
		
		$matchIds = $this->_contacts->searchFuzzyContact($searchFields, GROUP_GLOBAL);
		$contacts = array();
		foreach( $matchIds as $matchId )
		{
			
			$contacts[] = $this->_contacts->getContact($matchId['id'], GROUP_GLOBAL);
			
		}
		$contactsC = count($contacts);
		
		//do not return more results as requested in range
		$querylimit = (($rangeend + 1) < $contactsC) ? ($rangeend + 1) : $contactsC+1;

		$items = array();
		
		if ( $searchrange != '0' )
			$items['range'] = $rangestart.'-'.($querylimit - 1);
		//$items['searchtotal'] = $contactsC;
		
		$itemsRowNumber = 0;
		$lowestContactRowToHit = $rangestart;
		$highestContactRowToHit = $rangeend>$contactsC?$contactsC:$rangeend;
		for ($contactsRowNumber = $rangestart; $contactsRowNumber < $highestContactRowToHit; $contactsRowNumber++) 
		{
			$contact = $contacts[$contactsRowNumber];
			//$item = array();
			//$item["id"] = $contact['id'];
			$items[$itemsRowNumber] = array();
			
			$displayName = (!empty($contact['UserFirstName'])?$contacts[$contactsRowNumber]['UserFirstName']:"") . (!empty($contacts[$contactsRowNumber]['UserFirstName'])&&!empty($contacts[$contactsRowNumber]['UserLastName'])?" ":"") . (!empty($contacts[$contactsRowNumber]['UserLastName'])?$contacts[$contactsRowNumber]['UserLastName']:"");
			$items[$itemsRowNumber][SYNC_GAL_DISPLAYNAME] = $displayName;
			
			
			$items[$itemsRowNumber][SYNC_GAL_ALIAS] = $contact['UserEmail'];
			//it's not possible not get first and last name of an user
			//from the gab and user functions, so we just set lastname
			//to displayname and leave firstname unset
			//this was changed in Zarafa 6.40, so we try to get first and
			//last name and fall back to the old behaviour if these values are not set
			if ( isset($contact['UserFirstName']))
			    $items[$itemsRowNumber][SYNC_GAL_FIRSTNAME] = $contact['UserFirstName'];

			if ( isset($contact['UserLastName']))
			    $items[$itemsRowNumber][SYNC_GAL_LASTNAME] = $contact['UserLastName'];
//			if (!isset($items[$itemsRowNumber][SYNC_GAL_LASTNAME])) 
//				$items[$itemsRowNumber][SYNC_GAL_LASTNAME] = $items[$itemsRowNumber][SYNC_GAL_DISPLAYNAME];

			$items[$itemsRowNumber][SYNC_GAL_EMAILADDRESS] =
				!empty($contact['UserEmail'])?$contact['UserEmail']:
				(!empty($contact['UserEmail2'])?$contact['UserEmail2']:
				(!empty($contact['UserEmail3'])?$contact['UserEmail3']:
				(!empty($contact['UserEmail4'])?$contact['UserEmail4']:
				(!empty($contact['UserEmail5'])?$contact['UserEmail5']:''))));

            if ( isset($contact['UserWorkPhone']))
			    $items[$itemsRowNumber][SYNC_GAL_PHONE] = $contact['UserWorkPhone'];

			if ( isset($contact['UserWorkMobile']))
			    $items[$itemsRowNumber][SYNC_GAL_MOBILEPHONE] = $contact['UserWorkMobile'];

			if ( isset($contact['UserHomePhone']))
			    $items[$itemsRowNumber][SYNC_GAL_HOMEPHONE] = $contact['UserHomePhone'];
			
			if ( isset($contact['UserWorkCompany']))
			    $items[$itemsRowNumber][SYNC_GAL_COMPANY] = $contact['UserWorkCompany'];
			
			if ( isset($contact['UserTitle']))
			    $items[$itemsRowNumber][SYNC_GAL_TITLE] = $contact['UserTitle'];
			
			//if ( isset($contacts[$contactsRowNumber][PR_OFFICE_LOCATION]))
			//    $items[$itemsRowNumber][SYNC_GAL_OFFICE] = $contacts[$contactsRowNumber][PR_OFFICE_LOCATION];
			
			//if display name is still empty, then replace with email address
			if (strlen(trim($items[$itemsRowNumber][SYNC_GAL_DISPLAYNAME])) == 0)
			    $items[$itemsRowNumber][SYNC_GAL_DISPLAYNAME] = $items[$itemsRowNumber][SYNC_GAL_EMAILADDRESS];

			//not supported
			//if( !empty($contacts[$contactsRowNumber]['UserPhoto']) )
			//	$items[$itemsRowNumber][SYNC_GAL_PICTURE] = $contacts[$contactsRowNumber]['UserPhoto'];

			$itemsRowNumber++;
		}
		return $items;

	}

	function SendMail($rfc822, $forward = false, $reply = false, $parent = false)
	{
		return false;
	}

	function GetWasteBasket()
	{
		return false;
	}

	function GetAttachmentData($attname)
	{		
		return false;
	}

	function SetReadFlag($folderid, $id, $flags)
	{
		return false;
	}

	function MoveMessage($folderid, $id, $newfolderid)
	{
		return false;
	}

	// -----------------------------------

	function getPath()
	{
		return str_replace('%u', $this->_user, VCARDDIR_DIR);
	}

	function escape($data)
	{
	
		if (is_array($data))
		{
		
			foreach ($data as $key => $val)
				$data[$key] = $this->escape($val);
			return $data;
			
		}
		$data = str_replace("\r\n", "\n", $data);
		$data = str_replace("\r", "\n", $data);
		$data = str_replace(array('\\', ';', ',', "\n"), array('\\\\', '\\;', '\\,', '\\n'), $data);
		return u2wi($data);
		
	}

	function unescape($data)
	{
	
		return str_replace(array('\\\\', '\\;', '\\,', '\\n','\\N'),array('\\', ';', ',', "\n", "\n"),$data);
	
	}
	
	function convert_from_outlook_date($date)
	{

		$seconds = time(); // Current time in seconds

		$datecalc = $date; // This has the date
		$seconds = '';
		if (preg_match('/\.(.*)/', $date, $match))
			$seconds = $match[1];

		$datecalc = preg_replace('/\.(.*)/', '', $datecalc);

		// Number of days
		$datecalc = $datecalc - "25569";

		// Convert to seconds
		$datecalc = round($datecalc * 60 * 60 * 24);

		// Int rounds it up - 86400 seconds in 1 day 0.[secsyougiveme]
		$newsecs = round("0.$seconds"  * 86400);

		$datecalc = $datecalc + $newsecs;

		return $datecalc;

	}
}