[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/metamta/receiver/ -> PhabricatorObjectMailReceiver.php (source)

   1  <?php
   2  
   3  abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver {
   4  
   5    /**
   6     * Return a regular expression fragment which matches the name of an
   7     * object which can receive mail. For example, Differential uses:
   8     *
   9     *  D[1-9]\d*
  10     *
  11     * ...to match `D123`, etc., identifying Differential Revisions.
  12     *
  13     * @return string Regular expression fragment.
  14     */
  15    abstract protected function getObjectPattern();
  16  
  17  
  18    /**
  19     * Load the object receiving mail, based on an identifying pattern. Normally
  20     * this pattern is some sort of object ID.
  21     *
  22     * @param   string          A string matched by @{method:getObjectPattern}
  23     *                          fragment.
  24     * @param   PhabricatorUser The viewing user.
  25     * @return  void
  26     */
  27    abstract protected function loadObject($pattern, PhabricatorUser $viewer);
  28  
  29  
  30    final protected function processReceivedMail(
  31      PhabricatorMetaMTAReceivedMail $mail,
  32      PhabricatorUser $sender) {
  33  
  34      $object = $this->loadObjectFromMail($mail, $sender);
  35      $mail->setRelatedPHID($object->getPHID());
  36  
  37      $this->processReceivedObjectMail($mail, $object, $sender);
  38  
  39      return $this;
  40    }
  41  
  42    abstract protected function processReceivedObjectMail(
  43      PhabricatorMetaMTAReceivedMail $mail,
  44      PhabricatorLiskDAO $object,
  45      PhabricatorUser $sender);
  46  
  47    public function loadMailReceiverObject($pattern, PhabricatorUser $viewer) {
  48      return $this->loadObject($pattern, $viewer);
  49    }
  50  
  51    public function validateSender(
  52      PhabricatorMetaMTAReceivedMail $mail,
  53      PhabricatorUser $sender) {
  54  
  55      parent::validateSender($mail, $sender);
  56  
  57      $parts = $this->matchObjectAddressInMail($mail);
  58      $pattern = $parts['pattern'];
  59  
  60      try {
  61        $object = $this->loadObjectFromMail($mail, $sender);
  62      } catch (PhabricatorPolicyException $policy_exception) {
  63        throw new PhabricatorMetaMTAReceivedMailProcessingException(
  64          MetaMTAReceivedMailStatus::STATUS_POLICY_PROBLEM,
  65          pht(
  66            'This mail is addressed to an object ("%s") you do not have '.
  67            'permission to see: %s',
  68            $pattern,
  69            $policy_exception->getMessage()));
  70      }
  71  
  72      if (!$object) {
  73        throw new PhabricatorMetaMTAReceivedMailProcessingException(
  74          MetaMTAReceivedMailStatus::STATUS_NO_SUCH_OBJECT,
  75          pht(
  76            'This mail is addressed to an object ("%s"), but that object '.
  77            'does not exist.',
  78            $pattern));
  79      }
  80  
  81      $sender_identifier = $parts['sender'];
  82  
  83      if ($sender_identifier === 'public') {
  84        if (!PhabricatorEnv::getEnvConfig('metamta.public-replies')) {
  85          throw new PhabricatorMetaMTAReceivedMailProcessingException(
  86            MetaMTAReceivedMailStatus::STATUS_NO_PUBLIC_MAIL,
  87            pht(
  88              'This mail is addressed to the public email address of an object '.
  89              '("%s"), but public replies are not enabled on this Phabricator '.
  90              'install. An administrator may have recently disabled this '.
  91              'setting, or you may have replied to an old message. Try '.
  92              'replying to a more recent message instead.',
  93              $pattern));
  94        }
  95        $check_phid = $object->getPHID();
  96      } else {
  97        if ($sender_identifier != $sender->getID()) {
  98          throw new PhabricatorMetaMTAReceivedMailProcessingException(
  99            MetaMTAReceivedMailStatus::STATUS_USER_MISMATCH,
 100            pht(
 101              'This mail is addressed to the private email address of an object '.
 102              '("%s"), but you are not the user who is authorized to use the '.
 103              'address you sent mail to. Each private address is unique to the '.
 104              'user who received the original mail. Try replying to a message '.
 105              'which was sent directly to you instead.',
 106              $pattern));
 107        }
 108        $check_phid = $sender->getPHID();
 109      }
 110  
 111      $expect_hash = self::computeMailHash($object->getMailKey(), $check_phid);
 112  
 113      if ($expect_hash != $parts['hash']) {
 114        throw new PhabricatorMetaMTAReceivedMailProcessingException(
 115          MetaMTAReceivedMailStatus::STATUS_HASH_MISMATCH,
 116          pht(
 117            'This mail is addressed to an object ("%s"), but the address is '.
 118            'not correct (the security hash is wrong). Check that the address '.
 119            'is correct.',
 120            $pattern));
 121      }
 122    }
 123  
 124  
 125    final public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail) {
 126      if ($this->matchObjectAddressInMail($mail)) {
 127        return true;
 128      }
 129  
 130      return false;
 131    }
 132  
 133    private function matchObjectAddressInMail(
 134      PhabricatorMetaMTAReceivedMail $mail) {
 135  
 136      foreach ($mail->getToAddresses() as $address) {
 137        $parts = $this->matchObjectAddress($address);
 138        if ($parts) {
 139          return $parts;
 140        }
 141      }
 142  
 143      return null;
 144    }
 145  
 146    private function matchObjectAddress($address) {
 147      $regexp = $this->getAddressRegexp();
 148  
 149      $address = self::stripMailboxPrefix($address);
 150      $local = id(new PhutilEmailAddress($address))->getLocalPart();
 151  
 152      $matches = null;
 153      if (!preg_match($regexp, $local, $matches)) {
 154        return false;
 155      }
 156  
 157      return $matches;
 158    }
 159  
 160    private function getAddressRegexp() {
 161      $pattern = $this->getObjectPattern();
 162  
 163      $regexp =
 164        '(^'.
 165          '(?P<pattern>'.$pattern.')'.
 166          '\\+'.
 167          '(?P<sender>\w+)'.
 168          '\\+'.
 169          '(?P<hash>[a-f0-9]{16})'.
 170        '$)Ui';
 171  
 172      return $regexp;
 173    }
 174  
 175    private function loadObjectFromMail(
 176      PhabricatorMetaMTAReceivedMail $mail,
 177      PhabricatorUser $sender) {
 178      $parts = $this->matchObjectAddressInMail($mail);
 179  
 180      return $this->loadObject(
 181        phutil_utf8_strtoupper($parts['pattern']),
 182        $sender);
 183    }
 184  
 185    public static function computeMailHash($mail_key, $phid) {
 186      $global_mail_key = PhabricatorEnv::getEnvConfig('phabricator.mail-key');
 187  
 188      $hash = PhabricatorHash::digest($mail_key.$global_mail_key.$phid);
 189      return substr($hash, 0, 16);
 190    }
 191  
 192  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1