steffen: server/kolab-horde-fbview/kolab-horde-fbview/fbview/kronolith/lib/Driver ical.php, NONE, 1.1 kolab.php, NONE, 1.1 mcal.php, NONE, 1.1 sql.php, NONE, 1.1

cvs at intevation.de cvs at intevation.de
Mon Oct 31 12:43:20 CET 2005


Author: steffen

Update of /kolabrepository/server/kolab-horde-fbview/kolab-horde-fbview/fbview/kronolith/lib/Driver
In directory doto:/tmp/cvs-serv18388/kolab-horde-fbview/kolab-horde-fbview/fbview/kronolith/lib/Driver

Added Files:
	ical.php kolab.php mcal.php sql.php 
Log Message:
Fbview in separate package

--- NEW FILE: ical.php ---
<?php
/**
 * The Kronolith_Driver_ical:: class implements the Kronolith_Driver
 * API for iCalendar data.
 *
 * $Horde: kronolith/lib/Driver/ical.php,v 1.1 2004/05/02 21:50:23 chuck Exp $
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Kronolith 2.0
 * @package Kronolith
 */
class Kronolith_Driver_ical extends Kronolith_Driver {

    /**
     * Cache events as we fetch them to avoid fetching or parsing the
     * same event twice.
     *
     * @var array $_cache
     */
    var $_cache = array();

    function open($calendar)
    {
        $this->_calendar = $calendar;
    }

    function listAlarms($date)
    {
        return array();
    }

    function listEvents($startDate = null, $endDate = null, $hasAlarm = false)
    {
        $data = Kronolith::getRemoteCalendar($url);
        if (is_a($data, 'PEAR_Error')) {
            return $data;
        }

        require_once 'Horde/iCalendar.php';
        $iCal = &new Horde_iCalendar();
        if (!$iCal->parsevCalendar($data)) {
            return array();
        }

        $components = $iCal->getComponents();
        $events = array();
        $count = count($components);
        for ($i = 0; $i < $count; $i++) {
            $component = $components[$i];
            if ($component->getType() == 'vEvent') {
                $event = &new Kronolith_Event_ical($this);
                $event->fromiCalendar($component);
                $event->remoteCal = $url;
                $event->eventIndex = $i;
                $events[] = $event;
            }
        }

        return $events;
        if (!isset($endDate)) {
            $endDate = Kronolith::dateObject(array('mday' => 31, 'month' => 12, 'year' => 9999));
        } else {
            list($endDate->mday, $endDate->month, $endDate->year) = explode('/', Date_Calc::nextDay($endDate->mday, $endDate->month, $endDate->year, '%d/%m/%Y'));
        }
        $endDate = &new Kronolith_Date($endDate);

        return array();
    }

    function &getEvent($eventId = null)
    {
        $data = Kronolith::getRemoteCalendar($url);
        if (is_a($data, 'PEAR_Error')) {
            return $data;
        }

        require_once 'Horde/iCalendar.php';
        $iCal = &new Horde_iCalendar();
        if (!$iCal->parsevCalendar($data)) {
            return array();
        }

        $components = $iCal->getComponents();
        if (isset($components[$eventId]) && $components[$eventId]->getType() == 'vEvent') {
            $event = &new Kronolith_Event_ical($this);
            $event->fromiCalendar($components[$eventId]);
            $event->remoteCal = $url;
            $event->eventIndex = $eventId;

            return $event;
        }

        return false;
    }

    function &getByGUID($guid)
    {
    }

    function saveEvent($event)
    {
        return PEAR::raiseError('not supported');
    }

    /**
     * Move an event to a new calendar.
     *
     * @param string $eventId      The event to move.
     * @param string $newCalendar  The new calendar.
     */
    function move($eventId, $newCalendar)
    {
        return PEAR::raiseError('not supported');
    }

    /**
     * Delete a calendar and all its events.
     *
     * @param string $calendar  The name of the calendar to delete.
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function delete($calendar)
    {
        return PEAR::raiseError('not supported');
    }

    /**
     * Delete an event.
     *
     * @param string $eventId  The ID of the event to delete.
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function deleteEvent($eventId)
    {
        return PEAR::raiseError('not supported');
    }

    function close()
    {
        return true;
    }

}

class Kronolith_Event_ical extends Kronolith_Event {

    function fromDriver($vEvent)
    {
        $this->fromiCalendar($vEvent);
        $this->initialized = true;
        $this->stored = true;
    }

    function toDriver()
    {
        return $this->toiCalendar();
    }

}

--- NEW FILE: kolab.php ---
<?php

require_once 'Horde/Kolab.php';
require_once 'Horde/iCalendar.php';
require_once 'Horde/Identity.php';

/**
 * Horde Kronolith driver for the Kolab IMAP Server.
 * Copyright (C) 2003, 2004 Code Fusion, cc.
 *
 * $Horde: kronolith/lib/Driver/kolab.php,v 1.8 2004/05/24 12:38:11 stuart Exp $
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Stuart Bingë <s.binge at codefusion.co.za>
 * @version $Revision: 1.1 $
 * @package Kronolith
 */
class Kronolith_Driver_kolab extends Kronolith_Driver {

    /**
     * Our Kolab Cyrus server connection.
     *
     * @var object Kolab_Cyrus $_kc
     */
    var $_kc;

    /**
     * What IMAP folder we're using to store notes.
     *
     * @var string $_folder
     */
    var $_folder;

    /**
     * Should we create $_folder if it does not exist?
     *
     * @var boolean $_create
     */
    var $_create;

    /**
     * Constructor - we need to override this to set our default
     * 'folder' and 'server' parameters.
     *
     * @param optional array $params  Any parameters needed for this driver.
     */
    function Kronolith_Driver_kolab($params = array())
    {
        $this->_params = $params;

        $this->_params['folder'] = isset($params['folder']) ? $params['folder'] : 'Calendar';
        $this->_params['server'] = isset($params['server']) ? $params['server'] : $GLOBALS['conf']['kolab']['server'];

        $this->_kc = &new Kolab_Cyrus($this->_params['server']);
    }

    function open($calendar)
    {
        $result = Kolab_Cyrus::shareToFolder(
            'kronolith_shares',
            $calendar,
            $this->_params['folder'],
            $this->_folder,
            $this->_create
        );
        if (is_a('PEAR_Error', $result)) {
            return $result;
        }

        $this->_calendar = $result;

        $result = $this->_kc->openMailbox($this->_folder, $this->_create);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }

        return true;
    }

    function close()
    {
        $this->_kc->closeMailbox();
    }

    function listAlarms($date)
    {
        $events = $this->listEvents($date, $date);
        $alarms = array();

        $cal = &new Horde_iCalendar();
        foreach ($events as $event) {
            $matches = $this->_kc->getMessageList(SORTDATE, false, "SUBJECT \"$event\"");
            if (!is_array($matches) || count($matches) < 1) {
                continue;
            }

            $body = $this->_kc->getObject($matches[0], "text/calendar");
            if ($body === false) {
                continue;
            }

            $cal->parsevCalendar($body);
            $components = $cal->getComponents();

            foreach ($components as $component) {
                if (!is_a($component, 'Horde_iCalendar_vevent')) {
                    continue;
                }

                $subcomps = $component->getComponents();
                foreach ($subcomps as $subcomp) {
                    if (!is_a($subcomp, 'Horde_iCalendar_valarm')) {
                        continue;
                    }

                    $ts = Kronolith::objectToTimestamp($date);

                    $dateattr = $component->getAttribute('DTSTART');
                    if (is_array($dateattr) || is_a($dateattr, 'PEAR_Error')) {
                        continue;
                    }

                    $offset = $subcomp->getAttribute('TRIGGER');
                    $diff = $ts - $dateattr;
                    if ($diff >= $offset && $diff <= 0) {
                        array_push($alarms, $event);
                    }
                }
            }
        }

        return $alarms;
    }

