Skip to content
Snippets Groups Projects
iCalcreator.class.php 314 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    /*********************************************************************************/
    /**
     * iCalcreator class v2.6
     * copyright (c) 2007-2008 Kjell-Inge Gustafsson kigkonsult
     * www.kigkonsult.se/iCalcreator/index.php
     * ical@kigkonsult.se
     *
     * Description:
     * This file is a PHP implementation of RFC 2445.
     *
     * This library is free software; you can redistribute it and/or
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
     * version 2.1 of the License, or (at your option) any later version.
     *
     * This library is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * Lesser General Public License for more details.
     *
     * You should have received a copy of the GNU Lesser General Public
     * License along with this library; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     */
    /*********************************************************************************/
    /*********************************************************************************/
    /*         A little setup                                                        */
    /*********************************************************************************/
    
    /* your local language code */
    
    // define( 'ICAL_LANG', 'sv' );
    
    /*
    $langstr     = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
    $pos         = strpos( $langstr, ';' );
    if ($pos   !== false) {
      $langstr   = substr( $langstr, 0, $pos );
      $pos       = strpos( $langstr, ',' );
      if ($pos !== false) {
        $pos     = strpos( $langstr, ',' );
        $langstr = substr( $langstr, 0, $pos );
      }
      define( 'ICAL_LANG', $langstr );
    }
    */
    
    /* only for phpversion 5.x, date management, default timezone setting */
    if (substr(phpversion(), 0, 1) >= '5') // && ( 'UTC' == date_default_timezone_get() )) {
        date_default_timezone_set('Europe/Stockholm');
    /* version string, do NOT remove!! */
    define('ICALCREATOR_VERSION', 'iCalcreator 2.6');
    
    /*********************************************************************************/
    /*********************************************************************************/
    
    /**
     * vcalendar class
     *
     * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
     * @since 2.2.13 - 2007-12-30
     */
    
    class vcalendar
    {
        //  calendar property variables
        var $calscale;
        var $method;
        var $prodid;
        var $version;
        var $xprop;
        //  container for calendar components
        var $components;
        //  component config variables
        var $allowEmpty;
        var $unique_id;
        var $language;
        var $directory;
        var $filename;
        var $url;
        var $delimiter;
        var $nl;
        var $format;
        //  component internal variables
        var $attributeDelimiter;
        var $valueInit;
        //  component xCal declaration container
        var $xcaldecl;
    
        /*
    
     * constructor for calendar object
     *
     * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
     * @since 2.2.13 - 2007-12-30
     * @return void
     */
    
        function vcalendar()
        {
            $this->_makeVersion();
            $this->calscale = null;
            $this->method = null;
            $this->_makeUnique_id();
            $this->prodid = null;
            $this->xprop = array();
            /**
             *   language = <Text identifying a language, as defined in [RFC 1766]>
             */
            if (defined('ICAL_LANG'))
                $this->setConfig('language', ICAL_LANG);
            $this->setConfig('allowEmpty', TRUE);
            $this->setConfig('nl', "\n");
            $this->setConfig('format', 'iCal');
            $this->directory = null;
            $this->filename = null;
            $this->url = null;
            $this->setConfig('delimiter', DIRECTORY_SEPARATOR);
            $this->xcaldecl = array();
            $this->components = array();
        }
        /*********************************************************************************/
        /**
         * Property Name: CALSCALE
         */
        /**
         * creates formatted output for calendar property calscale
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.8 - 2008-10-21
         * @return string
         */
        function createCalscale()
        {
            if (empty($this->calscale)) return FALSE;
            switch ($this->format) {
                case 'xcal':
                    return ' calscale="' . $this->calscale . '"' . $this->nl;
                    break;
                default:
                    return 'CALSCALE:' . $this->calscale . $this->nl;
                    break;
            }
    
        /**
         * set calendar property calscale
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.8 - 2008-10-21
         * @param string $value
         * @return void
         */
        function setCalscale($value)
        {
            if (empty($value)) return FALSE;
            $this->calscale = $value;
        }
        /*********************************************************************************/
        /**
         * Property Name: METHOD
         */
        /**
         * creates formatted output for calendar property method
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 0.9.7 - 2006-11-20
         * @return string
         */
        function createMethod()
        {
            if (empty($this->method)) return FALSE;
            switch ($this->format) {
                case 'xcal':
                    return ' method="' . $this->method . '"' . $this->nl;
                    break;
                default:
                    return 'METHOD:' . $this->method . $this->nl;
                    break;
    
        }
    
        /**
         * set calendar property method
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.8 - 2008-20-23
         * @param string $value
         * @return bool
         */
        function setMethod($value)
        {
            if (empty($value)) return FALSE;
            $this->method = $value;
    
        /*********************************************************************************/
        /**
         * Property Name: PRODID
         *
         *  The identifier is RECOMMENDED to be the identical syntax to the
         * [RFC 822] addr-spec. A good method to assure uniqueness is to put the
         * domain name or a domain literal IP address of the host on which.. .
         */
        /**
         * creates formatted output for calendar property prodid
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 0.9.7 - 2006-11-20
         * @return string
         */
        function createProdid()
        {
            if (!isset($this->prodid))
                $this->_makeProdid();
            switch ($this->format) {
                case 'xcal':
                    return ' prodid="' . $this->prodid . '"' . $this->nl;
                    break;
                default:
                    return 'PRODID:' . $this->prodid . $this->nl;
                    break;
    
        }
    
        /**
         * make default value for calendar prodid
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 0.3.0 - 2006-08-10
         * @return void
         */
        function _makeProdid()
        {
            $this->prodid = '-//' . $this->unique_id . '//NONSGML ' . ICALCREATOR_VERSION . '//' . strtoupper($this->language);
        }
        /**
         * Conformance: The property MUST be specified once in an iCalendar object.
         * Description: The vendor of the implementation SHOULD assure that this
         * is a globally unique identifier; using some technique such as an FPI
         * value, as defined in [ISO 9070].
         */
        /**
         * make default unique_id for calendar prodid
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 0.3.0 - 2006-08-10
         * @return void
         */
        function _makeUnique_id()
        {
            $this->unique_id = (isset($_SERVER['SERVER_NAME'])) ? gethostbyname($_SERVER['SERVER_NAME']) : 'localhost';
        }
        /*********************************************************************************/
        /**
         * Property Name: VERSION
         *
         * Description: A value of "2.0" corresponds to this memo.
         */
        /**
         * creates formatted output for calendar property version
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 0.9.7 - 2006-11-20
         * @return string
         */
        function createVersion()
        {
            if (empty($this->version))
                $this->_makeVersion();
            switch ($this->format) {
                case 'xcal':
                    return ' version="' . $this->version . '"' . $this->nl;
                    break;
                default:
                    return 'VERSION:' . $this->version . $this->nl;
                    break;
    
    
        /**
         * set default calendar version
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 0.3.0 - 2006-08-10
         * @return void
         */
        function _makeVersion()
        {
            $this->version = '2.0';
    
    
        /**
         * set calendar version
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.8 - 2008-10-23
         * @param string $value
         * @return void
         */
        function setVersion($value)
        {
            if (empty($value)) return FALSE;
            $this->version = $value;
    
        /*********************************************************************************/
        /**
         * Property Name: x-prop
         */
        /**
         * creates formatted output for calendar property x-prop, iCal format only
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.11 - 2008-11-03
         * @return string
         */
        function createXprop()
        {
            if ('xcal' == $this->format)
                return false;
            if (0 >= count($this->xprop))
                return;
            $output = null;
            $toolbox = new calendarComponent();
            $toolbox->setConfig('language', $this->getConfig('language'));
            $toolbox->setConfig('nl', $this->getConfig('nl'));
            $toolbox->_createFormat($this->getConfig('format'));
            foreach ($this->xprop as $label => $xpropPart) {
                if (empty($xpropPart['value'])) {
                    $output .= $toolbox->_createElement($label);
    
                $attributes = $toolbox->_createParams($xpropPart['params'], array('LANGUAGE'));
                if (is_array($xpropPart['value'])) {
                    foreach ($xpropPart['value'] as $pix => $theXpart)
                        $xpropPart['value'][$pix] = $toolbox->_strrep($theXpart);
                    $xpropPart['value'] = implode(',', $xpropPart['value']);
                } else
                    $xpropPart['value'] = $toolbox->_strrep($xpropPart['value']);
                $output .= $toolbox->_createElement($label, $attributes, $xpropPart['value']);
    
    
        /**
         * set calendar property x-prop
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.11 - 2008-11-04
         * @param string $label
         * @param string $value
         * @param array $params optional
         * @return bool
         */
        function setXprop($label, $value, $params = FALSE)
        {
            if (empty($value)) if ($this->getConfig('allowEmpty')) $value = null; else return FALSE;
            if (empty($label)) return FALSE;
            $xprop = array('value' => $value);
            $toolbox = new calendarComponent();
            $xprop['params'] = $toolbox->_setParams($params);
            if (!is_array($this->xprop)) $this->xprop = array();
            $this->xprop[strtoupper($label)] = $xprop;
            return TRUE;
    
        /*********************************************************************************/
        /**
         * delete calendar property value
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.5 - 2008-11-14
         * @param mixed $propName , bool FALSE => X-property
         * @param int @propix, optional, if specific property is wanted in case of multiply occurences
         * @return bool, if successfull delete
         */
        function deleteProperty($propName, $propix = FALSE)
        {
            $propName = ($propName) ? strtoupper($propName) : 'X-PROP';
            if (!$propix)
                $propix = (isset($this->propdelix[$propName])) ? $this->propdelix[$propName] + 2 : 1;
            $this->propdelix[$propName] = --$propix;
            $return = FALSE;
            switch ($propName) {
                case 'CALSCALE':
                    if (isset($this->calscale)) {
                        $this->calscale = null;
                        $return = TRUE;
                    }
                    break;
                case 'METHOD':
                    if (isset($this->method)) {
                        $this->method = null;
                        $return = TRUE;
                    }
                    break;
    
                    $reduced = array();
                    if ($propName != 'X-PROP') {
                        if (!isset($this->xprop[$propName])) return FALSE;
                        foreach ($this->xprop as $k => $a) {
                            if (($k != $propName) && !empty($a))
                                $reduced[$k] = $a;
                        }
                    } else {
                        if (count($this->xprop) <= $propix) return FALSE;
                        $xpropno = 0;
                        foreach ($this->xprop as $xpropkey => $xpropvalue) {
                            if ($propix != $xpropno)
                                $reduced[$xpropkey] = $xpropvalue;
                            $xpropno++;
                        }
                    }
                    $this->xprop = $reduced;
                    return TRUE;
    
            return $return;
        }
    
        /**
         * get calendar property value/params
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.5.1 - 2008-11-02
         * @param string $propName , optional
         * @param int @propix, optional, if specific property is wanted in case of multiply occurences
         * @param bool $inclParam =FALSE
         * @return mixed
         */
        function getProperty($propName = FALSE, $propix = FALSE, $inclParam = FALSE)
        {
            $propName = ($propName) ? strtoupper($propName) : 'X-PROP';
            if ('X-PROP' == $propName) {
                if (!$propix)
                    $propix = (isset($this->propix[$propName])) ? $this->propix[$propName] + 2 : 1;
                $this->propix[$propName] = --$propix;
    
            switch ($propName) {
                case 'CALSCALE':
                    return (!empty($this->calscale)) ? $this->calscale : null;
                    break;
                case 'METHOD':
                    return (!empty($this->method)) ? $this->method : null;
                    break;
                case 'PRODID':
                    if (empty($this->prodid))
                        $this->_makeProdid();
                    return $this->prodid;
                    break;
                case 'VERSION':
                    return (!empty($this->version)) ? $this->version : null;
                    break;
                default:
                    if ($propName != 'X-PROP') {
                        if (!isset($this->xprop[$propName])) return FALSE;
                        return ($inclParam) ? array($propName, $this->xprop[$propName])
                            : array($propName, $this->xprop[$propName]['value']);
                    } else {
                        if (empty($this->xprop)) return FALSE;
                        $xpropno = 0;
                        foreach ($this->xprop as $xpropkey => $xpropvalue) {
                            if ($propix == $xpropno)
                                return ($inclParam) ? array($xpropkey, $this->xprop[$xpropkey])
                                    : array($xpropkey, $this->xprop[$xpropkey]['value']);
                            else
                                $xpropno++;
                        }
                        return FALSE; // not found ??
    
        /**
         * general vcalendar property setting
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.2.13 - 2007-11-04
         * @param mixed $args variable number of function arguments,
         *                    first argument is ALWAYS component name,
         *                    second ALWAYS component value!
         * @return bool
         */
        function setProperty()
        {
            $numargs = func_num_args();
            if (1 > $numargs)
                return FALSE;
            $arglist = func_get_args();
            $arglist[0] = strtoupper($arglist[0]);
            switch ($arglist[0]) {
                case 'CALSCALE':
                    return $this->setCalscale($arglist[1]);
                case 'METHOD':
                    return $this->setMethod($arglist[1]);
                case 'VERSION':
                    return $this->setVersion($arglist[1]);
                default:
                    if (!isset($arglist[1])) $arglist[1] = null;
                    if (!isset($arglist[2])) $arglist[2] = null;
                    return $this->setXprop($arglist[0], $arglist[1], $arglist[2]);
            }
    
        /*********************************************************************************/
        /**
         * get vcalendar config values or * calendar components
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.10 - 2008-10-23
         * @param string $config
         * @return value
         */
        function getConfig($config)
        {
            switch (strtoupper($config)) {
                case 'ALLOWEMPTY':
                    return $this->allowEmpty;
                    break;
                case 'COMPSINFO':
                    unset($this->compix);
                    $info = array();
                    foreach ($this->components as $cix => $component) {
                        if (empty($component)) continue;
                        unset($component->propix);
                        $info[$cix]['ordno'] = $cix + 1;
                        $info[$cix]['type'] = $component->objName;
                        $info[$cix]['uid'] = $component->getProperty('uid');
                        $info[$cix]['props'] = $component->getConfig('propinfo');
                        $info[$cix]['sub'] = $component->getConfig('compsinfo');
                        unset($component->propix);
    
                case 'DELIMITER':
                    return $this->delimiter;
    
                case 'DIRECTORY':
                    if (empty($this->directory))
                        $this->directory = '.';
                    return $this->directory;
    
                case 'DIRFILE':
                    return $this->getConfig('directory') . $this->getConfig('delimiter') . $this->getConfig('filename');
    
                case 'FILEINFO':
                    return array($this->getConfig('directory')
                    , $this->getConfig('filename')
                    , $this->getConfig('filesize'));
    
                case 'FILENAME':
                    if (empty($this->filename)) {
                        if ('xcal' == $this->format)
                            $this->filename = date('YmdHis') . '.xml'; // recommended xcs.. .
                        else
                            $this->filename = date('YmdHis') . '.ics';
                    }
                    return $this->filename;
                    break;
                case 'FILESIZE':
                    $size = 0;
                    if (empty($this->url)) {
                        $dirfile = $this->getConfig('dirfile');
                        if (FALSE === ($size = filesize($dirfile)))
                            $size = 0;
                        clearstatcache();
                    }
                    return $size;
                    break;
                case 'FORMAT':
                    return $this->format;
                    break;
                case 'LANGUAGE':
                    /* get language for calendar component as defined in [RFC 1766] */
                    return $this->language;
                    break;
                case 'NL':
                case 'NEWLINECHAR':
                    return $this->nl;
                    break;
                case 'UNIQUE_ID':
                    return $this->unique_id;
                    break;
                case 'URL':
                    if (!empty($this->url))
                        return $this->url;
                    else
                        return FALSE;
                    break;
            }
        }
    
        /**
         * general vcalendar config setting
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.8 - 2008-10-24
         * @param string $config
         * @param string $value
         * @return void
         */
        function setConfig($config, $value)
        {
            $res = FALSE;
            switch (strtoupper($config)) {
                case 'ALLOWEMPTY':
                    $this->allowEmpty = $value;
                    $subcfg = array('ALLOWEMPTY' => $value);
                    $res = TRUE;
                    break;
                case 'DELIMITER':
                    $this->delimiter = $value;
                    return TRUE;
                    break;
                case 'DIRECTORY':
                    $value = trim($value);
                    $nl = $this->getConfig('delimiter');
                    if ($nl == substr($value, (0 - strlen($nl))))
                        $value = substr($value, 0, (strlen($value) - strlen($nl)));
                    if (is_dir($value)) {
                        /* local directory */
                        clearstatcache();
                        $this->directory = $value;
                        $this->url = null;
                        return TRUE;
                    } else
                        return FALSE;
                    break;
                case 'FILENAME':
                    $value = trim($value);
                    if (!empty($this->url)) {
                        /* remote directory+file - URL */
                        $this->filename = $value;
                        return TRUE;
                    }
                    $dirfile = $this->getConfig('directory') . $this->getConfig('delimiter') . $value;
                    if (file_exists($dirfile)) {
                        /* local existing file */
                        if (is_readable($dirfile) || is_writable($dirfile)) {
                            clearstatcache();
                            $this->filename = $value;
                            return TRUE;
                        } else
                            return FALSE;
                    } elseif (FALSE !== touch($dirfile)) {
                        /* new local file created */
                        $this->filename = $value;
                        return TRUE;
                    } else
                        return FALSE;
                    break;
                case 'FORMAT':
                    $value = trim($value);
                    if ('xcal' == strtolower($value)) {
                        $this->format = 'xcal';
                        $this->attributeDelimiter = $this->nl;
                        $this->valueInit = null;
                    } else {
                        $this->format = null;
                        $this->attributeDelimiter = ';';
                        $this->valueInit = ':';
                    }
                    $subcfg = array('FORMAT' => $value);
                    $res = TRUE;
                    break;
                case 'LANGUAGE':
                    // set language for calendar component as defined in [RFC 1766]
                    $value = trim($value);
                    $this->language = $value;
                    $subcfg = array('LANGUAGE' => $value);
                    $res = TRUE;
                    break;
                case 'NL':
                case 'NEWLINECHAR':
                    $this->nl = $value;
                    $subcfg = array('NL' => $value);
                    $res = TRUE;
                    break;
                case 'UNIQUE_ID':
                    $value = trim($value);
                    $this->unique_id = $value;
                    $subcfg = array('UNIQUE_ID' => $value);
                    $res = TRUE;
                    break;
                case 'URL':
                    /* remote file - URL */
                    $value = trim($value);
                    $value = str_replace('HTTP://', 'http://', $value);
                    $value = str_replace('WEBCAL://', 'http://', $value);
                    $value = str_replace('webcal://', 'http://', $value);
                    $this->url = $value;
                    $this->directory = null;
                    $parts = pathinfo($value);
                    return $this->setConfig('filename', $parts['basename']);
                    break;
            }
            if (!$res) return FALSE;
            if (isset($subcfg) && !empty($this->components)) {
                foreach ($subcfg as $cfgkey => $cfgvalue) {
                    foreach ($this->components as $cix => $component) {
                        $res = $component->setConfig($cfgkey, $cfgvalue);
                        if (!$res)
                            break 2;
                        $this->components[$cix] = $component->copy(); // PHP4 compliant
                    }
    
            }
            return $res;
        }
        /*********************************************************************************/
        /**
         * add calendar component to container
         *
         * alias to setComponent
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 1.x.x - 2007-04-24
         * @param object $component calendar component
         * @return void
         */
        function addComponent($component)
        {
            $this->setComponent($component);
        }
    
        /**
         * delete calendar component from container
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.10 - 2008-08-05
         * @param mixed $arg1 ordno / component type / component uid
         * @param mixed $arg2 optional, ordno if arg1 = component type
         * @return void
         */
        function deleteComponent($arg1, $arg2 = FALSE)
        {
            $argType = $index = null;
            if (ctype_digit((string)$arg1)) {
                $argType = 'INDEX';
                $index = (int)$arg1 - 1;
            } elseif ((strlen($arg1) <= strlen('vfreebusy')) && (FALSE === strpos($arg1, '@'))) {
                $argType = strtolower($arg1);
                $index = (!empty($arg2) && ctype_digit((string)$arg2)) ? (( int )$arg2 - 1) : 0;
            }
            $cix1dC = 0;
            foreach ($this->components as $cix => $component) {
                if (empty($component)) continue;
                unset($component->propix);
                if (('INDEX' == $argType) && ($index == $cix)) {
                    unset($this->components[$cix]);
                    return TRUE;
                } elseif ($argType == $component->objName) {
                    if ($index == $cix1dC) {
                        unset($this->components[$cix]);
                        return TRUE;
                    }
                    $cix1dC++;
                } elseif (!$argType && ($arg1 == $component->getProperty('uid'))) {
                    unset($this->components[$cix]);
                    return TRUE;
    
            }
            return FALSE;
        }
    
        /**
         * get calendar component from container
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.10 - 2008-08-06
         * @param mixed $arg1 optional, ordno/component type/ component uid
         * @param mixed $arg2 optional, ordno if arg1 = component type
         * @return object
         */
        function getComponent($arg1 = FALSE, $arg2 = FALSE)
        {
            $index = $argType = null;
            if (!$arg1) {
                $argType = 'INDEX';
                $index = $this->compix['INDEX'] =
                    (isset($this->compix['INDEX'])) ? $this->compix['INDEX'] + 1 : 1;
            } elseif (ctype_digit((string)$arg1)) {
                $argType = 'INDEX';
                $index = (int)$arg1;
                unset($this->compix);
            } elseif ((strlen($arg1) <= strlen('vfreebusy')) && (FALSE === strpos($arg1, '@'))) {
                unset($this->compix['INDEX']);
                $argType = strtolower($arg1);
                if (!$arg2)
                    $index = $this->compix[$argType] =
                        (isset($this->compix[$argType])) ? $this->compix[$argType] + 1 : 1;
                else
                    $index = (int)$arg2;
            }
            $index -= 1;
            $ckeys = array_keys($this->components);
            if (!empty($index) && ($index > end($ckeys)))
                return FALSE;
            $cix1gC = 0;
            foreach ($this->components as $cix => $component) {
                if (empty($component)) continue;
                unset($component->propix);
                if (('INDEX' == $argType) && ($index == $cix))
                    return $component->copy();
                elseif ($argType == $component->objName) {
                    if ($index == $cix1gC)
                        return $component->copy();
                    $cix1gC++;
                } elseif (!$argType && ($arg1 == $component->getProperty('uid'))) {
                    unset($component->propix);
                    return $component->copy();
    
            }
            /* not found.. . */
            unset($this->compix);
            return FALSE;
    
    
        /**
         * select components from calendar on date basis
         *
         * Ensure DTSTART is set for every component.
         * No date controls occurs.
         *
         * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
         * @since 2.4.16 - 2008-10-18
         * @param int $startY optional,  start Year, default current Year
         * @param int $startM optional,  start Month, default current Month
         * @param int $startD optional,  start Day, default current Day
         * @param int $endY optional,    end Year, default $startY
         * @param int $endY optional,    end Month, default $startM
         * @param int $endY optional,    end Day, default $startD
         * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s)
         * @param bool $flat optional,   FALSE (default) => output : array[Year][Month][Day][]
         *                               TRUE => output : array[] (ignores split)
         * @param bool $any optional,    TRUE (default) - select component that take place within period
         *                               FALSE - only components that starts within period
         * @param bool $split optional,  TRUE (default) - one component copy every day it take place during the
         *                                       period (implies flat=FALSE)
         *                               FALSE - one occurance of component only in output array</tr>
         * @return array or FALSE
         */
        function selectComponents($startY = FALSE, $startM = FALSE, $startD = FALSE, $endY = FALSE, $endM = FALSE, $endD = FALSE, $cType = FALSE, $flat = FALSE, $any = TRUE, $split = TRUE)
        {
            /* check  if empty calendar */
            if (0 >= count($this->components)) return FALSE;
            /* check default dates */
            if (!$startY) $startY = date('Y');
            if (!$startM) $startM = date('m');
            if (!$startD) $startD = date('d');
            $startDate = mktime(0, 0, 0, $startM, $startD, $startY);
            if (!$endY) $endY = $startY;
            if (!$endM) $endM = $startM;
            if (!$endD) $endD = $startD;
            $endDate = mktime(23, 59, 59, $endM, $endD, $endY);
            /* check component types */
            $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy');
            if (is_array($cType)) {
                foreach ($cType as $cix => $theType) {
                    $cType[$cix] = $theType = strtolower($theType);
                    if (!in_array($theType, $validTypes))
                        $cType[$cix] = 'vevent';
                }
                $cType = array_unique($cType);
            } elseif (!empty($cType)) {
                $cType = strtolower($cType);
                if (!in_array($cType, $validTypes))
                    $cType = array('vevent');
                else
                    $cType = array($cType);
            } else
                $cType = $validTypes;
            if (0 >= count($cType))
                $cType = $validTypes;
            /* iterate components */
            $result = array();
            foreach ($this->components as $cix => $component) {
                if (empty($component)) continue;
                unset($component->propix, $start);
                /* deselect unvalid type components */
                if (!in_array($component->objName, $cType)) continue;
                /* deselect components without dtstart set */
                if (FALSE === ($start = $component->getProperty('dtstart'))) continue;
                $dtendExist = $dueExist = $durationExist = $endAllDayEvent = FALSE;
                unset($end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $workstart, $workend); // clean up
                $startWdate = $component->_date2timestamp($start);
                $startDateFormat = (isset($start['hour'])) ? 'Y-m-d H:i:s' : 'Y-m-d';
                /* get end date from dtend/due/duration properties */
                $end = $component->getProperty('dtend');
                if (!empty($end)) {
                    $dtendExist = TRUE;
                    $endDateFormat = (isset($end['hour'])) ? 'Y-m-d H:i:s' : 'Y-m-d';
                }
                // if( !empty($end))  echo 'selectComp 1 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
                if (empty($end) && ($component->objName == 'vtodo')) {
                    $end = $component->getProperty('due');
                    if (!empty($end)) {
                        $dueExist = TRUE;
                        $endDateFormat = (isset($end['hour'])) ? 'Y-m-d H:i:s' : 'Y-m-d';
                    }
                    // if( !empty($end))  echo 'selectComp 2 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
                }
                if (!empty($end) && !isset($end['hour'])) {
                    /* a DTEND without time part regards an event that ends the day before,
                 for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */
                    $endAllDayEvent = TRUE;
                    $endWdate = mktime(23, 59, 59, $end['month'], ($end['day'] - 1), $end['year']);
                    $end['year'] = date('Y', $endWdate);
                    $end['month'] = date('m', $endWdate);
                    $end['day'] = date('d', $endWdate);
                    $end['hour'] = 23;
                    $end['min'] = $end['sec'] = 59;
                    // if( !empty($end))  echo 'selectComp 3 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
                }
                if (empty($end)) {
                    $end = $component->getProperty('duration', FALSE, FALSE, TRUE);// in dtend (array) format
                    if (!empty($end))
                        $durationExist = TRUE;
                    // if( !empty($end))  echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
                }
                if (empty($end)) { // assume one day duration if missing end date
                    $end = array('year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59);
                    // if( isset($end))  echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
                }
                $endWdate = $component->_date2timestamp($end);
                if ($endWdate < $startWdate) { // MUST be after start date!!
                    $end = array('year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59);
                    $endWdate = $component->_date2timestamp($end);
                }
                $rdurWsecs = $endWdate - $startWdate; // compute component duration in seconds
                $rdur = $component->_date2duration($start, $end); // compute component duration, array
                /* make a list of optional exclude dates for component occurence from exrule and exdate */
                $exdatelist = array();
                $workstart = $component->_timestamp2date(($startDate - $rdurWsecs), 6);
                $workend = $component->_timestamp2date(($endDate + $rdurWsecs), 6);
                while (FALSE !== ($exrule = $component->getProperty('exrule')))    // check exrule
                    $component->_recur2date($exdatelist, $exrule, $start, $workstart, $workend);
                while (FALSE !== ($exdate = $component->getProperty('exdate'))) {  // check exdate
                    foreach ($exdate as $theExdate) {
                        $exWdate = $component->_date2timestamp($theExdate);
                        if ((($startDate - $rdurWsecs) <= $exWdate) && ($endDate >= $exWdate))
                            $exdatelist[$exWdate] = TRUE;
                    }
                }
                /* if 'any' components, check repeating components, removing all excluding dates */
                if (TRUE === $any) {
                    /* make a list of optional repeating dates for component occurence, rrule, rdate */
                    $recurlist = array();
                    while (FALSE !== ($rrule = $component->getProperty('rrule')))    // check rrule
                        $component->_recur2date($recurlist, $rrule, $start, $workstart, $workend);
                    foreach ($recurlist as $recurkey => $recurvalue) // key=match date as timestamp
                        $recurlist[$recurkey] = $rdurWsecs; // add duration in seconds
                    while (FALSE !== ($rdate = $component->getProperty('rdate'))) {  // check rdate
                        foreach ($rdate as $theRdate) {
                            if (is_array($theRdate) && (2 == count($theRdate)) &&  // all days within PERIOD
                                array_key_exists('0', $theRdate) && array_key_exists('1', $theRdate)
                            ) {
                                $rstart = $component->_date2timestamp($theRdate[0]);
                                if (($rstart < ($startDate - $rdurWsecs)) || ($rstart > $endDate))
                                    continue;
                                if (isset($theRdate[1]['year'])) // date-date period
                                    $rend = $component->_date2timestamp($theRdate[1]);
                                else {                             // date-duration period
                                    $rend = $component->duration2date($theRdate[0], $theRdate[1]);
                                    $rend = $component->_date2timestamp($rend);
                                }
                                if ((($startDate - $rdurWsecs) <= $rstart) && ($endDate >= $rstart))
                                    $recurlist[$rstart] = ($rstart - $rend); // set start date + rdate duration in seconds
                            } // PERIOD end
                            else { // single date
                                $theRdate = $component->_date2timestamp($theRdate);
                                if ((($startDate - $rdurWsecs) <= $theRdate) && ($endDate >= $theRdate))
                                    $recurlist[$theRdate] = $rdurWsecs; // set start date + event duration in seconds
                            }
                        }
                    }
                    if (0 < count($recurlist)) {
                        ksort($recurlist);
                        foreach ($recurlist as $recurkey => $durvalue) {
                            if ((($startDate - $rdurWsecs) > $recurkey) || ($endDate < $recurkey)) // not within period
                                continue;
                            if (isset($exdatelist[$recurkey])) // check excluded dates
                                continue;
                            if ($startWdate >= $recurkey) // exclude component start date
                                continue;
                            $component2 = $component->copy();
                            $rstart = $component2->_timestamp2date($recurkey, 6);
                            $datevalue = $rstart['month'] . '/' . $rstart['day'] . '/' . $rstart['year'];
                            if (isset($start['hour']) || isset($start['min']) || isset($start['sec'])) {
                                $datevalue .= (isset($rstart['hour'])) ? ' ' . $rstart['hour'] : ' 00';
                                $datevalue .= (isset($rstart['min'])) ? ':' . $rstart['min'] : ':00';
                                $datevalue .= (isset($rstart['sec'])) ? ':' . $rstart['sec'] : ':00';
                            }
                            $datestring = date($startDateFormat, strtotime($datevalue));
                            if (isset($start['tz']))
                                $datestring .= ' ' . $start['tz'];
                            $component2->setProperty('X-CURRENT-DTSTART', $datestring);
                            $rend = $component2->_timestamp2date(($recurkey + $durvalue), 6);
                            if ($dtendExist || $dueExist) {
                                if ($endAllDayEvent) {
                                    $rend2 = mktime(0, 0, 0, $rend['month'], ($rend['day'] + 1), $rend['year']);
                                    $datevalue = date('m', $rend2) . '/' . date('d', $rend2) . '/' . date('Y', $rend2);
                                } else {
                                    $datevalue = $rend['month'] . '/' . $rend['day'] . '/' . $rend['year'];
                                    if (isset($end['hour']) || isset($end['min']) || isset($end['sec'])) {
                                        $datevalue .= (isset($rend['hour'])) ? ' ' . $rend['hour'] : ' 00';