/**
     * 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;
    }