@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Flesh out web UI for mail a bit to prepare for Herald outbound rules

Summary:
Ref T9141. Ref T5791. Ref T7013. Major changes here is:

- Currently, we don't store the headers we actually sent, or the reasons we actually did or did not deliver a mail.
- Start storing these (as `headers.sent` and `actors.sent`).
- Show them in the web UI.
- Show them in `bin/mail show-outbound` (previously, we sort of re-computed them in a hacky way).
- Take them into account in `bin/mail volume`.

Then some minor changes:

- Show mail bodies.
- Show more mail information.
- Start renaming "MetaMTA" to "Mail", at least in the web UI.

Test Plan:
{F707501}
{F707502}
{F707503}
{F707504}
{F707505}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T5791, T7013, T9141

Differential Revision: https://secure.phabricator.com/D13878

+390 -120
+4 -8
src/applications/metamta/application/PhabricatorMetaMTAApplication.php
··· 3 3 final class PhabricatorMetaMTAApplication extends PhabricatorApplication { 4 4 5 5 public function getName() { 6 - return pht('MetaMTA'); 6 + return pht('Mail'); 7 7 } 8 8 9 9 public function getBaseURI() { ··· 15 15 } 16 16 17 17 public function getShortDescription() { 18 - return pht('Delivers Mail'); 18 + return pht('Send and Receive Mail'); 19 19 } 20 20 21 21 public function getFlavorText() { 22 - return pht('Yo dawg, we heard you like MTAs.'); 22 + return pht('Every program attempts to expand until it can read mail.'); 23 23 } 24 24 25 25 public function getApplicationGroup() { ··· 30 30 return false; 31 31 } 32 32 33 - public function isLaunchable() { 34 - return false; 35 - } 36 - 37 33 public function getTypeaheadURI() { 38 - return null; 34 + return '/mail/'; 39 35 } 40 36 41 37 public function getRoutes() {
+201 -35
src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php
··· 4 4 extends PhabricatorMetaMTAController { 5 5 6 6 public function handleRequest(AphrontRequest $request) { 7 - $viewer = $request->getUser(); 7 + $viewer = $this->getViewer(); 8 8 9 9 $mail = id(new PhabricatorMetaMTAMailQuery()) 10 10 ->setViewer($viewer) ··· 19 19 } else { 20 20 $title = $mail->getSubject(); 21 21 } 22 + 22 23 $header = id(new PHUIHeaderView()) 23 24 ->setHeader($title) 24 - ->setUser($this->getRequest()->getUser()) 25 + ->setUser($viewer) 25 26 ->setPolicyObject($mail); 26 27 28 + switch ($mail->getStatus()) { 29 + case PhabricatorMetaMTAMail::STATUS_QUEUE: 30 + $icon = 'fa-clock-o'; 31 + $color = 'blue'; 32 + $name = pht('Queued'); 33 + break; 34 + case PhabricatorMetaMTAMail::STATUS_SENT: 35 + $icon = 'fa-envelope'; 36 + $color = 'green'; 37 + $name = pht('Sent'); 38 + break; 39 + case PhabricatorMetaMTAMail::STATUS_FAIL: 40 + $icon = 'fa-envelope'; 41 + $color = 'red'; 42 + $name = pht('Delivery Failed'); 43 + break; 44 + case PhabricatorMetaMTAMail::STATUS_VOID: 45 + $icon = 'fa-envelope'; 46 + $color = 'black'; 47 + $name = pht('Voided'); 48 + break; 49 + default: 50 + $icon = 'fa-question-circle'; 51 + $color = 'yellow'; 52 + $name = pht('Unknown'); 53 + break; 54 + } 55 + 56 + $header->setStatus($icon, $color, $name); 57 + 27 58 $crumbs = $this->buildApplicationCrumbs() 28 - ->addTextCrumb( 29 - 'Mail '.$mail->getID()); 59 + ->addTextCrumb(pht('Mail %d', $mail->getID())); 60 + 30 61 $object_box = id(new PHUIObjectBoxView()) 31 62 ->setHeader($header) 32 - ->addPropertyList($this->buildPropertyView($mail)); 63 + ->addPropertyList($this->buildMessageProperties($mail), pht('Message')) 64 + ->addPropertyList($this->buildHeaderProperties($mail), pht('Headers')) 65 + ->addPropertyList($this->buildDeliveryProperties($mail), pht('Delivery')) 66 + ->addPropertyList($this->buildMetadataProperties($mail), pht('Metadata')); 33 67 34 68 return $this->buildApplicationPage( 35 69 array( ··· 42 76 )); 43 77 } 44 78 45 - private function buildPropertyView(PhabricatorMetaMTAMail $mail) { 79 + private function buildMessageProperties(PhabricatorMetaMTAMail $mail) { 46 80 $viewer = $this->getViewer(); 47 81 48 82 $properties = id(new PHUIPropertyListView()) 49 83 ->setUser($viewer) 50 84 ->setObject($mail); 51 85 52 - $properties->addProperty( 53 - pht('ID'), 54 - $mail->getID()); 55 - 56 - $properties->addProperty( 57 - pht('Status'), 58 - $mail->getStatus()); 59 - 60 - if ($mail->getMessage()) { 61 - $properties->addProperty( 62 - pht('Status Details'), 63 - $mail->getMessage()); 64 - } 65 - 66 - if ($mail->getRelatedPHID()) { 67 - $properties->addProperty( 68 - pht('Related Object'), 69 - $viewer->renderHandle($mail->getRelatedPHID())); 70 - } 71 - 72 - if ($mail->getActorPHID()) { 73 - $actor_str = $viewer->renderHandle($mail->getActorPHID()); 74 - } else { 75 - $actor_str = pht('Generated by Phabricator'); 76 - } 77 - $properties->addProperty( 78 - pht('Actor'), 79 - $actor_str); 80 - 81 86 if ($mail->getFrom()) { 82 87 $from_str = $viewer->renderHandle($mail->getFrom()); 83 88 } else { ··· 104 109 $properties->addProperty( 105 110 pht('Cc'), 106 111 $cc_list); 112 + 113 + $properties->addSectionHeader( 114 + pht('Message'), 115 + PHUIPropertyListView::ICON_SUMMARY); 116 + 117 + if ($mail->hasSensitiveContent()) { 118 + $body = phutil_tag( 119 + 'em', 120 + array(), 121 + pht( 122 + 'The content of this mail is sensitive and it can not be '. 123 + 'viewed from the web UI.')); 124 + } else { 125 + $body = phutil_tag( 126 + 'div', 127 + array( 128 + 'style' => 'white-space: pre-wrap', 129 + ), 130 + $mail->getBody()); 131 + } 132 + 133 + $properties->addTextContent($body); 134 + 135 + 136 + return $properties; 137 + } 138 + 139 + private function buildHeaderProperties(PhabricatorMetaMTAMail $mail) { 140 + $viewer = $this->getViewer(); 141 + 142 + $properties = id(new PHUIPropertyListView()) 143 + ->setUser($viewer) 144 + ->setStacked(true); 145 + 146 + $headers = $mail->getDeliveredHeaders(); 147 + if ($headers === null) { 148 + $headers = $mail->generateHeaders(); 149 + } 150 + 151 + // Sort headers by name. 152 + $headers = isort($headers, 0); 153 + 154 + foreach ($headers as $header) { 155 + list($key, $value) = $header; 156 + $properties->addProperty($key, $value); 157 + } 158 + 159 + return $properties; 160 + } 161 + 162 + private function buildDeliveryProperties(PhabricatorMetaMTAMail $mail) { 163 + $viewer = $this->getViewer(); 164 + 165 + $properties = id(new PHUIPropertyListView()) 166 + ->setUser($viewer); 167 + 168 + $actors = $mail->getDeliveredActors(); 169 + $reasons = null; 170 + if (!$actors) { 171 + // TODO: We can get rid of this special-cased message after these changes 172 + // have been live for a while, but provide a more tailored message for 173 + // now so things are a little less confusing for users. 174 + if ($mail->getStatus() == PhabricatorMetaMTAMail::STATUS_SENT) { 175 + $delivery = phutil_tag( 176 + 'em', 177 + array(), 178 + pht( 179 + 'This is an older message that predates recording delivery '. 180 + 'information, so none is available.')); 181 + } else { 182 + $delivery = phutil_tag( 183 + 'em', 184 + array(), 185 + pht( 186 + 'This message has not been delivered yet, so delivery information '. 187 + 'is not available.')); 188 + } 189 + } else { 190 + $actor = idx($actors, $viewer->getPHID()); 191 + if (!$actor) { 192 + $delivery = phutil_tag( 193 + 'em', 194 + array(), 195 + pht('This message was not delivered to you.')); 196 + } else { 197 + $deliverable = $actor['deliverable']; 198 + if ($deliverable) { 199 + $delivery = pht('Delivered'); 200 + } else { 201 + $delivery = pht('Voided'); 202 + } 203 + 204 + $reasons = id(new PHUIStatusListView()); 205 + 206 + $reason_codes = $actor['reasons']; 207 + if (!$reason_codes) { 208 + $reason_codes = array( 209 + PhabricatorMetaMTAActor::REASON_NONE, 210 + ); 211 + } 212 + 213 + $icon_yes = 'fa-check green'; 214 + $icon_no = 'fa-times red'; 215 + 216 + foreach ($reason_codes as $reason) { 217 + $target = phutil_tag( 218 + 'strong', 219 + array(), 220 + PhabricatorMetaMTAActor::getReasonName($reason)); 221 + 222 + if (PhabricatorMetaMTAActor::isDeliveryReason($reason)) { 223 + $icon = $icon_yes; 224 + } else { 225 + $icon = $icon_no; 226 + } 227 + 228 + $item = id(new PHUIStatusItemView()) 229 + ->setIcon($icon) 230 + ->setTarget($target) 231 + ->setNote(PhabricatorMetaMTAActor::getReasonDescription($reason)); 232 + 233 + $reasons->addItem($item); 234 + } 235 + } 236 + } 237 + 238 + $properties->addProperty(pht('Delivery'), $delivery); 239 + if ($reasons) { 240 + $properties->addProperty(pht('Reasons'), $reasons); 241 + } 242 + 243 + return $properties; 244 + } 245 + 246 + private function buildMetadataProperties(PhabricatorMetaMTAMail $mail) { 247 + $viewer = $this->getViewer(); 248 + 249 + $properties = id(new PHUIPropertyListView()) 250 + ->setUser($viewer); 251 + 252 + $details = $mail->getMessage(); 253 + if (!strlen($details)) { 254 + $details = phutil_tag('em', array(), pht('None')); 255 + } 256 + $properties->addProperty(pht('Status Details'), $details); 257 + 258 + $actor_phid = $mail->getActorPHID(); 259 + if ($actor_phid) { 260 + $actor_str = $viewer->renderHandle($actor_phid); 261 + } else { 262 + $actor_str = pht('Generated by Phabricator'); 263 + } 264 + $properties->addProperty(pht('Actor'), $actor_str); 265 + 266 + $related_phid = $mail->getRelatedPHID(); 267 + if ($related_phid) { 268 + $related = $viewer->renderHandle($mail->getRelatedPHID()); 269 + } else { 270 + $related = phutil_tag('em', array(), pht('None')); 271 + } 272 + $properties->addProperty(pht('Related Object'), $related); 107 273 108 274 return $properties; 109 275 }
+44 -29
src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php
··· 76 76 $info[] = pht('Related PHID: %s', $message->getRelatedPHID()); 77 77 $info[] = pht('Message: %s', $message->getMessage()); 78 78 79 + $ignore = array( 80 + 'body' => true, 81 + 'html-body' => true, 82 + 'headers' => true, 83 + 'attachments' => true, 84 + 'headers.sent' => true, 85 + 'authors.sent' => true, 86 + ); 87 + 79 88 $info[] = null; 80 89 $info[] = pht('PARAMETERS'); 81 90 $parameters = $message->getParameters(); 82 91 foreach ($parameters as $key => $value) { 83 - if ($key == 'body') { 84 - continue; 85 - } 86 - 87 - if ($key == 'html-body') { 88 - continue; 89 - } 90 - 91 - if ($key == 'headers') { 92 - continue; 93 - } 94 - 95 - if ($key == 'attachments') { 92 + if (isset($ignore[$key])) { 96 93 continue; 97 94 } 98 95 ··· 105 102 106 103 $info[] = null; 107 104 $info[] = pht('HEADERS'); 108 - foreach (idx($parameters, 'headers', array()) as $header) { 105 + 106 + $headers = $message->getDeliveredHeaders(); 107 + if (!$headers) { 108 + $headers = $message->generateHeaders(); 109 + } 110 + 111 + foreach ($headers as $header) { 109 112 list($name, $value) = $header; 110 113 $info[] = "{$name}: {$value}"; 111 114 } ··· 119 122 } 120 123 } 121 124 122 - $actors = $message->loadAllActors(); 123 - $actors = array_select_keys( 124 - $actors, 125 - array_merge($message->getToPHIDs(), $message->getCcPHIDs())); 126 - $info[] = null; 127 - $info[] = pht('RECIPIENTS'); 128 - foreach ($actors as $actor) { 129 - if ($actor->isDeliverable()) { 130 - $info[] = ' '.coalesce($actor->getName(), $actor->getPHID()); 131 - } else { 132 - $info[] = '! '.coalesce($actor->getName(), $actor->getPHID()); 133 - } 134 - foreach ($actor->getDeliverabilityReasons() as $reason) { 135 - $desc = PhabricatorMetaMTAActor::getReasonDescription($reason); 136 - $info[] = ' - '.$desc; 125 + $all_actors = $message->loadAllActors(); 126 + 127 + $actors = $message->getDeliveredActors(); 128 + if ($actors) { 129 + $info[] = null; 130 + $info[] = pht('RECIPIENTS'); 131 + foreach ($actors as $actor_phid => $actor_info) { 132 + $actor = idx($all_actors, $actor_phid); 133 + if ($actor) { 134 + $actor_name = coalesce($actor->getName(), $actor_phid); 135 + } else { 136 + $actor_name = $actor_phid; 137 + } 138 + 139 + $deliverable = $actor_info['deliverable']; 140 + if ($deliverable) { 141 + $info[] = ' '.$actor_name; 142 + } else { 143 + $info[] = '! '.$actor_name; 144 + } 145 + 146 + $reasons = $actor_info['reasons']; 147 + foreach ($reasons as $reason) { 148 + $name = PhabricatorMetaMTAActor::getReasonName($reason); 149 + $desc = PhabricatorMetaMTAActor::getReasonDescription($reason); 150 + $info[] = ' - '.$name.': '.$desc; 151 + } 137 152 } 138 153 } 139 154
+32 -3
src/applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php
··· 28 28 ->execute(); 29 29 30 30 $unfiltered = array(); 31 + $delivered = array(); 31 32 32 33 foreach ($mails as $mail) { 34 + // Count messages we attempted to deliver. This includes messages which 35 + // were voided by preferences or other rules. 33 36 $unfiltered_actors = mpull($mail->loadAllActors(), 'getPHID'); 34 37 foreach ($unfiltered_actors as $phid) { 35 38 if (empty($unfiltered[$phid])) { ··· 37 40 } 38 41 $unfiltered[$phid]++; 39 42 } 43 + 44 + // Now, count mail we actually delivered. 45 + $result = $mail->getDeliveredActors(); 46 + if ($result) { 47 + foreach ($result as $actor_phid => $actor_info) { 48 + if (!$actor_info['deliverable']) { 49 + continue; 50 + } 51 + if (empty($delivered[$actor_phid])) { 52 + $delivered[$actor_phid] = 0; 53 + } 54 + $delivered[$actor_phid]++; 55 + } 56 + } 40 57 } 41 58 59 + // Sort users by delivered mail, then unfiltered mail. 60 + arsort($delivered); 42 61 arsort($unfiltered); 62 + $delivered = $delivered + array_fill_keys(array_keys($unfiltered), 0); 43 63 44 64 $table = id(new PhutilConsoleTable()) 45 65 ->setBorders(true) ··· 52 72 'unfiltered', 53 73 array( 54 74 'title' => pht('Unfiltered'), 75 + )) 76 + ->addColumn( 77 + 'delivered', 78 + array( 79 + 'title' => pht('Delivered'), 55 80 )); 56 81 57 82 $handles = $viewer->loadHandles(array_keys($unfiltered)); 58 83 $names = mpull(iterator_to_array($handles), 'getName', 'getPHID'); 59 84 60 - foreach ($unfiltered as $phid => $count) { 85 + foreach ($delivered as $phid => $delivered_count) { 86 + $unfiltered_count = idx($unfiltered, $phid, 0); 61 87 $table->addRow( 62 88 array( 63 89 'user' => idx($names, $phid), 64 - 'unfiltered' => $count, 90 + 'unfiltered' => $unfiltered_count, 91 + 'delivered' => $delivered_count, 65 92 )); 66 93 } 67 94 ··· 70 97 echo "\n"; 71 98 echo pht('Mail sent in the last 30 days.')."\n"; 72 99 echo pht( 73 - '"Unfiltered" is raw volume before preferences were applied.')."\n"; 100 + '"Unfiltered" is raw volume before rules applied.')."\n"; 101 + echo pht( 102 + '"Delivered" shows email actually sent.')."\n"; 74 103 echo "\n"; 75 104 76 105 return 0;
+35
src/applications/metamta/query/PhabricatorMetaMTAActor.php
··· 5 5 const STATUS_DELIVERABLE = 'deliverable'; 6 6 const STATUS_UNDELIVERABLE = 'undeliverable'; 7 7 8 + const REASON_NONE = 'none'; 8 9 const REASON_UNLOADABLE = 'unloadable'; 9 10 const REASON_UNMAILABLE = 'unmailable'; 10 11 const REASON_NO_ADDRESS = 'noaddress'; ··· 71 72 return $this->reasons; 72 73 } 73 74 75 + public static function isDeliveryReason($reason) { 76 + switch ($reason) { 77 + case self::REASON_NONE: 78 + case self::REASON_FORCE: 79 + case self::REASON_FORCE_HERALD: 80 + return true; 81 + default: 82 + // All other reasons cause the message to not be delivered. 83 + return false; 84 + } 85 + } 86 + 87 + public static function getReasonName($reason) { 88 + $names = array( 89 + self::REASON_NONE => pht('None'), 90 + self::REASON_DISABLED => pht('Disabled Recipient'), 91 + self::REASON_BOT => pht('Bot Recipient'), 92 + self::REASON_NO_ADDRESS => pht('No Address'), 93 + self::REASON_EXTERNAL_TYPE => pht('External Recipient'), 94 + self::REASON_UNMAILABLE => pht('Not Mailable'), 95 + self::REASON_RESPONSE => pht('Similar Reply'), 96 + self::REASON_SELF => pht('Self Mail'), 97 + self::REASON_MAIL_DISABLED => pht('Mail Disabled'), 98 + self::REASON_MAILTAGS => pht('Mail Tags'), 99 + self::REASON_UNLOADABLE => pht('Bad Recipient'), 100 + self::REASON_FORCE => pht('Forced Mail'), 101 + self::REASON_FORCE_HERALD => pht('Forced by Herald'), 102 + ); 103 + 104 + return idx($names, $reason, pht('Unknown ("%s")', $reason)); 105 + } 106 + 74 107 public static function getReasonDescription($reason) { 75 108 $descriptions = array( 109 + self::REASON_NONE => pht( 110 + 'No special rules affected this mail.'), 76 111 self::REASON_DISABLED => pht( 77 112 'This user is disabled; disabled users do not receive mail.'), 78 113 self::REASON_BOT => pht(
+74 -45
src/applications/metamta/storage/PhabricatorMetaMTAMail.php
··· 436 436 } 437 437 438 438 try { 439 + $headers = $this->generateHeaders(); 440 + 439 441 $params = $this->parameters; 440 442 441 443 $actors = $this->loadAllActors(); ··· 535 537 $add_cc, 536 538 mpull($cc_actors, 'getEmailAddress')); 537 539 break; 538 - case 'headers': 539 - foreach ($value as $pair) { 540 - list($header_key, $header_value) = $pair; 541 - 542 - // NOTE: If we have \n in a header, SES rejects the email. 543 - $header_value = str_replace("\n", ' ', $header_value); 544 - 545 - $mailer->addHeader($header_key, $header_value); 546 - } 547 - break; 548 540 case 'attachments': 549 541 $value = $this->getAttachments(); 550 542 foreach ($value as $attachment) { ··· 593 585 594 586 $mailer->setSubject(implode(' ', array_filter($subject))); 595 587 break; 596 - case 'is-bulk': 597 - if ($value) { 598 - $mailer->addHeader('Precedence', 'bulk'); 599 - } 600 - break; 601 588 case 'thread-id': 602 589 603 590 // NOTE: Gmail freaks out about In-Reply-To and References which ··· 608 595 $value = '<'.$value.'@'.$domain.'>'; 609 596 610 597 if ($is_first && $mailer->supportsMessageIDHeader()) { 611 - $mailer->addHeader('Message-ID', $value); 598 + $headers[] = array('Message-ID', $value); 612 599 } else { 613 600 $in_reply_to = $value; 614 601 $references = array($value); ··· 620 607 $references[] = $parent_id; 621 608 } 622 609 $references = implode(' ', $references); 623 - $mailer->addHeader('In-Reply-To', $in_reply_to); 624 - $mailer->addHeader('References', $references); 610 + $headers[] = array('In-Reply-To', $in_reply_to); 611 + $headers[] = array('References', $references); 625 612 } 626 613 $thread_index = $this->generateThreadIndex($value, $is_first); 627 - $mailer->addHeader('Thread-Index', $thread_index); 614 + $headers[] = array('Thread-Index', $thread_index); 628 615 break; 629 - case 'mailtags': 630 - // Handled below. 616 + default: 617 + // Other parameters are handled elsewhere or are not relevant to 618 + // constructing the message. 631 619 break; 632 - case 'subject-prefix': 633 - case 'vary-subject-prefix': 634 - // Handled above. 635 - break; 636 - default: 637 - // Just discard. 638 620 } 639 621 } 640 622 ··· 660 642 $mailer->setHTMLBody($params['html-body']); 661 643 } 662 644 645 + // Pass the headers to the mailer, then save the state so we can show 646 + // them in the web UI. 647 + foreach ($headers as $header) { 648 + list($header_key, $header_value) = $header; 649 + $mailer->addHeader($header_key, $header_value); 650 + } 651 + $this->setParam('headers.sent', $headers); 652 + 653 + // Save the final deliverability outcomes and reasoning so we can 654 + // explain why things happened the way they did. 655 + $actor_list = array(); 656 + foreach ($actors as $actor) { 657 + $actor_list[$actor->getPHID()] = array( 658 + 'deliverable' => $actor->isDeliverable(), 659 + 'reasons' => $actor->getDeliverabilityReasons(), 660 + ); 661 + } 662 + $this->setParam('actors.sent', $actor_list); 663 + 663 664 if (!$add_to && !$add_cc) { 664 665 $this->setStatus(self::STATUS_VOID); 665 666 $this->setMessage( ··· 690 691 'in the configuration to change this setting.', 691 692 'phabricator.silent')); 692 693 return $this->save(); 693 - } 694 - 695 - $mailer->addHeader('X-Phabricator-Sent-This-Message', 'Yes'); 696 - $mailer->addHeader('X-Mail-Transport-Agent', 'MetaMTA'); 697 - 698 - // Some clients respect this to suppress OOF and other auto-responses. 699 - $mailer->addHeader('X-Auto-Response-Suppress', 'All'); 700 - 701 - // If the message has mailtags, filter out any recipients who don't want 702 - // to receive this type of mail. 703 - $mailtags = $this->getParam('mailtags'); 704 - if ($mailtags) { 705 - $tag_header = array(); 706 - foreach ($mailtags as $mailtag) { 707 - $tag_header[] = '<'.$mailtag.'>'; 708 - } 709 - $tag_header = implode(', ', $tag_header); 710 - $mailer->addHeader('X-Phabricator-Mail-Tags', $tag_header); 711 694 } 712 695 713 696 // Some mailers require a valid "To:" in order to deliver mail. If we ··· 1050 1033 $this->saveTransaction(); 1051 1034 1052 1035 return $ret; 1036 + } 1037 + 1038 + public function generateHeaders() { 1039 + $headers = array(); 1040 + 1041 + $headers[] = array('X-Phabricator-Sent-This-Message', 'Yes'); 1042 + $headers[] = array('X-Mail-Transport-Agent', 'MetaMTA'); 1043 + 1044 + // Some clients respect this to suppress OOF and other auto-responses. 1045 + $headers[] = array('X-Auto-Response-Suppress', 'All'); 1046 + 1047 + // If the message has mailtags, filter out any recipients who don't want 1048 + // to receive this type of mail. 1049 + $mailtags = $this->getParam('mailtags'); 1050 + if ($mailtags) { 1051 + $tag_header = array(); 1052 + foreach ($mailtags as $mailtag) { 1053 + $tag_header[] = '<'.$mailtag.'>'; 1054 + } 1055 + $tag_header = implode(', ', $tag_header); 1056 + $headers[] = array('X-Phabricator-Mail-Tags', $tag_header); 1057 + } 1058 + 1059 + $value = $this->getParam('headers', array()); 1060 + foreach ($value as $pair) { 1061 + list($header_key, $header_value) = $pair; 1062 + 1063 + // NOTE: If we have \n in a header, SES rejects the email. 1064 + $header_value = str_replace("\n", ' ', $header_value); 1065 + $headers[] = array($header_key, $header_value); 1066 + } 1067 + 1068 + $is_bulk = $this->getParam('is-bulk'); 1069 + if ($is_bulk) { 1070 + $headers[] = array('Precedence', 'bulk'); 1071 + } 1072 + 1073 + return $headers; 1074 + } 1075 + 1076 + public function getDeliveredHeaders() { 1077 + return $this->getParam('headers.sent'); 1078 + } 1079 + 1080 + public function getDeliveredActors() { 1081 + return $this->getParam('actors.sent'); 1053 1082 } 1054 1083 1055 1084