    function listEvents($startDate = null, $endDate = null)
    {
        $events = array();

        $msgs = $this->_kc->getMessageList();
        $cal = &new Horde_iCalendar();
        foreach ($msgs as $msg) {
            $body = $this->_kc->getObject($msg, 'text/calendar');
            $cal->_components = array();
            $cal->parsevCalendar($body);

            $components = $cal->getComponents();
            $ispresent = false;

            foreach ($components as $component) {
                if (!is_a($component, 'Horde_iCalendar_vevent')) {
                    continue;
                }

                $startattr = $component->getAttribute('DTSTART');
                if (is_array($startattr) || is_a($startattr, 'PEAR_Error')) {
                    continue;
                }

                $start = getdate($startattr);
                //Leaving this check in screws up recurring events
                /*if (!$ispresent &&
                    $start['year'] >= $startDate->year && $start['year'] <= $endDate->year &&
                    $start['mon'] >= $startDate->month && $start['mon'] <= $endDate->month &&
                    $start['mday'] >= $startDate->mday && $start['mday'] <= $endDate->mday)
                {*/
                    array_push($events, $component->getAttribute('UID'));
                    $ispresent = true;
                //}
            }
        }

        return $events;
    }

    function &getEvent($eventID = null)
    {
        if (is_null($eventID)) {
            return new Kronolith_Event_kolab($this);
        }

        $matches = $this->_kc->getMessageList(SORTDATE, false, "SUBJECT \"$eventID\"");

        $body = $this->_kc->getObject($matches, 'text/calendar');
        if ($body === false) {
            return $body;
        }

        $iCalendar = &new Horde_iCalendar();
        $iCalendar->parsevCalendar($body);
        $components = $iCalendar->getComponents();

        foreach ($components as $component) {
            if (!is_a($component, 'Horde_iCalendar_vevent')) {
                continue;
            }

            $event = &new Kronolith_Event_kolab($this, $component);
            return $event;
        }

        return false;
    }

    function &getByGUID($guid)
    {
        $pieces = explode(':', $guid);

        if ($pieces[0] != 'kronolith') {
            return PEAR::raiseError("GUID $guid is invalid");
        }

        array_shift($pieces);
        $calendar = array_pop($pieces);
        $eventId = implode(':', $pieces);

        $this->open($calendar);
        return $this->getEvent($eventId);
    }

    function saveEvent($event)
    {
        $eventID = $event->getID();
        $iCalendar = &new Horde_iCalendar();
        $vEvent = null;
        $vAlarm = null;
        $addEvent = false;
        $addAlarm = false;

        if (is_null($eventID)) {
            // We need the calendar UID in the event UID when moving messages
            // between calendars, as otherwise we have no way of knowing which
            // IMAP folder the event is stored in.
            $eventID = md5(uniqid(mt_rand(), true)) . ':' . $this->getCalendar();

            $vEvent = &$iCalendar->newComponent('VEVENT', $iCalendar);
            $vEvent->setAttribute('CREATED', time());
            $vEvent->setAttribute('UID', $eventID);

            $addEvent = true;
        } else {
            $matches = $this->_kc->getMessageList(SORTDATE, false, "SUBJECT \"$eventID\"");

            if (!is_array($matches) || count($matches) < 1) {
                $vEvent = &$iCalendar->newComponent('VEVENT', $iCalendar);
                $vEvent->setAttribute('CREATED', time());
                $vEvent->setAttribute('UID', $eventID);

                $addEvent = true;
            } else {
                $body = $this->_kc->getObject($matches[0], 'text/calendar');
                if ($body === false) {
                    return PEAR::raiseError('Corresponding message does not contain valid iCalendar data');
                }

                $this->_kc->deleteMessages($matches, true);
                $iCalendar->parsevCalendar($body);

                $vEvent = &$iCalendar->findComponent('VEVENT');
                if ($vEvent === false) {
                    return PEAR::raiseError('iCalendar data does not contain a valid vEvent object');
                }

                $vAlarm = &$vEvent->findComponent('VALARM');
                if ($vAlarm === false) {
                    $vAlarm = null;
                }
            }
        }

        $vEvent->setAttribute('SUMMARY', $event->getTitle(), array(), false);
        $vEvent->setAttribute('DESCRIPTION', $event->getDescription(), array(), false);
        $vEvent->setAttribute('LOCATION', $event->getLocation(), array(), false);
        $vEvent->setAttribute('X-HORDE-CATEGORY', $event->getCategory(), array(), false);
        $vEvent->setAttribute('STATUS', $this->statusToICal($event->getStatus()), array(), false);
        $vEvent->setAttribute('LAST-MODIFIED', time(), array(), false);
        $vEvent->setAttribute('ORGANIZER', "MAILTO:" . $event->getCreatorID(), array(), false);
        $vEvent->setAttribute('DTSTART', $event->getStartTimestamp(), array(), false);
        $vEvent->setAttribute('DTEND', $event->getEndTimestamp(), array(), false);
        $vEvent->setAttribute('TRANSP', 'OPAQUE', array(), false);

        $recend = ($event->recurEndTimestamp ? ';UNTIL=' . Horde_iCalendar::_exportDateTime($event->recurEndTimestamp) : '');
        switch ($event->recurType) {
        case KRONOLITH_RECUR_NONE:
            $vEvent->removeAttribute('RRULE');
            break;

        case KRONOLITH_RECUR_DAILY:
            $vEvent->setAttribute('RRULE', 'FREQ=DAILY;INTERVAL=' . $event->recurInterval . $recend, array(), false);
            break;

        case KRONOLITH_RECUR_WEEKLY:
            $rrule = 'FREQ=WEEKLY;INTERVAL=' . $event->recurInterval . ';BYDAY=';
            $vcaldays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA');

            for ($i = $flag = 0; $i <= 7 ; $i++) {
                if ($event->recurOnDay(pow(2, $i))) {
                    if ($flag) {
                        $rrule .= ',';
                    }
                    $rrule .= $vcaldays[$i];
                    $flag = true;
                }
            }
            $rrule .= $recend;
            $vEvent->setAttribute('RRULE', $rrule, array(), false);
            break;

        case KRONOLITH_RECUR_DAY_OF_MONTH:
            $vEvent->setAttribute('RRULE', 'FREQ=MONTHLY;INTERVAL=' . $event->recurInterval . $recend, array(), false);
            break;

        case KRONOLITH_RECUR_WEEK_OF_MONTH:
            $vcaldays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA');
            $vEvent->setAttribute('RRULE', 'FREQ=MONTHLY;INTERVAL=' . $event->recurInterval . ';BYDAY=' . ((
                date('W', $event->startTimestamp) - date('W', mktime(0, 0, 0, date('n', $event->startTimestamp),
                1, date('Y', $event->startTimestamp)))) + 1) . $vcaldays[date('w', $event->startTimestamp)] . $recend, array(), false);
            break;

        case KRONOLITH_RECUR_YEARLY:
            $vEvent->setAttribute('RRULE', 'FREQ=YEARLY;INTERVAL=' . $event->recurInterval . $recend, array(), false);
            break;
        }

        $vEvent->removeAttribute('ATTENDEE');
        foreach ($event->attendees as $email => $status) {
            $vEvent->setAttribute('ATTENDEE', "MAILTO:$email", array(
                    // 'RSVP' => 'FALSE',
                    'PARTSTAT' => $this->responseToICal($status['response']),
                    'ROLE' => $this->partToICal($status['attendance'])
                ), true
            );
        }

        $trigger = $event->getAlarm();
        if ($trigger != 0) {
            if (is_null($vAlarm)) {
                $addAlarm = true;
                $vAlarm = &$vEvent->newComponent('VALARM', $vEvent);
            }

            $vAlarm->setAttribute('TRIGGER', $trigger, array(), false);

            if ($addAlarm) {
                $vEvent->addComponent($vAlarm);
            }
        }

        if ($addEvent) {
            $iCalendar->addComponent($vEvent);
        }

        $result = $this->_kc->addObject(
            $eventID,
            $iCalendar->exportvCalendar(),
            'text/calendar',
            'kolab-calendar-entry.ics',
            'Kronolith'
        );
        if (is_a('PEAR_Error', $result)) {
            return $result;
        }

        // Generate 8 weeks worth of free/busy information.
        $vfbStart = time();
        $fb = Kronolith::generateFreeBusy($this->getCalendar(), $vfbStart, $vfbStart + 4838400);
        Kolab::storeFreeBusy('localhost', '/freebusy', $fb);

        return $eventID;
    }

