Changeset View
Changeset View
Standalone View
Standalone View
src/applications/metamta/storage/PhabricatorMetaMTAMail.php
Show First 20 Lines • Show All 457 Lines • ▼ Show 20 Lines | final class PhabricatorMetaMTAMail | ||||
* | * | ||||
* @return void | * @return void | ||||
*/ | */ | ||||
public function sendNow() { | public function sendNow() { | ||||
if ($this->getStatus() != PhabricatorMailOutboundStatus::STATUS_QUEUE) { | if ($this->getStatus() != PhabricatorMailOutboundStatus::STATUS_QUEUE) { | ||||
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(); | ||||
$options = $mailer->newLegacyOptions(); | $options = $mailer->newLegacyOptions(); | ||||
$options = $options + $defaults; | $options = $options + $defaults; | ||||
$mailer | $mailer | ||||
->setKey('default') | ->setKey('default') | ||||
->setPriority(-1) | |||||
amckinley: This works because we don't invoke `validateOptions()` before invoking `setOptions()`, right? | |||||
epriestleyAuthorUnsubmitted Not Done Inline ActionsThe top-level shared stuff (key, type, priority) is a level above options. setOptions() will validate options, but "options" only has mailer-specifc stuff (api key, domain, username, etc.), not shared stuff. epriestley: The top-level shared stuff (key, type, priority) is a level above options. `setOptions()` will… | |||||
->setOptions($options); | ->setOptions($options); | ||||
$mailer->prepareForSend(); | |||||
$mailers[] = $mailer; | $mailers[] = $mailer; | ||||
} else { | |||||
$adapters = PhabricatorMailImplementationAdapter::getAllAdapters(); | |||||
$next_priority = -1; | |||||
foreach ($config as $spec) { | |||||
$type = $spec['type']; | |||||
if (!isset($adapters[$type])) { | |||||
throw new Exception( | |||||
pht( | |||||
'Unknown mailer ("%s")!', | |||||
$type)); | |||||
} | |||||
return $mailers; | $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; | ||||
try { | try { | ||||
$mailer = $this->buildMailer($template_mailer); | $mailer = $this->buildMailer($template_mailer); | ||||
} catch (Exception $ex) { | } catch (Exception $ex) { | ||||
$exceptions[] = $ex; | $exceptions[] = $ex; | ||||
▲ Show 20 Lines • Show All 359 Lines • ▼ Show 20 Lines | private function buildMailer(PhabricatorMailImplementationAdapter $mailer) { | ||||
$add_to = array_unique($add_to); | $add_to = array_unique($add_to); | ||||
$add_cc = array_diff(array_unique($add_cc), $add_to); | $add_cc = array_diff(array_unique($add_cc), $add_to); | ||||
$mailer->addTos($add_to); | $mailer->addTos($add_to); | ||||
if ($add_cc) { | if ($add_cc) { | ||||
$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; | ||||
} | } | ||||
private function generateThreadIndex($seed, $is_first_mail) { | private function generateThreadIndex($seed, $is_first_mail) { | ||||
// When threading, Outlook ignores the 'References' and 'In-Reply-To' | // When threading, Outlook ignores the 'References' and 'In-Reply-To' | ||||
// headers that most clients use. Instead, it uses a custom 'Thread-Index' | // headers that most clients use. Instead, it uses a custom 'Thread-Index' | ||||
// header. The format of this header is something like this (from | // header. The format of this header is something like this (from | ||||
// camel-exchange-folder.c in Evolution Exchange): | // camel-exchange-folder.c in Evolution Exchange): | ||||
▲ Show 20 Lines • Show All 552 Lines • Show Last 20 Lines |
This works because we don't invoke validateOptions() before invoking setOptions(), right?