1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-28 17:52:43 +01:00
phorge-phorge/src/applications/maniphest/mail/ManiphestReplyHandler.php

154 lines
4.8 KiB
PHP
Raw Normal View History

<?php
final class ManiphestReplyHandler extends PhabricatorMailReplyHandler {
public function validateMailReceiver($mail_receiver) {
if (!($mail_receiver instanceof ManiphestTask)) {
throw new Exception('Mail receiver is not a ManiphestTask!');
}
}
public function getPrivateReplyHandlerEmailAddress(
PhabricatorObjectHandle $handle) {
return $this->getDefaultPrivateReplyHandlerEmailAddress($handle, 'T');
}
Allow Phabricator to be configured to use a public Reply-To address Summary: We already support this (and Facebook uses it) but it is difficult to configure and you have to write a bunch of code. Instead, provide a simple flag. See the documentation changes for details, but when this flag is enabled we send one email with a reply-to like "D2+public+23hf91fh19fh@phabricator.example.com". Anyone can reply to this, and we figure out who they are based on their "From" address instead of a unique hash. This is less secure, but a reasonable tradeoff in many cases. This also has the advantage over a naive implementation of at least doing object hash validation. @jungejason: I don't think this affects Facebook's implementation but this is an area where we've had problems in the past, so watch out for it when you deploy. Also note that you must set "metamta.public-replies" to true since Maniphest now looks for that key specifically before going into public reply mode; it no longer just tests for a public reply address being generateable (since it can always generate one now). Test Plan: Swapped my local install in and out of public reply mode and commented on objects. Got expected email behavior. Replied to public and private email addresses. Attacked public addresses by using them when the install was configured to disallow them and by altering the hash and the from address. All this stuff was rejected. Reviewed By: jungejason Reviewers: moskov, jungejason, tuomaspelkonen, aran CC: aran, epriestley, moskov, jungejason Differential Revision: 563
2011-06-30 22:01:35 +02:00
public function getPublicReplyHandlerEmailAddress() {
return $this->getDefaultPublicReplyHandlerEmailAddress('T');
}
protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) {
// NOTE: We'll drop in here on both the "reply to a task" and "create a
// new task" workflows! Make sure you test both if you make changes!
$task = $this->getMailReceiver();
$viewer = $this->getActor();
$is_new_task = !$task->getID();
$body_data = $mail->parseBody();
$body = $body_data['body'];
$body = $this->enhanceBodyWithAttachments($body, $mail->getAttachments());
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_EMAIL,
array(
'id' => $mail->getID(),
));
$template = new ManiphestTransaction();
$xactions = array();
if ($is_new_task) {
$xactions[] = id(clone $template)
->setTransactionType(ManiphestTransaction::TYPE_TITLE)
->setNewValue(nonempty($mail->getSubject(), pht('Untitled Task')));
$xactions[] = id(clone $template)
->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION)
->setNewValue($body);
} else {
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
id(new ManiphestTransactionComment())
->setContent($body));
}
$commands = $body_data['commands'];
foreach ($commands as $command_argv) {
$command = head($command_argv);
$args = array_slice($command_argv, 1);
switch ($command) {
case 'close':
$xactions[] = id(clone $template)
->setTransactionType(ManiphestTransaction::TYPE_STATUS)
->setNewValue(ManiphestTaskStatus::getDefaultClosedStatus());
break;
case 'claim':
$xactions[] = id(clone $template)
->setTransactionType(ManiphestTransaction::TYPE_OWNER)
->setNewValue($viewer->getPHID());
break;
case 'assign':
$assign_to = head($args);
if ($assign_to) {
$assign_user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withUsernames(array($assign_to))
->executeOne();
if ($assign_user) {
$assign_phid = $assign_user->getPHID();
}
}
// Treat bad "!assign" like "!claim".
if (!$assign_phid) {
$assign_phid = $viewer->getPHID();
}
$xactions[] = id(clone $template)
->setTransactionType(ManiphestTransaction::TYPE_OWNER)
->setNewValue($assign_phid);
break;
case 'unsubscribe':
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
->setNewValue(
array(
'-' => array($viewer->getPHID()),
));
break;
}
}
$ccs = $mail->loadCCPHIDs();
if ($ccs) {
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
->setNewValue(
array(
'+' => array($viewer->getPHID()),
));
}
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK,
array(
'task' => $task,
'mail' => $mail,
'new' => $is_new_task,
'transactions' => $xactions,
));
$event->setUser($viewer);
PhutilEventEngine::dispatchEvent($event);
$task = $event->getValue('task');
$xactions = $event->getValue('transactions');
$editor = id(new ManiphestTransactionEditor())
->setActor($viewer)
->setParentMessageID($mail->getMessageID())
->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs())
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSource($content_source);
if ($this->getApplicationEmail()) {
$editor->setApplicationEmail($this->getApplicationEmail());
}
$editor->applyTransactions($task, $xactions);
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK,
array(
'task' => $task,
'new' => $is_new_task,
'transactions' => $xactions,
));
$event->setUser($viewer);
PhutilEventEngine::dispatchEvent($event);
}
}