    /**
     * Move an event to a new calendar.
     *
     * @param string $eventId      The event to move.
     * @param string $newCalendar  The new calendar.
     */
    function move($eventID, $newCalendar)
    {
        $event = &$this->getByGUID(Kronolith_Driver::getGUID($eventID));
        $this->deleteEvent($eventID);

        $eventID = "$eventID:$newCalendar";

        $event->setID($eventID);
        $event->setCalendar($newCalendar);

        $this->open($newCalendar);
        $this->saveEvent($event);

        return true;
    }

    /**
     * Delete a calendar and all its events.
     *
     * @param string $calendar  The name of the calendar to delete.
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function delete($calendar)
    {
        $calendars = Kronolith::listCalendars(true, PERMS_EDIT);
        if (!isset($calendars[$calendar])) {
            return PEAR::raiseError("Unable to delete calendar $calendar");
        }

        $owner = $calendars[$calendar]->get('owner');
        $mailbox = $calendars[$calendar]->get('name');
        $mailbox = ($owner == $calendars[$calendar]->getName() ? $this->_params['folder'] : $mailbox);

        return $this->_kc->deleteMailbox($mailbox);
    }

    /**
     * Rename a calendar.
     *
     * @param string $from  The current name of the calendar.
     * @param string $to    The new name of the calendar.
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function rename($from, $to)
    {
        return $this->_kc->renameMailbox($from, $to);
    }

    /**
     * Delete an event.
     *
     * @param string $eventId  The ID of the event to delete.
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function deleteEvent($eventID)
    {
        $matches = $this->_kc->getMessageList(SORTDATE, false, "SUBJECT \"$eventID\"");
        $this->_kc->deleteMessages($matches, true);

        // Generate 8 weeks worth of free/busy information
        $vfbStart = time();
        Kolab::storeFreeBusy('localhost', '/freebusy/', Kronolith::generateFreeBusy($this->_calendar, $vfbStart, $vfbStart + 4838400));

        /* Log the deletion of this item in the history log. */
        $history = &Horde_History::singleton();
        $history->log($this->getGUID($eventID), array('action' => 'delete'), true);

        return true;
    }

    /**
     * Maps a Kronolith meeting status to the corresponding iCalendar
     * string.
     *
     * @param integer $status   The meeting status; one of the
     *                          KRONOLITH_STATUS_XXX constants.
     *
     * @return string   The iCalendar status string.
     */
    function statusToICal($status)
    {
        switch ($status) {
        case KRONOLITH_STATUS_CONFIRMED:
            return 'CONFIRMED';

        case KRONOLITH_STATUS_CANCELLED:
            return 'CANCELLED';

        case KRONOLITH_STATUS_TENTATIVE:
        default:
            return 'TENTATIVE';
        }
    }

    /**
     * Maps a Kronolith attendee response string to the corresponding
     * iCalendar string.
     *
     * @param integer $response  The attendee response; one of the
     *                           KRONOLITH_RESPONSE_XXX constants.
     *
     * @return string   The iCalendar response string.
     */
    function responseToICal($response)
    {
        switch ($response) {
        case KRONOLITH_RESPONSE_ACCEPTED:
            return 'ACCEPTED';

        case KRONOLITH_RESPONSE_DECLINED:
            return 'DECLINED';

        case KRONOLITH_RESPONSE_TENTATIVE:
            return 'TENTATIVE';

        case KRONOLITH_RESPONSE_NONE:
        default:
            return 'NEEDS-ACTION';
        }
    }

    /**
     * Maps a Kronolith attendee participation string to the
     * corresponding iCalendar string.
     *
     * @param integer $part      The attendee participation; one of the
     *                           KRONOLITH_PART_XXX constants.
     *
     * @return string   The iCalendar participation string.
     */
    function partToICal($part)
    {
        switch ($part) {
        case KRONOLITH_PART_OPTIONAL:
            return 'OPT-PARTICIPANT';

        case KRONOLITH_PART_NONE:
            return 'NON-PARTICIPANT';

        case KRONOLITH_PART_REQUIRED:
        default:
            return 'REQ-PARTICIPANT';
        }
    }

    /**
     * Maps an iCalendar meeting status to the corresponding Kronolith
     * value.
     *
     * @param string $status    The meeting status.
     *
     * @return integer   The Kronolith status value.
     */
    function statusFromICal($status)
    {
        switch (String::upper($status)) {
        case 'CONFIRMED':
            return KRONOLITH_STATUS_CONFIRMED;

        case 'CANCELLED':
            return KRONOLITH_STATUS_CANCELLED;

        case 'TENTATIVE':
        default:
            return KRONOLITH_STATUS_TENTATIVE;
        }
    }

    /**
     * Maps an iCalendar attendee participation string to the
     * corresponding Kronolith value.
     *
     * @param string $part       The attendee participation.
     *
     * @return string   The Kronolith participation value.
     */
    function partFromICal($part)
    {
        switch (String::upper($part)) {
        case 'OPT-PARTICIPANT':
            return KRONOLITH_PART_OPTIONAL;

        case 'NON-PARTICIPANT':
            return KRONOLITH_PART_NONE;

        case 'REQ-PARTICIPANT':
        default:
            return KRONOLITH_PART_REQUIRED;
        }
    }

}

class Kronolith_Event_kolab extends Kronolith_Event {

    var $_properties = array();

    function toDriver()
    {
        // Nothing - all of this is done in saveEvent().
    }

    /**
     * @param object Horde_iCalendar_vevent $icalEvent
     */
    function fromDriver($icalEvent)
    {
        $this->eventID = $icalEvent->getAttributeDefault('UID', '');
        $this->title = $icalEvent->getAttributeDefault('SUMMARY', '');
        $this->description = trim($icalEvent->getAttributeDefault('DESCRIPTION', ''));
        $this->location = $icalEvent->getAttributeDefault('LOCATION', '');
        $this->category = $icalEvent->getAttributeDefault('X-HORDE-CATEGORY', '');
        $this->status = Kronolith_Driver_kolab::statusFromICal($icalEvent->getAttributeDefault('STATUS', ''));
        $this->creatorID = preg_replace("/^mailto:\s*/i", '', $icalEvent->getAttributeDefault('ORGANIZER', ''));

        $this->startTimestamp = $icalEvent->getAttributeDefault('DTSTART', 0);
        if ($this->startTimestamp) {
            $this->start = Kronolith::timestampToObject($this->startTimestamp);
        }

        $this->endTimestamp = $icalEvent->getAttributeDefault('DTEND', 0);
        if ($this->endTimestamp) {
            $this->end = Kronolith::timestampToObject($this->endTimestamp);
        }

        if ($this->startTimestamp && $this->endTimestamp) {
            $this->durMin = ($this->endTimestamp - $this->startTimestamp) / 60;
        }

        $this->recurEnd = null;
        $this->recurType = KRONOLITH_RECUR_NONE;

        $tmp = $icalEvent->getAttributeDefault('RRULE', '');
        preg_match_all('/([^;=]*)=?([^;]*);?/', $tmp, $rmatches);

        for ($i = 0; $i < count($rmatches[2]); $i++) {
            switch ($rmatches[1][$i]) {
            case 'FREQ':
                $freq = $rmatches[2][$i];

                switch ($freq) {
                case 'DAILY':
                    $this->recurType = KRONOLITH_RECUR_DAILY;
                    break;

                case 'WEEKLY':
                    $this->recurType = KRONOLITH_RECUR_WEEKLY;
                    break;

                case 'MONTHLY':
                    $this->recurType = KRONOLITH_RECUR_DAY_OF_MONTH;
                    break;

                case 'YEARLY':
                    $this->recurType = KRONOLITH_RECUR_YEARLY;
                    break;

                default:
                    $this->recurType = KRONOLITH_RECUR_NONE;
                }
                break;

            case 'UNTIL':
                $until = $rmatches[2][$i];
                $this->recurEndTimestamp = $icalEvent->_parseDateTime($until);
                $this->recurEnd = Kronolith::timestampToObject($this->recurEndTimestamp);
                break;

            case 'INTERVAL':
                $interval = $rmatches[2][$i];
                $this->recurInterval = $interval;
                break;

            case 'COUNT':
                $this->recurEndTimestamp = Kolab::countToUntil($this->startTimestamp, $rmatches[2][$i], $this->recurType);
                $this->recurEnd = Kronolith::timestampToObject($this->recurEndTimestamp);
                break;

            case 'BYDAY':
                preg_match_all('/([^,]*)/', $rmatches[2][$i], $days);
                $bits = array('SU' => KRONOLITH_MASK_SUNDAY,
                              'MO' => KRONOLITH_MASK_MONDAY,
                              'TU' => KRONOLITH_MASK_TUESDAY,
                              'WE' => KRONOLITH_MASK_WEDNESDAY,
                              'TH' => KRONOLITH_MASK_THURSDAY,
                              'FR' => KRONOLITH_MASK_FRIDAY,
                              'SA' => KRONOLITH_MASK_SATURDAY,
                              );
                $mask = 0;
                foreach ($days[1] as $day) {
                    if (empty($day) || !isset($bits[$day])) {
                        continue;
                    }

                    $mask |= $bits[$day];
                }

                $this->setRecurOnDay($mask);
                break;
            }
        }

        $subcomps = $icalEvent->getComponents();
        foreach ($subcomps as $subcomp) {
            if (!is_a($subcomp, 'Horde_iCalendar_valarm')) {
                continue;
            }

            $this->alarm = Kolab::_getAttr($subcomp, 'TRIGGER', 0);
        }

        $atnames = $icalEvent->getAttribute('ATTENDEE');
        $atparms = $icalEvent->getAttribute('ATTENDEE', true);

        if (!is_a($atnames, 'PEAR_Error')) {
            if (!is_array($atnames)) {
                $atnames = array(strval($atnames));
            }
            foreach ($atnames as $index => $attendee) {
                $this->addAttendee(preg_replace("/^mailto:\s*/i", '', $attendee),
                                   Kronolith_Driver_kolab::partFromICal($atparms[$index]['ROLE']),
                                   Kronolith::responseFromICal($atparms[$index]['PARTSTAT'])
                                   );
            }
        }

        $this->initialized = true;
        $this->stored = true;
    }

    function getProperties()
    {
        return $this->_properties;
    }

}

--- NEW FILE: mcal.php ---
<?php
/**
 * The Kronolith_Driver_mcal:: class implements the Kronolith_Driver
 * API for an MCAL backend.
 *
 * $Horde: kronolith/lib/Driver/mcal.php,v 1.56 2004/04/26 19:33:48 chuck Exp $
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Kronolith 0.1
 * @package Kronolith
 */
class Kronolith_Driver_mcal extends Kronolith_Driver {

    /**
     * The current MCAL connection.
     *
     * @var resource $_stream
     */
    var $_stream;

    /**
     * Get a globally unique ID for an event. We need to override this
     * since we really need the calendar in mstore GUIDs in order to
     * look at the right file.
     *
     * @param integer $eventId  The event id.
     *
     * @return string  A GUID referring to $eventId.
     */
    function getGUID($eventId)
    {
        return 'kronolith:' . $this->_calendar . ':' . $eventId;
    }

    function open($calendar)
    {
        $this->_calendar = $calendar;
        $this->_stream = @mcal_popen("{/mstore}<$calendar>",
                                     $this->_params['username'],
                                     $this->_params['password']);
    }

    function close()
    {
        @mcal_close($this->_stream);
    }

    function listEvents($startDate = null, $endDate = null)
    {
        $events = @mcal_list_events($this->_stream, $startDate->year, $startDate->month, $startDate->mday, $endDate->year, $endDate->month, $endDate->mday);
        return is_array($events) ?
            Horde_Array::combine(array_map(create_function('$e', 'return "kronolith:' . $this->_calendar . ':" . $e;'), $events), $events) :
            array();
    }

    function listAlarms($date)
    {
        $events = @mcal_list_alarms($this->_stream, $date->year, $date->month, $date->mday, $date->hour, $date->min, $date->sec);
        return is_array($events) ? $events : array();
    }

    function &getEvent($eventID = null)
    {
        if (!is_null($eventID)) {
            $event = @mcal_fetch_event($this->_stream, (int)$eventID);
            if ($event && $event->id > 0) {
                return $ret = &new Kronolith_Event_mcal($this, $event);
            } else {
                return false;
            }
        } else {
            return $ret = &new Kronolith_Event_mcal($this);
        }
    }

    function &getByGUID($guid)
    {
        /* Validate the GUID. */
        $pieces = explode(':', $guid);

        /* Make sure this is a Kronolith GUID. */
        if ($pieces[0] != 'kronolith') {
            return array(false, false);
        }

        /* Strip off the kronolith entry. */
        array_shift($pieces);

        /* The event id is the last entry in the array. */
        $eventId = array_pop($pieces);

        /* The calendar id is everything else. */
        $calendar = implode(':', $pieces);

        /* Open $calendar and fetch the event. */
        $this->open($calendar);
        return $this->getEvent($eventId);
    }

    function saveEvent($event)
    {
        if (!is_null($event->getID())) {
            if ($id = mcal_store_event($this->_stream)) {
                /* Log the modification of this item in the history
                 * log. */
                $history = &Horde_History::singleton();
                $history->log($this->getGUID($id), array('action' => 'modify'), true);

                return $event->getID();
            } else {
                return false;
            }
        } else {
            if ($id = mcal_append_event($this->_stream)) {
                /* Log the creation of this item in the history
                 * log. */
                $history = &Horde_History::singleton();
                $history->log($this->getGUID($id), array('action' => 'add'), true);

                return $id;
            } else {
                return false;
            }
        }
    }

    function nextRecurrence($eventID, $afterDate, $weekstart = KRONOLITH_SUNDAY)
    {
        $next = mcal_next_recurrence($this->_stream, $weekstart, $afterDate);
        if (empty($next->year)) {
            return false;
        }

        return Kronolith::dateObject($next);
    }

    /**
     * Move an event to a new calendar.
     *
     * @param string $eventId      The event to move.
     * @param string $newCalendar  The new calendar.
     */
    function move($eventId, $newCalendar)
    {
        $event = &$this->getEvent($eventId);
        if (!$event) {
            return PEAR::raiseError('not found');
        }

        $event->setCalendar($newCalendar);
        return $event->save();
    }

    /**
     * Delete a calendar and all its events.
     *
     * @param string $calendar The name of the calendar to delete.
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function delete($calendar)
    {
        /**
         * @TODO FIXME: this is horrid, but will work for mstore for
         * now.
         */
        $file = '/var/calendar/' . basename($calendar);

        $this->close();

        @unlink($file);
        if (!@file_exists($file)) {
            $result = true;
        } else {
            $result = PEAR::raiseError(sprintf(_("Unable to delete %s."), $calendar));
        }

        return $result;
    }

    function deleteEvent($eventID)
    {
        if (mcal_delete_event($this->_stream, $eventID)) {
            /* Log the deletion of this item in the history log. */
            $history = &Horde_History::singleton();
            $history->log($this->getGUID($eventID), array('action' => 'delete'), true);

            return true;
        }

        return false;
    }

    function parseMCALDate($dateObject)
    {
        if (count($dateObject) === 0) {
            return 0;
        }

        $year = isset($dateObject->year) ? $dateObject->year : 0;
        $month = isset($dateObject->month) ? $dateObject->month : 0;
        $day = isset($dateObject->mday) ? $dateObject->mday : 0;

        // Check for events with no recur_enddate
        if ($year == 9999 && $month == 12 && $day == 31) {
            return 0;
        }

        $hour = isset($dateObject->hour) ? $dateObject->hour : 0;
        $minute = isset($dateObject->min) ? $dateObject->min : 0;
        $second = isset($dateObject->sec) ? $dateObject->sec : 0;

        return mktime($hour, $minute, $second, $month, $day, $year);
    }

}

class Kronolith_Event_mcal extends Kronolith_Event {

    function toDriver()
    {
        $driver = &$this->getDriver();

        // Basic fields.
        mcal_event_set_title($driver->_stream, $this->getTitle());
        mcal_event_set_description($driver->_stream, $this->getDescription());
        mcal_event_set_category($driver->_stream, $this->getCategory());
        mcal_event_add_attribute($driver->_stream, 'location', $this->getLocation());
        mcal_event_add_attribute($driver->_stream, 'keywords', implode(',', $this->getKeywords()));
        mcal_event_add_attribute($driver->_stream, 'exceptions', implode(',', $this->getExceptions()));
        mcal_event_add_attribute($driver->_stream, 'modified', time());
        mcal_event_add_attribute($driver->_stream, 'creatorid', $this->getCreatorID());

        // Event start.
        $start = explode(':', date('Y:n:j:G:i', $this->getStartTimestamp()));
        mcal_event_set_start($driver->_stream, $start[0], $start[1], $start[2], $start[3], $start[4]);

        // Event end.
        $end = explode(':', date('Y:n:j:G:i', $this->getEndTimestamp()));
        mcal_event_set_end($driver->_stream, $end[0], $end[1], $end[2], $end[3], $end[4]);

        // Alarm.
        mcal_event_set_alarm($driver->_stream, $this->getAlarm());

        // Recurrence.
        $recur_end = explode(':', date('Y:n:j', $this->getRecurEndTimestamp()));
        if ($recur_end[0] == 1969) {
            $recur_end[0] = 9999;
            $recur_end[1] = 12;
            $recur_end[2] = 31;
        }

        switch ($this->getRecurType()) {
        case KRONOLITH_RECUR_NONE:
            mcal_event_set_recur_none($driver->_stream);
            break;

        case KRONOLITH_RECUR_DAILY:
            mcal_event_set_recur_daily($driver->_stream,
                                       $recur_end[0],
                                       $recur_end[1],
                                       $recur_end[2],
                                       $this->getRecurInterval());
            break;

        case KRONOLITH_RECUR_WEEKLY:
            mcal_event_set_recur_weekly($driver->_stream,
                                        $recur_end[0],
                                        $recur_end[1],
                                        $recur_end[2],
                                        $this->getRecurInterval(),
                                        $this->getRecurOnDays());
            break;

        case KRONOLITH_RECUR_DAY_OF_MONTH:
            mcal_event_set_recur_monthly_mday($driver->_stream,
                                              $recur_end[0],
                                              $recur_end[1],
                                              $recur_end[2],
                                              $this->getRecurInterval());
            break;

        case KRONOLITH_RECUR_WEEK_OF_MONTH:
            mcal_event_set_recur_monthly_wday($driver->_stream,
                                              $recur_end[0],
                                              $recur_end[1],
                                              $recur_end[2],
                                              $this->getRecurInterval());
            break;

        case KRONOLITH_RECUR_YEARLY:
            mcal_event_set_recur_yearly($driver->_stream,
                                        $recur_end[0],
                                        $recur_end[1],
                                        $recur_end[2],
                                        $this->getRecurInterval());
            break;
        }
    }

