Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/audit/mail/PhabricatorAuditMailReceiver.php b/src/applications/audit/mail/PhabricatorAuditMailReceiver.php
index 68e9201524..a66490c2dd 100644
--- a/src/applications/audit/mail/PhabricatorAuditMailReceiver.php
+++ b/src/applications/audit/mail/PhabricatorAuditMailReceiver.php
@@ -1,23 +1,36 @@
<?php
final class PhabricatorAuditMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationAudit';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'C[1-9]\d*';
}
protected function loadObject($pattern, PhabricatorUser $viewer) {
$id = (int)trim($pattern, 'C');
return id(new DiffusionCommitQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
}
+ protected function processReceivedObjectMail(
+ PhabricatorMetaMTAReceivedMail $mail,
+ PhabricatorLiskDAO $object,
+ PhabricatorUser $sender) {
+
+ $handler = PhabricatorAuditCommentEditor::newReplyHandlerForCommit($object);
+
+ $handler->setActor($sender);
+ $handler->setExcludeMailRecipientPHIDs(
+ $mail->loadExcludeMailRecipientPHIDs());
+ $handler->processEmail($mail);
+ }
+
}
diff --git a/src/applications/conpherence/mail/ConpherenceCreateThreadMailReceiver.php b/src/applications/conpherence/mail/ConpherenceCreateThreadMailReceiver.php
index b26879cef3..6e2fa8cd71 100644
--- a/src/applications/conpherence/mail/ConpherenceCreateThreadMailReceiver.php
+++ b/src/applications/conpherence/mail/ConpherenceCreateThreadMailReceiver.php
@@ -1,65 +1,65 @@
<?php
final class ConpherenceCreateThreadMailReceiver
extends PhabricatorMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationConpherence';
return PhabricatorApplication::isClassInstalled($app_class);
}
public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail) {
$usernames = $this->getMailUsernames($mail);
if (!$usernames) {
return false;
}
$users = $this->loadMailUsers($mail);
if (count($users) != count($usernames)) {
// At least some of the addresses are not users, so don't accept this as
// a new Conpherence thread.
return false;
}
return true;
}
private function getMailUsernames(PhabricatorMetaMTAReceivedMail $mail) {
$usernames = array();
foreach ($mail->getToAddresses() as $to_address) {
$address = self::stripMailboxPrefix($to_address);
$usernames[] = id(new PhutilEmailAddress($address))->getLocalPart();
}
return array_unique($usernames);
}
private function loadMailUsers(PhabricatorMetaMTAReceivedMail $mail) {
$usernames = $this->getMailUsernames($mail);
if (!$usernames) {
return array();
}
return id(new PhabricatorUser())->loadAllWhere(
'username in (%Ls)',
$usernames);
}
- public function processReceivedMail(
+ protected function processReceivedMail(
PhabricatorMetaMTAReceivedMail $mail,
PhabricatorUser $sender) {
$users = $this->loadMailUsers($mail);
$phids = mpull($users, 'getPHID');
$conpherence = id(new ConpherenceReplyHandler())
->setMailReceiver(new ConpherenceThread())
->setMailAddedParticipantPHIDs($phids)
->setActor($sender)
->setExcludeMailRecipientPHIDs($mail->loadExcludeMailRecipientPHIDs())
->processEmail($mail);
$mail->setRelatedPHID($conpherence->getPHID());
}
}
diff --git a/src/applications/conpherence/mail/ConpherenceThreadMailReceiver.php b/src/applications/conpherence/mail/ConpherenceThreadMailReceiver.php
index 558e8b4f60..e46950f992 100644
--- a/src/applications/conpherence/mail/ConpherenceThreadMailReceiver.php
+++ b/src/applications/conpherence/mail/ConpherenceThreadMailReceiver.php
@@ -1,25 +1,38 @@
<?php
final class ConpherenceThreadMailReceiver
extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationConpherence';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'E[1-9]\d*';
}
protected function loadObject($pattern, PhabricatorUser $viewer) {
$id = (int)trim($pattern, 'E');
return id(new ConpherenceThreadQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
}
+ protected function processReceivedObjectMail(
+ PhabricatorMetaMTAReceivedMail $mail,
+ PhabricatorLiskDAO $object,
+ PhabricatorUser $sender) {
+
+ $handler = id(new ConpherenceReplyHandler())
+ ->setMailReceiver($object);
+
+ $handler->setActor($sender);
+ $handler->setExcludeMailRecipientPHIDs(
+ $mail->loadExcludeMailRecipientPHIDs());
+ $handler->processEmail($mail);
+ }
}
diff --git a/src/applications/differential/mail/DifferentialRevisionMailReceiver.php b/src/applications/differential/mail/DifferentialRevisionMailReceiver.php
index 3086d176a2..87f63a615e 100644
--- a/src/applications/differential/mail/DifferentialRevisionMailReceiver.php
+++ b/src/applications/differential/mail/DifferentialRevisionMailReceiver.php
@@ -1,27 +1,39 @@
<?php
final class DifferentialRevisionMailReceiver
extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationDifferential';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'D[1-9]\d*';
}
protected function loadObject($pattern, PhabricatorUser $viewer) {
$id = (int)trim($pattern, 'D');
$results = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withIDs(array($id))
->execute();
return head($results);
}
+ protected function processReceivedObjectMail(
+ PhabricatorMetaMTAReceivedMail $mail,
+ PhabricatorLiskDAO $object,
+ PhabricatorUser $sender) {
+
+ $handler = DifferentialMail::newReplyHandlerForRevision($object);
+
+ $handler->setActor($sender);
+ $handler->setExcludeMailRecipientPHIDs(
+ $mail->loadExcludeMailRecipientPHIDs());
+ $handler->processEmail($mail);
+ }
}
diff --git a/src/applications/macro/mail/PhabricatorMacroMailReceiver.php b/src/applications/macro/mail/PhabricatorMacroMailReceiver.php
index da9ede57b1..3901388e3d 100644
--- a/src/applications/macro/mail/PhabricatorMacroMailReceiver.php
+++ b/src/applications/macro/mail/PhabricatorMacroMailReceiver.php
@@ -1,24 +1,34 @@
<?php
final class PhabricatorMacroMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationManiphest';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'MCRO[1-9]\d*';
}
protected function loadObject($pattern, PhabricatorUser $viewer) {
$id = (int)substr($pattern, 4);
return id(new PhabricatorMacroQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
}
+ protected function processReceivedObjectMail(
+ PhabricatorMetaMTAReceivedMail $mail,
+ PhabricatorLiskDAO $object,
+ PhabricatorUser $sender) {
+
+ // TODO: For now, we just drop this mail on the floor.
+
+ }
+
+
}
diff --git a/src/applications/maniphest/mail/ManiphestCreateMailReceiver.php b/src/applications/maniphest/mail/ManiphestCreateMailReceiver.php
index 88d4a8874b..1e31ccbf17 100644
--- a/src/applications/maniphest/mail/ManiphestCreateMailReceiver.php
+++ b/src/applications/maniphest/mail/ManiphestCreateMailReceiver.php
@@ -1,81 +1,81 @@
<?php
final class ManiphestCreateMailReceiver extends PhabricatorMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationManiphest';
return PhabricatorApplication::isClassInstalled($app_class);
}
public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail) {
$config_key = 'metamta.maniphest.public-create-email';
$create_address = PhabricatorEnv::getEnvConfig($config_key);
foreach ($mail->getToAddresses() as $to_address) {
if ($this->matchAddresses($create_address, $to_address)) {
return true;
}
}
return false;
}
public function loadSender(PhabricatorMetaMTAReceivedMail $mail) {
try {
// Try to load the sender normally.
return parent::loadSender($mail);
} catch (PhabricatorMetaMTAReceivedMailProcessingException $ex) {
// If we failed to load the sender normally, use this special legacy
// black magic.
// TODO: Deprecate and remove this.
$default_author_key = 'metamta.maniphest.default-public-author';
$default_author = PhabricatorEnv::getEnvConfig($default_author_key);
if (!strlen($default_author)) {
throw $ex;
}
$user = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$default_author);
if ($user) {
return $user;
}
throw new PhabricatorMetaMTAReceivedMailProcessingException(
MetaMTAReceivedMailStatus::STATUS_UNKNOWN_SENDER,
pht(
"Phabricator is misconfigured, the configuration key ".
"'metamta.maniphest.default-public-author' is set to user ".
"'%s' but that user does not exist.",
$default_author));
}
}
- public function processReceivedMail(
+ protected function processReceivedMail(
PhabricatorMetaMTAReceivedMail $mail,
PhabricatorUser $sender) {
$task = new ManiphestTask();
$task->setAuthorPHID($sender->getPHID());
$task->setOriginalEmailSource($mail->getHeader('From'));
$task->setPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
$editor = new ManiphestTransactionEditor();
$editor->setActor($sender);
$handler = $editor->buildReplyHandler($task);
$handler->setActor($sender);
$handler->setExcludeMailRecipientPHIDs(
$mail->loadExcludeMailRecipientPHIDs());
$handler->processEmail($mail);
$mail->setRelatedPHID($task->getPHID());
}
}
diff --git a/src/applications/maniphest/mail/ManiphestTaskMailReceiver.php b/src/applications/maniphest/mail/ManiphestTaskMailReceiver.php
index 9a04cd0bbd..0e1c4ab04d 100644
--- a/src/applications/maniphest/mail/ManiphestTaskMailReceiver.php
+++ b/src/applications/maniphest/mail/ManiphestTaskMailReceiver.php
@@ -1,25 +1,40 @@
<?php
final class ManiphestTaskMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationManiphest';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'T[1-9]\d*';
}
protected function loadObject($pattern, PhabricatorUser $viewer) {
$id = (int)trim($pattern, 'T');
$results = id(new ManiphestTaskQuery())
->setViewer($viewer)
->withTaskIDs(array($id))
->execute();
return head($results);
}
+ protected function processReceivedObjectMail(
+ PhabricatorMetaMTAReceivedMail $mail,
+ PhabricatorLiskDAO $object,
+ PhabricatorUser $sender) {
+
+ $editor = new ManiphestTransactionEditor();
+ $editor->setActor($sender);
+ $handler = $editor->buildReplyHandler($object);
+
+ $handler->setActor($sender);
+ $handler->setExcludeMailRecipientPHIDs(
+ $mail->loadExcludeMailRecipientPHIDs());
+ $handler->processEmail($mail);
+ }
+
}
diff --git a/src/applications/metamta/constants/MetaMTAReceivedMailStatus.php b/src/applications/metamta/constants/MetaMTAReceivedMailStatus.php
index 062fcbdfbe..157523eecb 100644
--- a/src/applications/metamta/constants/MetaMTAReceivedMailStatus.php
+++ b/src/applications/metamta/constants/MetaMTAReceivedMailStatus.php
@@ -1,18 +1,19 @@
<?php
final class MetaMTAReceivedMailStatus
extends MetaMTAConstants {
const STATUS_DUPLICATE = 'err:duplicate';
const STATUS_FROM_PHABRICATOR = 'err:self';
const STATUS_NO_RECEIVERS = 'err:no-receivers';
const STATUS_ABUNDANT_RECEIVERS = 'err:multiple-receivers';
const STATUS_UNKNOWN_SENDER = 'err:unknown-sender';
const STATUS_DISABLED_SENDER = 'err:disabled-sender';
const STATUS_NO_PUBLIC_MAIL = 'err:no-public-mail';
const STATUS_USER_MISMATCH = 'err:bad-user';
const STATUS_POLICY_PROBLEM = 'err:policy';
const STATUS_NO_SUCH_OBJECT = 'err:not-found';
const STATUS_HASH_MISMATCH = 'err:bad-hash';
+ const STATUS_UNHANDLED_EXCEPTION = 'err:exception';
}
diff --git a/src/applications/metamta/receiver/PhabricatorMailReceiver.php b/src/applications/metamta/receiver/PhabricatorMailReceiver.php
index 3949c656a3..96f83d8435 100644
--- a/src/applications/metamta/receiver/PhabricatorMailReceiver.php
+++ b/src/applications/metamta/receiver/PhabricatorMailReceiver.php
@@ -1,191 +1,189 @@
<?php
abstract class PhabricatorMailReceiver {
abstract public function isEnabled();
abstract public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail);
- public function processReceivedMail(
+ abstract protected function processReceivedMail(
PhabricatorMetaMTAReceivedMail $mail,
- PhabricatorUser $sender) {
- return;
- }
+ PhabricatorUser $sender);
final public function receiveMail(
PhabricatorMetaMTAReceivedMail $mail,
PhabricatorUser $sender) {
$this->processReceivedMail($mail, $sender);
}
public function validateSender(
PhabricatorMetaMTAReceivedMail $mail,
PhabricatorUser $sender) {
if ($sender->getIsDisabled()) {
throw new PhabricatorMetaMTAReceivedMailProcessingException(
MetaMTAReceivedMailStatus::STATUS_DISABLED_SENDER,
pht(
"Sender '%s' has a disabled user account.",
$sender->getUsername()));
}
return;
}
/**
* Identifies the sender's user account for a piece of received mail. Note
* that this method does not validate that the sender is who they say they
* are, just that they've presented some credential which corresponds to a
* recognizable user.
*/
public function loadSender(PhabricatorMetaMTAReceivedMail $mail) {
$raw_from = $mail->getHeader('From');
$from = self::getRawAddress($raw_from);
$reasons = array();
// Try to find a user with this email address.
$user = PhabricatorUser::loadOneWithEmailAddress($from);
if ($user) {
return $user;
} else {
$reasons[] = pht(
"The email was sent from '%s', but this address does not correspond ".
"to any user account.",
$raw_from);
}
// If we missed on "From", try "Reply-To" if we're configured for it.
$reply_to_key = 'metamta.insecure-auth-with-reply-to';
$allow_reply_to = PhabricatorEnv::getEnvConfig($reply_to_key);
if ($allow_reply_to) {
$raw_reply_to = $mail->getHeader('Reply-To');
$reply_to = self::getRawAddress($raw_reply_to);
$user = PhabricatorUser::loadOneWithEmailAddress($reply_to);
if ($user) {
return $user;
} else {
$reasons[] = pht(
"Phabricator is configured to try to authenticate users using ".
"'Reply-To', but the reply to address ('%s') does not correspond ".
"to any user account.",
$raw_reply_to);
}
} else {
$reasons[] = pht(
"Phabricator is not configured to authenticate users using ".
"'Reply-To' (`metamta.insecure-auth-with-reply-to`), so the ".
"'Reply-To' header was not examined.");
}
// If we don't know who this user is, load or create an external user
// account for them if we're configured for it.
$email_key = 'phabricator.allow-email-users';
$allow_email_users = PhabricatorEnv::getEnvConfig($email_key);
if ($allow_email_users) {
$xuser = id(new PhabricatorExternalAccount())->loadOneWhere(
'accountType = %s AND accountDomain IS NULL and accountID = %s',
'email',
$from);
if (!$xuser) {
$xuser = id(new PhabricatorExternalAccount())
->setAccountID($from)
->setAccountType('email')
->setDisplayName($from)
->save();
}
return $xuser->getPhabricatorUser();
} else {
$reasons[] = pht(
"Phabricator is not configured to allow unknown external users to ".
"send mail to the system using just an email address ".
"(`phabricator.allow-email-users`), so an implicit external acount ".
"could not be created.");
}
throw new PhabricatorMetaMTAReceivedMailProcessingException(
MetaMTAReceivedMailStatus::STATUS_UNKNOWN_SENDER,
pht('Unknown sender: %s', implode(' ', $reasons)));
}
/**
* Determine if two inbound email addresses are effectively identical. This
* method strips and normalizes addresses so that equivalent variations are
* correctly detected as identical. For example, these addresses are all
* considered to match one another:
*
* "Abraham Lincoln" <alincoln@example.com>
* alincoln@example.com
* <ALincoln@example.com>
* "Abraham" <phabricator+ALINCOLN@EXAMPLE.COM> # With configured prefix.
*
* @param string Email address.
* @param string Another email address.
* @return bool True if addresses match.
*/
public static function matchAddresses($u, $v) {
$u = self::getRawAddress($u);
$v = self::getRawAddress($v);
$u = self::stripMailboxPrefix($u);
$v = self::stripMailboxPrefix($v);
return ($u === $v);
}
/**
* Strip a global mailbox prefix from an address if it is present. Phabricator
* can be configured to prepend a prefix to all reply addresses, which can
* make forwarding rules easier to write. A prefix looks like:
*
* example@phabricator.example.com # No Prefix
* phabricator+example@phabricator.example.com # Prefix "phabricator"
*
* @param string Email address, possibly with a mailbox prefix.
* @return string Email address with any prefix stripped.
*/
public static function stripMailboxPrefix($address) {
$address = id(new PhutilEmailAddress($address))->getAddress();
$prefix_key = 'metamta.single-reply-handler-prefix';
$prefix = PhabricatorEnv::getEnvConfig($prefix_key);
$len = strlen($prefix);
if ($len) {
$prefix = $prefix.'+';
$len = $len + 1;
}
if ($len) {
if (!strncasecmp($address, $prefix, $len)) {
$address = substr($address, strlen($prefix));
}
}
return $address;
}
/**
* Reduce an email address to its canonical form. For example, an adddress
* like:
*
* "Abraham Lincoln" < ALincoln@example.com >
*
* ...will be reduced to:
*
* alincoln@example.com
*
* @param string Email address in noncanonical form.
* @return string Canonical email address.
*/
public static function getRawAddress($address) {
$address = id(new PhutilEmailAddress($address))->getAddress();
return trim(phutil_utf8_strtolower($address));
}
}
diff --git a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php
index 7dc37661ab..4e09e7b50a 100644
--- a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php
+++ b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php
@@ -1,147 +1,179 @@
<?php
abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver {
/**
* 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.
*/
abstract protected function getObjectPattern();
/**
* 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);
+
+ 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);
+
public function loadMailReceiverObject($pattern, PhabricatorUser $viewer) {
return $this->loadObject($pattern, $viewer);
}
-
public function validateSender(
PhabricatorMetaMTAReceivedMail $mail,
PhabricatorUser $sender) {
+
parent::validateSender($mail, $sender);
- foreach ($mail->getToAddresses() as $address) {
- $parts = $this->matchObjectAddress($address);
- if ($parts) {
- break;
- }
- }
+ $parts = $this->matchObjectAddressInMail($mail);
try {
- $object = $this->loadObject($parts['pattern'], $sender);
+ $object = $this->loadObjectFromMail($mail, $sender);
} 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) {
+ if ($this->matchObjectAddressInMail($mail)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function matchObjectAddressInMail(
+ PhabricatorMetaMTAReceivedmail $mail) {
+
foreach ($mail->getToAddresses() as $address) {
- if ($this->matchObjectAddress($address)) {
- return true;
+ $parts = $this->matchObjectAddress($address);
+ if ($parts) {
+ return $parts;
}
}
- return false;
+ return null;
}
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;
}
private function getAddressRegexp() {
$pattern = $this->getObjectPattern();
$regexp =
'(^'.
'(?P<pattern>'.$pattern.')'.
'\\+'.
'(?P<sender>\w+)'.
'\\+'.
'(?P<hash>[a-f0-9]{16})'.
'$)U';
return $regexp;
}
+ private function loadObjectFromMail(
+ PhabricatorMetaMTAReceivedMail $mail,
+ PhabricatorUser $sender) {
+ $parts = $this->matchObjectAddressInMail($mail);
+
+ return $this->loadObject($parts['pattern'], $sender);
+ }
+
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);
}
}
diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php
index 8c7eb0f6e2..0b7b7c9040 100644
--- a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php
+++ b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php
@@ -1,395 +1,249 @@
<?php
final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
protected $headers = array();
protected $bodies = array();
protected $attachments = array();
protected $status = '';
protected $relatedPHID;
protected $authorPHID;
protected $message;
protected $messageIDHash = '';
public function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'headers' => self::SERIALIZATION_JSON,
'bodies' => self::SERIALIZATION_JSON,
'attachments' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
public function setHeaders(array $headers) {
// Normalize headers to lowercase.
$normalized = array();
foreach ($headers as $name => $value) {
$name = $this->normalizeMailHeaderName($name);
if ($name == 'message-id') {
$this->setMessageIDHash(PhabricatorHash::digestForIndex($value));
}
$normalized[$name] = $value;
}
$this->headers = $normalized;
return $this;
}
public function getHeader($key, $default = null) {
$key = $this->normalizeMailHeaderName($key);
return idx($this->headers, $key, $default);
}
private function normalizeMailHeaderName($name) {
return strtolower($name);
}
public function getMessageID() {
return $this->getHeader('Message-ID');
}
public function getSubject() {
return $this->getHeader('Subject');
}
public function getCCAddresses() {
return $this->getRawEmailAddresses(idx($this->headers, 'cc'));
}
public function getToAddresses() {
return $this->getRawEmailAddresses(idx($this->headers, 'to'));
}
public function loadExcludeMailRecipientPHIDs() {
$addresses = array_merge(
$this->getToAddresses(),
$this->getCCAddresses());
return $this->loadPHIDsFromAddresses($addresses);
}
final public function loadCCPHIDs() {
return $this->loadPHIDsFromAddresses($this->getCCAddresses());
}
private function loadPHIDsFromAddresses(array $addresses) {
if (empty($addresses)) {
return array();
}
$users = id(new PhabricatorUserEmail())
->loadAllWhere('address IN (%Ls)', $addresses);
$user_phids = mpull($users, 'getUserPHID');
$mailing_lists = id(new PhabricatorMetaMTAMailingList())
->loadAllWhere('email in (%Ls)', $addresses);
$mailing_list_phids = mpull($mailing_lists, 'getPHID');
return array_merge($user_phids, $mailing_list_phids);
}
- /**
- * Parses "to" addresses, looking for a public create email address
- * first and if not found parsing the "to" address for reply handler
- * information: receiver name, user id, and hash. If nothing can be
- * found, it then loads user phids for as many to: email addresses as
- * it can, theoretically falling back to create a conpherence amongst
- * those users.
- */
- private function getPhabricatorToInformation() {
- // Only one "public" create address so far
- $create_task = PhabricatorEnv::getEnvConfig(
- 'metamta.maniphest.public-create-email');
-
- // For replies, look for an object address with a format like:
- // D291+291+b0a41ca848d66dcc@example.com
- $single_handle_prefix = PhabricatorEnv::getEnvConfig(
- 'metamta.single-reply-handler-prefix');
-
- $prefixPattern = ($single_handle_prefix)
- ? preg_quote($single_handle_prefix, '/') . '\+'
- : '';
- $pattern = "/^{$prefixPattern}((?:D|T|C|E)\d+)\+([\w]+)\+([a-f0-9]{16})@/U";
-
- $phabricator_address = null;
- $receiver_name = null;
- $user_id = null;
- $hash = null;
- $user_names = array();
- foreach ($this->getToAddresses() as $address) {
- if ($address == $create_task) {
- $phabricator_address = $address;
- // it's okay to stop here because we just need to map a create
- // address to an application and don't need / won't have more
- // information in these cases.
- break;
- }
-
- $matches = null;
- $ok = preg_match(
- $pattern,
- $address,
- $matches);
-
- if ($ok) {
- $phabricator_address = $address;
- $receiver_name = $matches[1];
- $user_id = $matches[2];
- $hash = $matches[3];
- break;
- }
-
- $parts = explode('@', $address);
- $maybe_name = trim($parts[0]);
- $maybe_domain = trim($parts[1]);
- $mail_domain = PhabricatorEnv::getEnvConfig('metamta.domain');
- if ($mail_domain == $maybe_domain &&
- PhabricatorUser::validateUsername($maybe_name)) {
- $user_names[] = $maybe_name;
- }
- }
-
- return array(
- $phabricator_address,
- $receiver_name,
- $user_id,
- $hash,
- );
- }
-
-
public function processReceivedMail() {
try {
$this->dropMailFromPhabricator();
$this->dropMailAlreadyReceived();
$receiver = $this->loadReceiver();
$sender = $receiver->loadSender($this);
$receiver->validateSender($this, $sender);
$this->setAuthorPHID($sender->getPHID());
- // TODO: Once everything can receive mail, nuke this.
- $can_receive = false;
- if ($receiver instanceof ManiphestCreateMailReceiver) {
- $can_receive = true;
- }
- if ($receiver instanceof ConpherenceCreateThreadMailReceiver) {
- $can_receive = true;
- }
-
- if ($can_receive) {
- $receiver->receiveMail($this, $sender);
- return $this->setMessage('OK')->save();
- }
-
+ $receiver->receiveMail($this, $sender);
} catch (PhabricatorMetaMTAReceivedMailProcessingException $ex) {
$this
->setStatus($ex->getStatusCode())
->setMessage($ex->getMessage())
->save();
return $this;
- }
-
- list($to,
- $receiver_name,
- $user_id,
- $hash) = $this->getPhabricatorToInformation();
- if (!$to) {
- $raw_to = idx($this->headers, 'to');
- return $this->setMessage("Unrecognized 'to' format: {$raw_to}")->save();
- }
-
- $from = idx($this->headers, 'from');
-
- $user = $sender;
-
- $receiver = self::loadReceiverObject($receiver_name);
- if (!$receiver) {
- return $this->setMessage("Invalid object '{$receiver_name}'")->save();
- }
+ } catch (Exception $ex) {
+ $this
+ ->setStatus(MetaMTAReceivedMailStatus::STATUS_UNHANDLED_EXCEPTION)
+ ->setMessage(pht('Unhandled Exception: %s', $ex->getMessage()))
+ ->save();
- $this->setRelatedPHID($receiver->getPHID());
-
- if ($receiver instanceof ManiphestTask) {
- $editor = new ManiphestTransactionEditor();
- $editor->setActor($user);
- $handler = $editor->buildReplyHandler($receiver);
- } else if ($receiver instanceof DifferentialRevision) {
- $handler = DifferentialMail::newReplyHandlerForRevision($receiver);
- } else if ($receiver instanceof PhabricatorRepositoryCommit) {
- $handler = PhabricatorAuditCommentEditor::newReplyHandlerForCommit(
- $receiver);
- } else if ($receiver instanceof ConpherenceThread) {
- $handler = id(new ConpherenceReplyHandler())
- ->setMailReceiver($receiver);
+ throw $ex;
}
- $handler->setActor($user);
- $handler->setExcludeMailRecipientPHIDs(
- $this->loadExcludeMailRecipientPHIDs());
- $handler->processEmail($this);
-
- $this->setMessage('OK');
-
- return $this->save();
+ return $this->setMessage('OK')->save();
}
public function getCleanTextBody() {
$body = idx($this->bodies, 'text');
$parser = new PhabricatorMetaMTAEmailBodyParser();
return $parser->stripTextBody($body);
}
public function getRawTextBody() {
return idx($this->bodies, 'text');
}
- public static function loadReceiverObject($receiver_name) {
- if (!$receiver_name) {
- return null;
- }
-
- $receiver_type = $receiver_name[0];
- $receiver_id = substr($receiver_name, 1);
-
- $class_obj = null;
- switch ($receiver_type) {
- case 'T':
- $class_obj = new ManiphestTask();
- break;
- case 'D':
- $class_obj = new DifferentialRevision();
- break;
- case 'C':
- $class_obj = new PhabricatorRepositoryCommit();
- break;
- case 'E':
- $class_obj = new ConpherenceThread();
- break;
- default:
- return null;
- }
-
- return $class_obj->load($receiver_id);
- }
-
/**
* Strip an email address down to the actual user@domain.tld part if
* necessary, since sometimes it will have formatting like
* '"Abraham Lincoln" <alincoln@logcab.in>'.
*/
private function getRawEmailAddress($address) {
$matches = null;
$ok = preg_match('/<(.*)>/', $address, $matches);
if ($ok) {
$address = $matches[1];
}
return $address;
}
private function getRawEmailAddresses($addresses) {
$raw_addresses = array();
foreach (explode(',', $addresses) as $address) {
$raw_addresses[] = $this->getRawEmailAddress($address);
}
return array_filter($raw_addresses);
}
/**
* If Phabricator sent the mail, always drop it immediately. This prevents
* loops where, e.g., the public bug address is also a user email address
* and creating a bug sends them an email, which loops.
*/
private function dropMailFromPhabricator() {
if (!$this->getHeader('x-phabricator-sent-this-message')) {
return;
}
throw new PhabricatorMetaMTAReceivedMailProcessingException(
MetaMTAReceivedMailStatus::STATUS_FROM_PHABRICATOR,
"Ignoring email with 'X-Phabricator-Sent-This-Message' header to avoid ".
"loops.");
}
/**
* If this mail has the same message ID as some other mail, and isn't the
* first mail we we received with that message ID, we drop it as a duplicate.
*/
private function dropMailAlreadyReceived() {
$message_id_hash = $this->getMessageIDHash();
if (!$message_id_hash) {
// No message ID hash, so we can't detect duplicates. This should only
// happen with very old messages.
return;
}
$messages = $this->loadAllWhere(
'messageIDHash = %s ORDER BY id ASC LIMIT 2',
$message_id_hash);
$messages_count = count($messages);
if ($messages_count <= 1) {
// If we only have one copy of this message, we're good to process it.
return;
}
$first_message = reset($messages);
if ($first_message->getID() == $this->getID()) {
// If this is the first copy of the message, it is okay to process it.
// We may not have been able to to process it immediately when we received
// it, and could may have received several copies without processing any
// yet.
return;
}
$message = sprintf(
'Ignoring email with message id hash "%s" that has been seen %d '.
'times, including this message.',
$message_id_hash,
$messages_count);
throw new PhabricatorMetaMTAReceivedMailProcessingException(
MetaMTAReceivedMailStatus::STATUS_DUPLICATE,
$message);
}
/**
* Load a concrete instance of the @{class:PhabricatorMailReceiver} which
* accepts this mail, if one exists.
*/
private function loadReceiver() {
$receivers = id(new PhutilSymbolLoader())
->setAncestorClass('PhabricatorMailReceiver')
->loadObjects();
$accept = array();
foreach ($receivers as $key => $receiver) {
if (!$receiver->isEnabled()) {
continue;
}
if ($receiver->canAcceptMail($this)) {
$accept[$key] = $receiver;
}
}
if (!$accept) {
throw new PhabricatorMetaMTAReceivedMailProcessingException(
MetaMTAReceivedMailStatus::STATUS_NO_RECEIVERS,
"No concrete, enabled subclasses of `PhabricatorMailReceiver` can ".
"accept this mail.");
}
if (count($accept) > 1) {
$names = implode(', ', array_keys($accept));
throw new PhabricatorMetaMTAReceivedMailProcessingException(
MetaMTAReceivedMailStatus::STATUS_ABUNDANT_RECEIVERS,
"More than one `PhabricatorMailReceiver` claims to accept this mail.");
}
return head($accept);
}
}
diff --git a/src/applications/pholio/mail/PholioMockMailReceiver.php b/src/applications/pholio/mail/PholioMockMailReceiver.php
index bc8f12ede1..7ed6f73de1 100644
--- a/src/applications/pholio/mail/PholioMockMailReceiver.php
+++ b/src/applications/pholio/mail/PholioMockMailReceiver.php
@@ -1,24 +1,33 @@
<?php
final class PholioMockMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationPholio';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'M[1-9]\d*';
}
protected function loadObject($pattern, PhabricatorUser $viewer) {
$id = (int)trim($pattern, 'M');
return id(new PholioMockQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
}
+ protected function processReceivedObjectMail(
+ PhabricatorMetaMTAReceivedMail $mail,
+ PhabricatorLiskDAO $object,
+ PhabricatorUser $sender) {
+
+ // TODO: For now, we just drop this mail on the floor.
+
+ }
+
}
diff --git a/src/applications/ponder/mail/PonderQuestionMailReceiver.php b/src/applications/ponder/mail/PonderQuestionMailReceiver.php
index 484caee260..58511e1f00 100644
--- a/src/applications/ponder/mail/PonderQuestionMailReceiver.php
+++ b/src/applications/ponder/mail/PonderQuestionMailReceiver.php
@@ -1,24 +1,32 @@
<?php
final class PonderQuestionMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationPonder';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'Q[1-9]\d*';
}
protected function loadObject($pattern, PhabricatorUser $viewer) {
$id = (int)trim($pattern, 'Q');
return id(new PonderQuestionQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
}
+ protected function processReceivedObjectMail(
+ PhabricatorMetaMTAReceivedMail $mail,
+ PhabricatorLiskDAO $object,
+ PhabricatorUser $sender) {
+
+ // TODO: For now, we just drop this mail on the floor.
+
+ }
}
diff --git a/src/applications/releeph/mail/ReleephRequestMailReceiver.php b/src/applications/releeph/mail/ReleephRequestMailReceiver.php
index 67f6cce5b0..4a1ed016fc 100644
--- a/src/applications/releeph/mail/ReleephRequestMailReceiver.php
+++ b/src/applications/releeph/mail/ReleephRequestMailReceiver.php
@@ -1,24 +1,32 @@
<?php
final class ReleephRequestMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationReleeph';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'RQ[1-9]\d*';
}
protected function loadObject($pattern, PhabricatorUser $viewer) {
$id = (int)substr($pattern, 2);
return id(new ReleephRequestQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
}
+ protected function processReceivedObjectMail(
+ PhabricatorMetaMTAReceivedMail $mail,
+ PhabricatorLiskDAO $object,
+ PhabricatorUser $sender) {
+
+ // TODO: For now, we just drop this mail on the floor.
+
+ }
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Aug 14, 10:43 AM (5 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
202266
Default Alt Text
(40 KB)

Event Timeline