mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-27 16:00:59 +01:00
Support Postmark inbound mail via webhook
Summary: Depends on D19016. Ref T13053. Adds a listener for the Postmark webhook. Test Plan: Processed some test mail locally, at least: {F5416053} Reviewers: amckinley Maniphest Tasks: T13053 Differential Revision: https://secure.phabricator.com/D19017
This commit is contained in:
parent
0986c7f673
commit
5792032dc9
7 changed files with 125 additions and 22 deletions
|
@ -3272,6 +3272,7 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMetaMTAMailgunReceiveController' => 'PhabricatorMetaMTAController',
|
||||
'PhabricatorMetaMTAMemberQuery' => 'PhabricatorQuery',
|
||||
'PhabricatorMetaMTAPermanentFailureException' => 'Exception',
|
||||
'PhabricatorMetaMTAPostmarkReceiveController' => 'PhabricatorMetaMTAController',
|
||||
'PhabricatorMetaMTAReceivedMail' => 'PhabricatorMetaMTADAO',
|
||||
'PhabricatorMetaMTAReceivedMailProcessingException' => 'Exception',
|
||||
'PhabricatorMetaMTAReceivedMailTestCase' => 'PhabricatorTestCase',
|
||||
|
|
|
@ -42,6 +42,7 @@ final class PhabricatorMetaMTAApplication extends PhabricatorApplication {
|
|||
'detail/(?P<id>[1-9]\d*)/' => 'PhabricatorMetaMTAMailViewController',
|
||||
'sendgrid/' => 'PhabricatorMetaMTASendGridReceiveController',
|
||||
'mailgun/' => 'PhabricatorMetaMTAMailgunReceiveController',
|
||||
'postmark/' => 'PhabricatorMetaMTAPostmarkReceiveController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,15 +17,12 @@ final class PhabricatorMetaMTAMailgunReceiveController
|
|||
// 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;
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMetaMTAPostmarkReceiveController
|
||||
extends PhabricatorMetaMTAController {
|
||||
|
||||
public function shouldRequireLogin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PhabricatorStartup
|
||||
*/
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
// Don't process requests if we don't have a configured Postmark adapter.
|
||||
$mailers = PhabricatorMetaMTAMail::newMailersWithTypes(
|
||||
array(
|
||||
PhabricatorMailImplementationPostmarkAdapter::ADAPTERTYPE,
|
||||
));
|
||||
if (!$mailers) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$raw_input = PhabricatorStartup::getRawInput();
|
||||
|
||||
try {
|
||||
$data = phutil_json_decode($raw_input);
|
||||
} catch (Exception $ex) {
|
||||
return new Aphront400Response();
|
||||
}
|
||||
|
||||
$raw_headers = array();
|
||||
$header_items = idx($data, 'Headers', array());
|
||||
foreach ($header_items as $header_item) {
|
||||
$name = idx($header_item, 'Name');
|
||||
$value = idx($header_item, 'Value');
|
||||
$raw_headers[$name] = $value;
|
||||
}
|
||||
|
||||
$headers = array(
|
||||
'to' => 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"));
|
||||
}
|
||||
|
||||
}
|
|
@ -8,24 +8,14 @@ final class PhabricatorMetaMTASendGridReceiveController
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -482,6 +482,20 @@ final class PhabricatorMetaMTAMail
|
|||
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();
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ There are a few approaches available:
|
|||
| 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 @@ like this:
|
|||
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://<phabricator.yourdomain.com>/mail/postmark/
|
||||
```
|
||||
|
||||
|
||||
= SendGrid Setup =
|
||||
|
||||
To use SendGrid, you need a SendGrid account with access to the "Parse API" for
|
||||
|
|
Loading…
Reference in a new issue