stuart: server/kolab-resource-handlers/kolab-resource-handlers/resmgr resmgr.php, 1.18, 1.19

cvs at intevation.de cvs at intevation.de
Wed Sep 15 00:09:38 CEST 2004


Author: stuart

Update of /kolabrepository/server/kolab-resource-handlers/kolab-resource-handlers/resmgr
In directory doto:/tmp/cvs-serv14949/kolab-resource-handlers/kolab-resource-handlers/resmgr

Modified Files:
	resmgr.php 
Log Message:
Fix for Issue381: Changes can now be made to an event and the resource manager is able to handle them correctly. This uses the new no-cache and ignore functionality of the freebusy script.
(Possible) fix for Issue360: The iTip reply was not being decoded properly from the MIME message. This also fixes event requests from Outlook, as they use quoted-printable encoding and subsequently weren't working either.
(Possible) fix for Issue387: Net_IMAP is used to set the folder annotation when creating the calendar folder. If Net_IMAP is not present this step is simply skipped.

- Able to get the iTip METHOD from both the VEVENT object and the iCalendar container object (fix for outlook requests).
- Small text notifications are sent to the organiser when a CANCEL method is received and processed. This also indicates whether there was a problem (e.g. the event does not exist).
- iTip replies are sent on REQUEST updates as well. This is done as the organiser needs to know whether the updated event is still suitable (e.g. that we don't have another event in the new timeslot, etc)
- iTip replies are no longer multipart/mime messages with a text part explanation of what the iCal part is - they are now exclusively text/calendar messages. This is done to enable some UI enhancements in Outlook that are only present for the single-part messages.
- Fixed a bug in the iTip SEQUENCE logic. We must not increment the SEQUENCE when sending a reply, but rather return the value that we received.
- Added the X-Kolab-Type header to stored calendar messages.
- Fixed a bug in the busy time overlap testing. Previously you could create an event and another that started before and ended after the first (i.e. encapsulated the first event) and both would be accepted as having no conflicts.
- Small cleanups & optimisations.


Index: resmgr.php
===================================================================
RCS file: /kolabrepository/server/kolab-resource-handlers/kolab-resource-handlers/resmgr/resmgr.php,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -d -r1.18 -r1.19
--- resmgr.php	11 Sep 2004 01:55:43 -0000	1.18
+++ resmgr.php	14 Sep 2004 22:09:36 -0000	1.19
@@ -145,7 +145,7 @@
 
     // Get the resource's free/busy list
     // once more so it is up to date
-    getFreeBusy();
+    getFreeBusy(true);
 
     imapClose();
     logClose();
@@ -219,9 +219,9 @@
         return false;
     }
 
-    $result = ldap_search($ldap, $params['base_dn'], 
-			  "(&(objectClass=kolabInetOrgPerson)(mail=$resource))", 
-			  array("kolabEncryptedPassword", "kolabResourceAction" ));
+    $result = ldap_search($ldap, $params['base_dn'],
+                          "(&(objectClass=kolabInetOrgPerson)(mail=$resource))",
+                          array("kolabEncryptedPassword", "kolabResourceAction" ));
     if (!$result) {
         myLog('Unable to perform LDAP search: ' . ldap_error($ldap));
         return false;
@@ -306,7 +306,7 @@
 
     $uid = $entries[0]['uid'][0];
     ldap_close($ldap);
-    if( $uid ) return $uid; 
+    if( $uid ) return $uid;
     else return $resource;
 }
 
@@ -322,61 +322,65 @@
     $requestText = preg_replace("/\r\n|\n|\r/s", "\r\n", $requestText);
 }
 
-function check_lmtp_response( &$lmtp, $code )
+function checkLMTPResponse(&$lmtp, $code)
 {
-  $resp = $lmtp->getResponse();
-  if( $resp[0] != $code ) {
-    myLog( $resp[1], RM_LOG_ERROR );
-    shutdown( 1, $resp[1] );
-  } else return true;
+    $resp = $lmtp->getResponse();
+    if ($resp[0] != $code) {
+        myLog($resp[1], RM_LOG_ERROR);
+        shutdown(1, $resp[1]);
+    }
+
+    return true;
 }
 
