Page MenuHomePhabricator

D7989.id18119.diff
No OneTemporary

D7989.id18119.diff

Index: src/__phutil_library_map__.php
===================================================================
--- src/__phutil_library_map__.php
+++ src/__phutil_library_map__.php
@@ -1574,6 +1574,7 @@
'PhabricatorMail' => 'applications/metamta/PhabricatorMail.php',
'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAdapter.php',
'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAmazonSESAdapter.php',
+ 'PhabricatorMailImplementationMailgunAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationMailgunAdapter.php',
'PhabricatorMailImplementationPHPMailerAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationPHPMailerAdapter.php',
'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationPHPMailerLiteAdapter.php',
'PhabricatorMailImplementationSendGridAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationSendGridAdapter.php',
@@ -1589,6 +1590,7 @@
'PhabricatorMailReceiver' => 'applications/metamta/receiver/PhabricatorMailReceiver.php',
'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php',
'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php',
+ 'PhabricatorMailgunConfigOptions' => 'applications/config/option/PhabricatorMailgunConfigOptions.php',
'PhabricatorMailingListPHIDTypeList' => 'applications/mailinglists/phid/PhabricatorMailingListPHIDTypeList.php',
'PhabricatorMailingListQuery' => 'applications/mailinglists/query/PhabricatorMailingListQuery.php',
'PhabricatorMailingListSearchEngine' => 'applications/mailinglists/query/PhabricatorMailingListSearchEngine.php',
@@ -1620,6 +1622,7 @@
'PhabricatorMetaMTAMailBody' => 'applications/metamta/view/PhabricatorMetaMTAMailBody.php',
'PhabricatorMetaMTAMailBodyTestCase' => 'applications/metamta/view/__tests__/PhabricatorMetaMTAMailBodyTestCase.php',
'PhabricatorMetaMTAMailTestCase' => 'applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php',
+ 'PhabricatorMetaMTAMailgunReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php',
'PhabricatorMetaMTAMailingList' => 'applications/mailinglists/storage/PhabricatorMetaMTAMailingList.php',
'PhabricatorMetaMTAPermanentFailureException' => 'applications/metamta/exception/PhabricatorMetaMTAPermanentFailureException.php',
'PhabricatorMetaMTAReceivedMail' => 'applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php',
@@ -4194,6 +4197,7 @@
'PhabricatorMacroTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorMacroViewController' => 'PhabricatorMacroController',
'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter',
+ 'PhabricatorMailImplementationMailgunAdapter' => 'PhabricatorMailImplementationAdapter',
'PhabricatorMailImplementationPHPMailerAdapter' => 'PhabricatorMailImplementationAdapter',
'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',
'PhabricatorMailImplementationSendGridAdapter' => 'PhabricatorMailImplementationAdapter',
@@ -4207,6 +4211,7 @@
'PhabricatorMailManagementShowOutboundWorkflow' => 'PhabricatorMailManagementWorkflow',
'PhabricatorMailManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase',
+ 'PhabricatorMailgunConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorMailingListPHIDTypeList' => 'PhabricatorPHIDType',
'PhabricatorMailingListQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorMailingListSearchEngine' => 'PhabricatorApplicationSearchEngine',
@@ -4235,6 +4240,7 @@
'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO',
'PhabricatorMetaMTAMailBodyTestCase' => 'PhabricatorTestCase',
'PhabricatorMetaMTAMailTestCase' => 'PhabricatorTestCase',
+ 'PhabricatorMetaMTAMailgunReceiveController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAMailingList' =>
array(
0 => 'PhabricatorMetaMTADAO',
Index: src/applications/config/option/PhabricatorMailgunConfigOptions.php
===================================================================
--- /dev/null
+++ src/applications/config/option/PhabricatorMailgunConfigOptions.php
@@ -0,0 +1,27 @@
+<?php
+
+final class PhabricatorMailgunConfigOptions
+ extends PhabricatorApplicationConfigOptions {
+
+ public function getName() {
+ return pht("Integration with Mailgun");
+ }
+
+ public function getDescription() {
+ return pht("Configure Mailgun integration.");
+ }
+
+ public function getOptions() {
+ return array(
+ $this->newOption('mailgun.api-key', 'string', null)
+ ->setMasked(true)
+ ->setDescription(pht('Mailgun API key.')),
+ $this->newOption('mailgun.domain', 'string', null)
+ ->setDescription(pht(
+ 'Mailgun domain name. See https://mailgun.com/cp/domains'))
+ ->addExample('mycompany.com', 'Use specific domain'),
+ );
+
+ }
+
+}
Index: src/applications/metamta/adapter/PhabricatorMailImplementationMailgunAdapter.php
===================================================================
--- /dev/null
+++ src/applications/metamta/adapter/PhabricatorMailImplementationMailgunAdapter.php
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * Mail adapter that uses Mailgun's web API to deliver email.
+ */
+final class PhabricatorMailImplementationMailgunAdapter
+ extends PhabricatorMailImplementationAdapter {
+
+ private $params = array();
+
+ public function setFrom($email, $name = '') {
+ $this->params['from'] = $email;
+ $this->params['from-name'] = $name;
+ return $this;
+ }
+
+ public function addReplyTo($email, $name = '') {
+ if (empty($this->params['reply-to'])) {
+ $this->params['reply-to'] = array();
+ }
+ $this->params['reply-to'][] = array(
+ 'email' => $email,
+ 'name' => $name,
+ );
+ return $this;
+ }
+
+ public function addTos(array $emails) {
+ foreach ($emails as $email) {
+ $this->params['tos'][] = $email;
+ }
+ return $this;
+ }
+
+ public function addCCs(array $emails) {
+ foreach ($emails as $email) {
+ $this->params['ccs'][] = $email;
+ }
+ return $this;
+ }
+
+ public function addAttachment($data, $filename, $mimetype) {
+ if (empty($this->params['files'])) {
+ $this->params['files'] = array();
+ }
+ $this->params['files'][$filename] = $data;
+ }
+
+ public function addHeader($header_name, $header_value) {
+ $this->params['headers'][] = array($header_name, $header_value);
+ return $this;
+ }
+
+ public function setBody($body) {
+ $this->params['body'] = $body;
+ return $this;
+ }
+
+ public function setSubject($subject) {
+ $this->params['subject'] = $subject;
+ return $this;
+ }
+
+ public function setIsHTML($is_html) {
+ $this->params['is-html'] = $is_html;
+ return $this;
+ }
+
+ public function supportsMessageIDHeader() {
+ return false;
+ }
+
+ public function send() {
+ $key = PhabricatorEnv::getEnvConfig('mailgun.api-key');
+ $domain = PhabricatorEnv::getEnvConfig('mailgun.domain');
+ $params = array();
+
+ $params['to'] = idx($this->params, 'tos', array());
+ $params['subject'] = idx($this->params, 'subject');
+
+ if (idx($this->params, 'is-html')) {
+ $params['html'] = idx($this->params, 'body');
+ } else {
+ $params['text'] = idx($this->params, 'body');
+ }
+
+ $from = idx($this->params, 'from');
+ if (idx($this->params, 'from-name')) {
+ $params['from'] = "{$this->params['from-name']} <{$from}>";
+ }
+ else {
+ $params['from'] = $from;
+ }
+
+ if (idx($this->params, 'reply-to')) {
+ $replyto = $this->params['reply-to'];
+ $params['h:reply-to'] = $replyto;
+ }
+
+ if (idx($this->params, 'ccs')) {
+ $params['cc'] = $this->params['ccs'];
+ }
+
+ $future = new HTTPSFuture(
+ "https://api:{$key}@api.mailgun.net/v2/{$domain}/messages",
+ $params);
+ $future->setMethod('POST');
+
+ list($body) = $future->resolvex();
+
+ $response = json_decode($body, true);
+ if (!is_array($response)) {
+ throw new Exception("Failed to JSON decode response: {$body}");
+ }
+
+ if (!idx($response, 'id')) {
+ $message = $response['message'];
+ throw new Exception("Request failed with errors: {$message}.");
+ }
+
+ return true;
+ }
+
+}
Index: src/applications/metamta/application/PhabricatorApplicationMetaMTA.php
===================================================================
--- src/applications/metamta/application/PhabricatorApplicationMetaMTA.php
+++ src/applications/metamta/application/PhabricatorApplicationMetaMTA.php
@@ -34,6 +34,7 @@
return array(
$this->getBaseURI() => array(
'sendgrid/' => 'PhabricatorMetaMTASendGridReceiveController',
+ 'mailgun/' => 'PhabricatorMetaMTAMailgunReceiveController',
),
);
}
Index: src/applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php
===================================================================
--- /dev/null
+++ src/applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php
@@ -0,0 +1,76 @@
+<?php
+
+final class PhabricatorMetaMTAMailgunReceiveController
+ extends PhabricatorMetaMTAController {
+
+ public function shouldRequireLogin() {
+ return false;
+ }
+
+ private function verifyMessage() {
+ $api_key = PhabricatorEnv::getEnvConfig('mailgun.api-key');
+ $request = $this->getRequest();
+ $timestamp = $request->getStr('timestamp');
+ $token = $request->getStr('token');
+ $sig = $request->getStr('signature');
+ return hash_hmac('sha256', $timestamp.$token, $api_key) == $sig;
+
+ }
+ public function processRequest() {
+
+ // No CSRF for Mailgun.
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+
+ if (!$this->verifyMessage()) {
+ throw new Exception(
+ pht('Mail signature is not valid. Check your Mailgun API key.'));
+ }
+
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ $raw_headers = $request->getStr('headers');
+ $raw_headers = explode("\n", rtrim($raw_headers));
+ $raw_dict = array();
+ foreach (array_filter($raw_headers) as $header) {
+ list($name, $value) = explode(':', $header, 2);
+ $raw_dict[$name] = ltrim($value);
+ }
+
+ $headers = array(
+ 'to' => $request->getStr('recipient'),
+ 'from' => $request->getStr('from'),
+ 'subject' => $request->getStr('subject'),
+ ) + $raw_dict;
+
+ $received = new PhabricatorMetaMTAReceivedMail();
+ $received->setHeaders($headers);
+ $received->setBodies(array(
+ 'text' => $request->getStr('stripped-text'),
+ 'html' => $request->getStr('stripped-html'),
+ ));
+
+ $file_phids = array();
+ foreach ($_FILES as $file_raw) {
+ try {
+ $file = PhabricatorFile::newFromPHPUpload(
+ $file_raw,
+ array(
+ 'authorPHID' => $user->getPHID(),
+ ));
+ $file_phids[] = $file->getPHID();
+ } catch (Exception $ex) {
+ phlog($ex);
+ }
+ }
+ $received->setAttachments($file_phids);
+ $received->save();
+
+ $received->processReceivedMail();
+
+ $response = new AphrontWebpageResponse();
+ $response->setContent(pht("Got it! Thanks, Mailgun!\n"));
+ return $response;
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Thu, Nov 14, 10:39 PM (5 d, 7 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6767681
Default Alt Text
D7989.id18119.diff (11 KB)

Event Timeline