1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 00:32:42 +01:00

Move some received mail responsibility to applications

Summary:
Ref T1205. Continuation of D5915.

Currently, `PhabricatorMetaMTAReceivedMail` has //all// the logic for routing mail. In particular:

  - New mail receivers in applications must edit it.
  - Mail receivers don't drop out when applications are uninstalled.

Applications have some logic in subclasses of `PhabricatorMailReplyHandler`, but this class is a bit of a mess. It is also heavily based on the assumption that mail receivers are objects (like revisions), but this is not true in at least two cases today (creating new tasks with `bugs@`, creating a new Conpherence thread) and likely other cases in the future (e.g., revision-by-mail).

Move this logic into a new `PhabricatorMailReceiver` classtree. This is similar to `PhabricatorMailReplyHandler` but a bit cleaner and more general. I plan to heavily reduce the responsibilities of `PhabricatorMailReplyHandler` or possibly eliminate it entirely.

For now, the new classtree doesn't do much of interest. The only behavioral change this diff causes is that Phabricator will now reject mail to an application when that application is uninstalled.

I also moved all the `ReplyHandler` classes into `mail/` directories in their respective applications.

Test Plan: Unit tests, used receive test to route mail to various objects.

Reviewers: btrahan

Reviewed By: btrahan

CC: Afaque_Hussain, edward, aran

Maniphest Tasks: T1205

Differential Revision: https://secure.phabricator.com/D5922
This commit is contained in:
epriestley 2013-05-14 10:57:41 -07:00
parent af220fddc1
commit 341079c3cf
23 changed files with 376 additions and 8 deletions

View file