-function send_lmtp( $sender, $recip, &$data )
+function sendLMTP($sender, $recip, &$data)
 {
-  static $lmtp;
-  if (!isset($lmtp)) {
-    require_once 'Net/LMTP.php';
-    $lmtp = new Net_LMTP();
-    if( !$lmtp ) {
-      $msg = 'Could not create LMTP object';
-      myLog($msg, RM_LOG_ERROR );
-      shutdown(1, $msg );
-    }
-    if( PEAR::isError( $error = $lmtp->connect() ) ) {
-      $msg = 'Failed to connect to LMTP: '.$error->getMessage();
-	  myLog( $msg, RM_LOG_ERROR );
-	  shutdown( 1, $msg );
-    }
-    check_lmtp_response( $lmtp, 250 );
+    static $lmtp;
+    if (!isset($lmtp)) {
+        require_once 'Net/LMTP.php';
 
-    if( PEAR::isError( $error = $lmtp->mailFrom($sender) ) ) {
-      $msg = 'Failed to set sender: '.$error->getMessage();
-      myLog( $msg, RM_LOG_ERROR );
-      shutdown( 1, $msg );	  
-    }
-    check_lmtp_response( $lmtp, 250 );
-    if( PEAR::isError( $error = $lmtp->rcptTo($recip) ) ) {
-      $msg = 'Failed to set recipient: '.$error->getMessage();
-      myLog( $msg, RM_LOG_ERROR );
-	  shutdown( 1, $msg );	  
-    }
-    check_lmtp_response( $lmtp, 250 );
-    if( PEAR::isError( $error = $lmtp->data($data) ) ) {
-      $msg = 'Failed to send data: '.$error->getMessage();
-      myLog( $msg, RM_LOG_ERROR );
-      shutdown( 1, $msg );	  
+        $lmtp = &new Net_LMTP();
+        if (!$lmtp) {
+            $msg = 'Could not create LMTP object';
+            myLog($msg, RM_LOG_ERROR);
+            shutdown(1, $msg);
+        }
+
+        if (PEAR::isError($error = $lmtp->connect())) {
+            $msg = 'Failed to connect to LMTP: ' . $error->getMessage();
+            myLog($msg, RM_LOG_ERROR);
+            shutdown(1, $msg);
+        }
+        checkLMTPResponse($lmtp, 250);
+
+        if (PEAR::isError($error = $lmtp->mailFrom($sender))) {
+            $msg = 'Failed to set sender: ' . $error->getMessage();
+            myLog($msg, RM_LOG_ERROR);
+            shutdown(1, $msg);
+        }
+        checkLMTPResponse($lmtp, 250);
+
+        if (PEAR::isError($error = $lmtp->rcptTo($recip))) {
+            $msg = 'Failed to set recipient: ' . $error->getMessage();
+            myLog($msg, RM_LOG_ERROR);
+            shutdown(1, $msg);
+        }
+        checkLMTPResponse($lmtp, 250);
+
+        if (PEAR::isError($error = $lmtp->data($data))) {
+            $msg = 'Failed to send data: ' . $error->getMessage();
+            myLog($msg, RM_LOG_ERROR);
+            shutdown(1, $msg);
+        }
+        checkLMTPResponse($lmtp, 250);
+
+        $lmtp->disconnect();
     }
-    check_lmtp_response( $lmtp, 250 );
-    $lmtp->disconnect();
-  }
 }
 
-function &getITip()
+function &getICal()
 {
-    global $requestText;
-    global $resource;
-    global $sender;
-    global $params;
+    global $requestText, $resource, $sender, $params;
 
     $mime = &MIME_Structure::parseTextMIMEMessage($requestText);
 
@@ -386,24 +390,18 @@
             $part = $mime->getPart($mimeid);
 
             $iCalendar = &new Horde_iCalendar();
-            $iCalendar->parsevCalendar($part->getContents());
-
-            $event = &$iCalendar->findComponent('VEVENT');
-            if ($event === false) {
-                myLog('No VFREEBUSY found in iCalendar data', RM_LOG_ERROR);
-                shutdown(1, 'No VFREEBUSY found in iCalendar data' );
-            }
+            $iCalendar->parsevCalendar($part->transferDecode());
 
-            return $event;
+            return $iCalendar;
         }
     }
 
