Example #1
0
 /**
  * parse iCal text/file into VCalendar, components, properties and parameters
  *
  * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**>
  * @since 2.16.2 - 2012-12-18
  * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings
  * @return bool FALSE if error occurs during parsing
  *
  */
 function parse($unparsedtext = FALSE)
 {
     $nl = $this->getConfig('nl');
     if (FALSE === $unparsedtext || empty($unparsedtext)) {
         /* directory+filename is set previously via setConfig directory+filename or url */
         if (FALSE === ($filename = $this->getConfig('url'))) {
             $filename = $this->getConfig('dirfile');
         }
         /* READ FILE */
         if (FALSE === ($rows = file_get_contents($filename))) {
             return FALSE;
         }
         /* err 1 */
     } elseif (is_array($unparsedtext)) {
         $rows = implode('\\n' . $nl, $unparsedtext);
     } else {
         $rows =& $unparsedtext;
     }
     /* fix line folding */
     $rows = explode($nl, ICalUtilityFunctions::convEolChar($rows, $nl));
     /* skip leading (empty/invalid) lines */
     foreach ($rows as $lix => $line) {
         if (FALSE !== stripos($line, 'BEGIN:VCALENDAR')) {
             break;
         }
         unset($rows[$lix]);
     }
     $rcnt = count($rows);
     if (3 > $rcnt) {
         /* err 10 */
         return FALSE;
     }
     /* skip trailing empty lines and ensure an end row */
     $lix = array_keys($rows);
     $lix = end($lix);
     while (3 < $lix) {
         $tst = trim($rows[$lix]);
         if ('\\n' == $tst || empty($tst)) {
             unset($rows[$lix]);
             $lix--;
             continue;
         }
         if (FALSE === stripos($rows[$lix], 'END:VCALENDAR')) {
             $rows[] = 'END:VCALENDAR';
         }
         break;
     }
     $comp =& $this;
     $calsync = $compsync = 0;
     /* identify components and update unparsed data within component */
     $config = $this->getConfig();
     $endtxt = array('END:VE', 'END:VF', 'END:VJ', 'END:VT');
     foreach ($rows as $lix => $line) {
         if ('BEGIN:VCALENDAR' == strtoupper(substr($line, 0, 15))) {
             $calsync++;
             continue;
         } elseif ('END:VCALENDAR' == strtoupper(substr($line, 0, 13))) {
             if (0 < $compsync) {
                 $this->components[] = $comp->copy();
             }
             $compsync--;
             $calsync--;
             break;
         } elseif (1 != $calsync) {
             return FALSE;
         } elseif (in_array(strtoupper(substr($line, 0, 6)), $endtxt)) {
             $this->components[] = $comp->copy();
             $compsync--;
             continue;
         }
         if ('BEGIN:VEVENT' == strtoupper(substr($line, 0, 12))) {
             $comp = new VEvent($config);
             $compsync++;
         } elseif ('BEGIN:VFREEBUSY' == strtoupper(substr($line, 0, 15))) {
             $comp = new VFreeBusy($config);
             $compsync++;
         } elseif ('BEGIN:VJOURNAL' == strtoupper(substr($line, 0, 14))) {
             $comp = new VJournal($config);
             $compsync++;
         } elseif ('BEGIN:VTODO' == strtoupper(substr($line, 0, 11))) {
             $comp = new VTodo($config);
             $compsync++;
         } elseif ('BEGIN:VTIMEZONE' == strtoupper(substr($line, 0, 15))) {
             $comp = new VTimeZone($config);
             $compsync++;
         } else {
             /* update component with unparsed data */
             $comp->unparsed[] = $line;
         }
     }
     // end foreach( $rows as $line )
     unset($config, $endtxt);
     /* parse data for calendar (this) object */
     if (isset($this->unparsed) && is_array($this->unparsed) && 0 < count($this->unparsed)) {
         /* concatenate property values spread over several lines */
         $propnames = array('calscale', 'method', 'prodid', 'version', 'x-');
         $proprows = array();
         for ($i = 0; $i < count($this->unparsed); $i++) {
             // concatenate lines
             $line = rtrim($this->unparsed[$i], $nl);
             while (isset($this->unparsed[$i + 1]) && !empty($this->unparsed[$i + 1]) && ' ' == $this->unparsed[$i + 1][0]) {
                 $line .= rtrim(substr($this->unparsed[++$i], 1), $nl);
             }
             $proprows[] = $line;
         }
         $paramMStz = array('utc-', 'utc+', 'gmt-', 'gmt+');
         $paramProto3 = array('fax:', 'cid:', 'sms:', 'tel:', 'urn:');
         $paramProto4 = array('crid:', 'news:', 'pres:');
         foreach ($proprows as $line) {
             if ('\\n' == substr($line, -2)) {
                 $line = substr($line, 0, -2);
             }
             /* get property name */
             $propname = '';
             $cix = 0;
             while (FALSE !== ($char = substr($line, $cix, 1))) {
                 if (in_array($char, array(':', ';'))) {
                     break;
                 } else {
                     $propname .= $char;
                 }
                 $cix++;
             }
             /* skip non standard property names */
             if ('x-' != strtolower(substr($propname, 0, 2)) && !in_array(strtolower($propname), $propnames)) {
                 continue;
             }
             /* ignore version/prodid properties */
             if (in_array(strtolower($propname), array('version', 'prodid'))) {
                 continue;
             }
             /* rest of the line is opt.params and value */
             $line = substr($line, $cix);
             /* separate attributes from value */
             $attr = array();
             $attrix = -1;
             $strlen = strlen($line);
             $WithinQuotes = FALSE;
             $cix = 0;
             while (FALSE !== substr($line, $cix, 1)) {
                 if (':' == $line[$cix] && substr($line, $cix, 3) != '://' && !in_array(strtolower(substr($line, $cix - 6, 4)), $paramMStz) && !in_array(strtolower(substr($line, $cix - 3, 4)), $paramProto3) && !in_array(strtolower(substr($line, $cix - 4, 5)), $paramProto4) && strtolower(substr($line, $cix - 6, 7)) != 'mailto:' && !$WithinQuotes) {
                     $attrEnd = TRUE;
                     if ($cix < $strlen - 4 && ctype_digit(substr($line, $cix + 1, 4))) {
                         // an URI with a (4pos) portnr??
                         for ($c2ix = $cix; 3 < $c2ix; $c2ix--) {
                             if ('://' == substr($line, $c2ix - 2, 3)) {
                                 $attrEnd = FALSE;
                                 break;
                                 // an URI with a portnr!!
                             }
                         }
                     }
                     if ($attrEnd) {
                         $line = substr($line, $cix + 1);
                         break;
                     }
                 }
                 if ('"' == $line[$cix]) {
                     $WithinQuotes = FALSE === $WithinQuotes ? TRUE : FALSE;
                 }
                 if (';' == $line[$cix]) {
                     $attr[++$attrix] = null;
                 } else {
                     $attr[$attrix] .= $line[$cix];
                 }
                 $cix++;
             }
             /* make attributes in array format */
             $propattr = array();
             foreach ($attr as $attribute) {
                 $attrsplit = explode('=', $attribute, 2);
                 if (1 < count($attrsplit)) {
                     $propattr[$attrsplit[0]] = $attrsplit[1];
                 } else {
                     $propattr[] = $attribute;
                 }
             }
             /* update Property */
             if (FALSE !== strpos($line, ',')) {
                 $content = array(0 => '');
                 $cix = $lix = 0;
                 while (FALSE !== substr($line, $lix, 1)) {
                     if (0 < $lix && ',' == $line[$lix] && "\\" != $line[$lix - 1]) {
                         $cix++;
                         $content[$cix] = '';
                     } else {
                         $content[$cix] .= $line[$lix];
                     }
                     $lix++;
                 }
                 if (1 < count($content)) {
                     foreach ($content as $cix => $contentPart) {
                         $content[$cix] = ICalUtilityFunctions::_strunrep($contentPart);
                     }
                     $this->setProperty($propname, $content, $propattr);
                     continue;
                 } else {
                     $line = reset($content);
                 }
                 $line = ICalUtilityFunctions::_strunrep($line);
             }
             $this->setProperty($propname, rtrim($line, ".."), $propattr);
         }
         // end - foreach( $this->unparsed.. .
     }
     // end - if( is_array( $this->unparsed.. .
     unset($unparsedtext, $rows, $this->unparsed, $proprows);
     /* parse Components */
     if (is_array($this->components) && 0 < count($this->components)) {
         $ckeys = array_keys($this->components);
         foreach ($ckeys as $ckey) {
             if (!empty($this->components[$ckey]) && !empty($this->components[$ckey]->unparsed)) {
                 $this->components[$ckey]->parse();
             }
         }
     } else {
         return FALSE;
     }
     /* err 91 or something.. . */
     return TRUE;
 }
Example #2
0
 /**
  * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0)
  *
  * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**>
  * @since 2.15.1 - 2012-10-17
  * @param mixed  $date,   date to alter
  * @param string $tzFrom, PHP valid 'from' timezone
  * @param string $tzTo,   PHP valid 'to' timezone, default 'UTC'
  * @param string $format, date output format, default 'Ymd\THis'
  * @return bool
  */
 public static function transformDateTime(&$date, $tzFrom, $tzTo = 'UTC', $format = 'Ymd\\THis')
 {
     if (is_array($date) && isset($date['timestamp'])) {
         try {
             $d = new \DateTime("@{$date['timestamp']}");
             // set UTC date
             $d->setTimezone(new \DateTimeZone($tzFrom));
             // convert to 'from' date
         } catch (Exception $e) {
             return FALSE;
         }
     } else {
         if (ICalUtilityFunctions::_isArrayDate($date)) {
             if (isset($date['tz'])) {
                 unset($date['tz']);
             }
             $date = ICalUtilityFunctions::_date2strdate(ICalUtilityFunctions::_chkDateArr($date));
         }
         if ('Z' == substr($date, -1)) {
             $date = substr($date, 0, strlen($date) - 2);
         }
         try {
             $d = new \DateTime($date, new \DateTimeZone($tzFrom));
         } catch (Exception $e) {
             return FALSE;
         }
     }
     try {
         $d->setTimezone(new \DateTimeZone($tzTo));
     } catch (Exception $e) {
         return FALSE;
     }
     $date = $d->format($format);
     return TRUE;
 }
