/** * Parse a string containing vCalendar data. * * @access private * @param String $data The data to parse * */ function parse($data, $ignore) { global $_calendar_error, $PERS_TERMIN_KAT; // match categories $studip_categories = array(); $i = 1; foreach ($PERS_TERMIN_KAT as $cat) { $studip_categories[strtolower($cat['name'])] = $i++; } // Unfold any folded lines // the CR is optional for files imported from Korganizer (non-standard) $data = preg_replace('/\x0D?\x0A[\x20\x09]/', '', $data); // UTF-8 decoding $v_calendar = utf8_decode($data); if (!preg_match('/BEGIN:VCALENDAR(\r\n|\r|\n)([\W\w]*)END:VCALENDAR\r?\n?/', $v_calendar, $matches)) { $_calendar_error->throwError(ErrorHandler::ERROR_CRITICAL, _("Die Import-Datei ist keine gültige iCalendar-Datei!")); return false; } // client identifier if (!$this->_parseClientIdentifier($matches[2])) { return false; } // All sub components if (!preg_match_all('/BEGIN:VEVENT(\r\n|\r|\n)([\w\W]*?)END:VEVENT(\r\n|\r|\n)/', $matches[2], $v_events)) { $_calendar_error->throwError(ErrorHandler::ERROR_MESSAGE, _("Die importierte Datei enthält keine Termine.")); return true; } if ($this->count) { $this->count = 0; } foreach ($v_events[2] as $v_event) { $properties['CLASS'] = 'PRIVATE'; // Parse the remain attributes if (preg_match_all('/(.*):(.*)(\r|\n)+/', $v_event, $matches)) { $properties = array(); $check = array(); foreach ($matches[0] as $property) { preg_match('/([^;^:]*)((;[^:]*)?):(.*)/', $property, $parts); $tag = $parts[1]; $value = $parts[4]; $params = array(); // skip seminar events if ((!$this->import_sem) && $tag == 'UID') { if (strpos($value, 'Stud.IP-SEM') === 0) { continue 2; } } if (!empty($parts[2])) { preg_match_all('/;(([^;=]*)(=([^;]*))?)/', $parts[2], $param_parts); foreach ($param_parts[2] as $key => $param_name) $params[strtoupper($param_name)] = strtoupper($param_parts[4][$key]); if ($params['ENCODING']) { switch ($params['ENCODING']) { case 'QUOTED-PRINTABLE': $value = $this->_qp_decode($value); break; case 'BASE64': $value = base64_decode($value); break; } } } switch ($tag) { // text fields case 'DESCRIPTION': case 'SUMMARY': case 'LOCATION': $value = preg_replace('/\\\\,/', ',', $value); $value = preg_replace('/\\\\n/', "\n", $value); $properties[$tag] = trim($value); break; case 'CATEGORIES': $categories = array(); $properties['STUDIP_CATEGORY'] = null; foreach (explode(',', $value) as $category) { if (!$studip_categories[strtolower($category)]) { $categories[] = $category; } else if (!$properties['STUDIP_CATEGORY']) { $properties['STUDIP_CATEGORY'] = $studip_categories[strtolower($category)]; } } $properties[$tag] = implode(',', $categories); break; // Date fields case 'DCREATED': // vCalendar property name for "CREATED" $tag = "CREATED"; case 'DTSTAMP': case 'COMPLETED': case 'CREATED': case 'LAST-MODIFIED': $properties[$tag] = $this->_parseDateTime($value); break; case 'DTSTART': case 'DTEND': // checking for day events if ($params['VALUE'] == 'DATE') $check['DAY_EVENT'] = true; case 'DUE': case 'RECURRENCE-ID': $properties[$tag] = $this->_parseDateTime($value); break; case 'RDATE': if (array_key_exists('VALUE', $params)) { if ($params['VALUE'] == 'PERIOD') { $properties[$tag] = $this->_parsePeriod($value); } else { $properties[$tag] = $this->_parseDateTime($value); } } else { $properties[$tag] = $this->_parseDateTime($value); } break; case 'TRIGGER': if (array_key_exists('VALUE', $params)) { if ($params['VALUE'] == 'DATE-TIME') { $properties[$tag] = $this->_parseDateTime($value); } else { $properties[$tag] = $this->_parseDuration($value); } } else { $properties[$tag] = $this->_parseDuration($value); } break; case 'EXDATE': $properties[$tag] = array(); // comma seperated dates $values = array(); $dates = array(); preg_match_all('/,([^,]*)/', ',' . $value, $values); foreach ($values as $value) { if (array_key_exists('VALUE', $params)) { if ($params['VALUE'] == 'DATE-TIME') { $dates[] = $this->_parseDateTime($value); } else if ($params['VALUE'] == 'DATE') { $dates[] = $this->_parseDate($value); } } else { $dates[] = $this->_parseDateTime($value); } } // some iCalendar exports (e.g. KOrganizer) use an EXDATE-entry for every // exception, so we have to merge them array_merge($properties[$tag], $dates); break; // Duration fields case 'DURATION': $attibutes[$tag] = $this->_parseDuration($value); break; // Period of time fields case 'FREEBUSY': $values = array(); $periods = array(); preg_match_all('/,([^,]*)/', ',' . $value, $values); foreach ($values[1] as $value) { $periods[] = $this->_parsePeriod($value); } $properties[$tag] = $periods; break; // UTC offset fields case 'TZOFFSETFROM': case 'TZOFFSETTO': $properties[$tag] = $this->_parseUtcOffset($value); break; case 'PRIORITY': $properties[$tag] = $this->_parsePriority($value); break; case 'CLASS': switch (trim($value)) { case 'PUBLIC': $properties[$tag] = 'PUBLIC'; break; case 'CONFIDENTIAL': $properties[$tag] = 'CONFIDENTIAL'; break; default: $properties[$tag] = 'PRIVATE'; } break; // Integer fields case 'PERCENT-COMPLETE': case 'REPEAT': case 'SEQUENCE': $properties[$tag] = intval($value); break; // Geo fields case 'GEO': $floats = split(';', $value); $value['latitude'] = floatval($floats[0]); $value['longitude'] = floatval($floats[1]); $properties[$tag] = $value; break; // Recursion fields case 'EXRULE': case 'RRULE': $properties[$tag] = $this->_parseRecurrence($value); break; default: // string fields $properties[$tag] = trim($value); break; } } if (!$properties['RRULE']['rtype']) $properties['RRULE'] = array('rtype' => 'SINGLE'); $properties['RRULE'] = CalendarEvent::createRepeat($properties['RRULE'], $properties['DTSTART'], $properties['DTEND']); if (!$properties['LAST-MODIFIED']) $properties['LAST-MODIFIED'] = $properties['CREATED']; if (!$properties['DTSTART'] || ($properties['EXDATE'] && !$properties['RRULE'])) { $_calendar_error->throwError(ErrorHandler::ERROR_CRITICAL, _("Die Datei ist keine gültige iCalendar-Datei!")); $this->count = 0; return false; } if (!$properties['DTEND']) $properties['DTEND'] = $properties['DTSTART']; // day events starts at 00:00:00 and ends at 23:59:59 if ($check['DAY_EVENT']) $properties['DTEND']--; // default: all imported events are set to private if (!$properties['CLASS'] || ($this->public_to_private && $properties['CLASS'] == 'PUBLIC')) { $properties['CLASS'] = 'PRIVATE'; } /* if (isset($studip_categories[$properties['CATEGORIES']])) { $properties['STUDIP_CATEGORY'] = $studip_categories[$properties['CATEGORIES']]; $properties['CATEGORIES'] = ''; } * */ $this->components[] = $properties; } else { $_calendar_error->throwError(ErrorHandler::ERROR_CRITICAL, _("Die Datei ist keine gültige iCalendar-Datei!")); $this->count = 0; return false; } $this->count++; } return true; }