/** * 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'); }
/** * Generate the MDN according to the specifications listed in RFC * 3798 [3]. * * @param boolean $action Was this MDN type a result of a manual * action on part of the user? * @param boolean $sending Was this MDN sent as a result of a manual * action on part of the user? * @param string $type The type of action performed by the user. * Per RFC 3798 [3.2.6.2] the following types are * valid: * - deleted * - displayed * @param string $name The name of the local server. * @param Horde_Mail_Transport $mailer Mail transport object. * @param array $opts Additional options: * - charset: (string) Default charset. * DEFAULT: NONE * - from_addr: (string) From address. * DEFAULT: NONE * @param array $mod The list of modifications. Per RFC 3798 * [3.2.6.3] the following modifications are * valid: * - error * @param array $err If $mod is 'error', the additional * information to provide. Key is the type of * modification, value is the text. */ public function generate($action, $sending, $type, $name, $mailer, array $opts = array(), array $mod = array(), array $err = array()) { $opts = array_merge(array('charset' => null, 'from_addr' => null), $opts); if (!($hdr = $this->_headers[self::MDN_HEADER])) { throw new RuntimeException('Need at least one address to send MDN to.'); } $to = $hdr->getAddressList(true); $ua = Horde_Mime_Headers_UserAgent::create(); if ($orig_recip = $this->_headers['Original-Recipient']) { $orig_recip = $orig_recip->value_single; } /* Set up the mail headers. */ $msg_headers = new Horde_Mime_Headers(); $msg_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create()); $msg_headers->addHeaderOb($ua); /* RFC 3834 [5.2] */ $msg_headers->addHeader('Auto-Submitted', 'auto-replied'); $msg_headers->addHeaderOb(Horde_Mime_Headers_Date::create()); if ($opts['from_addr']) { $msg_headers->addHeader('From', $opts['from_addr']); } $msg_headers->addHeader('To', $to); $msg_headers->addHeader('Subject', Horde_Mime_Translation::t("Disposition Notification")); /* MDNs are a subtype of 'multipart/report'. */ $msg = new Horde_Mime_Part(); $msg->setType('multipart/report'); $msg->setContentTypeParameter('report-type', 'disposition-notification'); /* The first part is a human readable message. */ $part_one = new Horde_Mime_Part(); $part_one->setType('text/plain'); $part_one->setCharset($opts['charset']); if ($type == 'displayed') { $contents = sprintf(Horde_Mime_Translation::t("The message sent on %s to %s with subject \"%s\" has been displayed.\n\nThis is no guarantee that the message has been read or understood."), $this->_headers['Date'], $this->_headers['To'], $this->_headers['Subject']); $flowed = new Horde_Text_Flowed($contents, $opts['charset']); $flowed->setDelSp(true); $part_one->setContentTypeParameter('format', 'flowed'); $part_one->setContentTypeParameter('DelSp', 'Yes'); $part_one->setContents($flowed->toFlowed()); } // TODO: Messages for other notification types. $msg[] = $part_one; /* The second part is a machine-parseable description. */ $part_two = new Horde_Mime_Part(); $part_two->setType('message/disposition-notification'); $part_two_h = new Horde_Mime_Headers(); $part_two_h->addHeader('Reporting-UA', $name . '; ' . $ua); if (!empty($orig_recip)) { $part_two_h->addHeader('Original-Recipient', 'rfc822;' . $orig_recip); } if ($opts['from_addr']) { $part_two_h->addHeader('Final-Recipient', 'rfc822;' . $opts['from_addr']); } if ($msg_id = $this->_headers['Message-ID']) { $part_two_h->addHeader('Original-Message-ID', strval($msg_id)); } /* Create the Disposition field now (RFC 3798 [3.2.6]). */ $dispo = ($action ? 'manual-action' : 'automatic-action') . '/' . ($sending ? 'MDN-sent-manually' : 'MDN-sent-automatically') . '; ' . $type; if (!empty($mod)) { $dispo .= '/' . implode(', ', $mod); } $part_two_h->addHeader('Disposition', $dispo); if (in_array('error', $mod) && isset($err['error'])) { $part_two_h->addHeader('Error', $err['error']); } $part_two->setContents(trim($part_two_h->toString()) . "\n"); $msg[] = $part_two; /* The third part is the text of the original message. RFC 3798 [3] * allows us to return only a portion of the entire message - this * is left up to the user. */ $part_three = new Horde_Mime_Part(); $part_three->setType('message/rfc822'); $part_three_text = array(trim($this->_headers->toString()) . "\n"); if (!empty($this->_msgtext)) { $part_three_text[] = "\n" . $this->_msgtext; } $part_three->setContents($part_three_text); $msg[] = $part_three; return $msg->send($to, $msg_headers, $mailer); }
/** * Prepare the iCalendar MIME part of the response message. * * @param Horde_Mime_Part $ics The iCalendar MIME part of the response * message. * * @return NULL */ public function prepareResponseMimeHeaders(Horde_Mime_Headers $headers) { $headers->addHeaderOb(Horde_Mime_Headers_MessageId::create()); }
/** * Sends email notifications to a list of recipients. * * We do some ugly work in here to make sure that no one gets comments * mailed to them that they shouldn't see (because of group permissions). * * @param array $opts Option hash with notification information. * Possible values: * - ticket: (Whups_Ticket) A ticket. If not set, * this is assumed to be a reminder * message. * - recipients: (array|string) The list of recipients, * with user names as keys and user roles * as values. * - subject: (string) The email subject. * - view: (Horde_View) The view object for the * message text. * - template: (string) The template file for the * message text. * - from: (string) The email sender. * - new: (boolean, optional) Whether the passed * ticket was just created. */ public function mail(array $opts) { global $conf, $registry, $prefs; $opts = array_merge(array('ticket' => false, 'new' => false), $opts); /* Set up recipients and message headers. */ $mail = new Horde_Mime_Mail(array('X-Whups-Generated' => 1, 'User-Agent' => 'Whups ' . $registry->getVersion(), 'Precedence' => 'bulk', 'Auto-Submitted' => $opts['ticket'] ? 'auto-replied' : 'auto-generated')); $mail_always = null; if ($opts['ticket'] && !empty($conf['mail']['always_copy'])) { $mail_always = $conf['mail']['always_copy']; if (strpos($mail_always, '<@>') !== false) { try { $mail_always = str_replace('<@>', $opts['ticket']->get('queue_name'), $mail_always); } catch (Whups_Exception $e) { $mail_always = null; } } if ($mail_always && !isset($opts['recipients'][$mail_always])) { $opts['recipients'][$mail_always] = 'always'; } } if ($opts['ticket'] && ($queue = $this->getQueue($opts['ticket']->get('queue'))) && !empty($queue['email'])) { $mail->addHeader('From', $queue['email']); } elseif (!empty($conf['mail']['from_addr'])) { $mail->addHeader('From', $conf['mail']['from_addr']); } else { $mail->addHeader('From', Whups::formatUser($opts['from'])); } if (!empty($conf['mail']['return_path'])) { $mail->addHeader('Return-Path', $conf['mail']['return_path']); } if ($opts['ticket']) { $opts['subject'] = '[' . $registry->get('name') . ' #' . $opts['ticket']->getId() . '] ' . $opts['subject']; } $mail->addHeader('Subject', $opts['subject']); /* Get our array of comments, sorted in the appropriate order. */ if ($opts['ticket']) { $comments = $this->getHistory($opts['ticket']->getId()); if ($conf['mail']['commenthistory'] == 'new' && count($comments)) { $comments = array_pop($comments); $comments = array($comments); } elseif ($conf['mail']['commenthistory'] != 'chronological') { $comments = array_reverse($comments); } } else { $comments = array(); } /* Don't notify any email address more than once. */ $seen_email_addresses = array(); /* Get VFS handle for attachments. */ if ($opts['ticket']) { $vfs = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Vfs')->create(); try { $attachments = Whups::getAttachments($opts['ticket']->getId()); } catch (Whups_Exception $e) { $attachments = array(); Horde::log($e); } } $from = Whups::getUserAttributes($opts['from']); foreach ($opts['recipients'] as $user => $role) { /* Make sure to check permissions as a guest for the 'always_copy' * address, and as the recipient for all others. */ $to = $full_name = ''; if (!empty($mail_always) && $user == $mail_always) { $details = null; $mycomments = Whups::permissionsFilter($comments, 'comment', Horde_Perms::READ, ''); $to = $mail_always; } else { $details = Whups::getUserAttributes($user); if (!empty($details['email'])) { $to = Whups::formatUser($details); $mycomments = Whups::permissionsFilter($comments, 'comment', Horde_Perms::READ, $details['user']); } $full_name = $details['name']; } /* We may have no recipients due to users excluding themselves * from self notifies. */ if (!$to) { continue; } if ($details && $details['type'] == 'user') { $user_prefs = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Prefs')->create('whups', array('user' => $details['user'])); if (($details['user'] == $registry->getAuth() || !$registry->getAuth()) && $from['type'] == 'user' && $details['user'] == $from['user'] && $user_prefs->getValue('email_others_only')) { continue; } } if ($opts['ticket']) { /* Add attachments. */ $attachmentAdded = false; if (empty($GLOBALS['conf']['mail']['link_attach'])) { /* We need to remove all attachments because the attachment * list is potentially limited by permissions. */ $mail->clearParts(); foreach ($mycomments as $comment) { foreach ($comment['changes'] as $change) { if ($change['type'] != 'attachment') { continue; } foreach ($attachments as $attachment) { if ($attachment['name'] != $change['value']) { continue; } if (!isset($attachment['part'])) { $attachment['part'] = new Horde_Mime_Part(); $attachment['part']->setType(Horde_Mime_Magic::filenameToMime($change['value'], false)); $attachment['part']->setDisposition('attachment'); $attachment['part']->setContents($vfs->read(Whups::VFS_ATTACH_PATH . '/' . $opts['ticket']->getId(), $change['value'])); $attachment['part']->setName($change['value']); } $mail->addMimePart($attachment['part']); $attachmentAdded = true; break; } } } } $formattedComment = $this->formatComments($mycomments, $opts['ticket']->getId()); if (!$attachmentAdded && !strlen(trim($formattedComment)) && $details && $details['type'] == 'user' && $user_prefs->getValue('email_comments_only')) { continue; } $opts['view']->comment = $formattedComment; } $addr_ob = new Horde_Mail_Rfc822_Address($to); if ($addr_ob->valid) { $bare_address = $addr_ob->bare_address; if (!empty($seen_email_addresses[$bare_address])) { continue; } $seen_email_addresses[$bare_address] = true; if (empty($full_name) && !is_null($addr_ob->personal)) { $full_name = $addr_ob->personal; } } // Use email address as fallback. if (empty($full_name)) { $full_name = $to; } $opts['view']->full_name = $full_name; $opts['view']->role = $role; $body = $opts['view']->render($opts['template']); if (!strlen(trim($body))) { continue; } $mail->setBody($body); $mail->addHeaderOb(Horde_Mime_Headers_MessageId::create()); if ($opts['ticket']) { $message_id = '<whups-' . $opts['ticket']->getId() . '-' . md5($user) . '@' . $conf['server']['name'] . '>'; if ($opts['new']) { $mail->addHeader('Message-ID', $message_id); } else { $mail->addHeader('In-Reply-To', $message_id); $mail->addHeader('References', $message_id); } } $mail->clearRecipients(); $mail->addHeader('To', $to); try { $mail->send($GLOBALS['injector']->getInstance('Horde_Mail'), true); $entry = sprintf('%s Message sent to %s from "%s"', $_SERVER['REMOTE_ADDR'], $to, $GLOBALS['registry']->getAuth()); Horde::log($entry, 'INFO'); } catch (Horde_Mime_Exception $e) { Horde::log($e, 'ERR'); } } }
/** * 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 report(array $msgs, $action) { global $injector; $ret = 0; switch ($this->_format) { case 'redirect': /* Send the message. */ foreach ($msgs as $val) { try { $imp_compose = $injector->getInstance('IMP_Factory_Compose')->create(); $imp_compose->redirectMessage($val->getIndicesOb()); $imp_compose->sendRedirectMessage($this->_email, false); ++$ret; } catch (IMP_Compose_Exception $e) { $e->log(); } } break; case 'digest': default: try { $from_line = $injector->getInstance('IMP_Identity')->getFromLine(); } catch (Horde_Exception $e) { $from_line = null; } $self = $this; $reportDigest = function ($m) use($action, $from_line, $self) { global $injector, $registry; if (empty($m)) { return 0; } /* Build the MIME structure. */ $mime = new Horde_Mime_Part(); $mime->setType('multipart/digest'); foreach ($m as $val) { $rfc822 = new Horde_Mime_Part(); $rfc822->setType('message/rfc822'); $rfc822->setContents($val->fullMessageText(array('stream' => true))); $mime[] = $rfc822; } $spam_headers = new Horde_Mime_Headers(); $spam_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create()); $spam_headers->addHeaderOb(Horde_Mime_Headers_Date::create()); $spam_headers->addHeader('To', $this->_email); if (!is_null($from_line)) { $spam_headers->addHeader('From', $from_line); } $spam_headers->addHeader('Subject', sprintf(_("%s report from %s"), $action === IMP_Spam::SPAM ? 'spam' : 'innocent', $registry->getAuth())); /* Send the message. */ try { $imp_compose = $injector->getInstance('IMP_Factory_Compose')->create(); $recip_list = $imp_compose->recipientList(array('to' => $this->_email)); $imp_compose->sendMessage($recip_list['list'], $spam_headers, $mime, 'UTF-8'); return count($m); } catch (IMP_Compose_Exception $e) { $e->log(); return 0; } }; $mlimit = $orig_mlimit = empty($this->_opts['digest_limit_msgs']) ? null : $this->_opts['digest_limit_msgs']; $slimit = $orig_slimit = empty($this->_opts['digest_limit_size']) ? null : $this->_opts['digest_limit_size']; $todo = array(); foreach ($msgs as $val) { $process = false; $todo[] = $val; if (!is_null($mlimit) && !--$mlimit) { $process = true; } if (!is_null($slimit) && ($slimit -= $val->getMIMEMessage()->getBytes()) < 0) { $process = true; /* If we have exceeded size limits with this single * message, it exceeds the maximum limit and we can't * send it at all. Don't confuse the user and instead * report is as a "success" for UI purposes. */ if (count($todo) === 1) { ++$ret; $todo = array(); Horde::log('Could not send spam/innocent reporting message because original message was too large.', 'NOTICE'); } } if ($process) { $ret += $reportDigest($todo); $todo = array(); $mlimit = $orig_mlimit; $slimit = $orig_slimit; } } $ret += $reportDigest($todo); break; } return $ret; }
/** * @deprecated Use Horde_Mime_Headers_MessageId::create() instead. */ public static function generateMessageId() { return Horde_Mime_Headers_MessageId::create()->value; }
/** * Send a redirect (a/k/a resent) message. See RFC 5322 [3.6.6]. * * @param mixed $to The addresses to redirect to. * @param boolean $log Whether to log the resending in the history and * sentmail log. * * @return array An object with the following properties for each * redirected message: * - contents: (IMP_Contents) The contents object. * - headers: (Horde_Mime_Headers) The header object. * - mbox: (IMP_Mailbox) Mailbox of the message. * - uid: (string) UID of the message. * * @throws IMP_Compose_Exception */ public function sendRedirectMessage($to, $log = true) { global $injector, $registry; $recip = $this->recipientList(array('to' => $to)); $identity = $injector->getInstance('IMP_Identity'); $from_addr = $identity->getFromAddress(); $out = array(); foreach ($this->getMetadata('redirect_indices') as $val) { foreach ($val->uids as $val2) { try { $contents = $injector->getInstance('IMP_Factory_Contents')->create($val->mbox->getIndicesOb($val2)); } catch (IMP_Exception $e) { throw new IMP_Compose_Exception(_("Error when redirecting message.")); } $headers = $contents->getHeader(); /* We need to set the Return-Path header to the current user - * see RFC 2821 [4.4]. */ $headers->removeHeader('return-path'); $headers->addHeader('Return-Path', $from_addr); /* Generate the 'Resent' headers (RFC 5322 [3.6.6]). These * headers are prepended to the message. */ $resent_headers = new Horde_Mime_Headers(); $resent_headers->addHeader('Resent-Date', date('r')); $resent_headers->addHeader('Resent-From', $from_addr); $resent_headers->addHeader('Resent-To', $recip['header']['to']); $resent_headers->addHeader('Resent-Message-ID', Horde_Mime_Headers_MessageId::create()); $header_text = trim($resent_headers->toString(array('encode' => 'UTF-8'))) . "\n" . trim($contents->getHeader(IMP_Contents::HEADER_TEXT)); $this->_prepSendMessageAssert($recip['list']); $to = $this->_prepSendMessage($recip['list']); $hdr_array = $headers->toArray(array('charset' => 'UTF-8')); $hdr_array['_raw'] = $header_text; try { $injector->getInstance('IMP_Mail')->send($to, $hdr_array, $contents->getBody()); } catch (Horde_Mail_Exception $e) { $e2 = new IMP_Compose_Exception($e); if (($prev = $e->getPrevious()) && $prev instanceof Horde_Smtp_Exception) { if ($prev instanceof Horde_Smtp_Exception_Recipients) { $e2 = new IMP_Compose_Exception_Addresses($e); foreach ($prev->recipients as $val) { $e2->addAddress(new Horde_Mail_Rfc822_Address($val), _("Address rejected by the sending mail server."), $e2::BAD); } } Horde::log(sprintf("SMTP Error: %s (%u; %s)", $prev->raw_msg, $prev->getCode(), $prev->getEnhancedSmtpCode() ?: 'N/A'), 'ERR'); $e2->logged = true; } throw $e2; } $recipients = strval($recip['list']); Horde::log(sprintf("%s Redirected message sent to %s from %s", $_SERVER['REMOTE_ADDR'], $recipients, $registry->getAuth()), 'INFO'); if ($log && ($tmp = $headers['Message-ID'])) { $msg_id = reset($tmp->getIdentificationOb()->ids); /* Store history information. */ $injector->getInstance('IMP_Maillog')->log(new IMP_Maillog_Message($msg_id), new IMP_Maillog_Log_Redirect(array('msgid' => reset($resent_headers->getIdentificationOb()->ids), 'recipients' => $recipients))); $injector->getInstance('IMP_Sentmail')->log(IMP_Sentmail::REDIRECT, $msg_id, $recipients); } $tmp = new stdClass(); $tmp->contents = $contents; $tmp->headers = $headers; $tmp->mbox = $val->mbox; $tmp->uid = $val2; $out[] = $tmp; } } return $out; }
/** * 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'); } }
/** */ public function addMessageIdHeader() { $this->_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create()); }
/** * Sends this message. * * @param Mail $mailer A Mail object. * @param boolean $resend If true, the message id and date are re-used; * If false, they will be updated. * @param boolean $flowed Send message in flowed text format. * * @throws Horde_Mime_Exception */ public function send($mailer, $resend = false, $flowed = true) { /* Add mandatory headers if missing. */ if (!$resend || !isset($this->_headers['Message-ID'])) { $this->_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create()); } if (!isset($this->_headers['User-Agent'])) { $this->_headers->addHeaderOb(Horde_Mime_Headers_UserAgent::create()); } if (!$resend || !isset($this->_headers['Date'])) { $this->_headers->addHeaderOb(Horde_Mime_Headers_Date::create()); } if (isset($this->_base)) { $basepart = $this->_base; } else { /* Send in flowed format. */ if ($flowed && !empty($this->_body)) { $flowed = new Horde_Text_Flowed($this->_body->getContents(), $this->_body->getCharset()); $flowed->setDelSp(true); $this->_body->setContentTypeParameter('format', 'flowed'); $this->_body->setContentTypeParameter('DelSp', 'Yes'); $this->_body->setContents($flowed->toFlowed()); } /* Build mime message. */ $body = new Horde_Mime_Part(); if (!empty($this->_body) && !empty($this->_htmlBody)) { $body->setType('multipart/alternative'); $this->_body->setDescription(Horde_Mime_Translation::t("Plaintext Version of Message")); $body[] = $this->_body; $this->_htmlBody->setDescription(Horde_Mime_Translation::t("HTML Version of Message")); $body[] = $this->_htmlBody; } elseif (!empty($this->_htmlBody)) { $body = $this->_htmlBody; } elseif (!empty($this->_body)) { $body = $this->_body; } if (count($this->_parts)) { $basepart = new Horde_Mime_Part(); $basepart->setType('multipart/mixed'); $basepart->isBasePart(true); if ($body) { $basepart[] = $body; } foreach ($this->_parts as $mime_part) { $basepart[] = $mime_part; } } else { $basepart = $body; $basepart->isBasePart(true); } } $basepart->setHeaderCharset($this->_charset); /* Build recipients. */ $recipients = clone $this->_recipients; foreach (array('to', 'cc') as $header) { if ($h = $this->_headers[$header]) { $recipients->add($h->getAddressList()); } } if ($this->_bcc) { $recipients->add($this->_bcc); } /* Trick Horde_Mime_Part into re-generating the message headers. */ $this->_headers->removeHeader('MIME-Version'); /* Send message. */ $recipients->unique(); $basepart->send($recipients->writeAddress(), $this->_headers, $mailer); /* Remember the basepart */ $this->_base = $basepart; }
/** * Builds and sends a digest message. * * @param array $messages List of message contents (string|resource). * @param integer $action Either Horde_Spam::SPAM or Horde_Spam::INNOCENT. * * @return integer The number of reported messages. */ protected function _reportDigest(array $messages, $action) { if (empty($messages)) { return 0; } /* Build the MIME structure. */ $mime = new Horde_Mime_Part(); $mime->setType('multipart/digest'); foreach ($messages as $val) { $rfc822 = new Horde_Mime_Part(); $rfc822->setType('message/rfc822'); $rfc822->setContents($val); $mime[] = $rfc822; } $spam_headers = new Horde_Mime_Headers(); $spam_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create()); $spam_headers->addHeaderOb(Horde_Mime_Headers_Date::create()); $spam_headers->addHeader('To', $this->_email); $spam_headers->addHeader('From', $this->_from_addr); $spam_headers->addHeader('Subject', sprintf(Horde_Spam_Translation::t("%s report from %s"), $action === Horde_Spam::SPAM ? 'spam' : 'innocent', $this->_user)); /* Send the message. */ try { $mime->send($this->_email, $spam_headers, $this->_mail); return count($messages); } catch (Horde_Mail_Exception $e) { $this->_logger->warn($e); return 0; } }