-    if( $params['group'] ) {      
-      send_lmtp( $sender, $resource, $requestText );
-      shutdown(0);
-    } else {      
-      myLog('No iCalendar data found in message', RM_LOG_ERROR);
-      shutdown(1, 'No iCalendar data found in message' );
+    if ($params['group']) {
+        sendLMTP($sender, $resource, $requestText);
+        shutdown(0);
+    } else {
+        myLog('No iCalendar data found in message', RM_LOG_ERROR);
+        shutdown(1, 'No iCalendar data found in message' );
     }
 }
 
@@ -435,11 +433,21 @@
     return $uri;
 }
 
-function &getFreeBusy()
+function &getFreeBusy($cache = false)
 {
-    global $resource, $params;
+    global $resource, $params, $ignore;
 
-    $url = str_replace('${USER}', $resource, $params['freebusy_url']);
+    $url = str_replace('${USER}', urlencode($resource), $params['freebusy_url']);
+    if (!$cache) {
+        // We're not caching, so ignore any events with the specified UIDs. This
+        // is done to ignore events that are being updated, without actually
+        // having to remove the messages from IMAP. This way, if something goes
+        // wrong, we still have the original event in tact.
+        foreach ($ignore as $uid) {
+            $url .= '&i[]=' . urlencode($uid);
+        }
+    }
+    $url .= '&c=' . ($cache ? '1' : '0');
     myLog("Using f/b URL $url", RM_LOG_DEBUG);
 
     $parsed = parse_url($url);
@@ -449,29 +457,33 @@
 
     $text = @file_get_contents($url);
     if ($text == false || empty($text)) {
-        myLog("Unable to retrieve free/busy information for $resource", RM_LOG_ERROR);	
+        myLog("Unable to retrieve free/busy information for $resource", RM_LOG_ERROR);
         //shutdown(1, "Unable to retrieve free/busy information for $resource");
         return false;
     }
 
-    $iCalendar = &new Horde_iCalendar();
-    $iCalendar->parsevCalendar($text);
-    $vfb = &$iCalendar->findComponent('VFREEBUSY');
+    // If this call is purely to cache the f/b list then we don't need to
+    // bother parsing the VFB file
+    if (!$cache) {
+        $iCalendar = &new Horde_iCalendar();
+        $iCalendar->parsevCalendar($text);
+        $vfb = &$iCalendar->findComponent('VFREEBUSY');
 
-    if ($vfb === false) {
-        myLog("Invalid or no free/busy information available for $resource", RM_LOG_ERROR);
-        //shutdown();
-        return false;
-    }
+        if ($vfb === false) {
+            myLog("Invalid or no free/busy information available for $resource", RM_LOG_ERROR);
+            //shutdown();
+            return false;
+        }
 
-    $vfb->simplify();
+        $vfb->simplify();
 
-    return $vfb;
+        return $vfb;
+    }
 }
 
 function sendITipReply($type = RM_ITIP_ACCEPT)
 {
-    global $organiser, $itip, $resource, $uid;
+    global $organiser, $itip, $resource, $uid, $is_update, $summary;
 
     // Build the reply.
     $vCal = &new Horde_iCalendar();
@@ -496,7 +508,7 @@
         $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') + 1);
+        $itip_reply->setAttribute('SEQUENCE', $itip->getAttribute('SEQUENCE'));
     } else {
         $itip_reply->setAttribute('SEQUENCE', 0);
     }
