[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/horde/framework/Horde/Mime/ -> Mdn.php (source)

   1  <?php
   2  /**
   3   * The Horde_Mime_Mdn:: class implements Message Disposition Notifications as
   4   * described by RFC 3798.
   5   *
   6   * Copyright 2004-2014 Horde LLC (http://www.horde.org/)
   7   *
   8   * See the enclosed file COPYING for license information (LGPL). If you
   9   * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  10   *
  11   * @author   Michael Slusarz <slusarz@horde.org>
  12   * @category Horde
  13   * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
  14   * @package  Mime
  15   */
  16  class Horde_Mime_Mdn
  17  {
  18      /* RFC 3798 header for requesting a MDN. */
  19      const MDN_HEADER = 'Disposition-Notification-To';
  20  
  21      /**
  22       * The Horde_Mime_Headers object.
  23       *
  24       * @var Horde_Mime_Headers
  25       */
  26      protected $_headers;
  27  
  28      /**
  29       * The text of the original message.
  30       *
  31       * @var string
  32       */
  33      protected $_msgtext = false;
  34  
  35      /**
  36       * Constructor.
  37       *
  38       * @param Horde_Mime_Headers $mime_headers  A headers object.
  39       */
  40      public function __construct(Horde_Mime_Headers $headers)
  41      {
  42          $this->_headers = $headers;
  43      }
  44  
  45      /**
  46       * Returns the address to return the MDN to.
  47       *
  48       * @return string  The address to send the MDN to. Returns null if no
  49       *                 MDN is requested.
  50       */
  51      public function getMdnReturnAddr()
  52      {
  53          /* RFC 3798 [2.1] requires the Disposition-Notification-To header
  54           * for an MDN to be created. */
  55          return $this->_headers->getValue(self::MDN_HEADER);
  56      }
  57  
  58      /**
  59       * Is user input required to send the MDN?
  60       * Explicit confirmation is needed in some cases to prevent mail loops
  61       * and the use of MDNs for mail bombing.
  62       *
  63       * @return boolean  Is explicit user input required to send the MDN?
  64       */
  65      public function userConfirmationNeeded()
  66      {
  67          $return_path = $this->_headers->getValue('Return-Path');
  68  
  69          /* RFC 3798 [2.1]: Explicit confirmation is needed if there is no
  70           * Return-Path in the header. Also, "if the message contains more
  71           * than one Return-Path header, the implementation may [] treat the
  72           * situation as a failure of the comparison." */
  73          if (empty($return_path) || is_array($return_path)) {
  74              return true;
  75          }
  76  
  77          /* RFC 3798 [2.1]: Explicit confirmation is needed if there is more
  78           * than one distinct address in the Disposition-Notification-To
  79           * header. */
  80          $rfc822 = new Horde_Mail_Rfc822();
  81          $addr_ob = $rfc822->parseAddressList($this->getMdnReturnAddr());
  82  
  83          switch (count($addr_ob)) {
  84          case 0:
  85              return false;
  86  
  87          case 1:
  88              // No-op
  89              break;
  90  
  91          default:
  92              return true;
  93          }
  94  
  95          /* RFC 3798 [2.1] states that "MDNs SHOULD NOT be sent automatically
  96           * if the address in the Disposition-Notification-To header differs
  97           * from the address in the Return-Path header." This comparison is
  98           * case-sensitive for the mailbox part and case-insensitive for the
  99           * host part. */
 100          $ret_ob = new Horde_Mail_Rfc822_Address($return_path);
 101  
 102          return ($ret_ob->valid &&
 103                  ($addr_ob->bare_address == $ret_ob->bare_address));
 104      }
 105  
 106      /**
 107       * When generating the MDN, should we return the enitre text of the
 108       * original message?  The default is no - we only return the headers of
 109       * the original message. If the text is passed in via this method, we
 110       * will return the entire message.
 111       *
 112       * @param string $text  The text of the original message.
 113       */
 114      public function originalMessageText($text)
 115      {
 116          $this->_msgtext = $text;
 117      }
 118  
 119      /**
 120       * Generate the MDN according to the specifications listed in RFC
 121       * 3798 [3].
 122       *
 123       * @param boolean $action   Was this MDN type a result of a manual
 124       *                          action on part of the user?
 125       * @param boolean $sending  Was this MDN sent as a result of a manual
 126       *                          action on part of the user?
 127       * @param string $type      The type of action performed by the user.
 128       *                          Per RFC 3798 [3.2.6.2] the following types are
 129       *                          valid:
 130       *                            - deleted
 131       *                            - displayed
 132       * @param string $name      The name of the local server.
 133       * @param Mail $mailer      A Mail driver.
 134       * @param array $opts       Additional options:
 135       *   - charset: (string) Default charset.
 136       *              DEFAULT: NONE
 137       *   - from_addr: (string) From address.
 138       *                DEFAULT: NONE
 139       * @param array $mod        The list of modifications. Per RFC 3798
 140       *                          [3.2.6.3] the following modifications are
 141       *                          valid:
 142       *                            - error
 143       * @param array $err        If $mod is 'error', the additional
 144       *                          information to provide. Key is the type of
 145       *                          modification, value is the text.
 146       *
 147       * @throws Horde_Mime_Exception
 148       */
 149      public function generate($action, $sending, $type, $name, $mailer,
 150                               array $opts = array(), array $mod = array(),
 151                               array $err = array())
 152      {
 153          $opts = array_merge(array(
 154              'charset' => null,
 155              'from_addr' => null
 156          ), $opts);
 157  
 158          $to = $this->getMdnReturnAddr();
 159          $ua = $this->_headers->getUserAgent();
 160  
 161          $orig_recip = $this->_headers->getValue('Original-Recipient');
 162          if (!empty($orig_recip) && is_array($orig_recip)) {
 163              $orig_recip = $orig_recip[0];
 164          }
 165  
 166          $msg_id = $this->_headers->getValue('Message-ID');
 167  
 168          /* Create the Disposition field now (RFC 3798 [3.2.6]). */
 169          $dispo = 'Disposition: ' .
 170                   (($action) ? 'manual-action' : 'automatic-action') .
 171                   '/' .
 172                   (($sending) ? 'MDN-sent-manually' : 'MDN-sent-automatically') .
 173                   '; ' .
 174                   $type;
 175          if (!empty($mod)) {
 176              $dispo .= '/' . implode(', ', $mod);
 177          }
 178  
 179          /* Set up the mail headers. */
 180          $msg_headers = new Horde_Mime_Headers();
 181          $msg_headers->addMessageIdHeader();
 182          $msg_headers->addUserAgentHeader($ua);
 183          $msg_headers->addHeader('Date', date('r'));
 184          if ($opts['from_addr']) {
 185              $msg_headers->addHeader('From', $opts['from_addr']);
 186          }
 187          $msg_headers->addHeader('To', $this->getMdnReturnAddr());
 188          $msg_headers->addHeader('Subject', Horde_Mime_Translation::t("Disposition Notification"));
 189  
 190          /* MDNs are a subtype of 'multipart/report'. */
 191          $msg = new Horde_Mime_Part();
 192          $msg->setType('multipart/report');
 193          $msg->setContentTypeParameter('report-type', 'disposition-notification');
 194  
 195          /* The first part is a human readable message. */
 196          $part_one = new Horde_Mime_Part();
 197          $part_one->setType('text/plain');
 198          $part_one->setCharset($opts['charset']);
 199          if ($type == 'displayed') {
 200              $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->getValue('Date'), $this->_headers->getValue('To'), $this->_headers->getValue('Subject'));
 201              $flowed = new Horde_Text_Flowed($contents, $opts['charset']);
 202              $flowed->setDelSp(true);
 203              $part_one->setContentTypeParameter('format', 'flowed');
 204              $part_one->setContentTypeParameter('DelSp', 'Yes');
 205              $part_one->setContents($flowed->toFlowed());
 206          }
 207          // TODO: Messages for other notification types.
 208          $msg->addPart($part_one);
 209  
 210          /* The second part is a machine-parseable description. */
 211          $part_two = new Horde_Mime_Part();
 212          $part_two->setType('message/disposition-notification');
 213          $part_two_text = array('Reporting-UA: ' . $name . '; ' . $ua . "\n");
 214          if (!empty($orig_recip)) {
 215              $part_two_text[] = 'Original-Recipient: rfc822;' . $orig_recip . "\n";
 216          }
 217          if ($opts['from_addr']) {
 218              $part_two_text[] = 'Final-Recipient: rfc822;' . $opts['from_addr'] . "\n";
 219          }
 220          if (!empty($msg_id)) {
 221              $part_two_text[] = 'Original-Message-ID: rfc822;' . $msg_id . "\n";
 222          }
 223          $part_two_text[] = $dispo . "\n";
 224          if (in_array('error', $mod) && isset($err['error'])) {
 225              $part_two_text[] = 'Error: ' . $err['error'] . "\n";
 226          }
 227          $part_two->setContents($part_two_text);
 228          $msg->addPart($part_two);
 229  
 230          /* The third part is the text of the original message.  RFC 3798 [3]
 231           * allows us to return only a portion of the entire message - this
 232           * is left up to the user. */
 233          $part_three = new Horde_Mime_Part();
 234          $part_three->setType('message/rfc822');
 235          $part_three_text = array($this->_headers->toString());
 236          if (!empty($this->_msgtext)) {
 237              $part_three_text[] = $part_three->getEOL() . $this->_msgtext;
 238          }
 239          $part_three->setContents($part_three_text);
 240          $msg->addPart($part_three);
 241  
 242          return $msg->send($to, $msg_headers, $mailer);
 243      }
 244  
 245      /**
 246       * Add a MDN (read receipt) request headers to the Horde_Mime_Headers::
 247       * object.
 248       *
 249       * @param string $to  The address the receipt should be mailed to.
 250       */
 251      public function addMdnRequestHeaders($to)
 252      {
 253          /* This is the RFC 3798 way of requesting a receipt. */
 254          $this->_headers->addHeader(self::MDN_HEADER, $to);
 255      }
 256  
 257  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1