<?php
/**
 * @category 	Atmail
 * @package 	Models
 * @subpackage	Calendar
 * @copyright 	Copyright (c) 2009-2011 ATMAIL. All rights reserved
 * @license 	See http://atmail.com/license.php for license agreement
 * @mutilator	Evert Pot evert@rooftopsolutions.nl
 * @author		Atmail (http://atmail.com)
 */

require_once 'Sabre/autoload.php';
require_once 'Atmail/CalDav/json.php';
require_once 'Atmail/CalDav/caldav-support.php';
require_once 'application/models/sync.php';

/**
 * AtMail Calendar Model 
 *
 * This model is responsible for all the Calendaring interaction. 
 *
 */
class calendar
{

    /**
     * Username/email address
     *
     * @var string
     */
    private $_account;

    /**
     * Connection to the external calendar server
     *
     * @var Atmail_Calendar_Adapter_Abstract
     */
    private $_calendarAdapter;

    /**
     * The server type
     *
     * @var string
     */
    private $_server_type        = 'at';

    /**
     * Wether or not authentication was succesful
     *
     * @var bool
     */
    private $_server_auth_failed    = false;

    /**
     * Wether or not we're using the atmail built-in server
     */
    private $_local = false;

    /**
     * Used for time conversion  
     * 
     * @var int 
     */
    private $_outlook_time1;

    /**
     * Used for time conversion 
     * 
     * @var int 
     */
    private $_outlook_time2;

    /**
     * Some constants used for generating iCalendar objects
     */
    private $ICAL_TIME        = 1;
    private $ICAL_VERSION         = "2.0";
    
    private $user_timezone	= 'GMT';

    const ICAL_PRODID = "-//Atmail//6.0//EN";

    /**
     * Constructor
     *
     * The following arguments are supported:
     *   * Account:  a username / email address (required)
     *   * Type:     a server type (optional)
     *   * URL:      Base url to the CalDAV server
     *   * Password: The users' (caldav) password (required)
     *
     */  
    public function __construct(array $args)
    {
                
        // check to make sure we have valid arguments!
        if( !isset($args['Account']) )
        {
            throw new Atmail_Exception('Calendar constructor required Account field in arguments array');
        }
        if( !isset($args['Password']) )
        {
            throw new Atmail_Exception('Calendar constructor required Password field in arguments array');
        }
        if( !isset($args['Timezone']) )
        {
        	$args['Timezone'] = 'UTC';
        }

        $this->_calendarAdapter = Atmail_Calendar_Factory::instance(array(
            'url' => 'http://localhost:80/mail/dav/server.php/',
            'userName' => $args['Account'],
            'password' => $args['Password'],
        ));
		$this->_account = $args['Account'];
		$this->_password = $args['Password'];
		
		$this->user_timezone = $args['Timezone'];
    }

    /**
     * Returns the current calendar adapter
     * 
     * @return Atmail_Calendar_Adapter_Abstract 
     */
    public function getAdapter() {

        return $this->_calendarAdapter;

    }
    
    /**
     * Restarts the calendar model with new creds
     * 
     * @return null
     */
    public function restartAdapter($args = array())
    {
		
		if( !array_key_exists('Account', $args) )
			$args['Account'] = $this->_account;
		if( !array_key_exists('Password', $args) )
			$args['Password'] = $this->_password;
		
    	unset($this->_calendarAdapter);
       	$this->_calendarAdapter = Atmail_Calendar_Factory::instance(array(
            'url' => 'http://localhost:80/mail/dav/server.php/',
            'userName' => $args['Account'],
            'password' => $args['Password'],
        ));
		$this->_account = $args['Account'];
		$this->_password = $args['Password'];
    }

	public function uidInMyCalendars( $uid )
	{
		
		//get list of my calendars
		//for each one look for matching uid
		$calendars = $this->_calendarAdapter->getCalendars();
		$this->restartAdapter(); //currently getCalendars calls get_prob which mangles the calendarArapter instance config, requiring restart as a workaround
		
		foreach( $calendars as $calendar )
		{

			try 
			{
				
				//suppress throwing exception while searching for possibly non-existent entry (not found causes exception)
				$this->foundInEvent = $this->_calendarAdapter->getEventByUid($calendar, $uid);
				$this->foundInCalendar = $calendar;
				return true;
				
			}
			catch( Exception $e ) { }

		}
		return false;
		
	}

	/**
	 * Returns an ICS string of the calander event object
	 */
	public function getEvent($uid, $calendarUrl)
	{
		
		$calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
		//clear caching flag to always force fresh fetch
		unset($calendar->ctag);
		return $this->_calendarAdapter->getEventByUid($calendar, $uid);
		
	}

	/**
	* sets attendee fields and returns true on success or false
	* 
	* @param string $uid 
	* @param string $calendarUrl
	* @param array $attendees
	* @return boolean
	*/

	public function setAttendees($uid, $calendarUrl, $attendees)
	{

		$calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
		$event = $this->_calendarAdapter->getEventByUid($calendar, $uid);

		$vCalendar = $event->getVObject();

		/*
		[ATTENDEE] => MAILTO:test@centos.virgin
		[ATTENDEE2] => MAILTO:testagain@centos.virgi
		
		or
		
		ATTENDEE;CN="2@1.com";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:MAILTO:2@1.com
		ATTENDEE;CN="1@1.com";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:
		 MAILTO:1@1.com
		*/
		$attendeesArray = array();
		for( $a = 1 ; $a < 1000 ; $a++ )
		{
			
			if( $a === 1 )
				$a = '';
			unset($vCalendar->vevent->{"attendee" . $a});
			
		}
		unset($vCalendar->vevent->attendee);
		foreach( $attendees as $k => $attendee )
		{
			
			$element = new Sabre_VObject_Property('attendee', "MAILTO:" . $attendee);
			$element->add("CN", $attendee);
			$element->add("CUTYPE", "INDIVIDUAL");
			$element->add("PARTSTAT", "NEEDS-ACTION");
			$element->add("RSVP", "TRUE");
			$vCalendar->vevent->add($element);

			$myEmailAddress = $this->getAccountEmailAddress();
			if( array_key_exists('RealName', $this->UserSettings) && $this->UserSettings['RealName'] != null && $this->UserSettings['RealName'] != '' )
				$myRealName = $this->UserSettings['RealName'];
			else
				$myRealName = $myEmailAddress;
			$element = new Sabre_VObject_Property('organizer', "MAILTO:" . $myEmailAddress);
			$element->add("CN", $myRealName);
			$vCalendar->vEvent->add($element);			
		}
		
		$event->setVObject($vCalendar);
		$this->_calendarAdapter->updateEvent($calendar, $event);
		//remove the cache flag to force fetching freshly updated event
		unset($calendar->ctag);
		return true;
		
	}

	/**
	* tries to find attendee fields and returns them or null
	* 
	* @param string $uid 
	* @param string $calendarUrl
	* @return array of attendees or null
	*/

	public function getAttendees($uid, $calendarUrl)
	{

		$calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
		//clear caching flag to always force fresh fetch
		unset($calendar->ctag);
		$event = $this->_calendarAdapter->getEventByUid($calendar, $uid);

		$vCalendar = $event->getVObject();
		
		if( !isset($vCalendar->vevent->attendee) )
			return null;

		$attendeesArray = array();
		foreach($vCalendar->vevent->attendee as $attendee)
		{
			if(is_object($attendee)) $attendee = $attendee->value;
			if( strpos(strtoupper($attendee), "MAILTO:") === 0 )
				$attendee = substr($attendee, 7);
			$attendeesArray[] = (string)$attendee;
	
		}
		return $attendeesArray;

	}
    
    /**
     * Returns a list of events that have alarms within the specified date 
     * range.
     * 
     * The start and end arguments must be in a format that strtotime can 
     * understand.
     *
     * @param string $start_in 
     * @param string $end_in 
     * @return array 
     */
    public function checkAlarms($start_in, $end_in)
    {
        $entries = array();
        
        // Converting to unix timestamps
        $start_in = strtotime($start_in);
        $end_in = strtotime($end_in);

        if ($start_in=='' && $end_in=='') {
            throw new InvalidArgumentException('This path in the code is no longer supported. Put this exception here to ensure this is not actually used anymore.');
        }

        // We need a few more formats in this function
        $caldav_start = date('Ymd\\THis\\Z', $start_in);
        $caldav_end = date('Ymd\\THis\\Z', $end_in);
        $searchstart = new Date($caldav_start);
        $searchend = new Date($caldav_end);
    
        $calendars = $this->_calendarAdapter->getCalendars();

        $alarms = array();
        $munchedEvents = array();


        foreach( $calendars as $calendar )
        {
			if($calendar->owner != $this->_account)
			{
				// coming from someone else
				// check thats its not read-only (can't dismiss)
				if($calendar->readOnly) continue;
			}
            // fetch all events for range
            //$events = $this->_caldav_server->GetAlarms($caldav_start,$caldav_end, $calName['relative_href']);
            $events = $this->_calendarAdapter->getAlarmsByDateRange($calendar, $start_in, $end_in);
                        
            // 'munch' the events into a nice array
            $munchedEvents = $this->munchEvents($events);
            foreach($munchedEvents as $event)
            {
                $alarm = caldav_extract_alarm($event, $calendar->url);
                if(count($alarm) != 0)
                    $alarms[] = $alarm;
            }
        }
        
        return $alarms;
    }
    
    /**
     * Removes all alarms from an event 
     * 
     * @param string $uid 
     * @param string $calendarUrl
     * @return null 
     */
    public function dismissAlarm($uid, $calendarUrl)
    {
        $calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
        $event = $this->_calendarAdapter->getEventByUid($calendar, $uid);

        $vCalendar = $event->getVObject();
        unset($vCalendar->vevent->valarm);

        $event->setVObject($vCalendar);

        $this->_calendarAdapter->updateEvent($calendar, $event);

    }
    
    /**
     * Used to create a new alarm on an event
     * 
     * @param mixed $alertTrigger 
     * @param mixed $alertType 
     * @param mixed $alertPosition 
     * @param mixed $alertDescription 
     * @return Sabre_VObject_Component 
     */
    private function caldav_set_alarm_trigger($alertTrigger, $alertType, $alertPosition, $alertDescription)
    {
        $alarm = array();
        $alertDescription = str_replace("\r", "", $alertDescription);
        $alertDescription = str_replace("\n", " ", $alertDescription);
        if($alertType == "Date")
        {
            $newtrigger = new Date($alertTrigger);
            $trigger = ical_datetime(date($newtrigger->getTime()),'zulu') . 'Z';
        }
        else
        {
            if($alertPosition == "Before")
            {
                $alertTrigger *= -1; 
            }    
            if($alertType == "Minutes")
            {
                $newtrigger = ($alertTrigger * 60);                
            }
            else if ($alertType == "Hours")
            {
                $newtrigger = ($alertTrigger * 60 * 60);
            }
            else if ($alertType == "Days")
            {
                $newtrigger = ($alertTrigger * 60 * 60 * 24);
            }
            else
            {
                $newtrigger = 0;
            }
            $trigger = seconds_to_ical_duration($newtrigger);    
        }

        $vAlarm = new Sabre_VObject_Component('VALARM');
        $vAlarm->trigger = $trigger;
        $vAlarm->description = 'Alarm for: "' . $alertDescription . '"';
        $vAlarm->action = 'DISPLAY'; 

        return $vAlarm;
    }

    /**
     * Snoozes an alarm for x minutes
     * 
     * @param string $uid 
     * @param string $calendarUrl
     * @param int $mins Minutes 
     * @return void 
     */
    public function snoozeAlarm($uid, $calendarUrl, $mins)
    {

        $calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
        $event = $this->_calendarAdapter->getEventByUid($calendar, $uid);

        $vCalendar = $event->getVObject();

        if (!isset($vCalendar->vevent->valarm))
            return;

        $vCalendar->vevent->valarm[0]->trigger = gmdate('Ymd\\THis\\Z', time()+$mins*60);
        
        $event->setVObject($vCalendar);

        $this->_calendarAdapter->updateEvent($calendar, $event);
        
        return null;
    }

