diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3272,6 +3272,7 @@ 'PhabricatorMetaMTAMailgunReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php', 'PhabricatorMetaMTAMemberQuery' => 'applications/metamta/query/PhabricatorMetaMTAMemberQuery.php', 'PhabricatorMetaMTAPermanentFailureException' => 'applications/metamta/exception/PhabricatorMetaMTAPermanentFailureException.php', + 'PhabricatorMetaMTAPostmarkReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTAPostmarkReceiveController.php', 'PhabricatorMetaMTAReceivedMail' => 'applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php', 'PhabricatorMetaMTAReceivedMailProcessingException' => 'applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php', 'PhabricatorMetaMTAReceivedMailTestCase' => 'applications/metamta/storage/__tests__/PhabricatorMetaMTAReceivedMailTestCase.php', @@ -8787,6 +8788,7 @@ 'PhabricatorMetaMTAMailgunReceiveController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAMemberQuery' => 'PhabricatorQuery', 'PhabricatorMetaMTAPermanentFailureException' => 'Exception', + 'PhabricatorMetaMTAPostmarkReceiveController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAReceivedMail' => 'PhabricatorMetaMTADAO', 'PhabricatorMetaMTAReceivedMailProcessingException' => 'Exception', 'PhabricatorMetaMTAReceivedMailTestCase' => 'PhabricatorTestCase', diff --git a/src/applications/metamta/application/PhabricatorMetaMTAApplication.php b/src/applications/metamta/application/PhabricatorMetaMTAApplication.php --- a/src/applications/metamta/application/PhabricatorMetaMTAApplication.php +++ b/src/applications/metamta/application/PhabricatorMetaMTAApplication.php @@ -42,6 +42,7 @@ 'detail/(?P[1-9]\d*)/' => 'PhabricatorMetaMTAMailViewController', 'sendgrid/' => 'PhabricatorMetaMTASendGridReceiveController', 'mailgun/' => 'PhabricatorMetaMTAMailgunReceiveController', + 'postmark/' => 'PhabricatorMetaMTAPostmarkReceiveController', ), ); } diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php b/src/applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php --- a/src/applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php @@ -17,15 +17,12 @@ // 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; + $mailers = PhabricatorMetaMTAMail::newMailersWithTypes( + array( + 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; diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAPostmarkReceiveController.php b/src/applications/metamta/controller/PhabricatorMetaMTAPostmarkReceiveController.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/controller/PhabricatorMetaMTAPostmarkReceiveController.php @@ -0,0 +1,87 @@ + idx($data, 'To'), + 'from' => idx($data, 'From'), + 'cc' => idx($data, 'Cc'), + 'subject' => idx($data, 'Subject'), + ) + $raw_headers; + + + $received = id(new PhabricatorMetaMTAReceivedMail()) + ->setHeaders($headers) + ->setBodies( + array( + 'text' => idx($data, 'TextBody'), + 'html' => idx($data, 'HtmlBody'), + )); + + $file_phids = array(); + $attachments = idx($data, 'Attachments', array()); + foreach ($attachments as $attachment) { + $file_data = idx($attachment, 'Content'); + $file_data = base64_decode($file_data); + + try { + $file = PhabricatorFile::newFromFileData( + $file_data, + array( + 'name' => idx($attachment, 'Name'), + 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, + )); + $file_phids[] = $file->getPHID(); + } catch (Exception $ex) { + phlog($ex); + } + } + $received->setAttachments($file_phids); + + try { + $received->save(); + $received->processReceivedMail(); + } catch (Exception $ex) { + phlog($ex); + } + + return id(new AphrontWebpageResponse()) + ->setContent(pht("Got it! Thanks, Postmark!\n")); + } + +} diff --git a/src/applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php b/src/applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php --- a/src/applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php @@ -8,24 +8,14 @@ } 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) { + $mailers = PhabricatorMetaMTAMail::newMailersWithTypes( + array( + PhabricatorMailImplementationSendGridAdapter::ADAPTERTYPE, + )); + if (!$mailers) { return new Aphront404Response(); } diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php --- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php @@ -482,6 +482,20 @@ return $this->sendWithMailers($mailers); } + public static function newMailersWithTypes(array $types) { + $mailers = self::newMailers(); + $types = array_fuse($types); + + foreach ($mailers as $key => $mailer) { + $mailer_type = $mailer->getAdapterType(); + if (!isset($types[$mailer_type])) { + unset($mailers[$key]); + } + } + + return array_values($mailers); + } + public static function newMailers() { $mailers = array(); diff --git a/src/docs/user/configuration/configuring_inbound_email.diviner b/src/docs/user/configuration/configuring_inbound_email.diviner --- a/src/docs/user/configuration/configuring_inbound_email.diviner +++ b/src/docs/user/configuration/configuring_inbound_email.diviner @@ -14,6 +14,7 @@ | Receive Mail With | Setup | Cost | Notes | |--------|-------|------|-------| | Mailgun | Easy | Cheap | Recommended | +| Postmark | Easy | Cheap | Recommended | | SendGrid | Easy | Cheap | | | Local MTA | Extremely Difficult | Free | Strongly discouraged! | @@ -130,6 +131,17 @@ example domain with your actual domain. - Set the `mailgun.api-key` config key to your Mailgun API key. +Postmark Setup +============== + +To process inbound mail from Postmark, configure this URI as your inbound +webhook URI in the Postmark control panel: + +``` +https:///mail/postmark/ +``` + + = SendGrid Setup = To use SendGrid, you need a SendGrid account with access to the "Parse API" for