mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-23 12:09:12 +01:00
Use "cluster.mailers" if it is configured
Summary: Depends on D19004. Ref T13053. Ref T12677. If the new `cluster.mailers` is configured, make use of it. Also use it in the Sengrid/Mailgun inbound stuff. Also fix a bug where "Must Encrypt" mail to no recipients could fatal because no `$mail` was returned. Test Plan: Processed some mail locally. The testing on this is still pretty flimsy, but I plan to solidify it in an upcoming change. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13053, T12677 Differential Revision: https://secure.phabricator.com/D19005
This commit is contained in:
parent
4236952cdb
commit
994d2e8e15
5 changed files with 126 additions and 20 deletions
|
@ -3,6 +3,7 @@
|
||||||
abstract class PhabricatorMailImplementationAdapter extends Phobject {
|
abstract class PhabricatorMailImplementationAdapter extends Phobject {
|
||||||
|
|
||||||
private $key;
|
private $key;
|
||||||
|
private $priority;
|
||||||
private $options = array();
|
private $options = array();
|
||||||
|
|
||||||
final public function getAdapterType() {
|
final public function getAdapterType() {
|
||||||
|
@ -57,6 +58,15 @@ abstract class PhabricatorMailImplementationAdapter extends Phobject {
|
||||||
return $this->key;
|
return $this->key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function setPriority($priority) {
|
||||||
|
$this->priority = $priority;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getPriority() {
|
||||||
|
return $this->priority;
|
||||||
|
}
|
||||||
|
|
||||||
final public function getOption($key) {
|
final public function getOption($key) {
|
||||||
if (!array_key_exists($key, $this->options)) {
|
if (!array_key_exists($key, $this->options)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
|
|
|
@ -8,14 +8,31 @@ final class PhabricatorMetaMTAMailgunReceiveController
|
||||||
}
|
}
|
||||||
|
|
||||||
private function verifyMessage() {
|
private function verifyMessage() {
|
||||||
$api_key = PhabricatorEnv::getEnvConfig('mailgun.api-key');
|
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$timestamp = $request->getStr('timestamp');
|
$timestamp = $request->getStr('timestamp');
|
||||||
$token = $request->getStr('token');
|
$token = $request->getStr('token');
|
||||||
$sig = $request->getStr('signature');
|
$sig = $request->getStr('signature');
|
||||||
$hash = hash_hmac('sha256', $timestamp.$token, $api_key);
|
|
||||||
|
|
||||||
return phutil_hashes_are_identical($sig, $hash);
|
// An install may configure multiple Mailgun mailers, and we might receive
|
||||||
|
// inbound mail from any of them. Test the signature to see if it matches
|
||||||
|
// any configured Mailgun mailer.
|
||||||
|
|
||||||
|
$mailers = PhabricatorMetaMTAMail::newMailers();
|
||||||
|
$mailgun_type = PhabricatorMailImplementationMailgunAdapter::ADAPTERTYPE;
|
||||||
|
foreach ($mailers as $mailer) {
|
||||||
|
if ($mailer->getAdapterType() != $mailgun_type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$api_key = $mailer->getOption('api-key');
|
||||||
|
|
||||||
|
$hash = hash_hmac('sha256', $timestamp.$token, $api_key);
|
||||||
|
if (phutil_hashes_are_identical($sig, $hash)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
|
|
@ -8,6 +8,26 @@ final class PhabricatorMetaMTASendGridReceiveController
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$mailers = PhabricatorMetaMTAMail::newMailers();
|
||||||
|
$sendgrid_type = PhabricatorMailImplementationSendGridAdapter::ADAPTERTYPE;
|
||||||
|
|
||||||
|
// SendGrid doesn't sign payloads so we can't be sure that SendGrid
|
||||||
|
// actually sent this request, but require a configured SendGrid mailer
|
||||||
|
// before we activate this endpoint.
|
||||||
|
|
||||||
|
$has_sendgrid = false;
|
||||||
|
foreach ($mailers as $mailer) {
|
||||||
|
if ($mailer->getAdapterType() != $sendgrid_type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$has_sendgrid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$has_sendgrid) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
// No CSRF for SendGrid.
|
// No CSRF for SendGrid.
|
||||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||||
|
|
|
@ -463,14 +463,16 @@ final class PhabricatorMetaMTAMail
|
||||||
throw new Exception(pht('Trying to send an already-sent mail!'));
|
throw new Exception(pht('Trying to send an already-sent mail!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$mailers = $this->newMailers();
|
$mailers = self::newMailers();
|
||||||
|
|
||||||
return $this->sendWithMailers($mailers);
|
return $this->sendWithMailers($mailers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function newMailers() {
|
public static function newMailers() {
|
||||||
$mailers = array();
|
$mailers = array();
|
||||||
|
|
||||||
|
$config = PhabricatorEnv::getEnvConfig('cluster.mailers');
|
||||||
|
if ($config === null) {
|
||||||
$mailer = PhabricatorEnv::newObjectFromConfig('metamta.mail-adapter');
|
$mailer = PhabricatorEnv::newObjectFromConfig('metamta.mail-adapter');
|
||||||
|
|
||||||
$defaults = $mailer->newDefaultOptions();
|
$defaults = $mailer->newDefaultOptions();
|
||||||
|
@ -480,16 +482,66 @@ final class PhabricatorMetaMTAMail
|
||||||
|
|
||||||
$mailer
|
$mailer
|
||||||
->setKey('default')
|
->setKey('default')
|
||||||
|
->setPriority(-1)
|
||||||
->setOptions($options);
|
->setOptions($options);
|
||||||
|
|
||||||
$mailer->prepareForSend();
|
|
||||||
|
|
||||||
$mailers[] = $mailer;
|
$mailers[] = $mailer;
|
||||||
|
} else {
|
||||||
|
$adapters = PhabricatorMailImplementationAdapter::getAllAdapters();
|
||||||
|
$next_priority = -1;
|
||||||
|
|
||||||
return $mailers;
|
foreach ($config as $spec) {
|
||||||
|
$type = $spec['type'];
|
||||||
|
if (!isset($adapters[$type])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Unknown mailer ("%s")!',
|
||||||
|
$type));
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = $spec['key'];
|
||||||
|
$mailer = id(clone $adapters[$type])
|
||||||
|
->setKey($key);
|
||||||
|
|
||||||
|
$priority = idx($spec, 'priority');
|
||||||
|
if (!$priority) {
|
||||||
|
$priority = $next_priority;
|
||||||
|
$next_priority--;
|
||||||
|
}
|
||||||
|
$mailer->setPriority($priority);
|
||||||
|
|
||||||
|
$defaults = $mailer->newDefaultOptions();
|
||||||
|
$options = idx($spec, 'options', array()) + $defaults;
|
||||||
|
$mailer->setOptions($options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sorted = array();
|
||||||
|
$groups = mgroup($mailers, 'getPriority');
|
||||||
|
ksort($groups);
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
// Reorder services within the same priority group randomly.
|
||||||
|
shuffle($group);
|
||||||
|
foreach ($group as $mailer) {
|
||||||
|
$sorted[] = $mailer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($sorted as $mailer) {
|
||||||
|
$mailer->prepareForSend();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendWithMailers(array $mailers) {
|
public function sendWithMailers(array $mailers) {
|
||||||
|
if (!$mailers) {
|
||||||
|
return $this
|
||||||
|
->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID)
|
||||||
|
->setMessage(pht('No mailers are configured.'))
|
||||||
|
->save();
|
||||||
|
}
|
||||||
|
|
||||||
$exceptions = array();
|
$exceptions = array();
|
||||||
foreach ($mailers as $template_mailer) {
|
foreach ($mailers as $template_mailer) {
|
||||||
$mailer = null;
|
$mailer = null;
|
||||||
|
@ -865,6 +917,12 @@ final class PhabricatorMetaMTAMail
|
||||||
$mailer->addCCs($add_cc);
|
$mailer->addCCs($add_cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep track of which mailer actually ended up accepting the message.
|
||||||
|
$mailer_key = $mailer->getKey();
|
||||||
|
if ($mailer_key !== null) {
|
||||||
|
$this->setParam('mailer.key', $mailer_key);
|
||||||
|
}
|
||||||
|
|
||||||
return $mailer;
|
return $mailer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2575,12 +2575,13 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
|
|
||||||
$mail = $this->buildMailForTarget($object, $xactions, $target);
|
$mail = $this->buildMailForTarget($object, $xactions, $target);
|
||||||
|
|
||||||
|
if ($mail) {
|
||||||
if ($this->mustEncrypt) {
|
if ($this->mustEncrypt) {
|
||||||
$mail
|
$mail
|
||||||
->setMustEncrypt(true)
|
->setMustEncrypt(true)
|
||||||
->setMustEncryptReasons($this->mustEncrypt);
|
->setMustEncryptReasons($this->mustEncrypt);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$caught = $ex;
|
$caught = $ex;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue