/** * 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)); if (!($mime_part = $contents->getMimePart($vars->mime_id))) { throw new IMP_Exception(_("Cannot retrieve calendar data from message.")); } if ($vars->ctype) { $mime_part = clone $mime_part; $mime_part->setType($vars->ctype); } if (!$vCal->parsevCalendar($mime_part->getContents(), 'VCALENDAR', $mime_part->getCharset())) { throw new IMP_Exception(_("The calendar data is invalid")); } $components = $vCal->getComponents(); $v1 = $vCal->getAttribute('VERSION') == '1.0'; } 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]->getAttributeSingle('UID'); $recurrenceId = null; $range = null; try { // This is a cancellation of a recurring event instance. $recurrenceId = $components[$key]->getAttributeSingle('RECURRENCE-ID'); $atts = $components[$key]->getAttribute('RECURRENCE-ID', true); 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. // vTodo reply. switch ($components[$key]->getType()) { case 'vEvent': if ($registry->hasMethod('calendar/updateAttendee')) { try { if ($tmp = $contents->getHeader()->getHeader('from')) { $registry->call('calendar/updateAttendee', array($components[$key], $tmp->getAddressList(true)->first()->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 'vTodo': if ($registry->hasMethod('tasks/updateAttendee')) { try { if ($tmp = $contents->getHeader()->getHeader('from')) { $registry->call('tasks/updateAttendee', array($components[$key], $tmp->getAddressList(true)->first()->bare_address)); $notification->push(_("Respondent Status Updated."), 'horde.success'); $result = true; } } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error updating the task: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; } break; case 'import': case 'accept-import': // vFreebusy reply. // vFreebusy publish. // vEvent request. // vEvent publish. // vTodo publish. // vJournal publish. switch ($components[$key]->getType()) { case 'vEvent': // If we have accepted and are importing, update the // user's attendance status in the vCal so it will be // reflected when it is imported. if ($action == 'accept-import') { try { $a = $components[$key]->getAttribute('ATTENDEE'); if (!is_array($a)) { $a = array($a); } $a_params = $components[$key]->getAttribute('ATTENDEE', true); foreach ($a as $a_key => $attendee) { $attendee_email = preg_replace('/mailto:/i', '', $attendee); $identity = $injector->getInstance('IMP_Identity'); if (!is_null($id = $identity->getMatchingIdentity($attendee_email))) { $components[$key]->removeAttribute('ATTENDEE'); if ($v1) { $a_params[$a_key]['STATUS'] = 'ACCEPTED'; } else { $a_params[$a_key]['PARTSTAT'] = 'ACCEPTED'; } foreach ($a as $ai_key => $i_attendee) { $components[$key]->setAttribute('ATTENDEE', $i_attendee, $a_params[$ai_key]); } break; } } } catch (Horde_Icalendar_Exception $e) { $notification->push(sprintf(_("There was an error updating attendee status: %s"), $e->getMessage()), 'horde.error'); } } // Handle the import, and check for exceptions. $result = $this->_handlevEvent($key, $components, $mime_part); foreach ($components as $k => $component) { try { if ($component->getType() == 'vEvent' && $component->getAttribute('RECURRENCE-ID')) { $uid = $component->getAttributeSingle('UID'); if ($uid == $components[$key]->getAttributeSingle('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.") . ' ' . 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' || $components[$key]->getType() == 'vTodo')) { $vEvent = $components[$key]; try { $resource = new Horde_Itip_Resource_Identity($injector->getInstance('IMP_Identity'), $vEvent->getAttribute('ATTENDEE'), $vars->identity); } catch (Horde_Icalendar_Exception $e) { throw new Horde_Itip_Exception('No ATTENDEE data, unable to reply.'); } 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 { if ($vEvent->getType() == 'vEvent') { // Send the reply. Horde_Itip::factory($vEvent, $resource)->sendMultiPartResponse($type, new Horde_Core_Itip_Response_Options_Horde('UTF-8', array()), $injector->getInstance('IMP_Mail')); } elseif ($vEvent->getType() == 'vTodo') { Horde_Itip::vTodoFactory($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->getAttributeSingle('ORGANIZER')); } catch (Horde_Icalendar_Exception $e) { break; } $organizerEmail = $organizer['path']; $organizer = $vFb->getAttributeSingle('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->getAttributeSingle('DTSTART'); } catch (Horde_Icalendar_Exception $e) { $startStamp = time(); } try { $endStamp = $vFb->getAttributeSingle('DTEND'); } catch (Horde_Icalendar_Exception $e) { } if (!$endStamp) { try { $duration = $vFb->getAttributeSingle('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(); $vCal = new Horde_Icalendar(); $vCal->setAttribute('PRODID', '-//The Horde Project//' . strval(Horde_Mime_Headers_UserAgent::create()) . '//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[] = $body; $mime[] = $ics; // Build the reply headers. $msg_headers = new Horde_Mime_Headers(); $msg_headers->addHeaderOb(Horde_Core_Mime_Headers_Received::createHordeHop()); $msg_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create()); $msg_headers->addHeaderOb(Horde_Mime_Headers_Date::create()); $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; }
/** */ public function prepareResponseMimeHeaders(Horde_Mime_Headers $headers) { $headers->addHeaderOb(Horde_Core_Mime_Headers_Received::createHordeHop()); parent::prepareResponseMimeHeaders($headers); }
/** * Builds and sends a MIME message. * * @param string $body The message body. * @param array $header List of message headers. * @param IMP_Prefs_Identity $identity The Identity object for the sender * of this message. * @param array $opts An array of options w/the * following keys: * - encrypt: (integer) A flag whether to encrypt or sign the message. * One of: * - IMP_Pgp::ENCRYPT</li> * - IMP_Pgp::SIGNENC</li> * - IMP_Smime::ENCRYPT</li> * - IMP_Smime::SIGNENC</li> * - html: (boolean) Whether this is an HTML message. * DEFAULT: false * - pgp_attach_pubkey: (boolean) Attach the user's PGP public key to the * message? * - priority: (string) The message priority ('high', 'normal', 'low'). * - save_sent: (boolean) Save sent mail? * - sent_mail: (IMP_Mailbox) The sent-mail mailbox (UTF-8). * - strip_attachments: (bool) Strip attachments from the message? * - signature: (string) The message signature. * - readreceipt: (boolean) Add return receipt headers? * - useragent: (string) The User-Agent string to use. * - vcard_attach: (string) Attach the user's vCard (value is name to * display as vcard filename). * * @throws Horde_Exception * @throws IMP_Compose_Exception * @throws IMP_Compose_Exception_Address * @throws IMP_Exception */ public function buildAndSendMessage($body, $header, IMP_Prefs_Identity $identity, array $opts = array()) { global $injector, $prefs, $registry, $session; /* Set up defaults. */ $opts = array_merge(array('encrypt' => IMP::ENCRYPT_NONE), $opts); /* Check body size of message. */ $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create(); if (!$imp_imap->accessCompose(IMP_Imap::ACCESS_COMPOSE_BODYSIZE, strlen($body))) { Horde::permissionDeniedError('imp', 'max_bodysize'); throw new IMP_Compose_Exception(sprintf(_("Your message body has exceeded the limit by body size by %d characters."), strlen($body) - $imp_imap->max_compose_bodysize)); } /* We need at least one recipient. */ $recip = $this->recipientList($header); if (!count($recip['list'])) { if ($recip['has_input']) { throw new IMP_Compose_Exception(_("Invalid e-mail address.")); } throw new IMP_Compose_Exception(_("Need at least one message recipient.")); } /* Recipient checks. */ $this->_prepSendMessageAssert($recip['list']); /* Check for correct identity usage. */ if (!$this->getMetadata('identity_check') && count($recip['list']) === 1) { $identity_search = $identity->getMatchingIdentity($recip['list'], false); if (!is_null($identity_search) && $identity->getDefault() != $identity_search) { $this->_setMetadata('identity_check', true); $e = new IMP_Compose_Exception(_("Recipient address does not match the currently selected identity.")); $e->tied_identity = $identity_search; throw $e; } } /* Initalize a header object for the outgoing message. */ $headers = $this->_prepareHeaders($header, $opts); /* Add a Received header for the hop from browser to server. */ $headers->addHeaderOb(Horde_Core_Mime_Headers_Received::createHordeHop()); /* Add the 'User-Agent' header. */ $headers->addHeaderOb(new Horde_Mime_Headers_UserAgent(null, empty($opts['useragent']) ? 'Internet Messaging Program (IMP) ' . $registry->getVersion() : $opts['useragent'])); /* Add preferred reply language(s). */ if ($lang = @unserialize($prefs->getValue('reply_lang'))) { $headers->addHeader('Accept-Language', implode(',', $lang)); } $message = $this->_createMimeMessage($body, array('html' => !empty($opts['html']), 'identity' => $identity, 'pgp_attach_pubkey' => !empty($opts['pgp_attach_pubkey']) && $prefs->getValue('use_pgp') && $prefs->getValue('pgp_public_key'), 'recip' => $recip['list'], 'signature' => is_null($opts['signature']) ? $identity : $opts['signature'], 'vcard_attach' => !empty($opts['vcard_attach']) && $registry->hasMethod('contacts/ownVCard') ? (strlen($opts['vcard_attach']) ? $opts['vcard_attach'] : 'vcard') . '.vcf' : null)); /* Pass to hook to allow alteration of message details. */ try { $injector->getInstance('Horde_Core_Hooks')->callHook('pre_sent', 'imp', array($message, $headers, $this)); /* Re-parse headers to determine up-to-date recipient list. */ $tmp_recip = array(); foreach (array('to', 'cc', 'bcc') as $val) { if ($tmp_hdr = $headers[$val]) { $tmp_recip[$val] = $tmp_hdr->getAddressList(true); } } $recip = $this->recipientList($tmp_recip); } catch (Horde_Exception_HookNotSet $e) { } /* Get from address. Done after pre_sent hook since from address could * be changed by hook. */ $from = $headers['from']->getAddressList(true)->first(); if (is_null($from->host)) { $from->host = $imp_imap->config->maildomain; } /* Add Reply-To header. Done after pre_sent hook since from address * could be change by hook and/or Reply-To was set by hook. */ if (!empty($header['replyto']) && $header['replyto'] != $from->bare_address && !isset($headers['reply-to'])) { $headers->addHeader('Reply-To', $header['replyto']); } $message = $this->_encryptMessage($message, $opts['encrypt'], $recip['list'], $from); /* Send the messages out now. */ try { $this->sendMessage($recip['list'], $headers, $message); /* Store history information. Even if history is inactive, this * will provide Message ID of message. */ $msgid = $this->_logSentmail($headers, $recip['list'], true); } catch (IMP_Compose_Exception_Address $e) { throw $e; } catch (IMP_Compose_Exception $e) { /* Unsuccessful send. */ if ($e->log()) { $this->_logSentmail($headers, $recip['list'], false); } throw new IMP_Compose_Exception(sprintf(_("There was an error sending your message: %s"), $e->getMessage())); } if ($this->_replytype) { /* Log the reply. */ if ($indices = $this->getMetadata('indices')) { $log_data = array('msgid' => $msgid); switch ($this->_replytype) { case self::FORWARD: case self::FORWARD_ATTACH: case self::FORWARD_BODY: case self::FORWARD_BOTH: $ob = 'IMP_Maillog_Log_Forward'; $log_data['recipients'] = strval($recip['list']); break; case self::REPLY: case self::REPLY_SENDER: $ob = 'IMP_Maillog_Log_Reply'; break; case IMP_Compose::REPLY_ALL: $ob = 'IMP_Maillog_Log_Replyall'; break; case IMP_Compose::REPLY_LIST: $ob = 'IMP_Maillog_Log_Replylist'; break; } $log = new $ob($log_data); $log_msgs = array(); foreach ($indices as $val) { foreach ($val->uids as $val2) { $log_msgs[] = new IMP_Maillog_Message(new IMP_Indices($val->mbox, $val2)); } } $injector->getInstance('IMP_Maillog')->log($log_msgs, $log); } $reply_uid = new IMP_Indices($this); switch ($this->replyType(true)) { case self::FORWARD: /* Set the Forwarded flag, if possible, in the mailbox. * See RFC 5550 [5.9] */ $reply_uid->flag(array(Horde_Imap_Client::FLAG_FORWARDED)); break; case self::REPLY: /* Make sure to set the IMAP reply flag and unset any * 'flagged' flag. */ $reply_uid->flag(array(Horde_Imap_Client::FLAG_ANSWERED), array(Horde_Imap_Client::FLAG_FLAGGED)); break; } } Horde::log(sprintf("Message sent to %s from %s (%s)", strval($recip['list']), $registry->getAuth(), $session->get('horde', 'auth/remoteAddr')), 'INFO'); /* Save message to the sent mail mailbox. */ $this->_saveToSentMail($headers, $message, $recip['list'], $opts); /* Delete the attachment data. */ $this->deleteAllAttachments(); /* Save recipients to address book? */ $this->_saveRecipients($recip['list']); /* Call post-sent hook. */ try { $injector->getInstance('Horde_Core_Hooks')->callHook('post_sent', 'imp', array($message, $headers)); } catch (Horde_Exception_HookNotSet $e) { } }
/** * 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->addHeaderOb(Horde_Core_Mime_Headers_Received::createHordeHop()); $h->addHeaderOb(Horde_Mime_Headers_MessageId::create()); $h->addHeaderOb(Horde_Mime_Headers_UserAgent::create()); $h->addHeaderOb(Horde_Mime_Headers_Date::create()); $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'); } }