Пример #1
0
 /**
  * Sends a message to an email address supposed to be added to the
  * identity.
  *
  * A message is send to this address containing a time-sensitive link to
  * confirm that the address really belongs to that user.
  *
  * @param integer $id       The identity's ID.
  * @param string $old_addr  The old From: address.
  *
  * @throws Horde_Mime_Exception
  */
 public function verifyIdentity($id, $old_addr)
 {
     global $injector, $notification, $registry;
     $hash = strval(new Horde_Support_Randomid());
     $pref = $this->_confirmEmail();
     $pref[$hash] = $this->get($id);
     $pref[$hash][self::EXPIRE] = time() + self::EXPIRE_SECS;
     $this->_confirmEmail($pref);
     $new_addr = $this->getValue($this->_prefnames['from_addr'], $id);
     $confirm = Horde::url($registry->getServiceLink('emailconfirm')->add('h', $hash)->setRaw(true), true);
     $message = sprintf(Horde_Core_Translation::t("You have requested to add the email address \"%s\" to the list of your personal email addresses.\n\nGo to the following link to confirm that this is really your address:\n%s\n\nIf you don't know what this message means, you can delete it."), $new_addr, $confirm);
     $msg_headers = new Horde_Mime_Headers();
     $msg_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create());
     $msg_headers->addHeaderOb(Horde_Mime_Headers_UserAgent::create());
     $msg_headers->addHeaderOb(Horde_Mime_Headers_Date::create());
     $msg_headers->addHeader('To', $new_addr);
     $msg_headers->addHeader('From', $old_addr);
     $msg_headers->addHeader('Subject', Horde_Core_Translation::t("Confirm new email address"));
     $body = new Horde_Mime_Part();
     $body->setType('text/plain');
     $body->setContents(Horde_String::wrap($message, 76));
     $body->setCharset('UTF-8');
     $body->send($new_addr, $msg_headers, $injector->getInstance('Horde_Mail'));
     $notification->push(sprintf(Horde_Core_Translation::t("A message has been sent to \"%s\" to verify that this is really your address. The new email address is activated as soon as you confirm this message."), $new_addr), 'horde.message');
 }
