1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-26 15:30:58 +01:00

Extract some email address utility code from the receiver stack

Summary:
Ref T7477. We have some address normalization code in the reciever stack that is really shared code. I want to introduce some new callsites elsewhere but don't want to put a lot of static calls to other random objects all over the place.

This technically "solves" T7477 (it changes "to" to "to + cc" for finding receivers) but doesn't yet implement proper behavior (with multiple receivers, for example).

Test Plan: Ran unit tests, which cover this pretty well. Additional changes will vet this more thoroughly.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T7477

Differential Revision: https://secure.phabricator.com/D19948
This commit is contained in:
epriestley 2019-01-03 06:46:09 -08:00
parent e2f0571104
commit 57b3619181
7 changed files with 106 additions and 71 deletions

View file

@ -3408,6 +3408,7 @@ phutil_register_library_map(array(
'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 @@ phutil_register_library_map(array(
'PhabricatorMailRoutingRule' => 'Phobject',
'PhabricatorMailStamp' => 'Phobject',
'PhabricatorMailTarget' => 'Phobject',
'PhabricatorMailUtil' => 'Phobject',
'PhabricatorMainMenuBarExtension' => 'Phobject',
'PhabricatorMainMenuSearchView' => 'AphrontView',
'PhabricatorMainMenuView' => 'AphrontView',

View file

@ -25,10 +25,10 @@ abstract class PhabricatorMailReceiver extends Phobject {
->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 @@ abstract class PhabricatorMailReceiver extends Phobject {
$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>
* 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 = 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:

View file

@ -150,7 +150,7 @@ abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver {
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 @@ abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver {
return null;
}
private function matchObjectAddress($address) {
private function matchObjectAddress(PhutilEmailAddress $address) {
$address = PhabricatorMailUtil::normalizeAddress($address);
$local = $address->getLocalPart();
$regexp = $this->getAddressRegexp();
$address = self::stripMailboxPrefix($address);
$local = id(new PhutilEmailAddress($address))->getLocalPart();
$matches = null;
if (!preg_match($regexp, $local, $matches)) {
return false;

View file

@ -17,7 +17,9 @@ final class PhabricatorMailReceiverTestCase extends PhabricatorTestCase {
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 @@ final class PhabricatorMailReceiverTestCase extends PhabricatorTestCase {
foreach ($diff as $address) {
$this->assertFalse(
PhabricatorMailReceiver::matchAddresses($base, $address),
PhabricatorMailUtil::matchAddresses(
new PhutilEmailAddress($base),
new PhutilEmailAddress($address)),
pht('Address: %s', $address));
}
}

View file

@ -88,6 +88,10 @@ final class PhabricatorMetaMTAApplicationEmail
return $message;
}
public function newAddress() {
return new PhutilEmailAddress($this->getAddress());
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -82,6 +82,27 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
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(),

View file

@ -0,0 +1,65 @@
<?php
final class PhabricatorMailUtil
extends Phobject {
/**
* Normalize an email address for comparison or lookup.
*
* Phabricator can be configured to prepend a prefix to all reply addresses,
* which can make forwarding rules easier to write. This method strips the
* prefix if it is present, and normalizes casing and whitespace.
*
* @param PhutilEmailAddress Email address.
* @return PhutilEmailAddress Normalized address.
*/
public static function normalizeAddress(PhutilEmailAddress $address) {
$raw_address = $address->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>
* alincoln@example.com
* <ALincoln@example.com>
* "Abraham" <phabricator+ALINCOLN@EXAMPLE.COM> # 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());
}
}