From: Gunnar Wrobel
Date: Sun, 22 Aug 2010 16:28:10 +0000 (+0200)
Subject: Start using Horde_Itip in Kolab_Resource.
X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=8b696adbdbe5666e61e096a5d195fc86915f61ab;p=horde.git
Start using Horde_Itip in Kolab_Resource.
---
diff --git a/framework/Itip/TODO b/framework/Itip/TODO
index 6e934b060..4580e7378 100644
--- a/framework/Itip/TODO
+++ b/framework/Itip/TODO
@@ -1,3 +1,3 @@
-
+
- Merge with Horde Imp (Changes [Horde]: Extended message body; improved From, Changes [Kolab]: Do not automatically set the sequence attribute to 0 if it does not exist -> http://bugs.horde.org/ticket/4863; use lower case mailto -> http://bugs.horde.org/ticket/4872; Missing UID in the vEvent causes failure;
- - Complete unit testing
+ - Use parse_url
diff --git a/framework/Kolab_Resource/TODO b/framework/Kolab_Resource/TODO
new file mode 100644
index 000000000..356532e8a
--- /dev/null
+++ b/framework/Kolab_Resource/TODO
@@ -0,0 +1,12 @@
+ - Start cleaning up classes
+
+ - Ensure the unit tests are working in git again.
+
+ - Integrate into Kolab_Filter git and ensure it is working with the
+ resource handling.
+
+ - Use Horde_Lock
+
+ - At some point merge with Kronolith_Resource (wait for that until
+ you work on Kronolith again).
+
diff --git a/framework/Kolab_Resource/lib/Horde/Kolab/Resource.php b/framework/Kolab_Resource/lib/Horde/Kolab/Resource.php
index 623f3bba8..0079bbb60 100644
--- a/framework/Kolab_Resource/lib/Horde/Kolab/Resource.php
+++ b/framework/Kolab_Resource/lib/Horde/Kolab/Resource.php
@@ -619,125 +619,48 @@ class Kolab_Resource
* @param string $uid The UID of the event.
* @param boolean $is_update Is this an event update?
*/
- function sendITipReply($cn, $resource, $itip, $type = RM_ITIP_ACCEPT,
- $organiser, $uid, $is_update)
- {
+ function sendITipReply(
+ $cn, $resource, $itip, $type, $organiser, $uid, $is_update, $comment = null
+ ) {
Horde::logMessage(sprintf('sendITipReply(%s, %s, %s, %s)',
- $cn, $resource, get_class($itip), $type), 'DEBUG');
-
- // Build the reply.
- $vCal = new Horde_Icalendar();
- $vCal->setAttribute('PRODID', '-//kolab.org//NONSGML Kolab Server 2//EN');
- $vCal->setAttribute('METHOD', 'REPLY');
-
- $summary = _('No summary available');
+ $cn, $resource, get_class($itip), $type),
+ 'DEBUG');
- $itip_reply = Horde_Icalendar::newComponent('VEVENT', $vCal);
- $itip_reply->setAttribute('UID', $uid);
- if (!is_a($itip->getAttribute('SUMMARY'), 'PEAR_error')) {
- $itip_reply->setAttribute('SUMMARY', $itip->getAttribute('SUMMARY'));
- $summary = $itip->getAttribute('SUMMARY');
- }
- if (!is_a($itip->getAttribute('DESCRIPTION'), 'PEAR_error')) {
- $itip_reply->setAttribute('DESCRIPTION', $itip->getAttribute('DESCRIPTION'));
- }
- if (!is_a($itip->getAttribute('LOCATION'), 'PEAR_error')) {
- $itip_reply->setAttribute('LOCATION', $itip->getAttribute('LOCATION'));
- }
- $itip_reply->setAttribute('DTSTART', $itip->getAttribute('DTSTART'), array_pop($itip->getAttribute('DTSTART', true)));
- if (!is_a($itip->getAttribute('DTEND'), 'PEAR_error')) {
- $itip_reply->setAttribute('DTEND', $itip->getAttribute('DTEND'), array_pop($itip->getAttribute('DTEND', true)));
- } else {
- $itip_reply->setAttribute('DURATION', $itip->getAttribute('DURATION'), array_pop($itip->getAttribute('DURATION', true)));
- }
- if (!is_a($itip->getAttribute('SEQUENCE'), 'PEAR_error')) {
- $itip_reply->setAttribute('SEQUENCE', $itip->getAttribute('SEQUENCE'));
- } else {
- $itip_reply->setAttribute('SEQUENCE', 0);
- }
- $itip_reply->setAttribute('ORGANIZER', $itip->getAttribute('ORGANIZER'), array_pop($itip->getAttribute('ORGANIZER', true)));
-
- // Let's try and remove this code and just create
- // the ATTENDEE stuff in the reply from scratch
- // $attendees = $itip->getAttribute( 'ATTENDEE' );
- // if( !is_array( $attendees ) ) {
- // $attendees = array( $attendees );
- // }
- // $params = $itip->getAttribute( 'ATTENDEE', true );
- // for( $i = 0; $i < count($attendees); $i++ ) {
- // $attendee = preg_replace('/^mailto:\s*/i', '', $attendees[$i]);
- // if ($attendee != $resource) {
- // continue;
- // }
- // $params = $params[$i];
- // break;
- // }
-
- $params = array();
- $params['CN'] = $cn;
- switch ($type) {
+ $itip_reply = new Horde_Kolab_Resource_Itip_Response(
+ $itip,
+ new Horde_Kolab_Resource_Itip_Resource_Base(
+ $resource, $cn
+ )
+ );
+ switch($type) {
case RM_ITIP_DECLINE:
- Horde::logMessage(sprintf('Sending DECLINE iTip reply to %s',
- $organiser), 'DEBUG');
- $message = $is_update
- ? sprintf(_("%s has declined the update to the following event:"), $resource) . "\n\n" . $summary
- : sprintf(_("%s has declined the invitation to the following event:"), $resource) . "\n\n" . $summary;
- $subject = _("Declined: ") . $summary;
- $params['PARTSTAT'] = 'DECLINED';
+ $type = new Horde_Kolab_Resource_Itip_Response_Type_Decline(
+ $resource, $itip
+ );
break;
-
case RM_ITIP_ACCEPT:
- Horde::logMessage(sprintf('Sending ACCEPT iTip reply to %s', $organiser), 'DEBUG');
- $message = $is_update
- ? sprintf(_("%s has accepted the update to the following event:"), $resource) . "\n\n" . $summary
- : sprintf(_("%s has accepted the invitation to the following event:"), $resource) . "\n\n" . $summary;
- $subject = _("Accepted: ") . $summary;
- $params['PARTSTAT'] = 'ACCEPTED';
+ $type = new Horde_Kolab_Resource_Itip_Response_Type_Accept(
+ $resource, $itip
+ );
break;
-
case RM_ITIP_TENTATIVE:
- Horde::logMessage(sprintf('Sending TENTATIVE iTip reply to %s', $organiser), 'DEBUG');
- $message = $is_update
- ? sprintf(_("%s has tentatively accepted the update to the following event:"), $resource) . "\n\n" . $summary
- : sprintf(_("%s has tentatively accepted the invitation to the following event:"), $resource) . "\n\n" . $summary;
- $subject = _("Tentative: ") . $summary;
- $params['PARTSTAT'] = 'TENTATIVE';
+ $type = new Horde_Kolab_Resource_Itip_Response_Type_Tentative(
+ $resource, $itip
+ );
break;
-
- default:
- Horde::logMessage(sprintf('Unknown iTip method (%s passed to sendITipReply())', $type), 'ERR');
}
+ list($headers, $message) = $itip_reply->getMessage(
+ $type,
+ '-//kolab.org//NONSGML Kolab Server 2//EN',
+ $comment
+ );
- $itip_reply->setAttribute('ATTENDEE', 'MAILTO:' . $resource, $params);
- $vCal->addComponent($itip_reply);
-
- $ics = new MIME_Part('text/calendar', $vCal->exportvCalendar(), 'UTF-8' );
- //$ics->setName('event-reply.ics');
- $ics->setContentTypeParameter('method', 'REPLY');
-
- //$mime->addPart($body);
- //$mime->addPart($ics);
- // The following was ::convertMimePart($mime). This was removed so that we
- // send out single-part MIME replies that have the iTip file as the body,
- // with the correct mime-type header set, etc. The reason we want to do this
- // is so that Outlook interprets the messages as it does Outlook-generated
- // responses, i.e. double-clicking a reply will automatically update your
- // meetings, showing different status icons in the UI, etc.
- $mime = &MIME_Message::convertMimePart($ics);
- $mime->setCharset('UTF-8');
- $mime->setTransferEncoding('quoted-printable');
- $mime->transferEncodeContents();
-
- // Build the reply headers.
- $msg_headers = new MIME_Headers();
- $msg_headers->addHeader('Date', date('r'));
- $msg_headers->addHeader('From', "$cn <$resource>");
- $msg_headers->addHeader('To', $organiser);
- $msg_headers->addHeader('Subject', $subject);
- $msg_headers->addMIMEHeaders($mime);
+ Horde::logMessage(sprintf('Sending %s iTip reply to %s',
+ $type->getStatus(),
+ $organiser), 'DEBUG');
$reply = new Horde_Kolab_Resource_Reply(
- $resource, $organiser, $msg_headers, $mime
+ $resource, $organiser, $headers, $message
);
Horde::logMessage('Successfully prepared iTip reply', 'DEBUG');
return $reply;
diff --git a/framework/Kolab_Resource/lib/Horde/Kolab/Resource/Itip.php b/framework/Kolab_Resource/lib/Horde/Kolab/Resource/Itip.php
deleted file mode 100644
index 9d0926a1a..000000000
--- a/framework/Kolab_Resource/lib/Horde/Kolab/Resource/Itip.php
+++ /dev/null
@@ -1,257 +0,0 @@
-
- * @author Gunnar Wrobel
- * @license http://www.fsf.org/copyleft/lgpl.html LGPL
- * @link http://pear.horde.org/index.php?package=Kolab_Server
- */
-
-/**
- * Handles Itip data.
- *
- * Copyright 2004-2010 Klarälvdalens Datakonsult AB
- *
- * See the enclosed file COPYING for license information (LGPL). If you did not
- * receive this file, see
- * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
- *
- * @category Kolab
- * @package Kolab_Filter
- * @author Steffen Hansen
- * @author Gunnar Wrobel
- * @license http://www.fsf.org/copyleft/lgpl.html LGPL
- * @link http://pear.horde.org/index.php?package=Kolab_Server
- */
-class Horde_Kolab_Resource_Itip
-{
-
- /**
- * Reference to the Icalendar iTip object.
- *
- * @var Horde_Icalendar_Vevent
- */
- private $_itip;
-
- /**
- * Constructor.
- *
- * @param Horde_Icalendar_Vevent $itip An iCalendar iTip object.
- */
- public function __construct($itip)
- {
- $this->_itip = $itip;
- }
-
- public function __call($method, $args)
- {
- return call_user_func_array(array($this->_itip, $method), $args);
- }
-
- /**
- * Return the method of the iTip request.
- *
- * @return string The method of the request.
- */
- public function getMethod()
- {
- return $this->_itip->getAttributeDefault('METHOD', 'REQUEST');
- }
-
- /**
- * Return the uid of the iTip event.
- *
- * @return string The uid of the event.
- */
- public function getUid()
- {
- return $this->_itip->getAttributeDefault('UID', '');
- }
-
- /**
- * Return the organizer of the iTip event.
- *
- * @return string The organizer of the event.
- */
- public function getOrganizer()
- {
- return preg_replace('/^mailto:\s*/i', '', $this->_itip->getAttributeDefault('ORGANIZER', ''));
- }
-
- /**
- * Return the summary of the iTip event.
- *
- * @return string The summary of the event.
- */
- public function getSummary()
- {
- return $this->_itip->getAttributeDefault('SUMMARY', '');
- }
-
- /**
- * Return the start of the iTip event.
- *
- * @return string The start of the event.
- */
- public function getStart()
- {
- return $this->_itip->getAttributeDefault('DTSTART', 0);
- }
-
- /**
- * Return the end of the iTip event.
- *
- * @return string The end of the event.
- */
- public function getEnd()
- {
- return $this->_itip->getAttributeDefault('DTEND', 0);
- }
-
- public function getKolabObject()
- {
- $object = array();
- $object['uid'] = $this->_itip->getAttributeDefault('UID', '');
-
- $org_params = $this->_itip->getAttribute('ORGANIZER', true);
- if (!is_a( $org_params, 'PEAR_Error')) {
- if (!empty($org_params[0]['CN'])) {
- $object['organizer']['display-name'] = $org_params[0]['CN'];
- }
- $orgemail = $this->_itip->getAttributeDefault('ORGANIZER', '');
- if (preg_match('/mailto:(.*)/i', $orgemail, $regs )) {
- $orgemail = $regs[1];
- }
- $object['organizer']['smtp-address'] = $orgemail;
- }
- $object['summary'] = $this->_itip->getAttributeDefault('SUMMARY', '');
- $object['location'] = $this->_itip->getAttributeDefault('LOCATION', '');
- $object['body'] = $this->_itip->getAttributeDefault('DESCRIPTION', '');
- $dtend = $this->_itip->getAttributeDefault('DTEND', '');
- if (is_array($dtend)) {
- $object['_is_all_day'] = true;
- }
- $start = new Horde_Kolab_Resource_Epoch($this->getStart());
- $object['start-date'] = $start->getEpoch();
- $end = new Horde_Kolab_Resource_Epoch($dtend);
- $object['end-date'] = $end->getEpoch();
-
- $attendees = $this->_itip->getAttribute('ATTENDEE');
- if (!is_a( $attendees, 'PEAR_Error')) {
- $attendees_params = $this->_itip->getAttribute('ATTENDEE', true);
- if (!is_array($attendees)) {
- $attendees = array($attendees);
- }
- if (!is_array($attendees_params)) {
- $attendees_params = array($attendees_params);
- }
-
- $object['attendee'] = array();
- for ($i = 0; $i < count($attendees); $i++) {
- $attendee = array();
- if (isset($attendees_params[$i]['CN'])) {
- $attendee['display-name'] = $attendees_params[$i]['CN'];
- }
-
- $attendeeemail = $attendees[$i];
- if (preg_match('/mailto:(.*)/i', $attendeeemail, $regs)) {
- $attendeeemail = $regs[1];
- }
- $attendee['smtp-address'] = $attendeeemail;
-
- if (!isset($attendees_params[$i]['RSVP'])
- || $attendees_params[$i]['RSVP'] == 'FALSE') {
- $attendee['request-response'] = false;
- } else {
- $attendee['request-response'] = true;
- }
-
- if (isset($attendees_params[$i]['ROLE'])) {
- $attendee['role'] = $attendees_params[$i]['ROLE'];
- }
-
- if (isset($attendees_params[$i]['PARTSTAT'])) {
- $status = strtolower($attendees_params[$i]['PARTSTAT']);
- switch ($status) {
- case 'needs-action':
- case 'delegated':
- $attendee['status'] = 'none';
- break;
- default:
- $attendee['status'] = $status;
- break;
- }
- }
-
- $object['attendee'][] = $attendee;
- }
- }
-
- // Alarm
- $valarm = $this->_itip->findComponent('VALARM');
- if ($valarm) {
- $trigger = $valarm->getAttribute('TRIGGER');
- if (!is_a($trigger, 'PEAR_Error')) {
- $p = $valarm->getAttribute('TRIGGER', true);
- if ($trigger < 0) {
- // All OK, enter the alarm into the XML
- // NOTE: The Kolab XML format seems underspecified
- // wrt. alarms currently...
- $object['alarm'] = -$trigger / 60;
- }
- } else {
- Horde::logMessage('No TRIGGER in VALARM. ' . $trigger->getMessage(), 'ERR');
- }
- }
-
- // Recurrence
- $rrule_str = $this->_itip->getAttribute('RRULE');
- if (!is_a($rrule_str, 'PEAR_Error')) {
- require_once 'Horde/Date/Recurrence.php';
- $recurrence = new Horde_Date_Recurrence(time());
- $recurrence->fromRRule20($rrule_str);
- $object['recurrence'] = $recurrence->toHash();
- }
-
- Horde::logMessage(sprintf('Assembled event object: %s',
- print_r($object, true)), 'DEBUG');
-
- return $object;
- }
-
- public function setAccepted($resource)
- {
- // Update our status within the iTip request and send the reply
- $this->_itip->setAttribute('STATUS', 'CONFIRMED', array(), false);
- $attendees = $this->_itip->getAttribute('ATTENDEE');
- if (!is_array($attendees)) {
- $attendees = array($attendees);
- }
- $attparams = $this->_itip->getAttribute('ATTENDEE', true);
- foreach ($attendees as $i => $attendee) {
- $attendee = preg_replace('/^mailto:\s*/i', '', $attendee);
- if ($attendee != $resource) {
- continue;
- }
-
- $attparams[$i]['PARTSTAT'] = 'ACCEPTED';
- if (array_key_exists('RSVP', $attparams[$i])) {
- unset($attparams[$i]['RSVP']);
- }
- }
-
- // Re-add all the attendees to the event, using our updates status info
- $firstatt = array_pop($attendees);
- $firstattparams = array_pop($attparams);
- $this->_itip->setAttribute('ATTENDEE', $firstatt, $firstattparams, false);
- foreach ($attendees as $i => $attendee) {
- $this->_itip->setAttribute('ATTENDEE', $attendee, $attparams[$i]);
- }
- }
-
-}
diff --git a/framework/Kolab_Resource/test/Horde/Kolab/Resource/AllTests.php b/framework/Kolab_Resource/test/Horde/Kolab/Resource/AllTests.php
new file mode 100644
index 000000000..c47754ec5
--- /dev/null
+++ b/framework/Kolab_Resource/test/Horde/Kolab/Resource/AllTests.php
@@ -0,0 +1,45 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Resource
+ */
+
+/**
+ * Define the main method
+ */
+if (!defined('PHPUnit_MAIN_METHOD')) {
+ define('PHPUnit_MAIN_METHOD', 'Horde_Kolab_Resource_AllTests::main');
+}
+
+/**
+ * Prepare the test setup.
+ */
+require_once 'Horde/Test/AllTests.php';
+
+/**
+ * All tests for the Kolab_Resource:: package.
+ *
+ * @category Kolab
+ * @package Kolab_Resource
+ * @subpackage UnitTests
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Resource
+ */
+class Horde_Kolab_Resource_AllTests extends Horde_Test_AllTests
+{
+}
+
+Horde_Kolab_Resource_AllTests::init('Horde_Kolab_Resource', __FILE__);
+
+if (PHPUnit_MAIN_METHOD == 'Horde_Kolab_Resource_AllTests::main') {
+ Horde_Kolab_Resource_AllTests::main();
+}
diff --git a/framework/Kolab_Resource/test/Horde/Kolab/Resource/Autoload.php b/framework/Kolab_Resource/test/Horde/Kolab/Resource/Autoload.php
new file mode 100644
index 000000000..4c9c6cb1c
--- /dev/null
+++ b/framework/Kolab_Resource/test/Horde/Kolab/Resource/Autoload.php
@@ -0,0 +1,26 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Resource
+ */
+
+require_once 'Horde/Kolab/Resource.php';
+
+/** Do not catch strict standards */
+error_reporting(E_ALL | ~E_STRICT);
+
+/** Load the basic test definition */
+require_once dirname(__FILE__) . '/TestCase.php';
diff --git a/framework/Kolab_Resource/test/Horde/Kolab/Resource/fixtures/recur_invitation.eml b/framework/Kolab_Resource/test/Horde/Kolab/Resource/fixtures/recur_invitation.eml
new file mode 100644
index 000000000..3dda6efe6
--- /dev/null
+++ b/framework/Kolab_Resource/test/Horde/Kolab/Resource/fixtures/recur_invitation.eml
@@ -0,0 +1,202 @@
+Received: from localhost (example.org [127.0.0.1])
+ by example.org (Postfix) with ESMTP id 4E36AC2E001A
+ for ; Fri, 26 Sep 2008 10:54:08 +0200 (CEST)
+X-Virus-Scanned: by amavisd-new at example.org
+X-Spam-Flag: NO
+X-Spam-Score: 3.704
+X-Spam-Level: ***
+X-Spam-Status: No, score=3.704 tagged_above=3 required=6.3 tests=[AWL=-1.351,
+ HTML_IMAGE_ONLY_32=2.353, HTML_MESSAGE=0.001, MIME_BASE64_TEXT=2.701]
+Received: from example.org ([127.0.0.1])
+ by localhost (example.org [127.0.0.1]) (amavisd-new, port 10024)
+ with ESMTP id 151vjdIDehPR for ;
+ Fri, 26 Sep 2008 10:54:08 +0200 (CEST)
+Received: from localhost (example.org [127.0.0.1])
+ by example.org (Postfix) with ESMTP id 030C5C2E001C
+ for ; Fri, 26 Sep 2008 10:54:08 +0200 (CEST)
+Received: from ug-out-1516.google.com (ug-out-1516.google.com [66.249.92.165])
+ by example.org (Postfix) with ESMTP id D88AAC2E001A
+ for ; Fri, 26 Sep 2008 10:54:07 +0200 (CEST)
+Received: by ug-out-1516.google.com with SMTP id 34so6700uge.3
+ for ; Fri, 26 Sep 2008 01:54:07 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=google.com; s=beta;
+ h=domainkey-signature:mime-version:reply-to:sender:auto-submitted
+ :received:message-id:date:subject:from:to:content-type;
+ bh=NIbM6hZNgbcIAu3CGNdscvaw1JuMHR0cmVT+UvW5YdU=;
+ b=sGwxgD6sW0IDPFRSJcK9zvMtsS4jqziu+eF+p2fb7RsxCPFjU5FJFKbwhZQdVvA6I/
+ rzueuvNP0EqBhfsdLVyA==
+DomainKey-Signature: a=rsa-sha1; c=nofws;
+ d=google.com; s=beta;
+ h=mime-version:reply-to:sender:auto-submitted:message-id:date:subject
+ :from:to:content-type;
+ b=oo9wYH7lmDU5KDHTT4tpQ+BRlw/FYHBN2zt/an5FPuXE+dNM05UBVi8h87ig2c5Tvl
+ p53+hzDD2DoB/SlhqInA==
+MIME-Version: 1.0
+Reply-To: Gunnar Wrobel
+Sender: Google Kalender
+Auto-Submitted: auto-generated
+Received: by 10.210.130.14 with SMTP id c14mr120659ebd.11.1222419247313; Fri,
+ 26 Sep 2008 01:54:07 -0700 (PDT)
+Message-ID: <0015174c3d6200b58f0457c8a9b5@google.com>
+Date: Fri, 26 Sep 2008 01:54:07 -0700
+Subject: =?ISO-8859-1?B?W0VpbmxhZHVuZ10gdHJnIEAgVORnbGljaCB1bSAxMTowMCAocmVzQGRldi5wYXJkdXMu?=
+ =?ISO-8859-1?B?ZGUp?=
+From: Gunnar Wrobel
+To: "wrobel@example.org"
+Content-Type: multipart/mixed; boundary=0015174c3d6200b57b0457c8a97d
+
+--0015174c3d6200b57b0457c8a97d
+Content-Type: multipart/alternative; boundary=0015174c3d6200b5720457c8a97b
+
+--0015174c3d6200b5720457c8a97b
+Content-Type: text/plain; charset=ISO-8859-1; format=flowed; delsp=yes
+Content-Transfer-Encoding: base64
+
+cmVzQGRldi5wYXJkdXMuZGUsIFNpZSB3dXJkZW4gZWluZ2VsYWRlbiB6dQ0KDQpUaXRlbDogdHJn
+DQpaZWl0OiBU5GdsaWNoIHVtIDExOjAwIChaZWl0em9uZTogUm9tKQ0KS2FsZW5kZXI6IHJlc0Bk
+ZXYucGFyZHVzLmRlDQoNClNpZSBr9m5uZW4gZGllc2VuIFRlcm1pbiBhbnplaWdlbiB1bnRlciAg
+DQpodHRwOi8vd3d3Lmdvb2dsZS5jb20vY2FsZW5kYXIvZXZlbnQ/YWN0aW9uPVZJRVcmZWlkPWJX
+czJiVzR6YUdGdWNEUTJhVGt3T0dWbGIyNHlaMmR1ZFdzZ2NtVnpRR1JsZGk1d1lYSmtkWE11WkdV
+JnRvaz1NaklqWjNkeWIySmxiRUJuYjI5bmJHVnRZV2xzTG1OdmJUQTNaVGc1Tm1aaFpqZGxZVEE0
+WmpjM1pqYzBNMk16T0dSak5ETXlaVGc0WkRFeU0yVXpNV1EmY3R6PUV1cm9wZSUyRlJvbWUmaGw9
+ZGUNCg0KDQoNCkRpZXNlIEUtTWFpbCB3dXJkZSBhbiByZXNAZGV2LnBhcmR1cy5kZSBnZXNlbmRl
+dCwgZGEgU2llIGVpbiBUZWlsbmVobWVyICANCmRpZXNlcyBUZXJtaW5zIHNpbmQuDQoNCkxlaG5l
+biBTaWUgZGllc2VuIFRlcm1pbiBhYiwgdW0ga2VpbmUgd2VpdGVyZW4gTmFjaHJpY2h0ZW4genUg
+ZGllc2VtIFRlcm1pbiAgDQp6dSBlcmhhbHRlbi4gU2llIGsmb3VtbDtubmVuIHNpY2ggYWx0ZXJu
+YXRpdiBmJnV1bWw7ciBlaW4gR29vZ2xlLUtvbnRvICANCnVudGVyIGh0dHA6Ly93d3cuZ29vZ2xl
+LmNvbS9jYWxlbmRhci8gYW5tZWxkZW4gdW5kIElocmUgIA0KQmVuYWNocmljaHRpZ3VuZ3NlaW5z
+dGVsbHVuZ2VuIGYmdXVtbDtyIElocmVuIGdlc2FtdGVuIEthbGVuZGVyIHN0ZXVlcm4uDQo=
+--0015174c3d6200b5720457c8a97b
+Content-Type: text/html; charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+
+
+
wrobel@example.org,
+Sie wurden eingeladen zu
+
trg
+
Täglich um 11:00
+
+(Zeitzone:
+Rom)
+
+
+
+Kalender:
+wrobel@example.org
+
Weitere Termindetails»
+
Nehmen Sie teil?
+
+
+
Diese E-Mail wurde an wrobel@example.org gesendet, da Sie ein Teilnehmer dieses Termins sind.
Lehnen Sie diesen Termin ab, um keine weiteren Nachrichten zu diesem Termin zu erhalten. Sie können sich alternativ für ein Google-Konto unter http://www.google.com/calendar/ anmelden und Ihre Benachrichtigungseinstellungen für Ihren gesamten Kalender steuern.
+--0015174c3d6200b5720457c8a97b
+Content-Type: text/calendar; charset=ISO-8859-1; method=REQUEST
+Content-Transfer-Encoding: 7bit
+
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VTIMEZONE
+TZID:Europe/Rome
+X-LIC-LOCATION:Europe/Rome
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+TZNAME:CEST
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+TZNAME:CET
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=Europe/Rome:20080926T110000
+DTEND;TZID=Europe/Rome:20080926T120000
+RRULE:FREQ=DAILY;WKST=SU
+DTSTAMP:20080926T085407Z
+ORGANIZER;CN=Gunnar Wrobel:mailto:test@example.org
+UID:mk6mn3hanp46i908eeon2ggnuk@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=
+ TRUE;CN=wrobel@example.org;X-NUM-GUESTS=0:mailto:wrobel@example.org
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE
+ ;CN=Gunnar Wrobel;X-NUM-GUESTS=0:mailto:test@example.org
+CLASS:PRIVATE
+CREATED:20080926T085406Z
+DESCRIPTION:Zeigen Sie Ihren Termin unter http://www.google.com/calendar/ev
+ ent?action=VIEW&eid=bWs2bW4zaGFucDQ2aTkwOGVlb24yZ2dudWsgcmVzQGRldi5wYXJkdXM
+ uZGU&tok=MjIjZ3dyb2JlbEBnb29nbGVtYWlsLmNvbTA3ZTg5NmZhZjdlYTA4Zjc3Zjc0M2MzOG
+ RjNDMyZTg4ZDEyM2UzMWQ&ctz=Europe%2FRome&hl=de an.
+LAST-MODIFIED:20080926T085406Z
+LOCATION:Somewhere
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:trg
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+
+--0015174c3d6200b5720457c8a97b--
+--0015174c3d6200b57b0457c8a97d
+Content-Type: application/ics; name="invite.ics"
+Content-Disposition: attachment; filename="invite.ics"
+Content-Transfer-Encoding: 7bit
+
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VTIMEZONE
+TZID:Europe/Rome
+X-LIC-LOCATION:Europe/Rome
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+TZNAME:CEST
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+TZNAME:CET
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=Europe/Rome:20080926T110000
+DTEND;TZID=Europe/Rome:20080926T120000
+RRULE:FREQ=DAILY;WKST=SU
+DTSTAMP:20080926T085407Z
+ORGANIZER;CN=Gunnar Wrobel:mailto:test@example.org
+UID:mk6mn3hanp46i908eeon2ggnuk@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=
+ TRUE;CN=wrobel@example.org;X-NUM-GUESTS=0:mailto:wrobel@example.org
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE
+ ;CN=Gunnar Wrobel;X-NUM-GUESTS=0:mailto:test@example.org
+CLASS:PRIVATE
+CREATED:20080926T085406Z
+DESCRIPTION:Zeigen Sie Ihren Termin unter http://www.google.com/calendar/ev
+ ent?action=VIEW&eid=bWs2bW4zaGFucDQ2aTkwOGVlb24yZ2dudWsgcmVzQGRldi5wYXJkdXM
+ uZGU&tok=MjIjZ3dyb2JlbEBnb29nbGVtYWlsLmNvbTA3ZTg5NmZhZjdlYTA4Zjc3Zjc0M2MzOG
+ RjNDMyZTg4ZDEyM2UzMWQ&ctz=Europe%2FRome&hl=de an.
+LAST-MODIFIED:20080926T085406Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:trg
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+
+--0015174c3d6200b57b0457c8a97d--