@ -234,6 +234,7 @@ phutil_register_library_map(array(
'ConpherenceConfigOptions' => 'applications/conpherence/config/ConpherenceConfigOptions.php',
'ConpherenceConstants' => 'applications/conpherence/constants/ConpherenceConstants.php',
'ConpherenceController' => 'applications/conpherence/controller/ConpherenceController.php',
'ConpherenceCreateThreadMailReceiver' => 'applications/conpherence/mail/ConpherenceCreateThreadMailReceiver.php',
'ConpherenceDAO' => 'applications/conpherence/storage/ConpherenceDAO.php',
'ConpherenceEditor' => 'applications/conpherence/editor/ConpherenceEditor.php',
'ConpherenceFileWidgetView' => 'applications/conpherence/view/ConpherenceFileWidgetView.php',
@ -254,6 +255,7 @@ phutil_register_library_map(array(
'ConpherenceSettings' => 'applications/conpherence/constants/ConpherenceSettings.php',
'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php',
'ConpherenceThreadListView' => 'applications/conpherence/view/ConpherenceThreadListView.php',
'ConpherenceThreadMailReceiver' => 'applications/conpherence/mail/ConpherenceThreadMailReceiver.php',
'ConpherenceThreadQuery' => 'applications/conpherence/query/ConpherenceThreadQuery.php',
'ConpherenceTransaction' => 'applications/conpherence/storage/ConpherenceTransaction.php',
'ConpherenceTransactionComment' => 'applications/conpherence/storage/ConpherenceTransactionComment.php',
@ -365,7 +367,7 @@ phutil_register_library_map(array(
'DifferentialPrimaryPaneView' => 'applications/differential/view/DifferentialPrimaryPaneView.php',
'DifferentialReleephRequestFieldSpecification' => 'applications/releeph/differential/DifferentialReleephRequestFieldSpecification.php',
'DifferentialRemarkupRule' => 'applications/differential/remarkup/DifferentialRemarkupRule.php',
'DifferentialReplyHandler' => 'applications/differential/DifferentialReplyHandler.php',
'DifferentialReplyHandler' => 'applications/differential/mail/DifferentialReplyHandler.php',
'DifferentialResultsTableView' => 'applications/differential/view/DifferentialResultsTableView.php',
'DifferentialRevertPlanFieldSpecification' => 'applications/differential/field/specification/DifferentialRevertPlanFieldSpecification.php',
'DifferentialReviewRequestMail' => 'applications/differential/mail/DifferentialReviewRequestMail.php',
@ -386,6 +388,7 @@ phutil_register_library_map(array(
'DifferentialRevisionListController' => 'applications/differential/controller/DifferentialRevisionListController.php',
'DifferentialRevisionListData' => 'applications/differential/data/DifferentialRevisionListData.php',
'DifferentialRevisionListView' => 'applications/differential/view/DifferentialRevisionListView.php',
'DifferentialRevisionMailReceiver' => 'applications/differential/mail/DifferentialRevisionMailReceiver.php',
'DifferentialRevisionQuery' => 'applications/differential/query/DifferentialRevisionQuery.php',
'DifferentialRevisionStatsController' => 'applications/differential/controller/DifferentialRevisionStatsController.php',
'DifferentialRevisionStatsView' => 'applications/differential/view/DifferentialRevisionStatsView.php',
@ -610,6 +613,7 @@ phutil_register_library_map(array(
'ManiphestBatchEditController' => 'applications/maniphest/controller/ManiphestBatchEditController.php',
'ManiphestConstants' => 'applications/maniphest/constants/ManiphestConstants.php',
'ManiphestController' => 'applications/maniphest/controller/ManiphestController.php',
'ManiphestCreateMailReceiver' => 'applications/maniphest/mail/ManiphestCreateMailReceiver.php',
'ManiphestDAO' => 'applications/maniphest/storage/ManiphestDAO.php',
'ManiphestDefaultTaskExtensions' => 'applications/maniphest/extensions/ManiphestDefaultTaskExtensions.php',
'ManiphestEdgeEventListener' => 'applications/maniphest/event/ManiphestEdgeEventListener.php',
@ -619,7 +623,7 @@ phutil_register_library_map(array(
'ManiphestHovercardEventListener' => 'applications/maniphest/event/ManiphestHovercardEventListener.php',
'ManiphestPeopleMenuEventListener' => 'applications/maniphest/event/ManiphestPeopleMenuEventListener.php',
'ManiphestRemarkupRule' => 'applications/maniphest/remarkup/ManiphestRemarkupRule.php',
'ManiphestReplyHandler' => 'applications/maniphest/ManiphestReplyHandler.php',
'ManiphestReplyHandler' => 'applications/maniphest/mail/ManiphestReplyHandler.php',
'ManiphestReportController' => 'applications/maniphest/controller/ManiphestReportController.php',
'ManiphestSavedQuery' => 'applications/maniphest/storage/ManiphestSavedQuery.php',
'ManiphestSavedQueryDeleteController' => 'applications/maniphest/controller/ManiphestSavedQueryDeleteController.php',
@ -637,6 +641,7 @@ phutil_register_library_map(array(
'ManiphestTaskExtensions' => 'applications/maniphest/extensions/ManiphestTaskExtensions.php',
'ManiphestTaskListController' => 'applications/maniphest/controller/ManiphestTaskListController.php',
'ManiphestTaskListView' => 'applications/maniphest/view/ManiphestTaskListView.php',
'ManiphestTaskMailReceiver' => 'applications/maniphest/mail/ManiphestTaskMailReceiver.php',
'ManiphestTaskOwner' => 'applications/maniphest/constants/ManiphestTaskOwner.php',
'ManiphestTaskPriority' => 'applications/maniphest/constants/ManiphestTaskPriority.php',
'ManiphestTaskProject' => 'applications/maniphest/storage/ManiphestTaskProject.php',
@ -656,7 +661,7 @@ phutil_register_library_map(array(
'MetaMTANotificationType' => 'applications/metamta/constants/MetaMTANotificationType.php',
'MetaMTAReceivedMailStatus' => 'applications/metamta/constants/MetaMTAReceivedMailStatus.php',
'ObjectHandleLoader' => 'applications/phid/handle/ObjectHandleLoader.php',
'OwnersPackageReplyHandler' => 'applications/owners/OwnersPackageReplyHandler.php',
'OwnersPackageReplyHandler' => 'applications/owners/mail/OwnersPackageReplyHandler.php',
'PHUI' => 'view/phui/PHUI.php',
'PHUIBoxExample' => 'applications/uiexample/examples/PHUIBoxExample.php',
'PHUIBoxView' => 'view/phui/PHUIBoxView.php',
@ -768,9 +773,10 @@ phutil_register_library_map(array(
'PhabricatorAuditInlineComment' => 'applications/audit/storage/PhabricatorAuditInlineComment.php',
'PhabricatorAuditListController' => 'applications/audit/controller/PhabricatorAuditListController.php',
'PhabricatorAuditListView' => 'applications/audit/view/PhabricatorAuditListView.php',
'PhabricatorAuditMailReceiver' => 'applications/audit/mail/PhabricatorAuditMailReceiver.php',
'PhabricatorAuditPreviewController' => 'applications/audit/controller/PhabricatorAuditPreviewController.php',
'PhabricatorAuditQuery' => 'applications/audit/query/PhabricatorAuditQuery.php',
'PhabricatorAuditReplyHandler' => 'applications/audit/PhabricatorAuditReplyHandler.php',
'PhabricatorAuditReplyHandler' => 'applications/audit/mail/PhabricatorAuditReplyHandler.php',
'PhabricatorAuditStatusConstants' => 'applications/audit/constants/PhabricatorAuditStatusConstants.php',
'PhabricatorAuthController' => 'applications/auth/controller/PhabricatorAuthController.php',
'PhabricatorAuthenticationConfigOptions' => 'applications/config/option/PhabricatorAuthenticationConfigOptions.php',
@ -1059,6 +1065,7 @@ phutil_register_library_map(array(
'PhabricatorMacroEditController' => 'applications/macro/controller/PhabricatorMacroEditController.php',
'PhabricatorMacroEditor' => 'applications/macro/editor/PhabricatorMacroEditor.php',
'PhabricatorMacroListController' => 'applications/macro/controller/PhabricatorMacroListController.php',
'PhabricatorMacroMailReceiver' => 'applications/macro/mail/PhabricatorMacroMailReceiver.php',
'PhabricatorMacroMemeController' => 'applications/macro/controller/PhabricatorMacroMemeController.php',
'PhabricatorMacroMemeDialogController' => 'applications/macro/controller/PhabricatorMacroMemeDialogController.php',
'PhabricatorMacroQuery' => 'applications/macro/query/PhabricatorMacroQuery.php',
@ -1077,6 +1084,7 @@ phutil_register_library_map(array(
'PhabricatorMailImplementationTestAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationTestAdapter.php',
'PhabricatorMailManagementResendWorkflow' => 'applications/metamta/management/PhabricatorMailManagementResendWorkflow.php',
'PhabricatorMailManagementWorkflow' => 'applications/metamta/management/PhabricatorMailManagementWorkflow.php',
'PhabricatorMailReceiver' => 'applications/metamta/receiver/PhabricatorMailReceiver.php',
'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php',
'PhabricatorMailingListsController' => 'applications/mailinglists/controller/PhabricatorMailingListsController.php',
'PhabricatorMailingListsEditController' => 'applications/mailinglists/controller/PhabricatorMailingListsEditController.php',
@ -1175,6 +1183,7 @@ phutil_register_library_map(array(
'PhabricatorObjectItemListView' => 'view/layout/PhabricatorObjectItemListView.php',
'PhabricatorObjectItemView' => 'view/layout/PhabricatorObjectItemView.php',
'PhabricatorObjectListView' => 'view/control/PhabricatorObjectListView.php',
'PhabricatorObjectMailReceiver' => 'applications/metamta/receiver/PhabricatorObjectMailReceiver.php',
'PhabricatorObjectSelectorDialog' => 'view/control/PhabricatorObjectSelectorDialog.php',
'PhabricatorOffsetPagedQuery' => 'infrastructure/query/PhabricatorOffsetPagedQuery.php',
'PhabricatorOwnerPathQuery' => 'applications/owners/query/PhabricatorOwnerPathQuery.php',
@ -1574,6 +1583,7 @@ phutil_register_library_map(array(
'PholioMockEmbedView' => 'applications/pholio/view/PholioMockEmbedView.php',
'PholioMockImagesView' => 'applications/pholio/view/PholioMockImagesView.php',
'PholioMockListController' => 'applications/pholio/controller/PholioMockListController.php',
'PholioMockMailReceiver' => 'applications/pholio/mail/PholioMockMailReceiver.php',
'PholioMockQuery' => 'applications/pholio/query/PholioMockQuery.php',
'PholioMockViewController' => 'applications/pholio/controller/PholioMockViewController.php',
'PholioRemarkupRule' => 'applications/pholio/remarkup/PholioRemarkupRule.php',
@ -1684,12 +1694,13 @@ phutil_register_library_map(array(
'PonderQuestionAskController' => 'applications/ponder/controller/PonderQuestionAskController.php',
'PonderQuestionDetailView' => 'applications/ponder/view/PonderQuestionDetailView.php',
'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php',
'PonderQuestionMailReceiver' => 'applications/ponder/mail/PonderQuestionMailReceiver.php',
'PonderQuestionPreviewController' => 'applications/ponder/controller/PonderQuestionPreviewController.php',
'PonderQuestionQuery' => 'applications/ponder/query/PonderQuestionQuery.php',
'PonderQuestionSummaryView' => 'applications/ponder/view/PonderQuestionSummaryView.php',
'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php',
'PonderRemarkupRule' => 'applications/ponder/remarkup/PonderRemarkupRule.php',
'PonderReplyHandler' => 'applications/ponder/PonderReplyHandler.php',
'PonderReplyHandler' => 'applications/ponder/mail/PonderReplyHandler.php',
'PonderSearchIndexer' => 'applications/ponder/search/PonderSearchIndexer.php',
'PonderUserProfileView' => 'applications/ponder/view/PonderUserProfileView.php',
'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php',
@ -1752,6 +1763,7 @@ phutil_register_library_map(array(
'ReleephRequestHeaderView' => 'applications/releeph/view/request/header/ReleephRequestHeaderView.php',
'ReleephRequestIntentsView' => 'applications/releeph/view/request/ReleephRequestIntentsView.php',
'ReleephRequestQuery' => 'applications/releeph/query/ReleephRequestQuery.php',
'ReleephRequestMailReceiver' => 'applications/releeph/mail/ReleephRequestMailReceiver.php',
'ReleephRequestReplyHandler' => 'applications/releeph/mail/ReleephRequestReplyHandler.php',
'ReleephRequestStatus' => 'applications/releeph/constants/ReleephRequestStatus.php',
'ReleephRequestStatusView' => 'applications/releeph/view/request/ReleephRequestStatusView.php',
@ -2015,6 +2027,7 @@ phutil_register_library_map(array(
'ConduitSSHWorkflow' => 'PhabricatorSSHWorkflow',
'ConpherenceConfigOptions' => 'PhabricatorApplicationConfigOptions',
'ConpherenceController' => 'PhabricatorController',
'ConpherenceCreateThreadMailReceiver' => 'PhabricatorMailReceiver',
'ConpherenceDAO' => 'PhabricatorLiskDAO',
'ConpherenceEditor' => 'PhabricatorApplicationTransactionEditor',
'ConpherenceFileWidgetView' => 'ConpherenceWidgetView',
@ -2039,6 +2052,7 @@ phutil_register_library_map(array(
1 => 'PhabricatorPolicyInterface',
),
'ConpherenceThreadListView' => 'AphrontView',
'ConpherenceThreadMailReceiver' => 'PhabricatorObjectMailReceiver',
'ConpherenceThreadQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'ConpherenceTransaction' => 'PhabricatorApplicationTransaction',
'ConpherenceTransactionComment' => 'PhabricatorApplicationTransactionComment',
@ -2166,6 +2180,7 @@ phutil_register_library_map(array(
'DifferentialRevisionIDFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialRevisionListController' => 'DifferentialController',
'DifferentialRevisionListView' => 'AphrontView',
'DifferentialRevisionMailReceiver' => 'PhabricatorObjectMailReceiver',
'DifferentialRevisionStatsController' => 'DifferentialController',
'DifferentialRevisionStatsView' => 'AphrontView',
'DifferentialRevisionStatusFieldSpecification' => 'DifferentialFieldSpecification',
@ -2345,6 +2360,7 @@ phutil_register_library_map(array(
'ManiphestAuxiliaryFieldValidationException' => 'Exception',
'ManiphestBatchEditController' => 'ManiphestController',
'ManiphestController' => 'PhabricatorController',
'ManiphestCreateMailReceiver' => 'PhabricatorMailReceiver',
'ManiphestDAO' => 'PhabricatorLiskDAO',
'ManiphestDefaultTaskExtensions' => 'ManiphestTaskExtensions',
'ManiphestEdgeEventListener' => 'PhutilEventListener',
@ -2377,6 +2393,7 @@ phutil_register_library_map(array(
'ManiphestTaskEditController' => 'ManiphestController',
'ManiphestTaskListController' => 'ManiphestController',
'ManiphestTaskListView' => 'ManiphestView',
'ManiphestTaskMailReceiver' => 'PhabricatorObjectMailReceiver',
'ManiphestTaskOwner' => 'ManiphestConstants',
'ManiphestTaskPriority' => 'ManiphestConstants',
'ManiphestTaskProject' => 'ManiphestDAO',
@ -2519,6 +2536,7 @@ phutil_register_library_map(array(
),
'PhabricatorAuditListController' => 'PhabricatorAuditController',
'PhabricatorAuditListView' => 'AphrontView',
'PhabricatorAuditMailReceiver' => 'PhabricatorObjectMailReceiver',
'PhabricatorAuditPreviewController' => 'PhabricatorAuditController',
'PhabricatorAuditReplyHandler' => 'PhabricatorMailReplyHandler',
'PhabricatorAuthController' => 'PhabricatorController',
@ -2799,6 +2817,7 @@ phutil_register_library_map(array(
'PhabricatorMacroEditController' => 'PhabricatorMacroController',
'PhabricatorMacroEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorMacroListController' => 'PhabricatorMacroController',
'PhabricatorMacroMailReceiver' => 'PhabricatorObjectMailReceiver',
'PhabricatorMacroMemeController' => 'PhabricatorMacroController',
'PhabricatorMacroMemeDialogController' => 'PhabricatorMacroController',
'PhabricatorMacroQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
@ -2898,6 +2917,7 @@ phutil_register_library_map(array(
'PhabricatorObjectItemListView' => 'AphrontView',
'PhabricatorObjectItemView' => 'AphrontTagView',
'PhabricatorObjectListView' => 'AphrontView',
'PhabricatorObjectMailReceiver' => 'PhabricatorMailReceiver',
'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery',
'PhabricatorOwnersConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorOwnersController' => 'PhabricatorController',
@ -3315,6 +3335,7 @@ phutil_register_library_map(array(
'PholioMockEmbedView' => 'AphrontView',
'PholioMockImagesView' => 'AphrontView',
'PholioMockListController' => 'PholioController',
'PholioMockMailReceiver' => 'PhabricatorObjectMailReceiver',
'PholioMockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PholioMockViewController' => 'PholioController',
'PholioRemarkupRule' => 'PhabricatorRemarkupRuleObject',
@ -3457,6 +3478,7 @@ phutil_register_library_map(array(
'PonderQuestionAskController' => 'PonderController',
'PonderQuestionDetailView' => 'AphrontView',
'PonderQuestionEditor' => 'PhabricatorEditor',
'PonderQuestionMailReceiver' => 'PhabricatorObjectMailReceiver',
'PonderQuestionPreviewController' => 'PonderController',
'PonderQuestionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PonderQuestionSummaryView' => 'AphrontView',
@ -3528,6 +3550,7 @@ phutil_register_library_map(array(
'ReleephRequestHeaderView' => 'AphrontView',
'ReleephRequestIntentsView' => 'AphrontView',
'ReleephRequestQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'ReleephRequestMailReceiver' => 'PhabricatorObjectMailReceiver',
'ReleephRequestReplyHandler' => 'PhabricatorMailReplyHandler',
'ReleephRequestStatusView' => 'AphrontView',
'ReleephRequestTransaction' => 'PhabricatorApplicationTransaction',

View file

@ -0,0 +1,14 @@
<?php
final class PhabricatorAuditMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationAudit';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'C[1-9]\d*';
}
}

View file

@ -246,6 +246,11 @@ abstract class PhabricatorApplication {
break;
}
}
if (!$selected) {
throw new Exception("No application '{$class_name}'!");
}
return $selected;
}

View file

@ -0,0 +1,37 @@
<?php
final class ConpherenceCreateThreadMailReceiver
extends PhabricatorMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationConpherence';
return PhabricatorApplication::isClassInstalled($app_class);
}
public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail) {
$usernames = array();
foreach ($mail->getToAddresses() as $to_address) {
$address = self::stripMailboxPrefix($to_address);
$usernames[] = id(new PhutilEmailAddress($address))->getLocalPart();
}
$usernames = array_unique($usernames);
if (!$usernames) {
return false;
}
$users = id(new PhabricatorUser())->loadAllWhere(
'username in (%Ls)',
$usernames);
if (count($users) != count($usernames)) {
// At least some of the addresses are not users, so don't accept this as
// a new Conpherence thread.
return false;
}
return true;
}
}

View file

@ -0,0 +1,15 @@
<?php
final class ConpherenceThreadMailReceiver
extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationConpherence';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'E[1-9]\d*';
}
}

View file

@ -0,0 +1,15 @@
<?php
final class DifferentialRevisionMailReceiver
extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationDifferential';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'D[1-9]\d*';
}
}

View file

@ -0,0 +1,14 @@
<?php
final class PhabricatorMacroMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationManiphest';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'MCRO[1-9]\d*';
}
}

View file

@ -0,0 +1,23 @@
<?php
final class ManiphestCreateMailReceiver extends PhabricatorMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationManiphest';
return PhabricatorApplication::isClassInstalled($app_class);
}
public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail) {
$config_key = 'metamta.maniphest.public-create-email';
$create_address = PhabricatorEnv::getEnvConfig($config_key);
foreach ($mail->getToAddresses() as $to_address) {
if ($this->matchAddresses($create_address, $to_address)) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,14 @@
<?php
final class ManiphestTaskMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationManiphest';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'T[1-9]\d*';
}
}

View file

@ -3,7 +3,9 @@
final class MetaMTAReceivedMailStatus
extends MetaMTAConstants {
const STATUS_DUPLICATE = 'err:duplicate';
const STATUS_FROM_PHABRICATOR = 'err:self';
const STATUS_DUPLICATE = 'err:duplicate';
const STATUS_FROM_PHABRICATOR = 'err:self';
const STATUS_NO_RECEIVERS = 'err:no-receivers';
const STATUS_ABUNDANT_RECEIVERS = 'err:multiple-receivers';
}

View file

@ -11,7 +11,7 @@ final class PhabricatorMetaMTAReceiveController
if ($request->isFormPost()) {
$received = new PhabricatorMetaMTAReceivedMail();
$header_content = array(
'Message-ID' => Filesystem::readRandomBytes(12),
'Message-ID' => Filesystem::readRandomCharacters(12),
);
$from = $request->getStr('sender');
$to = $request->getStr('receiver');

View file

@ -0,0 +1,72 @@
<?php
abstract class PhabricatorMailReceiver {
abstract public function isEnabled();
abstract public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail);
/**
* Determine if two inbound email addresses are effectively identical. This
* method strips and normalizes addresses so that equivalent variations are
* correctly detected as identical. For example, these addresses are all
* considered to match one another:
*
* "Abraham Lincoln" <alincoln@example.com>
* alincoln@example.com
* <ALincoln@example.com>
* "Abraham" <phabricator+ALINCOLN@EXAMPLE.COM> # With configured prefix.
*
* @param string Email address.
* @param string Another email address.
* @return bool True if addresses match.
*/
public static function matchAddresses($u, $v) {
$u = id(new PhutilEmailAddress($u))->getAddress();
$v = id(new PhutilEmailAddress($v))->getAddress();
$u = self::stripMailboxPrefix($u);
$v = self::stripMailboxPrefix($v);
$u = trim(phutil_utf8_strtolower($u));
$v = trim(phutil_utf8_strtolower($v));
return ($u === $v);
}
/**
* Strip a global mailbox prefix from an address if it is present. Phabricator
* can be configured to prepend a prefix to all reply addresses, which can
* make forwarding rules easier to write. A prefix looks like:
*
* example@phabricator.example.com # No Prefix
* phabricator+example@phabricator.example.com # Prefix "phabricator"
*
* @param string Email address, possibly with a mailbox prefix.
* @return string Email address with any prefix stripped.
*/
public static function stripMailboxPrefix($address) {
$address = id(new PhutilEmailAddress($address))->getAddress();
$prefix_key = 'metamta.single-reply-handler-prefix';
$prefix = PhabricatorEnv::getEnvConfig($prefix_key);
$len = strlen($prefix);
if ($len) {
$prefix = $prefix.'+';
$len = $len + 1;
}
if ($len) {
if (!strncasecmp($address, $prefix, $len)) {
$address = substr($address, strlen($prefix));
}
}
return $address;
}
}

View file

@ -0,0 +1,37 @@
<?php
abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver {
abstract protected function getObjectPattern();
final public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail) {
$regexp = $this->getAddressRegexp();
foreach ($mail->getToAddresses() as $address) {
$address = self::stripMailboxPrefix($address);
$local = id(new PhutilEmailAddress($address))->getLocalPart();
if (preg_match($regexp, $local)) {
return true;
}
}
return false;
}
private function getAddressRegexp() {
$pattern = $this->getObjectPattern();
$regexp =
'(^'.
'(?P<pattern>'.$pattern.')'.
'\\+'.
'(?P<sender>\w+)'.
'\\+'.
'(?P<hash>[a-f0-9]{16})'.
'$)U';
return $regexp;
}
}

View file

@ -173,6 +173,9 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
try {
$this->dropMailFromPhabricator();
$this->dropMailAlreadyReceived();
$receiver = $this->loadReceiver();
} catch (PhabricatorMetaMTAReceivedMailProcessingException $ex) {
$this
->setStatus($ex->getStatusCode())
@ -511,4 +514,41 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
$message);
}
/**
* Load a concrete instance of the @{class:PhabricatorMailReceiver} which
* accepts this mail, if one exists.
*/
private function loadReceiver() {
$receivers = id(new PhutilSymbolLoader())
->setAncestorClass('PhabricatorMailReceiver')
->loadObjects();
$accept = array();
foreach ($receivers as $key => $receiver) {
if (!$receiver->isEnabled()) {
continue;
}
if ($receiver->canAcceptMail($this)) {
$accept[$key] = $receiver;
}
}
if (!$accept) {
throw new PhabricatorMetaMTAReceivedMailProcessingException(
MetaMTAReceivedMailStatus::STATUS_NO_RECEIVERS,
"No concrete, enabled subclasses of `PhabricatorMailReceiver` can ".
"accept this mail.");
}
if (count($accept) > 1) {
$names = implode(', ', array_keys($accept));
throw new PhabricatorMetaMTAReceivedMailProcessingException(
MetaMTAReceivedMailStatus::STATUS_ABUNDANT_RECEIVERS,
"More than one `PhabricatorMailReceiver` claims to accept this mail.");
}
return head($accept);
}
}

View file

@ -47,6 +47,21 @@ final class PhabricatorMetaMTAReceivedMailTestCase extends PhabricatorTestCase {
$mail_b->getStatus());
}
public function testDropUnreceivableMail() {
$mail = new PhabricatorMetaMTAReceivedMail();
$mail->setHeaders(
array(
'Message-ID' => 'test@example.com',
'To' => 'does+not+exist@example.com',
));
$mail->save();
$mail->processReceivedMail();
$this->assertEqual(
MetaMTAReceivedMailStatus::STATUS_NO_RECEIVERS,
$mail->getStatus());
}
}

View file

@ -0,0 +1,14 @@
<?php
final class PholioMockMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationPholio';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'M[1-9]\d*';
}
}

View file

@ -0,0 +1,14 @@
<?php
final class PonderQuestionMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationPonder';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'Q[1-9]\d*';
}
}

View file

@ -0,0 +1,14 @@
<?php
final class ReleephRequestMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorApplicationReleeph';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'RQ[1-9]\d*';
}
}