[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

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

   1  <?php
   2  
   3  abstract class PhabricatorMailReceiver {
   4  
   5    abstract public function isEnabled();
   6    abstract public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail);
   7  
   8    abstract protected function processReceivedMail(
   9      PhabricatorMetaMTAReceivedMail $mail,
  10      PhabricatorUser $sender);
  11  
  12    final public function receiveMail(
  13      PhabricatorMetaMTAReceivedMail $mail,
  14      PhabricatorUser $sender) {
  15      $this->processReceivedMail($mail, $sender);
  16    }
  17  
  18    public function validateSender(
  19      PhabricatorMetaMTAReceivedMail $mail,
  20      PhabricatorUser $sender) {
  21  
  22      $failure_reason = null;
  23      if ($sender->getIsDisabled()) {
  24        $failure_reason = pht(
  25          'Your account (%s) is disabled, so you can not interact with '.
  26          'Phabricator over email.',
  27          $sender->getUsername());
  28      } else if ($sender->getIsStandardUser()) {
  29        if (!$sender->getIsApproved()) {
  30          $failure_reason = pht(
  31            'Your account (%s) has not been approved yet. You can not interact '.
  32            'with Phabricator over email until your account is approved.',
  33            $sender->getUsername());
  34        } else if (PhabricatorUserEmail::isEmailVerificationRequired() &&
  35                 !$sender->getIsEmailVerified()) {
  36          $failure_reason = pht(
  37            'You have not verified the email address for your account (%s). '.
  38            'You must verify your email address before you can interact '.
  39            'with Phabricator over email.',
  40            $sender->getUsername());
  41        }
  42      }
  43  
  44      if ($failure_reason) {
  45        throw new PhabricatorMetaMTAReceivedMailProcessingException(
  46          MetaMTAReceivedMailStatus::STATUS_DISABLED_SENDER,
  47          $failure_reason);
  48      }
  49    }
  50  
  51    /**
  52     * Identifies the sender's user account for a piece of received mail. Note
  53     * that this method does not validate that the sender is who they say they
  54     * are, just that they've presented some credential which corresponds to a
  55     * recognizable user.
  56     */
  57    public function loadSender(PhabricatorMetaMTAReceivedMail $mail) {
  58      $raw_from = $mail->getHeader('From');
  59      $from = self::getRawAddress($raw_from);
  60  
  61      $reasons = array();
  62  
  63      // Try to find a user with this email address.
  64      $user = PhabricatorUser::loadOneWithEmailAddress($from);
  65      if ($user) {
  66        return $user;
  67      } else {
  68        $reasons[] = pht(
  69          'This email was sent from "%s", but that address is not recognized by '.
  70          'Phabricator and does not correspond to any known user account.',
  71          $raw_from);
  72      }
  73  
  74      // If we missed on "From", try "Reply-To" if we're configured for it.
  75      $raw_reply_to = $mail->getHeader('Reply-To');
  76      if (strlen($raw_reply_to)) {
  77        $reply_to_key = 'metamta.insecure-auth-with-reply-to';
  78        $allow_reply_to = PhabricatorEnv::getEnvConfig($reply_to_key);
  79        if ($allow_reply_to) {
  80          $reply_to = self::getRawAddress($raw_reply_to);
  81  
  82          $user = PhabricatorUser::loadOneWithEmailAddress($reply_to);
  83          if ($user) {
  84            return $user;
  85          } else {
  86            $reasons[] = pht(
  87              'Phabricator is configured to authenticate users using the '.
  88              '"Reply-To" header, but the reply address ("%s") on this '.
  89              'message does not correspond to any known user account.',
  90              $raw_reply_to);
  91          }
  92        } else {
  93          $reasons[] = pht(
  94            '(Phabricator is not configured to authenticate users using the '.
  95            '"Reply-To" header, so it was ignored.)');
  96        }
  97      }
  98  
  99      // If we don't know who this user is, load or create an external user
 100      // account for them if we're configured for it.
 101      $email_key = 'phabricator.allow-email-users';
 102      $allow_email_users = PhabricatorEnv::getEnvConfig($email_key);
 103      if ($allow_email_users) {
 104        $from_obj = new PhutilEmailAddress($from);
 105        $xuser = id(new PhabricatorExternalAccountQuery())
 106          ->setViewer(PhabricatorUser::getOmnipotentUser())
 107          ->withAccountTypes(array('email'))
 108          ->withAccountDomains(array($from_obj->getDomainName(), 'self'))
 109          ->withAccountIDs(array($from_obj->getAddress()))
 110          ->requireCapabilities(
 111            array(
 112              PhabricatorPolicyCapability::CAN_VIEW,
 113              PhabricatorPolicyCapability::CAN_EDIT,
 114            ))
 115          ->loadOneOrCreate();
 116        return $xuser->getPhabricatorUser();
 117      } else {
 118        $reasons[] = pht(
 119          'Phabricator is also not configured to allow unknown external users '.
 120          'to send mail to the system using just an email address.');
 121        $reasons[] = pht(
 122          'To interact with Phabricator, add this address ("%s") to your '.
 123          'account.',
 124          $raw_from);
 125      }
 126  
 127      $reasons = implode("\n\n", $reasons);
 128  
 129      throw new PhabricatorMetaMTAReceivedMailProcessingException(
 130        MetaMTAReceivedMailStatus::STATUS_UNKNOWN_SENDER,
 131        $reasons);
 132    }
 133  
 134    /**
 135     * Determine if two inbound email addresses are effectively identical. This
 136     * method strips and normalizes addresses so that equivalent variations are
 137     * correctly detected as identical. For example, these addresses are all
 138     * considered to match one another:
 139     *
 140     *   "Abraham Lincoln" <[email protected]>
 141     *   [email protected]
 142     *   <[email protected]>
 143     *   "Abraham" <[email protected]> # With configured prefix.
 144     *
 145     * @param   string  Email address.
 146     * @param   string  Another email address.
 147     * @return  bool    True if addresses match.
 148     */
 149    public static function matchAddresses($u, $v) {
 150      $u = self::getRawAddress($u);
 151      $v = self::getRawAddress($v);
 152  
 153      $u = self::stripMailboxPrefix($u);
 154      $v = self::stripMailboxPrefix($v);
 155  
 156      return ($u === $v);
 157    }
 158  
 159  
 160    /**
 161     * Strip a global mailbox prefix from an address if it is present. Phabricator
 162     * can be configured to prepend a prefix to all reply addresses, which can
 163     * make forwarding rules easier to write. A prefix looks like:
 164     *
 165     *  [email protected]              # No Prefix
 166     *  [email protected]  # Prefix "phabricator"
 167     *
 168     * @param   string  Email address, possibly with a mailbox prefix.
 169     * @return  string  Email address with any prefix stripped.
 170     */
 171    public static function stripMailboxPrefix($address) {
 172      $address = id(new PhutilEmailAddress($address))->getAddress();
 173  
 174      $prefix_key = 'metamta.single-reply-handler-prefix';
 175      $prefix = PhabricatorEnv::getEnvConfig($prefix_key);
 176  
 177      $len = strlen($prefix);
 178  
 179      if ($len) {
 180        $prefix = $prefix.'+';
 181        $len = $len + 1;
 182      }
 183  
 184      if ($len) {
 185        if (!strncasecmp($address, $prefix, $len)) {
 186          $address = substr($address, strlen($prefix));
 187        }
 188      }
 189  
 190      return $address;
 191    }
 192  
 193  
 194    /**
 195     * Reduce an email address to its canonical form. For example, an adddress
 196     * like:
 197     *
 198     *  "Abraham Lincoln" < [email protected] >
 199     *
 200     * ...will be reduced to:
 201     *
 202     *  [email protected]
 203     *
 204     * @param   string  Email address in noncanonical form.
 205     * @return  string  Canonical email address.
 206     */
 207    public static function getRawAddress($address) {
 208      $address = id(new PhutilEmailAddress($address))->getAddress();
 209      return trim(phutil_utf8_strtolower($address));
 210    }
 211  
 212  }


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