Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/differential/mail/base/DifferentialMail.php b/src/applications/differential/mail/base/DifferentialMail.php
index 6e506d6486..67b1b963a7 100644
--- a/src/applications/differential/mail/base/DifferentialMail.php
+++ b/src/applications/differential/mail/base/DifferentialMail.php
@@ -1,379 +1,371 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
abstract class DifferentialMail {
protected $to = array();
protected $cc = array();
protected $actorHandle;
protected $revision;
protected $comment;
protected $changesets;
protected $inlineComments;
protected $isFirstMailAboutRevision;
protected $isFirstMailToRecipients;
protected $heraldTranscriptURI;
protected $heraldRulesHeader;
protected $replyHandler;
protected $parentMessageID;
abstract protected function renderSubject();
abstract protected function renderBody();
public function setActorHandle($actor_handle) {
$this->actorHandle = $actor_handle;
return $this;
}
public function getActorHandle() {
return $this->actorHandle;
}
protected function getActorName() {
$handle = $this->getActorHandle();
if ($handle) {
return $handle->getName();
}
return '???';
}
public function setParentMessageID($parent_message_id) {
$this->parentMessageID = $parent_message_id;
return $this;
}
public function setXHeraldRulesHeader($header) {
$this->heraldRulesHeader = $header;
return $this;
}
public function send() {
$to_phids = $this->getToPHIDs();
if (!$to_phids) {
throw new Exception('No "To:" users provided!');
}
$cc_phids = $this->getCCPHIDs();
$subject = $this->buildSubject();
$body = $this->buildBody();
$attachments = $this->buildAttachments();
$template = new PhabricatorMetaMTAMail();
$actor_handle = $this->getActorHandle();
$reply_handler = $this->getReplyHandler();
if ($actor_handle) {
$template->setFrom($actor_handle->getPHID());
}
$template
->setSubject($subject)
->setBody($body)
->setIsHTML($this->shouldMarkMailAsHTML())
->setParentMessageID($this->parentMessageID)
->addHeader('Thread-Topic', $this->getRevision()->getTitle());
$template->setAttachments($attachments);
$template->setThreadID(
$this->getThreadID(),
$this->isFirstMailAboutRevision());
if ($this->heraldRulesHeader) {
$template->addHeader('X-Herald-Rules', $this->heraldRulesHeader);
}
$revision = $this->revision;
if ($revision) {
if ($revision->getAuthorPHID()) {
$template->addHeader(
'X-Differential-Author',
'<'.$revision->getAuthorPHID().'>');
}
if ($revision->getReviewers()) {
$template->addHeader(
'X-Differential-Reviewers',
'<'.implode('>, <', $revision->getReviewers()).'>');
}
if ($revision->getCCPHIDs()) {
$template->addHeader(
'X-Differential-CCs',
'<'.implode('>, <', $revision->getCCPHIDs()).'>');
}
}
$template->setIsBulk(true);
$template->setRelatedPHID($this->getRevision()->getPHID());
$mailtags = $this->getMailTags();
if ($mailtags) {
$template->setMailTags($mailtags);
}
$phids = array();
foreach ($to_phids as $phid) {
$phids[$phid] = true;
}
foreach ($cc_phids as $phid) {
$phids[$phid] = true;
}
$phids = array_keys($phids);
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL,
array(
'mail' => $template,
)
);
PhutilEventEngine::dispatchEvent($event);
$template = $event->getValue('mail');
$mails = $reply_handler->multiplexMail(
$template,
array_select_keys($handles, $to_phids),
array_select_keys($handles, $cc_phids));
foreach ($mails as $mail) {
$mail->saveAndSend();
}
}
protected function getMailTags() {
return array();
}
protected function getSubjectPrefix() {
return PhabricatorEnv::getEnvConfig('metamta.differential.subject-prefix');
}
protected function buildSubject() {
return trim($this->getSubjectPrefix().' '.$this->renderSubject());
}
protected function shouldMarkMailAsHTML() {
return false;
}
protected function buildBody() {
$body = $this->renderBody();
$reply_handler = $this->getReplyHandler();
$reply_instructions = $reply_handler->getReplyHandlerInstructions();
if ($reply_instructions) {
$body .=
"\nREPLY HANDLER ACTIONS\n".
" {$reply_instructions}\n";
}
if ($this->getHeraldTranscriptURI() && $this->isFirstMailToRecipients()) {
$manage_uri = PhabricatorEnv::getProductionURI(
'/herald/view/differential/');
$xscript_uri = $this->getHeraldTranscriptURI();
$body .= <<<EOTEXT
MANAGE HERALD DIFFERENTIAL RULES
{$manage_uri}
WHY DID I GET THIS EMAIL?
{$xscript_uri}
EOTEXT;
}
return $body;
}
/**
* You can override this method in a subclass and return array of attachments
* to be sent with the email. Each attachment is an instance of
* PhabricatorMetaMTAAttachment.
*/
protected function buildAttachments() {
return array();
}
public function getReplyHandler() {
- if ($this->replyHandler) {
- return $this->replyHandler;
+ if (!$this->replyHandler) {
+ $this->replyHandler =
+ self::newReplyHandlerForRevision($this->getRevision());
}
- $handler_class = PhabricatorEnv::getEnvConfig(
- 'metamta.differential.reply-handler');
-
- $reply_handler = self::newReplyHandlerForRevision($this->getRevision());
-
- $this->replyHandler = $reply_handler;
-
return $this->replyHandler;
}
public static function newReplyHandlerForRevision(
DifferentialRevision $revision) {
- $handler_class = PhabricatorEnv::getEnvConfig(
+ $reply_handler = PhabricatorEnv::newObjectFromConfig(
'metamta.differential.reply-handler');
-
- $reply_handler = newv($handler_class, array());
$reply_handler->setMailReceiver($revision);
return $reply_handler;
}
protected function formatText($text) {
$text = explode("\n", $text);
foreach ($text as &$line) {
$line = rtrim(' '.$line);
}
unset($line);
return implode("\n", $text);
}
public function setToPHIDs(array $to) {
$this->to = $this->filterContactPHIDs($to);
return $this;
}
public function setCCPHIDs(array $cc) {
$this->cc = $this->filterContactPHIDs($cc);
return $this;
}
protected function filterContactPHIDs(array $phids) {
return $phids;
// TODO: actually do this?
// Differential revisions use Subscriptions for CCs, so any arbitrary
// PHID can end up CC'd to them. Only try to actually send email PHIDs
// which have ToolsHandle types that are marked emailable. If we don't
// filter here, sending the email will fail.
/*
$handles = array();
prep(new ToolsHandleData($phids, $handles));
foreach ($handles as $phid => $handle) {
if (!$handle->isEmailable()) {
unset($handles[$phid]);
}
}
return array_keys($handles);
*/
}
protected function getToPHIDs() {
return $this->to;
}
protected function getCCPHIDs() {
return $this->cc;
}
public function setRevision($revision) {
$this->revision = $revision;
return $this;
}
public function getRevision() {
return $this->revision;
}
protected function getThreadID() {
$phid = $this->getRevision()->getPHID();
$domain = PhabricatorEnv::getEnvConfig('metamta.domain');
return "<differential-rev-{$phid}-req@{$domain}>";
}
public function setComment($comment) {
$this->comment = $comment;
return $this;
}
public function getComment() {
return $this->comment;
}
public function setChangesets($changesets) {
$this->changesets = $changesets;
return $this;
}
public function getChangesets() {
return $this->changesets;
}
public function setInlineComments(array $inline_comments) {
$this->inlineComments = $inline_comments;
return $this;
}
public function getInlineComments() {
return $this->inlineComments;
}
public function renderRevisionDetailLink() {
$uri = $this->getRevisionURI();
return "REVISION DETAIL\n {$uri}";
}
public function getRevisionURI() {
return PhabricatorEnv::getProductionURI('/D'.$this->getRevision()->getID());
}
public function setIsFirstMailToRecipients($first) {
$this->isFirstMailToRecipients = $first;
return $this;
}
public function isFirstMailToRecipients() {
return $this->isFirstMailToRecipients;
}
public function setIsFirstMailAboutRevision($first) {
$this->isFirstMailAboutRevision = $first;
return $this;
}
public function isFirstMailAboutRevision() {
return $this->isFirstMailAboutRevision;
}
public function setHeraldTranscriptURI($herald_transcript_uri) {
$this->heraldTranscriptURI = $herald_transcript_uri;
return $this;
}
public function getHeraldTranscriptURI() {
return $this->heraldTranscriptURI;
}
protected function renderHandleList(array $handles, array $phids) {
$names = array();
foreach ($phids as $phid) {
$names[] = $handles[$phid]->getName();
}
return implode(', ', $names);
}
}
diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php
index eb8f22faa0..2e3b6151c8 100644
--- a/src/infrastructure/env/PhabricatorEnv.php
+++ b/src/infrastructure/env/PhabricatorEnv.php
@@ -1,153 +1,168 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @task uri URI Validation
*/
final class PhabricatorEnv {
private static $env;
+ private static $requiredClasses = array(
+ 'metamta.differential.reply-handler' => 'PhabricatorMailReplyHandler',
+ );
+
public static function setEnvConfig(array $config) {
self::$env = $config;
}
public static function getEnvConfig($key, $default = null) {
return idx(self::$env, $key, $default);
}
+ public static function newObjectFromConfig($key, $args = array()) {
+ $class = self::getEnvConfig($key);
+ $object = newv($class, $args);
+ $instanceof = idx(self::$requiredClasses, $key);
+ if (!($object instanceof $instanceof)) {
+ throw new Exception("Config setting '$key' must be an instance of ".
+ "'$instanceof', is '".get_class($object)."'.");
+ }
+ return $object;
+ }
+
public static function envConfigExists($key) {
return array_key_exists($key, self::$env);
}
public static function getURI($path) {
return rtrim(self::getEnvConfig('phabricator.base-uri'), '/').$path;
}
public static function getProductionURI($path) {
// If we're passed a URI which already has a domain, simply return it
// unmodified. In particular, files may have URIs which point to a CDN
// domain.
$uri = new PhutilURI($path);
if ($uri->getDomain()) {
return $path;
}
$production_domain = self::getEnvConfig('phabricator.production-uri');
if (!$production_domain) {
$production_domain = self::getEnvConfig('phabricator.base-uri');
}
return rtrim($production_domain, '/').$path;
}
public static function getCDNURI($path) {
$alt = self::getEnvConfig('security.alternate-file-domain');
if (!$alt) {
$alt = self::getEnvConfig('phabricator.base-uri');
}
$uri = new PhutilURI($alt);
$uri->setPath($path);
return (string)$uri;
}
public static function getAllConfigKeys() {
return self::$env;
}
public static function getDoclink($resource) {
return 'http://phabricator.com/docs/phabricator/'.$resource;
}
/* -( URI Validation )----------------------------------------------------- */
/**
* Detect if a URI satisfies either @{method:isValidLocalWebResource} or
* @{method:isValidRemoteWebResource}, i.e. is a page on this server or the
* URI of some other resource which has a valid protocol. This rejects
* garbage URIs and URIs with protocols which do not appear in the
* ##uri.allowed-protocols## configuration, notably 'javascript:' URIs.
*
* NOTE: This method is generally intended to reject URIs which it may be
* unsafe to put in an "href" link attribute.
*
* @param string URI to test.
* @return bool True if the URI identifies a web resource.
* @task uri
*/
public static function isValidWebResource($uri) {
return self::isValidLocalWebResource($uri) ||
self::isValidRemoteWebResource($uri);
}
/**
* Detect if a URI identifies some page on this server.
*
* NOTE: This method is generally intended to reject URIs which it may be
* unsafe to issue a "Location:" redirect to.
*
* @param string URI to test.
* @return bool True if the URI identifies a local page.
* @task uri
*/
public static function isValidLocalWebResource($uri) {
$uri = (string)$uri;
if (!strlen($uri)) {
return false;
}
if (preg_match('/\s/', $uri)) {
// PHP hasn't been vulnerable to header injection attacks for a bunch of
// years, but we can safely reject these anyway since they're never valid.
return false;
}
// Valid URIs must begin with '/', followed by the end of the string or some
// other non-'/' character. This rejects protocol-relative URIs like
// "//evil.com/evil_stuff/".
return (bool)preg_match('@^/([^/]|$)@', $uri);
}
/**
* Detect if a URI identifies some valid remote resource.
*
* @param string URI to test.
* @return bool True if a URI idenfies a remote resource with an allowed
* protocol.
* @task uri
*/
public static function isValidRemoteWebResource($uri) {
$uri = (string)$uri;
$proto = id(new PhutilURI($uri))->getProtocol();
if (!$proto) {
return false;
}
$allowed = self::getEnvConfig('uri.allowed-protocols');
if (empty($allowed[$proto])) {
return false;
}
return true;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 15, 1:41 AM (1 d, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
337491
Default Alt Text
(15 KB)

Event Timeline