Пример #2
0
 /**
  * Variables required in form input:
  *   - identity (TODO: ? Code uses it, but it is never set anywhere)
  *   - imple_submit: itip_action(s)
  *   - mime_id
  *   - muid
  *
  * @return boolean  True on success.
  */
 protected function _handle(Horde_Variables $vars)
 {
     global $injector, $notification, $registry;
     $actions = (array) $vars->imple_submit;
     $result = false;
     $vCal = new Horde_Icalendar();
     /* Retrieve the calendar data from the message. */
     try {
         $contents = $injector->getInstance('IMP_Factory_Contents')->create(new IMP_Indices_Mailbox($vars));
         $mime_part = $contents->getMIMEPart($vars->mime_id);
         if (empty($mime_part)) {
             throw new IMP_Exception(_("Cannot retrieve calendar data from message."));
         } elseif (!$vCal->parsevCalendar($mime_part->getContents(), 'VCALENDAR', $mime_part->getCharset())) {
             throw new IMP_Exception(_("The calendar data is invalid"));
         }
         $components = $vCal->getComponents();
     } catch (Exception $e) {
         $notification->push($e, 'horde.error');
         $actions = array();
     }
     foreach ($actions as $key => $action) {
         $pos = strpos($key, '[');
         $key = substr($key, $pos + 1, strlen($key) - $pos - 2);
         switch ($action) {
             case 'delete':
                 // vEvent cancellation.
                 if ($registry->hasMethod('calendar/delete')) {
                     $guid = $components[$key]->getAttribute('UID');
                     $recurrenceId = null;
                     try {
                         // This is a cancellation of a recurring event instance.
                         $recurrenceId = $components[$key]->getAttribute('RECURRENCE-ID');
                         $atts = $components[$key]->getAttribute('RECURRENCE-ID', true);
                         $range = null;
                         foreach ($atts as $att) {
                             if (array_key_exists('RANGE', $att)) {
                                 $range = $att['RANGE'];
                             }
                         }
                     } catch (Horde_Icalendar_Exception $e) {
                     }
                     try {
                         $registry->call('calendar/delete', array($guid, $recurrenceId, $range));
                         $notification->push(_("Event successfully deleted."), 'horde.success');
                         $result = true;
                     } catch (Horde_Exception $e) {
                         $notification->push(sprintf(_("There was an error deleting the event: %s"), $e->getMessage()), 'horde.error');
                     }
                 } else {
                     $notification->push(_("This action is not supported."), 'horde.warning');
                 }
                 break;
             case 'update':
                 // vEvent reply.
                 if ($registry->hasMethod('calendar/updateAttendee')) {
                     try {
                         $from = $contents->getHeader()->getOb('from');
                         $registry->call('calendar/updateAttendee', array($components[$key], $from[0]->bare_address));
                         $notification->push(_("Respondent Status Updated."), 'horde.success');
                         $result = true;
                     } catch (Horde_Exception $e) {
                         $notification->push(sprintf(_("There was an error updating the event: %s"), $e->getMessage()), 'horde.error');
                     }
                 } else {
                     $notification->push(_("This action is not supported."), 'horde.warning');
                 }
                 break;
             case 'import':
             case 'accept-import':
                 // vFreebusy reply.
                 // vFreebusy publish.
                 // vEvent request.
                 // vEvent publish.
                 // vTodo publish.
                 // vJournal publish.
                 switch ($components[$key]->getType()) {
                     case 'vEvent':
                         $result = $this->_handlevEvent($key, $components, $mime_part);
                         // Must check for exceptions.
                         foreach ($components as $k => $component) {
                             try {
                                 if ($component->getType() == 'vEvent' && $component->getAttribute('RECURRENCE-ID')) {
                                     $uid = $component->getAttribute('UID');
                                     if ($uid == $components[$key]->getAttribute('UID')) {
                                         $this->_handlevEvent($k, $components, $mime_part);
                                     }
                                 }
                             } catch (Horde_Icalendar_Exception $e) {
                             }
                         }
                         break;
                     case 'vFreebusy':
                         // Import into Kronolith.
                         if ($registry->hasMethod('calendar/import_vfreebusy')) {
                             try {
                                 $registry->call('calendar/import_vfreebusy', array($components[$key]));
                                 $notification->push(_("The user's free/busy information was sucessfully stored."), 'horde.success');
                                 $result = true;
                             } catch (Horde_Exception $e) {
                                 $notification->push(sprintf(_("There was an error importing user's free/busy information: %s"), $e->getMessage()), 'horde.error');
                             }
                         } else {
                             $notification->push(_("This action is not supported."), 'horde.warning');
                         }
                         break;
                     case 'vTodo':
                         // Import into Nag.
                         if ($registry->hasMethod('tasks/import')) {
                             try {
                                 $guid = $registry->call('tasks/import', array($components[$key], $mime_part->getType()));
                                 $url = Horde::url($registry->link('tasks/show', array('uid' => $guid)));
                                 $notification->push(_("The task has been added to your tasklist.") . '&nbsp;' . Horde::link($url, _("View task"), null, '_blank') . Horde_Themes_Image::tag('mime/icalendar.png', array('alt' => _("View task"))) . '</a>', 'horde.success', array('content.raw'));
                                 $result = true;
                             } catch (Horde_Exception $e) {
                                 $notification->push(sprintf(_("There was an error importing the task: %s"), $e->getMessage()), 'horde.error');
                             }
                         } else {
                             $notification->push(_("This action is not supported."), 'horde.warning');
                         }
                         break;
                     case 'vJournal':
                     default:
                         $notification->push(_("This action is not supported."), 'horde.warning');
                 }
                 if ($action == 'import') {
                     break;
                 }
                 // Fall-through for 'accept-import'
             // Fall-through for 'accept-import'
             case 'accept':
             case 'deny':
             case 'tentative':
                 // vEvent request.
                 if (isset($components[$key]) && $components[$key]->getType() == 'vEvent') {
                     $vEvent = $components[$key];
                     $resource = new Horde_Itip_Resource_Identity($injector->getInstance('IMP_Identity'), $vEvent->getAttribute('ATTENDEE'), $vars->identity);
                     switch ($action) {
                         case 'accept':
                         case 'accept-import':
                             $type = new Horde_Itip_Response_Type_Accept($resource);
                             break;
                         case 'deny':
                             $type = new Horde_Itip_Response_Type_Decline($resource);
                             break;
                         case 'tentative':
                             $type = new Horde_Itip_Response_Type_Tentative($resource);
                             break;
                     }
                     try {
                         // Send the reply.
                         Horde_Itip::factory($vEvent, $resource)->sendMultiPartResponse($type, new Horde_Core_Itip_Response_Options_Horde('UTF-8', array()), $injector->getInstance('IMP_Mail'));
                         $notification->push(_("Reply Sent."), 'horde.success');
                         $result = true;
                     } catch (Horde_Itip_Exception $e) {
                         $notification->push(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error');
                     }
                 } else {
                     $notification->push(_("This action is not supported."), 'horde.warning');
                 }
                 break;
             case 'send':
             case 'reply':
             case 'reply2m':
                 // vfreebusy request.
                 if (isset($components[$key]) && $components[$key]->getType() == 'vFreebusy') {
                     $vFb = $components[$key];
                     // Get the organizer details.
                     try {
                         $organizer = parse_url($vFb->getAttribute('ORGANIZER'));
                     } catch (Horde_Icalendar_Exception $e) {
                         break;
                     }
                     $organizerEmail = $organizer['path'];
                     $organizer = $vFb->getAttribute('ORGANIZER', true);
                     $organizerFullEmail = new Horde_Mail_Rfc822_Address($organizerEmail);
                     if (isset($organizer['cn'])) {
                         $organizerFullEmail->personal = $organizer['cn'];
                     }
                     if ($action == 'reply2m') {
                         $startStamp = time();
                         $endStamp = $startStamp + 60 * 24 * 3600;
                     } else {
                         try {
                             $startStamp = $vFb->getAttribute('DTSTART');
                         } catch (Horde_Icalendar_Exception $e) {
                             $startStamp = time();
                         }
                         try {
                             $endStamp = $vFb->getAttribute('DTEND');
                         } catch (Horde_Icalendar_Exception $e) {
                         }
                         if (!$endStamp) {
                             try {
                                 $duration = $vFb->getAttribute('DURATION');
                                 $endStamp = $startStamp + $duration;
                             } catch (Horde_Icalendar_Exception $e) {
                                 $endStamp = $startStamp + 60 * 24 * 3600;
                             }
                         }
                     }
                     $vfb_reply = $registry->call('calendar/getFreeBusy', array($startStamp, $endStamp));
                     // Find out who we are and update status.
                     $identity = $injector->getInstance('IMP_Identity');
                     $email = $identity->getFromAddress();
                     // Build the reply.
                     $msg_headers = new Horde_Mime_Headers();
                     $vCal = new Horde_Icalendar();
                     $vCal->setAttribute('PRODID', '-//The Horde Project//' . $msg_headers->getUserAgent() . '//EN');
                     $vCal->setAttribute('METHOD', 'REPLY');
                     $vCal->addComponent($vfb_reply);
                     $message = _("Attached is a reply to a calendar request you sent.");
                     $body = new Horde_Mime_Part();
                     $body->setType('text/plain');
                     $body->setCharset('UTF-8');
                     $body->setContents(Horde_String::wrap($message, 76));
                     $ics = new Horde_Mime_Part();
                     $ics->setType('text/calendar');
                     $ics->setCharset('UTF-8');
                     $ics->setContents($vCal->exportvCalendar());
                     $ics->setName('icalendar.ics');
                     $ics->setContentTypeParameter('METHOD', 'REPLY');
                     $mime = new Horde_Mime_Part();
                     $mime->addPart($body);
                     $mime->addPart($ics);
                     // Build the reply headers.
                     $msg_headers->addReceivedHeader(array('dns' => $injector->getInstance('Net_DNS2_Resolver'), 'server' => $conf['server']['name']));
                     $msg_headers->addMessageIdHeader();
                     $msg_headers->addHeader('Date', date('r'));
                     $msg_headers->addHeader('From', $email);
                     $msg_headers->addHeader('To', $organizerFullEmail);
                     $identity->setDefault($vars->identity);
                     $replyto = $identity->getValue('replyto_addr');
                     if (!empty($replyto) && !$email->match($replyto)) {
                         $msg_headers->addHeader('Reply-To', $replyto);
                     }
                     $msg_headers->addHeader('Subject', _("Free/Busy Request Response"));
                     // Send the reply.
                     try {
                         $mime->send($organizerEmail, $msg_headers, $injector->getInstance('IMP_Mail'));
                         $notification->push(_("Reply Sent."), 'horde.success');
                         $result = true;
                     } catch (Exception $e) {
                         $notification->push(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error');
                     }
                 } else {
                     $notification->push(_("Invalid Action selected for this component."), 'horde.warning');
                 }
                 break;
             case 'nosup':
                 // vFreebusy request.
             // vFreebusy request.
             default:
                 $notification->push(_("This action is not supported."), 'horde.warning');
                 break;
         }
     }
     return $result;
 }
Пример #3
0
 /**
  * Return the response as a MIME message.
  *
  * @param Horde_Itip_Response_Type $type       The response type.
  * @param Horde_Itip_Response_Options $options The options for the response.
  *
  * @return array A list of two object: The mime headers and the mime
  *               message.
  */
 public function getMultiPartMessage(Horde_Itip_Response_Type $type, Horde_Itip_Response_Options $options)
 {
     $message = new Horde_Mime_Part();
     $message->setType('multipart/alternative');
     list($headers, $ics) = $this->getMessage($type, $options);
     $body = new Horde_Mime_Part();
     $body->setType('text/plain');
     $options->prepareMessageMimePart($body);
     $body->setContents(Horde_String::wrap($type->getMessage(), 76));
     $message->addPart($body);
     $message->addPart($ics);
     return array($headers, $message);
 }
Пример #4
0
 /**
  * @return string  A tooltip for quick descriptions of this event.
  */
 public function getTooltip()
 {
     $tooltip = $this->getTimeRange() . "\n" . sprintf(_("Owner: %s"), $this->creator == $GLOBALS['registry']->getAuth() ? _("Me") : Kronolith::getUserName($this->creator));
     if (!$this->isPrivate()) {
         if ($this->location) {
             $tooltip .= "\n" . _("Location") . ': ' . $this->location;
         }
         if ($this->description) {
             $tooltip .= "\n\n" . Horde_String::wrap($this->description);
         }
     }
     return $tooltip;
 }
Пример #5
0
 /**
  * Returns a hash with all information necessary to reply to a message.
  *
  * @param mixed $message  The ID of the parent message to reply to, or arry of its data.
  *
  * @return array  A hash with all relevant information.
  * @throws Horde_Exception_NotFound
  * @throws Agora_Exception
  */
 public function replyMessage($message)
 {
     if (!is_array($message)) {
         $message = $this->getMessage($message);
     }
     /* Set up the form subject with the parent subject. */
     if (Horde_String::lower(Horde_String::substr($message['message_subject'], 0, 3)) != 're:') {
         $message['message_subject'] = 'Re: ' . $message['message_subject'];
     }
     /* Prepare the message quite body . */
     $message['body'] = sprintf(_("Posted by %s on %s"), htmlspecialchars($message['message_author']), strftime($GLOBALS['prefs']->getValue('date_format'), $message['message_timestamp'])) . "\n-------------------------------------------------------\n" . $message['body'];
     $message['body'] = "\n> " . Horde_String::wrap($message['body'], 60, "\n> ");
     return $message;
 }
Пример #6
0
 /**
  * Returns the provided story as a MIME part.
  *
  * @param array $story  A data array representing a story.
  *
  * @return MIME_Part  The MIME message part containing the story parts.
  * @TODO: Refactor to use new Horde MIME library
  */
 protected function getStoryAsMessage($story)
 {
     require_once 'Horde/MIME/Part.php';
     /* Add the story to the message based on the story's body type. */
     switch ($story['body_type']) {
         case 'richtext':
             /* Get a plain text version of a richtext story. */
             $body_html = $story['body'];
             $body_text = $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($body_html, 'html2text');
             /* Add description. */
             $body_html = '<p>' . $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($story['desc'], 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO, 'callback' => null)) . "</p>\n" . $body_html;
             $body_text = Horde_String::wrap('  ' . $story['description'], 70) . "\n\n" . $body_text;
             /* Add the text version of the story to the base message. */
             $message_text = new MIME_Part('text/plain');
             $message_text->setCharset('UTF-8');
             $message_text->setContents($message_text->replaceEOL($body_text));
             $message_text->setDescription(_("Plaintext Version of Story"));
             /* Add an HTML version of the story to the base message. */
             $message_html = new MIME_Part('text/html', Horde_String::wrap($body_html), 'UTF-8', 'inline');
             $message_html->setDescription(_("HTML Version of Story"));
             /* Add the two parts as multipart/alternative. */
             $basepart = new MIME_Part('multipart/alternative');
             $basepart->addPart($message_text);
             $basepart->addPart($message_html);
             return $basepart;
         case 'text':
             /* This is just a plain text story. */
             $message_text = new MIME_Part('text/plain');
             $message_text->setContents($message_text->replaceEOL($story['description'] . "\n\n" . $story['body']));
             $message_text->setCharset('UTF-8');
             return $message_text;
     }
 }
Пример #7
0
 /**
  * Sets the message body text.
  *
  * @param string $body             The message content.
  * @param string $charset          The character set of the message.
  * @param boolean|integer $wrap    If true, wrap the message at column 76;
  *                                 If an integer wrap the message at that
  *                                 column. Don't use wrapping if sending
  *                                 flowed messages.
  */
 public function setBody($body, $charset = null, $wrap = false)
 {
     if (!$charset) {
         $charset = $this->_charset;
     }
     $body = Horde_String::convertCharset($body, 'UTF-8', $charset);
     if ($wrap) {
         $body = Horde_String::wrap($body, $wrap === true ? 76 : $wrap);
     }
     $this->_body = new Horde_Mime_Part();
     $this->_body->setType('text/plain');
     $this->_body->setCharset($charset);
     $this->_body->setContents($body);
     $this->_base = null;
 }
Пример #8
0
 /**
  * Add a change log entry to CHANGES
  *
  * @param string $entry   Change log entry to add.
  * @param string $changes Path to the CHANGES file.
  *
  * @return NULL
  */
 public function addChange($entry, $changes)
 {
     $tmp = Horde_Util::getTempFile();
     $entry = Horde_String::wrap($entry, 79, "\n      ");
     $oldfp = fopen($changes, 'r');
     $newfp = fopen($tmp, 'w');
     $counter = 0;
     while ($line = fgets($oldfp)) {
         if ($counter == 4) {
             fwrite($newfp, $entry . "\n");
         }
         $counter++;
         fwrite($newfp, $line);
     }
     fclose($oldfp);
     fclose($newfp);
     system("mv -f {$tmp} {$changes}");
 }
Пример #9
0
 /**
  * Returns a plain text representation of a ticket.
  */
 public function toString()
 {
     $fields = array('queue' => _("Queue"), 'version' => _("Version"), 'type' => _("Type"), 'state' => _("State"), 'priority' => _("Priority"), 'due' => _("Due"));
     /* Find longest translated field name. */
     $length = 0;
     foreach (array_merge($fields, array(_("Summary"), _("Owners"))) as $field) {
         $length = max($length, Horde_String::length($field));
     }
     $wrap_break = "\n" . str_repeat(' ', $length + 2) . '| ';
     $wrap_width = 73 - $length;
     /* Ticket properties. */
     $message = ' ' . Horde_String::pad(_("Ticket"), $length) . ' | ' . $this->_id . "\n" . ' ' . Horde_String::pad(_("Summary"), $length) . ' | ' . Horde_String::wrap($this->get('summary'), $wrap_width, $wrap_break) . "\n";
     foreach ($fields as $field => $label) {
         if ($name = $this->get($field . '_name')) {
             $message .= ' ' . Horde_String::pad($label, $length) . ' | ' . Horde_String::wrap($name, $wrap_width, $wrap_break) . "\n";
         }
     }
     $message .= ' ' . Horde_String::pad(_("Owners"), $length) . ' | ' . Horde_String::wrap(Whups::getOwners($this->_id, false, true), $wrap_width, $wrap_break) . "\n";
     return $message;
 }
Пример #10
0
 /**
  * Send notification to attachment owner.
  */
 public function sendNotification()
 {
     global $conf, $injector, $registry;
     if (empty($conf['compose']['link_attachments_notify'])) {
         return;
     }
     try {
         $identity = $injector->getInstance('Horde_Core_Factory_Identity')->create($this->_user);
         $address = $identity->getDefaultFromAddress();
         /* Ignore missing addresses, which are returned as <>. */
         if (strlen($address) < 3 || $this->_getDeleteToken()) {
             return;
         }
         $address_full = $identity->getDefaultFromAddress(true);
         /* Load user prefs to correctly translate gettext strings. */
         if (!$registry->getAuth()) {
             $prefs = $injector->getInstance('Horde_Core_Factory_Prefs')->create('imp', array('user' => $this->_user));
             $registry->setLanguageEnvironment($prefs->getValue('language'));
         }
         $h = new Horde_Mime_Headers();
         $h->addReceivedHeader(array('dns' => $injector->getInstance('Net_DNS2_Resolver'), 'server' => $conf['server']['name']));
         $h->addMessageIdHeader();
         $h->addUserAgentHeader();
         $h->addHeader('Date', date('r'));
         $h->addHeader('From', $address_full);
         $h->addHeader('To', $address_full);
         $h->addHeader('Subject', _("Notification: Linked attachment downloaded"));
         $h->addHeader('Auto-Submitted', 'auto-generated');
         $msg = new Horde_Mime_Part();
         $msg->setType('text/plain');
         $msg->setCharset('UTF-8');
         $md = $this->_atc->getMetadata();
         $msg->setContents(Horde_String::wrap(_("Your linked attachment has been downloaded by at least one user.") . "\n\n" . sprintf(_("Name: %s"), $md->filename) . "\n" . sprintf(_("Type: %s"), $md->type) . "\n" . sprintf(_("Sent Date: %s"), date('r', $md->time)) . "\n\n" . _("Click on the following link to permanently delete the attachment:") . "\n" . strval($this->_atc->link_url->add('d', $this->_getDeleteToken(true)))));
         $msg->send($address, $h, $injector->getInstance('Horde_Mail'));
     } catch (Exception $e) {
         Horde::log($e, 'ERR');
     }
 }
Пример #11
0
 /**
  * @return string  A tooltip for quick descriptions of this event.
  */
 public function getTooltip()
 {
     return Horde_String::wrap($this->description);
 }
Пример #12
0
 function handleMessage($fqhostname, $sender, $resource, $tmpfname)
 {
     global $conf;
     $rdata = $this->_getResourceData($sender, $resource);
     if (is_a($rdata, 'PEAR_Error')) {
         return $rdata;
     } else {
         if ($rdata === false) {
             /* No data, probably not a local user */
             return true;
         } else {
             if ($rdata['homeserver'] && $rdata['homeserver'] != $fqhostname) {
                 /* Not the users homeserver, ignore */
                 return true;
             }
         }
     }
     $cn = $rdata['cn'];
     $id = $rdata['id'];
     if (isset($rdata['action'])) {
         $action = $rdata['action'];
     } else {
         // Manual is the only safe default!
         $action = RM_ACT_MANUAL;
     }
     Horde::log(sprintf('Action for %s is %s', $sender, $action), 'DEBUG');
     // Get out as early as possible if manual
     if ($action == RM_ACT_MANUAL) {
         Horde::log(sprintf('Passing through message to %s', $id), 'INFO');
         return true;
     }
     /* Get the iCalendar data (i.e. the iTip request) */
     $iCalendar =& $this->_getICal($tmpfname);
     if ($iCalendar === false) {
         // No iCal in mail
         Horde::log(sprintf('Could not parse iCalendar data, passing through to %s', $id), 'INFO');
         return true;
     }
     // Get the event details out of the iTip request
     $itip =& $iCalendar->findComponent('VEVENT');
     if ($itip === false) {
         Horde::log(sprintf('No VEVENT found in iCalendar data, passing through to %s', $id), 'INFO');
         return true;
     }
     $itip = new Horde_Kolab_Resource_Itip($itip);
     // What is the request's method? i.e. should we create a new event/cancel an
     // existing event, etc.
     $method = Horde_String::upper($iCalendar->getAttributeDefault('METHOD', $itip->getMethod()));
     // What resource are we managing?
     Horde::log(sprintf('Processing %s method for %s', $method, $id), 'DEBUG');
     // This is assumed to be constant across event creation/modification/deletipn
     $uid = $itip->getUid();
     Horde::log(sprintf('Event has UID %s', $uid), 'DEBUG');
     // Who is the organiser?
     $organiser = $itip->getOrganizer();
     Horde::log(sprintf('Request made by %s', $organiser), 'DEBUG');
     // What is the events summary?
     $summary = $itip->getSummary();
     $estart = new Horde_Kolab_Resource_Epoch($itip->getStart());
     $dtstart = $estart->getEpoch();
     $eend = new Horde_Kolab_Resource_Epoch($itip->getEnd());
     $dtend = $eend->getEpoch();
     Horde::log(sprintf('Event starts on <%s> %s and ends on <%s> %s.', $dtstart, $this->iCalDate2Kolab($dtstart), $dtend, $this->iCalDate2Kolab($dtend)), 'DEBUG');
     if ($action == RM_ACT_ALWAYS_REJECT) {
         if ($method == 'REQUEST') {
             Horde::log(sprintf('Rejecting %s method', $method), 'INFO');
             return $this->sendITipReply($cn, $resource, $itip, RM_ITIP_DECLINE, $organiser, $uid, $is_update);
         } else {
             Horde::log(sprintf('Passing through %s method for ACT_ALWAYS_REJECT policy', $method), 'INFO');
             return true;
         }
     }
     $is_update = false;
     $imap_error = false;
     $ignore = array();
     $folder = $this->_imapConnect($id);
     if (is_a($folder, 'PEAR_Error')) {
         $imap_error =& $folder;
     }
     if (!is_a($imap_error, 'PEAR_Error') && !$folder->exists()) {
         $imap_error =& PEAR::raiseError('Error, could not open calendar folder!', OUT_LOG | EX_TEMPFAIL);
     }
     if (!is_a($imap_error, 'PEAR_Error')) {
         $data = $folder->getData();
         if (is_a($data, 'PEAR_Error')) {
             $imap_error =& $data;
         }
     }
     if (is_a($imap_error, 'PEAR_Error')) {
         Horde::log(sprintf('Failed accessing IMAP calendar: %s', $folder->getMessage()), 'ERR');
         if ($action == RM_ACT_MANUAL_IF_CONFLICTS) {
             return true;
         }
     }
     switch ($method) {
         case 'REQUEST':
             if ($action == RM_ACT_MANUAL) {
                 Horde::log(sprintf('Passing through %s method', $method), 'INFO');
                 break;
             }
             if (is_a($imap_error, 'PEAR_Error') || !$data->objectUidExists($uid)) {
                 $old_uid = null;
             } else {
                 $old_uid = $uid;
                 $ignore[] = $uid;
                 $is_update = true;
             }
             /** Generate the Kolab object */
             $object = $itip->getKolabObject();
             $outofperiod = 0;
             // Don't even bother checking free/busy info if RM_ACT_ALWAYS_ACCEPT
             // is specified
             if ($action != RM_ACT_ALWAYS_ACCEPT) {
                 try {
                     require_once 'Horde/Kolab/Resource/Freebusy.php';
                     $fb = Horde_Kolab_Resource_Freebusy::singleton();
                     $vfb = $fb->get($resource);
                 } catch (Exception $e) {
                     return PEAR::raiseError($e->getMessage(), OUT_LOG | EX_UNAVAILABLE);
                 }
                 $vfbstart = $vfb->getAttributeDefault('DTSTART', 0);
                 $vfbend = $vfb->getAttributeDefault('DTEND', 0);
                 Horde::log(sprintf('Free/busy info starts on <%s> %s and ends on <%s> %s', $vfbstart, $this->iCalDate2Kolab($vfbstart), $vfbend, $this->iCalDate2Kolab($vfbend)), 'DEBUG');
                 $evfbend = new Horde_Kolab_Resource_Epoch($vfbend);
                 if ($vfbstart && $dtstart > $evfbend->getEpoch()) {
                     $outofperiod = 1;
                 } else {
                     // Check whether we are busy or not
                     $busyperiods = $vfb->getBusyPeriods();
                     Horde::log(sprintf('Busyperiods: %s', print_r($busyperiods, true)), 'DEBUG');
                     $extraparams = $vfb->getExtraParams();
                     Horde::log(sprintf('Extraparams: %s', print_r($extraparams, true)), 'DEBUG');
                     $conflict = false;
                     if (!empty($object['recurrence'])) {
                         $recurrence = new Horde_Date_Recurrence($dtstart);
                         $recurrence->fromHash($object['recurrence']);
                         $duration = $dtend - $dtstart;
                         $events = array();
                         $next_start = $vfbstart;
                         $next = $recurrence->nextActiveRecurrence($vfbstart);
                         while ($next !== false && $next->compareDate($vfbend) <= 0) {
                             $next_ts = $next->timestamp();
                             $events[$next_ts] = $next_ts + $duration;
                             $next = $recurrence->nextActiveRecurrence(array('year' => $next->year, 'month' => $next->month, 'mday' => $next->mday + 1, 'hour' => $next->hour, 'min' => $next->min, 'sec' => $next->sec));
                         }
                     } else {
                         $events = array($dtstart => $dtend);
                     }
                     foreach ($events as $dtstart => $dtend) {
                         Horde::log(sprintf('Requested event from %s to %s', strftime('%a, %d %b %Y %H:%M:%S %z', $dtstart), strftime('%a, %d %b %Y %H:%M:%S %z', $dtend)), 'DEBUG');
                         foreach ($busyperiods as $busyfrom => $busyto) {
                             if (empty($busyfrom) && empty($busyto)) {
                                 continue;
                             }
                             Horde::log(sprintf('Busy period from %s to %s', strftime('%a, %d %b %Y %H:%M:%S %z', $busyfrom), strftime('%a, %d %b %Y %H:%M:%S %z', $busyto)), 'DEBUG');
                             if (isset($extraparams[$busyfrom]['X-UID']) && in_array(base64_decode($extraparams[$busyfrom]['X-UID']), $ignore) || isset($extraparams[$busyfrom]['X-SID']) && in_array(base64_decode($extraparams[$busyfrom]['X-SID']), $ignore)) {
                                 // Ignore
                                 continue;
                             }
                             if ($busyfrom >= $dtstart && $busyfrom < $dtend || $dtstart >= $busyfrom && $dtstart < $busyto) {
                                 Horde::log('Request overlaps', 'DEBUG');
                                 $conflict = true;
                                 break;
                             }
                         }
                         if ($conflict) {
                             break;
                         }
                     }
                     if ($conflict) {
                         if ($action == RM_ACT_MANUAL_IF_CONFLICTS) {
                             //sendITipReply(RM_ITIP_TENTATIVE);
                             Horde::log('Conflict detected; Passing mail through', 'INFO');
                             return true;
                         } else {
                             if ($action == RM_ACT_REJECT_IF_CONFLICTS) {
                                 Horde::log('Conflict detected; rejecting', 'INFO');
                                 return $this->sendITipReply($cn, $id, $itip, RM_ITIP_DECLINE, $organiser, $uid, $is_update);
                             }
                         }
                     }
                 }
             }
             if (is_a($imap_error, 'PEAR_Error')) {
                 Horde::log('Could not access users calendar; rejecting', 'INFO');
                 return $this->sendITipReply($cn, $id, $itip, RM_ITIP_DECLINE, $organiser, $uid, $is_update);
             }
             // At this point there was either no conflict or RM_ACT_ALWAYS_ACCEPT
             // was specified; either way we add the new event & send an 'ACCEPT'
             // iTip reply
             Horde::log(sprintf('Adding event %s', $uid), 'INFO');
             if (!empty($conf['kolab']['filter']['simple_locks'])) {
                 if (!empty($conf['kolab']['filter']['simple_locks_timeout'])) {
                     $timeout = $conf['kolab']['filter']['simple_locks_timeout'];
                 } else {
                     $timeout = 60;
                 }
                 if (!empty($conf['kolab']['filter']['simple_locks_dir'])) {
                     $lockdir = $conf['kolab']['filter']['simple_locks_dir'];
                 } else {
                     $lockdir = Horde::getTempDir() . '/Kolab_Filter_locks';
                     if (!is_dir($lockdir)) {
                         mkdir($lockdir, 0700);
                     }
                 }
                 if (is_dir($lockdir)) {
                     $lockfile = $lockdir . '/' . $resource . '.lock';
                     $counter = 0;
                     while ($counter < $timeout && file_exists($lockfile)) {
                         sleep(1);
                         $counter++;
                     }
                     if ($counter == $timeout) {
                         Horde::log(sprintf('Lock timeout of %s seconds exceeded. Rejecting invitation.', $timeout), 'ERR');
                         return $this->sendITipReply($cn, $id, $itip, RM_ITIP_DECLINE, $organiser, $uid, $is_update);
                     }
                     $result = file_put_contents($lockfile, 'LOCKED');
                     if ($result === false) {
                         Horde::log(sprintf('Failed creating lock file %s.', $lockfile), 'ERR');
                     } else {
                         $this->lockfile = $lockfile;
                     }
                 } else {
                     Horde::log(sprintf('The lock directory %s is missing. Disabled locking.', $lockdir), 'ERR');
                 }
             }
             $itip->setAccepted($resource);
             $result = $data->save($itip->getKolabObject(), $old_uid);
             if (is_a($result, 'PEAR_Error')) {
                 $result->code = OUT_LOG | EX_UNAVAILABLE;
                 return $result;
             }
             if ($outofperiod) {
                 Horde::log('No freebusy information available', 'NOTICE');
                 return $this->sendITipReply($cn, $resource, $itip, RM_ITIP_TENTATIVE, $organiser, $uid, $is_update);
             } else {
                 return $this->sendITipReply($cn, $resource, $itip, RM_ITIP_ACCEPT, $organiser, $uid, $is_update);
             }
         case 'CANCEL':
             Horde::log(sprintf('Removing event %s', $uid), 'INFO');
             if (is_a($imap_error, 'PEAR_Error')) {
                 $body = sprintf(Horde_Kolab_Resource_Translation::t("Unable to access %s's calendar:"), $resource) . "\n\n" . $summary;
                 $subject = sprintf(Horde_Kolab_Resource_Translation::t("Error processing \"%s\""), $summary);
             } else {
                 if (!$data->objectUidExists($uid)) {
                     Horde::log(sprintf('Canceled event %s is not present in %s\'s calendar', $uid, $resource), 'WARNING');
                     $body = sprintf(Horde_Kolab_Resource_Translation::t("The following event that was canceled is not present in %s's calendar:"), $resource) . "\n\n" . $summary;
                     $subject = sprintf(Horde_Kolab_Resource_Translation::t("Error processing \"%s\""), $summary);
                 } else {
                     /**
                      * Delete the messages from IMAP
                      * Delete any old events that we updated
                      */
                     Horde::log(sprintf('Deleting %s because of cancel', $uid), 'DEBUG');
                     $result = $data->delete($uid);
                     if (is_a($result, 'PEAR_Error')) {
                         Horde::log(sprintf('Deleting %s failed with %s', $uid, $result->getMessage()), 'DEBUG');
                     }
                     $body = Horde_Kolab_Resource_Translation::t("The following event has been successfully removed:") . "\n\n" . $summary;
                     $subject = sprintf(Horde_Kolab_Resource_Translation::t("%s has been cancelled"), $summary);
                 }
             }
             Horde::log(sprintf('Sending confirmation of cancelation to %s', $organiser), 'WARNING');
             $body = new MIME_Part('text/plain', Horde_String::wrap($body, 76));
             $mime =& MIME_Message::convertMimePart($body);
             $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', $resource);
             $msg_headers->addHeader('To', $organiser);
             $msg_headers->addHeader('Subject', $subject);
             $msg_headers->addMIMEHeaders($mime);
             $reply = new Horde_Kolab_Resource_Reply($resource, $organiser, $msg_headers, $mime);
             Horde::log('Successfully prepared cancellation reply', 'INFO');
             return $reply;
         default:
             // We either don't currently handle these iTip methods, or they do not
             // apply to what we're trying to accomplish here
             Horde::log(sprintf('Ignoring %s method and passing message through to %s', $method, $resource), 'INFO');
             return true;
     }
 }