@@ -506,22 +518,25 @@
     switch ($type) {
         case RM_ITIP_DECLINE:
             myLog("Sending DECLINE iTip reply to $organiser", RM_LOG_DEBUG);
-            $message = sprintf(_("%s has declined."), $resource);
-            $subject = 'Declined: ' . $itip->getAttribute('SUMMARY');
+            $message = $is_update ? sprintf(_("%s has declined the update to %s."), $resource, $summary) :
+                sprintf(_("%s has declined the invitation to %s."), $resource, $summary);
+            $subject = 'Declined: ' . $summary;
             $params['PARTSTAT'] = 'DECLINED';
             break;
 
         case RM_ITIP_ACCEPT:
             myLog("Sending ACCEPT iTip reply to $organiser", RM_LOG_DEBUG);
-            $message = sprintf(_("%s has accepted."), $resource);
-            $subject = 'Accepted: ' . $itip->getAttribute('SUMMARY');
+            $message = $is_update ? sprintf(_("%s has accepted the update to %s."), $resource, $summary) :
+                sprintf(_("%s has accepted the invitation to %s."), $resource, $summary);
+            $subject = 'Accepted: ' . $summary;
             $params['PARTSTAT'] = 'ACCEPTED';
             break;
 
         case RM_ITIP_TENTATIVE:
             myLog("Sending TENTATIVE iTip reply to $organiser", RM_LOG_DEBUG);
-            $message = sprintf(_("%s has tentatively accepted."), $resource);
-            $subject = 'Tentative: ' . $itip->getAttribute('SUMMARY');
+            $message = $is_update ? sprintf(_("%s has tentatively accepted the update to %s."), $resource, $summary) :
+                sprintf(_("%s has tentatively accepted the invitation to %s."), $resource, $summary);
+            $subject = 'Tentative: ' . $summary;
             $params['PARTSTAT'] = 'TENTATIVE';
             break;
 
@@ -533,16 +548,22 @@
     $itip_reply->setAttribute('ATTENDEE', 'MAILTO:' . $resource, $params);
     $vCal->addComponent($itip_reply);
 
-    $mime = &new MIME_Part('multipart/alternative');
-    $body = &new MIME_Part('text/plain', Text::wrap($message, 76, "\n"));
+    //$mime = &new MIME_Part('multipart/alternative');
+    //$body = &new MIME_Part('text/plain', Text::wrap($message, 76, "\n"));
 
     $ics = &new MIME_Part('text/calendar', $vCal->exportvCalendar());
-    $ics->setName('event-reply.ics');
-    $ics->setContentTypeParameter('METHOD', 'REPLY');
+    //$ics->setName('event-reply.ics');
+    $ics->setContentTypeParameter('method', 'REPLY');
 
-    $mime->addPart($body);
-    $mime->addPart($ics);
-    $mime = &MIME_Message::convertMimePart($mime);
+    //$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);
 
     // Build the reply headers.
     $msg_headers = &new MIME_Headers();
@@ -616,22 +637,69 @@
     $imap = @imap_open($fullmbox, $params['calendar_user'], $params['calendar_pass'], OP_HALFOPEN);
     testIMAPError();
 
-    myLog( "Reopening $fullmbox", RM_LOG_DEBUG );
+    myLog("Reopening $fullmbox", RM_LOG_DEBUG);
     @imap_reopen($imap, $fullmbox, CL_EXPUNGE);
     $errors = imap_errors();
     if (count($errors)) {
         myLog('IMAP Errors from reopen: ' . @join(', ', $errors));
+        $must_create = false;
+        foreach ($errors as $error) {
+            // Why oh why does the IMAP library use text error strings instead
+            // of numerical codes?
+            if ($error == 'Mailbox does not exist') {
+                $must_create = true;
+                break;
+            }
+        }
 
-        // Try create the Calendar folder
-        @imap_createmailbox($imap, $fullmbox);
-        myLog('IMAP Errors from createmailbox: ' . @join(', ', imap_errors()));
+        if ($must_create && !$inbox) {
+            // Try create the Calendar folder
+            @imap_createmailbox($imap, $fullmbox);
+            myLog('IMAP Errors from createmailbox: ' . @join(', ', imap_errors()));
 
-        $status = @imap_status($imap, $fullmbox, SA_ALL);
-        myLog('New folder status: ' . print_r($status, true));
+            @imap_close($imap);
 
-        myLog('Trying to reopen calendar box...');
-        @imap_reopen($imap, $fullmbox, CL_EXPUNGE);
-        testIMAPError();
+            // Try and set the Kolab folder-type annotation. This shouldn't
+            // happen that often so we can justify having this here - it
+            // wouldn't be neccessary if the c-client library supported
+            // ANNOTATEMORE.
+            if ((@include_once 'Net/IMAP.php')) {
+                // We should be creating the calendar mailbox using the current
+                // resource's credentials, so we rather specify the box as a
+                // child of INBOX/ rather than the user/ hierarchy.
+                $cal_mbox = 'INBOX/' . $params['calendar_store'];
+                $net_imap = &new Net_IMAP($params['server'], 143);
+                if (is_a($net_imap, 'PEAR_Error')) {
+                    myLog('Unable to create Net_IMAP object: ' . $net_imap->getMessage(), RM_LOG_ERROR);
+                } else {
+                    $result = $net_imap->login($params['calendar_user'],
+                                               $params['calendar_pass'], false, false);
+                    if (is_a($result, 'PEAR_Error')) {
+                        myLog('Unable to login with the Net_IMAP object: ' . $result->getMessage(),
+                              RM_LOG_ERROR);
+                    } else {
+                        $result = $net_imap->setAnnotation($cal_mbox, '/vendor/kolab/folder-type',
+                                                       array('value.shared' => 'event.default'));
+                        if (is_a($result, 'PEAR_Error')) {
+                            myLog("Unable to set the folder-type annotation on mailbox $mymailbox: "
+                                  . $result->getMessage(), RM_LOG_ERROR);
+                        }
+                    }
+
+                    $net_imap->disconnect();
+                }
+            }
+
+            $imap = @imap_open($fullmbox, $params['calendar_user'], $params['calendar_pass'], OP_HALFOPEN);
+            testIMAPError();
+
+            $status = @imap_status($imap, $fullmbox, SA_ALL);
+            myLog('New folder status: ' . print_r($status, true));
+
+            myLog('Trying to reopen calendar box...');
+            @imap_reopen($imap, $fullmbox, CL_EXPUNGE);
+            testIMAPError();
+        }
     }
 
     $connected = true;
@@ -726,7 +794,7 @@
 $options = getopt("s:r:g");
 
 if (!array_key_exists('r', $options)) {
-    print("Usage is $argv[0] -s sender at domain -r resource at domain [-g] [-m mode]");
+    print("Usage is $argv[0] -s sender at domain -r resource at domain [-g] [-m mode]\n");
     shutdown(1);
 }
 
@@ -745,12 +813,12 @@
 $params['calendar_pass'] = $ldapdata['password'];
 $params['action'] = $ldapdata['action'];
 if( !$params['action'] ) {
-  // Use defaults
-  if( $params['group'] ) {
-    $params['action'] = RM_ACT_MANUAL_IF_CONFLICTS;
-  } else {
-    $params['action'] = RM_ACT_REJECT_IF_CONFLICTS;    
-  }
+    // Use defaults
+    if( $params['group'] ) {
+        $params['action'] = RM_ACT_MANUAL_IF_CONFLICTS;
+    } else {
+        $params['action'] = RM_ACT_REJECT_IF_CONFLICTS;
+    }
 }
 
 require_once HORDE_BASE . '/lib/core.php';
@@ -769,12 +837,19 @@
 // Get our request from stdin
 getRequest();
 
-// Get the iTip request
-$itip = &getITip();
+// Get the iCalendar data (i.e. the iTip request)
+$iCalendar = &getICal();
+// Get the event details out of the iTip request
+$itip = &$iCalendar->findComponent('VEVENT');
+if ($itip === false) {
+    myLog('No VEVENT found in iCalendar data', RM_LOG_ERROR);
+    shutdown(1, 'No VEVENT found in iCalendar data' );
+}
 
 // What is the request's method? i.e. should we create a new event/cancel an
 // existing event, etc.
-$method = strtoupper($itip->getAttributeDefault('METHOD', 'REQUEST'));
+$method = strtoupper($iCalendar->getAttributeDefault('METHOD',
+    $itip->getAttributeDefault('METHOD', 'REQUEST')));
 
 // What resource are we managing?
 myLog("Processing $method method for $resource", RM_LOG_DEBUG);
@@ -787,6 +862,9 @@
 $organiser = preg_replace('/^mailto:\s*/i', '', $itip->getAttributeDefault('ORGANIZER', ''));
 myLog("Request made by $organiser", RM_LOG_DEBUG);
 
+// What is the events summary?
+$summary = $itip->getAttributeDefault('SUMMARY', '');
+
 $dtstart = $itip->getAttributeDefault('DTSTART', 0);
 $dtend = $itip->getAttributeDefault('DTEND', 0);
 
@@ -807,6 +885,8 @@
     shutdown(0);
 }
 
