/** * constructor for calendar component VALARM object * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.8.2 - 2011-05-01 * @param array $config * @uses valarm::calendarComponent() * @uses valarm::$action * @uses valarm::$attach * @uses valarm::$attendee * @uses valarm::$description * @uses valarm::$duration * @uses valarm::$repeat * @uses valarm::$summary * @uses valarm::$trigger * @uses valarm::$xprop * @uses calendarComponent::setConfig() */ function __construct($config = array()) { parent::__construct(); $this->action = ''; $this->attach = ''; $this->attendee = ''; $this->description = ''; $this->duration = ''; $this->repeat = ''; $this->summary = ''; $this->trigger = ''; $this->xprop = ''; if (defined('ICAL_LANG') && !isset($config['language'])) { $config['language'] = ICAL_LANG; } if (!isset($config['allowEmpty'])) { $config['allowEmpty'] = TRUE; } if (!isset($config['nl'])) { $config['nl'] = "\r\n"; } if (!isset($config['format'])) { $config['format'] = 'iCal'; } if (!isset($config['delimiter'])) { $config['delimiter'] = DIRECTORY_SEPARATOR; } $this->setConfig($config); }
/** * constructor for calendar component VFREEBUSY object * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.8.2 - 2011-05-01 * @param array $config * @uses vjournal::calendarComponent() * @uses vjournal::$attendee * @uses vjournal::$comment * @uses vjournal::$contact * @uses vjournal::$dtend * @uses vjournal::$dtstart * @uses vjournal::$dtduration * @uses vjournal::$organizer * @uses vjournal::$requeststatus * @uses vjournal::$url * @uses vjournal::$xprop * @uses calendarComponent::setConfig() */ function __construct($config = array()) { parent::__construct(); $this->attendee = ''; $this->comment = ''; $this->contact = ''; $this->dtend = ''; $this->dtstart = ''; $this->duration = ''; $this->freebusy = ''; $this->organizer = ''; $this->requeststatus = ''; $this->url = ''; $this->xprop = ''; if (defined('ICAL_LANG') && !isset($config['language'])) { $config['language'] = ICAL_LANG; } if (!isset($config['allowEmpty'])) { $config['allowEmpty'] = TRUE; } if (!isset($config['nl'])) { $config['nl'] = "\r\n"; } if (!isset($config['format'])) { $config['format'] = 'iCal'; } if (!isset($config['delimiter'])) { $config['delimiter'] = DIRECTORY_SEPARATOR; } $this->setConfig($config); }
/** * constructor for calendar component VJOURNAL object * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.8.2 - 2011-05-01 * @param array $config * @uses vjournal::calendarComponent() * @uses vjournal::$attach * @uses vjournal::$attendee * @uses vjournal::$categories * @uses vjournal::$class * @uses vjournal::$comment * @uses vjournal::$contact * @uses vjournal::$created * @uses vjournal::$description * @uses vjournal::$dtstart * @uses vjournal::$exdate * @uses vjournal::$exrule * @uses vjournal::$lastmodified * @uses vjournal::$organizer * @uses vjournal::$rdate * @uses vjournal::$recurrenceid * @uses vjournal::$relatedto * @uses vjournal::$requeststatus * @uses vjournal::$rrule * @uses vjournal::$sequence * @uses vjournal::$status * @uses vjournal::$summary * @uses vjournal::$url * @uses vjournal::$xprop * @uses calendarComponent::setConfig() */ function __construct($config = array()) { parent::__construct(); $this->attach = ''; $this->attendee = ''; $this->categories = ''; $this->class = ''; $this->comment = ''; $this->contact = ''; $this->created = ''; $this->description = ''; $this->dtstart = ''; $this->exdate = ''; $this->exrule = ''; $this->lastmodified = ''; $this->organizer = ''; $this->rdate = ''; $this->recurrenceid = ''; $this->relatedto = ''; $this->requeststatus = ''; $this->rrule = ''; $this->sequence = ''; $this->status = ''; $this->summary = ''; $this->url = ''; $this->xprop = ''; if (defined('ICAL_LANG') && !isset($config['language'])) { $config['language'] = ICAL_LANG; } if (!isset($config['allowEmpty'])) { $config['allowEmpty'] = TRUE; } if (!isset($config['nl'])) { $config['nl'] = "\r\n"; } if (!isset($config['format'])) { $config['format'] = 'iCal'; } if (!isset($config['delimiter'])) { $config['delimiter'] = DIRECTORY_SEPARATOR; } $this->setConfig($config); }
/** * set calendar component property x-prop * * @author Kjell-Inge Gustafsson <*****@*****.**> * @since 2.4.11 - 2008-11-04 * @param string $label * @param mixed $value * @param array $params optional * @return bool */ function setXprop($label, $value, $params = FALSE) { if (empty($label)) { return; } if (empty($value)) { if ($this->getConfig('allowEmpty')) { $value = null; } else { return FALSE; } } $xprop = array('value' => $value); $toolbox = new calendarComponent(); $xprop['params'] = $toolbox->_setParams($params); if (!is_array($this->xprop)) { $this->xprop = array(); } $this->xprop[strtoupper($label)] = $xprop; return TRUE; }
/** * parse component unparsed data into properties * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.15.10 - 2012-10-28 * @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] = calendarComponent::_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, calendarComponent::_strunrep($line), $propattr); break; case 'REQUEST-STATUS': $values = explode(';', $line, 3); $values[1] = !isset($values[1]) ? null : calendarComponent::_strunrep($values[1]); $values[2] = !isset($values[2]) ? null : calendarComponent::_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 = calendarComponent::_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; }
/** * creates formatted output for calendar property x-prop, iCal format only * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.16.2 - 2012-12-18 * @return string */ function createXprop() { if (empty($this->xprop) || !is_array($this->xprop)) { return FALSE; } $output = null; $toolbox = new calendarComponent(); $toolbox->setConfig($this->getConfig()); foreach ($this->xprop as $label => $xpropPart) { if (!isset($xpropPart['value']) || empty($xpropPart['value']) && !is_numeric($xpropPart['value'])) { $output .= $toolbox->_createElement($label); continue; } $attributes = $toolbox->_createParams($xpropPart['params'], array('LANGUAGE')); if (is_array($xpropPart['value'])) { foreach ($xpropPart['value'] as $pix => $theXpart) { $xpropPart['value'][$pix] = iCalUtilityFunctions::_strrep($theXpart, $this->format, $this->nl); } $xpropPart['value'] = implode(',', $xpropPart['value']); } else { $xpropPart['value'] = iCalUtilityFunctions::_strrep($xpropPart['value'], $this->format, $this->nl); } $output .= $toolbox->_createElement($label, $attributes, $xpropPart['value']); if (is_array($toolbox->xcaldecl) && 0 < count($toolbox->xcaldecl)) { foreach ($toolbox->xcaldecl as $localxcaldecl) { $this->xcaldecl[] = $localxcaldecl; } } } return $output; }
/** * parse component unparsed data into properties * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.8.2 - 2011-05-21 * @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) { if (!empty($unparsedtext)) { $nl = $this->getConfig('nl'); if (is_array($unparsedtext)) { $unparsedtext = implode('\\n' . $nl, $unparsedtext); } /* fix line folding */ $eolchars = array("\r\n", "\n\r", "\n", "\r"); // check all line endings $EOLmark = FALSE; foreach ($eolchars as $eolchar) { if (!$EOLmark && FALSE !== strpos($unparsedtext, $eolchar)) { $unparsedtext = str_replace($eolchar . " ", '', $unparsedtext); $unparsedtext = str_replace($eolchar . "\t", '', $unparsedtext); if ($eolchar != $nl) { $unparsedtext = str_replace($eolchar, $nl, $unparsedtext); } $EOLmark = TRUE; } } $tmp = explode($nl, $unparsedtext); $unparsedtext = array(); foreach ($tmp as $tmpr) { if (!empty($tmpr)) { $unparsedtext[] = $tmpr; } } } elseif (!isset($this->unparsed)) { $unparsedtext = array(); } else { $unparsedtext = $this->unparsed; } $this->unparsed = array(); $comp =& $this; $config = $this->getConfig(); foreach ($unparsedtext as $line) { // echo $comp->objName.": $line<br />"; // test ### if (in_array(strtoupper(substr($line, 0, 6)), array('END:VA', 'END:ST', 'END:DA'))) { $this->components[] = $comp->copy(); } elseif ('END:' == strtoupper(substr($line, 0, 4))) { break; } elseif ('BEGIN:VALARM' == strtoupper(substr($line, 0, 12))) { $comp = new valarm($config); } elseif ('BEGIN:STANDARD' == strtoupper(substr($line, 0, 14))) { $comp = new vtimezone('standard', $config); } elseif ('BEGIN:DAYLIGHT' == strtoupper(substr($line, 0, 14))) { $comp = new vtimezone('daylight', $config); } elseif ('BEGIN:' == strtoupper(substr($line, 0, 6))) { continue; } else { $comp->unparsed[] = $line; // echo $comp->objName.": $line<br />\n"; // test ### } } unset($config); // echo $this->objName.'<br />'.var_export( $this->unparsed, TRUE )."<br />\n"; // test ### /* 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(); foreach ($this->unparsed as $line) { $newProp = FALSE; foreach ($propnames as $propname) { if ($propname == strtolower(substr($line, 0, strlen($propname)))) { $newProp = TRUE; break; } } if ($newProp) { if (-1 < $lastix) { $proprows[$lastix] = $proprows[$lastix]; } $newProp = FALSE; $lastix++; $proprows[$lastix] = $line; } else { $proprows[$lastix] .= '!"#¤%&/()=?' . $line; } } /* parse each property 'line' */ foreach ($proprows as $line) { $line = str_replace('!"#¤%&/()=? ', '', $line); $line = str_replace('!"#¤%&/()=?', '', $line); if ('\\n' == substr($line, -2)) { $line = substr($line, 0, strlen($line) - 2); } /* get propname, (problem with x-properties, otherwise in previous loop) */ $cix = $propname = null; for ($cix = 0, $clen = strlen($line); $cix < $clen; $cix++) { if (in_array($line[$cix], array(':', ';'))) { break; } else { $propname .= $line[$cix]; } } if ('x-' == substr($propname, 0, 2) || 'X-' == substr($propname, 0, 2)) { $propname2 = $propname; $propname = 'X-'; } /* rest of the line is opt.params and value */ $line = substr($line, $cix); /* separate attributes from value */ $attr = array(); $attrix = -1; $clen = strlen($line); for ($cix = 0; $cix < $clen; $cix++) { if (':' == $line[$cix] && '://' != substr($line, $cix, 3) && !in_array(strtolower(substr($line, $cix - 3, 4)), array('fax:', 'cid:', 'sms:', 'tel:', 'urn:')) && !in_array(strtolower(substr($line, $cix - 4, 5)), array('crid:', 'news:', 'pres:')) && 'mailto:' != strtolower(substr($line, $cix - 6, 7))) { $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; } } if (';' == $line[$cix]) { $attr[++$attrix] = null; } else { $attr[$attrix] .= $line[$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) { $attr2 = explode(',', $attr); if (1 < count($attr2)) { $propattr[$pix] = $attr2; } } $this->setProperty($propname, $line, $propattr); break; case 'CATEGORIES': case 'RESOURCES': if (FALSE !== strpos($line, ',')) { $content = explode(',', $line); $clen = count($content); for ($cix = 0; $cix < $clen; $cix++) { if ("\\" == substr($content[$cix], -1)) { $content[$cix] .= ',' . $content[$cix + 1]; unset($content[$cix + 1]); $cix++; } } if (1 < count($content)) { $content = array_values($content); foreach ($content as $cix => $contentPart) { $content[$cix] = calendarComponent::_strunrep($contentPart); } $this->setProperty($propname, $content, $propattr); break; } else { $line = reset($content); } } case 'X-': $propname = isset($propname2) ? $propname2 : $propname; case 'COMMENT': case 'CONTACT': case 'DESCRIPTION': case 'LOCATION': case 'SUMMARY': if (empty($line)) { $propattr = null; } $this->setProperty($propname, calendarComponent::_strunrep($line), $propattr); unset($propname2); break; case 'REQUEST-STATUS': $values = explode(';', $line, 3); $values[1] = !isset($values[1]) ? null : calendarComponent::_strunrep($values[1]); $values[2] = !isset($values[2]) ? null : calendarComponent::_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 = calendarComponent::_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; }
/** * constructor for calendar component VTIMEZONE object * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.8.2 - 2011-05-01 * @param mixed $timezonetype default FALSE ( STANDARD / DAYLIGHT ) * @param array $config * @uses vtimezone::$timezonetype * @uses vtimezone::calendarComponent() * @uses vtimezone::$comment * @uses vtimezone::$dtstart * @uses vtimezone::$lastmodified * @uses vtimezone::$rdate * @uses vtimezone::$rrule * @uses vtimezone::$tzid * @uses vtimezone::$tzname * @uses vtimezone::$tzoffsetfrom * @uses vtimezone::$tzoffsetto * @uses vtimezone::$tzurl * @uses vtimezone::$xprop * @uses vtimezone::$components * @uses calendarComponent::setConfig() */ function __construct($timezonetype = FALSE, $config = array()) { if (is_array($timezonetype)) { $config = $timezonetype; $timezonetype = FALSE; } if (!$timezonetype) { $this->timezonetype = 'VTIMEZONE'; } else { $this->timezonetype = strtoupper($timezonetype); } parent::__construct(); $this->comment = ''; $this->dtstart = ''; $this->lastmodified = ''; $this->rdate = ''; $this->rrule = ''; $this->tzid = ''; $this->tzname = ''; $this->tzoffsetfrom = ''; $this->tzoffsetto = ''; $this->tzurl = ''; $this->xprop = ''; $this->components = array(); if (defined('ICAL_LANG') && !isset($config['language'])) { $config['language'] = ICAL_LANG; } if (!isset($config['allowEmpty'])) { $config['allowEmpty'] = TRUE; } if (!isset($config['nl'])) { $config['nl'] = "\r\n"; } if (!isset($config['format'])) { $config['format'] = 'iCal'; } if (!isset($config['delimiter'])) { $config['delimiter'] = DIRECTORY_SEPARATOR; } $this->setConfig($config); }
/** * validDate * * convert input parameters to (valid) iCalcreator date in array format (or FALSE) * if $utc=TRUE and $tz = utc offset ([[+/]-]HHmm) input (local) date array + UTC offset * returns ouput in UTC format date * * @author Kjell-Inge Gustafsson <*****@*****.**> * @since 1.x.x - 2007-05-12 * @param mixed $year * @param mixed $month optional * @param int $day optional * @param int $hour optional * @param int $min optional * @param int $sec optional * @param mixed $tz optional * @param bool $utc optional * @return bool false / array $date */ function validDate($year, $month = FALSE, $day = FALSE, $hour = FALSE, $min = FALSE, $sec = FALSE, $tz = FALSE, $utc = FALSE) { $input = array(); $toolbox = new calendarComponent(); $parno = null; if (is_int($year) && is_int($month) && is_int($day)) { $input['year'] = $year; $input['month'] = $month; $input['day'] = $day; if ($hour !== FALSE || $min !== FALSE || $sec !== FALSE) { $parno = 6; if ($hour !== FALSE) { $input['hour'] = $hour; } if ($min !== FALSE) { $input['min'] = $min; } if ($sec !== FALSE) { $input['sec'] = $sec; } } if ($tz !== FALSE) { $parno = 7; $input['tz'] = $tz; } elseif (!$parno) { $parno = 3; } $input = $toolbox->_date_time_array($input, $parno); } elseif (is_array($year) && isset($year['timestamp'])) { $input = $toolbox->_date_time_string(date('Y-m-d H:i:s', $year['timestamp']), 6); $input['tz'] = isset($year['tz']) ? $year['tz'] : null; $utc = TRUE === $month ? TRUE : FALSE; } elseif (is_array($year) && in_array(count($year), array(3, 4, 6, 7))) { if (isset($year['tz']) || 4 == count($year) || 7 == count($year)) { $parno = 7; } elseif (isset($year['hour']) || isset($year['min']) || isset($year['sec']) || 6 == count($year)) { $parno = 6; } else { $parno = 3; } $input = $toolbox->_date_time_array($year, $parno); $utc = TRUE === $month ? TRUE : FALSE; } elseif (8 <= strlen(trim($year))) { // ex. 2006-08-03 10:12:18 $input = $toolbox->_date_time_string($year); $utc = TRUE === $month ? TRUE : FALSE; } else { return FALSE; } if (!checkdate($input['month'], $input['day'], $input['year'])) { return FALSE; } if (isset($input['hour']) && (0 > $input['hour'] || 23 < $input['hour'])) { return FALSE; } if (isset($input['min']) && (0 > $input['min'] || 59 < $input['min'])) { return FALSE; } if (isset($input['sec']) && (0 > $input['sec'] || 59 < $input['sec'])) { return FALSE; } if (isset($input['tz']) && '' < trim($input['tz'])) { $input['tz'] = trim($input['tz']); if (ctype_digit($input['tz'][1])) { // only numeric tz=offset $offset = 0; if (ctype_digit($input['tz'][0])) { $input['tz'] = '+' . $input['tz']; } if (5 == strlen($input['tz']) && '0000' <= substr($input['tz'], -4) && '9999' >= substr($input['tz'], -4) && ('+' == substr($input['tz'], 0, 1) || '-' == substr($input['tz'], 0, 1))) { $hours2sec = substr($input['tz'], 1, 2) * 3600; $min2sec = substr($input['tz'], -2) * 60; $sign = substr($input['tz'], 0, 1); $offset = $sign . '1' * ($hours2sec + $min2sec); } elseif (7 == strlen($input['tz']) && '000000' <= substr($input['tz'], -6) && '999999' >= substr($input['tz'], -6) && ('+' == substr($input['tz'], 0, 1) || '-' == substr($input['tz'], 0, 1))) { $hours2sec = substr($input['tz'], 1, 2) * 3600; $min2sec = substr($input['tz'], 3, 2) * 60; $sec = substr($input['tz'], -2); $sign = substr($input['tz'], 0, 1); $offset = $sign . '1' * ($hours2sec + $min2sec + $sec); } if (0 != $offset) { if (!isset($input['hour'])) { $input['hour'] = 0; } if (!isset($input['min'])) { $input['min'] = 0; } if (!isset($input['sec'])) { $input['sec'] = 0; } $input = date('Y-m-d H:i:s\\Z', mktime($input['hour'], $input['min'], $input['sec'] + $offset, $input['month'], $input['day'], $input['year'])); $parno = $utc ? 7 : 6; $input = $toolbox->_date_time_string($input, $parno); if (!$utc && isset($input['tz']) && 'Z' == $input['tz']) { unset($input['tz']); } } } } return $input; }
/** * parse iCal text/file into iCal_Vcalendar, components, properties and parameters * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.8.2 - 2011-05-21 * @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; } /* identify BEGIN:VCALENDAR, MUST be first row */ if ('BEGIN:VCALENDAR' != strtoupper(substr($rows, 0, 15))) { return FALSE; } /* err 8 */ /* fix line folding */ $eolchars = array("\r\n", "\n\r", "\n", "\r"); // check all line endings $EOLmark = FALSE; foreach ($eolchars as $eolchar) { if (!$EOLmark && FALSE !== strpos($rows, $eolchar)) { $rows = str_replace($eolchar . " ", '', $rows); $rows = str_replace($eolchar . "\t", '', $rows); if ($eolchar != $nl) { $rows = str_replace($eolchar, $nl, $rows); } $EOLmark = TRUE; } } $tmp = explode($nl, $rows); $rows = array(); foreach ($tmp as $tmpr) { if (!empty($tmpr)) { $rows[] = $tmpr; } } /* skip trailing empty lines */ $lix = count($rows) - 1; while (empty($rows[$lix]) && 0 < $lix) { $lix -= 1; } /* identify ending END:VCALENDAR row, MUST be last row */ if ('END:VCALENDAR' != strtoupper(substr($rows[$lix], 0, 13))) { return FALSE; } /* err 9 */ if (3 > count($rows)) { return FALSE; } /* err 10 */ $comp =& $this; $calsync = 0; /* identify components and update unparsed data within component */ $config = $this->getConfig(); foreach ($rows as $line) { if ('' == trim($line)) { continue; } if ('BEGIN:VCALENDAR' == strtoupper(substr($line, 0, 15))) { $calsync++; continue; } elseif ('END:VCALENDAR' == strtoupper(substr($line, 0, 13))) { $calsync--; break; } elseif (1 != $calsync) { return FALSE; } elseif (in_array(strtoupper(substr($line, 0, 6)), array('END:VE', 'END:VF', 'END:VJ', 'END:VT'))) { $this->components[] = $comp->copy(); continue; } if ('BEGIN:VEVENT' == strtoupper(substr($line, 0, 12))) { $comp = new vevent($config); } elseif ('BEGIN:VFREEBUSY' == strtoupper(substr($line, 0, 15))) { $comp = new vfreebusy($config); } elseif ('BEGIN:VJOURNAL' == strtoupper(substr($line, 0, 14))) { $comp = new vjournal($config); } elseif ('BEGIN:VTODO' == strtoupper(substr($line, 0, 11))) { $comp = new vtodo($config); } elseif ('BEGIN:VTIMEZONE' == strtoupper(substr($line, 0, 15))) { $comp = new vtimezone($config); } else { /* update component with unparsed data */ $comp->unparsed[] = $line; } } // end - foreach( rows.. . unset($config); /* 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 */ $lastix = -1; $propnames = array('calscale', 'method', 'prodid', 'version', 'x-'); $proprows = array(); foreach ($this->unparsed as $line) { if ('' == trim($line)) { continue; } $newProp = FALSE; foreach ($propnames as $propname) { if ($propname == strtolower(substr($line, 0, strlen($propname)))) { $newProp = TRUE; break; } } if ($newProp) { $newProp = FALSE; $lastix++; $proprows[$lastix] = $line; } else { $proprows[$lastix] .= '!"#¤%&/()=?' . $line; } } foreach ($proprows as $line) { $line = str_replace('!"#¤%&/()=? ', '', $line); $line = str_replace('!"#¤%&/()=?', '', $line); if ('\\n' == substr($line, -2)) { $line = substr($line, 0, strlen($line) - 2); } /* get property name */ $cix = $propname = null; for ($cix = 0, $clen = strlen($line); $cix < $clen; $cix++) { if (in_array($line[$cix], array(':', ';'))) { break; } else { $propname .= $line[$cix]; } } /* ignore version/prodid properties */ if (in_array(strtoupper($propname), array('VERSION', 'PRODID'))) { continue; } $line = substr($line, $cix); /* separate attributes from value */ $attr = array(); $attrix = -1; $strlen = strlen($line); for ($cix = 0; $cix < $strlen; $cix++) { if (':' == $line[$cix] && '://' != substr($line, $cix, 3) && !in_array(strtolower(substr($line, $cix - 3, 4)), array('fax:', 'cid:', 'sms:', 'tel:', 'urn:')) && !in_array(strtolower(substr($line, $cix - 4, 5)), array('crid:', 'news:', 'pres:')) && 'mailto:' != strtolower(substr($line, $cix - 6, 7))) { $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]) { $attr[++$attrix] = null; } else { $attr[$attrix] .= $line[$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 = explode(',', $line); $clen = count($content); for ($cix = 0; $cix < $clen; $cix++) { if ("\\" == substr($content[$cix], -1)) { $content[$cix] .= ',' . $content[$cix + 1]; unset($content[$cix + 1]); $cix++; } } if (1 < count($content)) { foreach ($content as $cix => $contentPart) { $content[$cix] = calendarComponent::_strunrep($contentPart); } $this->setProperty($propname, $content, $propattr); continue; } else { $line = reset($content); } $line = calendarComponent::_strunrep($line); } $this->setProperty($propname, trim($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; }
/** * set calendar component property x-prop * * @author Kjell-Inge Gustafsson <*****@*****.**> * @since 2.0.7 - 2007-06-21 * @param string $label * @param mixed $value * @param array $params optional * @return void */ function setXprop($label, $value, $params = FALSE) { if (empty($label) || empty($value)) { return; } $xprop = array('value' => $value); $toolbox = new calendarComponent(); $xprop['params'] = $toolbox->_setParams($params); $this->xprop[$label] = $xprop; }