    function fromDriver($mcalEvent)
    {
        $this->title = $mcalEvent->title;
        if (isset($mcalEvent->category)) {
            $this->category = $mcalEvent->category;
        }
        if (isset($mcalEvent->description)) {
            $this->description = $mcalEvent->description;
        }
        if (isset($mcalEvent->attrlist['creatorid'])) {
            $this->creatorID = $mcalEvent->attrlist['creatorid'];
        }
        if (isset($mcalEvent->attrlist['location'])) {
            $this->location = $mcalEvent->attrlist['location'];
        }
        if (isset($mcalEvent->attrlist['keywords'])) {
            $this->keywords = explode(',', $mcalEvent->attrlist['keywords']);
        }
        if (isset($mcalEvent->attrlist['exceptions'])) {
            $this->exceptions = explode(',', $mcalEvent->attrlist['exceptions']);
        }
        $this->eventID = $mcalEvent->id;

        $this->startTimestamp = Kronolith_Driver_mcal::parseMCALDate($mcalEvent->start);
        $this->start = Kronolith::timestampToObject($this->startTimestamp);

        $this->endTimestamp = Kronolith_Driver_mcal::parseMCALDate($mcalEvent->end);
        $this->end = Kronolith::timestampToObject($this->endTimestamp);

        $this->durMin = ($this->endTimestamp - $this->startTimestamp) / 60;

        if (isset($mcalEvent->recur_enddate)) {
            $this->recurEndTimestamp = Kronolith_Driver_mcal::parseMCALDate($mcalEvent->recur_enddate);
            $this->recurEnd = $mcalEvent->recur_enddate;
        }

        $this->alarm = $mcalEvent->alarm;

        $this->recurType = $mcalEvent->recur_type;
        $this->recurInterval = $mcalEvent->recur_interval;
        if (isset($mcalEvent->recur_data)) {
            $this->recurData = $mcalEvent->recur_data;
        }

        $this->initialized = true;
        $this->stored = true;
    }

}

--- NEW FILE: sql.php ---
<?php
/**
 * The Kronolith_Driver_sql:: class implements the Kronolith_Driver
 * API for a SQL backend.
 *
 * $Horde: kronolith/lib/Driver/sql.php,v 1.121 2004/05/22 13:02:13 mdjukic Exp $
 *
 * @author  Luc Saillard <luc.saillard at fr.alcove.com>
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Kronolith 0.3
 * @package Kronolith
 */
class Kronolith_Driver_sql extends Kronolith_Driver {

    /**
     * The object handle for the current database connection.
     *
     * @var object DB $_db
     */
    var $_db;

    /**
     * Boolean indicating whether or not we're currently connected to
     * the SQL server.
     *
     * @var boolean $_connected
     */
    var $_connected = false;

    /**
     * Cache events as we fetch them to avoid fetching the same event
     * from the DB twice.
     *
     * @var array $_cache
     */
    var $_cache = array();

    function open($calendar)
    {
        $this->_calendar = $calendar;
        $this->_connect();
    }

    function listAlarms($date)
    {
        $allevents = $this->listEvents($date, $date, true);
        $events = array();

        foreach ($allevents as $eventId) {
            $event = &$this->getEvent($eventId);

            if ($event->getRecurType() == KRONOLITH_RECUR_NONE) {
                $start = &Kronolith::dateObject($event->start);
                $start->min -= $event->getAlarm();
                $start->correct();
                if (Kronolith::compareDateTimes($start, $date) <= 0 &&
                    Kronolith::compareDateTimes($date, $event->end) <= -1) {
                    $events[] = $eventId;
                }
            } else {
                if ($next = $this->nextRecurrence($eventId, $date)) {
                    $start = &Kronolith::dateObject($next);
                    $start->min -= $event->getAlarm();
                    $start->correct();
                    if (Kronolith::compareDateTimes($start, $date) <= 0 &&
                        Kronolith::compareDateTimes($date, Kronolith::dateObject(array('year' => $next->year,
                                                                                       'month' => $next->month,
                                                                                       'mday' => $next->mday,
                                                                                       'hour' => $event->end->hour,
                                                                                       'min' => $event->end->min,
                                                                                       'sec' => $event->end->sec)) <= -1)) {
                        $events[] = $eventId;
                    }
                }
            }
        }

        return is_array($events) ? $events : array();
    }

    function listEvents($startDate = null, $endDate = null, $hasAlarm = false)
    {
        $endInterval = &new Kronolith_Date($endDate);
        if (!isset($endDate)) {
            $endInterval = Kronolith::dateObject(array('mday' => 31, 'month' => 12, 'year' => 9999));
        } else {
            list($endInterval->mday, $endInterval->month, $endInterval->year) = explode('/', Date_Calc::nextDay($endDate->mday, $endDate->month, $endDate->year, '%d/%m/%Y'));
        }
        $etime = sprintf('%04d-%02d-%02d 00:00:00', $endInterval->year, $endInterval->month, $endInterval->mday);

        $startInterval = &new Kronolith_Date($startDate);
        if (isset($startDate)) {
            if ($startDate === 0) {
                $startInterval = Kronolith::dateObject(array('mday' => 1, 'month' => 1, 'year' => 0000));
            }
            if ($startInterval->month == 0) {
                $startInterval->month = 1;
            }
            if ($startInterval->mday == 0) {
                $startInterval->mday = 1;
            }
            $stime = sprintf('%04d-%02d-%02d 00:00:00', $startInterval->year, $startInterval->month, $startInterval->mday);
        }

        $q = 'SELECT DISTINCT event_id, event_description, event_location,' .
            ' event_status, event_attendees,' .
            ' event_keywords, event_title, event_category,' .
            ' event_recurtype, event_recurenddate, event_recurinterval,' .
            ' event_recurdays, event_start, event_end, event_alarm,' .
            ' event_modified, event_exceptions, event_creator_id FROM ' . $this->_params['table'] .
            ' WHERE calendar_id = ' . $this->_db->quote($this->_calendar) . ' AND ((';

        if ($hasAlarm) {
            $q .= 'event_alarm > 0)) AND ((';
        }

        if (isset($stime)) {
            $q .= 'event_end > ' . $this->_db->quote($stime) . ' AND ';
        }
        $q .= 'event_start < ' . $this->_db->quote($etime) . ') OR (';
        if (isset($stime)) {
            $q .= 'event_recurenddate >= ' . $this->_db->quote($stime) . ' AND ';
        }
        $q .= 'event_start <= ' . $this->_db->quote($etime) .
            ' AND event_recurtype != ' . KRONOLITH_RECUR_NONE . '))';

        /* Log the query at a DEBUG log level. */
        Horde::logMessage(sprintf('SQL event list by %s: query = "%s"',
                                  Auth::getAuth(), $q),
                          __FILE__, __LINE__, PEAR_LOG_DEBUG);

        /* Run the query. */
        $qr = $this->_db->query($q);

        $events = array();
        if (!is_a($qr, 'PEAR_Error')) {
            $row = $qr->fetchRow(DB_FETCHMODE_ASSOC);
            while ($row && !is_a($row, 'PEAR_Error')) {
                // We have all the information we need to create an
                // event object for this event, so go ahead and cache
                // it.
                $this->_cache[$this->_calendar][$row['event_id']] = &new Kronolith_Event_sql($this, $row);

                if ($row['event_recurtype'] == KRONOLITH_RECUR_NONE) {
                    $events[$this->getGUID($row['event_id'])] = $row['event_id'];
                } else {
                    $next = $this->nextRecurrence($row['event_id'], $startInterval);
                    if ($next && Kronolith::compareDates($next, $endInterval) < 0) {
                        $events[$this->getGUID($row['event_id'])] = $row['event_id'];
                    }
                }

                $row = $qr->fetchRow(DB_FETCHMODE_ASSOC);
            }
        }

        return $events;
    }