+$is_update = false;
+$ignore = array();
 imapConnect();
 switch ($method) {
     case 'REQUEST':
@@ -815,15 +895,28 @@
             break;
         }
 
+        // Check if the request is actually an update of an existing event
+        $updated_messages = @imap_sort($imap, SORTDATE, 0, SE_UID, 'SUBJECT "' . $uid . '"');
+        testIMAPError();
+        if (!empty($updated_messages)) {
+            myLog("Updating message $uid");
+            $ignore[] = $uid;
+            $is_update = true;
+
+            if (!is_array($updated_messages)) {
+                $updated_messages = array($updated_messages);
+            }
+        }
+
         // Don't even bother checking free/busy info if RM_ACT_ALWAYS_ACCEPT
         // is specified
         if ($params['action'] != RM_ACT_ALWAYS_ACCEPT) {
             // Get the resource's free/busy list
             $vfb = &getFreeBusy();
 
-	    if( !$vfb ) {
-	      shutdown(1,"Error building free/busy list");
-	    }
+            if (!$vfb) {
+                shutdown(1, "Error building free/busy list");
+            }
 
             $vfbstart = $vfb->getAttributeDefault('DTSTART', 0);
             $vfbend = $vfb->getAttributeDefault('DTEND', 0);
@@ -837,7 +930,7 @@
             foreach ($busyperiods as $busyfrom => $busyto) {
                 myLog('Busy period from ' . strftime('%a, %d %b %Y %H:%M:%S %z', $busyfrom) . ' to ' . strftime('%a, %d %b %Y %H:%M:%S %z', $busyto), RM_LOG_DEBUG);
 
-                if (($dtstart >= $busyfrom && $dtstart < $busyto) || ($dtend <= $busyto && $dtend > $busyfrom)) {
+                if (($busyfrom >= $dtstart && $busyfrom < $dtend) || ($dtstart >= $busyfrom && $dtstart < $busyto)) {
                     myLog('Request overlaps', RM_LOG_DEBUG);
                     $conflict = true;
                 }
@@ -845,9 +938,9 @@
 
             if ($conflict) {
                 if ($params['action'] == RM_ACT_MANUAL_IF_CONFLICTS) {
-		    //myLog("Conflict detected; tentatively accepting");
-		    //sendITipReply(RM_ITIP_TENTATIVE);
-		    myLog("Conflict detected; Passing mail through");
+                    //myLog("Conflict detected; tentatively accepting");
+                    //sendITipReply(RM_ITIP_TENTATIVE);
+                    myLog("Conflict detected; Passing mail through");
                     break;
                 } else if ($params['action'] == RM_ACT_REJECT_IF_CONFLICTS) {
                     myLog("Conflict detected; rejecting");
@@ -885,6 +978,7 @@
         $headers->addHeader("User-Agent", "proko2/resmgr");
         $headers->addHeader("Reply-To", "");
         $headers->addHeader("Date", date("r"));
+        $headers->addHeader("X-Kolab-Type", "application/x-vnd.kolab.event");
         $headers->addMIMEHeaders($message);
 
         $message = preg_replace("/\r\n|\n|\r/s", "\r\n",
@@ -925,6 +1019,11 @@
 
         sendITipReply(RM_ITIP_ACCEPT);
 
+        // Delete any old events that we updated
+        foreach ($updated_messages as $mid) {
+            @imap_delete($imap, $mid, FT_UID);
+        }
+
         shutdown(0);
 
     case 'CANCEL':
@@ -932,15 +1031,68 @@
 
         imapConnect();
 
-        // Delete the event
-        $messages = @imap_sort($imap, SORTDATE, 0, SE_UID, 'BODY "<uid>" BODY "' . $uid . '" BODY "</uid>"');
+        // Try to delete the event
+        $deleted_messages = @imap_sort($imap, SORTDATE, 0, SE_UID, 'SUBJECT "' . $uid . '"');
         testIMAPError();
+        if (empty($deleted_messages)) {
+            myLog("Canceled event $uid is not present in $resource's calendar", RM_LOG_WARN);
+            $body = sprintf(_("The canceled event %s is not present in %s's calendar."), $summary, $resource);
+            $subject = sprintf(_("Error canceling event %s"), $summary);
+        } else {
+            $body = sprintf(_("Event %s has been successfully removed from %s's calendar."), $summary, $resource);
+            $subject = sprintf(_("Confirmation of cancellation of event %s"), $summary);
+        }
 
-        if (!is_array($messages)) {
-            $messages = array($messages);
+        if (!is_array($deleted_messages)) {
+            $deleted_messages = array($deleted_messages);
         }
 
-        foreach ($messages as $mid) {
+        myLog("Sending confirmation of cancelation to $organiser");
+        $body = &new MIME_Part('text/plain', Text::wrap($body, 76, "\n"));
+        $mime = &MIME_Message::convertMimePart($body);
+
+        // Build the reply headers.
+        $msg_headers = &new MIME_Headers();
+        $msg_headers->addReceivedHeader();
+        $msg_headers->addMessageIdHeader();
+        $msg_headers->addHeader('Date', date('r'));
+        $msg_headers->addHeader('From', $resource);
+        $msg_headers->addHeader('To', $organiser);
+        $msg_headers->addHeader('Subject', $subject);
+        $msg_headers->addMIMEHeaders($mime);
+
+        // Send the reply.
+        static $mailer;
+
+        if (!isset($mailer)) {
+            require_once 'Mail.php';
+            $mailer = &Mail::factory('SMTP', array('auth' => false));
+        }
+
+        $msg = $mime->toString();
+        if (is_object($msg_headers)) {
+            $headerArray = $mime->encode($msg_headers->toArray(), $mime->getCharset());
+        } else {
+            $headerArray = $mime->encode($msg_headers, $mime->getCharset());
+        }
+
+        /* Make sure the message has a trailing newline. */
+        if (substr($msg, -1) != "\n") {
+            $msg .= "\n";
+        }
+
+        $status = $mailer->send(MIME::encodeAddress($organiser), $headerArray, $msg);
+
+        //$status = $mime->send($organiser, $msg_headers);
+        if (is_a($status, 'PEAR_Error')) {
+            myLog("Unable to send cancellation reply: " . $status->getMessage(), RM_LOG_ERROR);
+            shutdown(1, "Unable to send cancellation reply: " . $status->getMessage());
+        } else {
+            myLog("Successfully sent cancellation reply");
+        }
+
+        // Delete the messages from IMAP
+        foreach ($deleted_messages as $mid) {
             @imap_delete($imap, $mid, FT_UID);
         }
 
@@ -957,7 +1109,7 @@
 
 // Pass the message through to the group's mailbox
 myLog("Passing through $method method to $resource");
-send_lmtp(  $sender, $resource, $requestText );
+sendLMTP($sender, $resource, $requestText);
 shutdown(0);
 
 ?>





More information about the commits mailing list