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;
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract protected function processReceivedObjectMail(
|
|
|
|
PhabricatorMetaMTAReceivedMail $mail,
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorUser $sender);
|
|
|
|
|
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);
|
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(
|
|
|
|
"This mail is addressed to an object you are not permitted ".
|
|
|
|
"to see: %s",
|
|
|
|
$policy_exception->getMessage()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$object) {
|
|
|
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
|
|
|
MetaMTAReceivedMailStatus::STATUS_NO_SUCH_OBJECT,
|
|
|
|
pht(
|
|
|
|
"This mail is addressed to an object ('%s'), but that object ".
|
|
|
|
"does not exist.",
|
|
|
|
$parts['pattern']));
|
|
|
|
}
|
|
|
|
|
|
|
|
$sender_identifier = $parts['sender'];
|
|
|
|
|
|
|
|
if ($sender_identifier === 'public') {
|
|
|
|
if (!PhabricatorEnv::getEnvConfig('metamta.public-replies')) {
|
|
|
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
|
|
|
MetaMTAReceivedMailStatus::STATUS_NO_PUBLIC_MAIL,
|
|
|
|
pht(
|
|
|
|
"This mail is addressed to an object's public address, but ".
|
|
|
|
"public replies are not enabled (`metamta.public-replies`)."));
|
|
|
|
}
|
|
|
|
$check_phid = $object->getPHID();
|
|
|
|
} else {
|
|
|
|
if ($sender_identifier != $sender->getID()) {
|
|
|
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
|
|
|
MetaMTAReceivedMailStatus::STATUS_USER_MISMATCH,
|
|
|
|
pht(
|
|
|
|
"This mail is addressed to an object's private address, but ".
|
|
|
|
"the sending user and the private address owner are not the ".
|
|
|
|
"same user."));
|
|
|
|
}
|
|
|
|
$check_phid = $sender->getPHID();
|
|
|
|
}
|
|
|
|
|
|
|
|
$expect_hash = self::computeMailHash($object->getMailKey(), $check_phid);
|
|
|
|
|
|
|
|
if ($expect_hash != $parts['hash']) {
|
|
|
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
|
|
|
MetaMTAReceivedMailStatus::STATUS_HASH_MISMATCH,
|
|
|
|
pht(
|
|
|
|
"The hash in this object's address does not match the expected ".
|
|
|
|
"value."));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
}
|