Example #3
0
 /**
  * Returns an array containing time zone data from VTimeZone standard/daylight instances
  *
  * @param object $vtzc, an iCalcreator calendar standard/daylight instance
  * @return array, time zone data; array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname)
  *
  */
 public static function expandTimezoneDates($vtzc)
 {
     $tzdates = array();
     // prepare time zone "description" to attach to each change
     $tzbefore = array();
     $tzbefore['offsetHis'] = $vtzc->getProperty('tzoffsetfrom');
     $tzbefore['offsetSec'] = ICalUtilityFunctions::_tz2offset($tzbefore['offsetHis']);
     if ('-' != substr((string) $tzbefore['offsetSec'], 0, 1) && '+' != substr((string) $tzbefore['offsetSec'], 0, 1)) {
         $tzbefore['offsetSec'] = '+' . $tzbefore['offsetSec'];
     }
     $tzafter = array();
     $tzafter['offsetHis'] = $vtzc->getProperty('tzoffsetto');
     $tzafter['offsetSec'] = ICalUtilityFunctions::_tz2offset($tzafter['offsetHis']);
     if ('-' != substr((string) $tzafter['offsetSec'], 0, 1) && '+' != substr((string) $tzafter['offsetSec'], 0, 1)) {
         $tzafter['offsetSec'] = '+' . $tzafter['offsetSec'];
     }
     if (FALSE === ($tzafter['tzname'] = $vtzc->getProperty('tzname'))) {
         $tzafter['tzname'] = $tzafter['offsetHis'];
     }
     // find out where to start from
     $dtstart = $vtzc->getProperty('dtstart');
     $dtstarttimestamp = mktime($dtstart['hour'], $dtstart['min'], $dtstart['sec'], $dtstart['month'], $dtstart['day'], $dtstart['year']);
     if (!isset($dtstart['unparsedtext'])) {
         // ??
         $dtstart['unparsedtext'] = sprintf('%04d%02d%02dT%02d%02d%02d', $dtstart['year'], $dtstart['month'], $dtstart['day'], $dtstart['hour'], $dtstart['min'], $dtstart['sec']);
     }
     if ($dtstarttimestamp == 0) {
         // it seems that the dtstart string may not have parsed correctly
         // let's set a timestamp starting from 1902, using the time part of the original string
         // so that the time will change at the right time of day
         // at worst we'll get midnight again
         $origdtstartsplit = explode('T', $dtstart['unparsedtext']);
         $dtstarttimestamp = strtotime("19020101", 0);
         $dtstarttimestamp = strtotime($origdtstartsplit[1], $dtstarttimestamp);
     }
     // the date (in dtstart and opt RDATE/RRULE) is ALWAYS LOCAL (not utc!!), adjust from 'utc' to 'local' timestamp
     $diff = -1 * $tzbefore['offsetSec'];
     $dtstarttimestamp += $diff;
     // add this (start) change to the array of changes
     $tzdates[] = array('timestamp' => $dtstarttimestamp, 'tzbefore' => $tzbefore, 'tzafter' => $tzafter);
     $datearray = getdate($dtstarttimestamp);
     // save original array to use time parts, because strtotime (used below) apparently loses the time
     $changetime = $datearray;
     // generate dates according to an RRULE line
     $rrule = $vtzc->getProperty('rrule');
     if (is_array($rrule)) {
         if ($rrule['FREQ'] == 'YEARLY') {
             // calculate transition dates starting from DTSTART
             $offsetchangetimestamp = $dtstarttimestamp;
             // calculate transition dates until 10 years in the future
             $stoptimestamp = strtotime("+10 year", time());
             // if UNTIL is set, calculate until then (however far ahead)
             if (isset($rrule['UNTIL']) && $rrule['UNTIL'] != '') {
                 $stoptimestamp = mktime($rrule['UNTIL']['hour'], $rrule['UNTIL']['min'], $rrule['UNTIL']['sec'], $rrule['UNTIL']['month'], $rrule['UNTIL']['day'], $rrule['UNTIL']['year']);
             }
             $count = 0;
             $stopcount = isset($rrule['COUNT']) ? $rrule['COUNT'] : 0;
             $daynames = array('SU' => 'Sunday', 'MO' => 'Monday', 'TU' => 'Tuesday', 'WE' => 'Wednesday', 'TH' => 'Thursday', 'FR' => 'Friday', 'SA' => 'Saturday');
             // repeat so long as we're between DTSTART and UNTIL, or we haven't prepared COUNT dates
             while ($offsetchangetimestamp < $stoptimestamp && ($stopcount == 0 || $count < $stopcount)) {
                 // break up the timestamp into its parts
                 $datearray = getdate($offsetchangetimestamp);
                 if (isset($rrule['BYMONTH']) && $rrule['BYMONTH'] != 0) {
                     // set the month
                     $datearray['mon'] = $rrule['BYMONTH'];
                 }
                 if (isset($rrule['BYMONTHDAY']) && $rrule['BYMONTHDAY'] != 0) {
                     // set specific day of month
                     $datearray['mday'] = $rrule['BYMONTHDAY'];
                 } elseif (is_array($rrule['BYDAY'])) {
                     // find the Xth WKDAY in the month
                     // the starting point for this process is the first of the month set above
                     $datearray['mday'] = 1;
                     // turn $datearray as it is now back into a timestamp
                     $offsetchangetimestamp = mktime($datearray['hours'], $datearray['minutes'], $datearray['seconds'], $datearray['mon'], $datearray['mday'], $datearray['year']);
                     if ($rrule['BYDAY'][0] > 0) {
                         // to find Xth WKDAY in month, we find last WKDAY in month before
                         // we do that by finding first WKDAY in this month and going back one week
                         // then we add X weeks (below)
                         $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']], $offsetchangetimestamp);
                         $offsetchangetimestamp = strtotime("-1 week", $offsetchangetimestamp);
                     } else {
                         // to find Xth WKDAY before the end of the month, we find the first WKDAY in the following month
                         // we do that by going forward one month and going to WKDAY there
                         // then we subtract X weeks (below)
                         $offsetchangetimestamp = strtotime("+1 month", $offsetchangetimestamp);
                         $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']], $offsetchangetimestamp);
                     }
                     // now move forward or back the appropriate number of weeks, into the month we want
                     $offsetchangetimestamp = strtotime($rrule['BYDAY'][0] . " week", $offsetchangetimestamp);
                     $datearray = getdate($offsetchangetimestamp);
                 }
                 // convert the date parts back into a timestamp, setting the time parts according to the
                 // original time data which we stored
                 $offsetchangetimestamp = mktime($changetime['hours'], $changetime['minutes'], $changetime['seconds'] + $diff, $datearray['mon'], $datearray['mday'], $datearray['year']);
                 // add this change to the array of changes
                 $tzdates[] = array('timestamp' => $offsetchangetimestamp, 'tzbefore' => $tzbefore, 'tzafter' => $tzafter);
                 // update counters (timestamp and count)
                 $offsetchangetimestamp = strtotime("+" . (isset($rrule['INTERVAL']) && $rrule['INTERVAL'] != 0 ? $rrule['INTERVAL'] : 1) . " year", $offsetchangetimestamp);
                 $count += 1;
             }
         }
     }
     // generate dates according to RDATE lines
     while ($rdates = $vtzc->getProperty('rdate')) {
         if (is_array($rdates)) {
             foreach ($rdates as $rdate) {
                 // convert the explicit change date to a timestamp
                 $offsetchangetimestamp = mktime($rdate['hour'], $rdate['min'], $rdate['sec'] + $diff, $rdate['month'], $rdate['day'], $rdate['year']);
                 // add this change to the array of changes
                 $tzdates[] = array('timestamp' => $offsetchangetimestamp, 'tzbefore' => $tzbefore, 'tzafter' => $tzafter);
             }
         }
     }
     return $tzdates;
 }