    /**
     * Converts from an outlook date to a yyyy:mm:dd hh:mm:ss date string. 
     * 
     * @param string $date 
     * @return string 
     */
    public 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);
    
        // Calculate the number of days since 1899; it differs
        // if the time is greater/lesser then GMT
        if (!$this->_outlook_time1 || !$this->outlook_time2)
        {
            // Get our local time in Epoc
            $this->_outlook_time1 = date('U', time() + intval(date('Z')));
    
            // Get our GMT time
            $this->_outlook_time2 = date('U', gmmktime());
    
        }
        $time3 = $this->_outlook_time1 - $this->_outlook_time2;
    
        $now = gettimeofday();
        $diff = 0;    
    
        if(intval(date('Z')) < 0)
        {
            $diff = 1;
        }
    
        $datecalc = $datecalc - "25569" + $diff;
        $datecalc = round($datecalc * 60 * 60 * 24);
        $newdate = strftime("%Y-%m-%d", $datecalc );
    
        // Int rounds it up - 86400 seconds in 1 day 0.[secsyougiveme]
        $newsecs = round("0.$seconds"  * 86400);
    
        $time = gmstrftime("%T", $newsecs );
    
        if (preg_match('/:59$/', $time))
            $time = strftime("%T", $newsecs + 1);
        elseif (preg_match('/:01$/', $time))
            $time = strftime("%T", $newsecs - 1);
    
        $newdate = "$newdate $time";
    
        return $newdate;
    }
    
    /**
     * Converts from an yyyy:mm:dd hh:mm:ss date string to an outlook date.
     * 
     * @param string $date 
     * @return string 
     */
    public function convert_to_outlook_date($date)
    {
        $seconds = time();
    
        $datecalc = $date;
    
        // Regular expression out the time, just get the secs for that specified day
        $datecalc = preg_replace('/\s(\d+:\d+:\d+)/', ' 00:00:00', $datecalc);

        $seconds = strtotime($datecalc);
        $days = round($seconds / 60 / 60 / 24);
    
        // Calculate the number of days since 1899; it differs
        // if the time is greater/lesser then GMT
        if (!$this->_outlook_time1 || !$this->_outlook_time2)
        {
            // Get our local time in Epoc
            $this->_outlook_time1 = date('U', time() + intval(date('Z')));
    
            // Get our GMT time
            $this->_outlook_time2 = date('U', gmmktime());
    
        }
        
        $numdays = $days + "25569";
    
        $secsnow = strtotime($date);
        $diff = $secsnow - $seconds;
    
        $perc = $diff / 86400;
    
        $perc = sprintf("%6f", $perc);
        $perc = preg_replace('/\d+\./', '', $perc);
    
        return "$numdays.$perc";
    }

    /**
     * Returns a server type identifier
     *
     * This method is now deprecated and always returns 'at'. 
     * 
     * @return string
     */
    public function getServerType()
    {
        return 'at';
    }

    /**
     * Pings to see if the server is alive, and will also verify authentication 
     * information, etc.
     * 
     * If ping succeeded 'at' will be returned, otherwise 'un'.
     *
     * @return string 
     */
    public function ping()
    {
        return $this->_calendarAdapter->ping()?'at':'un';
    }

    /**
     * munchEvents
     * converts a vCalendar string to a nice array.
     *
     * This method requires the events to be passed as an array of 
     * Atmail_Calendar_Event objects.
     *
     * array format will be similar to following
     * Array (
     * [0] => Array (
     *    [VCALENDAR] => Array (
     *        [VERSION] => 2.0
     *        [PRODID] => -//Apple Inc.//iCal 3.0//EN
     *        [CALSCALE] => GREGORIAN )
     *    [VTIMEZONE] => Array (
     *        [TZID] => Australia/Sydney
     *        [STANDARD] => Array (
     *            [TZOFFSETFROM] => +1100
     *            [TZOFFSETTO] => +1000
     *            [DTSTART] => 20080406T030000
     *            [RRULE] => FREQ=YEARLY;BYMONTH=4;BYDAY=1SU
     *             [TZNAME] => EST )
     *         [DAYLIGHT] => Array    (
     *             [TZOFFSETFROM] => +1000
     *             [TZOFFSETTO] => +1100
     *            [DTSTART] => 20081005T020000
     *            [RRULE] => FREQ=YEARLY;BYMONTH=10;BYDAY=1SU
     *            [TZNAME] => EST ) )
     *    [VEVENT] => Array (
     *        [SEQUENCE] => 2
     *        [TRANSP] => OPAQUE
     *        [UID] => 7DDD0A22-0F82-42C9-BF3C-645E9138C690
     *        [DTSTART;TZID=Australia/Sydney] => 20090205T160000
     *        [DTSTAMP] => 20090205T014141Z
     *          [SUMMARY] => ical event!
     *        [CREATED] => 20090205T014134Z
     *        [DTEND;TZID=Australia/Sydney] => 20090205T170000 ) ) )
     *
     * 
     */ 
    private function munchEvents(array $events_in)
    {    
        $events = array();
        if ( is_array($events_in) )
        {
            foreach ( $events_in AS $k => $event )
            {
                
                if ($event instanceof Atmail_Calendar_Event) {
                    $event = array(
                        'href' => $event->url,
                        'etag' => $event->etag,
                        'data' => $event->icalendarData,
                    );
                } 
                    
                // ical_load_entry requires an odd structure
                array_push($events, ical_load_entry($event));

            }
        }
        return $events;
    }

		

    /**
     * Generates an HTML preview based on a parsed VCalendar
     * 
     * @param array $event 
     * @param string $acceptUrl 
     * @return void
     */
    public static function generateHtmlPreview(Sabre_VObject_Component $vCalendar, $acceptUrl, $update = false)
    {
        
		//if the dtstart has parameters and a tzid parameter, then populate $timezoneString
		$timezoneString = "";
		if( isset($vCalendar->vevent->dtstart) && isset($vCalendar->vevent->dtstart->parameters) )
		{
			
			foreach( $vCalendar->vevent->dtstart->parameters as $k => $v )
			{
				
				if( strtoupper($v->name) == 'TZID' )
					$timezoneString = " (" . $v->value . ")";
				
			}
			
		}
			
		$ics = $vCalendar->serialize();
		$event = ical_load_entry(array('data' => $ics));
		
		/*
    
            [sVCALENDAR] => Array
                (
                    [PRODID] => -//Microsoft Corporation//Outlook 12.0 MIMEDIR//EN
                    [VERSION] => 2.0
                    [METHOD] => REQUEST
                    [X-MS-OLK-FORCEINSPECTOROPEN] => TRUE
                    [VEVENT] => Array
                        (
                            [ATTENDEE] => MAILTO:test@centos.virgin
                            [ATTENDEE2] => MAILTO:testagain@centos.virgi
                            [CLASS] => PUBLIC
                            [CREATED] => 20100708T051132Z
                            [DESCRIPTION] => When: Thursday\, 8 July 2010 3:30 PM-4:00 PM (GMT+10:00) Canber
                            [DTEND] => 20100708T060000Z
                            [DTSTAMP] => 20100708T051132Z
                            [DTSTART] => 20100708T053000Z
                            [LAST-MODIFIED] => 20100708T051132Z
                            [ORGANIZER] => MAILTO:test@centos.virgin
                            [PRIORITY] => 5
                            [SEQUENCE] => 0
                            [SUMMARY] => fesfsef
                            [TRANSP] => OPAQUE
                            [UID] => 040000008200E00074C5B7101A82E00800000000600233D9AF1ECB01000000000000000
                            [X-ALT-DESC] => <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//E
                            [ri">When] =>  Thursday\, 8 July 2010 3:30 PM-4:00 PM (GMT+10:00) Canberra\, Me
                            [X-MICROSOFT-CDO-BUSYSTATUS] => TENTATIVE
                            [X-MICROSOFT-CDO-IMPORTANCE] => 1
                            [X-MICROSOFT-CDO-INTENDEDSTATUS] => BUSY
                            [X-MICROSOFT-DISALLOW-COUNTER] => FALSE
                            [X-MS-OLK-ALLOWEXTERNCHECK] => TRUE
                            [X-MS-OLK-AUTOSTARTCHECK] => FALSE
                            [X-MS-OLK-CONFTYPE] => 0
                            [X-MS-OLK-SENDER] => MAILTO:test@centos.virgin
                        )

                )

            [VALARM] => Array
                (
                    [TRIGGER] => -PT15M
                    [ACTION] => DISPLAY
                    [DESCRIPTION] => Reminder
                )

            [etag] => 
        )



		//Acceptance syntax:
		ATTENDEE;CN="2@1.com";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:MAILTO:2@1.com
		ATTENDEE;CN="1@1.com";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:MAILTO:1@1.com

        */

        if(!isset($event['VCALENDAR']) || !isset($event['VCALENDAR']['VEVENT']))
            return "Invalid ICS file.";

        $vcal = $event['VCALENDAR'];
        $vevent = $vcal['VEVENT'];
        $output = '';
		
        if(isset($vevent['ORGANIZER'])) // ORGANIZER is set
        {
            if($update)
            {
                $output .= htmlentities(str_ireplace("MAILTO:", "", $vevent['ORGANIZER'])) . " has updated the appointment below : <br><br>";
            }
            else
            {
                $output .= htmlentities(str_ireplace("MAILTO:", "", $vevent['ORGANIZER'])) . " has invited you to attend the appointment below : <br><br>";
            }
        }
        else
        {
            $output .= "Event invitation:<br />";                    
        }
        $entry = array();
        $entry['id'] = $vevent['UID'];
        list($dtstart, $dtend) = calendar::caldav_extract_dtstamps($event);

        $entry['DateStartCal'] = calendar::caldav_strip_date($dtstart);
        $entry['DateStartTime'] = calendar::caldav_strip_time($dtstart);
        $entry['DateEndCal'] = calendar::caldav_strip_date($dtend);
        $entry['DateEndTime'] = calendar::caldav_strip_time($dtend);
        
        $entry['Title'] = ical_decode($event['VCALENDAR']['VEVENT']['SUMMARY']);

        // websync compat patch    
        if(!isset($event['VCALENDAR']['VEVENT']['DESCRIPTION']))
            $event['VCALENDAR']['VEVENT']['DESCRIPTION'] = 'None';

        $entry['CalMessage'] = ical_decode($event['VCALENDAR']['VEVENT']['DESCRIPTION']);
        //replace \n with <br />
		$entry['CalMessage'] = str_replace("\n", "\n<br />\n", $entry['CalMessage']);
//Zend_Registry::get('log')->debug( "\n\n" . print_r($entry['CalMessage'],true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$entry['CalMessage']: \n\n");
        
        if (isset($event['VCALENDAR']['VEVENT']['LOCATION']))
            $entry['Location'] = ical_decode($event['VCALENDAR']['VEVENT']['LOCATION']);
        else
            $entry['Location'] = 'None';    
                
        $stamp = calendar::caldav_extract_dtstamp('DTSTAMP', $event);
        $entry['DateModified'] = calendar::caldav_strip_date($stamp) . ' ' . calendar::caldav_strip_time($stamp);

        $siteurl = Zend_Registry::get('siteBaseUrl');
        $version = Zend_registry::get('config')->global['version'];
        foreach($entry as $k => $aEntryComponent)
        {
            $entry[$k] = htmlentities($entry[$k]);
        }
        $output .= <<<EOF
        <link rel="stylesheet" href="{$siteurl}css/reset.css?{$version}" type="text/css" media="screen, print" />
        <link rel="stylesheet" href="{$siteurl}css/common.css?{$version}" type="text/css" media="screen, print" />
        <link rel="stylesheet" href="{$siteurl}css/calendar.css?{$version}" type="text/css" media="screen, print" />
        <link rel="stylesheet" href="{$siteurl}css/ui.css?{$version}" type="text/css" media="screen, print" />
        <link rel="stylesheet" href="{$siteurl}css/webmail.css?{$version}" type="text/css" media="screen, print" />
        <script type="text/javascript" src="{$siteurl}js/jQuery/jquery-1.3.2.min.js"></script>
        <script type="text/javascript" src="{$siteurl}js/jQuery/ui/jquery-ui-1.7.2.custom.min.js"></script>
        <script type="text/javascript" src="{$siteurl}js/jQuery/plugins/jquery.json-1.3.min.js"></script>
        <script type="text/javascript" src="{$siteurl}js/jQuery/plugins/jquery.jcacher-1.0.0.js"></script>
        <script type="text/javascript" src="{$siteurl}js/jQuery/plugins/jquery.php.pack.js"></script>
<div style="position: relative; width: 100%; height: 100%;">
<div id={$entry['id']} style="position: absolute; left: 0px; right: 0px; height: 134px; padding: 0px; background-color: #fff; border: 1px solid #ccc; color: #333;" class="private"><div id="appointment_header" style="position: absolute; overflow: hidden; left: 0px; right: 0px; height: 30px; top: 0px; padding: 0px; opacity: 0.5; background-color: #ECECEC;" class="private_header"></div><div style="left: 0px; right: 0px; overflow: hidden; height: 172px; padding: 2px; color: #000;"><div id="title_text" style="padding:5px;overflow: hidden; width: 95%; height: 25px; color:#000;" class="private_text_header">{$entry['DateStartCal']} {$entry['DateStartTime']}{$timezoneString} to {$entry['DateEndCal']} {$entry['DateEndTime']}{$timezoneString}</div>
<div id="textarea" class="private_text" style="height: auto; position: absolute; left: 1%; right: 1%; width: 97.8%; top: 33px; bottom: 10px; overflow: auto; word-wrap: break-word; border: 0pt none; background-color: transparent; color: #333; font-size: 13px;"><span style="color:#999;">Title :</span><span style="color:#333;"> {$entry['Title']}</span><br><span style="color:#999;">Description :</span><span style="color:#333;"> {$entry['CalMessage']}</span><br><span style="color:#999;">Location :</span> <span style="color:#333;">{$entry['Location']}</span><br/>
<div id="action_buttons" style="position:relative; margin-top: 6px;">
EOF;
        if(!$update)
        {
            $output .= <<<EOF
	<div onclick="$.php('{$acceptUrl}accept');" style="float:left;"><a href="#" style="color:#0066CC; background: none !important;">Accept Event</a>&nbsp; - &nbsp;</div>
	<div onclick="$.php('{$acceptUrl}tentative');" style="float:left;"><a href="#" style="color:#0066CC; background: none !important;">Accept Tentatively</a>&nbsp; - &nbsp;</div>
	<div onclick="$.php('{$acceptUrl}decline');" style="float:left;"><a href="#" style="color:#0066CC; background: none !important;">Decline Event</strong></a></div>
EOF;
        }
        else
        {
            $output .= <<<EOF
	<div onclick="$.php('{$acceptUrl}update');" style="float:left;"><a href="#" style="color:#0066CC; background: none !important;">Update Event</a>&nbsp;&nbsp;</div>
EOF;
        }
        $output .= <<<EOF
</div>
</div>
</div></div>
</div>

EOF;
        return $output;
    }

    /**
     * Returns YYYY-MMM-DD from YYYYMMDDTHHMMSS
     * 
     * @param string $caldav_datetime 
     * @return string 
     */
    static public function caldav_strip_date($caldav_datetime)
    {
        if($caldav_datetime == '')
            return '';
            
        $temp = str_split($caldav_datetime,4);
        $year = $temp[0];
        $temp = str_split($caldav_datetime,2);
        $month = $temp[2];
        $day = $temp[3];
        return sprintf("%s-%s-%s", $year, $month, $day);
    }

    /**
     * Returns HH:MM:SS from yyyymmddTHHMMSSZ
     *
     * @param string $caldav_datetime
     * @return string
     */
    static public function caldav_strip_time($caldav_datetime)
    {
        if($caldav_datetime == '')
            return '';

        if (!strpos($caldav_datetime, 'T')) {
            return '00:00:00';
        }
        list($junk, $temp) = explode("T", $caldav_datetime);
        $temp = str_split($temp,2);
        $hour = $temp[0];
        $min = $temp[1];
        $sec = $temp[2];
        if($hour == '')
            $hour = '00';
        if($min == '')
            $min = '00';
        if($sec == '')
            $sec = '00';

        return sprintf("%s:%s:%s", $hour, $min, $sec);
    }

    /**
     * Extracts a timezone id from an 'array-parsed' iCalendar object
     * 
     * @param mixed $garbage  
     * @param array $event 
     * @return void
     */
    private static function caldav_extract_dttz($garbage, $event)
    {
        if( isset($event['VCALENDAR']['VTIMEZONE']) && isset($event['VCALENDAR']['VTIMEZONE']['TZID']))
        {
            return $event['VCALENDAR']['VTIMEZONE']['TZID'];
        }
        
        return '';
    }
    
    
    /**
     * Returns a date and time from an 'array-parsed' iCalendar object
     *
     * dtstamp is the property that it needs to extract from (DTSTART, DTEND, 
     * etc) and event is the event array.
     * 
     * @param string $dtstamp 
     * @param array $event 
     * @return string 
     */
    public static function caldav_extract_dtstamp($dtstamp, $event)
    {
        if( isset($event['VCALENDAR']['VEVENT']) )
        {
            $type = "VEVENT";
        }
        else if( isset($event['VCALENDAR']['VTODO']) )
        {
            $type = "VTODO";
        }
        
        if( isset($event['VCALENDAR']['VTIMEZONE']) )
        {
            // we have timezone information
            if( isset($event['VCALENDAR'][$type][$dtstamp . ';TZID='.$event['VCALENDAR']['VTIMEZONE']['TZID']]) )
            {
                $stamp = $event['VCALENDAR'][$type][$dtstamp . ';TZID='.$event['VCALENDAR']['VTIMEZONE']['TZID']];
            }
            else if( isset($event['VCALENDAR'][$type][$dtstamp . ';VALUE=DATE']) )
            {
                $stamp = $event['VCALENDAR'][$type][$dtstamp . ';VALUE=DATE'] . "T235959Z";
            }
            else if( isset($event['VCALENDAR'][$type][$dtstamp]) )
            {
                $stamp = $event['VCALENDAR'][$type][$dtstamp];
            }
            
            return $stamp;
        }

        if( isset($event['VCALENDAR'][$type][$dtstamp]) )
        {
            return $event['VCALENDAR'][$type][$dtstamp];
        }
        else if ( isset($event['VCALENDAR'][$type][$dtstamp . ';VALUE=DATE']) )
        {
            return $event['VCALENDAR'][$type][$dtstamp . ';VALUE=DATE'] . 'T000000';
        }
    
        return null;
    }
    
    /**
     * Returns the calendar name used for 'pushing' (used by activesync).
     *
     * Because it appears to be fine to return the standard calendar list, 
     * that's what we're doing.
     *
     * @return array
     */
    public function getPushCalHref()
    {
        return $this->getCalNames();
    }
    

    /**
     * Returns calendar names
     *
     * Returns a list of calendar names for the current user as well as any 
     * calendar the current user has access to through delegation. 
     * 
     * @return array 
     */
    public function getCalNames($filters = false)
    {

        $calendars = $this->_calendarAdapter->getCalendars();

        $calNames = array();

		foreach($calendars as $calendar) {

            $calNames[] = array(
                'relative_href'  => $calendar->url,
                'href'           => $calendar->url,
                'ctag'           => $calendar->ctag,
                'displayname'    => $calendar->displayName?$calendar->displayName:basename($calendar->url),
                'description'    => $calendar->description,
                'principal_url'  => $calendar->principalUrl,
                'mode'           => $calendar->readOnly?'read':'write',
                'shared'         => $calendar->isShared,
            	'home'			 => $calendar->isHome,
                // Intentionally left blank. Etags on collections are undefined
                'etag'           => null,
                'server'         => $calendar->url,

                // Hack to determine some kind of username
                'calendar_owner' => $calendar->owner,
               
                // The stored value is RGBA, but this API wants plain RGB. 
                'color'          => substr($calendar->color,0,7),
                'order'          => $calendar->order, 

            );
        
        }

		if($filters)
		{
			$filterCalendars = $calNames;
			$calNames = array();
			foreach($filterCalendars as $calendar) {
				foreach( $filters as $k => $v)
				{
					if(isset($calendar[$k]) && $calendar[$k] == $v)
					{
						$calNames[] = $calendar;
					}
				}
			}
		}

        return $calNames;
    }

    /**
     * TODO: DO NOT UNDERSTAND
     */    
    public function getPrivateEtag()
    {
        return 0;
		//$etag = findetag($this->_caldav_server, $this->_account . '/calendar/');
		$etag = 0;
        if(strpos($etag, '<getetag>') != false)
        {
            list($junk, $etag) = explode('<getetag>', findetag($this->_caldav_server, $this->_account . '/calendar/'), 2);
            list($etag, $junk) = explode('</getetag>', $etag, 2);
        }
        
        if($etag)
        {
            $etag = trim($etag, 'W/');
            $etag = trim($etag, '"');
        }
        return $etag;
    }

    /**
     * Also returns the list of calendars
     * 
     * @return array 
     */
    public function getEtags()
    {
        return $this->getCalNames();
    }
    
    /**
     * Used for activesync 
     *
     * @param array $recurrence 
     * @return array 
     */
    public function PushArray2RecurrenceArray($recurrence)
    {
        if( gettype($recurrence) != "object" )
            return false;
        $recurrenceRules = array();
        
        /*[recurrence] => SyncRecurrence Object
                (
                    [type] => 0
                    [until] => 
                    [occurrences] => 
                    [interval] => 1
                    [dayofweek] => 
                    [dayofmonth] => 
                    [weekofmonth] => 
                    [monthofyear] => 
                    [content] => 
                    [attributes] => 
                    [flags] => 
        */
        $recurrenceRules['skipInterval'] = $recurrence->interval;
        switch($recurrence->type)
        {
            case 0: //daily
            case 1: //weekly
            case 2: //monthly
            {
                $recurrenceRules['RecurrenceType'] = $recurrence->type + 10;
            }
            break;
            case 5: // yearly
            {
                $recurrenceRules['RecurrenceType'] = 13;
            }
        }
        
        $recurrenceRules['IsSimple'] = 0;
        $recurrenceRules['recurrenceEndType'] = 0;

        if(isset($recurrence->until))
        {
            $recurrenceRules['recurrenceEndType'] = 2;
        }
        $recurrenceRules['recurrenceEndAfterX'] = $recurrenceRules['Occurences'] = $recurrence->occurrences;
        $recurrenceRules['recurrenceEndByDate'] = $recurrenceRules['Until'] = $recurrence->until;
        $recurrenceRules['DayOfWeek'] = $recurrence->dayofweek;
        $recurrenceRules['DayOfMonth'] = $recurrence->dayofmonth;
        $recurrenceRules['MonthOfYear'] = $recurrence->monthofyear;
        
        return $recurrenceRules;
    }
    
    /**
     * Used for activesync
     * 
     * @param array $event 
     * @return array 
     */
    public function RecurrenceArray2PushArray($event)
    {
        if(!isset($event['recurrenceArray']))
        {
            return;
        }
        $recurrenceRules = $event['recurrenceArray'];

        $recurrence = new SyncRecurrence;        
        /*RecurrenceType] => 10
                    [IsSimple] => 1
                    [DayOfWeekMask] => 127
                    [skipInterval] => 1
                    [DayOfWeek] => 
                    [DayOfMonth] => 
                    [MonthOfYear] => 
                    [NoEndDate] => 1
                    [Occurences] => 0
                    [PatternStartDate] => 20100816T170000
                    [PatternEndDate] => 20100816T180000
                    [Until] => 0
                    [Instance] => 
        
        */
        $recurrence->interval = $recurrenceRules['skipInterval'];
        switch($recurrenceRules['RecurrenceType'])
        {
            case 10: //daily
            case 11: //weekly
            case 12: //monthly
            {
                $recurrence->type = $recurrenceRules['RecurrenceType'] - 10;
            }
            break;
            case 13: // yearly
            {
                $recurrence->type = 5;
            }
        }
        
        if(isset($recurrenceRules['recurrenceEndType']) && $recurrenceRules['recurrenceEndType'] != '')
        {
            if( $recurrenceRules['recurrenceEndType'] == 2)
            {
                $recurrence->until = $recurrenceRules['Until'];
                $recurrence->occurrences = $recurrenceRules['Occurences'];
            }
        }
        $recurrence->dayofweek = $recurrenceRules['DayOfWeek'];
        $recurrence->dayofmonth = $recurrenceRules['DayOfMonth'];
        $recurrence->monthofyear = $recurrenceRules['MonthOfYear'];

        return $recurrence;
    }
    
    public function RecurrenceArray2ical($recurrenceRules)
    {
        $recurrenceRule = "";
        
        if(!isset($recurrenceRules['RecurrenceType'])) $recurrenceRules['RecurrenceType'] = 0;
        if(!isset($recurrenceRules['RecurrenceEndType'])) $recurrenceRules['RecurrenceEndType'] = 0;
        if(!isset($recurrenceRules['IsSimple'])) $recurrenceRules['IsSimple'] = 0;
        if(!isset($recurrenceRules['DayOfWeekMask'])) $recurrenceRules['DayOfWeekMask'] = 0;
        if(!isset($recurrenceRules['skipInterval'])) $recurrenceRules['skipInterval'] = 0;
        if(!isset($recurrenceRules['DayOfWeek'])) $recurrenceRules['DayOfWeek'] = 0;
        if(!isset($recurrenceRules['DayOfMonth'])) $recurrenceRules['DayOfMonth'] = 0;
        if(!isset($recurrenceRules['MonthOfYear'])) $recurrenceRules['MonthOfYear'] = 0;
        if(!isset($recurrenceRules['NoEndDate'])) $recurrenceRules['NoEndDate'] = 0;
        if(!isset($recurrenceRules['Occurences'])) $recurrenceRules['Occurences'] = 0;
        if(!isset($recurrenceRules['PatternStartDate'])) $recurrenceRules['PatternStartDate'] = 0;
        if(!isset($recurrenceRules['PatternEndDate'])) $recurrenceRules['PatternEndDate'] = 0;
        if(!isset($recurrenceRules['Until'])) $recurrenceRules['Until'] = 0;
                    
        $RecurrenceType = $recurrenceRules['RecurrenceType'];
        $recurrenceEndType = $recurrenceRules['RecurrenceEndType'];
        $simpleRecurrence = $recurrenceRules['IsSimple'];
        $DayOfWeekMask = $recurrenceRules['DayOfWeekMask'];
        $skipInterval = $recurrenceRules['skipInterval'];
        $DayOfWeek = $recurrenceRules['DayOfWeek'];
        $DayOfMonth = $recurrenceRules['DayOfMonth'];
        $MonthOfYear = $recurrenceRules['MonthOfYear'];
        $NoEndDate = $recurrenceRules['NoEndDate'];
        $Count = $recurrenceRules['Occurences'];
        $PatternStartDate = $recurrenceRules['PatternStartDate'];
        $PatternEndDate = $recurrenceRules['PatternEndDate'];
        $Until = $recurrenceRules['Until'];
        
        
        if( $skipInterval == '' )
            $skipInterval = 0;
            
        if(     $RecurrenceType != 10
            &&     $RecurrenceType != 11
            &&    $RecurrenceType != 12
            &&    $RecurrenceType != 13
            )
        {
            $recurrenceEndType = -1;
        }
        
        switch ($RecurrenceType)
        {
            case 10 : // daily
            {
                $freq = "DAILY";
            } break;
            case 11 : // Weekly
            {
                $freq = "WEEKLY";        
            } break;
            case 12 : // Monthly
            {
                $freq = "MONTHLY";
            } break;
            case 13 : // Yearly
            {
                $freq = "YEARLY";
            } break;
        }
        
        if($recurrenceEndType == 0)
        {
            // no end date
            $recurrenceRule['RRULE'] = 'FREQ=' . $freq . ';INTERVAL=' . $skipInterval;
            $recurrenceRule['RRULE2'] = 'FREQ=' . $freq . ';COUNT=400';
        }
        else if ($recurrenceEndType == 1)
        {
            // end after occurances
            $recurrenceRule['RRULE'] = 'FREQ=' . $freq . ";INTERVAL=" . $skipInterval .";COUNT=" . $Count;
        }
        else if ($recurrenceEndType == 2)
        {
            // end after occurances
            if(is_int($Until))
            {
                $Until = ical_datetime(date($Until));
            }
            else
            {
                $Until = ical_datetime(date(strtotime($Until)));
            }
            $recurrenceRule['RRULE'] = 'FREQ=' . $freq . ";INTERVAL=" . $skipInterval . ";UNTIL=" . $Until;
        }        
        
        return $recurrenceRule;
    }
    
    // convert ical ics entry to a recurrencearray    
    static public function ical2RecurrenceArray($munchedEvent)
    {
        $recurrencePattern = array();
        
        if(!isset($munchedEvent['VCALENDAR']['VEVENT']['RRULE']))
        {
            return;
        }
        
        list($PatternStartDate, $PatternEndDate) = calendar::caldav_extract_dtstamps($munchedEvent);
            
        $recurrenceRule = $munchedEvent['VCALENDAR']['VEVENT']['RRULE'];

        
        //[RRULE] => FREQ=DAILY;INTERVAL=2;COUNT=2

        $recurrenceRawRules = explode(";", $recurrenceRule);
        foreach($recurrenceRawRules as $recurrenceRule)
        {
            $recurrenceRule = explode("=", $recurrenceRule);
            $recurrenceRules[$recurrenceRule[0]] = $recurrenceRule[1];
        }

/*        list($RecurrenceType, $Interval) = explode(";", $recurrenceRule, 2);
        $RecurrenceType = str_replace('FREQ=', '', $RecurrenceType);
        list($Interval, $Count) = explode(";", $Interval);
        $Interval = str_replace("INTERVAL=", '', $Interval);
        $Count = str_replace("COUNT=", '', $Count);*/
        //$Until = UNTIL

        if(isset($recurrenceRules['FREQ']))
            $RecurrenceType = $recurrenceRules['FREQ'];
        if(isset($recurrenceRules['INTERVAL']))
            $Interval = $recurrenceRules['INTERVAL'];
        if(isset($recurrenceRules['COUNT']))
            $Count = $recurrenceRules['COUNT'];
        
        if(!isset($Count) || $Count == 400)
            $Count = 0;
            
        //RRULE:FREQ=WEEKLY;INTERVAL=1
        //RRULE:FREQ=WEEKLY;COUNT=400
        
        // GLUE for ical > old recurrence patterns
        $simpleRecurrence = 0;
        $DayOfWeekMask = '';
        $DayOfMonth = '';
        $MonthOfYear = '';
        $DayOfWeek = '';
        $Until = 0;
            
        if(isset($recurrenceRules['UNTIL']))
        {
            $Until = 1;
            $oldtz = date_default_timezone_get();
            date_default_timezone_set('GMT');
            $PatternEndDate = new Date(strtotime($recurrenceRules['UNTIL']));
            $simpleRecurrence = 0;
            $NoEndDate = 0;
            date_default_timezone_set($oldtz);    
        }
        else
        {
            $NoEndDate = 1;
        }
        
        if(isset($munchedEvent['VCALENDAR']['VEVENT']['RRULE2']))
        {
            $recurrenceRule2 = $munchedEvent['VCALENDAR']['VEVENT']['RRULE2'];
            $recurrenceRawRules2 = explode(";", $recurrenceRule2);
            foreach($recurrenceRawRules as $recurrenceRule)
            {
                $recurrenceRule = explode("=", $recurrenceRule);
                $recurrenceRules2[$recurrenceRule[0]] = $recurrenceRule[1];
            }
            
/*            list($RecurrenceType2, $Count) = explode(";", $recurrenceRule2);
            $RecurrenceType2 = str_replace('FREQ=', '', $RecurrenceType2);
            $Count = str_replace("COUNT=", '', $Count);*/
            if(isset($recurrenceRules2['FREQ']))
                $RecurrenceType2 = $recurrenceRules2['FREQ'];
            if(isset($recurrenceRules2['COUNT']))
                $Count = $recurrenceRules2['COUNT'];
            
        }

        // each day
        $skipInterval = $Interval;
        
        switch ($RecurrenceType)
        {
            case "DAILY" : // daily
            {

                $RecurrenceType = 10;
                if($Interval == 1 && $Count == 0)
                {
                    $simpleRecurrence = 1;
                }
                $RecurrenceInterval = $Interval;

                // every day is valid
                if($simpleRecurrence)
                {
                    $DayOfWeekMask = 0x7f;
                }
            } break;
            case "WEEKLY" : // Weekly
            {
                $RecurrenceType = 11;
                $date = new Date(strtotime($PatternStartDate));
                
                $DayOfWeek = $date->getDayOfWeek();
                
                if($Interval == 1 && $Count == 0 && !$Until)
                {
                    $simpleRecurrence = 1;
                }
                
                switch($DayOfWeek)
                {
                    case 0 : $DayOfWeekMask = $DayOfWeekMask | 1; break;        // sunday
                    case 1 : $DayOfWeekMask = $DayOfWeekMask | 2; break;        // monday
                    case 2 : $DayOfWeekMask = $DayOfWeekMask | 4; break;        // tuesday
                    case 3 : $DayOfWeekMask = $DayOfWeekMask | 8; break;        // wednesday
                    case 4 : $DayOfWeekMask = $DayOfWeekMask | 16; break;        // thursday
                    case 5 : $DayOfWeekMask = $DayOfWeekMask | 32; break;        // friday
                    case 6 : $DayOfWeekMask = $DayOfWeekMask | 64; break;        // saturday
                }
            } break;
            case "MONTHLY" : // Monthly
            {
                $RecurrenceType = 12;
                if($Interval == 1 && $Count == 0 && !$Until)
                {
                    $simpleRecurrence = 1;
                }
                $date = new Date(strtotime($PatternStartDate));
                $DayOfMonth = $date->getDay();
            } break;
            case "YEARLY" : // Yearly
            {
                $RecurrenceType = 13;
                if($Interval == 1 && $Count == 0 && !$Until)
                {
                    $simpleRecurrence = 1;
                }
                $date = new Date(strtotime($PatternStartDate));
                $DayOfMonth = $date->getDay();
                $MonthOfYear = $date->getMonth();
            } break;
        }
        
        $recurrencePattern['RecurrenceType'] = $RecurrenceType;
        $recurrencePattern['IsSimple'] = $simpleRecurrence;
        $recurrencePattern['DayOfWeekMask'] = $DayOfWeekMask;
        $recurrencePattern['skipInterval'] = $skipInterval;
        $recurrencePattern['DayOfWeek'] = $DayOfWeek;
        $recurrencePattern['DayOfMonth'] = $DayOfMonth;
        $recurrencePattern['MonthOfYear'] = $MonthOfYear;
        $recurrencePattern['NoEndDate'] = $NoEndDate;
        $recurrencePattern['Occurences'] = $Count;
        $recurrencePattern['PatternStartDate'] = $PatternStartDate;
        $recurrencePattern['PatternEndDate'] = $PatternEndDate;
        $recurrencePattern['Until'] = $Until;
        $recurrencePattern['Instance'] = '';
    //    $recurrencePattern['RecurrenceEndType'] = $recurrenceEndType;
        
         return $recurrencePattern;
    }
    
    // Generate a recurrence pattern for UI given ID
    static public function RecurrenceArray2RecurrencePattern($recurrenceArray)
    {
        $sync = false;
        /*if($shared==1)
        {
            $recurrencePattern = $sql->sqlhash("select * from recurrencePatterns where linkid = $linkid");
        }
        else
        {
            $recurrencePattern = $sql->sqlhash("select * from recurrencePatterns where linkid = $linkid and account = \"$account\"");
        }*/
    
        // we got a pattern ?
        /*if(!isset($munchedEvent['VCALENDAR']['VEVENT']['RRULE']))
            return "0";    */

        // [RRULE] => FREQ=DAILY;INTERVAL=1
        // [RRULE2] => FREQ=DAILY;COUNT=400


        $recurrencePattern = $recurrenceArray; //calendar::ical2RecurrenceArray($munchedEvent, $PatternStartDate, $PatternEndDate);
        
        $RecurrenceType = $recurrencePattern['RecurrenceType'];
        $simpleRecurrence = $recurrencePattern['IsSimple'];
        
        $pattern = "";
        
        switch ($RecurrenceType)
        {
            case 10 : // Daily
            {
                $pattern .= "1";
            } break;
            case 11 : // Weekly
            {
                $pattern .= "2";
            } break;
            case 12 : // Monthly
            {
                $pattern .= "3";
            } break;
            case 13 : // Yearly
            {
                $pattern .= "4";
            } break;
        }
        
        if($simpleRecurrence == 1 && !$sync)
        {
            return $pattern;
        }
    
        // ok, lets build the recurrence pattern for the ui...
        // we are some kind of advanced pattern...
        // ARGH.
        switch ($RecurrenceType)
        {
            case 10 : // Daily
            {
                // if every day is valid and we have a skip interval
                if($recurrencePattern['DayOfWeekMask']==0x7f && $recurrencePattern['skipInterval'] != 0)
                {
                    // we must be every x days
                    $pattern .= ",0," . $recurrencePattern['skipInterval'];
                }
                else if($sync)
                {
                    // we must be every weekday VIA sync ( Fake as a weekly, however @mail internal works as normal )
                    $pattern = "2,1,1,1,1,1,1,0,0";
    
                }
                else
                {
                    // we must be every weekday
                    $pattern .= ",1";
                }
            } break;
            case 11 : // Weekly
            {
                // freq - Different format for Websync again
                if($sync && $recurrencePattern['skipInterval'] == 0)
                $pattern .= ",1";
                // Normal pattern for Webmail
                else
                $pattern .= "," . $recurrencePattern['skipInterval'];
        
                if( ($recurrencePattern['DayOfWeekMask'] & 2)!=0)    // monday
                    $pattern .= ",1";
                else
                    $pattern .= ",0";
                if( ($recurrencePattern['DayOfWeekMask'] & 4)!=0)    // tuesday
                    $pattern .= ",1";
                else
                    $pattern .= ",0";
                if( ($recurrencePattern['DayOfWeekMask'] & 8)!=0)    // wednesday
                    $pattern .= ",1";
                else
                    $pattern .= ",0";
                if( ($recurrencePattern['DayOfWeekMask'] & 16)!=0)    // thursday
                    $pattern .= ",1";
                else
                    $pattern .= ",0";
                if( ($recurrencePattern['DayOfWeekMask'] & 32)!=0)    // friday
                    $pattern .= ",1";
                else
                    $pattern .= ",0";
                if( ($recurrencePattern['DayOfWeekMask'] & 64)!=0)    // saturday
                    $pattern .= ",1";
                else
                    $pattern .= ",0";
                if( ($recurrencePattern['DayOfWeekMask'] & 1)!=0)    // sunday
                    $pattern .= ",1";
                else
                    $pattern .= ",0";
    
            } break;
            case 12 : // Monthly
            {
                // see if we have a instance value
                if($recurrencePattern['Instance'] != 0)
                {
                    // we must be 'the x y of every z months
                    $pattern .= ",1";
    
                    // this is a INSTANCE PATTERN - not just instance.
                    if($recurrencePattern['Instance'] == 32)
                    {
                        // special value here, 32 = last day of month.
                        $pattern .= ",4";
                    }
                    else
                    {
                        $pattern .= "," . ($recurrencePattern['Instance'] - 1);
                    }
                    $DayOfWeekMask = $recurrencePattern['DayOfWeekMask'];
    
                    if($DayOfWeekMask==0x7F)        $pattern .= ",0";    // day
                    else if($DayOfWeekMask==0x3E)    $pattern .= ",1";    // weekday
                    else if($DayOfWeekMask==0x41)    $pattern .= ",2";    // weekend day
                    else if($DayOfWeekMask==0x2)    $pattern .= ",3";    // monday
                    else if($DayOfWeekMask==0x4)    $pattern .= ",4";    // tuesday
                    else if($DayOfWeekMask==0x8)    $pattern .= ",5";    // wednesday
                    else if($DayOfWeekMask==0x10)    $pattern .= ",6";    // thursday
                    else if($DayOfWeekMask==0x20)    $pattern .= ",7";    // friday
                    else if($DayOfWeekMask==0x40)    $pattern .= ",8";    // saturday
                    else if($DayOfWeekMask==0x1)    $pattern .= ",9";    // sunday
    
                    $pattern .= "," . $recurrencePattern['skipInterval'];
                }
                else
                {
                    // we must be Day X of every y months
                    $pattern .= ",0";
                    $pattern .= "," . $recurrencePattern['DayOfMonth'];
                    if($sync && $recurrencePattern['skipInterval'] == 0)
                        $pattern .= ",1";
    
                    else
                    $pattern .= "," . $recurrencePattern['skipInterval'];
                }
    
                if(!$sync)
                $pattern .= "," . $recurrencePattern['DayOfMonth'];
            } break;
            case 13 : // Yearly
            {
                // see if we have a interval value
                if($recurrencePattern['DayOfWeekMask'] != 0)
                {
                    // we must the x instance y of z month
                    $pattern .= ",1";
    
                    // instance
                    if($recurrencePattern['Instance'] == 32)
                    {
                        // special value here, 32 = last day of month.
                        $pattern .= ",4";
                    }
                    else
                    {
                        $pattern .= "," . ($recurrencePattern['Instance']-1);
                    }
    
                    // day mask
                    $DayOfWeekMask = $recurrencePattern['DayOfWeekMask'];
                    if($DayOfWeekMask==0x7F)        $pattern .= ",0";    // day
                    else if($DayOfWeekMask==0x3E)    $pattern .= ",1";    // weekday
                    else if($DayOfWeekMask==0x41)    $pattern .= ",2";    // weekend day
                    else if($DayOfWeekMask==0x2)    $pattern .= ",3";    // monday
                    else if($DayOfWeekMask==0x4)    $pattern .= ",4";    // tuesday
                    else if($DayOfWeekMask==0x8)    $pattern .= ",5";    // wednesday
                    else if($DayOfWeekMask==0x10)    $pattern .= ",6";    // thursday
                    else if($DayOfWeekMask==0x20)    $pattern .= ",7";    // friday
                    else if($DayOfWeekMask==0x40)    $pattern .= ",8";    // saturday
                    else if($DayOfWeekMask==0x1)    $pattern .= ",9";    // sunday
    
                    // month of year
                    $pattern .= "," . ($recurrencePattern['MonthOfYear'] - 1);
    
                    // day of month
                    if(!$sync)
                    $pattern .= "," . $recurrencePattern['DayOfMonth'];
                }
                else
                {
                    // we must be every x month y day
    
                    $pattern .= ",0";
                    $pattern .= "," . ($recurrencePattern['MonthOfYear'] - 1);
                    $pattern .= "," . $recurrencePattern['DayOfMonth'];
                    $pattern .= "," . $recurrencePattern['skipInterval'];
    
                }
            } break;
        }
    
    
    
        return $pattern;
    }

    // Generate a time pattern for UI given ID
    public function generateTimePattern($linkid, $shared=0)
    {
        /*$sql = new SQL();
    
        $account =     addslashes($this->_account) . '@' . addslashes($this->pop3host);
    
        if($shared==1)
        {
            $recurrencePattern = $sql->sqlhash("select * from recurrencePatterns where linkid = $linkid");
        }
        else
        {
            $recurrencePattern = $sql->sqlhash("select * from recurrencePatterns where linkid = $linkid and account = \"$account\"");
        }
    
        // we got a pattern ?
        if (count($recurrencePattern) == 0)*/
            return "0";
    
        $pattern = "";
    
        $NoEndDate = $recurrencePattern['NoEndDate'];
        $totalOccurances = $recurrencePattern['Occurences'];
        $EndDay = new Date($recurrencePattern['PatternEndDate']);
        $EndDay->setTZByID("UTC");
        
        if($NoEndDate == 1 && $totalOccurances == 0)
        {
            $pattern .= "0";
            return $pattern;
        }
    
        $pattern .= "1";
    
        if($totalOccurances != 0)
        {
            $pattern .= ",";
            $pattern .= $totalOccurances;
            return $pattern;
        }
    
        $pattern = "2,";
        $pattern .= $EndDay->getDay();
        $pattern .= ",";
        $pattern .= $EndDay->getMonth();
        $pattern .= ",";
        $pattern .= $EndDay->getYear();
    
        return $pattern;
    }

    public function recurrencePatternArray($arg)
    {
    
        // grab the recurrence pattern
        $recurrencePattern = $arg['recurrencePattern'];
    
        // Some variable setup.
    
        // RecurrenceType:
        // 10 = Daily
        // 11 = Weekly
        // 12 = Monthly
        // 13 = Yearly
        $RecurrenceType = 0;
    
        $DayOfMonth = 0;
        $Instance = 0;
        $Duration = 0;
        $MonthOfYear = 0;
        $linkid = 0;
        $account = "";
    
        $RecurrenceSimplePattern = 0;
    
        // Pattern recurrence end type
        // 0 = no end date
        // 1 = end after x occurances
        // 2 = end by date
        $PatternEndType = 0; //$arg['recurrenceEndType'];
    
        // PatternStartDate:
        // Well, this can happen anytime, but for the time being we will start the pattern on the same date as the entry starts...\
        // That kinda makes sense..
    
        $PatternStartDate = new Date($arg['DateStart']);
        $PatternStartDate->setTZByID("UTC");
        // PatternEndDate:
        // Again, this can happen anytime !
    
        $PatternEndDate = new Date($arg['recurrenceEndByDate']);
        $PatternEndDate->setTZByID("UTC");
        
        // NoEndDate:
        // Obviously this flags the current entry as having no end date.. Kinda redundant tho - we could just check for NULL data...
        // 1 = NoEndDate
        // 0 = PatternEndDate followed.
    
        $NoEndDate = ($PatternEndType == 2 ? 0 : 1);
    
        // totalOccurances:
        // Total number of repeats for the entry.
        // 0 = Unlimited
        // >0 = Total Limit
    
        if ($PatternEndType == 1)
            $totalOccurances = $arg['recurrenceEndAfterX'];
        else
            $totalOccurances = 0;
    
        // RecurrenceInterval:
        // Specifies the amount of units to skip in each mode.
        // A value of 0 is a special case for Daily:
        // >0 = every x days
        // 0 = every weekday
        // I.e. A value of 2 in Weekly will make the pattern occur every second week.
    
        $RecurrenceInterval = 0;
    
        // Instance:
        // This value will specify the total amount of hits before an actual event is produced.
        // So, if we are targeting the second Friday of sept, we would define this value to be 2.
        // 0 = all instances.
        // >0 = skip x instances between events.
    
        $Instance = 0;
    
        // DayOfWeekMask:
        // This can be applied to all modes.
        // Construct by OR'ing.
        // 1    = Sunday
        // 2    = Monday
        // 4    = Tuesday
        // 8    = Wednesday
        // 16    = Thurday
        // 32    = Friday
        // 64    = Saturday
        // Some common values :
        // 0x3E - All week days
        // 0x7F - All days
        // So, if we are a daily, with a skipInterval of 0, we would define DayOfWeekMask to be 0x3E.
    
        $DayOfWeekMask = 0;
    
        // DayOfMonth:
        // The day of the month to target when in monthly or yearly modes.
        // 0 = undefined.
    
        $DayOfMonth = 0;
    
        // Duration:
        // Amount of time for the recurrence pattern in seconds.
        // i.e. 3600 = 1 hour.
    
        $durationCalc = new Date_span();
        
        $DateEnd = new Date($arg['DateEnd']);
        $DateEnd->setTZByID("UTC");
        $durationCalc->setFromDateDiff( $PatternStartDate, $DateEnd );
    
        $Duration = $durationCalc->toSeconds();
    
        // MonthOfYear:
        // The month number to target when in monthly or yearly modes.
        // 0 = undefined.
    
        $MonthOfYear = 0;
    
        // linkid:
        // Id of the original event.
    
        $linkid = $arg['id'];
    
        // account:
        // The user account details i.e. joe@blogs.com
    
        $account = $arg['UserTo'];
    
        // Pattern definition
        // ------------------
        //
        // Daily pattern definition :
        // Pattern Pos    | Name                |    Value Behaviour
        // 0            | Repeat Type        |    1 = Daily
        // 1            | Daily Type (BOOL) |    0 = Every x days
        //                |                    |    1 = Every weekday
        // 2            | Frequency (INT)    |
        //
        // Weekly pattern definition :
        // Pattern Pos    | Name                |    Value Behaviour
        // 0            | Repeat Type        |    2 = Weekly
        // 1            | Frequency (INT)    |
        // 2            | Monday (BOOL)        |    1 = enabled
        // 3            | Tuesday (BOOL)    |    1 = enabled
        // 4            | Wednesday (BOOL)    |    1 = enabled
        // 5            | Thursday (BOOL)    |    1 = enabled
        // 6            | Friday (BOOL)        |    1 = enabled
        // 7            | Saturday (BOOL)    |    1 = enabled
        // 8            | Sunday (BOOL)        |    1 = enabled
        //
        // Monthly MAIN pattern definition :
        // Pattern Pos    | Name                |    Value Behaviour
        // 0            | Repeat Type        |    3 = Monthly
        // 1            | Monthly Type(BOOL)|    0 = Day x of every y months
        //                |                    |    1 = The x y of evert z months
        //
        // Monthly (Day x of every y months) SUB pattern definition:
        // Pattern Pos    | Name                |    Value Behaviour
        // 2            | Day of Month (INT)|
        // 3            | Freq Months(INT)    |
        //
        // Monthly (The x y of evert z months) SUB pattern definition:
        // Pattern Pos    | Name                |    Value Behaviour
        // 2            | Instance (INT)    |    See Instance pattern
        // 3            | Day mask (INT)    |    See Day Mask pattern
        // 4            | Frequency    (INT)    |
        //
        // Yearly MAIN pattern definition :
        // Pattern Pos    | Name                | Value Behaviour
        // 0            | Repeat Type        | 4 = Yearly
        // 1            | Yearly Type (BOOL)| 0 = every x month y day
        //                |                    | 1 = the x instance y of z month
        //
        // Yearly (every x month y day) SUB pattern definition :
        // Pattern Pos    | Name                | Value Behaviour
        // 2            | Month of year(INT)| Index of Month (starting 1 = January)
        // 3            | Frequency            |
        // 4            | Day Of Month        |
        //
        // Yearly (the x instance y of z month) SUB pattern definition :
        // Pattern Pos    | Name                | Value Behaviour
        // 2            | Instance (INT)    |    See Instance pattern
        // 3            | Day mask (INT)    |    See Day Mask pattern
        // 4            | Month of year(INT)| Index of Month (starting 1 = January)
        // 5            | DayOfMonth        |
        //
        // Instance pattern
        // Instance (INT)    |    0 = first
        //                    |    1 = second
        //                    |    2 = third
        //                    |    3 = fourth
        //                    |    4 = last
        //
        // Day Mask pattern
        // Day mask (INT)    |    0 = day
        //                    |    1 = weekday
        //                    |    2 = weekend day
        //                    |    3 = Monday
        //                    |    4 = Tuesday
        //                    |    5 = Wednesday
        //                    |    6 = Thursday
        //                    |    7 = Friday
        //                    |    8 = Saturday
        //                    |    9 = Sunday
        //
        // Some EXAMPLE patterns:
        //
        // Daily - repeat every 3 days (1,0,3)
        // Weekly - Mon Tues Sat every 2 weeks (2,2,1,1,0,0,0,1,0)
        // Monthly - 13th of every second month (3,0,13,2)
        // Yearly - the second Monday of Jan (4,1,1,3,1)
    
        // delete old recurrence (if it existed)
    /*    $query = "delete from recurrencePatterns where account = ? and linkid = ?";
    
        $data = array(
          $arg['UserTo'], $arg['id']
        );
    
        $res = $this->db->sqldo($query, $data);*/
    
        // we got a pattern ?
        if (!empty($recurrencePattern))
        {
    
            $patternArray = explode(',', $recurrencePattern);
    
            switch ($patternArray[0])
            {
                case "1" : // Daily
                {
                    $RecurrenceType = 10;
                } break;
                case "2" : // Weekly
                {
                    $RecurrenceType = 11;
                } break;
                case "3" : // Monthly
                {
                    $RecurrenceType = 12;
                } break;
                case "4" : // Yearly
                {
                    $RecurrenceType = 13;
                } break;
            }
    
            // if the length is just one - this is a simple pattern
            if (strlen($recurrencePattern) == 1)
            {
                // simple pattern
                $RecurrenceSimplePattern = 1;
                $RecurrenceInterval = 0;
    
                switch ($patternArray[0])
                {
                    case "1" : // daily
                    {
                        // every day is valid
                        $RecurrenceInterval = 1;
                        $DayOfWeekMask = 0x7f;
                    } break;
                    case "2" : // Weekly
                    {
                        $RecurrenceType = 11;
                        $DayOfWeek = $PatternStartDate->getDayOfWeek();
    
                        switch($DayOfWeek)
                        {
                            case 0 : $DayOfWeekMask = $DayOfWeekMask | 1; break;        // sunday
                            case 1 : $DayOfWeekMask = $DayOfWeekMask | 2; break;        // monday
                            case 2 : $DayOfWeekMask = $DayOfWeekMask | 4; break;        // tuesday
                            case 3 : $DayOfWeekMask = $DayOfWeekMask | 8; break;        // wednesday
                            case 4 : $DayOfWeekMask = $DayOfWeekMask | 16; break;        // thursday
                            case 5 : $DayOfWeekMask = $DayOfWeekMask | 32; break;        // friday
                            case 6 : $DayOfWeekMask = $DayOfWeekMask | 64; break;        // saturday
                        }
                    } break;
                    case "3" : // Monthly
                    {
                        $RecurrenceType = 12;
                        $DayOfMonth = $PatternStartDate->getDay();
                    } break;
                    case "4" : // Yearly
                    {
                        $RecurrenceType = 13;
                        $DayOfMonth = $PatternStartDate->getDay();
                        $MonthOfYear = $PatternStartDate->getMonth();
                    } break;
                }
            }
            else
            {
                // argh, we are some kind of advanced pattern.
                $RecurrenceSimplePattern = 0;
    
                switch ($patternArray[0])
                {
                    case "1" : // Daily
                    {
                        switch($patternArray[1])    // Daily type
                        {
                            case "0" :
                            {
                                // Every x days
                                $RecurrenceInterval = $patternArray[2];
    
                                // every day is valid
                                $DayOfWeekMask = 0x7f;
                            } break;
                            case "1" :
                            {
                                // Every weekday
    
                                // Special 0 = weekdays only
                                $RecurrenceInterval = 0;
    
                                // Weekdays only
                                $DayOfWeekMask = 0x3e;
                            } break;
                        }
                    } break;
                    case "2" : // Weekly
                    {
                        $RecurrenceInterval        = $patternArray[1];
                        if($patternArray[8]==1)        // sunday
                            $DayOfWeekMask = $DayOfWeekMask | 1;
                        if( $patternArray[2]==1)    // monday
                            $DayOfWeekMask = $DayOfWeekMask | 2;
                        if($patternArray[3]==1)        // tuesday
                            $DayOfWeekMask = $DayOfWeekMask | 4;
                        if($patternArray[4]==1)        // wednesday
                            $DayOfWeekMask = $DayOfWeekMask | 8;
                        if($patternArray[5]==1)        // thursday
                            $DayOfWeekMask = $DayOfWeekMask | 16;
                        if($patternArray[6]==1)        // friday
                            $DayOfWeekMask = $DayOfWeekMask | 32;
                        if($patternArray[7]==1)        // saturday
                            $DayOfWeekMask = $DayOfWeekMask | 64;
                    } break;
                    case "3" : // Monthly
                    {
                        switch($patternArray[1])    // monthly type
                        {
                            case "0" : // Day x of every y months
                            {
                                $DayOfMonth            = $patternArray[2];
                                $RecurrenceInterval    = $patternArray[3];
                            } break;
                            case "1" : // The x y of every z months
                            {
                                $Instance = $patternArray[2] + 1;
    
                                if($Instance == 5)
                                {
                                    // special - this is the LAST of something.
                                    // lets store a special value
                                    // 32.
    
                                    $Instance = 32;
                                }
    
                                if($patternArray[3]==0)            $DayOfWeekMask=0x7F;    // day
                                else if($patternArray[3]==1)    $DayOfWeekMask=0x3E;    // weekday
                                else if($patternArray[3]==2)    $DayOfWeekMask=0x41;    // weekend day
                                else if($patternArray[3]==3)    $DayOfWeekMask=0x2;        // monday
                                else if($patternArray[3]==4)    $DayOfWeekMask=0x4;        // tuesday
                                else if($patternArray[3]==5)    $DayOfWeekMask=0x8;        // wednesday
                                else if($patternArray[3]==6)    $DayOfWeekMask=0x10;    // thursday
                                else if($patternArray[3]==7)    $DayOfWeekMask=0x20;    // friday
                                else if($patternArray[3]==8)    $DayOfWeekMask=0x40;    // saturday
                                else if($patternArray[3]==9)    $DayOfWeekMask=0x1;        // sunday
    
                                $RecurrenceInterval = $patternArray[4];
                            } break;
                        }
                    } break;
                    case "4" : // Yearly
                    {
                        switch($patternArray[1])
                        {
    
                            case "0" : {
                                // every x month y day
                                $MonthOfYear        = ($patternArray[2] + 1);
                                $RecurrenceInterval = $patternArray[4];
                                $DayOfMonth            = $patternArray[3];
                            } break;
                            case "1" : {
                                //$RecurrenceInterval = 1;
    
                                // the x instance y of z month
                                $Instance            = ($patternArray[2] + 1);
    
                                if($Instance == 5)
                                {
                                    // special - this is the LAST of something.
                                    // lets store a special value
                                    // 32.
    
                                    $Instance = 32;
                                }
    
                                if($patternArray[3]==0)            $DayOfWeekMask=0x7F;    // day
                                else if($patternArray[3]==1)    $DayOfWeekMask=0x3E;    // weekday
                                else if($patternArray[3]==2)    $DayOfWeekMask=0x41;    // weekend day
                                else if($patternArray[3]==3)    $DayOfWeekMask=0x2;        // monday
                                else if($patternArray[3]==4)    $DayOfWeekMask=0x4;        // tuesday
                                else if($patternArray[3]==5)    $DayOfWeekMask=0x8;        // wednesday
                                else if($patternArray[3]==6)    $DayOfWeekMask=0x10;    // thursday
                                else if($patternArray[3]==7)    $DayOfWeekMask=0x20;    // friday
                                else if($patternArray[3]==8)    $DayOfWeekMask=0x40;    // saturday
                                else if($patternArray[3]==9)    $DayOfWeekMask=0x1;        // sunday
    
                                $MonthOfYear = ($patternArray[4] + 1);
                            } break;
                        }
                    //    $var['RepeatType']="Yearly";
                    } break;
                }
            }
    
            // create a new recurrence entry
            $query = "insert into recurrencePatterns (RecurrenceType, PatternStartDate, PatternEndDate, NoEndDate, Occurences, skipInterval, DayOfWeekMask, DayOfMonth, Instance, Duration, MonthOfYear, linkid, account, IsSimple) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
    
            $data = array(
                            $RecurrenceType,
                            $PatternStartDate->getDate(),
                            $PatternEndDate->getDate(),
                            $NoEndDate,
                            $totalOccurances,
                            $RecurrenceInterval,
                            $DayOfWeekMask,
                            $DayOfMonth,
                            $Instance,
                            $Duration,
                            $MonthOfYear,
                            $linkid,
                            $account,
                            $RecurrenceSimplePattern
                        );
    
            $res = $this->db->sqldo($query, $data);
            if (DB::isError($res))
                Log::logError($res->getMessage(), __FILE__, __LINE__ - 2, QUERY_ERROR);
        } // we got a pattern ?
    
    
    }

    public static function caldav_extract_dtstamps($munchedEvent)
    {
        $dtstart = calendar::caldav_extract_dtstamp('DTSTART', $munchedEvent);
        $dtend = calendar::caldav_extract_dtstamp('DTEND', $munchedEvent);
        $dttz = calendar::caldav_extract_dttz('TZID', $munchedEvent);
        if( $dtend == null )
        {
            // we have no end date, or - we have a duration instead.
            $dtend = calendar::caldav_extract_dtstamp('DURATION', $munchedEvent);
            if( $dtstart )
            {
                $converted_duration = ical_duration_to_seconds($dtend);
                $dtend = new Date($dtstart);
                $dtend->addSpan(new Date_Span($converted_duration));
                $dtendobj = $dtend;
                $dtend = $dtend->format("%Y%m%dT%H%M%S");
            }
        }
        return array($dtstart, $dtend, $dttz);
    }
    
    /**
     * Returns the exact number of events for the specified date range.
     * 
     * @param string $start_in Start date, as yyyy-mm-dd hh:mm 
     * @param mixed $end_in End date, as yyyy-mm-dd hh:mm
     * @param string $calendarUrl
     * @return int 
     */
    public function getCount($start_in, $end_in, $calendarUrl = '')
    {

        return count($this->getRange($start_in, $end_in, '', $calendarUrl));

    }

    
    /**
     * Alias to getRange
     * 
     * @param string $start_in 
     * @param string $end_in 
     * @param string $date_alert 
     * @param string $calendarhref 
     * @param bool $displayrecurrences 
     * @return void
     */
    public function getPushRange($start_in, $end_in, $date_alert='', $calendarhref='', $displayrecurrences=true)
    {

        return $this->getRange($start_in, $end_in, $date_alert, $calendarhref, $displayrecurrences);

    }

    /**
     * Formats an Event 
     * 
     * @param Atmail_Calendar_Calendar $calendar 
     * @param array $munchedEvent 
     * @return array 
     */
    private static function getMassagedArray(Atmail_Calendar_Calendar $calendar, $munchedEvent)
    {
        $entry = array();
        $entry['id'] = $munchedEvent['VCALENDAR']['VEVENT']['UID'];
        list($dtstart, $dtend, $dttz) = calendar::caldav_extract_dtstamps($munchedEvent);

        $entry['DateStartCal'] = calendar::caldav_strip_date($dtstart);
        $entry['DateStartTime'] = calendar::caldav_strip_time($dtstart);
        $entry['DateEndCal'] = calendar::caldav_strip_date($dtend);
        $entry['DateEndTime'] = calendar::caldav_strip_time($dtend);
        $entry['TimezoneID'] = $dttz;                
        $entry['Title'] = array_key_exists('SUMMARY', $munchedEvent['VCALENDAR']['VEVENT'])? ical_decode($munchedEvent['VCALENDAR']['VEVENT']['SUMMARY']) : '';
        
        $entry['ctag'] = trim($calendar->ctag, '"');
        $entry['calendar_owner'] = $calendar->owner;
        $entry['calendar'] = $calendar->displayName;
        $entry['calendar_color'] = substr($calendar->color,0, 7);
        $entry['relative_href'] = $calendar->url;
        
        $entry['alarmSet'] = isset($munchedEvent['VALARM']);

        // websync compat patch
        $entry['Importance'] = '1';
        $entry['DateAlertPeriod'] = '0';
        $entry['Availability'] = 'Free';
        $entry['Type'] = '2';
        $entry['AllDayEvent'] = '0';
        if(isset($munchedEvent['VCALENDAR']['VEVENT']['RRULE']))
        {
            $entry['IsRecurring'] = 1;
        }
        else
        {
            $entry['IsRecurring'] = 0;
        }
        $entry['reccurenceArray'] = calendar::ical2RecurrenceArray($munchedEvent);
        $entry['recurrencePattern']    = calendar::RecurrenceArray2RecurrencePattern($entry['reccurenceArray']);
        $entry['recurrenceTime'] = '0'; //$this->generateTimePattern($var['id'], $var['shared']);
        $entry['CalMessage'] = ical_decode((isset($munchedEvent['VCALENDAR']['VEVENT']['DESCRIPTION']) ? $munchedEvent['VCALENDAR']['VEVENT']['DESCRIPTION'] : ''));
        $entry['Location'] = (isset($munchedEvent['VCALENDAR']['VEVENT']['LOCATION']) ? ical_decode($munchedEvent['VCALENDAR']['VEVENT']['LOCATION']) : '');

        $stamp = calendar::caldav_extract_dtstamp('DTSTAMP', $munchedEvent);
        $entry['DateModified'] = calendar::caldav_strip_date($stamp) . ' ' . calendar::caldav_strip_time($stamp);

        $entry['Data'] = $munchedEvent;
        $recurring = $entry['IsRecurring'];
        $entry['IsRecurring'] = 0;
        $datestart = new Date($entry['DateStartCal'] . ' ' . $entry['DateStartTime']);
        $dateend = new Date($entry['DateEndCal'] . ' ' . $entry['DateEndTime']);

        if (isset($munchedEvent['VCALENDAR']['VEVENT']['DTSTART']) && strpos($munchedEvent['VCALENDAR']['VEVENT']['DTSTART'], 'T')===false) {
            $entry['AllDayEvent'] = '1';
        }

        $entry['IsRecurring'] = $recurring;
        
        return $entry;
    }

    /**
     * getRange
     *
     * Grabs all the events for the given range from the current account.
     *
     * The $start/$end format is yyyy-mm-dd hh:mm
     * if $date_alert == 1, only records that require an alert will be 
     * returned.
     *
     * If $calendarhref is specified, only events for that particular calendar 
     * will be returned, instead of all calendars from the account.
     *
     * If displayrecurrences is set to true, all recurrence rules for events 
     * will be expanded, and separate events will be returned.
     */    
    public function getRange($start_in, $end_in, $date_alert='', $calendarhref='', $displayrecurrences=true)
    {
        $entries = array();

        // Converting to unix timestamps
        if(!is_integer($start_in)) $start_in = strtotime($start_in);
        if(!is_integer($end_in)) $end_in = strtotime($end_in);

        if ($start_in === false || $start_in=='')
		{
            $start_in = strtotime('-4 years');
		}
		if ($end_in === false || $end_in=='')
		{
			$end_in = strtotime('+4 years');
        }

        // We need a few more formats in this function
        $caldav_start = date('Ymd\\THis\\Z', $start_in);
        $caldav_end = date('Ymd\\THis\\Z', $end_in);
        $searchstart = new Date($caldav_start);
        $searchend = new Date($caldav_end);

		try
		{
	        if ($calendarhref)
			{
				$calendars = array($this->_calendarAdapter->getCalendarByUrl($calendarhref));
			}
			else
			{
				$calendars = $this->_calendarAdapter->getCalendars(); 
				$this->restartAdapter();
				
			}
		}
		catch(Exception $e)
		{
			// unable to retrieve the requested calendar
			return array();
		}
		
        foreach( $calendars as $calendar )
        {
            $events = array();
            $events = $this->_calendarAdapter->getEventsByDateRange($calendar, $start_in, $end_in);

            // 'munch' the events into a nice array
            $munchedEvents = $this->munchEvents($events);
            
            // now we must format that array into the expected xml
            foreach( $munchedEvents as $munchedEvent )
            {
                $entry = calendar::getMassagedArray($calendar, $munchedEvent);
                
                if(!isset($munchedEvent['VCALENDAR']['VEVENT']['DESCRIPTION']))
                {
                    $munchedEvent['VCALENDAR']['VEVENT']['DESCRIPTION'] = '';
                }

                $datestart = new Date($entry['DateStartCal'] . ' ' . $entry['DateStartTime']);
                $dateend = new Date($entry['DateEndCal'] . ' ' . $entry['DateEndTime']);

                $searchstart->setHour(0);
                $searchstart->setMinute(0);
                $searchstart->setSecond(0);
                $searchend->setHour(0);
                $searchend->setMinute(0);
                $searchend->setSecond(0);
                $datestart->setHour(0);
                $datestart->setMinute(0);
                $datestart->setSecond(0);
                $dateend->setHour(0);
                $dateend->setMinute(0);
                $dateend->setSecond(0);

                if( strtotime($datestart->getDate()) >= strtotime($searchstart->getDate()) && strtotime($dateend->getDate()) <= strtotime($searchend->getDate()))
                {
                    array_push($entries, $entry);
                }
                
                // generate and list recurrences
                $recurrences = ical_get_recurrences($caldav_start, $caldav_end, $entry);
                $instances = 0;
                $guid = $entry['id'];
                if ($displayrecurrences && count($recurrences) > 0)
                {
                    foreach ($recurrences as $recurrence)
                    {
                        $instances++;
                        $dtstart = $recurrence['DateStart']->format("%Y%m%dT%H%M%S");
                        $dtend = $recurrence['DateEnd']->format("%Y%m%dT%H%M%S");

                        $entry['DateStartCal'] = calendar::caldav_strip_date($dtstart);
                        $entry['DateStartTime'] = calendar::caldav_strip_time($dtstart);        
                        $entry['DateEndCal'] = calendar::caldav_strip_date($dtend);
                        $entry['DateEndTime'] = calendar::caldav_strip_time($dtend);
                        $entry['IsRecurring'] = 1;
                        $entry['id'] = $guid . '_' . $instances;
                        $datestart = new Date($entry['DateStartCal'] . ' ' . $entry['DateStartTime']);
                        $dateend = new Date($entry['DateEndCal'] . ' ' . $entry['DateEndTime']);

                        if( strtotime($datestart->getDate()) >= strtotime($searchstart->getDate()) && strtotime($dateend->getDate()) <= strtotime($searchend->getDate()) )
                        {
                            array_push($entries, $entry);
                        }
                    }
                }
            }

        }
        
        return $entries;
    }
    
    function getPrincipalStore()
    {
        return $this->_dav->Get(array('func' => CMD_PRINCIPAL_STORE, 'serverID' => $this->_dav->id));;
    }

    /**
     * Adds a list of delegates to an existing calendar
     * 
     * @param string $calendarUrl 
     * @param array $proxy_users_array List of email addresses 
     * @param string $perms 'read' or 'write'. 
     * @return void
     */
    public function add_proxy($calendarUrl, $proxy_users_array, $perms = 'write')
    {

        if (!$this->_calendarAdapter instanceof Atmail_Calendar_Adapter_IDelegation)
            throw new Atmail_Exception('The current calendaring adapter does not supported delegation');

        $calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);

		list(
            $readMembers,
            $writeMembers
        ) = $this->_calendarAdapter->getMembers($calendar);
        
        if ($perms==='write') {
            $writeMembers = array_unique(array_merge($writeMembers,$proxy_users_array));
        } else {
            $readMembers = array_unique(array_merge($readMembers, $proxy_users_array));
        }

        $this->_calendarAdapter->setMembers($calendar, $readMembers, $writeMembers);
    }

    /**
     * Deletes a delegate from a calendar
     * 
     * @param string $calendarUrl 
     * @param string $proxy_users_ email addresses 
     * @param string $perms 'read' or 'write'. 
     * @return void
     */
    public function del_proxy($calendarUrl, $proxy_user, $perms = 'write', $username = null)
    {

        if (!$this->_calendarAdapter instanceof Atmail_Calendar_Adapter_IDelegation)
            throw new Atmail_Exception('The current calendaring adapter does not supported delegation');

		$targetUri = null;
		if(stripos($calendarUrl, 'calendar-delegation') !== false)
		{
			$calendardelegateid = str_replace('/calendar/', '', str_replace('calendar-delegation/', '', trim($calendarUrl, '/')));
			$calendar = $this->_calendarAdapter->fetchDelegateFromID($calendardelegateid);
			$targetUri = $calendar['uri'];
		}
		$calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);

        list(
            $readMembers,
            $writeMembers
        ) = $this->_calendarAdapter->getMembers($calendar, $username, $targetUri);
		if ($perms==='write') {
            unset($writeMembers[array_search($proxy_user, $writeMembers)]);
        } else {
            unset($readMembers[array_search($proxy_user, $readMembers)]);
        }

		 $this->_calendarAdapter->setMembers($calendar, $readMembers, $writeMembers, $username, $targetUri);
        
    }

    /**
     * Returns a list of proxy members for a specific calendar
     *
     * @param string $calendarUrl
     * @return array
     */
    public function get_proxy($calendarUrl)
    {

        $calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
        if (!$this->_calendarAdapter instanceof Atmail_Calendar_Adapter_IDelegation)
            throw new Atmail_Exception('The current calendaring adapter does not supported delegation');
       
        list($readMembers, $writeMembers) = 
            $this->_calendarAdapter->getMembers($calendar);
            
        $result = array();
        
        foreach($readMembers as $member) {
            $result[] = array('member' => $member, 'mode' => 'read');
        }
        foreach($writeMembers as $member) {
            $result[] = array('member' => $member, 'mode' => 'write');
        }

        return $result;

    }

    /**
     * Deletes an event from the server
     * 
     * @param string $uid 
     * @param mixed $garbage 
     * @param string $calendarUrl
     * @return void
     */
    public function delete_record($uid, $garbage, $calendarUrl)
    {
        if( $uid[0] == 'p' || $uid[0] == 's' )
        {
            $uid = substr($uid, 1, strlen($uid));
        }

        $calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
        $event = $this->_calendarAdapter->getEventByUid($calendar, $uid);

        $this->_calendarAdapter->deleteEvent($event);

        return null;//findetag($this->_caldav_server, $relative_href);
    }

    /**
     * Adds an event using a plain ICS blob 
     * 
     * If the etagMatch argument is specified, it will be used as 
     * 'If-None-Match', thus preventing the double-update problem.
     *
     * @param string $ics 
     * @param mixed $garbage
     * @param string $calendarUrl
     * @param string $etagMatch
     * @return void
     */
    public function add_raw_record($ics, $garbage, $calendarUrl, $etagMatch = null)
    {

        // add a new raw ics event
        $current_date = getdate();

        $event = new Atmail_Calendar_Event();
        $event->icalendarData = $ics;

        $vCalendar = $event->getVObject();

        $uid = (string)$vCalendar->vevent->uid;
        if (!$uid)
		{
            $uid = Sabre_DAV_UUIDUtil::getUUID();
            $vCalendar->vevent->uid = $uid;    
        }

        $event->url = $calendarUrl . $uid . '.ics';
        $calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
        $this->_calendarAdapter->updateEvent($calendar, $event);
            
        return array($uid, $event->etag);

    }
    
    function get_record_modified($uid, $calendarUrl)
    {
        try
        {
            $event = $this->get_record($uid, $calendarUrl);
        }
        catch (Exception $e)
        {
            $event  = false;
        }
        return $event;
    }
    
    function get_record($uid, $calendarUrl)
    {

        $calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
        $event = $this->_calendarAdapter->getEventByUID($calendar, $uid);
        $event = $event->icalendarData;
        list($junk, $event) = explode('BEGIN:VCALENDAR', $event);
        $event = 'BEGIN:VCALENDAR' . $event;
        $event_array = array();
        $event_array['data'] = $event;
        $event_array['href'] = $uid . '.ics';
        $event_array['etag'] = '';
        $events[] = $event_array;
    
        $munchedEvents = $this->munchEvents($events);
        $munchedEvent = $munchedEvents[0];
       
        $entry = array();
        
        $stamp = calendar::caldav_extract_dtstamp('DTSTAMP', $munchedEvent);        
        $entry['DateModified'] = calendar::caldav_strip_date($stamp) . ' ' . calendar::caldav_strip_time($stamp);
        $entry['id'] = $munchedEvent['VCALENDAR']['VEVENT']['UID'];
        list($dtstart, $dtend) = calendar::caldav_extract_dtstamps($munchedEvent);

        $entry['DateStartCal'] = calendar::caldav_strip_date($dtstart);
        $entry['DateStartTime'] = calendar::caldav_strip_time($dtstart);
        $entry['DateEndCal'] = calendar::caldav_strip_date($dtend);
        $entry['DateEndTime'] = calendar::caldav_strip_time($dtend);
        $entry['timezone'] = $this->caldav_extract_dttz(null, $munchedEvent);
        
        $entry['Title'] = ical_decode($munchedEvent['VCALENDAR']['VEVENT']['SUMMARY']);
        if (isset($munchedEvent['VCALENDAR']['VEVENT']['LOCATION']))
        {
            $entry['Location'] = ical_decode($munchedEvent['VCALENDAR']['VEVENT']['LOCATION']);
        }
        else
        {
            $entry['Location'] = '';    
        }
        if(!isset($munchedEvent['VCALENDAR']['VEVENT']['DESCRIPTION']))
        {
            $munchedEvent['VCALENDAR']['VEVENT']['DESCRIPTION'] = '';
        }

        $entry['CalMessage'] = ical_decode($munchedEvent['VCALENDAR']['VEVENT']['DESCRIPTION']);
        
        $entry['recurrenceArray'] = calendar::ical2RecurrenceArray($munchedEvent);
//        $entry['alertArray'] = calendar::ical2RecurrenceArray($munchedEvent);
        
        return $entry;
    }
    
    /**
     * NOTHING!
     * 
     * @return void
     */
    public function updateIcs() {
        // This does nothing anymore
    }
    
    /**
     * Adds a new event to the calendar.
     *
     * @param bool $task Must be false 
     * @param string $title Title of the event 
     * @param string $description Description of the event
     * @param string|int $dateStart Either a unix timestamp or yyyy-mm-dd hh:mm
     * @param string|int $dateEnd Either a unix timestamp or yyyy-mm-dd hh:mm 
     * @param string $location Location of the event 
     * @param bool $allDayEvent Wether or not it's an all day event. 
     * @param mixed $calendarUrl - Url to the calendar
     * @param string $timezone - Timezone identifier 
     * @return void
     */
    function add_record( $args_in )
	{    
	
		$args = array();
    	foreach($args_in as $k => $v)
    	{
    		$args[strtolower($k)] = $v;
    	}

        if (isset($args['task']) && $args['task'] == true)
            throw new InvalidArgumentException('Tasks are no longer supported');

        $vCalendar = new Sabre_VObject_Component('VCALENDAR');
        $vCalendar->version = "2.0";
        $vCalendar->prodid = self::ICAL_PRODID;
        $vCalendar->calscale = 'GREGORIAN';

        $vEvent = new Sabre_VObject_Component('VEVENT');
        $vCalendar->vEvent = $vEvent;

        $vEvent->sequence = 1;
        $vEvent->trans = 'OPAQUE';

        $uuid = Sabre_DAV_UUIDUtil::getUUID();
        $vEvent->uid = $uuid;
        
        if ($args['alldayevent']) {
            $vEvent->dtStart = substr(ical_datetime($args['datestart']),0,8);
            $vEvent->dtStart['VALUE'] = 'date';

            // For all day events, we default to simply 1 day events.
            $vEvent->dtEnd = is_numeric($args['datestart']) ?
				substr(ical_datetime(strtotime('+1 day', $args['datestart'])), 0, 8) :
				substr(ical_datetime(strtotime($args['datestart'] . ' +1 day')), 0, 8);
        } else {
            $vEvent->dtStart = ical_datetime($args['datestart']);
            $vEvent->dtEnd = ical_datetime($args['dateend']);
        }

        $vEvent->summary = $args['title']?$args['title']:'';
        $vEvent->description = $args['description']?$args['description']:'';
        if( $args['location'] )
			$vEvent->location = $args['location'];
        $vEvent->dtstamp = ical_datetime(time(), "zulu") . 'Z';
        $status = 0;
    

        $current_date = getdate();

        if(array_key_exists('timezone', $args) && $args['timezone'] != '')
        {
            $timezone = $args['timezone'];
            // timezone
            $vCalendar->vtimezone = Sabre_VObject_Reader::read($this->getVTIMEZONE($timezone));
            if (isset($vEvent->dtstart))
            {
                $vEvent->dtstart['tzid'] = $timezone;
            }
            if (isset($vEvent->dtend))
            {
                $vEvent->dtend['tzid'] = $timezone;
            }
        }
        
        $event = new Atmail_Calendar_Event();
        $event->icalendarData = $vCalendar->serialize();
        $event->url = $args['calendarurl'] . '/' . $uuid . '.ics';

		return $this->add($args, $event);
		
    }

	public function add( $args, Atmail_Calendar_Event $event ) 
	{

		//event must be ready to add at this point (this is a utility method to blindly add what it is given)
		$vCalendar = $event->getVObject();
		$uid = $vCalendar->vevent->uid->value;
		if( !isset($uid) || $uid == null || $uid == false || $uid == '' )
			throw new Exception(__METHOD__ . "#" . __LINE__ . ": uid missing from supplied event.");
			
		$returnCode = 201;
		try
		{

	        $result = $this->_calendarAdapter->createEvent( $this->_calendarAdapter->getCalendarByUrl($args['calendarurl']), $event);
	
		}
		catch(Exception $e)
		{

			$returnCode = 413; //Request Entity Too Large

		}
		return array($uid, $returnCode);
		
	}
	
	private function getAccountEmailAddress()
	{
		
		$this->UserSettings = Zend_Registry::get('UserSettings');
		if( strpos($this->UserSettings['Account'], '@') !== false )
			$this->myEmailAddress = $this->UserSettings['Account'];
		else
		{

			$configDovecot = Zend_Registry::get('config')->dovecot;
			if( !isset($configDovecot['default_domain']) || $configDovecot['default_domain'] == '' )
				throw new Exception( $this->view->translate('Unable to send, Administrator needs to set Default Domain in Admin > Services > POP3/IMAP') );
			$this->myEmailAddress = $this->UserSettings['Account'] . '@' . $configDovecot['default_domain'];

		}
		return $this->myEmailAddress;
		
	}
	
	public function handleIncomingEventAction($args, Atmail_Calendar_Event $event)
	{
		
		//takes user action (accept, tentative, decline), updates ics with my decition, adds it to my calendar (home for now) and then emails correctly formatted response to orginator
		if( !array_key_exists('responseStatus', $args) )
			throw new Exception('responseStatus field missing');
		//modify event with my action taken
		//N.B. Assuming this method will always be called with a valid user app context (depends on external data)
		$myEmailAddress = $this->getAccountEmailAddress();
		
		//Invite leg
		//ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Qurban Miggeil:MAILTO:qurban@miggeil.com
		//Accepted leg
		//ATTENDEE;CN="Qurban Miggeil";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT:MAILTO:qurban@miggeil.com
		if( $args['responseStatus'] == 'accept' )
		{
			$partstat = "ACCEPTED";
			$htmlResponseText = "Accepted";
		}	
		else if( $args['responseStatus'] == 'tentative' )
		{
			$partstat = "TENTATIVE";
			$htmlResponseText = "Tentatively accepted";
		}
        else if( $args['responseStatus'] == 'update' )
        {
            $htmlResponseText = "Updated";
        }
		else
		{
			$partstat = "DECLINED";
			$htmlResponseText = "Declined";
		}
		$vCalendar = $event->getVObject();	
//Zend_Registry::get('log')->debug( "\n\n" . print_r($vCalendar->vevent->attendee,true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$vCalendar->vevent->attendee: \n\n");


		foreach($vCalendar->vevent->attendee as $attendee)
		{

			//if me then update my row with new status
			if( strpos(strtolower($attendee->value), "mailto:" . $myEmailAddress) !== false )
			{

				//search parameters for fields that need to be changed
				foreach( $attendee->parameters as $k => &$parameter )
				{

					//find PARTSTAT and set it to $partstat
					if( $parameter->name == 'PARTSTAT' )
						$attendee->parameters[$k]->value = $partstat;
					//find RSVP and remove it
					if( strtoupper($parameter->name) == 'RSVP' || strtoupper($parameter->name) == 'CUTYPE' )
						unset($attendee->parameters[$k]);

				}
				//add CUTYPE=INDIVIDUAL
				$attendee->add("cutype", 'INDIVIDUAL');

			}
			else
			{

				//search parameters for fields that need to be changed
				foreach( $attendee->parameters as $k => &$parameter )
				{

					//find RSVP and remove it
					if( strtoupper($parameter->name) == 'RSVP' )
						unset($attendee->parameters[$k]);

				}

			}

		}
		$uid = (string)$vCalendar->vevent->uid;
		//save current vEvent to my calendar (if exists, then replace) then mod event to METHOD:REPLY and send email response
		unset($this->foundInEvent);
		unset($this->foundInCalendar);
		$found = $this->uidInMyCalendars($uid);
		if( $found )
		{
			
			$calendarUrl = $this->foundInCalendar->url;
			$result = $this->delete_record( $uid, false, $calendarUrl );
			
		}
		else
		{
			
			//if not found, prepare to add to home calendar (for now)
			$homeCalendar = $this->getCalNames(array('home' => 1));
			$calendarUrl = $homeCalendar[0]['relative_href'];
			
		}
		
		//drop the method field (not relevant for storage, only in exchanges with 3rd parties)
		unset($vCalendar->method);
		//add updated ICS to my calendar, then change to appropriate format for reply
		$event2 = new Atmail_Calendar_Event();
        $event2->icalendarData = $vCalendar->serialize();
        $event2->url = $calendarUrl . $uid . '.ics'; //TODO: eliminate double slashes //'/' . 

		$result = $this->add(array('calendarurl' => $calendarUrl), $event2);
		//add METHOD:REPLY and send email to originator
		$vCalendar2 = $event2->getVObject();
		$vCalendar2->add("method", "REPLY");
		$this->_globalConfig = Zend_Registry::get('config')->global;
		$vCalendar2->prodid->value = "Atmail Calendar Server" . $this->_globalConfig['version'];
		//remove all other attendee headers except my own for reply.
		$veventFieldCount = count($vCalendar2->vevent->attendee);
		
		foreach($vCalendar2->vevent->attendee as $k => $attendee)
		{

			if( strpos(strtolower($attendee->value), $myEmailAddress) === false )
			{
				
				$secureRandomRenameForDelete = "TODELETE" . round(microtime(true)*1000) . "X";
				$vCalendar2->vevent->attendee[$k]->name = $secureRandomRenameForDelete;
				unset($vCalendar2->vevent->{$secureRandomRenameForDelete});
				
			}
			else
			{
				
				//search parameters for fields that need to be changed
				foreach( $attendee->parameters as $k => &$parameter )
				{
					
					//find RSVP and remove it
					if( $parameter->name == 'RSVP' )
						unset($attendee->parameters[$k]);
					//find CUTYPE and remove it (SabreDAV seems to loose cal events somehow if this field left in - undocumented and rule not yet found in RFC)
					if( $parameter->name == 'CUTYPE' )
						unset($attendee->parameters[$k]);
					
				}
				
			}

		}
		$organizer = $vCalendar2->vevent->organizer->value;
		if( strpos(strtoupper($organizer), 'MAILTO:') === 0 )
			$organizer = substr($organizer, 7);
		$title = $vCalendar2->vevent->summary?$vCalendar2->vevent->summary:($vCalendar2->vevent->description?$vCalendar2->vevent->description:"");
        if(is_object($title))$title = $title->value;
        $title = trim($title);
		if( $title !== '' )
			$title = "Calendar invite response for \"" . $title . "\"" . " has been " . $htmlResponseText;
		else
			$title = "Calendar invite response.";
			
		$htmlBody = <<<EOF
<div class="invitation" style="background:#ECECEC;padding:30px;"><div class="invite-center" style="background:#fff;max-width:600px;margin:0 auto;border: 1px solid #ccc;padding:20px;"><div class="invite-text" style="padding:20px 100px;"><span style="font-weight:bold;display:block;font-size:17px;text-align:center;">{$title}</span>

</div>
</div></div>
EOF;
		
		$emailICSToRecipientsResult = $this->emailICSToRecipients($title, $htmlBody, array($organizer), $vCalendar2);
		return true;
		
	}
	
	private function emailICSToRecipients($title, $htmlEmailBody, array $recipientsArray, Sabre_VObject_Component $vCalendar)
	{

		//N.B. this method depends on valid user application state (logged in and hit registry properly populated)
		$noErrorYet = true;
		$this->_globalConfig = Zend_Registry::get('config')->global;
		if( !empty($this->_globalConfig['smtpauth_username']) && !empty($this->_globalConfig['smtpauth_password']) && !empty($this->_globalConfig['smtp_auth']) )
			$config = array( 'auth' => 'login', 'username' => $this->_globalConfig['smtpauth_username'], 'password' => $this->_globalConfig['smtpauth_password'] );
		else
			$config = array( );
		//$transportClass = $this->_helper->pluginCall("smtpTransportClass");
		if( empty($transportClass) )
			$transportClass = "Atmail_Mail_Transport_Smtp";
		$transport = new $transportClass($this->_globalConfig['smtphost'], $config);
		//$this->_helper->pluginCall("postCreateSmtpTransport", $transport);

		$newMessage = new Atmail_Mail( 'UTF-8' );
		$newMessage->setMessageId(true);
		$messageParams = array();
		if( strpos($this->UserSettings['Account'], '@') !== false )
			$messageParams['fromAddress'] = $this->UserSettings['Account'];
		else
		{

			$configDovecot = Zend_Registry::get('config')->dovecot;
			if( !isset($configDovecot['default_domain']) || $configDovecot['default_domain'] == '' )
				throw new Exception( $this->view->translate('Unable to send, Administrator needs to set Default Domain in Admin > Services > POP3/IMAP') );
			$messageParams['fromAddress'] = $this->UserSettings['Account'] . '@' . $configDovecot['default_domain'];

		}

		if( isset($this->UserSettings['RealName']) && strlen($this->UserSettings['RealName']) > 0 )
		{

			$messageParams['fromRealname'] = $this->UserSettings['RealName'];
			$messageParams['fromRealname'] = trim($messageParams['fromRealname'], " \t\n\r\0\x0B");

		}
		else
			$messageParams['fromRealname'] = '';

		if( strlen($messageParams['fromRealname']) > 0 )
			$newMessage->setFrom( $messageParams['fromAddress'], $messageParams['fromRealname'] );
		else
			$newMessage->setFrom( $messageParams['fromAddress']);

		if( isset($this->UserSettings['ReplyTo']) && strlen($this->UserSettings['ReplyTo']) > 0 )
			$messageParams['replyToAddress'] = $this->UserSettings['ReplyTo'];
		else
			$messageParams['replyToAddress'] = '';

		if( strlen( $messageParams['replyToAddress']) > 0 )
		{

			if( strlen($messageParams['fromRealname']) > 0 )
				$newMessage->setReplyTo( $messageParams['replyToAddress'], $messageParams['fromRealname'] );
			else
				$newMessage->setReplyTo( $messageParams['replyToAddress']);

		}

		$finalRecipients = array();
		$rcptLog = array();
		$addressObjectsArray = getProcessedRecipientObjects( implode(", ", $recipientsArray) );
		foreach ($addressObjectsArray as $addressObject)
		{

			$personal = '';
			if( is_string($addressObject) )
			{

				preg_match('/(.*?)<(.*?)>/', $addressObject, $m);
				$personal = trim($m[1]);
				$mail = $m[2];

			}
			else
			{

				$personal = $addressObject->personalUTF8;
				$mail = $addressObject->address;

			}
			$finalRecipients[] = array("personal" => strlen($personal)>0?$personal:null, "emailAddress" => $mail);
			$rcptLog[] = $mail;

		}
		// Set the X-Mailer to the Atmail version
		$xMailer = "Atmail";
		$newMessage->addHeader('X-Mailer', $xMailer . " " . $this->_globalConfig['version'] );
		$newMessage->addHeader('X-Originating-IP', $_SERVER['REMOTE_ADDR']);

		$newMessage->setSubject( $title );// . " scheduled for " . $eventArray['DateStartCal'] . " " . $eventArray['DateStartTime'] );

		require_once('class.html2text.inc');
		$html2text = new html2text($htmlEmailBody);
		$emailBodyText = $html2text->get_text();
		$newMessage->setBodyText($emailBodyText);
		if( !preg_match("'<body[^>]*>(.*?)</body>'i", $htmlEmailBody ) )
			$htmlEmailBody = '<body>' . $htmlEmailBody . '</body>';
		if( !preg_match("'<html[^>]*>(.*?)</html>'i", $htmlEmailBody ) )
			$htmlEmailBody = '<html>' . $htmlEmailBody . '</html>';
		$htmlEmailBody = str_replace('<body>', '<body style="font-family: \'Helvetica Neue\',Helvetica,Arial,sans-serif; font-size: 12px;">', $htmlEmailBody);
		$newMessage->setBodyHtml($htmlEmailBody);
		$uid = $vCalendar->vevent->uid;
        if(is_object($uid)) $uid = $uid->value;
		if( !$uid ) 
			throw new Exception(__METHOD__ . "#" . __LINE__ . ": Fatal: UID missing from ICS.");
		$attachmentFilename = $uid . ".ics";
		$icsString = $vCalendar->serialize();
		$newMessage->createAttachment($icsString,"text/calendar",Zend_Mime::DISPOSITION_ATTACHMENT,Zend_Mime::ENCODING_QUOTEDPRINTABLE, $attachmentFilename);

		// Send the message to each recipient so that we can build up a success fail list
		$successArray = array();
		$failArray = array();
		foreach( $finalRecipients as $recipientArray )
		{

			$newMessage->clearRecipients();
			$newMessage->addTo($recipientArray['emailAddress'], $recipientArray['personal'] );
			try
			{

				//$this->_helper->pluginCall("preEmailSend", $newMessage);
				$newMessage->send($transport);

				// If we are running the Webmail client, log the action to the DB - If server, the log-daemon process will log the action for us
				if($this->_globalConfig['install_type'] != 'server')
				{

					require_once('log.php');
					$log = new logEntry($this->UserSettings['Account']);
					foreach($rcptLog as $rcpt)
						$log->insert("Log_SendMail", array('LogIP' => $_SERVER['REMOTE_ADDR'], 'EmailTo' => $rcpt, 'Account' => $this->UserSettings['Account']));

				}
				$successArray[] = $recipientArray['personal']!=null?$recipientArray['personal']:$recipientArray['emailAddress'];

			}
			catch (Exception $e)
			{

				//$this->_helper->pluginCall("emailSendFailed", $e);
				// Check if a plugin has given a custom error message
				if (!empty($this->emailSendFailedError))
					$error = $this->emailSendFailedError;
				else 
				{

					$error = $e->getMessage();
					if( strpos($error, 'Blocked') !== false )
						$error = $this->view->translate('The server has rejected the email address. If you feel this address is legitimate please contact your administrator.');
					else if( $error == 'No recipient forward path has been supplied' )
						$error = $this->view->translate('Email address not valid. Please use an email address which looks like user@example.com');
				}
				$failArray[] = $recipientArray['personal']!=null?$recipientArray['personal']:$recipientArray['emailAddress'];

			}

		}
		$responseMessage = '';
		//if( count($successArray) > 0 )
		//	$responseMessage = "Successfully sent invite to " . implode(", ", $successArray) . ". ";
		if( count($failArray) > 0 )
			$responseMessage .= "Event saved, but failed sending invite to " . implode(", ", $failArray) . ". ";
		return array("result" => "Complete", "message" => $responseMessage, "status" => true);

	}

	private function cleanEmailAddresses( $rawRecipientsString )
	{

		$finalRecipients = array();
		foreach( getProcessedRecipientObjects($rawRecipientsString) as $addressObject)
		{

			$personal = '';
			if( is_string($addressObject) )
			{

				preg_match('/(.*?)<(.*?)>/', $addressObject, $m);
				$personal = trim($m[1]);
				$mail = $m[2];

			}
			else
			{

				$personal = $addressObject->personalUTF8;
				$mail = $addressObject->address;

			}
			$finalRecipients[] = (strlen($personal)>0?$personal . ' <':'') . $mail . (strlen($personal)>0?'>':'');

		}
		return $finalRecipients;

	}
	

    /**
     * Nothing!
     * 
     * @return void
     */
    public function updateUserQuota() {
    }

    function actual_time($offset,$timestamp)
    {
        $offset = $offset*60*60;
        $timestamp = $timestamp + $offset;
        return gmdate($timestamp);
    }

    public static function getZoneInfoListRecursive( $prefix='', $dir=null, $oldtz = false )
    {
        $rootRecursive = false;
        if($oldtz === false)
        {
            $oldtz = date_default_timezone_get();
            $rootRecursive = true;
        }

        $results = array();

        if($dir == null)
        {
            $dir = realpath('.') . "/library/Atmail/zoneinfo/";
        }
        $dir = rtrim($dir, "/") . '/';
        $scanResults = scandir($dir);
        foreach($scanResults as $test)
        {
            if($test != '.' && $test != '..' && $test[0] != '.')
            {
                if(is_dir($dir . $test))
                {
                    $results = array_merge($results, self::getZoneInfoListRecursive($test . '/', $dir . $test, $oldtz));
                }
                else
                {
                    // test the timezone is valid for this machine
                    $actualprefix = str_replace(realpath('.') . "/library/Atmail/zoneinfo/", "", $dir . $test);
                    if(@date_default_timezone_set(str_replace('.ics', '', $actualprefix)) == true)
                    {
                        $results[] = str_replace('.ics', '', $actualprefix);
                    }
                }
            }
        }

        if($rootRecursive) date_default_timezone_set($oldtz);

        return $results;
    }

    public static function getZoneInfoList( $prefix='', $dir=null )
    {
        $results = array();

        $tzList = @file_get_contents(realpath('.') . "/library/Atmail/zoneinfo/tz.cache");

        if($tzList === false || $tzList == "")
        {
            $results = self::getZoneInfoListRecursive($prefix, $dir);
            @file_put_contents(realpath('.') . "/library/Atmail/zoneinfo/tz.cache", implode("\n", $results));
        }
        else
        {
            $results = explode("\n", $tzList);
        }

        return $results;
    }
    
    static function getVTIMEZONE( $tzid )
    {

        // timezone ids are normally separated via "/" however, we really shouldn't assume his
        // is the path separator for the oslon data zoneinfo.
        //
        // we will try :
        // COUNTRY/AREA
        // COUNTRYAREA
        // AREA
        // RAWTZID
        // COUNTRY
        
        $tzinfo = false;
        
        $zoneinfoDir = APP_ROOT . "library/Atmail/zoneinfo/";
		if( strtolower($tzid) == 'none' )
			return "";
			
        if(strpos($tzid, '/') != -1)
        {
            // maybe path sep, lets try that first (most likely)
            list($country, $state, $area) = explode('/', $tzid);
            $country = preg_replace("/[^a-zA-Z0-9\_\-\+]+/", "", $country);
            $state = preg_replace("/[^a-zA-Z0-9\_\-\+]+/", "", $state);
            $area = preg_replace("/[^a-zA-Z0-9\_\-\+]+/", "", $area);

            if($area == "" && $state != "") $area = $state;

            if( file_exists( $zoneinfoDir . $country . "/" . $state . "/" . $area . '.ics') )
            {
                $tzinfo = file_get_contents( $zoneinfoDir . $country . "/" . $state . "/" . $area . '.ics');
            }
            else if( file_exists( $zoneinfoDir . $state . "/" . $area . '.ics') )
            {
                $tzinfo = file_get_contents( $zoneinfoDir . $state . "/" . $area . '.ics');
            }
            else if( file_exists( $zoneinfoDir . $country . "/" . $state . '.ics') )
            {
                $tzinfo = file_get_contents( $zoneinfoDir . $country . "/" . $state . '.ics');
            }
            else if( file_exists( $zoneinfoDir . $country . $area . '.ics' ) )
            {
                $tzinfo = file_get_contents( $zoneinfoDir . $country . $area . '.ics');
            }
            else if( file_exists( $zoneinfoDir . $area . '.ics' ) )
            {
                $tzinfo = file_get_contents( $zoneinfoDir . $area . '.ics');
            }
            else if( file_exists( $zoneinfoDir . $country . $area . '.ics' ) )
            {
                $tzinfo = file_get_contents( $zoneinfoDir . $country . "/" . $area . '.ics');
            }
            else if ( !is_dir( $zoneinfoDir ) )
            {
                // no zoneinformation directory !!
                throw new Exception("Unable to read zoneinfo directory.");
            }
            
        }
        if( !$tzinfo )
        {
            $tzid = preg_replace("/[^a-zA-Z0-9\-\+\/]+/", "", $tzid);

            // still have not found tz info, let search around a bit
            if( file_exists( realpath('.') . $zoneinfoDir . $tzid . '.ics' ) )
            {
                $tzinfo = file_get_contents(realpath('.') . $zoneinfoDir . $tzid . '.ics');
            }
        }
        $tzinfo = substr( $tzinfo, strpos( $tzinfo, "BEGIN:VTIMEZONE"), strpos( $tzinfo, "END:VCALENDAR") - strpos( $tzinfo, "BEGIN:VTIMEZONE"));
        
        return $tzinfo;
    }
    

    /**
     * Updates an event.
     *
     * Most of these arguments may be left to 'false' if that part of the event 
     * is not to be updated. 
     *
     * @param string $title The new title of the event
     * @param string|int $dateStart The start date
     * @param string|int $dateEnd the end date
     * @param string $uid UID of the event to update. 
     * @param string $description Description of the event
     * @param string $location Location of the event
     * @param string $category Category of the event
     * @param string $availibility Availability of the event
     * @param bool $task Whether it's a todo or not. Will throw an error if set 
     *                   to true.
     * @param int $dateStamp Creation date
     * @param mixed $recurrenceType
     * @param mixed $recurrenceEndType
     * @param mixed $recurrenceEndByDate
     * @param mixed $recurrenceEndAfterX
     * @param string $calendarUrl Url to calendar 
     * @param string $alertType (hour, minute, etc.)
     * @param string $alertTrigger (offset (5, 10, 15, etc))
     * @param string $alertPosition ('Before' or 'After')
     * @param string $recurrenceInterval (2 for every 2 days, years, etc.)
     * @param string $timezone (Timezone identifier)
     * @param bool $preserveOriginalTZ (Don't touch the timezone)
     * @return void 
     */
    public function update_record( $args_in )
    {
       	$args = array();
    	foreach($args_in as $k => $v)
    	{
    		$args[strtolower($k)] = $v;
    	}
    	
        $status = 0;
        
    	if (!isset($args['uid']))
    		throw new Atmail_Exception('UID must be specified');
        if (!isset($args['calendarurl'])) 
            throw new Atmail_Exception('The calendarUrl (relative_href) argument must be specified');


		// ******************************** CHECK EVENT ID AND URL / GRAB EVENT *************************** //
		$uid = $args['uid'];
    	$calendarUrl = isset($args['calendarurl']) ? $args['calendarurl'] : '';

		$calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
        $event = $this->_calendarAdapter->getEventByUid($calendar, $uid);       
        $vCalendar = Sabre_VObject_Reader::read($event->icalendarData);
        $vEvent = $vCalendar->vevent[0];
        
        // bump sequence number, date stamp and last modified
		$current_date = getdate();
		$vEvent->sequence = (isset($vEvent->sequence) ? (string)$vEvent->sequence + 1 : 1);
        $vEvent->dtstamp = ical_datetime($current_date[0],'zulu') . 'Z';
        $vEvent->{"last-modified"} = ical_datetime($current_date[0],'zulu') . 'Z';

		
		// ******************************* UPDATE TEXT FIELDS ******************************************** //
		if( isset($args['title']) )
		{
            $vEvent->summary = trim(trim($args['title'],'"'));
		}
        if( isset($args['description']) )
        {
            $vEvent->description = trim(trim($args['description'], '"'));
		}
        if( isset($args['location']) )
        {
            $vEvent->location = trim(trim($args['location'],'"'));
		}		
        if( isset($args['category']) )
        {
            $vEvent->category = trim(trim($args['category'],'"'));
		}
        if( isset($args['availablity']) )
        {
            $vEvent->availability = trim(trim($args['availablity'], '"'));
        }
		
		// ******************************* DATE / TIMEZONE CALC AND TRANSLATION ********************************** //
		$dateStart = is_numeric($args['datestart']) ? gmdate("Y-m-d H:i:s", $args['datestart']) : $args['datestart'];	// UTC/GMT ONLY
		$dateEnd = is_numeric($args['dateend']) ? gmdate("Y-m-d H:i:s", $args['dateend']) : $args['dateend'];		// UTC/GMT ONLY
        $allDayEvent = ( 
            $dateStart != null && $dateStart != ''
            && $dateEnd != null && $dateEnd != ''
            && strpos(ical_datetime(date(strtotime($dateStart))), 'T000000') != false
            && strpos(ical_datetime(date(strtotime($dateEnd))), 'T000000') != false
        ) ? true : false;
		$timezone = isset($args['timezone']) ? $args['timezone'] : null;
		$preserveOriginalTZ = !isset($args['timezone']);
		$current_timezone = isset($vCalendar->vtimezone) ? (string)$vCalendar->vtimezone[0]->tzid->value : '';

		if($preserveOriginalTZ && $current_timezone != '')
		{		
			// translate the start/end dates based on users timezone.
			$timezone = $current_timezone != 'None' ? (string)$vCalendar->vtimezone[0]->tzid->value : null;
		
			$current_php_timezone = date_default_timezone_get();
			date_default_timezone_set($current_timezone);
			
			$tz = new DateTimeZone($this->user_timezone);
            $timezone_translation_date = new DateTime($dateStart, $tz);
			$dateStart = $timezone_translation_date->format('Y-m-d H:i:s');
			
            $timezone_translation_date = new DateTime($dateEnd, $tz);
			$dateEnd = $timezone_translation_date->format('Y-m-d H:i:s');
			
			date_default_timezone_set($current_php_timezone);
		}
		
		if(isset($args['timezone']))
		{
			unset($vCalendar->vtimezone);
			if (isset($vEvent->dtstart))
			{
				unset($vEvent->dtstart['tzid']);
			}
			if (isset($vEvent->dtend))
			{
				unset($vEvent->dtend['tzid']);
			}
			if($args['timezone'] != '')
			{
				// timezone
				$vCalendar->vtimezone = Sabre_VObject_Reader::read($this->getVTIMEZONE($timezone));
				if (isset($vEvent->dtstart))
				{
					$vEvent->dtstart['tzid'] = $timezone;
				}
				if (isset($vEvent->dtend))
				{
					$vEvent->dtend['tzid'] = $timezone;
				}
			}
		}
        
        if( $dateStart) {
            if ($allDayEvent) {
                $vEvent->dtstart->value = substr(ical_datetime(strtotime($dateStart)), 0, 8);
                $vEvent->dtstart['value'] = 'DATE';
            } else {
                $vEvent->dtstart->value = ical_datetime(strtotime($dateStart));
                unset($vEvent->dtstart['value']);
            }
        }
        if ($dateEnd) {
        	// If it was specified as DURATION; we'll use that
            if (isset($vEvent->duration)) {
                $vEvent->duration = seconds_to_ical_duration(strtotime($dateEnd) - strtotime($dateStart));
                
                 
            // Otherwise we'll specify it as DTEND instead. 
            } else {
            	if ($allDayEvent) {
                    $vEvent->dtend->value = substr(ical_datetime(strtotime($dateEnd)), 0, 8);
                    $vEvent->dtend['value'] = 'DATE';
                } else {
                    $vEvent->dtend->value = ical_datetime(strtotime($dateEnd));
                    unset($vEvent->dtend['value']);
                }

            }
        }

        // ******************************** EVENT ALARM ********************************************* //
        $alertType = isset($args['alerttype']) ? $args['alerttype'] : '';
		$alertTrigger = isset($args['alerttrigger']) ? $args['alerttrigger'] : '';
		$alertPosition = isset($args['alertposition']) ? $args['alertposition'] : '';

        $foundcal = null;
        
        if(isset($args['alerttype']) && $args['alerttype'] != '' && $args['alerttype'] != 'None')
        {
            if (isset($vEvent->valarm))
            {
                unset($vEvent->valarm);
            }

            $vEvent->add($this->caldav_set_alarm_trigger(
            	isset($args['alerttrigger']) ? $args['alerttrigger'] : '',
            	$args['alerttype'], 
            	isset($args['alertposition']) ? $args['alertposition'] : '',
            	$args['title']));
        }
        else if ($args['alerttype'] = 'None')
        {
            if (isset($vEvent->valarm))
            {
                unset($vEvent->valarm);
            }
        }
        
		// ****************************** EVENT RECURRENCES **************************** //
        if( isset($args['recurrencetype']) && $args['recurrencetype'] != '0')
        {
            $recurrenceArray = array(
                'RecurrenceType' => isset($args['recurrencetype']) ? $args['recurrencetype'] : 0,
                'RecurrenceEndType' => isset($args['recurrenceendtype']) ? $args['recurrenceendtype'] : 0,
                'Until' => isset($args['recurrenceendbydate']) ? $args['recurrenceendbydate'] : 0,
                'Occurences' => isset($args['recurrenceendafterx']) ? $args['recurrenceendafterx'] : 0,
                'skipInterval' => isset($args['recurrenceinterval']) ? $args['recurrenceinterval'] : 1,
            );
                
            $recurrence = $this->RecurrenceArray2ical($recurrenceArray);

            if ($recurrence) {
                foreach($recurrence as $k=>$v) {
                    $vEvent->$k = $v;
                }
            }
        }
        else
        {
            if(isset($vEvent->RRULE))
            {
                unset($vEvent->RRULE);
                unset($vEvent->RRULE2);
            }
        }


		$returnCode = 200;
		try
		{
	        $event->icalendarData = $vCalendar->serialize();
			$this->_calendarAdapter->updateEvent($calendar, $event);
		}
		catch(Exception $e)
		{
			$returnCode = 413;
		}
        return $returnCode;
    }

    /**
     * Fetches a single event
     *
     * @param string $uid The uid of the event 
     * @param string $calendarUrl
     * @return array 
     */
    public function read_record($uid, $calendarUrl)
    {

        if (!$calendarUrl) {
            throw new Atmail_Exception('The calendarUrl (relative_href) must be supplied');
        }

        $calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
        $events = array($this->_calendarAdapter->getEventByUid($calendar, $uid));

        // 'munch' the events into a nice array
        $munchedEvents = $this->munchEvents($events);
        // should only be one event here !
        $event = $munchedEvents[0];
        $event['Permissions'] = 1;
        
        return $event;
    }

    function search_record($account)
    {        

        $existingUser = $this->_dbAdapter
            ->select()
            ->from('UserSession')
            ->where('Account = ' . $this->_dbAdapter->quote($account))
            ->query()
            ->fetchAll();

        if( count($existingUser) > 0 )
        {
            return true;
        }

        return false;
    }

    function make_principal_calendar($new_user, $caldav_server)
    {

$xml = <<<EOXML
<?xml version="1.0" encoding="UTF-8" ?>
<x0:mkcalendar xmlns:x0="urn:ietf:params:xml:ns:caldav" xmlns:x1="DAV:" xmlns:x2="http://apple.com/ns/ical/">
<x1:set>
<x1:prop>
<x1:displayname>Untitled</x1:displayname>
</x1:prop>
</x1:set>
</x0:mkcalendar>
EOXML;
        $caldav_server->DoXMLRequest( "MKCALENDAR", $xml, $caldav_server->user . '/' . $new_user . '/');
        return $caldav_server->user . '/' . $new_user . '/';
    }

    function add_group_user($username, $password)
    {
        $status = true;

        //lets see the group exists already
        $existingUser = $this->_dbAdapter->fetchOne("select Account from UserSession where Account = ?", array($username));
        if( empty($existingUser))
        {
            // it exists, but lets make sure the password is up to date.
            $this->_dbAdapter->insert('UserSession', array('Account' => $username, 'Password' => $password, 'CalUser' => '1'));
            users::changePassword($username, $password);
        }
        else
        {
            // user doesn't exist
            $status=false;
        }
        
        users::changePassword($username, $password);

         // dav controller
        $davConfiguration = array('protocol' => CALDAV_SERVER, 'username' => $username, 'password' => $password, 'uri' => $this->_caldav_URL);
        $provision_dav = new Atmail_Dav($davConfiguration);
        // perform query to ensure provisioning of user completed.
        $new_principal = $provision_dav->GetCurrentPrincipal();
        unset($provision_dav);
        
        return $new_principal;
    }

    function delete_group_user($username, $password)
    {
        users::deleteUser($username);        
        return true;
    }

    function get_server_type()
    {
        return $this->getServerType();
    }

    /**
     * Creates a new calendar
     *
     * @param string $account Ignored - using the value from the constructor 
     *                        instead.
     * @param string $name New calendar name
     * @param string $delegate Currently ignored as well
     * @return array
     */    
    function make_calendar($account, $name, $json = 0, $delegate = true)
    {
        $calendar = new Atmail_Calendar_Calendar();
        $calendar->displayName = $name;
		$calendar->principalUrl = $this->_calendarAdapter->getPrincipalUrl();
        $this->_calendarAdapter->createCalendar($calendar);
        return $calendar;
    }

    /**
     * Deletes a calendar 
     * 
     * @param string $calendarUrl
     * @return void
     */
    function delete_calendar($calendarUrl)
    {
        $calendar = $this->_calendarAdapter->getCalendarByUrl($calendarUrl);
        $this->_calendarAdapter->deleteCalendar($calendar);
    }

    /**
     * Updates a property in a calendar 
     * 
     * @param string $relative_href Relative url, as returned from getCalNames 
     * @param string $property The property to be updated. Possible options are 
     *                         displayname and calendar-color.
     * @param string $patch   The updated value 
     * @return void
     */
    function update_calendar($relative_href, $property, $patch)
    {
        $this->update_calendar_ex($relative_href, null, $property, $patch);
    }

    /**
     * Updates a property in a calendar 
     * 
     * @param string $relative_href Relative url, as returned from getCalNames 
     * @param mixed $garbage (unused)
     * @param string $property The property to be updated. Possible options are 
     *                         displayname and calendar-color.
     * @param string $patch   The updated value 
     * @return void
     */
    function update_calendar_ex($relative_href, $garbage, $property, $patch)
    {
        $oldCal = $this->_calendarAdapter->getCalendarByUrl($relative_href);
        
        switch($property) {
            case 'displayname' :
                $oldCal->displayName = $patch;
                break;
            case 'calendar-color' : 
                $oldCal->color = $patch;
                break;
        }
        $this->_calendarAdapter->updateCalendar($oldCal);
    }

    function print_mkcal($ctag, $etag, $relative_href, $status, $name, $description,$color,$order, $calendar_owner, $account, $json, $principal_url)
    {
if(!$json)
{
        print "<calName principal_url='{$principal_url}' relative_href='{$relative_href}' ctag='{$ctag}' etag='{$etag}' name='{$name}' description='{$description}' color='{$color}' order='{$order}' calendar_owner='{$calendar_owner}' account='{$account}'><Error Status='{$status}'></Error></calName>\n";
}
else
{
    print json_encode(array('ctag' => $ctag, 'etag' => $etag, 'principal_url' => $principal_url, 'relative_href' => $relative_href, 'status' => $status, 'name' => $name, 'description' => $description, 'color' => $color, 'order' => $order, 'calendar_owner' => $calendar_owner, 'account' => $account));
}
    }
    
    
    function xmlentities($text)
    {
    	$text = str_replace('&', '&amp;', $text);
    	$text = str_replace('<', '&lt;', $text);
    	$text = str_replace('>', '&gt;', $text);
    	$text = str_replace('"', '&quot;', $text);
    	return str_replace("'", '&apos;', $text);
    }
    
    function print_calnames($calNames, $json)
    {
if(!$json)
{
print <<<EOF
<CalNames>
EOF;
        $totalstr = '';
        foreach( $calNames as $calname )
        {
        	mb_detect_order("ASCII,UTF-8,ISO-8859-1,windows-1252,iso-8859-15");
        	$calname['displayname']=$this->xmlentities(mb_convert_encoding($calname['displayname'], 'utf-8', mb_detect_encoding($calname['displayname'])));
        	$calname['description']=$this->xmlentities(mb_convert_encoding($calname['description'], 'utf-8', mb_detect_encoding($calname['description'])));
        	$str = "<CalName home='{$calname['home']}' principal_url='{$calname['principal_url']}' mode='{$calname['mode']}' ctag='{$calname['ctag']}' shared='{$calname['shared']}' etag='{$calname['etag']}' server='{$calname['server']}' name=\"{$calname['displayname']}\" description=\"{$calname['description']}\" color='{$calname['color']}' order='{$calname['order']}' relative_href='{$calname['relative_href']}' calendar_owner='{$calname['calendar_owner']}'/>\n";
            $totalstr .= $str;
        }
        $md5checksum = md5($totalstr);
        print $totalstr;
        print "<SystemInfo MD5='$md5checksum'/>\n";
print <<<EOF
</CalNames>
EOF;
}
else
{
        foreach( $calNames as &$calname )
{
//    $calname['color'] = trim($calname['color'], '#');
//    $calname['color'] = substr($calname['color'], 0, -2);
}
        echo json_encode($calNames);
}

    }

    function print_total( $total )
    {
        $output['total'] = $total;
        echo json_encode($output);
    }

    function print_return_code($id, $status, $json)
    {
if(!$json)
{
        if( $status == '' )
        {
            $status = 0;
        }
        print "<Cal ID='{$id}'><Error Status='{$status}'></Error></Cal>\n";
}
else
{
    print json_encode(array('id'=> $id, 'status' => $status));
}
    }

    function print_cal($func, $md5client, $account, $event_entries, $date_alert, $json, $start, $end, $overview, $timezone, $extended=false)
    {
		
		if( !isset($timezone) || $timezone == null || $timezone == 'undefined') 
			$timezone = 'GMT';
		
        $oldtz = date_default_timezone_get();
        date_default_timezone_set($timezone);

        $recipients = array();

        // If using the Ajax calendar, we are only viewing our records and shared details
        if( $func == 'overview' || $func == 'ajaxcal' || $func == 'freebusy' )
        {
            $recipients[0] = $account;            // this should probably come from somewhere else
        }
        else
        {
            $recipients = ''; //$param['recipients'];
        }
if(!$json)
{
print <<<EOF
<Appointments>
EOF;
}
        $myarray = array(); 
        $totalstr = '';
        foreach( $recipients as $user )
        {
            list($username, $pop3host) = explode('@', $user);
            foreach ($event_entries as $db)
            {
                if( isset($db['Task']) && $db['Task'] != null && $json)
                        continue;
                        
                if($overview)
                {
		try
		{
                	$tz = new DateTimeZone($db['TimezoneID'] == '' ? $timezone : $db['TimezoneID']);
		}
		catch(Exception $e)
		{
			$tz = new DateTimeZone('GMT');
		}
                    $startdt = new DateTime($db['DateStartCal'] . " " . $db['DateStartTime'], $tz);
                    $enddt = new DateTime($db['DateEndCal'] . " " . $db['DateEndTime'], $tz);
                    $temp_start = $startdt->format('j-n-Y');
                    $temp_end = ($db['AllDayEvent'] ? date('j-n-Y', strtotime($db['DateEndCal'] . " " . $db['TimezoneID']) - (60*60*24)) : $enddt->format('j-n-Y'));
                    
                    if(!isset($myarray[$temp_start]))
                        $myarray[$temp_start] = 0;

                    if($temp_end != $temp_start && $temp_start < $temp_end)
                    {
                        $temp2 = $temp_start;
                        $loops = 0;
                        $timestamp = strtotime($temp2 . " " . $db['TimezoneID']);
                        if($timestamp == false)
                        {
                            $timestamp = strtotime($temp2);
                        }
                        $temp2 = date('j-n-Y', strtotime(date("Y-m-d", $timestamp) . " +1 day"));
                        while($temp2 != $temp_end && $loops < 100)
                        {
                            $loops++;
                            $myarray[$temp2]++;
                                                    $timestamp = strtotime($temp2 . " " . $db['TimezoneID']);
                                                    if($timestamp == false)
                                          {
                                                           $timestamp = strtotime($temp2);
                                                }

                            $temp2 = date('j-n-Y', strtotime(date("Y-m-d", $timestamp) . " +1 day"));
                        }
                        $myarray[$temp_end]++;
                    }
                    $myarray[$temp_start]++;
                }
                else
                {

                // map to json packet
                $temp = array();
                $temp['id'] = $db['id'];
                $temp['title'] = $db['Title'];
				
				if($extended) {
					$temp['Location'] = $db['Location'];
					$temp['CalMessage'] = $db['CalMessage'];
					//file_put_contents('php://stderr', "**********************\n" . print_r($db,1));
				}
				
                try
                {
                    if( $db['TimezoneID'] == '' )
                    {
                         throw new Exception('No timezone provided');
                    }
                    $tz = new DateTimeZone($db['TimezoneID']);
                    $startdt = new DateTime($db['DateStartCal'] . " " . $db['DateStartTime'], $tz);
                    $enddt = new DateTime($db['DateEndCal'] . " " . $db['DateEndTime'], $tz);
                    $temp['start'] = $startdt->format('c');
                    $temp['end'] =  $enddt->format('c');
                }
                catch(Exception $e)
                {
                    $startdt = new DateTime($db['DateStartCal'] . " " . $db['DateStartTime']);
                    $enddt = new DateTime($db['DateEndCal'] . " " . $db['DateEndTime']);
                    $temp['start'] = substr($startdt->format('c'), 0, -6);
                    $temp['end'] =  substr($enddt->format('c'), 0, -6);
                }

                $temp['user'] = $user;
                $temp['color'] = $db['calendar_color'];
                $temp['relative_href'] = $db['relative_href'];
                $temp['allDay'] = ($db['AllDayEvent'] == 0 ? false : true);
				
                $myarray[] = $temp;
                $str = '';
                if( $func == 'ajaxcal' )
                {
                    if( isset($db['Task']) && $db['Task'] != null )
                    {
                        $str = "<App ID='p{$db['id']}' Task='1' User='$username@$pop3host' ";
                        if( $db['DateStartCal'] != null )
                        {
                            $str .= "Start='{$db['DateStartCal']} {$db['DateStartTime']}' ";
                        }
                        if( $db['DateEndCal'] != null )
                        {
                            $str .= "Finish='{$db['DateEndCal']} {$db['DateEndTime']}' ";
                        }
                        $str .= "Shared='0' ctag='{$db['ctag']}' etag='{$db['etag']}' owner='{$db['calendar_owner']}' relative_href='{$db['relative_href']}' alarmSet='0'><Subject><![CDATA[{$db['Title']}]]></Subject></App>\n";
                    }
                    else
                    {
                        $str = "<App ID='p{$db['id']}' Task='0' User='$username@$pop3host' Start='{$db['DateStartCal']} {$db['DateStartTime']}' Finish='{$db['DateEndCal']} {$db['DateEndTime']}' Shared='0' ctag='{$db['ctag']}' owner='{$db['calendar_owner']}' IsRecurring='{$db['IsRecurring']}' recurrencePattern='{$db['recurrencePattern']}' recurrenceTime='{$db['recurrenceTime']}' relative_href='{$db['relative_href']}' alarmSet='{$db['alarmSet']}'><Subject><![CDATA[{$db['Title']}]]></Subject></App>\n";
                    }
                    
                    $totalstr .= $str;
                }
                else
                {
                    $str = "<App User='$user' Date='{$db['DateStartCal']}' Start='{$db['DateStartTime']}' Finish='{$db['DateEndTime']}' Title='{$db['Title']}' Type='Busy' owner='{$db['calendar_owner']}' ctag='{$db['ctag']}' etag='{$db['etag']}' relative_href='{$db['relative_href']}' alarmSet='{$db['alarmSet']}'/>\n";
                    $totalstr .= $str;
                }
                }
            }
        }

        $md5checksum = md5($totalstr);

        // Validate if we have anything that is due now
        if( $date_alert != '1' )
        {
            // TODO: Get the date from the users client in JS
            $due = 0;
            $count = count($due);
        }

if(!$json)
{
        if( $md5checksum == $md5client )
        {
            print "<SystemInfo MD5='$md5checksum' Changes='0' Due='$count'/>\n";
        }
        else
        {
            print $totalstr;
            print "<SystemInfo MD5='$md5checksum' Changes='1' Due='$count'/>\n";
        }
print <<<EOF
</Appointments>
EOF;
}
else
{
    echo json_encode($myarray);
}
        date_default_timezone_set($oldtz);
    }

    function print_users($users, $json)
    {
if(!$json)
{
print <<<EOF
<Users>
EOF;

        $totalstr = '';
        if( count($users) )
        {
            foreach( $users as $user )
            {
                $str = "<User username='{$user['member']}' permissions='{$user['mode']}'/>\n";
                $totalstr .= $str;
            }
        }
        $md5checksum = md5($totalstr);
        print $totalstr;
        print "<SystemInfo MD5='$md5checksum'/>\n";

print <<<EOF
</Users>
EOF;
}
else
{
    print json_encode($users);
}
    }
}
