2013-05-14 19:57:41 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver {
|
|
|
|
|
2013-05-17 12:49:29 +02:00
|
|
|
/**
|
|
|
|
* Return a regular expression fragment which matches the name of an
|
|
|
|
* object which can receive mail. For example, Differential uses:
|
|
|
|
*
|
|
|
|
* D[1-9]\d*
|
|
|
|
*
|
|
|
|
* ...to match `D123`, etc., identifying Differential Revisions.
|
|
|
|
*
|
|
|
|
* @return string Regular expression fragment.
|
|
|
|
*/
|
2013-05-14 19:57:41 +02:00
|
|
|
abstract protected function getObjectPattern();
|
|
|
|
|
2013-05-17 12:49:29 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Load the object receiving mail, based on an identifying pattern. Normally
|
|
|
|
* this pattern is some sort of object ID.
|
|
|
|
*
|
|
|
|
* @param string A string matched by @{method:getObjectPattern}
|
|
|
|
* fragment.
|
|
|
|
* @param PhabricatorUser The viewing user.
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
abstract protected function loadObject($pattern, PhabricatorUser $viewer);
|
|
|
|
|
2013-05-17 19:00:49 +02:00
|
|
|
|
|
|
|
final protected function processReceivedMail(
|
|
|
|
PhabricatorMetaMTAReceivedMail $mail,
|
|
|
|
PhabricatorUser $sender) {
|
|
|
|
|
|
|
|
$object = $this->loadObjectFromMail($mail, $sender);
|
|
|
|
$mail->setRelatedPHID($object->getPHID());
|
|
|
|
|
|
|
|
$this->processReceivedObjectMail($mail, $object, $sender);
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-04-01 20:01:21 +02:00
|
|
|
protected function processReceivedObjectMail(
|
2013-05-17 19:00:49 +02:00
|
|
|
PhabricatorMetaMTAReceivedMail $mail,
|
|
|
|
PhabricatorLiskDAO $object,
|
2015-04-01 20:01:21 +02:00
|
|
|
PhabricatorUser $sender) {
|
|
|
|
|
|
|
|
$handler = $this->getTransactionReplyHandler();
|
|
|
|
if ($handler) {
|
|
|
|
return $handler
|
|
|
|
->setMailReceiver($object)
|
|
|
|
->setActor($sender)
|
2015-09-09 23:07:07 +02:00
|
|
|
->setExcludeMailRecipientPHIDs($mail->loadAllRecipientPHIDs())
|
2015-04-01 20:01:21 +02:00
|
|
|
->processEmail($mail);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new PhutilMethodNotImplementedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getTransactionReplyHandler() {
|
|
|
|
return null;
|
|
|
|
}
|
2013-05-17 19:00:49 +02:00
|
|
|
|
2013-05-17 12:51:33 +02:00
|
|
|
public function loadMailReceiverObject($pattern, PhabricatorUser $viewer) {
|
|
|
|
return $this->loadObject($pattern, $viewer);
|
|
|
|
}
|
|
|
|
|
2013-05-17 12:49:29 +02:00
|
|
|
public function validateSender(
|
|
|
|
PhabricatorMetaMTAReceivedMail $mail,
|
|
|
|
PhabricatorUser $sender) {
|
2013-05-17 19:00:49 +02:00
|
|
|
|
2013-05-17 12:49:29 +02:00
|
|
|
parent::validateSender($mail, $sender);
|
2013-05-14 19:57:41 +02:00
|
|
|
|
2013-05-17 19:00:49 +02:00
|
|
|
$parts = $this->matchObjectAddressInMail($mail);
|
When we fail to process mail, tell the user about it
Summary:
Ref T4371. Ref T4699. Fixes T3994.
Currently, we're very conservative about sending errors back to users. A concern I had about this was that mistakes could lead to email loops, massive amounts of email spam, etc. Because of this, I was pretty hesitant about replying to email with more email when I wrote this stuff.
However, this was a long time ago. We now have Message-ID deduplication, "X-Phabricator-Sent-This-Mail", generally better mail infrastructure, and rate limiting. Together, these mechanisms should reasonably prevent anything crazy (primarily, infinite email loops) from happening.
Thus:
- When we hit any processing error after receiving a mail, try to send the author a reply with details about what went wrong. These are limited to 6 per hour per address.
- Rewrite most of the errors to be more detailed and informative.
- Rewrite most of the errors in a user-facing voice ("You sent this mail..." instead of "This mail was sent..").
- Remove the redundant, less sophisticated code which does something similar in Differential.
Test Plan:
- Using `scripts/mail/mail_receiver.php`, artificially received a pile of mail.
- Hit a bunch of different errors.
- Saw reasonable error mail get sent to me.
- Saw other reasonable error mail get rate limited.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T3994, T4371, T4699
Differential Revision: https://secure.phabricator.com/D8692
2014-04-04 03:43:18 +02:00
|
|
|
$pattern = $parts['pattern'];
|
2013-05-17 12:49:29 +02:00
|
|
|
|
|
|
|
try {
|
2013-05-17 19:00:49 +02:00
|
|
|
$object = $this->loadObjectFromMail($mail, $sender);
|
2013-05-17 12:49:29 +02:00
|
|
|
} catch (PhabricatorPolicyException $policy_exception) {
|
|
|
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
|
|
|
MetaMTAReceivedMailStatus::STATUS_POLICY_PROBLEM,
|
|
|
|
pht(
|
When we fail to process mail, tell the user about it
Summary:
Ref T4371. Ref T4699. Fixes T3994.
Currently, we're very conservative about sending errors back to users. A concern I had about this was that mistakes could lead to email loops, massive amounts of email spam, etc. Because of this, I was pretty hesitant about replying to email with more email when I wrote this stuff.
However, this was a long time ago. We now have Message-ID deduplication, "X-Phabricator-Sent-This-Mail", generally better mail infrastructure, and rate limiting. Together, these mechanisms should reasonably prevent anything crazy (primarily, infinite email loops) from happening.
Thus:
- When we hit any processing error after receiving a mail, try to send the author a reply with details about what went wrong. These are limited to 6 per hour per address.
- Rewrite most of the errors to be more detailed and informative.
- Rewrite most of the errors in a user-facing voice ("You sent this mail..." instead of "This mail was sent..").
- Remove the redundant, less sophisticated code which does something similar in Differential.
Test Plan:
- Using `scripts/mail/mail_receiver.php`, artificially received a pile of mail.
- Hit a bunch of different errors.
- Saw reasonable error mail get sent to me.
- Saw other reasonable error mail get rate limited.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T3994, T4371, T4699
Differential Revision: https://secure.phabricator.com/D8692
2014-04-04 03:43:18 +02:00
|
|
|
'This mail is addressed to an object ("%s") you do not have '.
|
|
|
|
'permission to see: %s',
|
|
|
|
$pattern,
|
2013-05-17 12:49:29 +02:00
|
|
|
$policy_exception->getMessage()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$object) {
|
|
|
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
|
|
|
MetaMTAReceivedMailStatus::STATUS_NO_SUCH_OBJECT,
|
|
|
|
pht(
|
When we fail to process mail, tell the user about it
Summary:
Ref T4371. Ref T4699. Fixes T3994.
Currently, we're very conservative about sending errors back to users. A concern I had about this was that mistakes could lead to email loops, massive amounts of email spam, etc. Because of this, I was pretty hesitant about replying to email with more email when I wrote this stuff.
However, this was a long time ago. We now have Message-ID deduplication, "X-Phabricator-Sent-This-Mail", generally better mail infrastructure, and rate limiting. Together, these mechanisms should reasonably prevent anything crazy (primarily, infinite email loops) from happening.
Thus:
- When we hit any processing error after receiving a mail, try to send the author a reply with details about what went wrong. These are limited to 6 per hour per address.
- Rewrite most of the errors to be more detailed and informative.
- Rewrite most of the errors in a user-facing voice ("You sent this mail..." instead of "This mail was sent..").
- Remove the redundant, less sophisticated code which does something similar in Differential.
Test Plan:
- Using `scripts/mail/mail_receiver.php`, artificially received a pile of mail.
- Hit a bunch of different errors.
- Saw reasonable error mail get sent to me.
- Saw other reasonable error mail get rate limited.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T3994, T4371, T4699
Differential Revision: https://secure.phabricator.com/D8692
2014-04-04 03:43:18 +02:00
|
|
|
'This mail is addressed to an object ("%s"), but that object '.
|
|
|
|
'does not exist.',
|
|
|
|
$pattern));
|
2013-05-17 12:49:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$sender_identifier = $parts['sender'];
|
|
|
|
|
|
|
|
if ($sender_identifier === 'public') {
|
|
|
|
if (!PhabricatorEnv::getEnvConfig('metamta.public-replies')) {
|
|
|
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
|
|
|
MetaMTAReceivedMailStatus::STATUS_NO_PUBLIC_MAIL,
|
|
|
|
pht(
|
When we fail to process mail, tell the user about it
Summary:
Ref T4371. Ref T4699. Fixes T3994.
Currently, we're very conservative about sending errors back to users. A concern I had about this was that mistakes could lead to email loops, massive amounts of email spam, etc. Because of this, I was pretty hesitant about replying to email with more email when I wrote this stuff.
However, this was a long time ago. We now have Message-ID deduplication, "X-Phabricator-Sent-This-Mail", generally better mail infrastructure, and rate limiting. Together, these mechanisms should reasonably prevent anything crazy (primarily, infinite email loops) from happening.
Thus:
- When we hit any processing error after receiving a mail, try to send the author a reply with details about what went wrong. These are limited to 6 per hour per address.
- Rewrite most of the errors to be more detailed and informative.
- Rewrite most of the errors in a user-facing voice ("You sent this mail..." instead of "This mail was sent..").
- Remove the redundant, less sophisticated code which does something similar in Differential.
Test Plan:
- Using `scripts/mail/mail_receiver.php`, artificially received a pile of mail.
- Hit a bunch of different errors.
- Saw reasonable error mail get sent to me.
- Saw other reasonable error mail get rate limited.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T3994, T4371, T4699
Differential Revision: https://secure.phabricator.com/D8692
2014-04-04 03:43:18 +02:00
|
|
|
'This mail is addressed to the public email address of an object '.
|
|
|
|
'("%s"), but public replies are not enabled on this Phabricator '.
|
|
|
|
'install. An administrator may have recently disabled this '.
|
|
|
|
'setting, or you may have replied to an old message. Try '.
|
|
|
|
'replying to a more recent message instead.',
|
|
|
|
$pattern));
|
2013-05-17 12:49:29 +02:00
|
|
|
}
|
|
|
|
$check_phid = $object->getPHID();
|
|
|
|
} else {
|
|
|
|
if ($sender_identifier != $sender->getID()) {
|
|
|
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
|
|
|
MetaMTAReceivedMailStatus::STATUS_USER_MISMATCH,
|
|
|
|
pht(
|
When we fail to process mail, tell the user about it
Summary:
Ref T4371. Ref T4699. Fixes T3994.
Currently, we're very conservative about sending errors back to users. A concern I had about this was that mistakes could lead to email loops, massive amounts of email spam, etc. Because of this, I was pretty hesitant about replying to email with more email when I wrote this stuff.
However, this was a long time ago. We now have Message-ID deduplication, "X-Phabricator-Sent-This-Mail", generally better mail infrastructure, and rate limiting. Together, these mechanisms should reasonably prevent anything crazy (primarily, infinite email loops) from happening.
Thus:
- When we hit any processing error after receiving a mail, try to send the author a reply with details about what went wrong. These are limited to 6 per hour per address.
- Rewrite most of the errors to be more detailed and informative.
- Rewrite most of the errors in a user-facing voice ("You sent this mail..." instead of "This mail was sent..").
- Remove the redundant, less sophisticated code which does something similar in Differential.
Test Plan:
- Using `scripts/mail/mail_receiver.php`, artificially received a pile of mail.
- Hit a bunch of different errors.
- Saw reasonable error mail get sent to me.
- Saw other reasonable error mail get rate limited.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T3994, T4371, T4699
Differential Revision: https://secure.phabricator.com/D8692
2014-04-04 03:43:18 +02:00
|
|
|
'This mail is addressed to the private email address of an object '.
|
|
|
|
'("%s"), but you are not the user who is authorized to use the '.
|
|
|
|
'address you sent mail to. Each private address is unique to the '.
|
|
|
|
'user who received the original mail. Try replying to a message '.
|
|
|
|
'which was sent directly to you instead.',
|
|
|
|
$pattern));
|
2013-05-17 12:49:29 +02:00
|
|
|
}
|
|
|
|
$check_phid = $sender->getPHID();
|
|
|
|
}
|
|
|
|
|
|
|
|
$expect_hash = self::computeMailHash($object->getMailKey(), $check_phid);
|
|
|
|
|
2015-11-10 18:51:57 +01:00
|
|
|
if (!phutil_hashes_are_identical($expect_hash, $parts['hash'])) {
|
2013-05-17 12:49:29 +02:00
|
|
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
|
|
|
MetaMTAReceivedMailStatus::STATUS_HASH_MISMATCH,
|
|
|
|
pht(
|
When we fail to process mail, tell the user about it
Summary:
Ref T4371. Ref T4699. Fixes T3994.
Currently, we're very conservative about sending errors back to users. A concern I had about this was that mistakes could lead to email loops, massive amounts of email spam, etc. Because of this, I was pretty hesitant about replying to email with more email when I wrote this stuff.
However, this was a long time ago. We now have Message-ID deduplication, "X-Phabricator-Sent-This-Mail", generally better mail infrastructure, and rate limiting. Together, these mechanisms should reasonably prevent anything crazy (primarily, infinite email loops) from happening.
Thus:
- When we hit any processing error after receiving a mail, try to send the author a reply with details about what went wrong. These are limited to 6 per hour per address.
- Rewrite most of the errors to be more detailed and informative.
- Rewrite most of the errors in a user-facing voice ("You sent this mail..." instead of "This mail was sent..").
- Remove the redundant, less sophisticated code which does something similar in Differential.
Test Plan:
- Using `scripts/mail/mail_receiver.php`, artificially received a pile of mail.
- Hit a bunch of different errors.
- Saw reasonable error mail get sent to me.
- Saw other reasonable error mail get rate limited.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T3994, T4371, T4699
Differential Revision: https://secure.phabricator.com/D8692
2014-04-04 03:43:18 +02:00
|
|
|
'This mail is addressed to an object ("%s"), but the address is '.
|
|
|
|
'not correct (the security hash is wrong). Check that the address '.
|
|
|
|
'is correct.',
|
|
|
|
$pattern));
|
2013-05-17 12:49:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
final public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail) {
|
2013-05-17 19:00:49 +02:00
|
|
|
if ($this->matchObjectAddressInMail($mail)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function matchObjectAddressInMail(
|
2013-06-05 20:51:28 +02:00
|
|
|
PhabricatorMetaMTAReceivedMail $mail) {
|
2013-05-17 19:00:49 +02:00
|
|
|
|
2013-05-17 12:49:29 +02:00
|
|
|
foreach ($mail->getToAddresses() as $address) {
|
2013-05-17 19:00:49 +02:00
|
|
|
$parts = $this->matchObjectAddress($address);
|
|
|
|
if ($parts) {
|
|
|
|
return $parts;
|
2013-05-14 19:57:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-17 19:00:49 +02:00
|
|
|
return null;
|
2013-05-14 19:57:41 +02:00
|
|
|
}
|
|
|
|
|
2013-05-17 12:49:29 +02:00
|
|
|
private function matchObjectAddress($address) {
|
|
|
|
$regexp = $this->getAddressRegexp();
|
|
|
|
|
|
|
|
$address = self::stripMailboxPrefix($address);
|
|
|
|
$local = id(new PhutilEmailAddress($address))->getLocalPart();
|
|
|
|
|
|
|
|
$matches = null;
|
|
|
|
if (!preg_match($regexp, $local, $matches)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $matches;
|
|
|
|
}
|
|
|
|
|
2013-05-14 19:57:41 +02:00
|
|
|
private function getAddressRegexp() {
|
|
|
|
$pattern = $this->getObjectPattern();
|
|
|
|
|
|
|
|
$regexp =
|
|
|
|
'(^'.
|
|
|
|
'(?P<pattern>'.$pattern.')'.
|
|
|
|
'\\+'.
|
|
|
|
'(?P<sender>\w+)'.
|
|
|
|
'\\+'.
|
|
|
|
'(?P<hash>[a-f0-9]{16})'.
|
2013-11-13 06:23:23 +01:00
|
|
|
'$)Ui';
|
2013-05-14 19:57:41 +02:00
|
|
|
|
|
|
|
return $regexp;
|
|
|
|
}
|
|
|
|
|
2013-05-17 19:00:49 +02:00
|
|
|
private function loadObjectFromMail(
|
|
|
|
PhabricatorMetaMTAReceivedMail $mail,
|
|
|
|
PhabricatorUser $sender) {
|
|
|
|
$parts = $this->matchObjectAddressInMail($mail);
|
|
|
|
|
2013-11-13 06:23:23 +01:00
|
|
|
return $this->loadObject(
|
|
|
|
phutil_utf8_strtoupper($parts['pattern']),
|
|
|
|
$sender);
|
2013-05-17 19:00:49 +02:00
|
|
|
}
|
|
|
|
|
2013-05-17 12:49:00 +02:00
|
|
|
public static function computeMailHash($mail_key, $phid) {
|
|
|
|
$global_mail_key = PhabricatorEnv::getEnvConfig('phabricator.mail-key');
|
|
|
|
|
|
|
|
$hash = PhabricatorHash::digest($mail_key.$global_mail_key.$phid);
|
|
|
|
return substr($hash, 0, 16);
|
|
|
|
}
|
2013-05-14 19:57:41 +02:00
|
|
|
|
|
|
|
}
|