Correct a parameter order swap in "diffusion.historyquery" for Mercurial
[phabricator.git] / src / applications / metamta / controller / PhabricatorMetaMTAMailgunReceiveController.php
blob8de908e4ea09f30dcf71df3a9835ec88978fa11a
1 <?php
3 final class PhabricatorMetaMTAMailgunReceiveController
4 extends PhabricatorMetaMTAController {
6 public function shouldRequireLogin() {
7 return false;
10 private function verifyMessage() {
11 $request = $this->getRequest();
12 $timestamp = $request->getStr('timestamp');
13 $token = $request->getStr('token');
14 $sig = $request->getStr('signature');
16 // An install may configure multiple Mailgun mailers, and we might receive
17 // inbound mail from any of them. Test the signature to see if it matches
18 // any configured Mailgun mailer.
20 $mailers = PhabricatorMetaMTAMail::newMailers(
21 array(
22 'inbound' => true,
23 'types' => array(
24 PhabricatorMailMailgunAdapter::ADAPTERTYPE,
26 ));
27 foreach ($mailers as $mailer) {
28 $api_key = $mailer->getOption('api-key');
29 $hash = hash_hmac('sha256', $timestamp.$token, $api_key);
30 if (phutil_hashes_are_identical($sig, $hash)) {
31 return true;
35 return false;
38 public function handleRequest(AphrontRequest $request) {
40 // No CSRF for Mailgun.
41 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
43 if (!$this->verifyMessage()) {
44 throw new Exception(
45 pht('Mail signature is not valid. Check your Mailgun API key.'));
48 $raw_headers = $request->getStr('message-headers');
49 $raw_dict = array();
50 if (strlen($raw_headers)) {
51 $raw_headers = phutil_json_decode($raw_headers);
52 foreach ($raw_headers as $raw_header) {
53 list($name, $value) = $raw_header;
54 $raw_dict[$name] = $value;
58 $headers = array(
59 'to' => $request->getStr('recipient'),
60 'from' => $request->getStr('from'),
61 'subject' => $request->getStr('subject'),
62 ) + $raw_dict;
64 $received = new PhabricatorMetaMTAReceivedMail();
65 $received->setHeaders($headers);
66 $received->setBodies(array(
67 'text' => $request->getStr('stripped-text'),
68 'html' => $request->getStr('stripped-html'),
69 ));
71 $file_phids = array();
72 foreach ($_FILES as $file_raw) {
73 try {
74 $file = PhabricatorFile::newFromPHPUpload(
75 $file_raw,
76 array(
77 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
78 ));
79 $file_phids[] = $file->getPHID();
80 } catch (Exception $ex) {
81 phlog($ex);
84 $received->setAttachments($file_phids);
86 try {
87 $received->save();
88 $received->processReceivedMail();
89 } catch (Exception $ex) {
90 // We can get exceptions here in two cases.
92 // First, saving the message may throw if we have already received a
93 // message with the same Message ID. In this case, we're declining to
94 // process a duplicate message, so failing silently is correct.
96 // Second, processing the message may throw (for example, if it contains
97 // an invalid !command). This will generate an email as a side effect,
98 // so we don't need to explicitly handle the exception here.
100 // In these cases, we want to return HTTP 200. If we do not, MailGun will
101 // re-transmit the message later.
102 phlog($ex);
105 $response = new AphrontWebpageResponse();
106 $response->setContent(pht("Got it! Thanks, Mailgun!\n"));
107 return $response;