1 Handle broken headers like double encoded personal field of email in Mail_API::getAddressInfo()
2 Fix typo in Mail_API::getAddressInfo().
4 --- eventum-1.7.1/include/class.mail.php 2006-04-12 00:48:18.349874724 +0300
5 +++ /home/glen/class.mail.php 2006-04-12 00:52:33.195568053 +0300
7 $address = Mime_Helper::encodeValue($address);
8 include_once(APP_PEAR_PATH . "Mail/RFC822.php");
9 $t = Mail_RFC822::parseAddressList($address, null, null, false);
10 + if (PEAR::isError($t)) {
11 + Error_Handler::logError(array($t->getMessage(), $t->getDebugInfo()), __FILE__, __LINE__);
12 + // ugly hack: remove double quotes and retry.
13 + $address = str_replace('""', '"', $address);
14 + $t = Mail_RFC822::parseAddressList($address, null, null, false);
16 + if (PEAR::isError($t)) {
17 + Error_Handler::logError(array($t->getMessage(), $t->getDebugInfo()), __FILE__, __LINE__);
22 for ($i = 0; $i < count($t); $i++) {
24 'sender_name' => $t[$i]->personal,
25 - 'email' => $t[$i]->mailbox . '@' . $t[0]->host,
26 + 'email' => $t[$i]->mailbox . '@' . $t[$i]->host,
27 'username' => $t[$i]->mailbox,
28 'host' => $t[$i]->host
31 -------------------------------------------------------------------------------------------------------
32 Detect and log possibly corrupted MIME emails.
34 --- eventum-1.7.1/include/class.mime_helper.php 2006-04-12 00:48:18.579879862 +0300
35 +++ /home/glen/class.mime_helper.php 2006-04-12 00:52:33.225568723 +0300
39 include_once(APP_PEAR_PATH . "Mail/mimeDecode.php");
40 +include_once(APP_INC_PATH . "class.error_handler.php");
43 * Class to handle the business logic related to the MIME email
47 Mime_Helper::parse_output($output, $parts);
48 + if (empty($parts)) {
49 + Error_Handler::logError(array("Mime_Helper::parse_output failed. Corrupted MIME in email?", $output), __FILE__, __LINE__);
50 + // we continue as if nothing happened until it's clear it's right check to do.
54 if (isset($parts["text"])) {
56 -------------------------------------------------------------------------------------------------------
57 Rewrite routing part to have consistent API for matching issue_ids from mail headers.
58 Add new method Routing::getMatchingIssueIDs().
60 --- eventum-1.7.1/include/class.routing.php 2006-04-12 00:48:18.189871149 +0300
61 +++ /home/glen/class.routing.php 2006-04-12 00:52:33.295570287 +0300
63 if ($setup['email_routing']['status'] != 'enabled') {
64 return array(78, "Error: The email routing interface is disabled.\n");
66 - $prefix = $setup['email_routing']['address_prefix'];
67 - // escape plus signs so 'issue+1@example.com' becomes a valid routing address
68 - $prefix = str_replace('+', '\+', $prefix);
69 - $mail_domain = quotemeta($setup['email_routing']['address_host']);
70 - $mail_domain_alias = quotemeta(@$setup['email_routing']['host_alias']);
71 - if (!empty($mail_domain_alias)) {
72 - $mail_domain = "(?:" . $mail_domain . "|" . $mail_domain_alias . ")";
74 - if (empty($prefix)) {
75 + if (empty($setup['email_routing']['address_prefix'])) {
76 return array(78, "Error: Please configure the email address prefix.\n");
78 - if (empty($mail_domain)) {
79 + if (empty($setup['email_routing']['address_host'])) {
80 return array(78, "Error: Please configure the email address domain.\n");
83 $structure = Mime_Helper::decode($full_message, true, true);
85 // find which issue ID this email refers to
86 - @preg_match("/$prefix(\d*)@$mail_domain/i", $structure->headers['to'], $matches);
87 - @$issue_id = $matches[1];
88 + if (isset($structure->headers['to'])) {
89 + $issue_id = Routing::getMatchingIssueIDs($structure->headers['to'], 'email');
91 // validation is always a good idea
92 - if (empty($issue_id)) {
93 + if (empty($issue_id) and isset($structure->headers['cc'])) {
94 // we need to try the Cc header as well
95 - @preg_match("/$prefix(\d*)@$mail_domain/i", $structure->headers['cc'], $matches);
96 - if (!empty($matches[1])) {
97 - $issue_id = $matches[1];
99 - return array(65, "Error: The routed email had no associated Eventum issue ID or had an invalid recipient address.\n");
101 + $issue_id = Routing::getMatchingIssueIDs($structure->headers['cc'], 'email');
104 + if (empty($issue_id)) {
105 + return array(65, "Error: The routed email had no associated Eventum issue ID or had an invalid recipient address.\n");
108 if (empty($email_account_id)) {
109 $issue_prj_id = Issue::getProjectID($issue_id);
110 if (empty($issue_prj_id)) {
111 @@ -305,30 +299,26 @@
112 if (@$setup['note_routing']['status'] != 'enabled') {
113 return array(78, "Error: The internal note routing interface is disabled.\n");
115 - $prefix = $setup['note_routing']['address_prefix'];
116 - // escape plus signs so 'note+1@example.com' becomes a valid routing address
117 - $prefix = str_replace('+', '\+', $prefix);
118 - $mail_domain = quotemeta($setup['note_routing']['address_host']);
119 - if (empty($prefix)) {
120 + if (empty($setup['note_routing']['address_prefix'])) {
121 return array(78, "Error: Please configure the email address prefix.\n");
123 - if (empty($mail_domain)) {
124 + if (empty($setup['note_routing']['address_host'])) {
125 return array(78, "Error: Please configure the email address domain.\n");
127 $structure = Mime_Helper::decode($full_message, true, true);
129 // find which issue ID this email refers to
130 - @preg_match("/$prefix(\d*)@$mail_domain/i", $structure->headers['to'], $matches);
131 - @$issue_id = $matches[1];
132 + if (isset($structure->headers['to'])) {
133 + $issue_id = Routing::getMatchingIssueIDs($structure->headers['to'], 'note');
135 // validation is always a good idea
136 - if (empty($issue_id)) {
137 + if (empty($issue_id) and isset($structure->headers['cc'])) {
138 // we need to try the Cc header as well
139 - @preg_match("/$prefix(\d*)@$mail_domain/i", $structure->headers['cc'], $matches);
140 - if (!empty($matches[1])) {
141 - $issue_id = $matches[1];
143 - return array(65, "Error: The routed note had no associated Eventum issue ID or had an invalid recipient address.\n");
145 + $issue_id = Routing::getMatchingIssueIDs($structure->headers['cc'], 'note');
148 + if (empty($issue_id)) {
149 + return array(65, "Error: The routed note had no associated Eventum issue ID or had an invalid recipient address.\n");
152 $prj_id = Issue::getProjectID($issue_id);
155 Support::extractAttachments($issue_id, $full_message, true, $res);
157 + // FIXME! $res == -2 is not handled
158 History::add($issue_id, Auth::getUserID(), History::getTypeID('note_routed'), "Note routed from " . $structure->headers['from']);
161 @@ -433,30 +424,27 @@
162 if (@$setup['draft_routing']['status'] != 'enabled') {
163 return array(78, "Error: The email draft interface is disabled.\n");
165 - $prefix = $setup['draft_routing']['address_prefix'];
166 - // escape plus signs so 'draft+1@example.com' becomes a valid address
167 - $prefix = str_replace('+', '\+', $prefix);
168 - $mail_domain = quotemeta($setup['draft_routing']['address_host']);
169 - if (empty($prefix)) {
170 + if (empty($setup['draft_routing']['address_prefix'])) {
171 return array(78, "Error: Please configure the email address prefix.\n");
173 - if (empty($mail_domain)) {
174 + if (empty($setup['draft_routing']['address_host'])) {
175 return array(78, "Error: Please configure the email address domain.\n");
178 $structure = Mime_Helper::decode($full_message, true, false);
180 // find which issue ID this email refers to
181 - @preg_match("/$prefix(\d*)@$mail_domain/i", $structure->headers['to'], $matches);
182 - @$issue_id = $matches[1];
183 + if (isset($structure->headers['to'])) {
184 + $issue_id = Routing::getMatchingIssueIDs($structure->headers['to'], 'draft');
186 // validation is always a good idea
187 - if (empty($issue_id)) {
188 + if (empty($issue_id) and isset($structure->headers['cc'])) {
189 // we need to try the Cc header as well
190 - @preg_match("/$prefix(\d*)@$mail_domain/i", $structure->headers['cc'], $matches);
191 - if (!empty($matches[1])) {
192 - $issue_id = $matches[1];
194 - return array(65, "Error: The routed draft had no associated Eventum issue ID or had an invalid recipient address.\n");
196 + $issue_id = Routing::getMatchingIssueIDs($structure->headers['cc'], 'draft');
199 + if (empty($issue_id)) {
200 + return array(65, "Error: The routed email had no associated Eventum issue ID or had an invalid recipient address.\n");
203 $prj_id = Issue::getProjectID($issue_id);
205 History::add($issue_id, Auth::getUserID(), History::getTypeID('draft_routed'), "Draft routed from " . $structure->headers['from']);
210 + * Check for $adresses for matches
212 + * @param mixed $addresses to check
213 + * @param string Type of address match to find (email, note, draft)
214 + * @return mixed $issue_id in case of match otherwise false
216 + function getMatchingIssueIDs($addresses, $type)
218 + $setup = Setup::load();
219 + $settings = $setup["${type}_routing"];
220 + if (!is_array($settings)) {
224 + if (empty($settings['address_prefix'])) {
227 + // escape plus signs so 'issue+1@example.com' becomes a valid routing address
228 + $prefix = quotemeta($settings['address_prefix']);
230 + if (empty($settings['address_host'])) {
233 + $mail_domain = quotemeta($settings['address_host']);
235 + // it is not checked for type when host alias is asked. this leaves
236 + // room foradding host_alias for other than email routing.
237 + if (isset($settings['host_alias'])) {
238 + // TODO: can't quotemeta() host alias as it can contain multiple hosts separated with pipe
239 + $mail_domain = '(?:' . $mail_domain . '|' . $settings['host_alias'] . ')';
242 + // if there are multiple CC or To headers Mail_Mime creates array.
243 + // handle both cases (strings and arrays).
244 + if (!is_array($addresses)) {
245 + $addresses = array($addresses);
248 + // everything safely escaped and checked, try matching address
249 + foreach ($addresses as $address) {
250 + if (preg_match("/$prefix(\d*)@$mail_domain/i", $address, $matches)) {
251 + return $matches[1];
259 --- eventum-1.7.1/include/class.support.php 2006-04-12 00:48:18.619880756 +0300
260 +++ /home/glen/class.support.php 2006-04-12 00:52:33.395572521 +0300
261 @@ -504,22 +504,22 @@
262 if ($info['ema_use_routing'] == 1) {
263 $setup = Setup::load();
265 - if (@$setup['email_routing']['status'] == 'enabled') {
266 - $prefix = $setup['email_routing']['address_prefix'];
267 - // escape plus signs so 'issue+1@example.com' becomes a valid routing address
268 - $prefix = str_replace('+', '\+', $prefix);
269 - $mail_domain = $setup['email_routing']['address_host'];
270 - $mail_domain_alias = @$setup['email_routing']['host_alias'];
271 - if (!empty($mail_domain_alias)) {
272 - $mail_domain = "[" . $mail_domain . "|" . $mail_domain_alias . "]";
274 - if (empty($prefix)) {
276 + // we create addresses array so it can be reused
277 + $addresses = array();
278 + if (isset($email->to)) {
279 + foreach ($email->to as $address) {
280 + $addresses[] = $address->mailbox . '@' . $address->host;
282 - if (empty($mail_domain)) {
285 + if (isset($email->cc)) {
286 + foreach ($email->cc as $address) {
287 + $addresses[] = $address->mailbox . '@' . $address->host;
289 - if (preg_match("/$prefix(\d*)@$mail_domain/i", $email->toaddress, $matches)) {
292 + if (@$setup['email_routing']['status'] == 'enabled') {
293 + $res = Routing::getMatchingIssueIDs($addresses, 'email');
294 + if ($res != false) {
295 $return = Routing::route_emails($message);
296 if ($return == true) {
297 Support::deleteMessage($info, $mbox, $num);
301 if (@$setup['note_routing']['status'] == 'enabled') {
302 - $prefix = $setup['note_routing']['address_prefix'];
303 - // escape plus signs so 'note+1@example.com' becomes a valid routing address
304 - $prefix = str_replace('+', '\+', $prefix);
305 - $mail_domain = $setup['note_routing']['address_host'];
306 - if (empty($prefix)) {
309 - if (empty($mail_domain)) {
313 - if (preg_match("/$prefix(\d*)@$mail_domain/i", $email->toaddress, $matches)) {
314 + $res = Routing::getMatchingIssueIDs($addresses, 'note');
315 + if ($res != false) {
316 $return = Routing::route_notes($message);
317 if ($return == true) {
318 Support::deleteMessage($info, $mbox, $num);
322 if (@$setup['draft_routing']['status'] == 'enabled') {
323 - $prefix = $setup['draft_routing']['address_prefix'];
324 - // escape plus signs so 'draft+1@example.com' becomes a valid routing address
325 - $prefix = str_replace('+', '\+', $prefix);
326 - $mail_domain = $setup['draft_routing']['address_host'];
327 - if (empty($prefix)) {
330 - if (empty($mail_domain)) {
334 - if (preg_match("/$prefix(\d*)@$mail_domain/i", $email->toaddress, $matches)) {
335 + $res = Routing::getMatchingIssueIDs($addresses, 'draft');
336 + if ($res != false) {
337 $return = Routing::route_drafts($message);
338 if ($return == true) {
339 Support::deleteMessage($info, $mbox, $num);
341 -------------------------------------------------------------------------------------------------------
342 Fix possible Cc: headers composition of routed Notes where Eventum user email and email from headers differ with case.
344 --- eventum-1.7.1/include/class.routing.php 2006-04-12 00:48:18.189871149 +0300
345 +++ /home/glen/class.routing.php 2006-04-12 00:52:33.295570287 +0300
346 @@ -356,12 +346,11 @@
348 foreach ($addresses as $email) {
349 if (in_array(strtolower($email), $user_emails)) {
350 - $cc_users[] = $users[$email];
351 + $cc_users[] = $users[strtolower($email)];
355 $body = Mime_Helper::getMessageBody($structure);
357 $reference_msg_id = Mail_API::getReferenceMessageID($headers);
358 if (!empty($reference_msg_id)) {
359 $parent_id = Note::getIDByMessageID($reference_msg_id);
360 -------------------------------------------------------------------------------------------------------
361 API cleanup: drop unneccessary $email_account_id in route_emails to be consistent with download_emails based routing.
363 --- eventum-1.7.1/misc/route_emails.php 2006-04-12 00:54:57.398789518 +0300
364 +++ /home/glen/route_emails.php 2006-04-12 01:03:52.300737408 +0300
366 include_once(APP_INC_PATH . "db_access.php");
367 include_once(APP_INC_PATH . "class.routing.php");
369 -$email_account_id = $HTTP_SERVER_VARS['argv'][1];
370 $full_message = Misc::getInput();
372 -$return = Routing::route_emails($full_message, $email_account_id);
373 +$return = Routing::route_emails($full_message);
374 if (is_array($return)) {
377 --- eventum-1.7.1/include/class.routing.php 2006-04-12 00:54:57.278786838 +0300
378 +++ /home/glen/class.routing.php 2006-04-12 01:03:45.370582745 +0300
380 * Routes an email to the correct issue.
382 * @param string $full_message The full email message, including headers
383 - * @param integer $email_account_id The ID of the email account this email should be routed too. If empty this method will try to figure it out
385 - function route_emails($full_message, $email_account_id = 0)
386 + function route_emails($full_message)
388 GLOBAL $HTTP_POST_VARS;