diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3408,6 +3408,7 @@ 'PhabricatorMailRoutingRule' => 'applications/metamta/constants/PhabricatorMailRoutingRule.php', 'PhabricatorMailStamp' => 'applications/metamta/stamp/PhabricatorMailStamp.php', 'PhabricatorMailTarget' => 'applications/metamta/replyhandler/PhabricatorMailTarget.php', + 'PhabricatorMailUtil' => 'applications/metamta/util/PhabricatorMailUtil.php', 'PhabricatorMainMenuBarExtension' => 'view/page/menu/PhabricatorMainMenuBarExtension.php', 'PhabricatorMainMenuSearchView' => 'view/page/menu/PhabricatorMainMenuSearchView.php', 'PhabricatorMainMenuView' => 'view/page/menu/PhabricatorMainMenuView.php', @@ -9215,6 +9216,7 @@ 'PhabricatorMailRoutingRule' => 'Phobject', 'PhabricatorMailStamp' => 'Phobject', 'PhabricatorMailTarget' => 'Phobject', + 'PhabricatorMailUtil' => 'Phobject', 'PhabricatorMainMenuBarExtension' => 'Phobject', 'PhabricatorMainMenuSearchView' => 'AphrontView', 'PhabricatorMainMenuView' => 'AphrontView', diff --git a/src/applications/metamta/receiver/PhabricatorMailReceiver.php b/src/applications/metamta/receiver/PhabricatorMailReceiver.php --- a/src/applications/metamta/receiver/PhabricatorMailReceiver.php +++ b/src/applications/metamta/receiver/PhabricatorMailReceiver.php @@ -25,10 +25,10 @@ ->withApplicationPHIDs(array($app->getPHID())) ->execute(); - foreach ($mail->getToAddresses() as $to_address) { + foreach ($mail->newTargetAddresses() as $address) { foreach ($application_emails as $application_email) { - $create_address = $application_email->getAddress(); - if ($this->matchAddresses($create_address, $to_address)) { + $create_address = $application_email->newAddress(); + if (PhabricatorMailUtil::matchAddresses($create_address, $address)) { $this->setApplicationEmail($application_email); return true; } @@ -194,66 +194,6 @@ $reasons); } - /** - * 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 - * - * "Abraham" # 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 = self::getRawAddress($u); - $v = self::getRawAddress($v); - - $u = self::stripMailboxPrefix($u); - $v = self::stripMailboxPrefix($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; - } - - /** * Reduce an email address to its canonical form. For example, an address * like: diff --git a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php --- a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php +++ b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php @@ -150,7 +150,7 @@ private function matchObjectAddressInMail( PhabricatorMetaMTAReceivedMail $mail) { - foreach ($mail->getToAddresses() as $address) { + foreach ($mail->newTargetAddresses() as $address) { $parts = $this->matchObjectAddress($address); if ($parts) { return $parts; @@ -160,12 +160,11 @@ return null; } - private function matchObjectAddress($address) { - $regexp = $this->getAddressRegexp(); - - $address = self::stripMailboxPrefix($address); - $local = id(new PhutilEmailAddress($address))->getLocalPart(); + private function matchObjectAddress(PhutilEmailAddress $address) { + $address = PhabricatorMailUtil::normalizeAddress($address); + $local = $address->getLocalPart(); + $regexp = $this->getAddressRegexp(); $matches = null; if (!preg_match($regexp, $local, $matches)) { return false; diff --git a/src/applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php b/src/applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php --- a/src/applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php +++ b/src/applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php @@ -17,7 +17,9 @@ foreach ($same as $address) { $this->assertTrue( - PhabricatorMailReceiver::matchAddresses($base, $address), + PhabricatorMailUtil::matchAddresses( + new PhutilEmailAddress($base), + new PhutilEmailAddress($address)), pht('Address %s', $address)); } @@ -32,7 +34,9 @@ foreach ($diff as $address) { $this->assertFalse( - PhabricatorMailReceiver::matchAddresses($base, $address), + PhabricatorMailUtil::matchAddresses( + new PhutilEmailAddress($base), + new PhutilEmailAddress($address)), pht('Address: %s', $address)); } } diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAApplicationEmail.php b/src/applications/metamta/storage/PhabricatorMetaMTAApplicationEmail.php --- a/src/applications/metamta/storage/PhabricatorMetaMTAApplicationEmail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAApplicationEmail.php @@ -88,6 +88,10 @@ return $message; } + public function newAddress() { + return new PhutilEmailAddress($this->getAddress()); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php --- a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php @@ -82,6 +82,27 @@ return $this->getRawEmailAddresses(idx($this->headers, 'to')); } + public function newTargetAddresses() { + $raw_addresses = array(); + + foreach ($this->getToAddresses() as $raw_address) { + $raw_addresses[] = $raw_address; + } + + foreach ($this->getCCAddresses() as $raw_address) { + $raw_addresses[] = $raw_address; + } + + $raw_addresses = array_unique($raw_addresses); + + $addresses = array(); + foreach ($raw_addresses as $raw_address) { + $addresses[] = new PhutilEmailAddress($raw_address); + } + + return $addresses; + } + public function loadAllRecipientPHIDs() { $addresses = array_merge( $this->getToAddresses(), diff --git a/src/applications/metamta/util/PhabricatorMailUtil.php b/src/applications/metamta/util/PhabricatorMailUtil.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/util/PhabricatorMailUtil.php @@ -0,0 +1,65 @@ +getAddress(); + $raw_address = phutil_utf8_strtolower($raw_address); + $raw_address = trim($raw_address); + + // If a mailbox prefix is configured and present, strip it off. + $prefix_key = 'metamta.single-reply-handler-prefix'; + $prefix = PhabricatorEnv::getEnvConfig($prefix_key); + $len = strlen($prefix); + + if ($len) { + $prefix = $prefix.'+'; + $len = $len + 1; + + if (!strncasecmp($raw_address, $prefix, $len)) { + $raw_address = substr($raw_address, $len); + } + } + + return id(clone $address) + ->setAddress($raw_address); + } + + /** + * 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 + * + * "Abraham" # With configured prefix. + * + * @param PhutilEmailAddress Email address. + * @param PhutilEmailAddress Another email address. + * @return bool True if addresses are effectively the same address. + */ + public static function matchAddresses( + PhutilEmailAddress $u, + PhutilEmailAddress $v) { + + $u = self::normalizeAddress($u); + $v = self::normalizeAddress($v); + + return ($u->getAddress() === $v->getAddress()); + } + +}