    function &getEvent($eventId = null)
    {
        if (is_null($eventId)) {
            return $ret = &new Kronolith_Event_sql($this);
        }

        if (isset($this->_cache[$this->_calendar][$eventId])) {
            return $this->_cache[$this->_calendar][$eventId];
        }

        $query = 'SELECT event_id, event_description, event_location,' .
            ' event_status, event_attendees,' .
            ' event_keywords, event_title, event_category,' .
            ' event_recurtype, event_recurenddate, event_recurinterval,' .
            ' event_recurdays, event_start, event_end, event_alarm,' .
            ' event_modified, event_exceptions, event_creator_id' .
            ' FROM ' . $this->_params['table'] .
            ' WHERE event_id = ' . $this->_db->quote($eventId) .
            ' AND calendar_id = ' . $this->_db->quote($this->_calendar);

        /* Log the query at a DEBUG log level. */
        Horde::logMessage(sprintf('SQL event fetch by %s: query = "%s"',
                                  Auth::getAuth(), $query),
                          __FILE__, __LINE__, PEAR_LOG_DEBUG);

        $event = &$this->_db->getRow($query, DB_FETCHMODE_ASSOC);
        if (is_a($event, 'PEAR_Error')) {
            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
            return $event;
        }

        if ($event) {
            $this->_cache[$this->_calendar][$eventId] = &new Kronolith_Event_sql($this, $event);
            return $this->_cache[$this->_calendar][$eventId];
        } else {
            return false;
        }
    }

    function &getByGUID($guid)
    {
        /* Validate the GUID. */
        if (substr($guid, 0, 10) != 'kronolith:') {
            return PEAR::raiseError('Invalid GUID');
        }
        $guid = substr($guid, 10);

        $this->_connect();

        $query = 'SELECT event_id, calendar_id, event_description, event_location,' .
            ' event_status, event_attendees,' .
            ' event_keywords, event_title, event_category,' .
            ' event_recurtype, event_recurenddate, event_recurinterval,' .
            ' event_recurdays, event_start, event_end, event_alarm,' .
            ' event_modified, event_exceptions, event_creator_id' .
            ' FROM ' . $this->_params['table'] .
            ' WHERE event_id = ' . $this->_db->quote($guid);

        /* Log the query at a DEBUG log level. */
        Horde::logMessage(sprintf('SQL event fetch by %s: query = "%s"',
                                  Auth::getAuth(), $query),
                          __FILE__, __LINE__, PEAR_LOG_DEBUG);

        $event = &$this->_db->getRow($query, DB_FETCHMODE_ASSOC);
        if (is_a($event, 'PEAR_Error')) {
            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
            return $event;
        }

        if ($event) {
            $this->open($event['calendar_id']);
            $this->_cache[$this->_calendar][$event['event_id']] = &new Kronolith_Event_sql($this, $event);
            return $this->_cache[$this->_calendar][$event['event_id']];
        } else {
            return PEAR::raiseError($guid . ' not found');
        }
    }

    function saveEvent($event)
    {
        if ($event->isStored()) {
            $query = 'UPDATE ' . $this->_params['table'] . ' SET ';

            foreach ($event->getProperties() as $key => $val) {
                $query .= " $key = " . $this->_db->quote($val) . ',';
            }
            $query = substr($query, 0, -1);
            $query .= ' WHERE event_id = ' . $this->_db->quote($event->getID());

            /* Log the query at a DEBUG log level. */
            Horde::logMessage(sprintf('SQL event update by %s: query = "%s"',
                                      Auth::getAuth(), $query),
                              __FILE__, __LINE__, PEAR_LOG_DEBUG);

            $res = $this->_db->query($query);
            if (is_a($res, 'PEAR_Error')) {
                Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
                return $res;
            }

            /* Log the modification of this item in the history
             * log. */
            $history = &Horde_History::singleton();
            $history->log($this->getGUID($event->getID()), array('action' => 'modify'), true);

            return $event->getID();
        } else {
            if ($event->getID()) {
                $id = $event->getID();
            } else {
                $id = md5(uniqid(mt_rand(), true));
            }

            $query = 'INSERT INTO ' . $this->_params['table'] . ' ';
            $cols_name = '(event_id,';
            $cols_values = 'values (' . $this->_db->quote($id) . ',';

            foreach ($event->getProperties() as $key => $val) {
                $cols_name .= " $key,";
                $cols_values .= $this->_db->quote($val) . ',';
            }

            $cols_name .= ' calendar_id)';
            $cols_values .= $this->_db->quote($this->_calendar) . ') ';

            $query .= $cols_name . $cols_values;

            /* Log the query at a DEBUG log level. */
            Horde::logMessage(sprintf('SQL event store by %s: query = "%s"',
                                Auth::getAuth(), $query),
                                __FILE__, __LINE__, PEAR_LOG_DEBUG);

            $res = $this->_db->query($query);
            if (is_a($res, 'PEAR_Error')) {
                Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
                return $res;
            }

            /* Log the creation of this item in the history log. */
            $history = &Horde_History::singleton();
            $history->log($this->getGUID($id), array('action' => 'add'), true);

            return $id;
        }
    }

    /**
     * Move an event to a new calendar.
     *
     * @param string $eventId      The event to move.
     * @param string $newCalendar  The new calendar.
     */
    function move($eventId, $newCalendar)
    {
        /* Make sure we have a valid database connection. */
        $this->_connect();

        $query = sprintf('UPDATE %s SET calendar_id = %s WHERE calendar_id = %s AND event_id = %s',
                         $this->_params['table'],
                         $this->_db->quote($newCalendar),
                         $this->_db->quote($this->_calendar),
                         $this->_db->quote($eventId));

        /* Log the query at a DEBUG log level. */
        Horde::logMessage(sprintf('Kronolith_Driver_sql::move(): %s', $query),
                          __FILE__, __LINE__, PEAR_LOG_DEBUG);

        /* Attempt the move query. */
        $result = $this->_db->query($query);
        if (is_a($result, 'PEAR_Error')) {
            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
            return $result;
        }

        return true;
    }

    /**
     * Delete a calendar and all its events.
     *
     * @param string $calendar  The name of the calendar to delete.
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function delete($calendar)
    {
        $this->_connect();

        $query = sprintf('DELETE FROM %s WHERE calendar_id = %s',
                         $this->_params['table'],
                         $this->_db->quote($calendar));

        /* Log the query at a DEBUG log level. */
        Horde::logMessage(sprintf('SQL Calender Delete by %s: query = "%s"',
                                  Auth::getAuth(), $query),
                          __FILE__, __LINE__, PEAR_LOG_DEBUG);

