diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 0a91f59874..013a4e8002 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -359,6 +359,7 @@ phutil_register_library_map(array(
'PhabricatorMetaMTAReceivedListController' => 'applications/metamta/controller/receivedlist',
'PhabricatorMetaMTAReceivedMail' => 'applications/metamta/storage/receivedmail',
'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send',
+ 'PhabricatorMetaMTASendGridReceiveController' => 'applications/metamta/controller/sendgridreceive',
'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view',
'PhabricatorOAuthDefaultRegistrationController' => 'applications/auth/controller/oauthregistration/default',
'PhabricatorOAuthDiagnosticsController' => 'applications/auth/controller/oauthdiagnostics',
@@ -798,6 +799,7 @@ phutil_register_library_map(array(
'PhabricatorMetaMTAReceivedListController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAReceivedMail' => 'PhabricatorMetaMTADAO',
'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController',
+ 'PhabricatorMetaMTASendGridReceiveController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController',
'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController',
diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
index fa2c2ea83f..d198df326e 100644
--- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
+++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
@@ -126,6 +126,7 @@ class AphrontDefaultApplicationConfiguration
=> 'PhabricatorMetaMTAMailingListEditController',
'receive/$' => 'PhabricatorMetaMTAReceiveController',
'received/$' => 'PhabricatorMetaMTAReceivedListController',
+ 'sendgrid/$' => 'PhabricatorMetaMTASendGridReceiveController',
),
'/login/' => array(
diff --git a/src/applications/metamta/controller/sendgridreceive/PhabricatorMetaMTASendGridReceiveController.php b/src/applications/metamta/controller/sendgridreceive/PhabricatorMetaMTASendGridReceiveController.php
new file mode 100644
index 0000000000..281eef3559
--- /dev/null
+++ b/src/applications/metamta/controller/sendgridreceive/PhabricatorMetaMTASendGridReceiveController.php
@@ -0,0 +1,70 @@
+getRequest();
+
+ $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('to'),
+ 'from' => $request->getStr('from'),
+ 'subject' => $request->getStr('subject'),
+ ) + $raw_dict;
+
+ $received = new PhabricatorMetaMTAReceivedMail();
+ $received->setHeaders($headers);
+ $received->setBodies(array(
+ 'text' => $request->getStr('text'),
+ 'html' => $request->getStr('from'),
+ ));
+
+ $file_phids = array();
+ foreach ($_FILES as $file_raw) {
+ try {
+ $file = PhabricatorFile::newFromPHPUpload($file_raw);
+ $file_phids[] = $file->getPHID();
+ } catch (Exception $ex) {
+ phlog($ex);
+ }
+ }
+ $received->setAttachments($file_phids);
+ $received->save();
+
+ $received->processReceivedMail();
+
+ $response = new AphrontWebpageResponse();
+ $response->setContent("Got it! Thanks, SendGrid!\n");
+ return $response;
+ }
+
+}
diff --git a/src/applications/metamta/controller/sendgridreceive/__init__.php b/src/applications/metamta/controller/sendgridreceive/__init__.php
new file mode 100644
index 0000000000..013ff110b2
--- /dev/null
+++ b/src/applications/metamta/controller/sendgridreceive/__init__.php
@@ -0,0 +1,17 @@
+ $value) {
+ $normalized[strtolower($name)] = $value;
+ }
+ $this->headers = $normalized;
+ return $this;
+ }
+
public function processReceivedMail() {
$to = idx($this->headers, 'to');
diff --git a/src/docs/configuring_inbound_email.diviner b/src/docs/configuring_inbound_email.diviner
index f3ead1557a..8211892936 100644
--- a/src/docs/configuring_inbound_email.diviner
+++ b/src/docs/configuring_inbound_email.diviner
@@ -6,13 +6,114 @@ may update Differential and Maniphest by replying to messages.
= Preamble =
-This is extremely difficult to configure correctly. This is doubly true if
+This can be extremely difficult to configure correctly. This is doubly true if
you use sendmail.
+There are basically a few approaches available:
+
+ - Use SendGrid (), which is very easy but is not free.
+ - Run your own MTA, which can be quite harrowing to configure but is free.
+ - Tell the Phabricator devteam about another service you'd like support for,
+ this stuff is seriously terrible to configure on your own.
+
+= Configuring Phabricator =
+
+By default, Phabricator uses a "noreply@example.com" email address as the 'From'
+(configurable with ##metamta.default-address##) and sets 'Reply-To' to the
+user generating the email (e.g., by making a comment), if the mail was generated
+by a user action. This means that users can reply (or reply-all) to email to
+discuss changes, but the conversation won't be recorded in Phabricator and users
+will not be able to take actions like claiming tasks or requesting changes to
+revisions.
+
+To change this behavior so that users can interact with objects in Phabricator
+over email, set these configuration keys:
+
+ - ##metamta.differential.reply-handler-domain##: enables email replies for
+ Differential.
+ - ##metamta.maniphest.reply-handler-domain##: enables email replies for
+ Maniphest.
+
+Set these keys to some domain which you configure according to the instructions
+below, e.g. "##phabricator.example.com##". You can set these both to the same
+domain, and will generally want to. Once you set these keys, emails will use a
+'Reply-To' like "##T123+273+af310f9220ad@example.com##", which -- when
+configured correctly, according to the instructions below -- will parse incoming
+email and allow users to interact with Maniphest tasks and Differential
+revisions over email.
+
+= Security =
+
+The email reply channel is "somewhat" authenticated. Each reply-to address is
+unique to the recipient and includes a hash of user information and a unique
+object ID, so it can only be used to update that object and only be used to act
+on behalf of the recipient.
+
+However, if an address is leaked (which is fairly easy -- for instance,
+forwarding an email will leak a live reply address, or a user might take a
+screenshot), //anyone// who can send mail to your reply-to domain may interact
+with the object the email relates to as the user who leaked the mail. Because
+the authentication around email has this weakness, some actions (like accepting
+revisions) are not permitted over email.
+
+This implementation is an attempt to balance utility and security, but makes
+some sacrifices on both sides to achieve it because of the difficulty of
+authenticating senders in the general case (e.g., where you are an open source
+project and need to interact with users whose email accounts you have no control
+over).
+
+If you leak a bunch of reply-to addresses by accident, you can change
+##phabricator.mail-key## in your configuration to invalidate all the old hashes.
+
+NOTE: Phabricator does not currently attempt to verify "From" addresses because
+this is technically complex, seems unreasonably difficult in the general case,
+and no installs have had a need for it yet. If you have a specific case where a
+reasonable mechanism exists to provide sender verification (e.g., DKIM
+signatures are sufficient to authenticate the sender under your configuration,
+or you are willing to require all users to sign their email), file a feature
+request.
+
+= Testing =
+
+You can view a log of received mail by going to MetaMTA -> Received in the
+Phabricator web interface. This can help you determine if mail is being
+delivered to Phabricator or not.
+
+You can also use the "Test Receiver" button, but note that this just simulates
+receiving mail and doesn't send any information over the network. It is
+primarily aimed at developing email handlers: it will still work properly
+if your inbound email configuration is incorrect or even disabled.
+
+= SendGrid =
+
+To use SendGrid, you need a SendGrid account with access to the "Parse API" for
+inbound email. Provided you have such an account, configure it like this:
+
+ - Configure an MX record according to SendGrid's instructions, i.e. add
+ ##phabricator.example.com MX 10 mx.sendgrid.net.## or similar.
+ - Go to the "Parse Incoming Emails" page on SendGrid
+ () and add the domain as the
+ "Hostname".
+ - Add the URL ##https://phabricator.example.com/mail/sendgrid/## as the "Url",
+ using your domain (and HTTP instead of HTTPS if you are not configured with
+ SSL).
+ - If you get an error that the hostname "can't be located or verified", it
+ means your MX record is either incorrectly configured or hasn't propagated
+ yet.
+ - Set ##metamta.maniphest.reply-handler-domain## and/or
+ ##metamta.differential.reply-handler-domain## to
+ "##phabricator.example.com##" (whatever you configured the MX record for),
+ depending on whether you want to support email replies for Maniphest,
+ Differential, or both.
+
+That's it! If everything is working properly you should be able to send email
+to ##anything@phabricator.example.com## and it should appear in the "Received"
+tab of MetaMTA within a few seconds.
+
= Installing Mailparse =
-You need to install the PECL mailparse extension. In theory, you can do that
-with:
+If you're going to run your own MTA, you need to install the PECL mailparse
+extension. In theory, you can do that with:
$ sudo pecl install mailparse
@@ -32,10 +133,13 @@ If you get a linker error like this:
mailparse.so. This is not the default if you have individual files in
##php.d/##.
-= Configuring Sendmail =
+= MTA: Configuring Sendmail =
+
+Before you can configure Sendmail, you need to install Mailparse. See the
+section "Installing Mailparse" above.
Sendmail is very difficult to configure. First, you need to configure it for
-your domain so that mail can be delievered correctly. In broad strokes, this
+your domain so that mail can be delivered correctly. In broad strokes, this
probably means something like this:
- add an MX record;
@@ -61,5 +165,5 @@ Finally, edit ##/etc/mail/virtusertable## and add an entry like this:
That will forward all mail to @yourdomain.com to the Phabricator processing
script. Run ##sudo /etc/mail/make## or similar and then restart sendmail with
-##sudo /etc/init.d/sendmail restart##
+##sudo /etc/init.d/sendmail restart##.
diff --git a/src/infrastructure/markup/remarkup/markuprule/youtube/__init__.php b/src/infrastructure/markup/remarkup/markuprule/youtube/__init__.php
index 29225031f1..80117182c0 100644
--- a/src/infrastructure/markup/remarkup/markuprule/youtube/__init__.php
+++ b/src/infrastructure/markup/remarkup/markuprule/youtube/__init__.php
@@ -8,6 +8,7 @@
phutil_require_module('phutil', 'markup/engine/remarkup/markuprule/base');
phutil_require_module('phutil', 'parser/uri');
+phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorRemarkupRuleYoutube.php');