[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |