source: trunk/library/iCalcreator/iCalcreator.class.php @ 7655

Revision 7655, 249.9 KB checked in by douglasz, 11 years ago (diff)

Ticket #3236 - Melhorias de performance no codigo do Expresso.

Line 
1<?php
2/*********************************************************************************/
3/**
4 * iCalcreator class v2.8
5 * copyright (c) 2007-2011 Kjell-Inge Gustafsson kigkonsult
6 * www.kigkonsult.se/iCalcreator/index.php
7 * ical@kigkonsult.se
8 *
9 * Description:
10 * This file is a PHP implementation of RFC 2445.
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 * Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 */
26/*********************************************************************************/
27/*********************************************************************************/
28/*         A little setup                                                        */
29/*********************************************************************************/
30            /* your local language code */
31// define( 'ICAL_LANG', 'sv' );
32            // alt. autosetting
33/*
34$langstr     = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
35$pos         = strpos( $langstr, ';' );
36if ($pos   !== false) {
37  $langstr   = substr( $langstr, 0, $pos );
38  $pos       = strpos( $langstr, ',' );
39  if ($pos !== false) {
40    $pos     = strpos( $langstr, ',' );
41    $langstr = substr( $langstr, 0, $pos );
42  }
43  define( 'ICAL_LANG', $langstr );
44}
45*/
46/*********************************************************************************/
47/*         only for phpversion 5.1 and later,                                    */
48/*         date management, default timezone setting                             */
49/*         since 2.6.36 - 2010-12-31 */
50if( substr( phpversion(), 0, 3) >= '5.1' )
51  // && ( 'UTC' == date_default_timezone_get()))
52  date_default_timezone_set( 'America/Sao_paulo' );
53/*********************************************************************************/
54/*         since 2.6.22 - 2010-09-25, do NOT remove!!                            */
55require_once ( dirname(__FILE__).'/iCalUtilityFunctions.class.php' );
56/*********************************************************************************/
57/*         version string, do NOT remove!!                                       */
58define( 'ICALCREATOR_VERSION', 'iCalcreator 2.8' );
59/*********************************************************************************/
60/*********************************************************************************/
61/**
62 * vcalendar class
63 *
64 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
65 * @since 2.2.13 - 2007-12-30
66 */
67class vcalendar {
68            //  calendar property variables
69  var $calscale;
70  var $method;
71  var $prodid;
72  var $version;
73  var $xprop;
74            //  container for calendar components
75  var $components;
76            //  component config variables
77  var $allowEmpty;
78  var $unique_id;
79  var $language;
80  var $directory;
81  var $filename;
82  var $url;
83  var $delimiter;
84  var $nl;
85  var $format;
86            //  component internal variables
87  var $attributeDelimiter;
88  var $valueInit;
89            //  component xCal declaration container
90  var $xcaldecl;
91/*
92 * constructor for calendar object
93 *
94 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
95 * @since 2.6.27 - 2010-11-20
96 * @param array $config
97 * @return void
98 */
99  function vcalendar ( $config = array()) {
100    $this->_makeVersion();
101    $this->calscale   = null;
102    $this->method     = null;
103    $this->_makeUnique_id();
104    $this->prodid     = null;
105    $this->xprop      = array();
106    $this->language   = null;
107    $this->directory  = null;
108    $this->filename   = null;
109    $this->url        = null;
110/**
111 *   language = <Text identifying a language, as defined in [RFC 1766]>
112 */
113    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
114                                          $config['language']   = ICAL_LANG;
115    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
116    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
117    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
118    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
119    $this->setConfig( $config );
120
121    $this->xcaldecl   = array();
122    $this->components = array();
123  }
124/*********************************************************************************/
125/**
126 * Property Name: CALSCALE
127 */
128/**
129 * creates formatted output for calendar property calscale
130 *
131 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
132 * @since 2.4.8 - 2008-10-21
133 * @return string
134 */
135  function createCalscale() {
136    if( empty( $this->calscale )) return FALSE;
137    switch( $this->format ) {
138      case 'xcal':
139        return ' calscale="'.$this->calscale.'"'.$this->nl;
140        break;
141      default:
142        return 'CALSCALE:'.$this->calscale.$this->nl;
143        break;
144    }
145  }
146/**
147 * set calendar property calscale
148 *
149 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
150 * @since 2.4.8 - 2008-10-21
151 * @param string $value
152 * @return void
153 */
154  function setCalscale( $value ) {
155    if( empty( $value )) return FALSE;
156    $this->calscale = $value;
157  }
158/*********************************************************************************/
159/**
160 * Property Name: METHOD
161 */
162/**
163 * creates formatted output for calendar property method
164 *
165 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
166 * @since 0.9.7 - 2006-11-20
167 * @return string
168 */
169  function createMethod() {
170    if( empty( $this->method )) return FALSE;
171    switch( $this->format ) {
172      case 'xcal':
173        return ' method="'.$this->method.'"'.$this->nl;
174        break;
175      default:
176        return 'METHOD:'.$this->method.$this->nl;
177        break;
178    }
179  }
180/**
181 * set calendar property method
182 *
183 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
184 * @since 2.4.8 - 2008-20-23
185 * @param string $value
186 * @return bool
187 */
188  function setMethod( $value ) {
189    if( empty( $value )) return FALSE;
190    $this->method = $value;
191    return TRUE;
192  }
193/*********************************************************************************/
194/**
195 * Property Name: PRODID
196 *
197 *  The identifier is RECOMMENDED to be the identical syntax to the
198 * [RFC 822] addr-spec. A good method to assure uniqueness is to put the
199 * domain name or a domain literal IP address of the host on which.. .
200 */
201/**
202 * creates formatted output for calendar property prodid
203 *
204 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
205 * @since 0.9.7 - 2006-11-20
206 * @return string
207 */
208  function createProdid() {
209    if( !isset( $this->prodid ))
210      $this->_makeProdid();
211    switch( $this->format ) {
212      case 'xcal':
213        return ' prodid="'.$this->prodid.'"'.$this->nl;
214        break;
215      default:
216        return 'PRODID:'.$this->prodid.$this->nl;
217        break;
218    }
219  }
220/**
221 * make default value for calendar prodid
222 *
223 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
224 * @since 2.6.8 - 2009-12-30
225 * @return void
226 */
227  function _makeProdid() {
228    $this->prodid  = '-//'.$this->unique_id.'//NONSGML kigkonsult.se '.ICALCREATOR_VERSION.'//'.strtoupper( $this->language );
229  }
230/**
231 * Conformance: The property MUST be specified once in an iCalendar object.
232 * Description: The vendor of the implementation SHOULD assure that this
233 * is a globally unique identifier; using some technique such as an FPI
234 * value, as defined in [ISO 9070].
235 */
236/**
237 * make default unique_id for calendar prodid
238 *
239 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
240 * @since 0.3.0 - 2006-08-10
241 * @return void
242 */
243  function _makeUnique_id() {
244    $this->unique_id  = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
245  }
246/*********************************************************************************/
247/**
248 * Property Name: VERSION
249 *
250 * Description: A value of "2.0" corresponds to this memo.
251 */
252/**
253 * creates formatted output for calendar property version
254
255 *
256 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
257 * @since 0.9.7 - 2006-11-20
258 * @return string
259 */
260  function createVersion() {
261    if( empty( $this->version ))
262      $this->_makeVersion();
263    switch( $this->format ) {
264      case 'xcal':
265        return ' version="'.$this->version.'"'.$this->nl;
266        break;
267      default:
268        return 'VERSION:'.$this->version.$this->nl;
269        break;
270    }
271  }
272/**
273 * set default calendar version
274 *
275 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
276 * @since 0.3.0 - 2006-08-10
277 * @return void
278 */
279  function _makeVersion() {
280    $this->version = '2.0';
281  }
282/**
283 * set calendar version
284 *
285 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
286 * @since 2.4.8 - 2008-10-23
287 * @param string $value
288 * @return void
289 */
290  function setVersion( $value ) {
291    if( empty( $value )) return FALSE;
292    $this->version = $value;
293    return TRUE;
294  }
295/*********************************************************************************/
296/**
297 * Property Name: x-prop
298 */
299/**
300 * creates formatted output for calendar property x-prop, iCal format only
301 *
302 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
303 * @since 2.4.11 - 2008-11-03
304 * @return string
305 */
306  function createXprop() {
307    if( 'xcal' == $this->format )
308      return false;
309    if( 0 >= count( $this->xprop ))
310      return;
311    $output = null;
312    $toolbox = new calendarComponent();
313    $toolbox->setConfig( 'language', $this->getConfig( 'language' ));
314    $toolbox->setConfig( 'nl',       $this->getConfig( 'nl' ));
315    $toolbox->_createFormat(         $this->getConfig( 'format' ));
316    foreach( $this->xprop as $label => $xpropPart ) {
317      if( empty( $xpropPart['value'] )) {
318        $output  .= $toolbox->_createElement( $label );
319        continue;
320      }
321      $attributes = $toolbox->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
322      if( is_array( $xpropPart['value'] )) {
323        foreach( $xpropPart['value'] as $pix => $theXpart )
324          $xpropPart['value'][$pix] = $toolbox->_strrep( $theXpart );
325        $xpropPart['value']  = implode( ',', $xpropPart['value'] );
326      }
327      else
328        $xpropPart['value'] = $toolbox->_strrep( $xpropPart['value'] );
329      $output    .= $toolbox->_createElement( $label, $attributes, $xpropPart['value'] );
330    }
331    return $output;
332  }
333/**
334 * set calendar property x-prop
335 *
336 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
337 * @since 2.6.22 - 2010-09-25
338 * @param string $label
339 * @param string $value
340 * @param array $params optional
341 * @return bool
342 */
343  function setXprop( $label, $value, $params=FALSE ) {
344    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
345    if( empty( $label )) return FALSE;
346    $xprop           = array( 'value' => $value );
347    $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
348    if( !is_array( $this->xprop )) $this->xprop = array();
349    $this->xprop[strtoupper( $label )] = $xprop;
350    return TRUE;
351  }
352/*********************************************************************************/
353/**
354 * delete calendar property value
355 *
356 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
357 * @since 2.6.4 - 2011-01-05
358 * @param mixed $propName, bool FALSE => X-property
359 * @param int $propix, optional, if specific property is wanted in case of multiply occurences
360 * @return bool, if successfull delete
361 */
362  function deleteProperty( $propName=FALSE, $propix=FALSE ) {
363    $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
364    if( !$propix )
365      $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
366    $this->propdelix[$propName] = --$propix;
367    $return = FALSE;
368    switch( $propName ) {
369      case 'CALSCALE':
370        if( isset( $this->calscale )) {
371          $this->calscale = null;
372          $return = TRUE;
373        }
374        break;
375      case 'METHOD':
376        if( isset( $this->method )) {
377          $this->method   = null;
378          $return = TRUE;
379        }
380        break;
381      default:
382        $reduced = array();
383        if( $propName != 'X-PROP' ) {
384          if( !isset( $this->xprop[$propName] )) return FALSE;
385          foreach( $this->xprop as $k => $a ) {
386            if(( $k != $propName ) && !empty( $a ))
387              $reduced[$k] = $a;
388          }
389        }
390        else {
391          if( count( $this->xprop ) <= $propix )  return FALSE;
392          $xpropno = 0;
393          foreach( $this->xprop as $xpropkey => $xpropvalue ) {
394            if( $propix != $xpropno )
395              $reduced[$xpropkey] = $xpropvalue;
396            ++$xpropno;
397          }
398        }
399        $this->xprop = $reduced;
400        if( empty( $this->xprop ))
401          return FALSE;
402        return TRUE;
403    }
404    return $return;
405  }
406/**
407 * get calendar property value/params
408 *
409 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
410 * @since 2.5.1 - 2008-11-02
411 * @param string $propName, optional
412 * @param int $propix, optional, if specific property is wanted in case of multiply occurences
413 * @param bool $inclParam=FALSE
414 * @return mixed
415 */
416  function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) {
417    $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
418    if( 'X-PROP' == $propName ) {
419      if( !$propix )
420        $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
421      $this->propix[$propName] = --$propix;
422    }
423    switch( $propName ) {
424      case 'CALSCALE':
425        return ( !empty( $this->calscale )) ? $this->calscale : null;
426        break;
427      case 'METHOD':
428        return ( !empty( $this->method )) ? $this->method : null;
429        break;
430      case 'PRODID':
431        if( empty( $this->prodid ))
432          $this->_makeProdid();
433        return $this->prodid;
434        break;
435      case 'VERSION':
436        return ( !empty( $this->version )) ? $this->version : null;
437        break;
438      default:
439        if( $propName != 'X-PROP' ) {
440          if( !isset( $this->xprop[$propName] )) return FALSE;
441          return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
442                                : array( $propName, $this->xprop[$propName]['value'] );
443        }
444        else {
445          if( empty( $this->xprop )) return FALSE;
446          $xpropno = 0;
447          foreach( $this->xprop as $xpropkey => $xpropvalue ) {
448            if( $propix == $xpropno )
449              return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
450                                    : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
451            else
452              ++$xpropno;
453          }
454          return FALSE; // not found ??
455        }
456    }
457    return FALSE;
458  }
459/**
460 * general vcalendar property setting
461 *
462 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
463 * @since 2.2.13 - 2007-11-04
464 * @param mixed $args variable number of function arguments,
465 *                    first argument is ALWAYS component name,
466 *                    second ALWAYS component value!
467 * @return bool
468 */
469  function setProperty () {
470    $numargs    = func_num_args();
471    if( 1 > $numargs )
472      return FALSE;
473    $arglist    = func_get_args();
474    $arglist[0] = strtoupper( $arglist[0] );
475    switch( $arglist[0] ) {
476      case 'CALSCALE':
477        return $this->setCalscale( $arglist[1] );
478      case 'METHOD':
479        return $this->setMethod( $arglist[1] );
480      case 'VERSION':
481        return $this->setVersion( $arglist[1] );
482      default:
483        if( !isset( $arglist[1] )) $arglist[1] = null;
484        if( !isset( $arglist[2] )) $arglist[2] = null;
485        return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
486    }
487    return FALSE;
488  }
489/*********************************************************************************/
490/**
491 * get vcalendar config values or * calendar components
492 *
493 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
494 * @since 2.6.27 - 2010-11-27
495 * @param mixed $config
496 * @return value
497 */
498  function getConfig( $config = FALSE ) {
499    if( !$config ) {
500      $return = array();
501      $return['ALLOWEMPTY']  = $this->getConfig( 'ALLOWEMPTY' );
502      $return['DELIMITER']   = $this->getConfig( 'DELIMITER' );
503      $return['DIRECTORY']   = $this->getConfig( 'DIRECTORY' );
504      $return['FILENAME']    = $this->getConfig( 'FILENAME' );
505      $return['DIRFILE']     = $this->getConfig( 'DIRFILE' );
506      $return['FILESIZE']    = $this->getConfig( 'FILESIZE' );
507      $return['FORMAT']      = $this->getConfig( 'FORMAT' );
508      if( FALSE !== ( $lang  = $this->getConfig( 'LANGUAGE' )))
509        $return['LANGUAGE']  = $lang;
510      $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
511      $return['UNIQUE_ID']   = $this->getConfig( 'UNIQUE_ID' );
512      if( FALSE !== ( $url   = $this->getConfig( 'URL' )))
513        $return['URL']       = $url;
514      return $return;
515    }
516    switch( strtoupper( $config )) {
517      case 'ALLOWEMPTY':
518        return $this->allowEmpty;
519        break;
520      case 'COMPSINFO':
521        unset( $this->compix );
522        $info = array();
523        foreach( $this->components as $cix => $component ) {
524          if( empty( $component )) continue;
525          unset( $component->propix );
526          $info[$cix]['ordno'] = $cix + 1;
527          $info[$cix]['type']  = $component->objName;
528          $info[$cix]['uid']   = $component->getProperty( 'uid' );
529          $info[$cix]['props'] = $component->getConfig( 'propinfo' );
530          $info[$cix]['sub']   = $component->getConfig( 'compsinfo' );
531          unset( $component->propix );
532        }
533        return $info;
534        break;
535      case 'DELIMITER':
536        return $this->delimiter;
537        break;
538      case 'DIRECTORY':
539        if( empty( $this->directory ))
540          $this->directory = '.';
541        return $this->directory;
542        break;
543      case 'DIRFILE':
544        return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' );
545        break;
546      case 'FILEINFO':
547        return array( $this->getConfig( 'directory' )
548                    , $this->getConfig( 'filename' )
549                    , $this->getConfig( 'filesize' ));
550        break;
551      case 'FILENAME':
552        if( empty( $this->filename )) {
553          if( 'xcal' == $this->format )
554            $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. .
555          else
556            $this->filename = date( 'YmdHis' ).'.ics';
557        }
558        return $this->filename;
559        break;
560      case 'FILESIZE':
561        $size    = 0;
562        if( empty( $this->url )) {
563          $dirfile = $this->getConfig( 'dirfile' );
564          if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile ))))
565            $size = 0;
566          clearstatcache();
567        }
568        return $size;
569        break;
570      case 'FORMAT':
571        return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal';
572        break;
573      case 'LANGUAGE':
574         /* get language for calendar component as defined in [RFC 1766] */
575        return $this->language;
576        break;
577      case 'NL':
578      case 'NEWLINECHAR':
579        return $this->nl;
580        break;
581      case 'UNIQUE_ID':
582        return $this->unique_id;
583        break;
584      case 'URL':
585        if( !empty( $this->url ))
586          return $this->url;
587        else
588          return FALSE;
589        break;
590    }
591  }
592/**
593 * general vcalendar config setting
594 *
595 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
596 * @since 2.6.27 - 2010-12-12
597 * @param mixed  $config
598 * @param string $value
599 * @return void
600 */
601  function setConfig( $config, $value = FALSE) {
602    if( is_array( $config )) {
603      foreach( $config as $cKey => $cValue ) {
604        if( FALSE === $this->setConfig( $cKey, $cValue ))
605          return FALSE;
606      }
607      return TRUE;
608    }
609    $res = FALSE;
610    switch( strtoupper( $config )) {
611      case 'ALLOWEMPTY':
612        $this->allowEmpty = $value;
613        $subcfg  = array( 'ALLOWEMPTY' => $value );
614        $res = TRUE;
615        break;
616      case 'DELIMITER':
617        $this->delimiter = $value;
618        return TRUE;
619        break;
620      case 'DIRECTORY':
621        $value   = trim( $value );
622        $del     = $this->getConfig('delimiter');
623        if( $del == substr( $value, ( 0 - strlen( $del ))))
624          $value = substr( $value, 0, ( strlen( $value ) - strlen( $del )));
625        if( is_dir( $value )) {
626            /* local directory */
627          clearstatcache();
628          $this->directory = $value;
629          $this->url       = null;
630          return TRUE;
631        }
632        else
633          return FALSE;
634        break;
635      case 'FILENAME':
636        $value   = trim( $value );
637        if( !empty( $this->url )) {
638            /* remote directory+file -> URL */
639          $this->filename = $value;
640          return TRUE;
641        }
642        $dirfile = $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$value;
643        if( file_exists( $dirfile )) {
644            /* local file exists */
645          if( is_readable( $dirfile ) || is_writable( $dirfile )) {
646            clearstatcache();
647            $this->filename = $value;
648            return TRUE;
649          }
650          else
651            return FALSE;
652        }
653        elseif( is_readable($this->getConfig( 'directory' ) ) || is_writable( $this->getConfig( 'directory' ) )) {
654            /* read- or writable directory */
655          $this->filename = $value;
656          return TRUE;
657        }
658        else
659          return FALSE;
660        break;
661      case 'FORMAT':
662        $value   = trim( strtolower( $value ));
663        if( 'xcal' == $value ) {
664          $this->format             = 'xcal';
665          $this->attributeDelimiter = $this->nl;
666          $this->valueInit          = null;
667        }
668        else {
669          $this->format             = null;
670          $this->attributeDelimiter = ';';
671          $this->valueInit          = ':';
672        }
673        $subcfg  = array( 'FORMAT' => $value );
674        $res = TRUE;
675        break;
676      case 'LANGUAGE':
677         // set language for calendar component as defined in [RFC 1766]
678        $value   = trim( $value );
679        $this->language = $value;
680        $subcfg  = array( 'LANGUAGE' => $value );
681        $res = TRUE;
682        break;
683      case 'NL':
684      case 'NEWLINECHAR':
685        $this->nl = $value;
686        $subcfg  = array( 'NL' => $value );
687        $res = TRUE;
688        break;
689      case 'UNIQUE_ID':
690        $value   = trim( $value );
691        $this->unique_id = $value;
692        $subcfg  = array( 'UNIQUE_ID' => $value );
693        $res = TRUE;
694        break;
695      case 'URL':
696            /* remote file - URL */
697        $value     = trim( $value );
698        $value     = str_replace( 'HTTP://',   'http://', $value );
699        $value     = str_replace( 'WEBCAL://', 'http://', $value );
700        $value     = str_replace( 'webcal://', 'http://', $value );
701        $this->url = $value;
702        $this->directory = null;
703        $parts     = pathinfo( $value );
704        return $this->setConfig( 'filename',  $parts['basename'] );
705        break;
706      default:  // any unvalid config key.. .
707        return TRUE;
708    }
709    if( !$res ) return FALSE;
710    if( isset( $subcfg ) && !empty( $this->components )) {
711      foreach( $subcfg as $cfgkey => $cfgvalue ) {
712        foreach( $this->components as $cix => $component ) {
713          $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE );
714          if( !$res )
715            break 2;
716          $this->components[$cix] = $component->copy(); // PHP4 compliant
717        }
718      }
719    }
720    return $res;
721  }
722/*********************************************************************************/
723/**
724 * add calendar component to container
725 *
726 * alias to setComponent
727 *
728 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
729 * @since 1.x.x - 2007-04-24
730 * @param object $component calendar component
731 * @return void
732 */
733  function addComponent( $component ) {
734    $this->setComponent( $component );
735  }
736/**
737 * delete calendar component from container
738 *
739 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
740 * @since 2.4.10 - 2008-08-05
741 * @param mixed $arg1 ordno / component type / component uid
742 * @param mixed $arg2 optional, ordno if arg1 = component type
743 * @return void
744 */
745  function deleteComponent( $arg1, $arg2=FALSE  ) {
746    $argType = $index = null;
747    if ( ctype_digit( (string) $arg1 )) {
748      $argType = 'INDEX';
749      $index   = (int) $arg1 - 1;
750    }
751    elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
752      $argType = strtolower( $arg1 );
753      $index   = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
754    }
755    $cix1dC = 0;
756    foreach ( $this->components as $cix => $component) {
757      if( empty( $component )) continue;
758      unset( $component->propix );
759      if(( 'INDEX' == $argType ) && ( $index == $cix )) {
760        unset( $this->components[$cix] );
761        return TRUE;
762      }
763      elseif( $argType == $component->objName ) {
764        if( $index == $cix1dC ) {
765          unset( $this->components[$cix] );
766          return TRUE;
767        }
768        ++$cix1dC;
769      }
770      elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
771        unset( $this->components[$cix] );
772        return TRUE;
773      }
774    }
775    return FALSE;
776  }
777/**
778 * get calendar component from container
779 *
780 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
781 * @since 2.4.10 - 2008-08-06
782 * @param mixed $arg1 optional, ordno/component type/ component uid
783 * @param mixed $arg2 optional, ordno if arg1 = component type
784 * @return object
785 */
786  function getComponent( $arg1=FALSE, $arg2=FALSE ) {
787    $index = $argType = null;
788    if ( !$arg1 ) {
789      $argType = 'INDEX';
790      $index   = $this->compix['INDEX'] =
791        ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
792    }
793    elseif ( ctype_digit( (string) $arg1 )) {
794      $argType = 'INDEX';
795      $index   = (int) $arg1;
796      unset( $this->compix );
797    }
798    elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
799      unset( $this->compix['INDEX'] );
800      $argType = strtolower( $arg1 );
801      if( !$arg2 )
802        $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
803      else
804        $index = (int) $arg2;
805    }
806    $index  -= 1;
807    $ckeys =  array_keys( $this->components );
808    if( !empty( $index) && ( $index > end(  $ckeys )))
809      return FALSE;
810    $cix1gC = 0;
811    foreach ( $this->components as $cix => $component) {
812      if( empty( $component )) continue;
813      unset( $component->propix );
814      if(( 'INDEX' == $argType ) && ( $index == $cix ))
815        return $component->copy();
816      elseif( $argType == $component->objName ) {
817         if( $index == $cix1gC )
818           return $component->copy();
819         ++$cix1gC;
820      }
821      elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
822        unset( $component->propix );
823        return $component->copy();
824      }
825    }
826            /* not found.. . */
827    unset( $this->compix );
828    return FALSE;
829  }
830/**
831 * create new calendar component, already included within calendar
832 *
833 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
834 * @since 2.6.33 - 2011-01-03
835 * @param string $compType component type
836 * @return object (reference)
837 */
838  function & newComponent( $compType ) {
839    $config = $this->getConfig();
840    $keys   = array_keys( $this->components );
841    $ix     = end( $keys) + 1;
842    switch( strtoupper( $compType )) {
843      case 'EVENT':
844      case 'VEVENT':
845        $this->components[$ix] = new vevent( $config );
846        break;
847      case 'TODO':
848      case 'VTODO':
849        $this->components[$ix] = new vtodo( $config );
850        break;
851      case 'JOURNAL':
852      case 'VJOURNAL':
853        $this->components[$ix] = new vjournal( $config );
854        break;
855      case 'FREEBUSY':
856      case 'VFREEBUSY':
857        $this->components[$ix] = new vfreebusy( $config );
858        break;
859      case 'TIMEZONE':
860      case 'VTIMEZONE':
861        array_unshift( $this->components, new vtimezone( $config ));
862        $ix = 0;
863        break;
864      default:
865        return FALSE;
866    }
867    return $this->components[$ix];
868  }
869/**
870 * select components from calendar on date basis
871 *
872 * Ensure DTSTART is set for every component.
873 * No date controls occurs.
874 *
875 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
876 * @since 2.6.22 - 2010-10-21
877 * @param int $startY optional,  start Year, default current Year
878 * @param int $startM optional,  start Month, default current Month
879 * @param int $startD optional,  start Day, default current Day
880 * @param int $endY optional,    end Year, default $startY
881 * @param int $endY optional,    end Month, default $startM
882 * @param int $endY optional,    end Day, default $startD
883 * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s)
884 * @param bool $flat optional,   FALSE (default) => output : array[Year][Month][Day][]
885 *                               TRUE => output : array[] (ignores split)
886 * @param bool $any optional,    TRUE (default) - select component that take place within period
887 *                               FALSE - only components that starts within period
888 * @param bool $split optional,  TRUE (default) - one component copy every day it take place during the
889 *                                       period (implies flat=FALSE)
890 *                               FALSE - one occurance of component only in output array
891 * @return array or FALSE
892 */
893  function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) {
894            /* check  if empty calendar */
895    if( 0 >= count( $this->components )) return FALSE;
896            /* check default dates */
897    if( !$startY ) $startY = date( 'Y' );
898    if( !$startM ) $startM = date( 'm' );
899    if( !$startD ) $startD = date( 'd' );
900    $startDate = mktime( 0, 0, 0, $startM, $startD, $startY );
901    if( !$endY )   $endY   = $startY;
902    if( !$endM )   $endM   = $startM;
903    if( !$endD )   $endD   = $startD;
904    $endDate   = mktime( 23, 59, 59, $endM, $endD, $endY );
905            /* check component types */
906    $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' );
907    if( is_array( $cType )) {
908      foreach( $cType as $cix => $theType ) {
909        $cType[$cix] = $theType = strtolower( $theType );
910        if( !in_array( $theType, $validTypes ))
911          $cType[$cix] = 'vevent';
912      }
913      $cType = array_unique( $cType );
914    }
915    elseif( !empty( $cType )) {
916      $cType = strtolower( $cType );
917      if( !in_array( $cType, $validTypes ))
918        $cType = array( 'vevent' );
919      else
920        $cType = array( $cType );
921    }
922    else
923      $cType = $validTypes;
924    if( 0 >= count( $cType ))
925      $cType = $validTypes;
926            /* iterate components */
927    $result = array();
928    foreach ( $this->components as $cix => $component ) {
929      if( empty( $component )) continue;
930      unset( $component->propix, $start );
931            /* deselect unvalid type components */
932      if( !in_array( $component->objName, $cType )) continue;
933      $start = $component->getProperty( 'dtstart' );
934            /* select due when dtstart is missing */
935      if( empty( $start ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $start = $component->getProperty( 'due' ))))
936        continue;
937      $dtendExist = $dueExist = $durationExist = $endAllDayEvent = FALSE;
938      unset( $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $workstart, $workend, $endDateFormat ); // clean up
939      $startWdate = iCalUtilityFunctions::_date2timestamp( $start );
940      $startDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
941            /* get end date from dtend/due/duration properties */
942      $end = $component->getProperty( 'dtend' );
943      if( !empty( $end )) {
944        $dtendExist = TRUE;
945        $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
946      }
947   // if( !empty($end))  echo 'selectComp 1 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
948      if( empty( $end ) && ( $component->objName == 'vtodo' )) {
949        $end = $component->getProperty( 'due' );
950        if( !empty( $end )) {
951          $dueExist = TRUE;
952          $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
953        }
954   // if( !empty($end))  echo 'selectComp 2 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
955      }
956      if( !empty( $end ) && !isset( $end['hour'] )) {
957          /* a DTEND without time part regards an event that ends the day before,
958             for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */
959        $endAllDayEvent = TRUE;
960        $endWdate = mktime( 23, 59, 59, $end['month'], ($end['day'] - 1), $end['year'] );
961        $end['year']  = date( 'Y', $endWdate );
962        $end['month'] = date( 'm', $endWdate );
963        $end['day']   = date( 'd', $endWdate );
964        $end['hour']  = 23;
965        $end['min']   = $end['sec'] = 59;
966   // if( !empty($end))  echo 'selectComp 3 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
967      }
968      if( empty( $end )) {
969        $end = $component->getProperty( 'duration', FALSE, FALSE, TRUE );// in dtend (array) format
970        if( !empty( $end ))
971          $durationExist = TRUE;
972          $endDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
973   // if( !empty($end))  echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
974      }
975      if( empty( $end )) { // assume one day duration if missing end date
976        $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
977   // if( isset($end))  echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
978      }
979      $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
980      if( $endWdate < $startWdate ) { // MUST be after start date!!
981        $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
982        $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
983      }
984      $rdurWsecs  = $endWdate - $startWdate; // compute component duration in seconds
985            /* make a list of optional exclude dates for component occurence from exrule and exdate */
986      $exdatelist = array();
987      $workstart  = iCalUtilityFunctions::_timestamp2date(( $startDate - $rdurWsecs ), 6);
988      $workend    = iCalUtilityFunctions::_timestamp2date(( $endDate + $rdurWsecs ), 6);
989      while( FALSE !== ( $exrule = $component->getProperty( 'exrule' )))    // check exrule
990        iCalUtilityFunctions::_recur2date( $exdatelist, $exrule, $start, $workstart, $workend );
991      while( FALSE !== ( $exdate = $component->getProperty( 'exdate' ))) {  // check exdate
992        foreach( $exdate as $theExdate ) {
993          $exWdate = iCalUtilityFunctions::_date2timestamp( $theExdate );
994          $exWdate = mktime( 0, 0, 0, date( 'm', $exWdate ), date( 'd', $exWdate ), date( 'Y', $exWdate ) ); // on a day-basis !!!
995          if((( $startDate - $rdurWsecs ) <= $exWdate ) && ( $endDate >= $exWdate ))
996            $exdatelist[$exWdate] = TRUE;
997        }
998      }
999            /* if 'any' components, check repeating components, removing all excluding dates */
1000      if( TRUE === $any ) {
1001            /* make a list of optional repeating dates for component occurence, rrule, rdate */
1002        $recurlist = array();
1003        while( FALSE !== ( $rrule = $component->getProperty( 'rrule' )))    // check rrule
1004          iCalUtilityFunctions::_recur2date( $recurlist, $rrule, $start, $workstart, $workend );
1005        foreach( $recurlist as $recurkey => $recurvalue ) // key=match date as timestamp
1006          $recurlist[$recurkey] = $rdurWsecs; // add duration in seconds
1007        while( FALSE !== ( $rdate = $component->getProperty( 'rdate' ))) {  // check rdate
1008          foreach( $rdate as $theRdate ) {
1009            if( is_array( $theRdate ) && ( 2 == count( $theRdate )) &&  // all days within PERIOD
1010                   array_key_exists( '0', $theRdate ) &&  array_key_exists( '1', $theRdate )) {
1011              $rstart = iCalUtilityFunctions::_date2timestamp( $theRdate[0] );
1012              if(( $rstart < ( $startDate - $rdurWsecs )) || ( $rstart > $endDate ))
1013                continue;
1014              if( isset( $theRdate[1]['year'] )) // date-date period
1015                $rend = iCalUtilityFunctions::_date2timestamp( $theRdate[1] );
1016              else {                             // date-duration period
1017                $rend = iCalUtilityFunctions::_duration2date( $theRdate[0], $theRdate[1] );
1018                $rend = iCalUtilityFunctions::_date2timestamp( $rend );
1019              }
1020              while( $rstart < $rend ) {
1021                $recurlist[$rstart] = $rdurWsecs; // set start date for recurrence instance + rdate duration in seconds
1022                $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
1023              }
1024            } // PERIOD end
1025            else { // single date
1026              $theRdate = iCalUtilityFunctions::_date2timestamp( $theRdate );
1027              if((( $startDate - $rdurWsecs ) <= $theRdate ) && ( $endDate >= $theRdate ))
1028                $recurlist[$theRdate] = $rdurWsecs; // set start date for recurrence instance + event duration in seconds
1029            }
1030          }
1031        }
1032        if( 0 < count( $recurlist )) {
1033          ksort( $recurlist );
1034          $xRecurrence = 1;
1035          foreach( $recurlist as $recurkey => $durvalue ) {
1036            if((( $startDate - $rdurWsecs ) > $recurkey ) || ( $endDate < $recurkey )) // not within period
1037              continue;
1038            $checkDate = mktime( 0, 0, 0, date( 'm', $recurkey ), date( 'd', $recurkey ), date( 'Y', $recurkey ) ); // on a day-basis !!!
1039            if( isset( $exdatelist[$checkDate] )) // check excluded dates
1040              continue;
1041            if( $startWdate >= $recurkey ) // exclude component start date
1042              continue;
1043            $component2   = $component->copy();
1044            $rstart = $recurkey;
1045            $rend   = $recurkey + $durvalue;
1046           /* add repeating components within valid dates to output array, only start date set */
1047            if( $flat ) {
1048              $datestring = date( $startDateFormat, $recurkey );
1049              if( isset( $start['tz'] ))
1050                $datestring .= ' '.$start['tz'];
1051              $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
1052              if( $dtendExist || $dueExist || $durationExist ) {
1053                $datestring = date( $endDateFormat, $recurkey + $durvalue );   // fixa korrekt sluttid
1054                if( isset( $end['tz'] ))
1055                  $datestring .= ' '.$end['tz'];
1056                $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1057                $component2->setProperty( $propName, $datestring );
1058              } // end if( $dtendExist || $dueExist || $durationExist )
1059              $component2->setProperty( 'X-RECURRENCE', ++$xRecurrence );
1060              $result[$component2->getProperty( 'UID' )] = $component2->copy(); // copy to output
1061            }
1062           /* add repeating components within valid dates to output array, one each day */
1063            elseif( $split ) {
1064              $xRecurrence += 1;
1065              if( $rend > $endDate )
1066                $rend = $endDate;
1067              while( $rstart <= $rend ) { // iterate.. .
1068                $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1069                if( isset( $exdatelist[$checkDate] ))  // exclude any recurrence START date, found in exdatelist
1070                  break;
1071                if( $rstart > $startDate ) {    // date after dtstart
1072                  $datestring = date( $startDateFormat, $rstart );
1073                  if( isset( $start['tz'] ))
1074                    $datestring .= ' '.$start['tz'];
1075                  $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
1076                  if( $dtendExist || $dueExist || $durationExist ) {
1077                    $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1078                    $datestring = date( $endDateFormat, $tend );
1079                    if( isset( $end['tz'] ))
1080                      $datestring .= ' '.$end['tz'];
1081                    $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1082                    $component2->setProperty( $propName, $datestring );
1083                  } // end if( $dtendExist || $dueExist || $durationExist )
1084                  $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
1085                  $wd = getdate( $rstart );
1086                  $result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty( 'UID' )] = $component2->copy(); // copy to output
1087                }
1088                $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
1089              } // end while( $rstart <= $rend )
1090            } // end elseif( $split )
1091            elseif( $rstart >= $startDate ) {     // date within period   //* flat=FALSE && split=FALSE *//
1092              $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1093              if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
1094                $xRecurrence += 1;
1095                $datestring = date( $startDateFormat, $rstart );
1096                if( isset( $start['tz'] ))
1097                  $datestring .= ' '.$start['tz'];
1098                $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
1099                if( $dtendExist || $dueExist || $durationExist ) {
1100                  $rstart += $rdurWsecs;
1101                  $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1102                  $datestring = date( $endDateFormat, $tend );
1103                  if( isset( $end['tz'] ))
1104                    $datestring .= ' '.$end['tz'];
1105                  $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1106                  $component2->setProperty( $propName, $datestring );
1107                } // end if( $dtendExist || $dueExist || $durationExist )
1108                $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
1109                $wd = getdate( $rstart );
1110                $result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty( 'UID' )] = $component2->copy(); // copy to output
1111              } // end if( !isset( $exdatelist[$checkDate] ))
1112            } // end elseif( $rstart >= $startDate )
1113          } // end foreach( $recurlist as $recurkey => $durvalue )
1114        } // end if( 0 < count( $recurlist ))
1115            /* deselect components with startdate/enddate not within period */
1116        if(( $endWdate < $startDate ) || ( $startWdate > $endDate ))
1117          continue;
1118      } // end if( TRUE === $any )
1119            /* deselect components with startdate not within period */
1120      elseif(( $startWdate < $startDate ) || ( $startWdate > $endDate ))
1121        continue;
1122            /* add the selected component (WITHIN valid dates) to output array */
1123      if( $flat )
1124        $result[$component->getProperty( 'UID' )] = $component->copy(); // copy to output;
1125      elseif( $split ) {
1126        if( $endWdate > $endDate )
1127          $endWdate = $endDate;     // use period end date
1128        $rstart = $startWdate;
1129        if( $rstart < $startDate )
1130          $rstart = $startDate; // use period start date
1131        $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1132        if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
1133          while( $rstart <= $endWdate ) { // iterate
1134            if( $rstart > $startWdate ) { // if NOT startdate, set X-properties
1135              $datestring = date( $startDateFormat, $rstart );
1136              if( isset( $start['tz'] ))
1137                $datestring .= ' '.$start['tz'];
1138              $component->setProperty( 'X-CURRENT-DTSTART', $datestring );
1139              if( $dtendExist || $dueExist || $durationExist ) {
1140                $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1141                $datestring = date( $endDateFormat, $tend );
1142                if( isset( $end['tz'] ))
1143                  $datestring .= ' '.$end['tz'];
1144                $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1145                $component->setProperty( $propName, $datestring );
1146              } // end if( $dtendExist || $dueExist || $durationExist )
1147            } // end if( $rstart > $startWdate )
1148            $wd = getdate( $rstart );
1149            $result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty( 'UID' )] = $component->copy(); // copy to output
1150            $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
1151          } // end while( $rstart <= $endWdate )
1152        } // end if( !isset( $exdatelist[$checkDate] ))
1153      } // end if( $split )   -  else use component date
1154      elseif( $startWdate >= $startDate ) {          // within period
1155        $checkDate = mktime( 0, 0, 0, date( 'm', $startWdate ), date( 'd', $startWdate ), date( 'Y', $startWdate ) ); // on a day-basis !!!
1156        if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
1157          $wd = getdate( $startWdate );
1158          $result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty( 'UID' )] = $component->copy(); // copy to output
1159        }
1160      }
1161    } // end foreach ( $this->components as $cix => $component )
1162    if( 0 >= count( $result )) return FALSE;
1163    elseif( !$flat ) {
1164      foreach( $result as $y => $yeararr ) {
1165        foreach( $yeararr as $m => $montharr ) {
1166          foreach( $montharr as $d => $dayarr )
1167            $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index
1168          ksort( $result[$y][$m] );
1169        }
1170        ksort( $result[$y] );
1171      }
1172      ksort( $result );
1173    } // end elseif( !$flat )
1174    return $result;
1175  }
1176/**
1177 * add calendar component to container
1178 *
1179 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1180 * @since 2.6.28 - 2011-01-01
1181 * @param object $component calendar component
1182 * @param mixed $arg1 optional, ordno/component type/ component uid
1183 * @param mixed $arg2 optional, ordno if arg1 = component type
1184 * @return void
1185 */
1186  function setComponent( $component, $arg1=FALSE, $arg2=FALSE  ) {
1187    $component->setConfig( $this->getConfig(), FALSE, TRUE );
1188    if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) {
1189      unset( $component->propix );
1190            /* make sure dtstamp and uid is set */
1191      $dummy1 = $component->getProperty( 'dtstamp' );
1192      $dummy2 = $component->getProperty( 'uid' );
1193    }
1194    if( !$arg1 ) { // plain insert, last in chain
1195      $this->components[] = $component->copy();
1196      return TRUE;
1197    }
1198    $argType = $index = null;
1199    if ( ctype_digit( (string) $arg1 )) { // index insert/replace
1200      $argType = 'INDEX';
1201      $index   = (int) $arg1 - 1;
1202    }
1203    elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) {
1204      $argType = strtolower( $arg1 );
1205      $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
1206    }
1207    // else if arg1 is set, arg1 must be an UID
1208    $cix1sC = 0;
1209    foreach ( $this->components as $cix => $component2) {
1210      if( empty( $component2 )) continue;
1211      unset( $component2->propix );
1212      if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
1213        $this->components[$cix] = $component->copy();
1214        return TRUE;
1215      }
1216      elseif( $argType == $component2->objName ) { // component Type index insert/replace
1217        if( $index == $cix1sC ) {
1218          $this->components[$cix] = $component->copy();
1219          return TRUE;
1220        }
1221        ++$cix1sC;
1222      }
1223      elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
1224        $this->components[$cix] = $component->copy();
1225        return TRUE;
1226      }
1227    }
1228            /* arg1=index and not found.. . insert at index .. .*/
1229    if( 'INDEX' == $argType ) {
1230      $this->components[$index] = $component->copy();
1231      ksort( $this->components, SORT_NUMERIC );
1232    }
1233    else    /* not found.. . insert last in chain anyway .. .*/
1234      $this->components[] = $component->copy();
1235    return TRUE;
1236  }
1237/**
1238 * sort iCal compoments, only local date sort
1239 *
1240 * ascending sort on properties (if exist) x-current-dtstart, dtstart,
1241 * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid
1242 *
1243 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1244 * @since 2.6.3 - 2010-12-05
1245 * @return void
1246 *
1247 */
1248  function sort() {
1249    if( is_array( $this->components )) {
1250            /* set sort parameters for each component */
1251      foreach( $this->components as & $c ) {
1252        $c->srtk = array( '0', '0', '0', '0' );
1253        if( 'vtimezone' == $c->objName ) {
1254          if( FALSE === ( $c->srtk[0] = $c->getProperty( 'tzid' )))
1255            $c->srtk[0] = 0;
1256          continue;
1257        }
1258        if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' )))
1259          $c->srtk[0] = iCalUtilityFunctions::_date_time_string( $d[1] );
1260        elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' )))
1261          $c->srtk[1] = 0;                                                  // sortkey 0 : dtstart
1262        if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' )))
1263          $c->srtk[1] = iCalUtilityFunctions::_date_time_string( $d[1] );   // sortkey 1 : dtend/due(/dtstart+duration)
1264        elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) {
1265          if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' )))
1266            $c->srtk[1] = iCalUtilityFunctions::_date_time_string( $d[1] );
1267          elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' )))
1268            if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE )))
1269              $c->srtk[1] = 0;
1270        }
1271        if( FALSE === ( $c->srtk[2] = $c->getProperty( 'created' )))      // sortkey 2 : created/dtstamp
1272          if( FALSE === ( $c->srtk[2] = $c->getProperty( 'dtstamp' )))
1273            $c->srtk[2] = 0;
1274        if( FALSE === ( $c->srtk[3] = $c->getProperty( 'uid' )))          // sortkey 3 : uid
1275          $c->srtk[3] = 0;
1276      } // end foreach( $this->components as & $c
1277            /* sort */
1278      usort( $this->components, array( $this, '_cmpfcn' ));
1279    }
1280  }
1281  function _cmpfcn( $a, $b ) {
1282    if(        empty( $a ))                       return -1;
1283    if(        empty( $b ))                       return  1;
1284    if( 'vtimezone' == $a->objName ) {
1285      if( 'vtimezone' != $b->objName )            return -1;
1286      elseif( $a->srtk[0] <= $b->srtk[0] )        return -1;
1287      else                                        return  1;
1288    }
1289    elseif( 'vtimezone' == $b->objName )          return  1;
1290    $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' );
1291    for( $k = 0; $k < 4 ; ++$k ) {
1292      if(        empty( $a->srtk[$k] ))           return -1;
1293      elseif(    empty( $b->srtk[$k] ))           return  1;
1294      if( is_array( $a->srtk[$k] )) {
1295        if( is_array( $b->srtk[$k] )) {
1296          foreach( $sortkeys as $key ) {
1297            if    (  empty( $a->srtk[$k][$key] )) return -1;
1298            elseif(  empty( $b->srtk[$k][$key] )) return  1;
1299            if    (         $a->srtk[$k][$key] == $b->srtk[$k][$key])
1300                                                  continue;
1301            if    ((  (int) $a->srtk[$k][$key] ) < ((int) $b->srtk[$k][$key] ))
1302                                                  return -1;
1303            elseif((  (int) $a->srtk[$k][$key] ) > ((int) $b->srtk[$k][$key] ))
1304                                                  return  1;
1305          }
1306        }
1307        else                                      return -1;
1308      }
1309      elseif( is_array( $b->srtk[$k] ))           return  1;
1310      elseif( $a->srtk[$k] < $b->srtk[$k] )       return -1;
1311      elseif( $a->srtk[$k] > $b->srtk[$k] )       return  1;
1312    }
1313    return 0;
1314  }
1315/**
1316 * parse iCal text/file into vcalendar, components, properties and parameters
1317 *
1318 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1319 * @since 2.6.32 - 2010-12-19
1320 * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings
1321 * @return bool FALSE if error occurs during parsing
1322 *
1323 */
1324  function parse( $unparsedtext=FALSE ) {
1325    $nl = $this->getConfig( 'nl' );
1326    if( FALSE === $unparsedtext ) {
1327            /* directory+filename is set previously via setConfig directory+filename or url */
1328      if( FALSE === ( $filename = $this->getConfig( 'url' )))
1329        $filename = $this->getConfig( 'dirfile' );
1330            /* READ FILE */
1331      if( FALSE === ( $rows = file( $filename )))
1332        return FALSE;                 /* err 1 */
1333    }
1334    elseif( !is_array( $unparsedtext ))
1335      $rows =  explode( $nl, $unparsedtext );
1336    else
1337      $rows = & $unparsedtext;
1338
1339            /* identify BEGIN:VCALENDAR, MUST be first row */
1340    if( 'BEGIN:VCALENDAR' != strtoupper( trim( $rows[0] )))
1341      return FALSE;                   /* err 8 */
1342
1343    $lix = count( $rows );
1344
1345    /*skicp empty line*/
1346
1347    //Remove  espços em branco no final do arquivo ics
1348    for($i = $lix; $i >= 0; $i--)
1349    {
1350        if(strtoupper(trim($rows[$i])) == 'END:VCALENDAR')
1351            break;
1352         else
1353           unset($rows[$i]);
1354    }
1355   
1356    $lix = count( $rows ) -1;
1357
1358
1359    if( 'END:VCALENDAR'   != strtoupper( trim( $rows[ $lix ] )))
1360      return FALSE;                   /* err 9 */
1361
1362
1363    if( 3 > count( $rows ))
1364      return FALSE;                   /* err 10 */
1365    $comp    = & $this;
1366    $calsync = 0;
1367
1368
1369            /* identify components and update unparsed data within component */
1370    foreach( $rows as $line ) {
1371      if( '' == trim( $line ))
1372        continue;
1373      if( $nl == substr( $line, 0 - strlen( $nl )))
1374        $line = substr( $line, 0, ( strlen( $line ) - strlen( $nl ))).'\n';
1375      elseif( "\n\r" == substr( $line, 0 - strlen( "\n\r" )))
1376        $line = substr( $line, 0, ( strlen( $line ) - strlen( "\n\r" ))).'\n';
1377      if(     'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) {
1378        ++$calsync;
1379        continue;
1380      }
1381      elseif( 'END:VCALENDAR'   == strtoupper( substr( $line, 0, 13 ))) {
1382        $calsync--;
1383        break;
1384      }
1385      elseif( 1 != $calsync )
1386        return FALSE;                 /* err 20 */
1387      elseif( in_array( strtoupper( substr( $line, 0, 6 )), array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' ))) {
1388        $this->components[] = $comp->copy();
1389        continue;
1390      }
1391      if(     'BEGIN:VEVENT'    == strtoupper( substr( $line, 0, 12 )))
1392        $comp = new vevent();
1393      elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 )))
1394        $comp = new vfreebusy();
1395      elseif( 'BEGIN:VJOURNAL'  == strtoupper( substr( $line, 0, 14 )))
1396        $comp = new vjournal();
1397      elseif( 'BEGIN:VTODO'     == strtoupper( substr( $line, 0, 11 )))
1398        $comp = new vtodo();
1399      elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 )))
1400        $comp = new vtimezone();
1401      else  /* update component with unparsed data */
1402        $comp->unparsed[] = $line;
1403    } // end - foreach( rows.. .
1404            /* parse data for calendar (this) object */
1405    if( is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) {
1406            /* concatenate property values spread over several lines */
1407      $lastix    = -1;
1408      $propnames = array( 'calscale','method','prodid','version','x-' );
1409      $proprows  = array();
1410      foreach( $this->unparsed as $line ) {
1411        if( '' == trim( $line ))
1412          continue;
1413        $newProp = FALSE;
1414        foreach ( $propnames as $propname ) {
1415          if( $propname == strtolower( substr( $line, 0, strlen( $propname )))) {
1416            $newProp = TRUE;
1417            break;
1418          }
1419        }
1420        if( $newProp ) {
1421          $newProp = FALSE;
1422          ++$lastix;
1423          $proprows[$lastix]  = $line;
1424        }
1425        else {
1426
1427
1428            /* todo: fix count for multibyte characters.. . */
1429          $strlen = ( defined( MB_OVERLOAD_STRING )) ? mb_strlen( $proprows[$lastix] ) : strlen( $proprows[$lastix] );
1430            /* remove line breaks */
1431//          if(( 1 < strlen( $proprows[$lastix] ))         &&
1432          if(( 73 < $strlen )                            &&
1433             ( '\n' == substr( $proprows[$lastix], -2 )) &&
1434             (  ' ' == substr( $line, 0, 1 ))) {
1435//            $proprows[$lastix] = substr( $proprows[$lastix], 0, strlen( $proprows[$lastix] ) - 2 );
1436            $proprows[$lastix] = substr( $proprows[$lastix], 0, ( $strlen - 2 ));
1437            $line = substr( $line, 1 );
1438          }
1439            /* concatenate lines within the same property */
1440          $proprows[$lastix] .= $line;
1441        }
1442      }
1443      foreach( $proprows as $line ) {
1444        if( '\n' == substr( $line, -2 ))
1445          $line = substr( $line, 0, strlen( $line ) - 2 );
1446            /* get propname */
1447        $cix = $propname = null;
1448        for( $cix=0, $clen = strlen( $line ); $cix < $clen; ++$cix ) {
1449          if( in_array( $line[$cix], array( ':', ';' )))
1450            break;
1451          else
1452            $propname .= $line[$cix];
1453        }
1454            /* ignore version/prodid properties */
1455        if( in_array( strtoupper( $propname ), array( 'VERSION', 'PRODID' )))
1456          continue;
1457        $line = substr( $line, $cix);
1458            /* separate attributes from value */
1459        $attr   = array();
1460        $attrix = -1;
1461        $strlen = strlen( $line );
1462        for( $cix=0; $cix < $strlen; ++$cix ) {
1463          if((       ':'   == $line[$cix] )             &&
1464                   ( '://' != substr( $line, $cix, 3 )) &&
1465             ( !in_array( strtolower( substr( $line, $cix - 3, 4 )), array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ))) &&
1466             ( !in_array( strtolower( substr( $line, $cix - 4, 5 )), array( 'crid:', 'news:', 'pres:' ))) &&
1467             ( 'mailto:'   != strtolower( substr( $line, $cix - 6, 7 )))) {
1468            $attrEnd = TRUE;
1469            if(( $cix < ( $strlen - 4 )) &&
1470                 ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
1471              for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
1472                if( '://' == substr( $line, $c2ix - 2, 3 )) {
1473                  $attrEnd = FALSE;
1474                  break; // an URI with a portnr!!
1475                }
1476              }
1477            }
1478            if( $attrEnd) {
1479              $line = substr( $line, $cix + 1 );
1480              break;
1481            }
1482          }
1483          if( ';' == $line[$cix] )
1484            $attr[++$attrix] = null;
1485          else
1486            $attr[$attrix] .= $line[$cix];
1487        }
1488
1489            /* make attributes in array format */
1490        $propattr = array();
1491        foreach( $attr as $attribute ) {
1492          $attrsplit = explode( '=', $attribute, 2 );
1493          if( 1 < count( $attrsplit ))
1494            $propattr[$attrsplit[0]] = $attrsplit[1];
1495          else
1496            $propattr[] = $attribute;
1497        }
1498            /* update Property */
1499        if( FALSE !== strpos( $line, ',' )) {
1500          $content  = explode( ',', $line );
1501          $clen     = count( $content );
1502          for( $cix = 0; $cix < $clen; ++$cix ) {
1503            if( "\\" == substr( $content[$cix], -1 )) {
1504              $content[$cix] .= ','.$content[$cix + 1];
1505              unset( $content[$cix + 1] );
1506              ++$cix;
1507            }
1508          }
1509          if( 1 < count( $content )) {
1510            foreach( $content as $cix => $contentPart )
1511              $content[$cix] = calendarComponent::_strunrep( $contentPart );
1512            $this->setProperty( $propname, $content, $propattr );
1513            continue;
1514          }
1515          else
1516            $line = reset( $content );
1517          $line = calendarComponent::_strunrep( $line );
1518        }
1519        $this->setProperty( $propname, trim( $line ), $propattr );
1520      } // end - foreach( $this->unparsed.. .
1521    } // end - if( is_array( $this->unparsed.. .
1522    unset( $unparsedtext, $rows, $this->unparsed, $proprows );
1523            /* parse Components */
1524    if( is_array( $this->components ) && ( 0 < count( $this->components ))) {
1525      $ckeys = array_keys( $this->components );
1526      foreach( $ckeys as $ckey ) {
1527        if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
1528          $this->components[$ckey]->parse();
1529        }
1530      }
1531    }
1532    else
1533      return FALSE;                   /* err 91 or something.. . */
1534    return TRUE;
1535  }
1536/*********************************************************************************/
1537/**
1538 * creates formatted output for calendar object instance
1539 *
1540 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1541 * @since 2.6.27 - 2010-12-12
1542 * @return string
1543 */
1544  function createCalendar() {
1545    $calendarInit1 = $calendarInit2 = $calendarxCaldecl = $calendarStart = $calendar = null;
1546    switch( $this->format ) {
1547      case 'xcal':
1548        $calendarInit1 = '<?xml version="1.0" encoding="UTF-8"?>'.$this->nl.
1549                         '<!DOCTYPE iCalendar PUBLIC "-//IETF//DTD XCAL/iCalendar XML//EN"'.$this->nl.
1550                         '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"';
1551        $calendarInit2 = '>'.$this->nl;
1552        $calendarStart = '<vcalendar';
1553        break;
1554      default:
1555        $calendarStart = 'BEGIN:VCALENDAR'.$this->nl;
1556        break;
1557    }
1558    $calendarStart .= $this->createProdid();
1559    $calendarStart .= $this->createVersion();
1560    $calendarStart .= $this->createCalscale();
1561    $calendarStart .= $this->createMethod();
1562    switch( $this->format ) {
1563      case 'xcal':
1564        $nlstrlen = strlen( $this->nl );
1565        if( $this->nl == substr( $calendarStart, ( 0 - $nlstrlen )))
1566          $calendarStart = substr( $calendarStart, 0, ( strlen( $calendarStart ) - $nlstrlen ));
1567        $calendarStart .= '>'.$this->nl;
1568        break;
1569      default:
1570        break;
1571    }
1572    $calendar .= $this->createXprop();
1573    foreach( $this->components as $component ) {
1574      if( empty( $component )) continue;
1575      $component->setConfig( $this->getConfig(), FALSE, TRUE );
1576      $calendar .= $component->createComponent( $this->xcaldecl );
1577    }
1578    if(( 0 < count( $this->xcaldecl )) && ( 'xcal' == $this->format )) { // xCal only
1579      $calendarInit1 .= $this->nl.'['.$this->nl;
1580      $old_xcaldecl = array();
1581      foreach( $this->xcaldecl as $declix => $declPart ) {
1582        if(( 0 < count( $old_xcaldecl)) &&
1583           ( in_array( $declPart['uri'],      $old_xcaldecl['uri'] )) &&
1584           ( in_array( $declPart['external'], $old_xcaldecl['external'] )))
1585          continue; // no duplicate uri and ext. references
1586        $calendarxCaldecl .= '<!';
1587        foreach( $declPart as $declKey => $declValue ) {
1588          switch( $declKey ) {                    // index
1589            case 'xmldecl':                       // no 1
1590              $calendarxCaldecl .= $declValue.' ';
1591              break;
1592            case 'uri':                           // no 2
1593              $calendarxCaldecl .= $declValue.' ';
1594              $old_xcaldecl['uri'][] = $declValue;
1595              break;
1596            case 'ref':                           // no 3
1597              $calendarxCaldecl .= $declValue.' ';
1598              break;
1599            case 'external':                      // no 4
1600              $calendarxCaldecl .= '"'.$declValue.'" ';
1601              $old_xcaldecl['external'][] = $declValue;
1602              break;
1603            case 'type':                          // no 5
1604              $calendarxCaldecl .= $declValue.' ';
1605              break;
1606            case 'type2':                         // no 6
1607              $calendarxCaldecl .= $declValue;
1608              break;
1609          }
1610        }
1611        $calendarxCaldecl .= '>'.$this->nl;
1612      }
1613      $calendarInit2 = ']'.$calendarInit2;
1614    }
1615    switch( $this->format ) {
1616      case 'xcal':
1617        $calendar .= '</vcalendar>'.$this->nl;
1618        break;
1619      default:
1620        $calendar .= 'END:VCALENDAR'.$this->nl;
1621        break;
1622    }
1623    return $calendarInit1.$calendarxCaldecl.$calendarInit2.$calendarStart.$calendar;
1624  }
1625/**
1626 * a HTTP redirect header is sent with created, updated and/or parsed calendar
1627 *
1628 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1629 * @since 2.2.12 - 2007-10-23
1630 * @return redirect
1631 */
1632  function returnCalendar() {
1633    $filename = $this->getConfig( 'filename' );
1634    $output   = $this->createCalendar();
1635    $filesize = strlen( $output );
1636//    if( headers_sent( $filename, $linenum ))
1637//      die( "Headers already sent in $filename on line $linenum\n" );
1638    if( 'xcal' == $this->format )
1639      header( 'Content-Type: application/calendar+xml; charset=utf-8' );
1640    else
1641      header( 'Content-Type: text/calendar; charset=utf-8' );
1642    header( 'Content-Length: '.$filesize );
1643    header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
1644    header( 'Cache-Control: max-age=10' );
1645    echo $output;
1646    die();
1647  }
1648/**
1649 * save content in a file
1650 *
1651 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1652 * @since 2.2.12 - 2007-12-30
1653 * @param string $directory optional
1654 * @param string $filename optional
1655 * @param string $delimiter optional
1656 * @return bool
1657 */
1658  function saveCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE ) {
1659    if( $directory )
1660      $this->setConfig( 'directory', $directory );
1661    if( $filename )
1662      $this->setConfig( 'filename',  $filename );
1663    if( $delimiter && ($delimiter != DIRECTORY_SEPARATOR ))
1664      $this->setConfig( 'delimiter', $delimiter );
1665    if( FALSE === ( $dirfile = $this->getConfig( 'url' )))
1666      $dirfile = $this->getConfig( 'dirfile' );
1667    $iCalFile = @fopen( $dirfile, 'w' );
1668    if( $iCalFile ) {
1669      if( FALSE === fwrite( $iCalFile, $this->createCalendar() ))
1670        return FALSE;
1671      fclose( $iCalFile );
1672      return TRUE;
1673    }
1674    else
1675      return FALSE;
1676  }
1677/**
1678 * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent
1679 * else FALSE is returned
1680 *
1681 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1682 * @since 2.2.12 - 2007-10-28
1683 * @param string $directory optional alt. int timeout
1684 * @param string $filename optional
1685 * @param string $delimiter optional
1686 * @param int timeout optional, default 3600 sec
1687 * @return redirect/FALSE
1688 */
1689  function useCachedCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE, $timeout=3600) {
1690    if ( $directory && ctype_digit( (string) $directory ) && !$filename ) {
1691      $timeout   = (int) $directory;
1692      $directory = FALSE;
1693    }
1694    if( $directory )
1695      $this->setConfig( 'directory', $directory );
1696    if( $filename )
1697      $this->setConfig( 'filename',  $filename );
1698    if( $delimiter && ( $delimiter != DIRECTORY_SEPARATOR ))
1699      $this->setConfig( 'delimiter', $delimiter );
1700    $filesize    = $this->getConfig( 'filesize' );
1701    if( 0 >= $filesize )
1702      return FALSE;
1703    $dirfile     = $this->getConfig( 'dirfile' );
1704    if( time() - filemtime( $dirfile ) < $timeout) {
1705      clearstatcache();
1706      $dirfile   = $this->getConfig( 'dirfile' );
1707      $filename  = $this->getConfig( 'filename' );
1708//    if( headers_sent( $filename, $linenum ))
1709//      die( "Headers already sent in $filename on line $linenum\n" );
1710      if( 'xcal' == $this->format )
1711        header( 'Content-Type: application/calendar+xml; charset=utf-8' );
1712      else
1713        header( 'Content-Type: text/calendar; charset=utf-8' );
1714      header( 'Content-Length: '.$filesize );
1715      header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
1716      header( 'Cache-Control: max-age=10' );
1717      $fp = @fopen( $dirfile, 'r' );
1718      if( $fp ) {
1719        fpassthru( $fp );
1720        fclose( $fp );
1721      }
1722      die();
1723    }
1724    else
1725      return FALSE;
1726  }
1727}
1728/*********************************************************************************/
1729/*********************************************************************************/
1730/**
1731 *  abstract class for calendar components
1732 *
1733 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1734 * @since 2.4.19 - 2008-10-12
1735 */
1736class calendarComponent {
1737            //  component property variables
1738  var $uid;
1739  var $dtstamp;
1740
1741            //  component config variables
1742  var $allowEmpty;
1743  var $language;
1744  var $nl;
1745  var $unique_id;
1746  var $format;
1747  var $objName; // created automatically at instance creation
1748            //  component internal variables
1749  var $componentStart1;
1750  var $componentStart2;
1751  var $componentEnd1;
1752  var $componentEnd2;
1753  var $elementStart1;
1754  var $elementStart2;
1755  var $elementEnd1;
1756  var $elementEnd2;
1757  var $intAttrDelimiter;
1758  var $attributeDelimiter;
1759  var $valueInit;
1760            //  component xCal declaration container
1761  var $xcaldecl;
1762/**
1763 * constructor for calendar component object
1764 *
1765 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1766 * @since 2.4.19 - 2008-10-23
1767 */
1768  function calendarComponent() {
1769    $this->objName         = ( isset( $this->timezonetype )) ?
1770                          strtolower( $this->timezonetype )  :  get_class ( $this );
1771    $this->uid             = array();
1772    $this->dtstamp         = array();
1773
1774    $this->language        = null;
1775    $this->nl              = null;
1776    $this->unique_id       = null;
1777    $this->format          = null;
1778    $this->allowEmpty      = TRUE;
1779    $this->xcaldecl        = array();
1780
1781    $this->_createFormat();
1782    $this->_makeDtstamp();
1783  }
1784/*********************************************************************************/
1785/**
1786 * Property Name: ACTION
1787 */
1788/**
1789 * creates formatted output for calendar component property action
1790 *
1791 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1792 * @since 2.4.8 - 2008-10-22
1793 * @return string
1794 */
1795  function createAction() {
1796    if( empty( $this->action )) return FALSE;
1797    if( empty( $this->action['value'] ))
1798      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ACTION' ) : FALSE;
1799    $attributes = $this->_createParams( $this->action['params'] );
1800    return $this->_createElement( 'ACTION', $attributes, $this->action['value'] );
1801  }
1802/**
1803 * set calendar component property action
1804 *
1805 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1806 * @since 2.4.8 - 2008-11-04
1807 * @param string $value  "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
1808 * @param mixed $params
1809 * @return bool
1810 */
1811  function setAction( $value, $params=FALSE ) {
1812    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
1813    $this->action = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
1814    return TRUE;
1815  }
1816/*********************************************************************************/
1817/**
1818 * Property Name: ATTACH
1819 */
1820/**
1821 * creates formatted output for calendar component property attach
1822 *
1823 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1824 * @since 0.9.7 - 2006-11-23
1825 * @return string
1826 */
1827  function createAttach() {
1828    if( empty( $this->attach )) return FALSE;
1829    $output       = null;
1830    foreach( $this->attach as $attachPart ) {
1831      if(! empty( $attachPart['value'] )) {
1832        $attributes = $this->_createParams( $attachPart['params'] );
1833        $output    .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] );
1834      }
1835      elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' );
1836    }
1837    return $output;
1838  }
1839/**
1840 * set calendar component property attach
1841 *
1842 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1843 * @since 2.5.1 - 2008-11-06
1844 * @param string $value
1845 * @param array $params, optional
1846 * @param integer $index, optional
1847 * @return bool
1848 */
1849  function setAttach( $value, $params=FALSE, $index=FALSE ) {
1850    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
1851    iCalUtilityFunctions::_setMval( $this->attach, $value, $params, FALSE, $index );
1852    return TRUE;
1853  }
1854/*********************************************************************************/
1855/**
1856 * Property Name: ATTENDEE
1857 */
1858/**
1859 * creates formatted output for calendar component property attendee
1860 *
1861 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1862 * @since 2.6.29 - 2010-11-29
1863 * @return string
1864 */
1865  function createAttendee() {
1866    if( empty( $this->attendee )) return FALSE;
1867    $output = null;
1868    foreach( $this->attendee as $attendeePart ) {                      // start foreach 1
1869      if( empty( $attendeePart['value'] )) {
1870        if( $this->getConfig( 'allowEmpty' ))
1871          $output .= $this->_createElement( 'ATTENDEE' );
1872        continue;
1873      }
1874      $attendee1 = $attendee2 = null;
1875      foreach( $attendeePart as $paramlabel => $paramvalue ) {         // start foreach 2
1876        if( 'value' == $paramlabel )
1877          $attendee2     .= $paramvalue;
1878        elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif
1879        // set attenddee parameters in rfc2445 order
1880          if( isset( $paramvalue['CUTYPE'] ))
1881            $attendee1   .= $this->intAttrDelimiter.'CUTYPE="'.$paramvalue['CUTYPE'].'"';
1882          if( isset( $paramvalue['MEMBER'] )) {
1883            $attendee1   .= $this->intAttrDelimiter.'MEMBER=';
1884            foreach( $paramvalue['MEMBER'] as $cix => $opv )
1885              $attendee1 .= ( $cix ) ? ', "'.$opv.'"' : '"'.$opv.'"' ;
1886          }
1887          if( isset( $paramvalue['ROLE'] ))
1888            $attendee1   .= $this->intAttrDelimiter.'ROLE="'.$paramvalue['ROLE'].'"';
1889          if( isset( $paramvalue['PARTSTAT'] ))
1890            $attendee1   .= $this->intAttrDelimiter.'PARTSTAT="'.$paramvalue['PARTSTAT'].'"';
1891          if( isset( $paramvalue['RSVP'] ))
1892            $attendee1   .= $this->intAttrDelimiter.'RSVP="'.$paramvalue['RSVP'].'"';
1893          if( isset( $paramvalue['DELEGATED-TO'] )) {
1894            $attendee1   .= $this->intAttrDelimiter.'DELEGATED-TO=';
1895            foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv )
1896              $attendee1 .= ( $cix ) ? ', "'.$opv.'"' : '"'.$opv.'"' ;
1897          }
1898          if( isset( $paramvalue['DELEGATED-FROM'] )) {
1899            $attendee1   .= $this->intAttrDelimiter.'DELEGATED-FROM=';
1900            foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv )
1901              $attendee1 .= ( $cix ) ? ', "'.$opv.'"' : '"'.$opv.'"' ;
1902          }
1903          if( isset( $paramvalue['SENT-BY'] ))
1904            $attendee1   .= $this->intAttrDelimiter.'SENT-BY="'.$paramvalue['SENT-BY'].'"';
1905          if( isset( $paramvalue['CN'] ))
1906            $attendee1   .= $this->intAttrDelimiter.'CN="'.$paramvalue['CN'].'"';
1907          if( isset( $paramvalue['DIR'] ))
1908            $attendee1   .= $this->intAttrDelimiter.'DIR="'.$paramvalue['DIR'].'"';
1909          if( isset( $paramvalue['LANGUAGE'] ))
1910            $attendee1   .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE'];
1911          $xparams = array();
1912          foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3
1913            if( ctype_digit( (string) $optparamlabel )) {
1914              $xparams[]  = $optparamvalue;
1915              continue;
1916            }
1917            if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' )))
1918              $xparams[$optparamlabel] = $optparamvalue;
1919          } // end foreach 3
1920          ksort( $xparams, SORT_STRING );
1921          foreach( $xparams as $paramKey => $paramValue ) {
1922            if( ctype_digit( (string) $paramKey ))
1923              $attendee1 .= $this->intAttrDelimiter.$paramValue;
1924            else
1925              $attendee1 .= $this->intAttrDelimiter."$paramKey=$paramValue";
1926          }      // end foreach 3
1927        }        // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue )))
1928      }          // end foreach 2
1929      $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 );
1930    }              // end foreach 1
1931    return $output;
1932  }
1933/**
1934 * set calendar component property attach
1935 *
1936 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
1937 * @since 2.6.34 - 2010-12-18
1938 * @param string $value
1939 * @param array $params, optional
1940 * @param integer $index, optional
1941 * @return bool
1942 */
1943  function setAttendee( $value, $params=FALSE, $index=FALSE ) {
1944    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
1945          // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero://  may exist.. . also in params
1946    if( FALSE !== ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
1947      $value = strtoupper( substr( $value, 0, $pos )).substr( $value, $pos );
1948    elseif( !empty( $value ))
1949      $value = 'MAILTO:'.$value;
1950    $params2 = array();
1951    if( is_array($params )) {
1952      $optarrays = array();
1953      foreach( $params as $optparamlabel => $optparamvalue ) {
1954        $optparamlabel = strtoupper( $optparamlabel );
1955        switch( $optparamlabel ) {
1956          case 'MEMBER':
1957          case 'DELEGATED-TO':
1958          case 'DELEGATED-FROM':
1959            if( !is_array( $optparamvalue ))
1960              $optparamvalue = array( $optparamvalue );
1961            foreach( $optparamvalue as $part ) {
1962              $part = trim( $part );
1963              if(( '"' == substr( $part, 0, 1 )) &&
1964                 ( '"' == substr( $part, -1 )))
1965                $part = substr( $part, 1, ( strlen( $part ) - 2 ));
1966              if( 'mailto:' != strtolower( substr( $part, 0, 7 )))
1967                $part = "MAILTO:$part";
1968              else
1969                $part = 'MAILTO:'.substr( $part, 7 );
1970              $optarrays[$optparamlabel][] = $part;
1971            }
1972            break;
1973          default:
1974            if(( '"' == substr( $optparamvalue, 0, 1 )) &&
1975               ( '"' == substr( $optparamvalue, -1 )))
1976              $optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 ));
1977            if( 'SENT-BY' ==  $optparamlabel ) {
1978              if( 'mailto:' != strtolower( substr( $optparamvalue, 0, 7 )))
1979                $optparamvalue = "MAILTO:$optparamvalue";
1980              else
1981                $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 );
1982            }
1983            $params2[$optparamlabel] = $optparamvalue;
1984            break;
1985        } // end switch( $optparamlabel.. .
1986      } // end foreach( $optparam.. .
1987      foreach( $optarrays as $optparamlabel => $optparams )
1988        $params2[$optparamlabel] = $optparams;
1989    }
1990        // remove defaults
1991    iCalUtilityFunctions::_existRem( $params2, 'CUTYPE',   'INDIVIDUAL' );
1992    iCalUtilityFunctions::_existRem( $params2, 'PARTSTAT', 'NEEDS-ACTION' );
1993    iCalUtilityFunctions::_existRem( $params2, 'ROLE',     'REQ-PARTICIPANT' );
1994    iCalUtilityFunctions::_existRem( $params2, 'RSVP',     'FALSE' );
1995        // check language setting
1996    if( isset( $params2['CN' ] )) {
1997      $lang = $this->getConfig( 'language' );
1998      if( !isset( $params2['LANGUAGE' ] ) && !empty( $lang ))
1999        $params2['LANGUAGE' ] = $lang;
2000    }
2001    iCalUtilityFunctions::_setMval( $this->attendee, $value, $params2, FALSE, $index );
2002    return TRUE;
2003  }
2004/*********************************************************************************/
2005/**
2006 * Property Name: CATEGORIES
2007 */
2008/**
2009 * creates formatted output for calendar component property categories
2010 *
2011 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2012 * @since 2.4.8 - 2008-10-22
2013 * @return string
2014 */
2015  function createCategories() {
2016    if( empty( $this->categories )) return FALSE;
2017    $output = null;
2018    foreach( $this->categories as $category ) {
2019      if( empty( $category['value'] )) {
2020        if ( $this->getConfig( 'allowEmpty' ))
2021          $output .= $this->_createElement( 'CATEGORIES' );
2022        continue;
2023      }
2024      $attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' ));
2025      if( is_array( $category['value'] )) {
2026        foreach( $category['value'] as $cix => $categoryPart )
2027          $category['value'][$cix] = $this->_strrep( $categoryPart );
2028        $content  = implode( ',', $category['value'] );
2029      }
2030      else
2031        $content  = $this->_strrep( $category['value'] );
2032      $output    .= $this->_createElement( 'CATEGORIES', $attributes, $content );
2033    }
2034    return $output;
2035  }
2036/**
2037 * set calendar component property categories
2038 *
2039 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2040 * @since 2.5.1 - 2008-11-06
2041 * @param mixed $value
2042 * @param array $params, optional
2043 * @param integer $index, optional
2044 * @return bool
2045 */
2046  function setCategories( $value, $params=FALSE, $index=FALSE ) {
2047    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2048    iCalUtilityFunctions::_setMval( $this->categories, $value, $params, FALSE, $index );
2049    return TRUE;
2050 }
2051/*********************************************************************************/
2052/**
2053 * Property Name: CLASS
2054 */
2055/**
2056 * creates formatted output for calendar component property class
2057 *
2058 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2059 * @since 0.9.7 - 2006-11-20
2060 * @return string
2061 */
2062  function createClass() {
2063    if( empty( $this->class )) return FALSE;
2064    if( empty( $this->class['value'] ))
2065      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'CLASS' ) : FALSE;
2066    $attributes = $this->_createParams( $this->class['params'] );
2067    return $this->_createElement( 'CLASS', $attributes, $this->class['value'] );
2068  }
2069/**
2070 * set calendar component property class
2071 *
2072 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2073 * @since 2.4.8 - 2008-11-04
2074 * @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name
2075 * @param array $params optional
2076 * @return bool
2077 */
2078  function setClass( $value, $params=FALSE ) {
2079    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2080    $this->class = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2081    return TRUE;
2082  }
2083/*********************************************************************************/
2084/**
2085 * Property Name: COMMENT
2086 */
2087/**
2088 * creates formatted output for calendar component property comment
2089 *
2090 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2091 * @since 2.4.8 - 2008-10-22
2092 * @return string
2093 */
2094  function createComment() {
2095    if( empty( $this->comment )) return FALSE;
2096    $output = null;
2097    foreach( $this->comment as $commentPart ) {
2098      if( empty( $commentPart['value'] )) {
2099        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' );
2100        continue;
2101      }
2102      $attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' ));
2103      $content    = $this->_strrep( $commentPart['value'] );
2104      $output    .= $this->_createElement( 'COMMENT', $attributes, $content );
2105    }
2106    return $output;
2107  }
2108/**
2109 * set calendar component property comment
2110 *
2111 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2112 * @since 2.5.1 - 2008-11-06
2113 * @param string $value
2114 * @param array $params, optional
2115 * @param integer $index, optional
2116 * @return bool
2117 */
2118  function setComment( $value, $params=FALSE, $index=FALSE ) {
2119    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2120    iCalUtilityFunctions::_setMval( $this->comment, $value, $params, FALSE, $index );
2121    return TRUE;
2122  }
2123/*********************************************************************************/
2124/**
2125 * Property Name: COMPLETED
2126 */
2127/**
2128 * creates formatted output for calendar component property completed
2129 *
2130 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2131 * @since 2.4.8 - 2008-10-22
2132 * @return string
2133 */
2134  function createCompleted( ) {
2135    if( empty( $this->completed )) return FALSE;
2136    if( !isset( $this->completed['value']['year'] )  &&
2137        !isset( $this->completed['value']['month'] ) &&
2138        !isset( $this->completed['value']['day'] )   &&
2139        !isset( $this->completed['value']['hour'] )  &&
2140        !isset( $this->completed['value']['min'] )   &&
2141        !isset( $this->completed['value']['sec'] ))
2142      if( $this->getConfig( 'allowEmpty' ))
2143        return $this->_createElement( 'COMPLETED' );
2144      else return FALSE;
2145    $formatted  = iCalUtilityFunctions::_format_date_time( $this->completed['value'], 7 );
2146    $attributes = $this->_createParams( $this->completed['params'] );
2147    return $this->_createElement( 'COMPLETED', $attributes, $formatted );
2148  }
2149/**
2150 * set calendar component property completed
2151 *
2152 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2153 * @since 2.4.8 - 2008-10-23
2154 * @param mixed $year
2155 * @param mixed $month optional
2156 * @param int $day optional
2157 * @param int $hour optional
2158 * @param int $min optional
2159 * @param int $sec optional
2160 * @param array $params optional
2161 * @return bool
2162 */
2163  function setCompleted( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2164    if( empty( $year )) {
2165      if( $this->getConfig( 'allowEmpty' )) {
2166        $this->completed = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2167        return TRUE;
2168      }
2169      else
2170        return FALSE;
2171    }
2172    $this->completed = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2173    return TRUE;
2174  }
2175/*********************************************************************************/
2176/**
2177 * Property Name: CONTACT
2178 */
2179/**
2180 * creates formatted output for calendar component property contact
2181 *
2182 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2183 * @since 2.4.8 - 2008-10-23
2184 * @return string
2185 */
2186  function createContact() {
2187    if( empty( $this->contact )) return FALSE;
2188    $output = null;
2189    foreach( $this->contact as $contact ) {
2190      if( !empty( $contact['value'] )) {
2191        $attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' ));
2192        $content    = $this->_strrep( $contact['value'] );
2193        $output    .= $this->_createElement( 'CONTACT', $attributes, $content );
2194      }
2195      elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' );
2196    }
2197    return $output;
2198  }
2199/**
2200 * set calendar component property contact
2201 *
2202 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2203 * @since 2.5.1 - 2008-11-05
2204 * @param string $value
2205 * @param array $params, optional
2206 * @param integer $index, optional
2207 * @return bool
2208 */
2209  function setContact( $value, $params=FALSE, $index=FALSE ) {
2210    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2211    iCalUtilityFunctions::_setMval( $this->contact, $value, $params, FALSE, $index );
2212    return TRUE;
2213  }
2214/*********************************************************************************/
2215/**
2216 * Property Name: CREATED
2217 */
2218/**
2219 * creates formatted output for calendar component property created
2220 *
2221 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2222 * @since 2.4.8 - 2008-10-21
2223 * @return string
2224 */
2225  function createCreated() {
2226    if( empty( $this->created )) return FALSE;
2227    $formatted  = iCalUtilityFunctions::_format_date_time( $this->created['value'], 7 );
2228    $attributes = $this->_createParams( $this->created['params'] );
2229    return $this->_createElement( 'CREATED', $attributes, $formatted );
2230  }
2231/**
2232 * set calendar component property created
2233 *
2234 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2235 * @since 2.4.8 - 2008-10-23
2236 * @param mixed $year optional
2237 * @param mixed $month optional
2238 * @param int $day optional
2239 * @param int $hour optional
2240 * @param int $min optional
2241 * @param int $sec optional
2242 * @param mixed $params optional
2243 * @return bool
2244 */
2245  function setCreated( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2246    if( !isset( $year )) {
2247      $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' )));
2248    }
2249    $this->created = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2250    return TRUE;
2251  }
2252/*********************************************************************************/
2253/**
2254 * Property Name: DESCRIPTION
2255 */
2256/**
2257 * creates formatted output for calendar component property description
2258 *
2259 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2260 * @since 2.4.8 - 2008-10-22
2261 * @return string
2262 */
2263  function createDescription() {
2264    if( empty( $this->description )) return FALSE;
2265    $output       = null;
2266    foreach( $this->description as $description ) {
2267      if( !empty( $description['value'] )) {
2268        $attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' ));
2269        $content    = $this->_strrep( $description['value'] );
2270        $output    .= $this->_createElement( 'DESCRIPTION', $attributes, $content );
2271      }
2272      elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' );
2273    }
2274    return $output;
2275  }
2276/**
2277 * set calendar component property description
2278 *
2279 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2280 * @since 2.6.24 - 2010-11-06
2281 * @param string $value
2282 * @param array $params, optional
2283 * @param integer $index, optional
2284 * @return bool
2285 */
2286  function setDescription( $value, $params=FALSE, $index=FALSE ) {
2287    if( empty( $value )) { if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; }
2288    if( 'vjournal' != $this->objName )
2289      $index = 1;
2290    iCalUtilityFunctions::_setMval( $this->description, $value, $params, FALSE, $index );
2291    return TRUE;
2292  }
2293/*********************************************************************************/
2294/**
2295 * Property Name: DTEND
2296 */
2297/**
2298 * creates formatted output for calendar component property dtend
2299 *
2300 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2301 * @since 2.4.8 - 2008-10-21
2302 * @return string
2303 */
2304  function createDtend() {
2305    if( empty( $this->dtend )) return FALSE;
2306    if( !isset( $this->dtend['value']['year'] )  &&
2307        !isset( $this->dtend['value']['month'] ) &&
2308        !isset( $this->dtend['value']['day'] )   &&
2309        !isset( $this->dtend['value']['hour'] )  &&
2310        !isset( $this->dtend['value']['min'] )   &&
2311        !isset( $this->dtend['value']['sec'] ))
2312      if( $this->getConfig( 'allowEmpty' ))
2313        return $this->_createElement( 'DTEND' );
2314      else return FALSE;
2315    $formatted  = iCalUtilityFunctions::_format_date_time( $this->dtend['value'] );
2316    $attributes = $this->_createParams( $this->dtend['params'] );
2317    return $this->_createElement( 'DTEND', $attributes, $formatted );
2318  }
2319/**
2320 * set calendar component property dtend
2321 *
2322 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2323 * @since 2.4.8 - 2008-10-23
2324 * @param mixed $year
2325 * @param mixed $month optional
2326 * @param int $day optional
2327 * @param int $hour optional
2328 * @param int $min optional
2329 * @param int $sec optional
2330 * @param string $tz optional
2331 * @param array params optional
2332 * @return bool
2333 */
2334  function setDtend( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
2335    if( empty( $year )) {
2336      if( $this->getConfig( 'allowEmpty' )) {
2337        $this->dtend = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2338        return TRUE;
2339      }
2340      else
2341        return FALSE;
2342    }
2343    $this->dtend = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params );
2344    return TRUE;
2345  }
2346/*********************************************************************************/
2347/**
2348 * Property Name: DTSTAMP
2349 */
2350/**
2351 * creates formatted output for calendar component property dtstamp
2352 *
2353 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2354 * @since 2.4.4 - 2008-03-07
2355 * @return string
2356 */
2357  function createDtstamp() {
2358    if( !isset( $this->dtstamp['value']['year'] )  &&
2359        !isset( $this->dtstamp['value']['month'] ) &&
2360        !isset( $this->dtstamp['value']['day'] )   &&
2361        !isset( $this->dtstamp['value']['hour'] )  &&
2362        !isset( $this->dtstamp['value']['min'] )   &&
2363        !isset( $this->dtstamp['value']['sec'] ))
2364      $this->_makeDtstamp();
2365    $formatted  = iCalUtilityFunctions::_format_date_time( $this->dtstamp['value'], 7 );
2366    $attributes = $this->_createParams( $this->dtstamp['params'] );
2367    return $this->_createElement( 'DTSTAMP', $attributes, $formatted );
2368  }
2369/**
2370 * computes datestamp for calendar component object instance dtstamp
2371 *
2372 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2373 * @since 2.6.25 - 2010-11-09
2374 * @return void
2375 */
2376  function _makeDtstamp() {
2377    $d = mktime( date('H'), date('m'), (date('s') - date( 'Z' )), date('m'), date('d'), date('Y'));
2378    $this->dtstamp['value'] = array( 'year'  => date( 'Y', $d )
2379                                   , 'month' => date( 'm', $d )
2380                                   , 'day'   => date( 'd', $d )
2381                                   , 'hour'  => date( 'H', $d )
2382                                   , 'min'   => date( 'i', $d )
2383                                   , 'sec'   => date( 's', $d ));
2384    $this->dtstamp['params'] = null;
2385  }
2386/**
2387 * set calendar component property dtstamp
2388 *
2389 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2390 * @since 2.4.8 - 2008-10-23
2391 * @param mixed $year
2392 * @param mixed $month optional
2393 * @param int $day optional
2394 * @param int $hour optional
2395 * @param int $min optional
2396 * @param int $sec optional
2397 * @param array $params optional
2398 * @return TRUE
2399 */
2400  function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2401    if( empty( $year ))
2402      $this->_makeDtstamp();
2403    else
2404      $this->dtstamp = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2405    return TRUE;
2406  }
2407/*********************************************************************************/
2408/**
2409 * Property Name: DTSTART
2410 */
2411/**
2412 * creates formatted output for calendar component property dtstart
2413 *
2414 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2415 * @since 2.4.16 - 2008-10-26
2416 * @return string
2417 */
2418  function createDtstart() {
2419    if( empty( $this->dtstart )) return FALSE;
2420    if( !isset( $this->dtstart['value']['year'] )  &&
2421        !isset( $this->dtstart['value']['month'] ) &&
2422        !isset( $this->dtstart['value']['day'] )   &&
2423        !isset( $this->dtstart['value']['hour'] )  &&
2424        !isset( $this->dtstart['value']['min'] )   &&
2425        !isset( $this->dtstart['value']['sec'] ))
2426    if( $this->getConfig( 'allowEmpty' ))
2427      return $this->_createElement( 'DTSTART' );
2428    else return FALSE;
2429    if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' )))
2430      unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] );
2431    $formatted  = iCalUtilityFunctions::_format_date_time( $this->dtstart['value'] );
2432    $attributes = $this->_createParams( $this->dtstart['params'] );
2433    return $this->_createElement( 'DTSTART', $attributes, $formatted );
2434  }
2435/**
2436 * set calendar component property dtstart
2437 *
2438 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2439 * @since 2.6.22 - 2010-09-22
2440 * @param mixed $year
2441 * @param mixed $month optional
2442 * @param int $day optional
2443 * @param int $hour optional
2444 * @param int $min optional
2445 * @param int $sec optional
2446 * @param string $tz optional
2447 * @param array $params optional
2448 * @return bool
2449 */
2450  function setDtstart( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
2451    if( empty( $year )) {
2452      if( $this->getConfig( 'allowEmpty' )) {
2453        $this->dtstart = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2454        return TRUE;
2455      }
2456      else
2457        return FALSE;
2458    }
2459    $this->dtstart = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName );
2460    return TRUE;
2461  }
2462/*********************************************************************************/
2463/**
2464 * Property Name: DUE
2465 */
2466/**
2467 * creates formatted output for calendar component property due
2468 *
2469 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2470 * @since 2.4.8 - 2008-10-22
2471 * @return string
2472 */
2473  function createDue() {
2474    if( empty( $this->due )) return FALSE;
2475    if( !isset( $this->due['value']['year'] )  &&
2476        !isset( $this->due['value']['month'] ) &&
2477        !isset( $this->due['value']['day'] )   &&
2478        !isset( $this->due['value']['hour'] )  &&
2479        !isset( $this->due['value']['min'] )   &&
2480        !isset( $this->due['value']['sec'] ))
2481      if( $this->getConfig( 'allowEmpty' ))
2482        return $this->_createElement( 'DUE' );
2483      else return FALSE;
2484    $formatted  = iCalUtilityFunctions::_format_date_time( $this->due['value'] );
2485    $attributes = $this->_createParams( $this->due['params'] );
2486    return $this->_createElement( 'DUE', $attributes, $formatted );
2487  }
2488/**
2489 * set calendar component property due
2490 *
2491 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2492 * @since 2.4.8 - 2008-11-04
2493 * @param mixed $year
2494 * @param mixed $month optional
2495 * @param int $day optional
2496 * @param int $hour optional
2497 * @param int $min optional
2498 * @param int $sec optional
2499 * @param array $params optional
2500 * @return bool
2501 */
2502  function setDue( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
2503    if( empty( $year )) {
2504      if( $this->getConfig( 'allowEmpty' )) {
2505        $this->due = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2506        return TRUE;
2507      }
2508      else
2509        return FALSE;
2510    }
2511    $this->due = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params );
2512    return TRUE;
2513  }
2514/*********************************************************************************/
2515/**
2516 * Property Name: DURATION
2517 */
2518/**
2519 * creates formatted output for calendar component property duration
2520 *
2521 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2522 * @since 2.4.8 - 2008-10-21
2523 * @return string
2524 */
2525  function createDuration() {
2526    if( empty( $this->duration )) return FALSE;
2527    if( !isset( $this->duration['value']['week'] ) &&
2528        !isset( $this->duration['value']['day'] )  &&
2529        !isset( $this->duration['value']['hour'] ) &&
2530        !isset( $this->duration['value']['min'] )  &&
2531        !isset( $this->duration['value']['sec'] ))
2532      if( $this->getConfig( 'allowEmpty' ))
2533        return $this->_createElement( 'DURATION', array(), null );
2534      else return FALSE;
2535    $attributes = $this->_createParams( $this->duration['params'] );
2536    return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_format_duration( $this->duration['value'] ));
2537  }
2538/**
2539 * set calendar component property duration
2540 *
2541 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2542 * @since 2.4.8 - 2008-11-04
2543 * @param mixed $week
2544 * @param mixed $day optional
2545 * @param int $hour optional
2546 * @param int $min optional
2547 * @param int $sec optional
2548 * @param array $params optional
2549 * @return bool
2550 */
2551  function setDuration( $week, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2552    if( empty( $week )) if( $this->getConfig( 'allowEmpty' )) $week = null; else return FALSE;
2553    if( is_array( $week ) && ( 1 <= count( $week )))
2554      $this->duration = array( 'value' => iCalUtilityFunctions::_duration_array( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
2555    elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) {
2556      $week = trim( $week );
2557      if( in_array( substr( $week, 0, 1 ), array( '+', '-' )))
2558        $week = substr( $week, 1 );
2559      $this->duration = array( 'value' => iCalUtilityFunctions::_duration_string( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
2560    }
2561    elseif( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec ))
2562      return FALSE;
2563    else
2564      $this->duration = array( 'value' => iCalUtilityFunctions::_duration_array( array( $week, $day, $hour, $min, $sec )), 'params' => iCalUtilityFunctions::_setParams( $params ));
2565    return TRUE;
2566  }
2567/*********************************************************************************/
2568/**
2569 * Property Name: EXDATE
2570 */
2571/**
2572 * creates formatted output for calendar component property exdate
2573 *
2574 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2575 * @since 2.4.8 - 2008-10-22
2576 * @return string
2577 */
2578  function createExdate() {
2579    if( empty( $this->exdate )) return FALSE;
2580    $output = null;
2581    foreach( $this->exdate as $ex => $theExdate ) {
2582      if( empty( $theExdate['value'] )) {
2583        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'EXDATE' );
2584        continue;
2585      }
2586      $content = $attributes = null;
2587      foreach( $theExdate['value'] as $eix => $exdatePart ) {
2588        $parno = count( $exdatePart );
2589        $formatted = iCalUtilityFunctions::_format_date_time( $exdatePart, $parno );
2590        if( isset( $theExdate['params']['TZID'] ))
2591          $formatted = str_replace( 'Z', '', $formatted);
2592        if( 0 < $eix ) {
2593          if( isset( $theExdate['value'][0]['tz'] )) {
2594            if( ctype_digit( substr( $theExdate['value'][0]['tz'], -4 )) ||
2595               ( 'Z' == $theExdate['value'][0]['tz'] )) {
2596              if( 'Z' != substr( $formatted, -1 ))
2597                $formatted .= 'Z';
2598            }
2599            else
2600              $formatted = str_replace( 'Z', '', $formatted );
2601          }
2602          else
2603            $formatted = str_replace( 'Z', '', $formatted );
2604        }
2605        $content .= ( 0 < $eix ) ? ','.$formatted : $formatted;
2606      }
2607      $attributes .= $this->_createParams( $theExdate['params'] );
2608      $output .= $this->_createElement( 'EXDATE', $attributes, $content );
2609    }
2610    return $output;
2611  }
2612/**
2613 * set calendar component property exdate
2614 *
2615 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2616 * @since 2.5.1 - 2008-11-05
2617 * @param array exdates
2618 * @param array $params, optional
2619 * @param integer $index, optional
2620 * @return bool
2621 */
2622  function setExdate( $exdates, $params=FALSE, $index=FALSE ) {
2623    if( empty( $exdates )) {
2624      if( $this->getConfig( 'allowEmpty' )) {
2625        iCalUtilityFunctions::_setMval( $this->exdate, null, $params, FALSE, $index );
2626        return TRUE;
2627      }
2628      else
2629        return FALSE;
2630    }
2631    $input  = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
2632            /* ev. check 1:st date and save ev. timezone **/
2633    iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] );
2634    iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter
2635    foreach( $exdates as $eix => $theExdate ) {
2636      if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate ))
2637        $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno );
2638      elseif(  is_array( $theExdate ))
2639        $exdatea = iCalUtilityFunctions::_date_time_array( $theExdate, $parno );
2640      elseif( 8 <= strlen( trim( $theExdate ))) // ex. 2006-08-03 10:12:18
2641        $exdatea = iCalUtilityFunctions::_date_time_string( $theExdate, $parno );
2642      if( 3 == $parno )
2643        unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] );
2644      elseif( isset( $exdatea['tz'] ))
2645        $exdatea['tz'] = (string) $exdatea['tz'];
2646      if(  isset( $input['params']['TZID'] ) ||
2647         ( isset( $exdatea['tz'] ) && !iCalUtilityFunctions::_isOffset( $exdatea['tz'] )) ||
2648         ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) ||
2649         ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] )))
2650        unset( $exdatea['tz'] );
2651      $input['value'][] = $exdatea;
2652    }
2653    if( 0 >= count( $input['value'] ))
2654      return FALSE;
2655    if( 3 == $parno ) {
2656      $input['params']['VALUE'] = 'DATE';
2657      unset( $input['params']['TZID'] );
2658    }
2659    iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index );
2660    return TRUE;
2661  }
2662/*********************************************************************************/
2663/**
2664 * Property Name: EXRULE
2665 */
2666/**
2667 * creates formatted output for calendar component property exrule
2668 *
2669 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2670 * @since 2.4.8 - 2008-10-22
2671 * @return string
2672 */
2673  function createExrule() {
2674    if( empty( $this->exrule )) return FALSE;
2675    return $this->_format_recur( 'EXRULE', $this->exrule );
2676  }
2677/**
2678 * set calendar component property exdate
2679 *
2680 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2681 * @since 2.5.1 - 2008-11-05
2682 * @param array $exruleset
2683 * @param array $params, optional
2684 * @param integer $index, optional
2685 * @return bool
2686 */
2687  function setExrule( $exruleset, $params=FALSE, $index=FALSE ) {
2688    if( empty( $exruleset )) if( $this->getConfig( 'allowEmpty' )) $exruleset = null; else return FALSE;
2689    iCalUtilityFunctions::_setMval( $this->exrule, iCalUtilityFunctions::_setRexrule( $exruleset ), $params, FALSE, $index );
2690    return TRUE;
2691  }
2692/*********************************************************************************/
2693/**
2694 * Property Name: FREEBUSY
2695 */
2696/**
2697 * creates formatted output for calendar component property freebusy
2698 *
2699 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2700 * @since 2.4.8 - 2008-10-22
2701 * @return string
2702 */
2703  function createFreebusy() {
2704    if( empty( $this->freebusy )) return FALSE;
2705    $output = null;
2706    foreach( $this->freebusy as $freebusyPart ) {
2707      if( empty( $freebusyPart['value'] )) {
2708        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' );
2709        continue;
2710      }
2711      $attributes = $content = null;
2712      if( isset( $freebusyPart['value']['fbtype'] )) {
2713        $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype'];
2714        unset( $freebusyPart['value']['fbtype'] );
2715        $freebusyPart['value'] = array_values( $freebusyPart['value'] );
2716      }
2717      else
2718        $attributes .= $this->intAttrDelimiter.'FBTYPE=BUSY';
2719      $attributes .= $this->_createParams( $freebusyPart['params'] );
2720      $fno = 1;
2721      $cnt = count( $freebusyPart['value']);
2722      foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) {
2723        $formatted   = iCalUtilityFunctions::_format_date_time( $freebusyPeriod[0] );
2724        $content .= $formatted;
2725        $content .= '/';
2726        $cnt2 = count( $freebusyPeriod[1]);
2727        if( array_key_exists( 'year', $freebusyPeriod[1] ))      // date-time
2728          $cnt2 = 7;
2729        elseif( array_key_exists( 'week', $freebusyPeriod[1] ))  // duration
2730          $cnt2 = 5;
2731        if(( 7 == $cnt2 )   &&    // period=  -> date-time
2732            isset( $freebusyPeriod[1]['year'] )  &&
2733            isset( $freebusyPeriod[1]['month'] ) &&
2734            isset( $freebusyPeriod[1]['day'] )) {
2735          $content .= iCalUtilityFunctions::_format_date_time( $freebusyPeriod[1] );
2736        }
2737        else {                                  // period=  -> dur-time
2738          $content .= iCalUtilityFunctions::_format_duration( $freebusyPeriod[1] );
2739        }
2740        if( $fno < $cnt )
2741          $content .= ',';
2742        ++$fno;
2743      }
2744      $output .= $this->_createElement( 'FREEBUSY', $attributes, $content );
2745    }
2746    return $output;
2747  }
2748/**
2749 * set calendar component property freebusy
2750 *
2751 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2752 * @since 2.5.1 - 2008-11-05
2753 * @param string $fbType
2754 * @param array $fbValues
2755 * @param array $params, optional
2756 * @param integer $index, optional
2757 * @return bool
2758 */
2759  function setFreebusy( $fbType, $fbValues, $params=FALSE, $index=FALSE ) {
2760    if( empty( $fbValues )) {
2761      if( $this->getConfig( 'allowEmpty' )) {
2762        iCalUtilityFunctions::_setMval( $this->freebusy, null, $params, FALSE, $index );
2763        return TRUE;
2764      }
2765      else
2766        return FALSE;
2767    }
2768    $fbType = strtoupper( $fbType );
2769    if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) &&
2770       ( 'X-' != substr( $fbType, 0, 2 )))
2771      $fbType = 'BUSY';
2772    $input = array( 'fbtype' => $fbType );
2773    foreach( $fbValues as $fbPeriod ) {   // periods => period
2774      $freebusyPeriod = array();
2775      foreach( $fbPeriod as $fbMember ) { // pairs => singlepart
2776        $freebusyPairMember = array();
2777        if( is_array( $fbMember )) {
2778          if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value
2779            $freebusyPairMember       = iCalUtilityFunctions::_date_time_array( $fbMember, 7 );
2780            $freebusyPairMember['tz'] = 'Z';
2781          }
2782          elseif( iCalUtilityFunctions::_isArrayTimestampDate( $fbMember )) { // timestamp value
2783            $freebusyPairMember       = iCalUtilityFunctions::_timestamp2date( $fbMember['timestamp'], 7 );
2784            $freebusyPairMember['tz'] = 'Z';
2785          }
2786          else {                                         // array format duration
2787            $freebusyPairMember = iCalUtilityFunctions::_duration_array( $fbMember );
2788          }
2789        }
2790        elseif(( 3 <= strlen( trim( $fbMember ))) &&    // string format duration
2791               ( in_array( $fbMember{0}, array( 'P', '+', '-' )))) {
2792          if( 'P' != $fbMember{0} )
2793            $fbmember = substr( $fbMember, 1 );
2794          $freebusyPairMember = iCalUtilityFunctions::_duration_string( $fbMember );
2795        }
2796        elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18
2797          $freebusyPairMember       = iCalUtilityFunctions::_date_time_string( $fbMember, 7 );
2798          $freebusyPairMember['tz'] = 'Z';
2799        }
2800        $freebusyPeriod[]   = $freebusyPairMember;
2801      }
2802      $input[]              = $freebusyPeriod;
2803    }
2804    iCalUtilityFunctions::_setMval( $this->freebusy, $input, $params, FALSE, $index );
2805    return TRUE;
2806  }
2807/*********************************************************************************/
2808/**
2809 * Property Name: GEO
2810 */
2811/**
2812 * creates formatted output for calendar component property geo
2813 *
2814 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2815 * @since 2.4.8 - 2008-10-21
2816 * @return string
2817 */
2818  function createGeo() {
2819    if( empty( $this->geo )) return FALSE;
2820    if( empty( $this->geo['value'] ))
2821      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE;
2822    $attributes = $this->_createParams( $this->geo['params'] );
2823    $content    = null;
2824    $content   .= number_format( (float) $this->geo['value']['latitude'], 6, '.', '');
2825    $content   .= ';';
2826    $content   .= number_format( (float) $this->geo['value']['longitude'], 6, '.', '');
2827    return $this->_createElement( 'GEO', $attributes, $content );
2828  }
2829/**
2830 * set calendar component property geo
2831 *
2832 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2833 * @since 2.4.8 - 2008-11-04
2834 * @param float $latitude
2835 * @param float $longitude
2836 * @param array $params optional
2837 * @return bool
2838 */
2839  function setGeo( $latitude, $longitude, $params=FALSE ) {
2840    if( !empty( $latitude ) && !empty( $longitude )) {
2841      if( !is_array( $this->geo )) $this->geo = array();
2842      $this->geo['value']['latitude']  = $latitude;
2843      $this->geo['value']['longitude'] = $longitude;
2844      $this->geo['params'] = iCalUtilityFunctions::_setParams( $params );
2845    }
2846    elseif( $this->getConfig( 'allowEmpty' ))
2847      $this->geo = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) );
2848    else
2849      return FALSE;
2850    return TRUE;
2851  }
2852/*********************************************************************************/
2853/**
2854 * Property Name: LAST-MODIFIED
2855 */
2856/**
2857 * creates formatted output for calendar component property last-modified
2858 *
2859 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2860 * @since 2.4.8 - 2008-10-21
2861 * @return string
2862 */
2863  function createLastModified() {
2864    if( empty( $this->lastmodified )) return FALSE;
2865    $attributes = $this->_createParams( $this->lastmodified['params'] );
2866    $formatted  = iCalUtilityFunctions::_format_date_time( $this->lastmodified['value'], 7 );
2867    return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted );
2868  }
2869/**
2870 * set calendar component property completed
2871 *
2872 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2873 * @since 2.4.8 - 2008-10-23
2874 * @param mixed $year optional
2875 * @param mixed $month optional
2876 * @param int $day optional
2877 * @param int $hour optional
2878 * @param int $min optional
2879 * @param int $sec optional
2880 * @param array $params optional
2881 * @return boll
2882 */
2883  function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2884    if( empty( $year ))
2885      $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' )));
2886    $this->lastmodified = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2887    return TRUE;
2888  }
2889/*********************************************************************************/
2890/**
2891 * Property Name: LOCATION
2892 */
2893/**
2894 * creates formatted output for calendar component property location
2895 *
2896 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2897 * @since 2.4.8 - 2008-10-22
2898 * @return string
2899 */
2900  function createLocation() {
2901    if( empty( $this->location )) return FALSE;
2902    if( empty( $this->location['value'] ))
2903      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'LOCATION' ) : FALSE;
2904    $attributes = $this->_createParams( $this->location['params'], array( 'ALTREP', 'LANGUAGE' ));
2905    $content    = $this->_strrep( $this->location['value'] );
2906    return $this->_createElement( 'LOCATION', $attributes, $content );
2907  }
2908/**
2909 * set calendar component property location
2910 '
2911 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2912 * @since 2.4.8 - 2008-11-04
2913 * @param string $value
2914 * @param array params optional
2915 * @return bool
2916 */
2917  function setLocation( $value, $params=FALSE ) {
2918    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2919    $this->location = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2920    return TRUE;
2921  }
2922/*********************************************************************************/
2923/**
2924 * Property Name: ORGANIZER
2925 */
2926/**
2927 * creates formatted output for calendar component property organizer
2928 *
2929 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2930 * @since 2.6.33 - 2010-12-17
2931 * @return string
2932 */
2933  function createOrganizer() {
2934    if( empty( $this->organizer )) return FALSE;
2935    if( empty( $this->organizer['value'] ))
2936      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ORGANIZER' ) : FALSE;
2937    $attributes = $this->_createParams( $this->organizer['params']
2938                                      , array( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' ));
2939    return $this->_createElement( 'ORGANIZER', $attributes, $this->organizer['value'] );
2940  }
2941/**
2942 * set calendar component property organizer
2943 *
2944 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2945 * @since 2.6.27 - 2010-11-29
2946 * @param string $value
2947 * @param array params optional
2948 * @return bool
2949 */
2950  function setOrganizer( $value, $params=FALSE ) {
2951    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2952    if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
2953      $value = 'MAILTO:'.$value;
2954    else
2955      $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos );
2956    $value = str_replace( 'mailto:', 'MAILTO:', $value );
2957    $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2958    if( isset( $this->organizer['params']['SENT-BY'] )){
2959      if( 'mailto:' !== strtolower( substr( $this->organizer['params']['SENT-BY'], 0, 7 )))
2960        $this->organizer['params']['SENT-BY'] = 'MAILTO:'.$this->organizer['params']['SENT-BY'];
2961      else
2962        $this->organizer['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer['params']['SENT-BY'], 7 );
2963    }
2964    return TRUE;
2965  }
2966/*********************************************************************************/
2967/**
2968 * Property Name: PERCENT-COMPLETE
2969 */
2970/**
2971 * creates formatted output for calendar component property percent-complete
2972 *
2973 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2974 * @since 2.4.8 - 2008-10-22
2975 * @return string
2976 */
2977  function createPercentComplete() {
2978    if( empty( $this->percentcomplete )) return FALSE;
2979    if( empty( $this->percentcomplete['value'] ))
2980      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PERCENT-COMPLETE' ) : FALSE;
2981    $attributes = $this->_createParams( $this->percentcomplete['params'] );
2982    return $this->_createElement( 'PERCENT-COMPLETE', $attributes, $this->percentcomplete['value'] );
2983  }
2984/**
2985 * set calendar component property percent-complete
2986 *
2987 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
2988 * @since 2.4.8 - 2008-11-04
2989 * @param int $value
2990 * @param array $params optional
2991 * @return bool
2992 */
2993  function setPercentComplete( $value, $params=FALSE ) {
2994    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2995    $this->percentcomplete = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2996    return TRUE;
2997  }
2998/*********************************************************************************/
2999/**
3000 * Property Name: PRIORITY
3001 */
3002/**
3003 * creates formatted output for calendar component property priority
3004 *
3005 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3006 * @since 2.4.8 - 2008-10-21
3007 * @return string
3008 */
3009  function createPriority() {
3010    if( empty( $this->priority )) return FALSE;
3011    if( empty( $this->priority['value'] ))
3012      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PRIORITY' ) : FALSE;
3013    $attributes = $this->_createParams( $this->priority['params'] );
3014    return $this->_createElement( 'PRIORITY', $attributes, $this->priority['value'] );
3015  }
3016/**
3017 * set calendar component property priority
3018 *
3019 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3020 * @since 2.4.8 - 2008-11-04
3021 * @param int $value
3022 * @param array $params optional
3023 * @return bool
3024 */
3025  function setPriority( $value, $params=FALSE  ) {
3026    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3027    $this->priority = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3028    return TRUE;
3029  }
3030/*********************************************************************************/
3031/**
3032 * Property Name: RDATE
3033 */
3034/**
3035 * creates formatted output for calendar component property rdate
3036 *
3037 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3038 * @since 2.4.16 - 2008-10-26
3039 * @return string
3040 */
3041  function createRdate() {
3042    if( empty( $this->rdate )) return FALSE;
3043    $utctime = ( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
3044    $output = null;
3045    if( $utctime  )
3046      unset( $this->rdate['params']['TZID'] );
3047    foreach( $this->rdate as $theRdate ) {
3048      if( empty( $theRdate['value'] )) {
3049        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' );
3050        continue;
3051      }
3052      if( $utctime  )
3053        unset( $theRdate['params']['TZID'] );
3054      $attributes = $this->_createParams( $theRdate['params'] );
3055      $cnt = count( $theRdate['value'] );
3056      $content = null;
3057      $rno = 1;
3058      foreach( $theRdate['value'] as $rpix => $rdatePart ) {
3059        $contentPart = null;
3060        if( is_array( $rdatePart ) &&
3061            isset( $theRdate['params']['VALUE'] ) && ( 'PERIOD' == $theRdate['params']['VALUE'] )) { // PERIOD
3062          if( $utctime )
3063            unset( $rdatePart[0]['tz'] );
3064          $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart[0]); // PERIOD part 1
3065          if( $utctime || !empty( $theRdate['params']['TZID'] ))
3066            $formatted = str_replace( 'Z', '', $formatted);
3067          if( 0 < $rpix ) {
3068            if( !empty( $rdatePart[0]['tz'] ) && iCalUtilityFunctions::_isOffset( $rdatePart[0]['tz'] )) {
3069              if( 'Z' != substr( $formatted, -1 )) $formatted .= 'Z';
3070            }
3071            else
3072              $formatted = str_replace( 'Z', '', $formatted );
3073          }
3074          $contentPart .= $formatted;
3075          $contentPart .= '/';
3076          $cnt2 = count( $rdatePart[1]);
3077          if( array_key_exists( 'year', $rdatePart[1] )) {
3078            if( array_key_exists( 'hour', $rdatePart[1] ))
3079              $cnt2 = 7;                                      // date-time
3080            else
3081              $cnt2 = 3;                                      // date
3082          }
3083          elseif( array_key_exists( 'week', $rdatePart[1] ))  // duration
3084            $cnt2 = 5;
3085          if(( 7 == $cnt2 )   &&    // period=  -> date-time
3086              isset( $rdatePart[1]['year'] )  &&
3087              isset( $rdatePart[1]['month'] ) &&
3088              isset( $rdatePart[1]['day'] )) {
3089            if( $utctime )
3090              unset( $rdatePart[1]['tz'] );
3091            $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart[1] ); // PERIOD part 2
3092            if( $utctime || !empty( $theRdate['params']['TZID'] ))
3093              $formatted = str_replace( 'Z', '', $formatted);
3094            if( !empty( $rdatePart[0]['tz'] ) && iCalUtilityFunctions::_isOffset( $rdatePart[0]['tz'] )) {
3095              if( 'Z' != substr( $formatted, -1 )) $formatted .= 'Z';
3096            }
3097            else
3098              $formatted = str_replace( 'Z', '', $formatted );
3099           $contentPart .= $formatted;
3100          }
3101          else {                                  // period=  -> dur-time
3102            $contentPart .= iCalUtilityFunctions::_format_duration( $rdatePart[1] );
3103          }
3104        } // PERIOD end
3105        else { // SINGLE date start
3106          if( $utctime )
3107            unset( $rdatePart['tz'] );
3108          $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart);
3109          if( $utctime || !empty( $theRdate['params']['TZID'] ))
3110            $formatted = str_replace( 'Z', '', $formatted);
3111          if( !$utctime && ( 0 < $rpix )) {
3112            if( !empty( $theRdate['value'][0]['tz'] ) && iCalUtilityFunctions::_isOffset( $theRdate['value'][0]['tz'] )) {
3113              if( 'Z' != substr( $formatted, -1 ))
3114                $formatted .= 'Z';
3115            }
3116            else
3117              $formatted = str_replace( 'Z', '', $formatted );
3118          }
3119          $contentPart .= $formatted;
3120        }
3121        $content .= $contentPart;
3122        if( $rno < $cnt )
3123          $content .= ',';
3124        ++$rno;
3125      }
3126      $output    .= $this->_createElement( 'RDATE', $attributes, $content );
3127    }
3128    return $output;
3129  }
3130/**
3131 * set calendar component property rdate
3132 *
3133 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3134 * @since 2.5.1 - 2008-11-07
3135 * @param array $rdates
3136 * @param array $params, optional
3137 * @param integer $index, optional
3138 * @return bool
3139 */
3140  function setRdate( $rdates, $params=FALSE, $index=FALSE ) {
3141    if( empty( $rdates )) {
3142      if( $this->getConfig( 'allowEmpty' )) {
3143        iCalUtilityFunctions::_setMval( $this->rdate, null, $params, FALSE, $index );
3144        return TRUE;
3145      }
3146      else
3147        return FALSE;
3148    }
3149    $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
3150    if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) {
3151      unset( $input['params']['TZID'] );
3152      $input['params']['VALUE'] = 'DATE-TIME';
3153    }
3154            /*  check if PERIOD, if not set */
3155    if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) &&
3156          isset( $rdates[0] )    && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) &&
3157          isset( $rdates[0][0] ) &&    isset( $rdates[0][1] ) && !isset( $rdates[0]['timestamp'] ) &&
3158    (( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0]['timestamp'] ) ||
3159                                      iCalUtilityFunctions::_isArrayDate( $rdates[0][0] ))) ||
3160                                    ( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] )))))  &&
3161     ( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] ))))))
3162      $input['params']['VALUE'] = 'PERIOD';
3163            /* check 1:st date, upd. $parno (opt) and save ev. timezone **/
3164    $date  = reset( $rdates );
3165    if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) // PERIOD
3166      $date  = reset( $date );
3167    iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] );
3168    if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' )))
3169      unset( $input['params']['TZID'] );
3170    iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default
3171    foreach( $rdates as $rpix => $theRdate ) {
3172      $inputa = null;
3173      if( is_array( $theRdate )) {
3174        if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD
3175          foreach( $theRdate as $rix => $rPeriod ) {
3176            if( is_array( $rPeriod )) {
3177              if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod ))      // timestamp
3178                $inputab  = ( isset( $rPeriod['tz'] )) ? iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno ) : iCalUtilityFunctions::_timestamp2date( $rPeriod, 6 );
3179              elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod ))
3180                $inputab  = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_date_time_array( $rPeriod, $parno ) : iCalUtilityFunctions::_date_time_array( $rPeriod, 6 );
3181              elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod ))))  // text-date
3182                $inputab  = iCalUtilityFunctions::_date_time_string( reset( $rPeriod ), $parno );
3183              else                                               // array format duration
3184                $inputab  = iCalUtilityFunctions::_duration_array( $rPeriod );
3185            }
3186            elseif(( 3 <= strlen( trim( $rPeriod ))) &&          // string format duration
3187                   ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) {
3188              if( 'P' != $rPeriod[0] )
3189                $rPeriod  = substr( $rPeriod, 1 );
3190              $inputab    = iCalUtilityFunctions::_duration_string( $rPeriod );
3191            }
3192            elseif( 8 <= strlen( trim( $rPeriod )))              // text date ex. 2006-08-03 10:12:18
3193              $inputab    = iCalUtilityFunctions::_date_time_string( $rPeriod, $parno );
3194            if(  isset( $input['params']['TZID'] ) ||
3195               ( isset( $inputab['tz'] )   && !iCalUtilityFunctions::_isOffset( $inputab['tz'] )) ||
3196               ( isset( $inputa[0] )       && ( !isset( $inputa[0]['tz'] )))       ||
3197               ( isset( $inputa[0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputa[0]['tz'] )))
3198              unset( $inputab['tz'] );
3199            $inputa[]     = $inputab;
3200          }
3201        } // PERIOD end
3202        elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate ))      // timestamp
3203          $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno );
3204        else                                                                    // date[-time]
3205          $inputa = iCalUtilityFunctions::_date_time_array( $theRdate, $parno );
3206      }
3207      elseif( 8 <= strlen( trim( $theRdate )))                   // text date ex. 2006-08-03 10:12:18
3208        $inputa       = iCalUtilityFunctions::_date_time_string( $theRdate, $parno );
3209      if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD
3210        if( 3 == $parno )
3211          unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] );
3212        elseif( isset( $inputa['tz'] ))
3213          $inputa['tz'] = (string) $inputa['tz'];
3214        if(  isset( $input['params']['TZID'] ) ||
3215           ( isset( $inputa['tz'] )            && !iCalUtilityFunctions::_isOffset( $inputa['tz'] ))     ||
3216           ( isset( $input['value'][0] )       && ( !isset( $input['value'][0]['tz'] )))  ||
3217           ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] )))
3218          unset( $inputa['tz'] );
3219      }
3220      $input['value'][] = $inputa;
3221    }
3222    if( 3 == $parno ) {
3223      $input['params']['VALUE'] = 'DATE';
3224      unset( $input['params']['TZID'] );
3225    }
3226    iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index );
3227    return TRUE;
3228  }
3229/*********************************************************************************/
3230/**
3231 * Property Name: RECURRENCE-ID
3232 */
3233/**
3234 * creates formatted output for calendar component property recurrence-id
3235 *
3236 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3237 * @since 2.4.8 - 2008-10-21
3238 * @return string
3239 */
3240  function createRecurrenceid() {
3241    if( empty( $this->recurrenceid )) return FALSE;
3242    if( empty( $this->recurrenceid['value'] ))
3243      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE;
3244    $formatted  = iCalUtilityFunctions::_format_date_time( $this->recurrenceid['value'] );
3245    $attributes = $this->_createParams( $this->recurrenceid['params'] );
3246    return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted );
3247  }
3248/**
3249 * set calendar component property recurrence-id
3250 *
3251 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3252 * @since 2.4.8 - 2008-10-23
3253 * @param mixed $year
3254 * @param mixed $month optional
3255 * @param int $day optional
3256 * @param int $hour optional
3257 * @param int $min optional
3258 * @param int $sec optional
3259 * @param array $params optional
3260 * @return bool
3261 */
3262  function setRecurrenceid( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
3263    if( empty( $year )) {
3264      if( $this->getConfig( 'allowEmpty' )) {
3265        $this->recurrenceid = array( 'value' => null, 'params' => null );
3266        return TRUE;
3267      }
3268      else
3269        return FALSE;
3270    }
3271    $this->recurrenceid = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params );
3272    return TRUE;
3273  }
3274/*********************************************************************************/
3275/**
3276 * Property Name: RELATED-TO
3277 */
3278/**
3279 * creates formatted output for calendar component property related-to
3280 *
3281 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3282 * @since 2.4.8 - 2008-10-23
3283 * @return string
3284 */
3285  function createRelatedTo() {
3286    if( empty( $this->relatedto )) return FALSE;
3287    $output = null;
3288    foreach( $this->relatedto as $relation ) {
3289      if( empty( $relation['value'] )) {
3290        if( $this->getConfig( 'allowEmpty' )) $output.= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ));
3291        continue;
3292      }
3293      $attributes = $this->_createParams( $relation['params'] );
3294      $content    = ( 'xcal' != $this->format ) ? '<' : '';
3295      $content   .= $this->_strrep( $relation['value'] );
3296      $content   .= ( 'xcal' != $this->format ) ? '>' : '';
3297      $output    .= $this->_createElement( 'RELATED-TO', $attributes, $content );
3298    }
3299    return $output;
3300  }
3301/**
3302 * set calendar component property related-to
3303 *
3304 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3305 * @since 2.5.1 - 2008-11-07
3306 * @param float $relid
3307 * @param array $params, optional
3308 * @param index $index, optional
3309 * @return bool
3310 */
3311  function setRelatedTo( $value, $params=FALSE, $index=FALSE ) {
3312    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3313    if(( '<' == substr( $value, 0, 1 )) && ( '>' == substr( $value, -1 )))
3314      $value = substr( $value, 1, ( strlen( $value ) - 2 ));
3315    iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default
3316    iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index );
3317    return TRUE;
3318  }
3319/*********************************************************************************/
3320/**
3321 * Property Name: REPEAT
3322 */
3323/**
3324 * creates formatted output for calendar component property repeat
3325 *
3326 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3327 * @since 2.4.8 - 2008-10-21
3328 * @return string
3329 */
3330  function createRepeat() {
3331    if( empty( $this->repeat )) return FALSE;
3332    if( empty( $this->repeat['value'] ))
3333      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'REPEAT' ) : FALSE;
3334    $attributes = $this->_createParams( $this->repeat['params'] );
3335    return $this->_createElement( 'REPEAT', $attributes, $this->repeat['value'] );
3336  }
3337/**
3338 * set calendar component property transp
3339 *
3340 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3341 * @since 2.4.8 - 2008-11-04
3342 * @param string $value
3343 * @param array $params optional
3344 * @return void
3345 */
3346  function setRepeat( $value, $params=FALSE ) {
3347    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3348    $this->repeat = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3349    return TRUE;
3350  }
3351/*********************************************************************************/
3352/**
3353 * Property Name: REQUEST-STATUS
3354 */
3355/**
3356 * creates formatted output for calendar component property request-status
3357 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3358 * @since 2.4.8 - 2008-10-23
3359 * @return string
3360 */
3361  function createRequestStatus() {
3362    if( empty( $this->requeststatus )) return FALSE;
3363    $output = null;
3364    foreach( $this->requeststatus as $rstat ) {
3365      if( empty( $rstat['value']['statcode'] )) {
3366        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' );
3367        continue;
3368      }
3369      $attributes  = $this->_createParams( $rstat['params'], array( 'LANGUAGE' ));
3370      $content     = number_format( (float) $rstat['value']['statcode'], 2, '.', '');
3371      $content    .= ';'.$this->_strrep( $rstat['value']['text'] );
3372      if( isset( $rstat['value']['extdata'] ))
3373        $content  .= ';'.$this->_strrep( $rstat['value']['extdata'] );
3374      $output     .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content );
3375    }
3376    return $output;
3377  }
3378/**
3379 * set calendar component property request-status
3380 *
3381 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3382 * @since 2.5.1 - 2008-11-05
3383 * @param float $statcode
3384 * @param string $text
3385 * @param string $extdata, optional
3386 * @param array $params, optional
3387 * @param integer $index, optional
3388 * @return bool
3389 */
3390  function setRequestStatus( $statcode, $text, $extdata=FALSE, $params=FALSE, $index=FALSE ) {
3391    if( empty( $statcode ) || empty( $text )) if( $this->getConfig( 'allowEmpty' )) $statcode = $text = null; else return FALSE;
3392    $input              = array( 'statcode' => $statcode, 'text' => $text );
3393    if( $extdata )
3394      $input['extdata'] = $extdata;
3395    iCalUtilityFunctions::_setMval( $this->requeststatus, $input, $params, FALSE, $index );
3396    return TRUE;
3397  }
3398/*********************************************************************************/
3399/**
3400 * Property Name: RESOURCES
3401 */
3402/**
3403 * creates formatted output for calendar component property resources
3404 *
3405 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3406 * @since 2.4.8 - 2008-10-23
3407 * @return string
3408 */
3409  function createResources() {
3410    if( empty( $this->resources )) return FALSE;
3411    $output = null;
3412    foreach( $this->resources as $resource ) {
3413      if( empty( $resource['value'] )) {
3414        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' );
3415        continue;
3416      }
3417      $attributes  = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' ));
3418      if( is_array( $resource['value'] )) {
3419        foreach( $resource['value'] as $rix => $resourcePart )
3420          $resource['value'][$rix] = $this->_strrep( $resourcePart );
3421        $content   = implode( ',', $resource['value'] );
3422      }
3423      else
3424        $content   = $this->_strrep( $resource['value'] );
3425      $output     .= $this->_createElement( 'RESOURCES', $attributes, $content );
3426    }
3427    return $output;
3428  }
3429/**
3430 * set calendar component property recources
3431 *
3432 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3433 * @since 2.5.1 - 2008-11-05
3434 * @param mixed $value
3435 * @param array $params, optional
3436 * @param integer $index, optional
3437 * @return bool
3438 */
3439  function setResources( $value, $params=FALSE, $index=FALSE ) {
3440    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3441    iCalUtilityFunctions::_setMval( $this->resources, $value, $params, FALSE, $index );
3442    return TRUE;
3443  }
3444/*********************************************************************************/
3445/**
3446 * Property Name: RRULE
3447 */
3448/**
3449 * creates formatted output for calendar component property rrule
3450 *
3451 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3452 * @since 2.4.8 - 2008-10-21
3453 * @return string
3454 */
3455  function createRrule() {
3456    if( empty( $this->rrule )) return FALSE;
3457    return $this->_format_recur( 'RRULE', $this->rrule );
3458  }
3459/**
3460 * set calendar component property rrule
3461 *
3462 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3463 * @since 2.5.1 - 2008-11-05
3464 * @param array $rruleset
3465 * @param array $params, optional
3466 * @param integer $index, optional
3467 * @return void
3468 */
3469  function setRrule( $rruleset, $params=FALSE, $index=FALSE ) {
3470    if( empty( $rruleset )) if( $this->getConfig( 'allowEmpty' )) $rruleset = null; else return FALSE;
3471    iCalUtilityFunctions::_setMval( $this->rrule, iCalUtilityFunctions::_setRexrule( $rruleset ), $params, FALSE, $index );
3472    return TRUE;
3473  }
3474/*********************************************************************************/
3475/**
3476 * Property Name: SEQUENCE
3477 */
3478/**
3479 * creates formatted output for calendar component property sequence
3480 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3481 * @since 2.6.9 - 2009-12-30
3482 * @return string
3483 */
3484  function createSequence() {
3485    if( empty( $this->sequence )) return FALSE;
3486    if( empty( $this->sequence['value'] ) && ( '0' != $this->sequence['value'] ))
3487      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SEQUENCE' ) : FALSE;
3488    $attributes = $this->_createParams( $this->sequence['params'] );
3489    return $this->_createElement( 'SEQUENCE', $attributes, $this->sequence['value'] );
3490  }
3491/**
3492 * set calendar component property sequence
3493 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3494 * @since 2.6.9 - 2009-12-30
3495 * @param int $value optional
3496 * @param array $params optional
3497 * @return bool
3498 */
3499  function setSequence( $value=FALSE, $params=FALSE ) {
3500    if( empty( $value ) && ( '0' != $value ))
3501      $value = ( isset( $this->sequence['value'] ) && ( 0 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : 1;
3502    $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3503    return TRUE;
3504  }
3505/*********************************************************************************/
3506/**
3507 * Property Name: STATUS
3508 */
3509/**
3510 * creates formatted output for calendar component property status
3511 *
3512 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3513 * @since 2.4.8 - 2008-10-21
3514 * @return string
3515 */
3516  function createStatus() {
3517    if( empty( $this->status )) return FALSE;
3518    if( empty( $this->status['value'] ))
3519      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'STATUS' ) : FALSE;
3520    $attributes = $this->_createParams( $this->status['params'] );
3521    return $this->_createElement( 'STATUS', $attributes, $this->status['value'] );
3522  }
3523/**
3524 * set calendar component property status
3525 *
3526 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3527 * @since 2.4.8 - 2008-11-04
3528 * @param string $value
3529 * @param array $params optional
3530 * @return bool
3531 */
3532  function setStatus( $value, $params=FALSE ) {
3533    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3534    $this->status = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3535    return TRUE;
3536  }
3537/*********************************************************************************/
3538/**
3539 * Property Name: SUMMARY
3540 */
3541/**
3542 * creates formatted output for calendar component property summary
3543 *
3544 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3545 * @since 2.4.8 - 2008-10-21
3546 * @return string
3547 */
3548  function createSummary() {
3549    if( empty( $this->summary )) return FALSE;
3550    if( empty( $this->summary['value'] ))
3551      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SUMMARY' ) : FALSE;
3552    $attributes = $this->_createParams( $this->summary['params'], array( 'ALTREP', 'LANGUAGE' ));
3553    $content    = $this->_strrep( $this->summary['value'] );
3554    return $this->_createElement( 'SUMMARY', $attributes, $content );
3555  }
3556/**
3557 * set calendar component property summary
3558 *
3559 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3560 * @since 2.4.8 - 2008-11-04
3561 * @param string $value
3562 * @param string $params optional
3563 * @return bool
3564 */
3565  function setSummary( $value, $params=FALSE ) {
3566    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3567    $this->summary = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3568    return TRUE;
3569  }
3570/*********************************************************************************/
3571/**
3572 * Property Name: TRANSP
3573 */
3574/**
3575 * creates formatted output for calendar component property transp
3576 *
3577 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3578 * @since 2.4.8 - 2008-10-21
3579 * @return string
3580 */
3581  function createTransp() {
3582    if( empty( $this->transp )) return FALSE;
3583    if( empty( $this->transp['value'] ))
3584      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRANSP' ) : FALSE;
3585    $attributes = $this->_createParams( $this->transp['params'] );
3586    return $this->_createElement( 'TRANSP', $attributes, $this->transp['value'] );
3587  }
3588/**
3589 * set calendar component property transp
3590 *
3591 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3592 * @since 2.4.8 - 2008-11-04
3593 * @param string $value
3594 * @param string $params optional
3595 * @return bool
3596 */
3597  function setTransp( $value, $params=FALSE ) {
3598    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3599    $this->transp = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3600    return TRUE;
3601  }
3602/*********************************************************************************/
3603/**
3604 * Property Name: TRIGGER
3605 */
3606/**
3607 * creates formatted output for calendar component property trigger
3608 *
3609 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3610 * @since 2.4.16 - 2008-10-21
3611 * @return string
3612 */
3613  function createTrigger() {
3614    if( empty( $this->trigger )) return FALSE;
3615    if( empty( $this->trigger['value'] ))
3616      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRIGGER' ) : FALSE;
3617    $content = $attributes = null;
3618    if( isset( $this->trigger['value']['year'] )   &&
3619        isset( $this->trigger['value']['month'] )  &&
3620        isset( $this->trigger['value']['day'] ))
3621      $content      .= iCalUtilityFunctions::_format_date_time( $this->trigger['value'] );
3622    else {
3623      if( TRUE !== $this->trigger['value']['relatedStart'] )
3624        $attributes .= $this->intAttrDelimiter.'RELATED=END';
3625      if( $this->trigger['value']['before'] )
3626        $content    .= '-';
3627      $content      .= iCalUtilityFunctions::_format_duration( $this->trigger['value'] );
3628    }
3629    $attributes     .= $this->_createParams( $this->trigger['params'] );
3630    return $this->_createElement( 'TRIGGER', $attributes, $content );
3631  }
3632/**
3633 * set calendar component property trigger
3634 *
3635 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3636 * @since 2.4.16 - 2008-11-04
3637 * @param mixed $year
3638 * @param mixed $month optional
3639 * @param int $day optional
3640 * @param int $week optional
3641 * @param int $hour optional
3642 * @param int $min optional
3643 * @param int $sec optional
3644 * @param bool $relatedStart optional
3645 * @param bool $before optional
3646 * @param array $params optional
3647 * @return bool
3648 */
3649  function setTrigger( $year, $month=null, $day=null, $week=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $relatedStart=TRUE, $before=TRUE, $params=FALSE ) {
3650    if( empty( $year ) && empty( $month ) && empty( $day ) && empty( $week ) && empty( $hour ) && empty( $min ) && empty( $sec ))
3651      if( $this->getConfig( 'allowEmpty' )) {
3652        $this->trigger = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) );
3653        return TRUE;
3654      }
3655      else
3656        return FALSE;
3657    if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp
3658      $params = iCalUtilityFunctions::_setParams( $month );
3659      $date   = iCalUtilityFunctions::_timestamp2date( $year, 7 );
3660      foreach( $date as $k => $v )
3661        $$k = $v;
3662    }
3663    elseif( is_array( $year ) && ( is_array( $month ) || empty( $month ))) {
3664      $params = iCalUtilityFunctions::_setParams( $month );
3665      if(!(array_key_exists( 'year',  $year ) &&   // exclude date-time
3666           array_key_exists( 'month', $year ) &&
3667           array_key_exists( 'day',   $year ))) {  // so this must be a duration
3668        if( isset( $params['RELATED'] ) && ( 'END' == $params['RELATED'] ))
3669          $relatedStart = FALSE;
3670        else
3671          $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ? FALSE : TRUE;
3672        $before         = ( array_key_exists( 'before', $year )       && ( TRUE !== $year['before'] ))       ? FALSE : TRUE;
3673      }
3674      $SSYY  = ( array_key_exists( 'year',  $year )) ? $year['year']  : null;
3675      $month = ( array_key_exists( 'month', $year )) ? $year['month'] : null;
3676      $day   = ( array_key_exists( 'day',   $year )) ? $year['day']   : null;
3677      $week  = ( array_key_exists( 'week',  $year )) ? $year['week']  : null;
3678      $hour  = ( array_key_exists( 'hour',  $year )) ? $year['hour']  : 0; //null;
3679      $min   = ( array_key_exists( 'min',   $year )) ? $year['min']   : 0; //null;
3680      $sec   = ( array_key_exists( 'sec',   $year )) ? $year['sec']   : 0; //null;
3681      $year  = $SSYY;
3682    }
3683    elseif(is_string( $year ) && ( is_array( $month ) || empty( $month ))) {  // duration or date in a string
3684      $params = iCalUtilityFunctions::_setParams( $month );
3685      if( in_array( $year{0}, array( 'P', '+', '-' ))) { // duration
3686        $relatedStart = ( isset( $params['RELATED'] ) && ( 'END' == $params['RELATED'] )) ? FALSE : TRUE;
3687        $before       = ( '-'  == $year{0} ) ? TRUE : FALSE;
3688        if(     'P'  != $year{0} )
3689          $year       = substr( $year, 1 );
3690        $date         = iCalUtilityFunctions::_duration_string( $year);
3691      }
3692      else   // date
3693        $date    = iCalUtilityFunctions::_date_time_string( $year, 7 );
3694      unset( $year, $month, $day );
3695      foreach( $date as $k => $v )
3696        $$k = $v;
3697    }
3698    else // single values in function input parameters
3699      $params = iCalUtilityFunctions::_setParams( $params );
3700    if( !empty( $year ) && !empty( $month ) && !empty( $day )) { // date
3701      $params['VALUE'] = 'DATE-TIME';
3702      $hour = ( $hour ) ? $hour : 0;
3703      $min  = ( $min  ) ? $min  : 0;
3704      $sec  = ( $sec  ) ? $sec  : 0;
3705      $this->trigger = array( 'params' => $params );
3706      $this->trigger['value'] = array( 'year'  => $year
3707                                     , 'month' => $month
3708                                     , 'day'   => $day
3709                                     , 'hour'  => $hour
3710                                     , 'min'   => $min
3711                                     , 'sec'   => $sec
3712                                     , 'tz'    => 'Z' );
3713      return TRUE;
3714    }
3715    elseif(( empty( $year ) && empty( $month )) &&    // duration
3716           (!empty( $week ) || !empty( $day ) || !empty( $hour ) || !empty( $min ) || !empty( $sec ))) {
3717      unset( $params['RELATED'] ); // set at output creation (END only)
3718      unset( $params['VALUE'] );   // 'DURATION' default
3719      $this->trigger = array( 'params' => $params );
3720      $relatedStart = ( FALSE !== $relatedStart ) ? TRUE : FALSE;
3721      $before       = ( FALSE !== $before )       ? TRUE : FALSE;
3722      $this->trigger['value']  = array( 'relatedStart' => $relatedStart
3723                                      , 'before'       => $before );
3724      if( !empty( $week )) $this->trigger['value']['week'] = $week;
3725      if( !empty( $day  )) $this->trigger['value']['day']  = $day;
3726      if( !empty( $hour )) $this->trigger['value']['hour'] = $hour;
3727      if( !empty( $min  )) $this->trigger['value']['min']  = $min;
3728      if( !empty( $sec  )) $this->trigger['value']['sec']  = $sec;
3729      return TRUE;
3730    }
3731    return FALSE;
3732  }
3733/*********************************************************************************/
3734/**
3735 * Property Name: TZID
3736 */
3737/**
3738 * creates formatted output for calendar component property tzid
3739 *
3740 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3741 * @since 2.4.8 - 2008-10-21
3742 * @return string
3743 */
3744  function createTzid() {
3745    if( empty( $this->tzid )) return FALSE;
3746    if( empty( $this->tzid['value'] ))
3747      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZID' ) : FALSE;
3748    $attributes = $this->_createParams( $this->tzid['params'] );
3749    return $this->_createElement( 'TZID', $attributes, $this->_strrep( $this->tzid['value'] ));
3750  }
3751/**
3752 * set calendar component property tzid
3753 *
3754 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3755 * @since 2.4.8 - 2008-11-04
3756 * @param string $value
3757 * @param array $params optional
3758 * @return bool
3759 */
3760  function setTzid( $value, $params=FALSE ) {
3761    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3762    $this->tzid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3763    return TRUE;
3764  }
3765/*********************************************************************************/
3766/**
3767 * .. .
3768 * Property Name: TZNAME
3769 */
3770/**
3771 * creates formatted output for calendar component property tzname
3772 *
3773 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3774 * @since 2.4.8 - 2008-10-21
3775 * @return string
3776 */
3777  function createTzname() {
3778    if( empty( $this->tzname )) return FALSE;
3779    $output = null;
3780    foreach( $this->tzname as $theName ) {
3781      if( !empty( $theName['value'] )) {
3782        $attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' ));
3783        $output    .= $this->_createElement( 'TZNAME', $attributes, $this->_strrep( $theName['value'] ));
3784      }
3785      elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' );
3786    }
3787    return $output;
3788  }
3789/**
3790 * set calendar component property tzname
3791 *
3792 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3793 * @since 2.5.1 - 2008-11-05
3794 * @param string $value
3795 * @param string $params, optional
3796 * @param integer $index, optional
3797 * @return bool
3798 */
3799  function setTzname( $value, $params=FALSE, $index=FALSE ) {
3800    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3801    iCalUtilityFunctions::_setMval( $this->tzname, $value, $params, FALSE, $index );
3802    return TRUE;
3803  }
3804/*********************************************************************************/
3805/**
3806 * Property Name: TZOFFSETFROM
3807 */
3808/**
3809 * creates formatted output for calendar component property tzoffsetfrom
3810 *
3811 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3812 * @since 2.4.8 - 2008-10-21
3813 * @return string
3814 */
3815  function createTzoffsetfrom() {
3816    if( empty( $this->tzoffsetfrom )) return FALSE;
3817    if( empty( $this->tzoffsetfrom['value'] ))
3818      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETFROM' ) : FALSE;
3819    $attributes = $this->_createParams( $this->tzoffsetfrom['params'] );
3820    return $this->_createElement( 'TZOFFSETFROM', $attributes, $this->tzoffsetfrom['value'] );
3821  }
3822/**
3823 * set calendar component property tzoffsetfrom
3824 *
3825 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3826 * @since 2.4.8 - 2008-11-04
3827 * @param string $value
3828 * @param string $params optional
3829 * @return bool
3830 */
3831  function setTzoffsetfrom( $value, $params=FALSE ) {
3832    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3833    $this->tzoffsetfrom = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3834    return TRUE;
3835  }
3836/*********************************************************************************/
3837/**
3838 * Property Name: TZOFFSETTO
3839 */
3840/**
3841 * creates formatted output for calendar component property tzoffsetto
3842 *
3843 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3844 * @since 2.4.8 - 2008-10-21
3845 * @return string
3846 */
3847  function createTzoffsetto() {
3848    if( empty( $this->tzoffsetto )) return FALSE;
3849    if( empty( $this->tzoffsetto['value'] ))
3850      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETTO' ) : FALSE;
3851    $attributes = $this->_createParams( $this->tzoffsetto['params'] );
3852    return $this->_createElement( 'TZOFFSETTO', $attributes, $this->tzoffsetto['value'] );
3853  }
3854/**
3855 * set calendar component property tzoffsetto
3856 *
3857 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3858 * @since 2.4.8 - 2008-11-04
3859 * @param string $value
3860 * @param string $params optional
3861 * @return bool
3862 */
3863  function setTzoffsetto( $value, $params=FALSE ) {
3864    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3865    $this->tzoffsetto = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3866    return TRUE;
3867  }
3868/*********************************************************************************/
3869/**
3870 * Property Name: TZURL
3871 */
3872/**
3873 * creates formatted output for calendar component property tzurl
3874 *
3875 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3876 * @since 2.4.8 - 2008-10-21
3877 * @return string
3878 */
3879  function createTzurl() {
3880    if( empty( $this->tzurl )) return FALSE;
3881    if( empty( $this->tzurl['value'] ))
3882      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZURL' ) : FALSE;
3883    $attributes = $this->_createParams( $this->tzurl['params'] );
3884    return $this->_createElement( 'TZURL', $attributes, $this->tzurl['value'] );
3885  }
3886/**
3887 * set calendar component property tzurl
3888 *
3889 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3890 * @since 2.4.8 - 2008-11-04
3891 * @param string $value
3892 * @param string $params optional
3893 * @return boll
3894 */
3895  function setTzurl( $value, $params=FALSE ) {
3896    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3897    $this->tzurl = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3898    return TRUE;
3899  }
3900/*********************************************************************************/
3901/**
3902 * Property Name: UID
3903 */
3904/**
3905 * creates formatted output for calendar component property uid
3906 *
3907 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3908 * @since 0.9.7 - 2006-11-20
3909 * @return string
3910 */
3911  function createUid() {
3912    if( 0 >= count( $this->uid ))
3913      $this->_makeuid();
3914    $attributes = $this->_createParams( $this->uid['params'] );
3915    return $this->_createElement( 'UID', $attributes, $this->uid['value'] );
3916  }
3917/**
3918 * create an unique id for this calendar component object instance
3919 *
3920 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3921 * @since 2.2.7 - 2007-09-04
3922 * @return void
3923 */
3924  function _makeUid() {
3925    $date   = date('Ymd\THisT');
3926    $unique = substr(microtime(), 2, 4);
3927    $base   = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890';
3928    $start  = 0;
3929    $end    = strlen( $base ) - 1;
3930    $length = 6;
3931    $str    = null;
3932    for( $p = 0; $p < $length; ++$p )
3933      $unique .= $base{mt_rand( $start, $end )};
3934    $this->uid = array( 'params' => null );
3935   
3936    /*
3937     * Modificado por Cristiano Correa Schmidt
3938     */
3939        //$this->uid['value']  = $date.'-'.$unique.'@'.$this->getConfig( 'unique_id' );
3940    $this->uid['value']  = $this->getConfig( 'unique_id' );
3941  }
3942/**
3943 * set calendar component property uid
3944 *
3945 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3946 * @since 2.4.8 - 2008-11-04
3947 * @param string $value
3948 * @param string $params optional
3949 * @return bool
3950 */
3951  function setUid( $value, $params=FALSE ) {
3952    if( empty( $value )) return FALSE; // no allowEmpty check here !!!!
3953    $this->uid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3954    return TRUE;
3955  }
3956/*********************************************************************************/
3957/**
3958 * Property Name: URL
3959 */
3960/**
3961 * creates formatted output for calendar component property url
3962 *
3963 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3964 * @since 2.4.8 - 2008-10-21
3965 * @return string
3966 */
3967  function createUrl() {
3968    if( empty( $this->url )) return FALSE;
3969    if( empty( $this->url['value'] ))
3970      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'URL' ) : FALSE;
3971    $attributes = $this->_createParams( $this->url['params'] );
3972    return $this->_createElement( 'URL', $attributes, $this->url['value'] );
3973  }
3974/**
3975 * set calendar component property url
3976 *
3977 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3978 * @since 2.4.8 - 2008-11-04
3979 * @param string $value
3980 * @param string $params optional
3981 * @return bool
3982 */
3983  function setUrl( $value, $params=FALSE ) {
3984    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3985    $this->url = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3986    return TRUE;
3987  }
3988/*********************************************************************************/
3989/**
3990 * Property Name: x-prop
3991 */
3992/**
3993 * creates formatted output for calendar component property x-prop
3994 *
3995 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
3996 * @since 2.4.11 - 2008-10-22
3997 * @return string
3998 */
3999  function createXprop() {
4000    if( empty( $this->xprop )) return FALSE;
4001    $output = null;
4002    foreach( $this->xprop as $label => $xpropPart ) {
4003      if( empty( $xpropPart['value'] )) {
4004        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $label );
4005        continue;
4006      }
4007      $attributes = $this->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
4008      if( is_array( $xpropPart['value'] )) {
4009        foreach( $xpropPart['value'] as $pix => $theXpart )
4010          $xpropPart['value'][$pix] = $this->_strrep( $theXpart );
4011        $xpropPart['value']  = implode( ',', $xpropPart['value'] );
4012      }
4013      else
4014        $xpropPart['value'] = $this->_strrep( $xpropPart['value'] );
4015      $output    .= $this->_createElement( $label, $attributes, $xpropPart['value'] );
4016    }
4017    return $output;
4018  }
4019/**
4020 * set calendar component property x-prop
4021 *
4022 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4023 * @since 2.6.22 - 2010-09-25
4024 * @param string $label
4025 * @param mixed $value
4026 * @param array $params optional
4027 * @return bool
4028 */
4029  function setXprop( $label, $value, $params=FALSE ) {
4030    if( empty( $label )) return;
4031    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4032    $xprop           = array( 'value' => $value );
4033    $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
4034    if( !is_array( $this->xprop )) $this->xprop = array();
4035    $this->xprop[strtoupper( $label )] = $xprop;
4036    return TRUE;
4037  }
4038/*********************************************************************************/
4039/*********************************************************************************/
4040/**
4041 * create element format parts
4042 *
4043 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4044 * @since 2.0.6 - 2006-06-20
4045 * @return string
4046 */
4047  function _createFormat() {
4048    $objectname                   = null;
4049    switch( $this->format ) {
4050      case 'xcal':
4051        $objectname               = ( isset( $this->timezonetype )) ?
4052                                 strtolower( $this->timezonetype )  :  strtolower( $this->objName );
4053        $this->componentStart1    = $this->elementStart1 = '<';
4054        $this->componentStart2    = $this->elementStart2 = '>';
4055        $this->componentEnd1      = $this->elementEnd1   = '</';
4056        $this->componentEnd2      = $this->elementEnd2   = '>'.$this->nl;
4057        $this->intAttrDelimiter   = '<!-- -->';
4058        $this->attributeDelimiter = $this->nl;
4059        $this->valueInit          = null;
4060        break;
4061      default:
4062        $objectname               = ( isset( $this->timezonetype )) ?
4063                                 strtoupper( $this->timezonetype )  :  strtoupper( $this->objName );
4064        $this->componentStart1    = 'BEGIN:';
4065        $this->componentStart2    = null;
4066        $this->componentEnd1      = 'END:';
4067        $this->componentEnd2      = $this->nl;
4068        $this->elementStart1      = null;
4069        $this->elementStart2      = null;
4070        $this->elementEnd1        = null;
4071        $this->elementEnd2        = $this->nl;
4072        $this->intAttrDelimiter   = '<!-- -->';
4073        $this->attributeDelimiter = ';';
4074        $this->valueInit          = ':';
4075        break;
4076    }
4077    return $objectname;
4078  }
4079/**
4080 * creates formatted output for calendar component property
4081 *
4082 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4083 * @since 2.6.22 - 2010-12-06
4084 * @param string $label property name
4085 * @param string $attributes property attributes
4086 * @param string $content property content (optional)
4087 * @return string
4088 */
4089  function _createElement( $label, $attributes=null, $content=FALSE ) {
4090    switch( $this->format ) {
4091      case 'xcal':
4092        $label = strtolower( $label );
4093        break;
4094      default:
4095        $label = strtoupper( $label );
4096        break;
4097    }
4098    $output = $this->elementStart1.$label;
4099    $categoriesAttrLang = null;
4100    $attachInlineBinary = FALSE;
4101    $attachfmttype      = null;
4102    if( !empty( $attributes ))  {
4103      $attributes  = trim( $attributes );
4104      if ( 'xcal' == $this->format) {
4105        $attributes2 = explode( $this->intAttrDelimiter, $attributes );
4106        $attributes  = null;
4107        foreach( $attributes2 as $attribute ) {
4108          $attrKVarr = explode( '=', $attribute );
4109          if( empty( $attrKVarr[0] ))
4110            continue;
4111          if( !isset( $attrKVarr[1] )) {
4112            $attrValue = $attrKVarr[0];
4113            $attrKey   = null;
4114          }
4115          elseif( 2 == count( $attrKVarr)) {
4116            $attrKey   = strtolower( $attrKVarr[0] );
4117            $attrValue = $attrKVarr[1];
4118          }
4119          else {
4120            $attrKey   = strtolower( $attrKVarr[0] );
4121            unset( $attrKVarr[0] );
4122            $attrValue = implode( '=', $attrKVarr );
4123          }
4124          if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) {
4125            $attachInlineBinary = TRUE;
4126            if( 'fmttype' == $attrKey )
4127              $attachfmttype = $attrKey.'='.$attrValue;
4128            continue;
4129          }
4130          elseif(( 'categories' == $label ) && ( 'language' == $attrKey ))
4131            $categoriesAttrLang = $attrKey.'='.$attrValue;
4132          else {
4133            $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
4134            $attributes .= ( !empty( $attrKey )) ? $attrKey.'=' : null;
4135            if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, -1 ))) {
4136              $attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 ));
4137              $attrValue = str_replace( '"', '', $attrValue );
4138            }
4139            $attributes .= '"'.htmlspecialchars( $attrValue ).'"';
4140          }
4141        }
4142      }
4143      else {
4144        $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes );
4145      }
4146    }
4147    if(((( 'attach' == $label ) && !$attachInlineBinary ) ||
4148         ( in_array( $label, array( 'tzurl', 'url' ))))      && ( 'xcal' == $this->format)) {
4149      $pos = strrpos($content, "/");
4150      $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content;
4151      $this->xcaldecl[] = array( 'xmldecl'  => 'ENTITY'
4152                               , 'uri'      => $docname
4153                               , 'ref'      => 'SYSTEM'
4154                               , 'external' => $content
4155                               , 'type'     => 'NDATA'
4156                               , 'type2'    => 'BINERY' );
4157      $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
4158      $attributes .= 'uri="'.$docname.'"';
4159      $content = null;
4160      if( 'attach' == $label ) {
4161        $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes );
4162        $content = $this->_createElement( 'extref', $attributes, null );
4163        $attributes = null;
4164      }
4165    }
4166    elseif(( 'attach' == $label ) && $attachInlineBinary && ( 'xcal' == $this->format)) {
4167      $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute
4168    }
4169    $output .= $attributes;
4170    if( !$content && ( '0' != $content )) {
4171      switch( $this->format ) {
4172        case 'xcal':
4173          $output .= ' /';
4174          $output .= $this->elementStart2;
4175          return $output;
4176          break;
4177        default:
4178          $output .= $this->elementStart2.$this->valueInit;
4179          return $this->_size75( $output );
4180          break;
4181      }
4182    }
4183    $output .= $this->elementStart2;
4184    $output .= $this->valueInit.$content;
4185    switch( $this->format ) {
4186      case 'xcal':
4187        return $output.$this->elementEnd1.$label.$this->elementEnd2;
4188        break;
4189      default:
4190        return $this->_size75( $output );
4191        break;
4192    }
4193  }
4194/**
4195 * creates formatted output for calendar component property parameters
4196 *
4197 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4198 * @since 2.6.33 - 2010-12-18
4199 * @param array $params  optional
4200 * @param array $ctrKeys optional
4201 * @return string
4202 */
4203  function _createParams( $params=array(), $ctrKeys=array() ) {
4204    if( !is_array( $params ) || empty( $params ))
4205      $params = array();
4206    $attrLANG = $attr1 = $attr2 = $lang = null;
4207    $CNattrKey   = ( in_array( 'CN',       $ctrKeys )) ? TRUE : FALSE ;
4208    $LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ;
4209    $CNattrExist = $LANGattrExist = FALSE;
4210    $xparams = array();
4211    foreach( $params as $paramKey => $paramValue ) {
4212      if( ctype_digit( (string) $paramKey )) {
4213        $xparams[]          = $paramValue;
4214        continue;
4215      }
4216      $paramKey             = strtoupper( $paramKey );
4217      if( !in_array( $paramKey, array( 'ALTREP', 'CN', 'DIR', 'ENCODING', 'FMTTYPE', 'LANGUAGE', 'RANGE', 'RELTYPE', 'SENT-BY', 'TZID', 'VALUE' )))
4218        $xparams[$paramKey] = $paramValue;
4219      else
4220        $params[$paramKey]  = $paramValue;
4221    }
4222    ksort( $xparams, SORT_STRING );
4223    foreach( $xparams as $paramKey => $paramValue ) {
4224      if( ctype_digit( (string) $paramKey ))
4225        $attr2             .= $this->intAttrDelimiter.$paramValue;
4226      else
4227        $attr2             .= $this->intAttrDelimiter."$paramKey=$paramValue";
4228    }
4229    if( isset( $params['FMTTYPE'] )  && !in_array( 'FMTTYPE', $ctrKeys )) {
4230      $attr1               .= $this->intAttrDelimiter.'FMTTYPE='.$params['FMTTYPE'].$attr2;
4231      $attr2                = null;
4232    }
4233    if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING',   $ctrKeys )) {
4234      if( !empty( $attr2 )) {
4235        $attr1             .= $attr2;
4236        $attr2              = null;
4237      }
4238      $attr1               .= $this->intAttrDelimiter.'ENCODING='.$params['ENCODING'];
4239    }
4240    if( isset( $params['VALUE'] )    && !in_array( 'VALUE',   $ctrKeys ))
4241      $attr1               .= $this->intAttrDelimiter.'VALUE='.$params['VALUE'];
4242    if( isset( $params['TZID'] )     && !in_array( 'TZID',    $ctrKeys ))
4243      $attr1               .= $this->intAttrDelimiter.'TZID='.$params['TZID'];
4244    if( isset( $params['RANGE'] )    && !in_array( 'RANGE',   $ctrKeys ))
4245      $attr1               .= $this->intAttrDelimiter.'RANGE='.$params['RANGE'];
4246    if( isset( $params['RELTYPE'] )  && !in_array( 'RELTYPE', $ctrKeys ))
4247      $attr1               .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE'];
4248    if( isset( $params['CN'] )       && $CNattrKey ) {
4249      $attr1                = $this->intAttrDelimiter.'CN="'.$params['CN'].'"';
4250      $CNattrExist          = TRUE;
4251    }
4252    if( isset( $params['DIR'] )      && in_array( 'DIR',      $ctrKeys ))
4253      $attr1               .= $this->intAttrDelimiter.'DIR="'.$params['DIR'].'"';
4254    if( isset( $params['SENT-BY'] )  && in_array( 'SENT-BY',  $ctrKeys ))
4255      $attr1               .= $this->intAttrDelimiter.'SENT-BY="'.$params['SENT-BY'].'"';
4256    if( isset( $params['ALTREP'] )   && in_array( 'ALTREP',   $ctrKeys ))
4257      $attr1               .= $this->intAttrDelimiter.'ALTREP="'.$params['ALTREP'].'"';
4258    if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) {
4259      $attrLANG            .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE'];
4260      $LANGattrExist        = TRUE;
4261    }
4262    if( !$LANGattrExist ) {
4263      $lang = $this->getConfig( 'language' );
4264      if(( $CNattrExist || $LANGattrKey ) && $lang )
4265        $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$lang;
4266    }
4267    return $attr1.$attrLANG.$attr2;
4268  }
4269/**
4270 * creates formatted output for calendar component property data value type recur
4271 *
4272 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4273 * @since 2.4.8 - 2008-10-22
4274 * @param array $recurlabel
4275 * @param array $recurdata
4276 * @return string
4277 */
4278  function _format_recur( $recurlabel, $recurdata ) {
4279    $output = null;
4280    foreach( $recurdata as $therule ) {
4281      if( empty( $therule['value'] )) {
4282        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel );
4283        continue;
4284      }
4285      $attributes = ( isset( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null;
4286      $content1  = $content2  = null;
4287      foreach( $therule['value'] as $rulelabel => $rulevalue ) {
4288        switch( $rulelabel ) {
4289          case 'FREQ': {
4290            $content1 .= "FREQ=$rulevalue";
4291            break;
4292          }
4293          case 'UNTIL': {
4294            $content2 .= ";UNTIL=";
4295            $content2 .= iCalUtilityFunctions::_format_date_time( $rulevalue );
4296            break;
4297          }
4298          case 'COUNT':
4299          case 'INTERVAL':
4300          case 'WKST': {
4301            $content2 .= ";$rulelabel=$rulevalue";
4302            break;
4303          }
4304          case 'BYSECOND':
4305          case 'BYMINUTE':
4306          case 'BYHOUR':
4307          case 'BYMONTHDAY':
4308          case 'BYYEARDAY':
4309          case 'BYWEEKNO':
4310          case 'BYMONTH':
4311          case 'BYSETPOS': {
4312            $content2 .= ";$rulelabel=";
4313            if( is_array( $rulevalue )) {
4314              foreach( $rulevalue as $vix => $valuePart ) {
4315                $content2 .= ( $vix ) ? ',' : null;
4316                $content2 .= $valuePart;
4317              }
4318            }
4319            else
4320             $content2 .= $rulevalue;
4321            break;
4322          }
4323          case 'BYDAY': {
4324            $content2 .= ";$rulelabel=";
4325            $bydaycnt = 0;
4326            foreach( $rulevalue as $vix => $valuePart ) {
4327              $content21 = $content22 = null;
4328              if( is_array( $valuePart )) {
4329                $content2 .= ( $bydaycnt ) ? ',' : null;
4330                foreach( $valuePart as $vix2 => $valuePart2 ) {
4331                  if( 'DAY' != strtoupper( $vix2 ))
4332                      $content21 .= $valuePart2;
4333                  else
4334                    $content22 .= $valuePart2;
4335                }
4336                $content2 .= $content21.$content22;
4337                ++$bydaycnt;
4338              }
4339              else {
4340                $content2 .= ( $bydaycnt ) ? ',' : null;
4341                if( 'DAY' != strtoupper( $vix ))
4342                    $content21 .= $valuePart;
4343                else {
4344                  $content22 .= $valuePart;
4345                  ++$bydaycnt;
4346                }
4347                $content2 .= $content21.$content22;
4348              }
4349            }
4350            break;
4351          }
4352          default: {
4353            $content2 .= ";$rulelabel=$rulevalue";
4354            break;
4355          }
4356        }
4357      }
4358      $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 );
4359    }
4360    return $output;
4361  }
4362/**
4363 * check if property not exists within component
4364 *
4365 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4366 * @since 2.5.1 - 2008-10-15
4367 * @param string $propName
4368 * @return bool
4369 */
4370  function _notExistProp( $propName ) {
4371    if( empty( $propName )) return FALSE; // when deleting x-prop, an empty propName may be used=allowed
4372    $propName = strtolower( $propName );
4373    if(     'last-modified'    == $propName )  { if( !isset( $this->lastmodified ))    return TRUE; }
4374    elseif( 'percent-complete' == $propName )  { if( !isset( $this->percentcomplete )) return TRUE; }
4375    elseif( 'recurrence-id'    == $propName )  { if( !isset( $this->recurrenceid ))    return TRUE; }
4376    elseif( 'related-to'       == $propName )  { if( !isset( $this->relatedto ))       return TRUE; }
4377    elseif( 'request-status'   == $propName )  { if( !isset( $this->requeststatus ))   return TRUE; }
4378    elseif((       'x-' != substr($propName,0,2)) && !isset( $this->$propName ))       return TRUE;
4379    return FALSE;
4380  }
4381/*********************************************************************************/
4382/*********************************************************************************/
4383/**
4384 * get general component config variables or info about subcomponents
4385 *
4386 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4387 * @since 2.6.27 - 2010-11-27
4388 * @param mixed $config
4389 * @return value
4390 */
4391  function getConfig( $config = FALSE) {
4392    if( !$config ) {
4393      $return = array();
4394      $return['ALLOWEMPTY']  = $this->getConfig( 'ALLOWEMPTY' );
4395      $return['FORMAT']      = $this->getConfig( 'FORMAT' );
4396      if( FALSE !== ( $lang  = $this->getConfig( 'LANGUAGE' )))
4397        $return['LANGUAGE']  = $lang;
4398      $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
4399      $return['UNIQUE_ID']   = $this->getConfig( 'UNIQUE_ID' );
4400      return $return;
4401    }
4402    switch( strtoupper( $config )) {
4403      case 'ALLOWEMPTY':
4404        return $this->allowEmpty;
4405        break;
4406      case 'COMPSINFO':
4407        unset( $this->compix );
4408        $info = array();
4409        if( isset( $this->components )) {
4410          foreach( $this->components as $cix => $component ) {
4411            if( empty( $component )) continue;
4412            unset( $component->propix );
4413            $info[$cix]['ordno'] = $cix + 1;
4414            $info[$cix]['type']  = $component->objName;
4415            $info[$cix]['uid']   = $component->getProperty( 'uid' );
4416            $info[$cix]['props'] = $component->getConfig( 'propinfo' );
4417            $info[$cix]['sub']   = $component->getConfig( 'compsinfo' );
4418            unset( $component->propix );
4419          }
4420        }
4421        return $info;
4422        break;
4423      case 'FORMAT':
4424        return $this->format;
4425        break;
4426      case 'LANGUAGE':
4427         // get language for calendar component as defined in [RFC 1766]
4428        return $this->language;
4429        break;
4430      case 'NL':
4431      case 'NEWLINECHAR':
4432        return $this->nl;
4433        break;
4434      case 'PROPINFO':
4435        $output = array();
4436        if( !in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
4437          if( empty( $this->uid['value'] )) $this->_makeuid();
4438                                              $output['UID']              = 1;
4439        }
4440        if( !empty( $this->dtstamp ))         $output['DTSTAMP']          = 1;
4441        if( !empty( $this->summary ))         $output['SUMMARY']          = 1;
4442        if( !empty( $this->description ))     $output['DESCRIPTION']      = count( $this->description );
4443        if( !empty( $this->dtstart ))         $output['DTSTART']          = 1;
4444        if( !empty( $this->dtend ))           $output['DTEND']            = 1;
4445        if( !empty( $this->due ))             $output['DUE']              = 1;
4446        if( !empty( $this->duration ))        $output['DURATION']         = 1;
4447        if( !empty( $this->rrule ))           $output['RRULE']            = count( $this->rrule );
4448        if( !empty( $this->rdate ))           $output['RDATE']            = count( $this->rdate );
4449        if( !empty( $this->exdate ))          $output['EXDATE']           = count( $this->exdate );
4450        if( !empty( $this->exrule ))          $output['EXRULE']           = count( $this->exrule );
4451        if( !empty( $this->action ))          $output['ACTION']           = 1;
4452        if( !empty( $this->attach ))          $output['ATTACH']           = count( $this->attach );
4453        if( !empty( $this->attendee ))        $output['ATTENDEE']         = count( $this->attendee );
4454        if( !empty( $this->categories ))      $output['CATEGORIES']       = count( $this->categories );
4455        if( !empty( $this->class ))           $output['CLASS']            = 1;
4456        if( !empty( $this->comment ))         $output['COMMENT']          = count( $this->comment );
4457        if( !empty( $this->completed ))       $output['COMPLETED']        = 1;
4458        if( !empty( $this->contact ))         $output['CONTACT']          = count( $this->contact );
4459        if( !empty( $this->created ))         $output['CREATED']          = 1;
4460        if( !empty( $this->freebusy ))        $output['FREEBUSY']         = count( $this->freebusy );
4461        if( !empty( $this->geo ))             $output['GEO']              = 1;
4462        if( !empty( $this->lastmodified ))    $output['LAST-MODIFIED']    = 1;
4463        if( !empty( $this->location ))        $output['LOCATION']         = 1;
4464        if( !empty( $this->organizer ))       $output['ORGANIZER']        = 1;
4465        if( !empty( $this->percentcomplete )) $output['PERCENT-COMPLETE'] = 1;
4466        if( !empty( $this->priority ))        $output['PRIORITY']         = 1;
4467        if( !empty( $this->recurrenceid ))    $output['RECURRENCE-ID']    = 1;
4468        if( !empty( $this->relatedto ))       $output['RELATED-TO']       = count( $this->relatedto );
4469        if( !empty( $this->repeat ))          $output['REPEAT']           = 1;
4470        if( !empty( $this->requeststatus ))   $output['REQUEST-STATUS']   = count( $this->requeststatus );
4471        if( !empty( $this->resources ))       $output['RESOURCES']        = count( $this->resources );
4472        if( !empty( $this->sequence ))        $output['SEQUENCE']         = 1;
4473        if( !empty( $this->sequence ))        $output['SEQUENCE']         = 1;
4474        if( !empty( $this->status ))          $output['STATUS']           = 1;
4475        if( !empty( $this->transp ))          $output['TRANSP']           = 1;
4476        if( !empty( $this->trigger ))         $output['TRIGGER']          = 1;
4477        if( !empty( $this->tzid ))            $output['TZID']             = 1;
4478        if( !empty( $this->tzname ))          $output['TZNAME']           = count( $this->tzname );
4479        if( !empty( $this->tzoffsetfrom ))    $output['TZOFFSETFROM']     = 1;
4480        if( !empty( $this->tzoffsetto ))      $output['TZOFFSETTO']       = 1;
4481        if( !empty( $this->tzurl ))           $output['TZURL']            = 1;
4482        if( !empty( $this->url ))             $output['URL']              = 1;
4483        if( !empty( $this->xprop ))           $output['X-PROP']           = count( $this->xprop );
4484        return $output;
4485        break;
4486      case 'UNIQUE_ID':
4487        if( empty( $this->unique_id ))
4488          $this->unique_id  = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
4489        return $this->unique_id;
4490        break;
4491    }
4492  }
4493/**
4494 * general component config setting
4495 *
4496 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4497 * @since 2.6.27 - 2010-12-12
4498 * @param mixed  $config
4499 * @param string $value
4500 * @param bool   $softUpdate
4501 * @return void
4502 */
4503  function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) {
4504    if( is_array( $config )) {
4505      foreach( $config as $cKey => $cValue ) {
4506        if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate ))
4507          return FALSE;
4508      }
4509      return TRUE;
4510    }
4511    $res = FALSE;
4512    switch( strtoupper( $config )) {
4513      case 'ALLOWEMPTY':
4514        $this->allowEmpty = $value;
4515        $subcfg = array( 'ALLOWEMPTY' => $value );
4516        $res    = TRUE;
4517        break;
4518      case 'FORMAT':
4519        $value  = trim( strtolower( $value ));
4520        $this->format = $value;
4521        $this->_createFormat();
4522        $subcfg = array( 'FORMAT' => $value );
4523        $res    = TRUE;
4524        break;
4525      case 'LANGUAGE':
4526         // set language for calendar component as defined in [RFC 1766]
4527        $value  = trim( $value );
4528        if( empty( $this->language ) || !$softUpdate )
4529          $this->language = $value;
4530        $subcfg = array( 'LANGUAGE' => $value );
4531        $res    = TRUE;
4532        break;
4533      case 'NL':
4534      case 'NEWLINECHAR':
4535        $this->nl = $value;
4536        $subcfg = array( 'NL' => $value );
4537        $res    = TRUE;
4538        break;
4539      case 'UNIQUE_ID':
4540        $value  = trim( $value );
4541        $this->unique_id = $value;
4542        $subcfg = array( 'UNIQUE_ID' => $value );
4543        $res    = TRUE;
4544        break;
4545      default:  // any unvalid config key.. .
4546        return TRUE;
4547    }
4548    if( !$res ) return FALSE;
4549    if( isset( $subcfg ) && !empty( $this->components )) {
4550      foreach( $subcfg as $cfgkey => $cfgvalue ) {
4551        foreach( $this->components as $cix => $component ) {
4552          $res = $component->setConfig( $cfgkey, $cfgvalue, $softUpdate );
4553          if( !$res )
4554            break 2;
4555          $this->components[$cix] = $component->copy(); // PHP4 compliant
4556        }
4557      }
4558    }
4559    return $res;
4560  }
4561/*********************************************************************************/
4562/**
4563 * delete component property value
4564 *
4565 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4566 * @since 2.6.4 - 2011-01-05
4567 * @param mixed $propName, bool FALSE => X-property
4568 * @param int   $propix, optional, if specific property is wanted in case of multiply occurences
4569 * @return bool, if successfull delete TRUE
4570 */
4571  function deleteProperty( $propName=FALSE, $propix=FALSE ) {
4572    if( $this->_notExistProp( $propName )) return FALSE;
4573    $propName = strtoupper( $propName );
4574    if( in_array( $propName, array( 'ATTACH',   'ATTENDEE', 'CATEGORIES', 'COMMENT',   'CONTACT', 'DESCRIPTION',    'EXDATE', 'EXRULE',
4575                                    'FREEBUSY', 'RDATE',    'RELATED-TO', 'RESOURCES', 'RRULE',   'REQUEST-STATUS', 'TZNAME', 'X-PROP'  ))) {
4576      if( !$propix )
4577        $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
4578      $this->propdelix[$propName] = --$propix;
4579    }
4580    $return = FALSE;
4581    switch( $propName ) {
4582      case 'ACTION':
4583        if( !empty( $this->action )) {
4584          $this->action = '';
4585          $return = TRUE;
4586        }
4587        break;
4588      case 'ATTACH':
4589        return $this->deletePropertyM( $this->attach, $propix );
4590        break;
4591      case 'ATTENDEE':
4592        return $this->deletePropertyM( $this->attendee, $propix );
4593        break;
4594      case 'CATEGORIES':
4595        return $this->deletePropertyM( $this->categories, $propix );
4596        break;
4597      case 'CLASS':
4598        if( !empty( $this->class )) {
4599          $this->class = '';
4600          $return = TRUE;
4601        }
4602        break;
4603      case 'COMMENT':
4604        return $this->deletePropertyM( $this->comment, $propix );
4605        break;
4606      case 'COMPLETED':
4607        if( !empty( $this->completed )) {
4608          $this->completed = '';
4609          $return = TRUE;
4610        }
4611        break;
4612      case 'CONTACT':
4613        return $this->deletePropertyM( $this->contact, $propix );
4614        break;
4615      case 'CREATED':
4616        if( !empty( $this->created )) {
4617          $this->created = '';
4618          $return = TRUE;
4619        }
4620        break;
4621      case 'DESCRIPTION':
4622        return $this->deletePropertyM( $this->description, $propix );
4623        break;
4624      case 'DTEND':
4625        if( !empty( $this->dtend )) {
4626          $this->dtend = '';
4627          $return = TRUE;
4628        }
4629        break;
4630      case 'DTSTAMP':
4631        if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
4632          return FALSE;
4633        if( !empty( $this->dtstamp )) {
4634          $this->dtstamp = '';
4635          $return = TRUE;
4636        }
4637        break;
4638      case 'DTSTART':
4639        if( !empty( $this->dtstart )) {
4640          $this->dtstart = '';
4641          $return = TRUE;
4642        }
4643        break;
4644      case 'DUE':
4645        if( !empty( $this->due )) {
4646          $this->due = '';
4647          $return = TRUE;
4648        }
4649        break;
4650      case 'DURATION':
4651        if( !empty( $this->duration )) {
4652          $this->duration = '';
4653          $return = TRUE;
4654        }
4655        break;
4656      case 'EXDATE':
4657        return $this->deletePropertyM( $this->exdate, $propix );
4658        break;
4659      case 'EXRULE':
4660        return $this->deletePropertyM( $this->exrule, $propix );
4661        break;
4662      case 'FREEBUSY':
4663        return $this->deletePropertyM( $this->freebusy, $propix );
4664        break;
4665      case 'GEO':
4666        if( !empty( $this->geo )) {
4667          $this->geo = '';
4668          $return = TRUE;
4669        }
4670        break;
4671      case 'LAST-MODIFIED':
4672        if( !empty( $this->lastmodified )) {
4673          $this->lastmodified = '';
4674          $return = TRUE;
4675        }
4676        break;
4677      case 'LOCATION':
4678        if( !empty( $this->location )) {
4679          $this->location = '';
4680          $return = TRUE;
4681        }
4682        break;
4683      case 'ORGANIZER':
4684        if( !empty( $this->organizer )) {
4685          $this->organizer = '';
4686          $return = TRUE;
4687        }
4688        break;
4689      case 'PERCENT-COMPLETE':
4690        if( !empty( $this->percentcomplete )) {
4691          $this->percentcomplete = '';
4692          $return = TRUE;
4693        }
4694        break;
4695      case 'PRIORITY':
4696        if( !empty( $this->priority )) {
4697          $this->priority = '';
4698          $return = TRUE;
4699        }
4700        break;
4701      case 'RDATE':
4702        return $this->deletePropertyM( $this->rdate, $propix );
4703        break;
4704      case 'RECURRENCE-ID':
4705        if( !empty( $this->recurrenceid )) {
4706          $this->recurrenceid = '';
4707          $return = TRUE;
4708        }
4709        break;
4710      case 'RELATED-TO':
4711        return $this->deletePropertyM( $this->relatedto, $propix );
4712        break;
4713      case 'REPEAT':
4714        if( !empty( $this->repeat )) {
4715          $this->repeat = '';
4716          $return = TRUE;
4717        }
4718        break;
4719      case 'REQUEST-STATUS':
4720        return $this->deletePropertyM( $this->requeststatus, $propix );
4721        break;
4722      case 'RESOURCES':
4723        return $this->deletePropertyM( $this->resources, $propix );
4724        break;
4725      case 'RRULE':
4726        return $this->deletePropertyM( $this->rrule, $propix );
4727        break;
4728      case 'SEQUENCE':
4729        if( !empty( $this->sequence )) {
4730          $this->sequence = '';
4731          $return = TRUE;
4732        }
4733        break;
4734      case 'STATUS':
4735        if( !empty( $this->status )) {
4736          $this->status = '';
4737          $return = TRUE;
4738        }
4739        break;
4740      case 'SUMMARY':
4741        if( !empty( $this->summary )) {
4742          $this->summary = '';
4743          $return = TRUE;
4744        }
4745        break;
4746      case 'TRANSP':
4747        if( !empty( $this->transp )) {
4748          $this->transp = '';
4749          $return = TRUE;
4750        }
4751        break;
4752      case 'TRIGGER':
4753        if( !empty( $this->trigger )) {
4754          $this->trigger = '';
4755          $return = TRUE;
4756        }
4757        break;
4758      case 'TZID':
4759        if( !empty( $this->tzid )) {
4760          $this->tzid = '';
4761          $return = TRUE;
4762        }
4763        break;
4764      case 'TZNAME':
4765        return $this->deletePropertyM( $this->tzname, $propix );
4766        break;
4767      case 'TZOFFSETFROM':
4768        if( !empty( $this->tzoffsetfrom )) {
4769          $this->tzoffsetfrom = '';
4770          $return = TRUE;
4771        }
4772        break;
4773      case 'TZOFFSETTO':
4774        if( !empty( $this->tzoffsetto )) {
4775          $this->tzoffsetto = '';
4776          $return = TRUE;
4777        }
4778        break;
4779      case 'TZURL':
4780        if( !empty( $this->tzurl )) {
4781          $this->tzurl = '';
4782          $return = TRUE;
4783        }
4784        break;
4785      case 'UID':
4786        if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
4787          return FALSE;
4788        if( !empty( $this->uid )) {
4789          $this->uid = '';
4790          $return = TRUE;
4791        }
4792        break;
4793      case 'URL':
4794        if( !empty( $this->url )) {
4795          $this->url = '';
4796          $return = TRUE;
4797        }
4798        break;
4799      default:
4800        $reduced = '';
4801        if( $propName != 'X-PROP' ) {
4802          if( !isset( $this->xprop[$propName] )) return FALSE;
4803          foreach( $this->xprop as $k => $a ) {
4804            if(( $k != $propName ) && !empty( $a ))
4805              $reduced[$k] = $a;
4806          }
4807        }
4808        else {
4809          if( count( $this->xprop ) <= $propix ) return FALSE;
4810          $xpropno = 0;
4811          foreach( $this->xprop as $xpropkey => $xpropvalue ) {
4812            if( $propix != $xpropno )
4813              $reduced[$xpropkey] = $xpropvalue;
4814            ++$xpropno;
4815          }
4816        }
4817        $this->xprop = $reduced;
4818        if( empty( $this->xprop ))
4819          return FALSE;
4820        return TRUE;
4821    }
4822    return $return;
4823  }
4824/*********************************************************************************/
4825/**
4826 * delete component property value, fixing components with multiple occurencies
4827 *
4828 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4829 * @since 2.6.19 - 2009-12-30
4830 * @param array $multiprop, reference to a component property
4831 * @param int   $propix, default 0
4832 * @return bool TRUE
4833 */
4834  function deletePropertyM( & $multiprop, $propix=0 ) {
4835    if( !isset( $multiprop[$propix])) return FALSE;
4836    unset( $multiprop[$propix] );
4837    if( empty( $multiprop )) $multiprop = '';
4838    return ( isset( $multiprop[$propix] )) ? FALSE : TRUE;
4839  }
4840/**
4841 * get component property value/params
4842 *
4843 * if property has multiply values, consequtive function calls are needed
4844 *
4845 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
4846 * @since 2.6.22 - 2010-10-21
4847 * @param string $propName, optional
4848 * @param int @propix, optional, if specific property is wanted in case of multiply occurences
4849 * @param bool $inclParam=FALSE
4850 * @param bool $specform=FALSE
4851 * @return mixed
4852 */
4853  function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) {
4854    if( $this->_notExistProp( $propName )) return FALSE;
4855    $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
4856    if( in_array( $propName, array( 'ATTACH',   'ATTENDEE', 'CATEGORIES', 'COMMENT',   'CONTACT', 'DESCRIPTION',    'EXDATE', 'EXRULE',
4857                                    'FREEBUSY', 'RDATE',    'RELATED-TO', 'RESOURCES', 'RRULE',   'REQUEST-STATUS', 'TZNAME', 'X-PROP'  ))) {
4858      if( !$propix )
4859        $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
4860      $this->propix[$propName] = --$propix;
4861    }
4862    switch( $propName ) {
4863      case 'ACTION':
4864        if( !empty( $this->action['value'] )) return ( $inclParam ) ? $this->action : $this->action['value'];
4865        break;
4866      case 'ATTACH':
4867        if( !isset( $this->attach[$propix] )) return FALSE;
4868        return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value'];
4869        break;
4870      case 'ATTENDEE':
4871        if( !isset( $this->attendee[$propix] )) return FALSE;
4872        return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value'];
4873        break;
4874      case 'CATEGORIES':
4875        if( !isset( $this->categories[$propix] )) return FALSE;
4876        return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value'];
4877        break;
4878      case 'CLASS':
4879        if( !empty( $this->class['value'] )) return ( $inclParam ) ? $this->class : $this->class['value'];
4880        break;
4881      case 'COMMENT':
4882        if( !isset( $this->comment[$propix] )) return FALSE;
4883        return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value'];
4884        break;
4885      case 'COMPLETED':
4886        if( !empty( $this->completed['value'] )) return ( $inclParam ) ? $this->completed : $this->completed['value'];
4887        break;
4888      case 'CONTACT':
4889        if( !isset( $this->contact[$propix] )) return FALSE;
4890        return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value'];
4891        break;
4892      case 'CREATED':
4893        if( !empty( $this->created['value'] )) return ( $inclParam ) ? $this->created : $this->created['value'];
4894        break;
4895      case 'DESCRIPTION':
4896        if( !isset( $this->description[$propix] )) return FALSE;
4897        return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value'];
4898        break;
4899      case 'DTEND':
4900        if( !empty( $this->dtend['value'] )) return ( $inclParam ) ? $this->dtend : $this->dtend['value'];
4901        break;
4902      case 'DTSTAMP':
4903        if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
4904          return;
4905        if( !isset( $this->dtstamp['value'] ))
4906          $this->_makeDtstamp();
4907        return ( $inclParam ) ? $this->dtstamp : $this->dtstamp['value'];
4908        break;
4909      case 'DTSTART':
4910        if( !empty( $this->dtstart['value'] )) return ( $inclParam ) ? $this->dtstart : $this->dtstart['value'];
4911        break;
4912      case 'DUE':
4913        if( !empty( $this->due['value'] )) return ( $inclParam ) ? $this->due : $this->due['value'];
4914        break;
4915      case 'DURATION':
4916        if( !isset( $this->duration['value'] )) return FALSE;
4917        $value = ( $specform && isset( $this->dtstart['value'] ) && isset( $this->duration['value'] )) ? iCalUtilityFunctions::_duration2date( $this->dtstart['value'], $this->duration['value'] ) : $this->duration['value'];
4918        return ( $inclParam ) ? array( 'value' => $value, 'params' =>  $this->duration['params'] ) : $value;
4919        break;
4920      case 'EXDATE':
4921        if( !isset( $this->exdate[$propix] )) return FALSE;
4922        return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value'];
4923        break;
4924      case 'EXRULE':
4925        if( !isset( $this->exrule[$propix] )) return FALSE;
4926        return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value'];
4927        break;
4928      case 'FREEBUSY':
4929        if( !isset( $this->freebusy[$propix] )) return FALSE;
4930        return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value'];
4931        break;
4932      case 'GEO':
4933        if( !empty( $this->geo['value'] )) return ( $inclParam ) ? $this->geo : $this->geo['value'];
4934        break;
4935      case 'LAST-MODIFIED':
4936        if( !empty( $this->lastmodified['value'] )) return ( $inclParam ) ? $this->lastmodified : $this->lastmodified['value'];
4937        break;
4938      case 'LOCATION':
4939        if( !empty( $this->location['value'] )) return ( $inclParam ) ? $this->location : $this->location['value'];
4940        break;
4941      case 'ORGANIZER':
4942        if( !empty( $this->organizer['value'] )) return ( $inclParam ) ? $this->organizer : $this->organizer['value'];
4943        break;
4944      case 'PERCENT-COMPLETE':
4945        if( !empty( $this->percentcomplete['value'] )) return ( $inclParam ) ? $this->percentcomplete : $this->percentcomplete['value'];
4946        break;
4947      case 'PRIORITY':
4948        if( !empty( $this->priority['value'] )) return ( $inclParam ) ? $this->priority : $this->priority['value'];
4949        break;
4950      case 'RDATE':
4951        if( !isset( $this->rdate[$propix] )) return FALSE;
4952        return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value'];
4953        break;
4954      case 'RECURRENCE-ID':
4955        if( !empty( $this->recurrenceid['value'] )) return ( $inclParam ) ? $this->recurrenceid : $this->recurrenceid['value'];
4956        break;
4957      case 'RELATED-TO':
4958        if( !isset( $this->relatedto[$propix] )) return FALSE;
4959        return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value'];
4960        break;
4961      case 'REPEAT':
4962        if( !empty( $this->repeat['value'] )) return ( $inclParam ) ? $this->repeat : $this->repeat['value'];
4963        break;
4964      case 'REQUEST-STATUS':
4965        if( !isset( $this->requeststatus[$propix] )) return FALSE;
4966        return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value'];
4967        break;
4968      case 'RESOURCES':
4969        if( !isset( $this->resources[$propix] )) return FALSE;
4970        return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value'];
4971        break;
4972      case 'RRULE':
4973        if( !isset( $this->rrule[$propix] )) return FALSE;
4974        return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value'];
4975        break;
4976      case 'SEQUENCE':
4977        if( isset( $this->sequence['value'] ) && ( '0' <= $this->sequence['value'] )) return ( $inclParam ) ? $this->sequence : $this->sequence['value'];
4978        break;
4979      case 'STATUS':
4980        if( !empty( $this->status['value'] )) return ( $inclParam ) ? $this->status : $this->status['value'];
4981        break;
4982      case 'SUMMARY':
4983        if( !empty( $this->summary['value'] )) return ( $inclParam ) ? $this->summary : $this->summary['value'];
4984        break;
4985      case 'TRANSP':
4986        if( !empty( $this->transp['value'] )) return ( $inclParam ) ? $this->transp : $this->transp['value'];
4987        break;
4988      case 'TRIGGER':
4989        if( !empty( $this->trigger['value'] )) return ( $inclParam ) ? $this->trigger : $this->trigger['value'];
4990        break;
4991      case 'TZID':
4992        if( !empty( $this->tzid['value'] )) return ( $inclParam ) ? $this->tzid : $this->tzid['value'];
4993        break;
4994      case 'TZNAME':
4995        if( !isset( $this->tzname[$propix] )) return FALSE;
4996        return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value'];
4997        break;
4998      case 'TZOFFSETFROM':
4999        if( !empty( $this->tzoffsetfrom['value'] )) return ( $inclParam ) ? $this->tzoffsetfrom : $this->tzoffsetfrom['value'];
5000        break;
5001      case 'TZOFFSETTO':
5002        if( !empty( $this->tzoffsetto['value'] )) return ( $inclParam ) ? $this->tzoffsetto : $this->tzoffsetto['value'];
5003        break;
5004      case 'TZURL':
5005        if( !empty( $this->tzurl['value'] )) return ( $inclParam ) ? $this->tzurl : $this->tzurl['value'];
5006        break;
5007      case 'UID':
5008        if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5009          return FALSE;
5010        if( empty( $this->uid['value'] ))
5011          $this->_makeuid();
5012        return ( $inclParam ) ? $this->uid : $this->uid['value'];
5013        break;
5014      case 'URL':
5015        if( !empty( $this->url['value'] )) return ( $inclParam ) ? $this->url : $this->url['value'];
5016        break;
5017      default:
5018        if( $propName != 'X-PROP' ) {
5019          if( !isset( $this->xprop[$propName] )) return FALSE;
5020          return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
5021                                : array( $propName, $this->xprop[$propName]['value'] );
5022        }
5023        else {
5024          if( empty( $this->xprop )) return FALSE;
5025          $xpropno = 0;
5026          foreach( $this->xprop as $xpropkey => $xpropvalue ) {
5027            if( $propix == $xpropno )
5028              return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
5029                                    : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
5030            else
5031              ++$xpropno;
5032          }
5033          return FALSE; // not found ??
5034        }
5035    }
5036    return FALSE;
5037  }
5038/**
5039 * general component property setting
5040 *
5041 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5042 * @since 2.5.1 - 2008-11-05
5043 * @param mixed $args variable number of function arguments,
5044 *                    first argument is ALWAYS component name,
5045 *                    second ALWAYS component value!
5046 * @return void
5047 */
5048  function setProperty() {
5049    $numargs    = func_num_args();
5050    if( 1 > $numargs ) return FALSE;
5051    $arglist    = func_get_args();
5052    if( $this->_notExistProp( $arglist[0] )) return FALSE;
5053    if( !$this->getConfig( 'allowEmpty' ) && ( !isset( $arglist[1] ) || empty( $arglist[1] )))
5054      return FALSE;
5055    $arglist[0] = strtoupper( $arglist[0] );
5056    for( $argix=$numargs; $argix < 12; ++$argix ) {
5057      if( !isset( $arglist[$argix] ))
5058        $arglist[$argix] = null;
5059    }
5060    switch( $arglist[0] ) {
5061      case 'ACTION':
5062        return $this->setAction( $arglist[1], $arglist[2] );
5063      case 'ATTACH':
5064        return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] );
5065      case 'ATTENDEE':
5066        return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] );
5067      case 'CATEGORIES':
5068        return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] );
5069      case 'CLASS':
5070        return $this->setClass( $arglist[1], $arglist[2] );
5071      case 'COMMENT':
5072        return $this->setComment( $arglist[1], $arglist[2], $arglist[3] );
5073      case 'COMPLETED':
5074        return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5075      case 'CONTACT':
5076        return $this->setContact( $arglist[1], $arglist[2], $arglist[3] );
5077      case 'CREATED':
5078        return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5079      case 'DESCRIPTION':
5080        return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] );
5081      case 'DTEND':
5082        return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5083      case 'DTSTAMP':
5084        return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5085      case 'DTSTART':
5086        return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5087      case 'DUE':
5088        return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5089      case 'DURATION':
5090        return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] );
5091      case 'EXDATE':
5092        return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] );
5093      case 'EXRULE':
5094        return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] );
5095      case 'FREEBUSY':
5096        return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] );
5097      case 'GEO':
5098        return $this->setGeo( $arglist[1], $arglist[2], $arglist[3] );
5099      case 'LAST-MODIFIED':
5100        return $this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5101      case 'LOCATION':
5102        return $this->setLocation( $arglist[1], $arglist[2] );
5103      case 'ORGANIZER':
5104        return $this->setOrganizer( $arglist[1], $arglist[2] );
5105      case 'PERCENT-COMPLETE':
5106        return $this->setPercentComplete( $arglist[1], $arglist[2] );
5107      case 'PRIORITY':
5108        return $this->setPriority( $arglist[1], $arglist[2] );
5109      case 'RDATE':
5110        return $this->setRdate( $arglist[1], $arglist[2], $arglist[3] );
5111      case 'RECURRENCE-ID':
5112       return $this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5113      case 'RELATED-TO':
5114        return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] );
5115      case 'REPEAT':
5116        return $this->setRepeat( $arglist[1], $arglist[2] );
5117      case 'REQUEST-STATUS':
5118        return $this->setRequestStatus( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5] );
5119      case 'RESOURCES':
5120        return $this->setResources( $arglist[1], $arglist[2], $arglist[3] );
5121      case 'RRULE':
5122        return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] );
5123      case 'SEQUENCE':
5124        return $this->setSequence( $arglist[1], $arglist[2] );
5125      case 'STATUS':
5126        return $this->setStatus( $arglist[1], $arglist[2] );
5127      case 'SUMMARY':
5128        return $this->setSummary( $arglist[1], $arglist[2] );
5129      case 'TRANSP':
5130        return $this->setTransp( $arglist[1], $arglist[2] );
5131      case 'TRIGGER':
5132        return $this->setTrigger( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8], $arglist[9], $arglist[10], $arglist[11] );
5133      case 'TZID':
5134        return $this->setTzid( $arglist[1], $arglist[2] );
5135      case 'TZNAME':
5136        return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] );
5137      case 'TZOFFSETFROM':
5138        return $this->setTzoffsetfrom( $arglist[1], $arglist[2] );
5139      case 'TZOFFSETTO':
5140        return $this->setTzoffsetto( $arglist[1], $arglist[2] );
5141      case 'TZURL':
5142        return $this->setTzurl( $arglist[1], $arglist[2] );
5143      case 'UID':
5144        return $this->setUid( $arglist[1], $arglist[2] );
5145      case 'URL':
5146        return $this->setUrl( $arglist[1], $arglist[2] );
5147      default:
5148        return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
5149    }
5150    return FALSE;
5151  }
5152/*********************************************************************************/
5153/**
5154 * parse component unparsed data into properties
5155 *
5156 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5157 * @since 2.6.33 - 2010-12-14
5158 * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings
5159 * @return bool FALSE if error occurs during parsing
5160 *
5161 */
5162  function parse( $unparsedtext=null ) {
5163    if( $unparsedtext ) {
5164      if( !is_array( $unparsedtext ))
5165        $unparsedtext = array( $unparsedtext );
5166    }
5167    elseif( !isset( $this->unparsed ))
5168      $unparsedtext = array();
5169    else
5170      $unparsedtext = $this->unparsed;
5171    $this->unparsed = array();
5172    $comp = & $this;
5173    foreach ( $unparsedtext as $line ) {
5174// echo $comp->objName.": $line<br />"; // test ###
5175      if( in_array( strtoupper( substr( $line, 0, 6 )), array( 'END:VA', 'END:ST', 'END:DA' )))
5176      $this->components[] = $comp->copy();
5177      elseif( 'END:' == strtoupper( substr( $line, 0, 4 )))
5178        break;
5179      elseif( 'BEGIN:VALARM'   == strtoupper( substr( $line, 0, 12 )))
5180        $comp = new valarm();
5181      elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 )))
5182        $comp = new vtimezone( 'standard' );
5183      elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 )))
5184        $comp = new vtimezone( 'daylight' );
5185      elseif( 'BEGIN:'         == strtoupper( substr( $line, 0, 6 )))
5186        continue;
5187      else {
5188        $comp->unparsed[] = $line;
5189// echo $comp->objName.": $line<br />\n"; // test ###
5190      }
5191    }
5192// echo $this->objName.'<br />'.var_export( $this->unparsed, TRUE )."<br />\n"; // test ###
5193            /* concatenate property values spread over several lines */
5194    $lastix    = -1;
5195    $propnames = array( 'action', 'attach', 'attendee', 'categories', 'comment', 'completed'
5196                      , 'contact', 'class', 'created', 'description', 'dtend', 'dtstart'
5197                      , 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo'
5198                      , 'last-modified', 'location', 'organizer', 'percent-complete'
5199                      , 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat'
5200                      , 'request-status', 'resources', 'rrule', 'sequence', 'status'
5201                      , 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom'
5202                      , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' );
5203    $proprows  = array();
5204    foreach( $this->unparsed as $line ) {
5205      $newProp = FALSE;
5206      foreach ( $propnames as $propname ) {
5207        if( $propname == strtolower( substr( $line, 0, strlen( $propname )))) {
5208          $newProp = TRUE;
5209          break;
5210        }
5211      }
5212      if( $newProp ) {
5213        $newProp = FALSE;
5214        ++$lastix;
5215        $proprows[$lastix]  = $line;
5216      }
5217      else {
5218
5219            /* remove line breaks */
5220        if(( 1 < strlen( $proprows[$lastix] ))         &&
5221           ( '\n' == substr( $proprows[$lastix], -2 )) &&
5222           (  ' ' == substr( $line, 0, 1 ))) {
5223          $proprows[$lastix] = substr( $proprows[$lastix], 0, strlen( $proprows[$lastix] ) - 2 );
5224          $line = substr( $line, 1 );
5225        }
5226
5227        if(( 1 < strlen( $proprows[$lastix] ))         &&
5228           (  ' ' == substr( $line, 0, 1 ))) {
5229          $proprows[$lastix] = substr( $proprows[$lastix], 0, strlen( $proprows[$lastix] ) - 1 );
5230          $line = substr( $line, 1 );
5231        }
5232        $proprows[$lastix] .= $line;
5233      }
5234    }
5235            /* parse each property 'line' */
5236    foreach( $proprows as $line ) {
5237      $line = str_replace( "\n ", '', $line );
5238      if( '\n' == substr( $line, -2 ))
5239        $line = substr( $line, 0, strlen( $line ) - 2 );
5240            /* get propname, (problem with x-properties, otherwise in previous loop) */
5241      $cix = $propname = null;
5242      for( $cix=0, $clen = strlen( $line ); $cix < $clen; ++$cix ) {
5243        if( in_array( $line[$cix], array( ':', ';' )))
5244          break;
5245        else {
5246          $propname .= $line[$cix];
5247        }
5248      }
5249      if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) {
5250        $propname2 = $propname;
5251        $propname  = 'X-';
5252      }
5253            /* rest of the line is opt.params and value */
5254      $line = substr( $line, $cix );
5255            /* separate attributes from value */
5256      $attr   = array();
5257      $attrix = -1;
5258      $clen = strlen( $line );
5259      $controlCN = false;
5260
5261      for( $cix=0; $cix < $clen; ++$cix ) {
5262
5263        if('"' == $line[$cix] && $controlCN)  $controlCN = false;
5264        if('"'   == $line[$cix] && strtoupper( substr( $line, $cix - 3, 3 )) == 'CN=' )  $controlCN = true;
5265
5266        if((  ':'   == $line[$cix] )             &&
5267             ( $controlCN === false)                        &&
5268             ( '://' != substr( $line, $cix, 3 )) &&
5269             ( !in_array( strtolower( substr( $line, $cix - 3, 4 )), array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ))) &&
5270             ( !in_array( strtolower( substr( $line, $cix - 4, 5 )), array( 'crid:', 'news:', 'pres:' ))) &&
5271             ( 'mailto:'   != strtolower( substr( $line, $cix - 6, 7 )))) {
5272          $attrEnd = TRUE;
5273          if(( $cix < ( $clen - 4 )) &&
5274               ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
5275            for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
5276              if( '://' == substr( $line, $c2ix - 2, 3 )) {
5277                $attrEnd = FALSE;
5278                break; // an URI with a portnr!!
5279              }
5280            }
5281          }
5282          if( $attrEnd) {
5283            $line = substr( $line, $cix + 1 );
5284            break;
5285          }
5286        }
5287        if( ';' == $line[$cix])
5288          $attr[++$attrix] = null;
5289        else
5290          $attr[$attrix] .= $line[$cix];
5291      }     
5292            /* make attributes in array format */
5293      $propattr = array();
5294      foreach( $attr as $attribute ) {
5295        $attrsplit = explode( '=', $attribute, 2 );
5296        if( 1 < count( $attrsplit ))
5297          $propattr[$attrsplit[0]] = $attrsplit[1];
5298        else
5299          $propattr[] = $attribute;
5300      }
5301            /* call setProperty( $propname.. . */
5302      switch( strtoupper( $propname )) {
5303        case 'ATTENDEE':
5304          foreach( $propattr as $pix => $attr ) {
5305            if(strtoupper($pix) == 'CN') continue;         
5306            $attr2 = explode( ',', $attr );
5307              if( 1 < count( $attr2 ))
5308                $propattr[$pix] = $attr2;
5309          }
5310          $this->setProperty( $propname, $line, $propattr );
5311          break;
5312        case 'CATEGORIES':
5313        case 'RESOURCES':
5314          if( FALSE !== strpos( $line, ',' )) {
5315            $content  = explode( ',', $line );
5316            $clen     = count( $content );
5317            for( $cix = 0; $cix < $clen; ++$cix ) {
5318              if( "\\" == substr($content[$cix], -1)) {
5319                $content[$cix] .= ','.$content[$cix + 1];
5320                unset($content[$cix + 1]);
5321                ++$cix;
5322              }
5323            }
5324            if( 1 < count( $content )) {
5325              $content = array_values( $content );
5326              foreach( $content as $cix => $contentPart )
5327                $content[$cix] = calendarComponent::_strunrep( $contentPart );
5328              $this->setProperty( $propname, $content, $propattr );
5329              break;
5330            }
5331            else
5332              $line = reset( $content );
5333          }
5334        case 'X-':
5335          $propname = ( isset( $propname2 )) ? $propname2 : $propname;
5336        case 'COMMENT':
5337        case 'CONTACT':
5338        case 'DESCRIPTION':
5339        case 'LOCATION':
5340        case 'SUMMARY':
5341          if( empty( $line ))
5342            $propattr = null;
5343          $this->setProperty( $propname, calendarComponent::_strunrep( $line ), $propattr );
5344          unset( $propname2 );
5345          break;
5346        case 'REQUEST-STATUS':
5347          $values    = explode( ';', $line, 3 );
5348          $values[1] = ( !isset( $values[1] )) ? null : calendarComponent::_strunrep( $values[1] );
5349          $values[2] = ( !isset( $values[2] )) ? null : calendarComponent::_strunrep( $values[2] );
5350          $this->setProperty( $propname
5351                            , $values[0]  // statcode
5352                            , $values[1]  // statdesc
5353                            , $values[2]  // extdata
5354                            , $propattr );
5355          break;
5356        case 'FREEBUSY':
5357          $fbtype = ( isset( $propattr['FBTYPE'] )) ? $propattr['FBTYPE'] : ''; // force setting default, if missing
5358          unset( $propattr['FBTYPE'] );
5359          $values = explode( ',', $line );
5360          foreach( $values as $vix => $value ) {
5361            $value2 = explode( '/', $value );
5362            if( 1 < count( $value2 ))
5363              $values[$vix] = $value2;
5364          }
5365          $this->setProperty( $propname, $fbtype, $values, $propattr );
5366          break;
5367        case 'GEO':
5368          $value = explode( ';', $line, 2 );
5369          if( 2 > count( $value ))
5370            $value[1] = null;
5371          $this->setProperty( $propname, $value[0], $value[1], $propattr );
5372          break;
5373        case 'EXDATE':
5374          $values = ( !empty( $line )) ? explode( ',', $line ) : null;
5375          $this->setProperty( $propname, $values, $propattr );
5376          break;
5377        case 'RDATE':
5378          if( empty( $line )) {
5379            $this->setProperty( $propname, $line, $propattr );
5380            break;
5381          }
5382          $values = explode( ',', $line );
5383          foreach( $values as $vix => $value ) {
5384            $value2 = explode( '/', $value );
5385            if( 1 < count( $value2 ))
5386              $values[$vix] = $value2;
5387          }
5388          $this->setProperty( $propname, $values, $propattr );
5389          break;
5390        case 'EXRULE':
5391        case 'RRULE':
5392          $values = explode( ';', $line );
5393          $recur = array();
5394          foreach( $values as $value2 ) {
5395            if( empty( $value2 ))
5396              continue; // ;-char in ending position ???
5397            $value3 = explode( '=', $value2, 2 );
5398            $rulelabel = strtoupper( $value3[0] );
5399            switch( $rulelabel ) {
5400              case 'BYDAY': {
5401                $value4 = explode( ',', $value3[1] );
5402                if( 1 < count( $value4 )) {
5403                  foreach( $value4 as $v5ix => $value5 ) {
5404                    $value6 = array();
5405                    $dayno = $dayname = null;
5406                    $value5 = trim( (string) $value5 );
5407                    if(( ctype_alpha( substr( $value5, -1 ))) &&
5408                       ( ctype_alpha( substr( $value5, -2, 1 )))) {
5409                      $dayname = substr( $value5, -2, 2 );
5410                      if( 2 < strlen( $value5 ))
5411                        $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
5412                    }
5413                    if( $dayno )
5414                      $value6[] = $dayno;
5415                    if( $dayname )
5416                      $value6['DAY'] = $dayname;
5417                    $value4[$v5ix] = $value6;
5418                  }
5419                }
5420                else {
5421                  $value4 = array();
5422                  $dayno  = $dayname = null;
5423                  $value5 = trim( (string) $value3[1] );
5424                  if(( ctype_alpha( substr( $value5, -1 ))) &&
5425                     ( ctype_alpha( substr( $value5, -2, 1 )))) {
5426                      $dayname = substr( $value5, -2, 2 );
5427                    if( 2 < strlen( $value5 ))
5428                      $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
5429                  }
5430                  if( $dayno )
5431                    $value4[] = $dayno;
5432                  if( $dayname )
5433                    $value4['DAY'] = $dayname;
5434                }
5435                $recur[$rulelabel] = $value4;
5436                break;
5437              }
5438              default: {
5439                $value4 = explode( ',', $value3[1] );
5440                if( 1 < count( $value4 ))
5441                  $value3[1] = $value4;
5442                $recur[$rulelabel] = $value3[1];
5443                break;
5444              }
5445            } // end - switch $rulelabel
5446          } // end - foreach( $values.. .
5447          $this->setProperty( $propname, $recur, $propattr );
5448          break;
5449        case 'ACTION':
5450        case 'CLASSIFICATION':
5451        case 'STATUS':
5452        case 'TRANSP':
5453        case 'UID':
5454        case 'TZID':
5455        case 'RELATED-TO':
5456        case 'TZNAME':
5457          $line = calendarComponent::_strunrep( $line );
5458        default:
5459          $this->setProperty( $propname, $line, $propattr );
5460          break;
5461      } // end  switch( $propname.. .
5462    } // end - foreach( $proprows.. .
5463    unset( $unparsedtext, $this->unparsed, $proprows );
5464    if( isset( $this->components ) && is_array( $this->components ) && ( 0 < count( $this->components ))) {
5465      $ckeys = array_keys( $this->components );
5466      foreach( $ckeys as $ckey ) {
5467        if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
5468          $this->components[$ckey]->parse();
5469        }
5470      }
5471    }
5472    return TRUE;
5473  }
5474/*********************************************************************************/
5475/*********************************************************************************/
5476/**
5477 * return a copy of this component
5478 *
5479 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5480 * @since 2.2.16 - 2007-11-07
5481 * @return object
5482 */
5483  function copy() {
5484    $serialized_contents = serialize( $this );
5485    $copy = unserialize( $serialized_contents );
5486    unset( $copy->propix );
5487    return $copy;
5488 }
5489/*********************************************************************************/
5490/*********************************************************************************/
5491/**
5492 * delete calendar subcomponent from component container
5493 *
5494 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5495 * @since 2.5.1 - 2008-10-15
5496 * @param mixed $arg1 ordno / component type / component uid
5497 * @param mixed $arg2 optional, ordno if arg1 = component type
5498 * @return void
5499 */
5500  function deleteComponent( $arg1, $arg2=FALSE  ) {
5501    if( !isset( $this->components )) return FALSE;
5502    $argType = $index = null;
5503    if ( ctype_digit( (string) $arg1 )) {
5504      $argType = 'INDEX';
5505      $index   = (int) $arg1 - 1;
5506    }
5507    elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
5508      $argType = strtolower( $arg1 );
5509      $index   = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
5510    }
5511    $cix2dC = 0;
5512    foreach ( $this->components as $cix => $component) {
5513      if( empty( $component )) continue;
5514      unset( $component->propix );
5515      if(( 'INDEX' == $argType ) && ( $index == $cix )) {
5516        unset( $this->components[$cix] );
5517        return TRUE;
5518      }
5519      elseif( $argType == $component->objName ) {
5520        if( $index == $cix2dC ) {
5521          unset( $this->components[$cix] );
5522          return TRUE;
5523        }
5524        ++$cix2dC;
5525      }
5526      elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
5527        unset( $this->components[$cix] );
5528        return TRUE;
5529      }
5530    }
5531    return FALSE;
5532  }
5533/**
5534 * get calendar component subcomponent from component container
5535 *
5536 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5537 * @since 2.5.1 - 2008-10-15
5538 * @param mixed $arg1 optional, ordno/component type/ component uid
5539 * @param mixed $arg2 optional, ordno if arg1 = component type
5540 * @return object
5541 */
5542  function getComponent ( $arg1=FALSE, $arg2=FALSE ) {
5543    if( !isset( $this->components )) return FALSE;
5544    $index = $argType = null;
5545    if ( !$arg1 ) {
5546      $argType = 'INDEX';
5547      $index   = $this->compix['INDEX'] =
5548        ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
5549    }
5550    elseif ( ctype_digit( (string) $arg1 )) {
5551      $argType = 'INDEX';
5552      $index   = (int) $arg1;
5553      unset( $this->compix );
5554    }
5555    elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
5556      unset( $this->compix['INDEX'] );
5557      $argType = strtolower( $arg1 );
5558      if( !$arg2 )
5559        $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
5560      else
5561        $index = (int) $arg2;
5562    }
5563    $index  -= 1;
5564    $ckeys = array_keys( $this->components );
5565    if( !empty( $index) && ( $index > end( $ckeys )))
5566      return FALSE;
5567    $cix2gC = 0;
5568    foreach( $this->components as $cix => $component ) {
5569      if( empty( $component )) continue;
5570      unset( $component->propix );
5571      if(( 'INDEX' == $argType ) && ( $index == $cix ))
5572        return $component->copy();
5573      elseif( $argType == $component->objName ) {
5574         if( $index == $cix2gC )
5575           return $component->copy();
5576         ++$cix2gC;
5577      }
5578      elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' ))) {
5579        unset( $component->propix );
5580        return $component->copy();
5581      }
5582    }
5583            /* not found.. . */
5584    unset( $this->compix );
5585    return false;
5586  }
5587/**
5588 * add calendar component as subcomponent to container for subcomponents
5589 *
5590 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5591 * @since 1.x.x - 2007-04-24
5592 * @param object $component calendar component
5593 * @return void
5594 */
5595  function addSubComponent ( $component ) {
5596    $this->setComponent( $component );
5597  }
5598/**
5599 * create new calendar component subcomponent, already included within component
5600 *
5601 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5602 * @since 2.6.33 - 2011-01-03
5603 * @param string $compType subcomponent type
5604 * @return object (reference)
5605 */
5606  function & newComponent( $compType ) {
5607    $config = $this->getConfig();
5608    $keys   = array_keys( $this->components );
5609    $ix     = end( $keys) + 1;
5610    switch( strtoupper( $compType )) {
5611      case 'ALARM':
5612      case 'VALARM':
5613        $this->components[$ix] = new valarm( $config );
5614        break;
5615      case 'STANDARD':
5616        array_unshift( $this->components, new vtimezone( 'STANDARD', $config ));
5617        $ix = 0;
5618        break;
5619      case 'DAYLIGHT':
5620        $this->components[$ix] = new vtimezone( 'DAYLIGHT', $config );
5621        break;
5622      default:
5623        return FALSE;
5624    }
5625    return $this->components[$ix];
5626  }
5627/**
5628 * add calendar component as subcomponent to container for subcomponents
5629 *
5630 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5631 * @since 2.6.28 - 2011-01-01
5632 * @param object $component calendar component
5633 * @param mixed $arg1 optional, ordno/component type/ component uid
5634 * @param mixed $arg2 optional, ordno if arg1 = component type
5635 * @return bool
5636 */
5637  function setComponent( $component, $arg1=FALSE, $arg2=FALSE  ) {
5638    if( !isset( $this->components )) return FALSE;
5639    $component->setConfig( $this->getConfig(), FALSE, TRUE );
5640    if( !in_array( $component->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
5641      unset( $component->propix );
5642            /* make sure dtstamp and uid is set */
5643      $dummy = $component->getProperty( 'dtstamp' );
5644      $dummy = $component->getProperty( 'uid' );
5645    }
5646    if( !$arg1 ) { // plain insert, last in chain
5647      $this->components[] = $component->copy();
5648      return TRUE;
5649    }
5650    $argType = $index = null;
5651    if ( ctype_digit( (string) $arg1 )) { // index insert/replace
5652      $argType = 'INDEX';
5653      $index   = (int) $arg1 - 1;
5654    }
5655    elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) {
5656      $argType = strtolower( $arg1 );
5657      $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
5658    }
5659    // else if arg1 is set, arg1 must be an UID
5660    $cix2sC = 0;
5661    foreach ( $this->components as $cix => $component2 ) {
5662      if( empty( $component2 )) continue;
5663      unset( $component2->propix );
5664      if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
5665        $this->components[$cix] = $component->copy();
5666        return TRUE;
5667      }
5668      elseif( $argType == $component2->objName ) { // component Type index insert/replace
5669        if( $index == $cix2sC ) {
5670          $this->components[$cix] = $component->copy();
5671          return TRUE;
5672        }
5673        ++$cix2sC;
5674      }
5675      elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
5676        $this->components[$cix] = $component->copy();
5677        return TRUE;
5678      }
5679    }
5680            /* arg1=index and not found.. . insert at index .. .*/
5681    if( 'INDEX' == $argType ) {
5682      $this->components[$index] = $component->copy();
5683      ksort( $this->components, SORT_NUMERIC );
5684    }
5685    else    /* not found.. . insert last in chain anyway .. .*/
5686    $this->components[] = $component->copy();
5687    return TRUE;
5688  }
5689/**
5690 * creates formatted output for subcomponents
5691 *
5692 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5693 * @since 2.6.27 - 2010-12-12
5694 * @return string
5695 */
5696  function createSubComponent() {
5697    $output = null;
5698    foreach( $this->components as $component ) {
5699      if( empty( $component )) continue;
5700      $component->setConfig( $this->getConfig(), FALSE, TRUE );
5701      $output .= $component->createComponent( $this->xcaldecl );
5702    }
5703    return $output;
5704  }
5705/********************************************************************************/
5706/**
5707 * break lines at pos 75
5708 *
5709 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
5710 * break. Long content lines SHOULD be split into a multiple line
5711 * representations using a line "folding" technique. That is, a long
5712 * line can be split between any two characters by inserting a CRLF
5713 * immediately followed by a single linear white space character (i.e.,
5714 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
5715 * of CRLF followed immediately by a single linear white space character
5716 * is ignored (i.e., removed) when processing the content type.
5717 *
5718 * Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where
5719 * the reserved expression "\n" in the arg $string could be broken up by the
5720 * folding of lines, causing ambiguity in the return string.
5721 * Fix uses var $breakAtChar=75 and breaks the line at $breakAtChar-1 if need be.
5722 *
5723 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5724 * @since 2.6.13 - 2010-12-06
5725 * @param string $value
5726 * @return string
5727 */
5728  function _size75( $string ) {
5729    $tmp    = $string;
5730    $string = null;
5731            /* if PHP is config with  mb_string.. . */
5732    if( defined( MB_OVERLOAD_STRING )) {
5733      $strlen  = mb_strlen( $tmp );
5734      while( $strlen > 75 ) {
5735         $breakAtChar = 75;
5736         if( substr( $tmp, ( $breakAtChar - 1 ), strlen( '\n' )) == '\n' )
5737           $breakAtChar = $breakAtChar - 1;
5738         $string .= mb_substr( $tmp, 0, $breakAtChar ).$this->nl;
5739         $tmp     = ' '.mb_substr( $tmp, $breakAtChar );
5740         $strlen  = mb_strlen( $tmp );
5741      } // end while
5742      $string .= rtrim( $tmp ); // the rest
5743      if( $this->nl != mb_substr( $string, ( 0 - strlen( $this->nl ))))
5744        $string .= $this->nl;
5745      return $string;
5746    }
5747            /* if PHP is not config with  mb_string.. . */
5748    $eolcharlen = strlen( '\n' );
5749    $eolchrxlen = strlen( "\n" );
5750    while( TRUE ) {
5751      $bytecnt = strlen( $tmp );
5752      $charCnt = $ix = 0;
5753      for( $ix = 0; $ix < $bytecnt; ++$ix ) {
5754        if(( 73 < $charCnt ) && ( '\n' == substr( $tmp, $ix, $eolcharlen ))) {
5755          $ix += $eolcharlen;
5756          break;                                    // break when '\n' and eol
5757        }
5758        elseif( 74 < $charCnt )
5759          break;                                    // always break for-loop here
5760        else {
5761          $byte = ord( $tmp[$ix] );
5762          if ($byte <= 127) {                       // add a one byte character
5763            $string .= substr( $tmp, $ix, 1 );
5764            $charCnt += 1;
5765          }
5766          else if ($byte >= 194 && $byte <= 223) {  // start byte in two byte character
5767            $string .= substr( $tmp, $ix, 2 );      // add a two bytes character
5768            $charCnt += 1;
5769          }
5770          else if ($byte >= 224 && $byte <= 239) {  // start byte in three bytes character
5771            $string .= substr( $tmp, $ix, 3 );      // add a three bytes character
5772            $charCnt += 1;
5773          }
5774          else if ($byte >= 240 && $byte <= 244) {  // start byte in four bytes character
5775            $string .= substr( $tmp, $ix, 4 );      // add a four bytes character
5776            $charCnt += 1;
5777          }
5778        }
5779      } // end for
5780      $string .= $this->nl;
5781      $tmp     = substr( $tmp, $ix );
5782      if( empty( $tmp ))
5783        break; // while-loop breakes here
5784      else
5785        $tmp  = ' '.$tmp;
5786    } // end while
5787    $string .= rtrim( $tmp ); // the rest
5788    $string = str_replace( "\\\\n", "\\n", $string );
5789    if( $this->nl != substr( $string, ( 0 - strlen( $this->nl ))))
5790      $string .= $this->nl;
5791    return $string;
5792  }
5793/**
5794 * special characters management output
5795 *
5796 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5797 * @since 2.6.15 - 2010-09-24
5798 * @param string $string
5799 * @return string
5800 */
5801  function _strrep( $string ) {
5802    switch( $this->format ) {
5803      case 'xcal':
5804        $string = str_replace( '\n',  $this->nl, $string);
5805        $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string ))));
5806        break;
5807      default:
5808        $pos = 0;
5809        $specChars = array( 'n', 'N', 'r', ',', ';' );
5810        while( $pos <= strlen( $string )) {
5811          $pos = strpos( $string, "\\", $pos );
5812          if( FALSE === $pos )
5813            break;
5814          if( !in_array( substr( $string, $pos, 1 ), $specChars )) {
5815            $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos + 1 ));
5816            $pos += 1;
5817          }
5818          $pos += 1;
5819        }
5820        if( FALSE !== strpos( $string, '"' ))
5821          $string = str_replace('"',   "'",       $string);
5822        if( FALSE !== strpos( $string, ',' ))
5823          $string = str_replace(',',   '\,',      $string);
5824        if( FALSE !== strpos( $string, ';' ))
5825          $string = str_replace(';',   '\;',      $string);
5826        if( FALSE !== strpos( $string, "\r\n" ))
5827          $string = str_replace( "\r\n", '\n',    $string);
5828        elseif( FALSE !== strpos( $string, "\r" ))
5829          $string = str_replace( "\r", '\n',      $string);
5830
5831        elseif( FALSE !== strpos( $string, "\n" ))
5832          $string = str_replace( "\n", '\n',      $string);
5833
5834        if( FALSE !== strpos( $string, '\N' ))
5835          $string = str_replace( '\N', '\n',      $string);
5836//        if( FALSE !== strpos( $string, $this->nl ))
5837          $string = str_replace( $this->nl, '\n', $string);
5838        break;
5839    }
5840    return $string;
5841  }
5842/**
5843 * special characters management input (from iCal file)
5844 *
5845 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5846 * @since 2.6.22 - 2010-10-17
5847 * @param string $string
5848 * @return string
5849 */
5850  static function _strunrep( $string ) {
5851    $string = str_replace( '\\\\', '\\',     $string);
5852    $string = str_replace( '\,',   ',',      $string);
5853    $string = str_replace( '\;',   ';',      $string);
5854//    $string = str_replace( '\n',  $this->nl, $string); // ??
5855    return $string;
5856  }
5857}
5858/*********************************************************************************/
5859/*********************************************************************************/
5860/**
5861 * class for calendar component VEVENT
5862 *
5863 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5864 * @since 2.5.1 - 2008-10-12
5865 */
5866class vevent extends calendarComponent {
5867  var $attach;
5868  var $attendee;
5869  var $categories;
5870  var $comment;
5871  var $contact;
5872  var $class;
5873  var $created;
5874  var $description;
5875  var $dtend;
5876  var $dtstart;
5877  var $duration;
5878  var $exdate;
5879  var $exrule;
5880  var $geo;
5881  var $lastmodified;
5882  var $location;
5883  var $organizer;
5884  var $priority;
5885  var $rdate;
5886  var $recurrenceid;
5887  var $relatedto;
5888  var $requeststatus;
5889  var $resources;
5890  var $rrule;
5891  var $sequence;
5892  var $status;
5893  var $summary;
5894  var $transp;
5895  var $url;
5896  var $xprop;
5897            //  component subcomponents container
5898  var $components;
5899/**
5900 * constructor for calendar component VEVENT object
5901 *
5902 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5903 * @since 2.6.27 - 2010-11-20
5904 * @param  array $config
5905 * @return void
5906 */
5907  function vevent( $config = array()) {
5908    $this->calendarComponent();
5909
5910    $this->attach          = '';
5911    $this->attendee        = '';
5912    $this->categories      = '';
5913    $this->class           = '';
5914    $this->comment         = '';
5915    $this->contact         = '';
5916    $this->created         = '';
5917    $this->description     = '';
5918    $this->dtstart         = '';
5919    $this->dtend           = '';
5920    $this->duration        = '';
5921    $this->exdate          = '';
5922    $this->exrule          = '';
5923    $this->geo             = '';
5924    $this->lastmodified    = '';
5925    $this->location        = '';
5926    $this->organizer       = '';
5927    $this->priority        = '';
5928    $this->rdate           = '';
5929    $this->recurrenceid    = '';
5930    $this->relatedto       = '';
5931    $this->requeststatus   = '';
5932    $this->resources       = '';
5933    $this->rrule           = '';
5934    $this->sequence        = '';
5935    $this->status          = '';
5936    $this->summary         = '';
5937    $this->transp          = '';
5938    $this->url             = '';
5939    $this->xprop           = '';
5940
5941    $this->components      = array();
5942
5943    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
5944                                          $config['language']   = ICAL_LANG;
5945    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
5946    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
5947    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
5948    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
5949    $this->setConfig( $config );
5950
5951  }
5952/**
5953 * create formatted output for calendar component VEVENT object instance
5954 *
5955 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
5956 * @since 2.5.1 - 2008-11-07
5957 * @param array $xcaldecl
5958 * @return string
5959 */
5960  function createComponent( &$xcaldecl ) {
5961    $objectname    = $this->_createFormat();
5962    $component     = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
5963    $component    .= $this->createUid();
5964    $component    .= $this->createDtstamp();
5965    $component    .= $this->createAttach();
5966    $component    .= $this->createAttendee();
5967    $component    .= $this->createCategories();
5968    $component    .= $this->createComment();
5969    $component    .= $this->createContact();
5970    $component    .= $this->createClass();
5971    $component    .= $this->createCreated();
5972    $component    .= $this->createDescription();
5973    $component    .= $this->createDtstart();
5974    $component    .= $this->createDtend();
5975    $component    .= $this->createDuration();
5976    $component    .= $this->createExdate();
5977    $component    .= $this->createExrule();
5978    $component    .= $this->createGeo();
5979    $component    .= $this->createLastModified();
5980    $component    .= $this->createLocation();
5981    $component    .= $this->createOrganizer();
5982    $component    .= $this->createPriority();
5983    $component    .= $this->createRdate();
5984    $component    .= $this->createRrule();
5985    $component    .= $this->createRelatedTo();
5986    $component    .= $this->createRequestStatus();
5987    $component    .= $this->createRecurrenceid();
5988    $component    .= $this->createResources();
5989    $component    .= $this->createSequence();
5990    $component    .= $this->createStatus();
5991    $component    .= $this->createSummary();
5992    $component    .= $this->createTransp();
5993    $component    .= $this->createUrl();
5994    $component    .= $this->createXprop();
5995    $component    .= $this->createSubComponent();
5996    $component    .= $this->componentEnd1.$objectname.$this->componentEnd2;
5997    if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
5998      foreach( $this->xcaldecl as $localxcaldecl )
5999        $xcaldecl[] = $localxcaldecl;
6000    }
6001    return $component;
6002  }
6003}
6004/*********************************************************************************/
6005/*********************************************************************************/
6006/**
6007 * class for calendar component VTODO
6008 *
6009 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6010 * @since 2.5.1 - 2008-10-12
6011 */
6012class vtodo extends calendarComponent {
6013  var $attach;
6014  var $attendee;
6015  var $categories;
6016  var $comment;
6017  var $completed;
6018  var $contact;
6019  var $class;
6020  var $created;
6021  var $description;
6022  var $dtstart;
6023  var $due;
6024  var $duration;
6025  var $exdate;
6026  var $exrule;
6027  var $geo;
6028  var $lastmodified;
6029  var $location;
6030  var $organizer;
6031  var $percentcomplete;
6032  var $priority;
6033  var $rdate;
6034  var $recurrenceid;
6035  var $relatedto;
6036  var $requeststatus;
6037  var $resources;
6038  var $rrule;
6039  var $sequence;
6040  var $status;
6041  var $summary;
6042  var $url;
6043  var $xprop;
6044            //  component subcomponents container
6045  var $components;
6046/**
6047 * constructor for calendar component VTODO object
6048 *
6049 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6050 * @since 2.6.27 - 2010-11-20
6051 * @param array $config
6052 * @return void
6053 */
6054  function vtodo( $config = array()) {
6055    $this->calendarComponent();
6056
6057    $this->attach          = '';
6058    $this->attendee        = '';
6059    $this->categories      = '';
6060    $this->class           = '';
6061    $this->comment         = '';
6062    $this->completed       = '';
6063    $this->contact         = '';
6064    $this->created         = '';
6065    $this->description     = '';
6066    $this->dtstart         = '';
6067    $this->due             = '';
6068    $this->duration        = '';
6069    $this->exdate          = '';
6070    $this->exrule          = '';
6071    $this->geo             = '';
6072    $this->lastmodified    = '';
6073    $this->location        = '';
6074    $this->organizer       = '';
6075    $this->percentcomplete = '';
6076    $this->priority        = '';
6077    $this->rdate           = '';
6078    $this->recurrenceid    = '';
6079    $this->relatedto       = '';
6080    $this->requeststatus   = '';
6081    $this->resources       = '';
6082    $this->rrule           = '';
6083    $this->sequence        = '';
6084    $this->status          = '';
6085    $this->summary         = '';
6086    $this->url             = '';
6087    $this->xprop           = '';
6088
6089    $this->components      = array();
6090
6091    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6092                                          $config['language']   = ICAL_LANG;
6093    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
6094    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
6095    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
6096    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
6097    $this->setConfig( $config );
6098
6099  }
6100/**
6101 * create formatted output for calendar component VTODO object instance
6102 *
6103 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6104 * @since 2.5.1 - 2008-11-07
6105 * @param array $xcaldecl
6106 * @return string
6107 */
6108  function createComponent( &$xcaldecl ) {
6109    $objectname    = $this->_createFormat();
6110    $component     = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6111    $component    .= $this->createUid();
6112    $component    .= $this->createDtstamp();
6113    $component    .= $this->createAttach();
6114    $component    .= $this->createAttendee();
6115    $component    .= $this->createCategories();
6116    $component    .= $this->createClass();
6117    $component    .= $this->createComment();
6118    $component    .= $this->createCompleted();
6119    $component    .= $this->createContact();
6120    $component    .= $this->createCreated();
6121    $component    .= $this->createDescription();
6122    $component    .= $this->createDtstart();
6123    $component    .= $this->createDue();
6124    $component    .= $this->createDuration();
6125    $component    .= $this->createExdate();
6126    $component    .= $this->createExrule();
6127    $component    .= $this->createGeo();
6128    $component    .= $this->createLastModified();
6129    $component    .= $this->createLocation();
6130    $component    .= $this->createOrganizer();
6131    $component    .= $this->createPercentComplete();
6132    $component    .= $this->createPriority();
6133    $component    .= $this->createRdate();
6134    $component    .= $this->createRelatedTo();
6135    $component    .= $this->createRequestStatus();
6136    $component    .= $this->createRecurrenceid();
6137    $component    .= $this->createResources();
6138    $component    .= $this->createRrule();
6139    $component    .= $this->createSequence();
6140    $component    .= $this->createStatus();
6141    $component    .= $this->createSummary();
6142    $component    .= $this->createUrl();
6143    $component    .= $this->createXprop();
6144    $component    .= $this->createSubComponent();
6145    $component    .= $this->componentEnd1.$objectname.$this->componentEnd2;
6146    if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6147      foreach( $this->xcaldecl as $localxcaldecl )
6148        $xcaldecl[] = $localxcaldecl;
6149    }
6150    return $component;
6151  }
6152}
6153/*********************************************************************************/
6154/*********************************************************************************/
6155/**
6156 * class for calendar component VJOURNAL
6157 *
6158 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6159 * @since 2.5.1 - 2008-10-12
6160 */
6161class vjournal extends calendarComponent {
6162  var $attach;
6163  var $attendee;
6164  var $categories;
6165  var $comment;
6166  var $contact;
6167  var $class;
6168  var $created;
6169  var $description;
6170  var $dtstart;
6171  var $exdate;
6172  var $exrule;
6173  var $lastmodified;
6174  var $organizer;
6175  var $rdate;
6176  var $recurrenceid;
6177  var $relatedto;
6178  var $requeststatus;
6179  var $rrule;
6180  var $sequence;
6181  var $status;
6182  var $summary;
6183  var $url;
6184  var $xprop;
6185/**
6186 * constructor for calendar component VJOURNAL object
6187 *
6188 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6189 * @since 2.6.27 - 2010-11-20
6190 * @param array $config
6191 * @return void
6192 */
6193  function vjournal( $config = array()) {
6194    $this->calendarComponent();
6195
6196    $this->attach          = '';
6197    $this->attendee        = '';
6198    $this->categories      = '';
6199    $this->class           = '';
6200    $this->comment         = '';
6201    $this->contact         = '';
6202    $this->created         = '';
6203    $this->description     = '';
6204    $this->dtstart         = '';
6205    $this->exdate          = '';
6206    $this->exrule          = '';
6207    $this->lastmodified    = '';
6208    $this->organizer       = '';
6209    $this->rdate           = '';
6210    $this->recurrenceid    = '';
6211    $this->relatedto       = '';
6212    $this->requeststatus   = '';
6213    $this->rrule           = '';
6214    $this->sequence        = '';
6215    $this->status          = '';
6216    $this->summary         = '';
6217    $this->url             = '';
6218    $this->xprop           = '';
6219
6220    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6221                                          $config['language']   = ICAL_LANG;
6222    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
6223    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
6224    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
6225    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
6226    $this->setConfig( $config );
6227
6228  }
6229/**
6230 * create formatted output for calendar component VJOURNAL object instance
6231 *
6232 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6233 * @since 2.5.1 - 2008-10-12
6234 * @param array $xcaldecl
6235 * @return string
6236 */
6237  function createComponent( &$xcaldecl ) {
6238    $objectname = $this->_createFormat();
6239    $component  = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6240    $component .= $this->createUid();
6241    $component .= $this->createDtstamp();
6242    $component .= $this->createAttach();
6243    $component .= $this->createAttendee();
6244    $component .= $this->createCategories();
6245    $component .= $this->createClass();
6246    $component .= $this->createComment();
6247    $component .= $this->createContact();
6248    $component .= $this->createCreated();
6249    $component .= $this->createDescription();
6250    $component .= $this->createDtstart();
6251    $component .= $this->createExdate();
6252    $component .= $this->createExrule();
6253    $component .= $this->createLastModified();
6254    $component .= $this->createOrganizer();
6255    $component .= $this->createRdate();
6256    $component .= $this->createRequestStatus();
6257    $component .= $this->createRecurrenceid();
6258    $component .= $this->createRelatedTo();
6259    $component .= $this->createRrule();
6260    $component .= $this->createSequence();
6261    $component .= $this->createStatus();
6262    $component .= $this->createSummary();
6263    $component .= $this->createUrl();
6264    $component .= $this->createXprop();
6265    $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6266    if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6267      foreach( $this->xcaldecl as $localxcaldecl )
6268        $xcaldecl[] = $localxcaldecl;
6269    }
6270    return $component;
6271  }
6272}
6273/*********************************************************************************/
6274/*********************************************************************************/
6275/**
6276 * class for calendar component VFREEBUSY
6277 *
6278 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6279 * @since 2.5.1 - 2008-10-12
6280 */
6281class vfreebusy extends calendarComponent {
6282  var $attendee;
6283  var $comment;
6284  var $contact;
6285  var $dtend;
6286  var $dtstart;
6287  var $duration;
6288  var $freebusy;
6289  var $organizer;
6290  var $requeststatus;
6291  var $url;
6292  var $xprop;
6293            //  component subcomponents container
6294  var $components;
6295/**
6296 * constructor for calendar component VFREEBUSY object
6297 *
6298 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6299 * @since 2.6.27 - 2010-11-10
6300 * @param array $config
6301 * @return void
6302 */
6303  function vfreebusy( $config = array()) {
6304    $this->calendarComponent();
6305
6306    $this->attendee        = '';
6307    $this->comment         = '';
6308    $this->contact         = '';
6309    $this->dtend           = '';
6310    $this->dtstart         = '';
6311    $this->duration        = '';
6312    $this->freebusy        = '';
6313    $this->organizer       = '';
6314    $this->requeststatus   = '';
6315    $this->url             = '';
6316    $this->xprop           = '';
6317
6318    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6319                                          $config['language']   = ICAL_LANG;
6320    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
6321    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
6322    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
6323    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
6324    $this->setConfig( $config );
6325
6326  }
6327/**
6328 * create formatted output for calendar component VFREEBUSY object instance
6329 *
6330 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6331 * @since 2.3.1 - 2007-11-19
6332 * @param array $xcaldecl
6333 * @return string
6334 */
6335  function createComponent( &$xcaldecl ) {
6336    $objectname = $this->_createFormat();
6337    $component  = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6338    $component .= $this->createUid();
6339    $component .= $this->createDtstamp();
6340    $component .= $this->createAttendee();
6341    $component .= $this->createComment();
6342    $component .= $this->createContact();
6343    $component .= $this->createDtstart();
6344    $component .= $this->createDtend();
6345    $component .= $this->createDuration();
6346    $component .= $this->createFreebusy();
6347    $component .= $this->createOrganizer();
6348    $component .= $this->createRequestStatus();
6349    $component .= $this->createUrl();
6350    $component .= $this->createXprop();
6351    $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6352    if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6353      foreach( $this->xcaldecl as $localxcaldecl )
6354        $xcaldecl[] = $localxcaldecl;
6355    }
6356    return $component;
6357  }
6358}
6359/*********************************************************************************/
6360/*********************************************************************************/
6361/**
6362 * class for calendar component VALARM
6363 *
6364 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6365 * @since 2.5.1 - 2008-10-12
6366 */
6367class valarm extends calendarComponent {
6368  var $action;
6369  var $attach;
6370  var $attendee;
6371  var $description;
6372  var $duration;
6373  var $repeat;
6374  var $summary;
6375  var $trigger;
6376  var $xprop;
6377/**
6378 * constructor for calendar component VALARM object
6379 *
6380 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6381 * @since 2.6.27 - 2010-11-20
6382 * @param array $config
6383 * @return void
6384 */
6385  function valarm( $config = array()) {
6386    $this->calendarComponent();
6387
6388    $this->action          = '';
6389    $this->attach          = '';
6390    $this->attendee        = '';
6391    $this->description     = '';
6392    $this->duration        = '';
6393    $this->repeat          = '';
6394    $this->summary         = '';
6395    $this->trigger         = '';
6396    $this->xprop           = '';
6397
6398    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6399                                          $config['language']   = ICAL_LANG;
6400    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
6401    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
6402    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
6403    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
6404    $this->setConfig( $config );
6405
6406  }
6407/**
6408 * create formatted output for calendar component VALARM object instance
6409 *
6410 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6411 * @since 2.5.1 - 2008-10-22
6412 * @param array $xcaldecl
6413 * @return string
6414 */
6415  function createComponent( &$xcaldecl ) {
6416    $objectname    = $this->_createFormat();
6417    $component     = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6418    $component    .= $this->createAction();
6419    $component    .= $this->createAttach();
6420    $component    .= $this->createAttendee();
6421    $component    .= $this->createDescription();
6422    $component    .= $this->createDuration();
6423    $component    .= $this->createRepeat();
6424    $component    .= $this->createSummary();
6425    $component    .= $this->createTrigger();
6426    $component    .= $this->createXprop();
6427    $component    .= $this->componentEnd1.$objectname.$this->componentEnd2;
6428    return $component;
6429  }
6430}
6431/**********************************************************************************
6432/*********************************************************************************/
6433/**
6434 * class for calendar component VTIMEZONE
6435 *
6436 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6437 * @since 2.5.1 - 2008-10-12
6438 */
6439class vtimezone extends calendarComponent {
6440  var $timezonetype;
6441
6442  var $comment;
6443  var $dtstart;
6444  var $lastmodified;
6445  var $rdate;
6446  var $rrule;
6447  var $tzid;
6448  var $tzname;
6449  var $tzoffsetfrom;
6450  var $tzoffsetto;
6451  var $tzurl;
6452  var $xprop;
6453            //  component subcomponents container
6454  var $components;
6455/**
6456 * constructor for calendar component VTIMEZONE object
6457 *
6458 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6459 * @since 2.6.27 - 2010-11-20
6460 * @param mixed $timezonetype optional, default FALSE ( STANDARD / DAYLIGHT )
6461 * @param array $config
6462 * @return void
6463 */
6464  function vtimezone( $timezonetype=FALSE, $config = array()) {
6465    if( is_array( $timezonetype )) {
6466      $config       = $timezonetype;
6467      $timezonetype = FALSE;
6468    }
6469    if( !$timezonetype )
6470      $this->timezonetype = 'VTIMEZONE';
6471    else
6472      $this->timezonetype = strtoupper( $timezonetype );
6473    $this->calendarComponent();
6474
6475    $this->comment         = '';
6476    $this->dtstart         = '';
6477    $this->lastmodified    = '';
6478    $this->rdate           = '';
6479    $this->rrule           = '';
6480    $this->tzid            = '';
6481    $this->tzname          = '';
6482    $this->tzoffsetfrom    = '';
6483    $this->tzoffsetto      = '';
6484    $this->tzurl           = '';
6485    $this->xprop           = '';
6486
6487    $this->components      = array();
6488
6489    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6490                                          $config['language']   = ICAL_LANG;
6491    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
6492    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
6493    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
6494    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
6495    $this->setConfig( $config );
6496
6497  }
6498/**
6499 * create formatted output for calendar component VTIMEZONE object instance
6500 *
6501 * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
6502 * @since 2.5.1 - 2008-10-25
6503 * @param array $xcaldecl
6504 * @return string
6505 */
6506  function createComponent( &$xcaldecl ) {
6507    $objectname    = $this->_createFormat();
6508    $component     = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6509    $component    .= $this->createTzid();
6510    $component    .= $this->createLastModified();
6511    $component    .= $this->createTzurl();
6512    $component    .= $this->createDtstart();
6513    $component    .= $this->createTzoffsetfrom();
6514    $component    .= $this->createTzoffsetto();
6515    $component    .= $this->createComment();
6516    $component    .= $this->createRdate();
6517    $component    .= $this->createRrule();
6518    $component    .= $this->createTzname();
6519    $component    .= $this->createXprop();
6520    $component    .= $this->createSubComponent();
6521    $component    .= $this->componentEnd1.$objectname.$this->componentEnd2;
6522    if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6523      foreach( $this->xcaldecl as $localxcaldecl )
6524        $xcaldecl[] = $localxcaldecl;
6525    }
6526    return $component;
6527  }
6528}
6529?>
Note: See TracBrowser for help on using the repository browser.