Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php
12256 views
1
<?php
2
3
abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver {
4
5
/**
6
* Return a regular expression fragment which matches the name of an
7
* object which can receive mail. For example, Differential uses:
8
*
9
* D[1-9]\d*
10
*
11
* ...to match `D123`, etc., identifying Differential Revisions.
12
*
13
* @return string Regular expression fragment.
14
*/
15
abstract protected function getObjectPattern();
16
17
18
/**
19
* Load the object receiving mail, based on an identifying pattern. Normally
20
* this pattern is some sort of object ID.
21
*
22
* @param string A string matched by @{method:getObjectPattern}
23
* fragment.
24
* @param PhabricatorUser The viewing user.
25
* @return void
26
*/
27
abstract protected function loadObject($pattern, PhabricatorUser $viewer);
28
29
30
final protected function processReceivedMail(
31
PhabricatorMetaMTAReceivedMail $mail,
32
PhutilEmailAddress $target) {
33
34
$parts = $this->matchObjectAddress($target);
35
if (!$parts) {
36
// We should only make it here if we matched already in "canAcceptMail()",
37
// so this is a surprise.
38
throw new Exception(
39
pht(
40
'Failed to parse object address ("%s") during processing.',
41
(string)$target));
42
}
43
44
$pattern = $parts['pattern'];
45
$sender = $this->getSender();
46
47
try {
48
$object = $this->loadObject($pattern, $sender);
49
} catch (PhabricatorPolicyException $policy_exception) {
50
throw new PhabricatorMetaMTAReceivedMailProcessingException(
51
MetaMTAReceivedMailStatus::STATUS_POLICY_PROBLEM,
52
pht(
53
'This mail is addressed to an object ("%s") you do not have '.
54
'permission to see: %s',
55
$pattern,
56
$policy_exception->getMessage()));
57
}
58
59
if (!$object) {
60
throw new PhabricatorMetaMTAReceivedMailProcessingException(
61
MetaMTAReceivedMailStatus::STATUS_NO_SUCH_OBJECT,
62
pht(
63
'This mail is addressed to an object ("%s"), but that object '.
64
'does not exist.',
65
$pattern));
66
}
67
68
$sender_identifier = $parts['sender'];
69
if ($sender_identifier === 'public') {
70
if (!PhabricatorEnv::getEnvConfig('metamta.public-replies')) {
71
throw new PhabricatorMetaMTAReceivedMailProcessingException(
72
MetaMTAReceivedMailStatus::STATUS_NO_PUBLIC_MAIL,
73
pht(
74
'This mail is addressed to the public email address of an object '.
75
'("%s"), but public replies are not enabled on this server. An '.
76
'administrator may have recently disabled this setting, or you '.
77
'may have replied to an old message. Try replying to a more '.
78
'recent message instead.',
79
$pattern));
80
}
81
$check_phid = $object->getPHID();
82
} else {
83
if ($sender_identifier != $sender->getID()) {
84
throw new PhabricatorMetaMTAReceivedMailProcessingException(
85
MetaMTAReceivedMailStatus::STATUS_USER_MISMATCH,
86
pht(
87
'This mail is addressed to the private email address of an object '.
88
'("%s"), but you are not the user who is authorized to use the '.
89
'address you sent mail to. Each private address is unique to the '.
90
'user who received the original mail. Try replying to a message '.
91
'which was sent directly to you instead.',
92
$pattern));
93
}
94
$check_phid = $sender->getPHID();
95
}
96
97
$mail_key = PhabricatorMetaMTAMailProperties::loadMailKey($object);
98
$expect_hash = self::computeMailHash($mail_key, $check_phid);
99
100
if (!phutil_hashes_are_identical($expect_hash, $parts['hash'])) {
101
throw new PhabricatorMetaMTAReceivedMailProcessingException(
102
MetaMTAReceivedMailStatus::STATUS_HASH_MISMATCH,
103
pht(
104
'This mail is addressed to an object ("%s"), but the address is '.
105
'not correct (the security hash is wrong). Check that the address '.
106
'is correct.',
107
$pattern));
108
}
109
110
$mail->setRelatedPHID($object->getPHID());
111
$this->processReceivedObjectMail($mail, $object, $sender);
112
113
return $this;
114
}
115
116
protected function processReceivedObjectMail(
117
PhabricatorMetaMTAReceivedMail $mail,
118
PhabricatorLiskDAO $object,
119
PhabricatorUser $sender) {
120
121
$handler = $this->getTransactionReplyHandler();
122
if ($handler) {
123
return $handler
124
->setMailReceiver($object)
125
->setActor($sender)
126
->setExcludeMailRecipientPHIDs($mail->loadAllRecipientPHIDs())
127
->processEmail($mail);
128
}
129
130
throw new PhutilMethodNotImplementedException();
131
}
132
133
protected function getTransactionReplyHandler() {
134
return null;
135
}
136
137
public function loadMailReceiverObject($pattern, PhabricatorUser $viewer) {
138
return $this->loadObject($pattern, $viewer);
139
}
140
141
final public function canAcceptMail(
142
PhabricatorMetaMTAReceivedMail $mail,
143
PhutilEmailAddress $target) {
144
145
// If we don't have a valid sender user account, we can never accept
146
// mail to any object.
147
$sender = $this->getSender();
148
if (!$sender) {
149
return false;
150
}
151
152
return (bool)$this->matchObjectAddress($target);
153
}
154
155
private function matchObjectAddress(PhutilEmailAddress $address) {
156
$address = PhabricatorMailUtil::normalizeAddress($address);
157
$local = $address->getLocalPart();
158
159
$regexp = $this->getAddressRegexp();
160
$matches = null;
161
if (!preg_match($regexp, $local, $matches)) {
162
return false;
163
}
164
165
return $matches;
166
}
167
168
private function getAddressRegexp() {
169
$pattern = $this->getObjectPattern();
170
171
$regexp =
172
'(^'.
173
'(?P<pattern>'.$pattern.')'.
174
'\\+'.
175
'(?P<sender>\w+)'.
176
'\\+'.
177
'(?P<hash>[a-f0-9]{16})'.
178
'$)Ui';
179
180
return $regexp;
181
}
182
183
public static function computeMailHash($mail_key, $phid) {
184
$hash = PhabricatorHash::digestWithNamedKey(
185
$mail_key.$phid,
186
'mail.object-address-key');
187
return substr($hash, 0, 16);
188
}
189
190
}
191
192