Example #4
0
 /**
  * parse component unparsed data into properties
  *
  * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**>
  * @since 2.16.2 - 2012-12-18
  * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of strings
  * @return bool FALSE if error occurs during parsing
  *
  */
 function parse($unparsedtext = null)
 {
     $nl = $this->getConfig('nl');
     if (!empty($unparsedtext)) {
         if (is_array($unparsedtext)) {
             $unparsedtext = implode('\\n' . $nl, $unparsedtext);
         }
         $unparsedtext = explode($nl, ICalUtilityFunctions::convEolChar($unparsedtext, $nl));
     } elseif (!isset($this->unparsed)) {
         $unparsedtext = array();
     } else {
         $unparsedtext = $this->unparsed;
     }
     /* skip leading (empty/invalid) lines */
     foreach ($unparsedtext as $lix => $line) {
         $tst = trim($line);
         if ('\\n' == $tst || empty($tst)) {
             unset($unparsedtext[$lix]);
         } else {
             break;
         }
     }
     $this->unparsed = array();
     $comp =& $this;
     $config = $this->getConfig();
     $compsync = $subsync = 0;
     foreach ($unparsedtext as $lix => $line) {
         if ('END:VALARM' == strtoupper(substr($line, 0, 10))) {
             if (1 != $subsync) {
                 return FALSE;
             }
             $this->components[] = $comp->copy();
             $subsync--;
         } elseif ('END:DAYLIGHT' == strtoupper(substr($line, 0, 12))) {
             if (1 != $subsync) {
                 return FALSE;
             }
             $this->components[] = $comp->copy();
             $subsync--;
         } elseif ('END:STANDARD' == strtoupper(substr($line, 0, 12))) {
             if (1 != $subsync) {
                 return FALSE;
             }
             array_unshift($this->components, $comp->copy());
             $subsync--;
         } elseif ('END:' == strtoupper(substr($line, 0, 4))) {
             // end:<component>
             if (1 != $compsync) {
                 return FALSE;
             }
             if (0 < $subsync) {
                 $this->components[] = $comp->copy();
             }
             $compsync--;
             break;
             /* skip trailing empty lines */
         } elseif ('BEGIN:VALARM' == strtoupper(substr($line, 0, 12))) {
             $comp = new VAlarm($config);
             $subsync++;
         } elseif ('BEGIN:STANDARD' == strtoupper(substr($line, 0, 14))) {
             $comp = new VTimeZone('standard', $config);
             $subsync++;
         } elseif ('BEGIN:DAYLIGHT' == strtoupper(substr($line, 0, 14))) {
             $comp = new VTimeZone('daylight', $config);
             $subsync++;
         } elseif ('BEGIN:' == strtoupper(substr($line, 0, 6))) {
             // begin:<component>
             $compsync++;
         } else {
             $comp->unparsed[] = $line;
         }
     }
     if (0 < $subsync) {
         $this->components[] = $comp->copy();
     }
     unset($config);
     /* concatenate property values spread over several lines */
     $lastix = -1;
     $propnames = array('action', 'attach', 'attendee', 'categories', 'comment', 'completed', 'contact', 'class', 'created', 'description', 'dtend', 'dtstart', 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo', 'last-modified', 'location', 'organizer', 'percent-complete', 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat', 'request-status', 'resources', 'rrule', 'sequence', 'status', 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom', 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-');
     $proprows = array();
     for ($i = 0; $i < count($this->unparsed); $i++) {
         // concatenate lines
         $line = rtrim($this->unparsed[$i], $nl);
         while (isset($this->unparsed[$i + 1]) && !empty($this->unparsed[$i + 1]) && ' ' == $this->unparsed[$i + 1][0]) {
             $line .= rtrim(substr($this->unparsed[++$i], 1), $nl);
         }
         $proprows[] = $line;
     }
     /* parse each property 'line' */
     $paramMStz = array('utc-', 'utc+', 'gmt-', 'gmt+');
     $paramProto3 = array('fax:', 'cid:', 'sms:', 'tel:', 'urn:');
     $paramProto4 = array('crid:', 'news:', 'pres:');
     foreach ($proprows as $line) {
         if ('\\n' == substr($line, -2)) {
             $line = substr($line, 0, -2);
         }
         /* get propname */
         $propname = null;
         $cix = 0;
         while (isset($line[$cix])) {
             if (in_array($line[$cix], array(':', ';'))) {
                 break;
             } else {
                 $propname .= $line[$cix];
             }
             $cix++;
         }
         if ('x-' == substr($propname, 0, 2) || 'X-' == substr($propname, 0, 2)) {
             $propname2 = $propname;
             $propname = 'X-';
         }
         if (!in_array(strtolower($propname), $propnames)) {
             // skip non standard property names
             continue;
         }
         /* rest of the line is opt.params and value */
         $line = substr($line, $cix);
         /* separate attributes from value */
         $attr = array();
         $attrix = -1;
         $clen = strlen($line);
         $WithinQuotes = FALSE;
         $cix = 0;
         while (FALSE !== substr($line, $cix, 1)) {
             if (':' == $line[$cix] && substr($line, $cix, 3) != '://' && !in_array(strtolower(substr($line, $cix - 6, 4)), $paramMStz) && !in_array(strtolower(substr($line, $cix - 3, 4)), $paramProto3) && !in_array(strtolower(substr($line, $cix - 4, 5)), $paramProto4) && strtolower(substr($line, $cix - 6, 7)) != 'mailto:' && !$WithinQuotes) {
                 $attrEnd = TRUE;
                 if ($cix < $clen - 4 && ctype_digit(substr($line, $cix + 1, 4))) {
                     // an URI with a (4pos) portnr??
                     for ($c2ix = $cix; 3 < $c2ix; $c2ix--) {
                         if ('://' == substr($line, $c2ix - 2, 3)) {
                             $attrEnd = FALSE;
                             break;
                             // an URI with a portnr!!
                         }
                     }
                 }
                 if ($attrEnd) {
                     $line = substr($line, $cix + 1);
                     break;
                 }
                 $cix++;
             }
             if ('"' == $line[$cix]) {
                 $WithinQuotes = FALSE === $WithinQuotes ? TRUE : FALSE;
             }
             if (';' == $line[$cix]) {
                 $attr[++$attrix] = null;
             } else {
                 $attr[$attrix] .= $line[$cix];
             }
             $cix++;
         }
         /* make attributes in array format */
         $propattr = array();
         foreach ($attr as $attribute) {
             $attrsplit = explode('=', $attribute, 2);
             if (1 < count($attrsplit)) {
                 $propattr[$attrsplit[0]] = $attrsplit[1];
             } else {
                 $propattr[] = $attribute;
             }
         }
         /* call setProperty( $propname.. . */
         switch (strtoupper($propname)) {
             case 'ATTENDEE':
                 foreach ($propattr as $pix => $attr) {
                     if (!in_array(strtoupper($pix), array('MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM'))) {
                         continue;
                     }
                     $attr2 = explode(',', $attr);
                     if (1 < count($attr2)) {
                         $propattr[$pix] = $attr2;
                     }
                 }
                 $this->setProperty($propname, $line, $propattr);
                 break;
             case 'X-':
                 $propname = isset($propname2) ? $propname2 : $propname;
                 unset($propname2);
             case 'CATEGORIES':
             case 'RESOURCES':
                 if (FALSE !== strpos($line, ',')) {
                     $content = array(0 => '');
                     $cix = $lix = 0;
                     while (FALSE !== substr($line, $lix, 1)) {
                         if (',' == $line[$lix] && "\\" != $line[$lix - 1]) {
                             $cix++;
                             $content[$cix] = '';
                         } else {
                             $content[$cix] .= $line[$lix];
                         }
                         $lix++;
                     }
                     if (1 < count($content)) {
                         $content = array_values($content);
                         foreach ($content as $cix => $contentPart) {
                             $content[$cix] = ICalUtilityFunctions::_strunrep($contentPart);
                         }
                         $this->setProperty($propname, $content, $propattr);
                         break;
                     } else {
                         $line = reset($content);
                     }
                 }
             case 'COMMENT':
             case 'CONTACT':
             case 'DESCRIPTION':
             case 'LOCATION':
             case 'SUMMARY':
                 if (empty($line)) {
                     $propattr = null;
                 }
                 $this->setProperty($propname, ICalUtilityFunctions::_strunrep($line), $propattr);
                 break;
             case 'REQUEST-STATUS':
                 $values = explode(';', $line, 3);
                 $values[1] = !isset($values[1]) ? null : ICalUtilityFunctions::_strunrep($values[1]);
                 $values[2] = !isset($values[2]) ? null : ICalUtilityFunctions::_strunrep($values[2]);
                 $this->setProperty($propname, $values[0], $values[1], $values[2], $propattr);
                 break;
             case 'FREEBUSY':
                 $fbtype = isset($propattr['FBTYPE']) ? $propattr['FBTYPE'] : '';
                 // force setting default, if missing
                 unset($propattr['FBTYPE']);
                 $values = explode(',', $line);
                 foreach ($values as $vix => $value) {
                     $value2 = explode('/', $value);
                     if (1 < count($value2)) {
                         $values[$vix] = $value2;
                     }
                 }
                 $this->setProperty($propname, $fbtype, $values, $propattr);
                 break;
             case 'GEO':
                 $value = explode(';', $line, 2);
                 if (2 > count($value)) {
                     $value[1] = null;
                 }
                 $this->setProperty($propname, $value[0], $value[1], $propattr);
                 break;
             case 'EXDATE':
                 $values = !empty($line) ? explode(',', $line) : null;
                 $this->setProperty($propname, $values, $propattr);
                 break;
             case 'RDATE':
                 if (empty($line)) {
                     $this->setProperty($propname, $line, $propattr);
                     break;
                 }
                 $values = explode(',', $line);
                 foreach ($values as $vix => $value) {
                     $value2 = explode('/', $value);
                     if (1 < count($value2)) {
                         $values[$vix] = $value2;
                     }
                 }
                 $this->setProperty($propname, $values, $propattr);
                 break;
             case 'EXRULE':
             case 'RRULE':
                 $values = explode(';', $line);
                 $recur = array();
                 foreach ($values as $value2) {
                     if (empty($value2)) {
                         continue;
                     }
                     // ;-char in ending position ???
                     $value3 = explode('=', $value2, 2);
                     $rulelabel = strtoupper($value3[0]);
                     switch ($rulelabel) {
                         case 'BYDAY':
                             $value4 = explode(',', $value3[1]);
                             if (1 < count($value4)) {
                                 foreach ($value4 as $v5ix => $value5) {
                                     $value6 = array();
                                     $dayno = $dayname = null;
                                     $value5 = trim((string) $value5);
                                     if (ctype_alpha(substr($value5, -1)) && ctype_alpha(substr($value5, -2, 1))) {
                                         $dayname = substr($value5, -2, 2);
                                         if (2 < strlen($value5)) {
                                             $dayno = substr($value5, 0, strlen($value5) - 2);
                                         }
                                     }
                                     if ($dayno) {
                                         $value6[] = $dayno;
                                     }
                                     if ($dayname) {
                                         $value6['DAY'] = $dayname;
                                     }
                                     $value4[$v5ix] = $value6;
                                 }
                             } else {
                                 $value4 = array();
                                 $dayno = $dayname = null;
                                 $value5 = trim((string) $value3[1]);
                                 if (ctype_alpha(substr($value5, -1)) && ctype_alpha(substr($value5, -2, 1))) {
                                     $dayname = substr($value5, -2, 2);
                                     if (2 < strlen($value5)) {
                                         $dayno = substr($value5, 0, strlen($value5) - 2);
                                     }
                                 }
                                 if ($dayno) {
                                     $value4[] = $dayno;
                                 }
                                 if ($dayname) {
                                     $value4['DAY'] = $dayname;
                                 }
                             }
                             $recur[$rulelabel] = $value4;
                             break;
                         default:
                             $value4 = explode(',', $value3[1]);
                             if (1 < count($value4)) {
                                 $value3[1] = $value4;
                             }
                             $recur[$rulelabel] = $value3[1];
                             break;
                     }
                     // end - switch $rulelabel
                 }
                 // end - foreach( $values.. .
                 $this->setProperty($propname, $recur, $propattr);
                 break;
             case 'ACTION':
             case 'CLASSIFICATION':
             case 'STATUS':
             case 'TRANSP':
             case 'UID':
             case 'TZID':
             case 'RELATED-TO':
             case 'TZNAME':
                 $line = ICalUtilityFunctions::_strunrep($line);
             default:
                 $this->setProperty($propname, $line, $propattr);
                 break;
         }
         // end  switch( $propname.. .
     }
     // end - foreach( $proprows.. .
     unset($unparsedtext, $this->unparsed, $proprows);
     if (isset($this->components) && is_array($this->components) && 0 < count($this->components)) {
         $ckeys = array_keys($this->components);
         foreach ($ckeys as $ckey) {
             if (!empty($this->components[$ckey]) && !empty($this->components[$ckey]->unparsed)) {
                 $this->components[$ckey]->parse();
             }
         }
     }
     return TRUE;
 }