@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorMailUtil
4 extends Phobject {
5
6 /**
7 * Normalize an email address for comparison or lookup.
8 *
9 * Phabricator can be configured to prepend a prefix to all reply addresses,
10 * which can make forwarding rules easier to write. This method strips the
11 * prefix if it is present, and normalizes casing and whitespace.
12 *
13 * @param PhutilEmailAddress $address Email address.
14 * @return PhutilEmailAddress Normalized address.
15 */
16 public static function normalizeAddress(PhutilEmailAddress $address) {
17 $raw_address = $address->getAddress();
18 $raw_address = phutil_utf8_strtolower($raw_address);
19 $raw_address = trim($raw_address);
20
21 // If a mailbox prefix is configured and present, strip it off.
22 $prefix_key = 'metamta.single-reply-handler-prefix';
23 $prefix = PhabricatorEnv::getEnvConfig($prefix_key);
24
25 if (phutil_nonempty_string($prefix)) {
26 $prefix = $prefix.'+';
27 $len = strlen($prefix);
28
29 if (!strncasecmp($raw_address, $prefix, $len)) {
30 $raw_address = substr($raw_address, $len);
31 }
32 }
33
34 return id(clone $address)
35 ->setAddress($raw_address);
36 }
37
38 /**
39 * Determine if two inbound email addresses are effectively identical.
40 *
41 * This method strips and normalizes addresses so that equivalent variations
42 * are correctly detected as identical. For example, these addresses are all
43 * considered to match one another:
44 *
45 * "Abraham Lincoln" <alincoln@example.com>
46 * alincoln@example.com
47 * <ALincoln@example.com>
48 * "Abraham" <phabricator+ALINCOLN@EXAMPLE.COM> # With configured prefix.
49 *
50 * @param PhutilEmailAddress $u Email address.
51 * @param PhutilEmailAddress $v Another email address.
52 * @return bool True if addresses are effectively the same address.
53 */
54 public static function matchAddresses(
55 PhutilEmailAddress $u,
56 PhutilEmailAddress $v) {
57
58 $u = self::normalizeAddress($u);
59 $v = self::normalizeAddress($v);
60
61 return ($u->getAddress() === $v->getAddress());
62 }
63
64 public static function isReservedAddress(PhutilEmailAddress $address) {
65 $address = self::normalizeAddress($address);
66 $local = $address->getLocalPart();
67
68 $reserved = array(
69 'admin',
70 'administrator',
71 'hostmaster',
72 'list',
73 'list-request',
74 'majordomo',
75 'postmaster',
76 'root',
77 'ssl-admin',
78 'ssladmin',
79 'ssladministrator',
80 'sslwebmaster',
81 'sysadmin',
82 'uucp',
83 'webmaster',
84
85 'noreply',
86 'no-reply',
87 );
88
89 $reserved = array_fuse($reserved);
90
91 if (isset($reserved[$local])) {
92 return true;
93 }
94
95 $default_address = id(new PhabricatorMailEmailEngine())
96 ->newDefaultEmailAddress();
97 if (self::matchAddresses($address, $default_address)) {
98 return true;
99 }
100
101 $void_address = id(new PhabricatorMailEmailEngine())
102 ->newVoidEmailAddress();
103 if (self::matchAddresses($address, $void_address)) {
104 return true;
105 }
106
107 return false;
108 }
109
110 public static function isUserAddress(PhutilEmailAddress $address) {
111 $user_email = id(new PhabricatorUserEmail())->loadOneWhere(
112 'address = %s',
113 $address->getAddress());
114
115 return (bool)$user_email;
116 }
117
118}