Пример #1
0
 /**
  * Parses a string containing vCalendar data.
  *
  * @todo This method doesn't work well at all, if $base is VCARD.
  *
  * @param string $text     The data to parse.
  * @param string $base     The type of the base object.
  * @param boolean $clear   If true clears this object before parsing.
  *
  * @return boolean  True on successful import, false otherwise.
  * @throws Horde_Icalendar_Exception
  */
 public function parsevCalendar($text, $base = 'VCALENDAR', $clear = true)
 {
     if ($clear) {
         $this->clear();
     }
     $text = Horde_String::trimUtf8Bom($text);
     if (preg_match('/^BEGIN:' . $base . '(.*)^END:' . $base . '/ism', $text, $matches)) {
         $container = true;
         $vCal = $matches[1];
     } else {
         // Text isn't enclosed in BEGIN:VCALENDAR
         // .. END:VCALENDAR. We'll try to parse it anyway.
         $container = false;
         $vCal = $text;
     }
     $vCal = trim($vCal);
     // Extract all subcomponents.
     $matches = $components = null;
     if (preg_match_all('/^BEGIN:(.*)\\s*?(\\r\\n|\\r|\\n)(.*)^END:\\1\\s*?/Uims', $vCal, $components)) {
         foreach ($components[0] as $key => $data) {
             // Remove from the vCalendar data.
             $vCal = str_replace($data, '', $vCal);
         }
     } elseif (!$container) {
         return false;
     }
     // Unfold "quoted printable" folded lines like:
     //  BODY;ENCODING=QUOTED-PRINTABLE:=
     //  another=20line=
     //  last=20line
     while (preg_match_all('/^([^:]+;\\s*(ENCODING=)?QUOTED-PRINTABLE(.*=\\r?\\n)+(.*[^=])?(\\r?\\n|$))/mU', $vCal, $matches)) {
         foreach ($matches[1] as $s) {
             $r = preg_replace('/=\\r?\\n/', '', $s);
             $vCal = str_replace($s, $r, $vCal);
         }
     }
     // Unfold any folded lines.
     $vCal = preg_replace('/[\\r\\n]+[ \\t]/', '', $vCal);
     // Parse the remaining attributes.
     if (preg_match_all('/^((?:[^":]+|(?:"[^"]*")+)*):([^\\r\\n]*)\\r?$/m', $vCal, $matches)) {
         foreach ($matches[0] as $attribute) {
             preg_match('/([^;^:]*)((;(?:[^":]+|(?:"[^"]*")+)*)?):([^\\r\\n]*)[\\r\\n]*/', $attribute, $parts);
             $tag = trim(preg_replace('/^.*\\./', '', Horde_String::upper($parts[1])));
             $value = $parts[4];
             $params = array();
             // Parse parameters.
             if (!empty($parts[2])) {
                 preg_match_all('/;(([^;=]*)(=("[^"]*"|[^;]*))?)/', $parts[2], $param_parts);
                 foreach ($param_parts[2] as $key => $paramName) {
                     $paramName = Horde_String::upper($paramName);
                     $paramValue = $param_parts[4][$key];
                     if ($paramName == 'TYPE') {
                         $paramValue = preg_split('/(?<!\\\\),/', $paramValue);
                         if (count($paramValue) == 1) {
                             $paramValue = $paramValue[0];
                         }
                     }
                     if (is_string($paramValue)) {
                         if (preg_match('/"([^"]*)"/', $paramValue, $parts)) {
                             $paramValue = $parts[1];
                         }
                     } else {
                         foreach ($paramValue as $k => $tmp) {
                             if (preg_match('/"([^"]*)"/', $tmp, $parts)) {
                                 $paramValue[$k] = $parts[1];
                             }
                         }
                     }
                     if (isset($params[$paramName])) {
                         if (is_array($params[$paramName])) {
                             $params[$paramName][] = $paramValue;
                         } else {
                             $params[$paramName] = array($params[$paramName], $paramValue);
                         }
                     } else {
                         $params[$paramName] = $paramValue;
                     }
                 }
             }
             // Charset and encoding handling.
             if (isset($params['ENCODING']) && Horde_String::upper($params['ENCODING']) == 'QUOTED-PRINTABLE' || isset($params['QUOTED-PRINTABLE'])) {
                 $value = quoted_printable_decode($value);
                 if (isset($params['CHARSET'])) {
                     $value = Horde_String::convertCharset($value, $params['CHARSET'], 'UTF-8');
                 }
             } elseif (isset($params['CHARSET'])) {
                 $value = Horde_String::convertCharset($value, $params['CHARSET'], 'UTF-8');
             }
             // Get timezone info for date fields from $params.
             $tzid = isset($params['TZID']) ? trim($params['TZID'], '\\"') : false;
             switch ($tag) {
                 // Date fields.
                 case 'COMPLETED':
                 case 'CREATED':
                 case 'LAST-MODIFIED':
                 case 'X-MOZ-LASTACK':
                 case 'X-MOZ-SNOOZE-TIME':
                     $this->setAttribute($tag, $this->_parseDateTime($value, $tzid), $params);
                     break;
                 case 'BDAY':
                 case 'X-ANNIVERSARY':
                     $this->setAttribute($tag, $this->_parseDate($value), $params);
                     break;
                 case 'DTEND':
                 case 'DTSTART':
                 case 'DTSTAMP':
                 case 'DUE':
                 case 'AALARM':
                 case 'RECURRENCE-ID':
                     // types like AALARM may contain additional data after a ;
                     // ignore these.
                     $ts = explode(';', $value);
                     if (isset($params['VALUE']) && $params['VALUE'] == 'DATE') {
                         $this->setAttribute($tag, $this->_parseDate($ts[0]), $params);
                     } else {
                         $this->setAttribute($tag, $this->_parseDateTime($ts[0], $tzid), $params);
                     }
                     break;
                 case 'TRIGGER':
                     if (isset($params['VALUE']) && $params['VALUE'] == 'DATE-TIME') {
                         $this->setAttribute($tag, $this->_parseDateTime($value, $tzid), $params);
                     } else {
                         $this->setAttribute($tag, $this->_parseDuration($value), $params);
                     }
                     break;
                     // Comma seperated dates.
                 // Comma seperated dates.
                 case 'EXDATE':
                 case 'RDATE':
                     if (!strlen($value)) {
                         break;
                     }
                     $dates = array();
                     $separator = $this->_oldFormat ? ';' : ',';
                     preg_match_all('/' . $separator . '([^' . $separator . ']*)/', $separator . $value, $values);
                     foreach ($values[1] as $value) {
                         $stamp = $this->_parseDateTime($value);
                         if (!is_int($stamp)) {
                             continue;
                         }
                         $dates[] = array('year' => date('Y', $stamp), 'month' => date('m', $stamp), 'mday' => date('d', $stamp));
                     }
                     $this->setAttribute($tag, isset($dates[0]) ? $dates[0] : null, $params, true, $dates);
                     break;
                     // Duration fields.
                 // Duration fields.
                 case 'DURATION':
                     $this->setAttribute($tag, $this->_parseDuration($value), $params);
                     break;
                     // Period of time fields.
                 // Period of time fields.
                 case 'FREEBUSY':
                     $periods = array();
                     preg_match_all('/,([^,]*)/', ',' . $value, $values);
                     foreach ($values[1] as $value) {
                         $periods[] = $this->_parsePeriod($value);
                     }
                     $this->setAttribute($tag, isset($periods[0]) ? $periods[0] : null, $params, true, $periods);
                     break;
                     // UTC offset fields.
                 // UTC offset fields.
                 case 'TZOFFSETFROM':
                 case 'TZOFFSETTO':
                     $this->setAttribute($tag, $this->_parseUtcOffset($value), $params);
                     break;
                     // Integer fields.
                 // Integer fields.
                 case 'PERCENT-COMPLETE':
                 case 'PRIORITY':
                 case 'REPEAT':
                 case 'SEQUENCE':
                     $this->setAttribute($tag, intval($value), $params);
                     break;
                     // Geo fields.
                 // Geo fields.
                 case 'GEO':
                     if ($value) {
                         if ($this->_oldFormat) {
                             $floats = explode(',', $value);
                             $value = array('latitude' => floatval($floats[1]), 'longitude' => floatval($floats[0]));
                         } else {
                             $floats = explode(';', $value);
                             $value = array('latitude' => floatval($floats[0]), 'longitude' => floatval($floats[1]));
                         }
                     }
                     $this->setAttribute($tag, $value, $params);
                     break;
                     // Recursion fields.
                 // Recursion fields.
                 case 'EXRULE':
                 case 'RRULE':
                     $this->setAttribute($tag, trim($value), $params);
                     break;
                     // ADR, ORG and N are lists seperated by unescaped semicolons
                     // with a specific number of slots.
                 // ADR, ORG and N are lists seperated by unescaped semicolons
                 // with a specific number of slots.
                 case 'ADR':
                 case 'N':
                 case 'ORG':
                     $value = trim($value);
                     // As of rfc 2426 2.4.2 semicolon, comma, and colon must
                     // be escaped (comma is unescaped after splitting below).
                     $value = str_replace(array('\\n', '\\N', '\\;', '\\:'), array($this->_newline, $this->_newline, ';', ':'), $value);
                     // Split by unescaped semicolons:
                     $values = preg_split('/(?<!\\\\);/', $value);
                     $value = str_replace('\\;', ';', $value);
                     $values = str_replace('\\;', ';', $values);
                     $this->setAttribute($tag, trim($value), $params, true, $values);
                     break;
                     // String fields.
                 // String fields.
                 default:
                     if ($this->_oldFormat) {
                         // vCalendar 1.0 and vCard 2.1 only escape semicolons
                         // and use unescaped semicolons to create lists.
                         $value = trim($value);
                         // Split by unescaped semicolons:
                         $values = preg_split('/(?<!\\\\);/', $value);
                         $value = str_replace('\\;', ';', $value);
                         $values = str_replace('\\;', ';', $values);
                         $this->setAttribute($tag, trim($value), $params, true, $values);
                     } else {
                         $value = trim($value);
                         // As of rfc 2426 2.4.2 semicolon, comma, and colon
                         // must be escaped (comma is unescaped after splitting
                         // below).
                         $value = str_replace(array('\\n', '\\N', '\\;', '\\:', '\\\\'), array($this->_newline, $this->_newline, ';', ':', '\\'), $value);
                         // Split by unescaped commas.
                         $values = preg_split('/(?<!\\\\),/', $value);
                         $value = str_replace('\\,', ',', $value);
                         $values = str_replace('\\,', ',', $values);
                         $this->setAttribute($tag, trim($value), $params, true, $values);
                     }
                     break;
             }
         }
     }
     // Process all components.
     if ($components) {
         // vTimezone components are processed first. They are
         // needed to process vEvents that may use a TZID.
         foreach ($components[0] as $key => $data) {
             $type = trim($components[1][$key]);
             if ($type != 'VTIMEZONE') {
                 continue;
             }
             $component = $this->newComponent($type, $this);
             if ($component === false) {
                 throw new Horde_Icalendar_Exception('Unable to create object for type ' . $type);
             }
             $component->parsevCalendar($data, $type);
             $this->addComponent($component);
             // Remove from the vCalendar data.
             $vCal = str_replace($data, '', $vCal);
         }
         // Now process the non-vTimezone components.
         foreach ($components[0] as $key => $data) {
             $type = trim($components[1][$key]);
             if ($type == 'VTIMEZONE') {
                 continue;
             }
             $component = $this->newComponent($type, $this);
             if ($component === false) {
                 throw new Horde_Icalendar_Exception('Unable to create object for type ' . $type);
             }
             $component->parsevCalendar($data, $type);
             $this->addComponent($component);
         }
     }
     return true;
 }