        return $this->_db->query($query);
    }

    /**
     * Delete an event.
     *
     * @param string $eventId  The ID of the event to delete.
     *
     * @return mixed  True or a PEAR_Error on failure.
     */
    function deleteEvent($eventId)
    {
        $query = sprintf('DELETE FROM %s WHERE event_id = %s AND calendar_id = %s',
                         $this->_params['table'],
                         $this->_db->quote($eventId),
                         $this->_db->quote($this->_calendar));

        /* Log the query at a DEBUG log level. */
        Horde::logMessage(sprintf('SQL Event Delete by %s: query = "%s"',
                                  Auth::getAuth(), $query),
                          __FILE__, __LINE__, PEAR_LOG_DEBUG);

        $result = $this->_db->query($query);
        if (is_a($result, 'PEAR_Error')) {
            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
            return $result;
        }

        /* Log the deletion of this item in the history log. */
        $history = &Horde_History::singleton();
        $history->log($this->getGUID($eventId), array('action' => 'delete'), true);

        return true;
    }

    /**
     * Attempts to open a persistent connection to the SQL server.
     *
     * @return boolean True.
     */
    function _connect()
    {
        if (!$this->_connected) {
            Horde::assertDriverConfig($this->_params, 'calendar',
                array('phptype', 'hostspec', 'username', 'database'));

            if (!isset($this->_params['table'])) {
                $this->_params['table'] = 'kronolith_events';
            }

            /* Connect to the SQL server using the supplied parameters. */
            require_once 'DB.php';
            $this->_db = &DB::connect($this->_params,
                                      array('persistent' => !empty($this->_params['persistent'])));
            if (is_a($this->_db, 'PEAR_Error')) {
                Horde::fatal($this->_db, __FILE__, __LINE__);
            }

            /* Enable the "portability" option. */
            $this->_db->setOption('optimize', 'portability');

            $this->_connected = true;

            /* Handle any database specific initialization code to
             * run. */
            switch ($this->_db->dbsyntax) {
            case 'oci8':
                $query = "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'";

                /* Log the query at a DEBUG log level. */
                Horde::logMessage(sprintf('SQL session setup by %s: query = "%s"',
                                          Auth::getAuth(), $query),
                                  __FILE__, __LINE__, PEAR_LOG_DEBUG);

                $this->_db->query($query);
                break;
            }
        }

        return true;
    }

    function close()
    {
        return true;
    }

    /**
     * Converts a value from the driver's charset to the default
     * charset.
     *
     * @param mixed $value  A value to convert.
     *
     * @return mixed  The converted value.
     */
    function convertFromDriver($value)
    {
        return String::convertCharset($value, $this->_params['charset']);
    }

    /**
     * Converts a value from the default charset to the driver's
     * charset.
     *
     * @param mixed $value  A value to convert.
     *
     * @return mixed  The converted value.
     */
    function convertToDriver($value)
    {
        return String::convertCharset($value, NLS::getCharset(), $this->_params['charset']);
    }

}

class Kronolith_Event_sql extends Kronolith_Event {

    var $_properties = array();

    function fromDriver($SQLEvent)
    {
        $driver = &$this->getDriver();

        $this->start = &new stdClass();
        $this->end = &new stdClass();
        list($this->start->year, $this->start->month, $this->start->mday, $this->start->hour, $this->start->min, $this->start->sec) = sscanf($SQLEvent['event_start'], '%04d-%02d-%02d %02d:%02d:%02d');
        list($this->end->year, $this->end->month, $this->end->mday, $this->end->hour, $this->end->min, $this->end->sec) = sscanf($SQLEvent['event_end'], '%04d-%02d-%02d %02d:%02d:%02d');

        $this->startTimestamp = mktime($this->start->hour, $this->start->min, $this->start->sec, $this->start->month, $this->start->mday, $this->start->year);
        $this->endTimestamp = mktime($this->end->hour, $this->end->min, $this->end->sec, $this->end->month, $this->end->mday, $this->end->year);

        $this->durMin = ($this->endTimestamp - $this->startTimestamp) / 60;

        if (isset($SQLEvent['event_recurenddate'])) {
            $this->recurEnd = &new stdClass();
            list($this->recurEnd->year, $this->recurEnd->month, $this->recurEnd->mday, $this->recurEnd->hour, $this->recurEnd->min, $this->recurEnd->sec) = sscanf($SQLEvent['event_recurenddate'], '%04d-%02d-%02d %02d:%02d:%02d');
            $this->recurEndTimestamp = @mktime($this->recurEnd->hour, $this->recurEnd->min, $this->recurEnd->sec, $this->recurEnd->month, $this->recurEnd->mday, $this->recurEnd->year);
        }

        $this->title = $driver->convertFromDriver($SQLEvent['event_title']);
        $this->eventID = $SQLEvent['event_id'];
        $this->creatorID = $SQLEvent['event_creator_id'];
        $this->recurType = (int)$SQLEvent['event_recurtype'];
        $this->recurInterval = (int)$SQLEvent['event_recurinterval'];

        if (isset($SQLEvent['event_category'])) {
            $this->category = $SQLEvent['event_category'];
        }
        if (isset($SQLEvent['event_location'])) {
            $this->location = $driver->convertFromDriver($SQLEvent['event_location']);
        }
        if (isset($SQLEvent['event_status'])) {
            $this->status = $SQLEvent['event_status'];
        }
        if (isset($SQLEvent['event_attendees'])) {
            $this->attendees = unserialize($driver->convertFromDriver($SQLEvent['event_attendees']));
        }
        if (isset($SQLEvent['event_keywords'])) {
            $this->keywords = explode(',', $driver->convertFromDriver($SQLEvent['event_keywords']));
        }
        if (isset($SQLEvent['event_exceptions'])) {
            $this->exceptions = explode(',', $SQLEvent['event_exceptions']);
        }
        if (isset($SQLEvent['event_description'])) {
            $this->description = $driver->convertFromDriver($SQLEvent['event_description']);
        }
        if (isset($SQLEvent['event_alarm'])) {
            $this->alarm = (int)$SQLEvent['event_alarm'];
        }
        if (isset($SQLEvent['event_recurdays'])) {
            $this->recurData = (int)$SQLEvent['event_recurdays'];
        }

        $this->initialized = true;
        $this->stored = true;
    }

    function toDriver()
    {
        $driver = &$this->getDriver();

        // Basic fields.
        $this->_properties['event_creator_id'] = $driver->convertToDriver($this->getCreatorID());
        $this->_properties['event_title'] = $driver->convertToDriver($this->title);
        $this->_properties['event_description'] = $driver->convertToDriver($this->getDescription());
        $this->_properties['event_category'] = $driver->convertToDriver($this->getCategory());
        $this->_properties['event_location'] = $driver->convertToDriver($this->getLocation());
        $this->_properties['event_status'] = $this->getStatus();
        $this->_properties['event_attendees'] = $driver->convertToDriver(serialize($this->getAttendees()));
        $this->_properties['event_keywords'] = $driver->convertToDriver(implode(',', $this->getKeywords()));
        $this->_properties['event_exceptions'] = implode(',', $this->getExceptions());
        $this->_properties['event_modified'] = time();

        $this->_properties['event_start'] = date('Y-m-d H:i:s', $this->getStartTimestamp());

        // Event end.
        $this->_properties['event_end'] = date('Y-m-d H:i:s', $this->getEndTimestamp());

        // Alarm.
        $this->_properties['event_alarm'] = $this->getAlarm();

        // Recurrence.
        $recur_end = explode(':', @date('Y:n:j', $this->getRecurEndTimestamp()));
        if (empty($recur_end[0]) || $recur_end[0] <= 1970) {
            $recur_end[0] = 9999;
            $recur_end[1] = 12;
            $recur_end[2] = 31;
        }

        $recur = $this->getRecurType();
        $this->_properties['event_recurtype'] = $recur;
        if ($recur != KRONOLITH_RECUR_NONE) {
            $this->_properties['event_recurinterval'] = $this->getRecurInterval();
            $this->_properties['event_recurenddate'] = sprintf('%04d%02d%02d', $recur_end[0],
                                                               $recur_end[1], $recur_end[2]);

            switch ($recur) {
            case KRONOLITH_RECUR_WEEKLY:
                $this->_properties['event_recurdays'] = $this->getRecurOnDays();
                break;
            }
        }
    }

    function getProperties()
    {
        return $this->_properties;
    }

}





More information about the commits mailing list