Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
fee14197 | 4 | | CiviCRM version 5 | |
6a488035 | 5 | +--------------------------------------------------------------------+ |
f299f7db | 6 | | Copyright CiviCRM LLC (c) 2004-2020 | |
6a488035 TO |
7 | +--------------------------------------------------------------------+ |
8 | | This file is a part of CiviCRM. | | |
9 | | | | |
10 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
11 | | under the terms of the GNU Affero General Public License | | |
12 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | |
13 | | | | |
14 | | CiviCRM is distributed in the hope that it will be useful, but | | |
15 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
17 | | See the GNU Affero General Public License for more details. | | |
18 | | | | |
19 | | You should have received a copy of the GNU Affero General Public | | |
20 | | License and the CiviCRM Licensing Exception along | | |
21 | | with this program; if not, contact CiviCRM LLC | | |
22 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
23 | | GNU Affero General Public License or the licensing of CiviCRM, | | |
24 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
25 | +--------------------------------------------------------------------+ | |
d25dd0ee | 26 | */ |
6a488035 TO |
27 | |
28 | /** | |
29 | * | |
30 | * @package CRM | |
ca5cec67 | 31 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
6a488035 TO |
32 | */ |
33 | ||
34 | // we should consider moving these to the settings table | |
35 | // before the 4.1 release | |
36 | define('EMAIL_ACTIVITY_TYPE_ID', NULL); | |
37 | define('MAIL_BATCH_SIZE', 50); | |
5bc392e6 EM |
38 | |
39 | /** | |
b8c71ffa | 40 | * Class CRM_Utils_Mail_EmailProcessor. |
5bc392e6 | 41 | */ |
6a488035 TO |
42 | class CRM_Utils_Mail_EmailProcessor { |
43 | ||
01467aec EE |
44 | const MIME_MAX_RECURSION = 10; |
45 | ||
6a488035 TO |
46 | /** |
47 | * Process the default mailbox (ie. that is used by civiMail for the bounce) | |
48 | * | |
3ff238ae | 49 | * @param bool $is_create_activities |
50 | * Should activities be created | |
6a488035 | 51 | */ |
3ff238ae | 52 | public static function processBounces($is_create_activities) { |
ae5ffbb7 | 53 | $dao = new CRM_Core_DAO_MailSettings(); |
353ffa53 | 54 | $dao->domain_id = CRM_Core_Config::domainID(); |
6a488035 TO |
55 | $dao->is_default = TRUE; |
56 | $dao->find(); | |
57 | ||
58 | while ($dao->fetch()) { | |
3ff238ae | 59 | self::_process(TRUE, $dao, $is_create_activities); |
6a488035 | 60 | } |
6a488035 TO |
61 | } |
62 | ||
63 | /** | |
b8c71ffa | 64 | * Delete old files from a given directory (recursively). |
6a488035 | 65 | * |
77855840 TO |
66 | * @param string $dir |
67 | * Directory to cleanup. | |
68 | * @param int $age | |
69 | * Files older than this many seconds will be deleted (default: 60 days). | |
6a488035 | 70 | */ |
00be9182 | 71 | public static function cleanupDir($dir, $age = 5184000) { |
6a488035 TO |
72 | // return early if we can’t read/write the dir |
73 | if (!is_writable($dir) or !is_readable($dir) or !is_dir($dir)) { | |
74 | return; | |
75 | } | |
76 | ||
77 | foreach (scandir($dir) as $file) { | |
78 | ||
79 | // don’t go up the directory stack and skip new files/dirs | |
80 | if ($file == '.' or $file == '..') { | |
81 | continue; | |
82 | } | |
83 | if (filemtime("$dir/$file") > time() - $age) { | |
84 | continue; | |
85 | } | |
86 | ||
87 | // it’s an old file/dir, so delete/recurse | |
88 | is_dir("$dir/$file") ? self::cleanupDir("$dir/$file", $age) : unlink("$dir/$file"); | |
89 | } | |
90 | } | |
91 | ||
92 | /** | |
b8c71ffa | 93 | * Process the mailboxes that aren't default (ie. that aren't used by civiMail for the bounce). |
6a488035 | 94 | */ |
00be9182 | 95 | public static function processActivities() { |
ae5ffbb7 | 96 | $dao = new CRM_Core_DAO_MailSettings(); |
353ffa53 | 97 | $dao->domain_id = CRM_Core_Config::domainID(); |
6a488035 TO |
98 | $dao->is_default = FALSE; |
99 | $dao->find(); | |
100 | $found = FALSE; | |
101 | while ($dao->fetch()) { | |
102 | $found = TRUE; | |
2c2409c3 | 103 | self::_process(FALSE, $dao, TRUE); |
6a488035 TO |
104 | } |
105 | if (!$found) { | |
106 | CRM_Core_Error::fatal(ts('No mailboxes have been configured for Email to Activity Processing')); | |
107 | } | |
108 | return $found; | |
109 | } | |
110 | ||
111 | /** | |
fe482240 | 112 | * Process the mailbox for all the settings from civicrm_mail_settings. |
6a488035 | 113 | * |
fd31fa4c | 114 | * @param bool|string $civiMail if true, processing is done in CiviMail context, or Activities otherwise. |
6a488035 | 115 | */ |
00be9182 | 116 | public static function process($civiMail = TRUE) { |
ae5ffbb7 | 117 | $dao = new CRM_Core_DAO_MailSettings(); |
6a488035 TO |
118 | $dao->domain_id = CRM_Core_Config::domainID(); |
119 | $dao->find(); | |
120 | ||
121 | while ($dao->fetch()) { | |
122 | self::_process($civiMail, $dao); | |
123 | } | |
124 | } | |
125 | ||
5bc392e6 EM |
126 | /** |
127 | * @param $civiMail | |
040073c9 | 128 | * @param CRM_Core_DAO_MailSettings $dao |
3ff238ae | 129 | * @param bool $is_create_activities |
130 | * Create activities. | |
5bc392e6 EM |
131 | * |
132 | * @throws Exception | |
133 | */ | |
3ff238ae | 134 | public static function _process($civiMail, $dao, $is_create_activities) { |
6a488035 TO |
135 | // 0 = activities; 1 = bounce; |
136 | $usedfor = $dao->is_default; | |
137 | ||
ae5ffbb7 | 138 | $emailActivityTypeId |
d15a97f4 | 139 | = (defined('EMAIL_ACTIVITY_TYPE_ID') && EMAIL_ACTIVITY_TYPE_ID) |
140 | ? EMAIL_ACTIVITY_TYPE_ID | |
70a32eb8 | 141 | : CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Inbound Email'); |
6a488035 TO |
142 | |
143 | if (!$emailActivityTypeId) { | |
144 | CRM_Core_Error::fatal(ts('Could not find a valid Activity Type ID for Inbound Email')); | |
145 | } | |
146 | ||
353ffa53 TO |
147 | $config = CRM_Core_Config::singleton(); |
148 | $verpSeperator = preg_quote($config->verpSeparator); | |
6a488035 | 149 | $twoDigitStringMin = $verpSeperator . '(\d+)' . $verpSeperator . '(\d+)'; |
353ffa53 TO |
150 | $twoDigitString = $twoDigitStringMin . $verpSeperator; |
151 | $threeDigitString = $twoDigitString . '(\d+)' . $verpSeperator; | |
6a488035 TO |
152 | |
153 | // FIXME: legacy regexen to handle CiviCRM 2.1 address patterns, with domain id and possible VERP part | |
154 | $commonRegex = '/^' . preg_quote($dao->localpart) . '(b|bounce|c|confirm|o|optOut|r|reply|re|e|resubscribe|u|unsubscribe)' . $threeDigitString . '([0-9a-f]{16})(-.*)?@' . preg_quote($dao->domain) . '$/'; | |
155 | $subscrRegex = '/^' . preg_quote($dao->localpart) . '(s|subscribe)' . $twoDigitStringMin . '@' . preg_quote($dao->domain) . '$/'; | |
156 | ||
157 | // a common-for-all-actions regex to handle CiviCRM 2.2 address patterns | |
158 | $regex = '/^' . preg_quote($dao->localpart) . '(b|c|e|o|r|u)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '$/'; | |
159 | ||
160 | // a tighter regex for finding bounce info in soft bounces’ mail bodies | |
619526f6 | 161 | $rpRegex = '/Return-Path:\s*' . preg_quote($dao->localpart) . '(b)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '/'; |
6a488035 | 162 | |
1a4d92b6 | 163 | // a regex for finding bound info X-Header |
08523e94 O |
164 | $rpXheaderRegex = '/X-CiviMail-Bounce: ' . preg_quote($dao->localpart) . '(b)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '/i'; |
165 | // CiviMail in regex and Civimail in header !!! | |
1a4d92b6 | 166 | |
6a488035 TO |
167 | // retrieve the emails |
168 | try { | |
169 | $store = CRM_Mailing_MailStore::getStore($dao->name); | |
170 | } | |
353ffa53 | 171 | catch (Exception$e) { |
86bfa4f6 | 172 | $message = ts('Could not connect to MailStore for ') . $dao->username . '@' . $dao->server . '<p>'; |
6a488035 TO |
173 | $message .= ts('Error message: '); |
174 | $message .= '<pre>' . $e->getMessage() . '</pre><p>'; | |
175 | CRM_Core_Error::fatal($message); | |
176 | } | |
177 | ||
6a488035 TO |
178 | // process fifty at a time, CRM-4002 |
179 | while ($mails = $store->fetchNext(MAIL_BATCH_SIZE)) { | |
180 | foreach ($mails as $key => $mail) { | |
181 | ||
182 | // for every addressee: match address elements if it's to CiviMail | |
be2fb01f | 183 | $matches = []; |
6a488035 TO |
184 | $action = NULL; |
185 | ||
186 | if ($usedfor == 1) { | |
187 | foreach ($mail->to as $address) { | |
188 | if (preg_match($regex, $address->email, $matches)) { | |
189 | list($match, $action, $job, $queue, $hash) = $matches; | |
190 | break; | |
191 | // FIXME: the below elseifs should be dropped when we drop legacy support | |
192 | } | |
193 | elseif (preg_match($commonRegex, $address->email, $matches)) { | |
194 | list($match, $action, $_, $job, $queue, $hash) = $matches; | |
195 | break; | |
196 | } | |
197 | elseif (preg_match($subscrRegex, $address->email, $matches)) { | |
198 | list($match, $action, $_, $job) = $matches; | |
199 | break; | |
200 | } | |
201 | } | |
202 | ||
203 | // CRM-5471: if $matches is empty, it still might be a soft bounce sent | |
204 | // to another address, so scan the body for ‘Return-Path: …bounce-pattern…’ | |
205 | if (!$matches and preg_match($rpRegex, $mail->generateBody(), $matches)) { | |
206 | list($match, $action, $job, $queue, $hash) = $matches; | |
207 | } | |
208 | ||
1a4d92b6 DL |
209 | // if $matches is still empty, look for the X-CiviMail-Bounce header |
210 | // CRM-9855 | |
211 | if (!$matches and preg_match($rpXheaderRegex, $mail->generateBody(), $matches)) { | |
212 | list($match, $action, $job, $queue, $hash) = $matches; | |
213 | } | |
08523e94 O |
214 | // With Mandrilla, the X-CiviMail-Bounce header is produced by generateBody |
215 | // is base64 encoded | |
216 | // Check all parts | |
217 | if (!$matches) { | |
6ac3485f | 218 | $all_parts = $mail->fetchParts(); |
08523e94 O |
219 | foreach ($all_parts as $k_part => $v_part) { |
220 | if ($v_part instanceof ezcMailFile) { | |
221 | $p_file = $v_part->__get('fileName'); | |
6ac3485f | 222 | $c_file = file_get_contents($p_file); |
08523e94 | 223 | if (preg_match($rpXheaderRegex, $c_file, $matches)) { |
08523e94 O |
224 | list($match, $action, $job, $queue, $hash) = $matches; |
225 | } | |
226 | } | |
227 | } | |
228 | } | |
229 | ||
6a488035 TO |
230 | // if all else fails, check Delivered-To for possible pattern |
231 | if (!$matches and preg_match($regex, $mail->getHeader('Delivered-To'), $matches)) { | |
232 | list($match, $action, $job, $queue, $hash) = $matches; | |
233 | } | |
234 | } | |
235 | ||
236 | // preseve backward compatibility | |
3ff238ae | 237 | if ($usedfor == 0 || $is_create_activities) { |
6a488035 | 238 | // if its the activities that needs to be processed .. |
5fdd5f80 | 239 | try { |
83cd2236 | 240 | $mailParams = CRM_Utils_Mail_Incoming::parseMailingObject($mail); |
fad9031a | 241 | } |
83cd2236 DH |
242 | catch (Exception $e) { |
243 | echo $e->getMessage(); | |
244 | $store->markIgnored($key); | |
245 | continue; | |
246 | } | |
6a488035 | 247 | |
69c9ffb3 | 248 | require_once 'CRM/Utils/DeprecatedUtils.php'; |
6a488035 TO |
249 | $params = _civicrm_api3_deprecated_activity_buildmailparams($mailParams, $emailActivityTypeId); |
250 | ||
251 | $params['version'] = 3; | |
040073c9 CW |
252 | if (!empty($dao->activity_status)) { |
253 | $params['status_id'] = $dao->activity_status; | |
254 | } | |
6a488035 TO |
255 | $result = civicrm_api('activity', 'create', $params); |
256 | ||
257 | if ($result['is_error']) { | |
258 | $matches = FALSE; | |
259 | echo "Failed Processing: {$mail->subject}. Reason: {$result['error_message']}\n"; | |
260 | } | |
261 | else { | |
262 | $matches = TRUE; | |
7349cf08 | 263 | CRM_Utils_Hook::emailProcessor('activity', $params, $mail, $result); |
6a488035 TO |
264 | echo "Processed as Activity: {$mail->subject}\n"; |
265 | } | |
6a488035 TO |
266 | } |
267 | ||
268 | // if $matches is empty, this email is not CiviMail-bound | |
269 | if (!$matches) { | |
270 | $store->markIgnored($key); | |
271 | continue; | |
272 | } | |
273 | ||
274 | // get $replyTo from either the Reply-To header or from From | |
275 | // FIXME: make sure it works with Reply-Tos containing non-email stuff | |
276 | $replyTo = $mail->getHeader('Reply-To') ? $mail->getHeader('Reply-To') : $mail->from->email; | |
277 | ||
278 | // handle the action by passing it to the proper API call | |
279 | // FIXME: leave only one-letter cases when dropping legacy support | |
280 | if (!empty($action)) { | |
281 | $result = NULL; | |
282 | ||
283 | switch ($action) { | |
284 | case 'b': | |
285 | case 'bounce': | |
286 | $text = ''; | |
287 | if ($mail->body instanceof ezcMailText) { | |
288 | $text = $mail->body->text; | |
289 | } | |
290 | elseif ($mail->body instanceof ezcMailMultipart) { | |
01467aec | 291 | $text = self::getTextFromMultipart($mail->body); |
6a488035 | 292 | } |
9dcd46ef | 293 | elseif ($mail->body instanceof ezcMailFile) { |
294 | $text = file_get_contents($mail->body->__get('fileName')); | |
295 | } | |
6a488035 TO |
296 | |
297 | if ( | |
9b9a8713 | 298 | empty($text) && |
6a488035 TO |
299 | $mail->subject == "Delivery Status Notification (Failure)" |
300 | ) { | |
301 | // Exchange error - CRM-9361 | |
302 | foreach ($mail->body->getParts() as $part) { | |
303 | if ($part instanceof ezcMailDeliveryStatus) { | |
304 | foreach ($part->recipients as $rec) { | |
305 | if ($rec["Status"] == "5.1.1") { | |
9b9a8713 SL |
306 | if (isset($rec["Description"])) { |
307 | $text = $rec["Description"]; | |
308 | } | |
309 | else { | |
0addad12 | 310 | $text = $rec["Status"] . " Delivery to the following recipients failed"; |
9b9a8713 | 311 | } |
6a488035 TO |
312 | break; |
313 | } | |
314 | } | |
315 | } | |
316 | } | |
317 | } | |
318 | ||
319 | if (empty($text)) { | |
320 | // If bounce processing fails, just take the raw body. Cf. CRM-11046 | |
321 | $text = $mail->generateBody(); | |
322 | ||
323 | // if text is still empty, lets fudge a blank text so the api call below will succeed | |
6ac9d864 DL |
324 | if (empty($text)) { |
325 | $text = ts('We could not extract the mail body from this bounce message.'); | |
326 | } | |
6a488035 TO |
327 | } |
328 | ||
be2fb01f | 329 | $params = [ |
6a488035 TO |
330 | 'job_id' => $job, |
331 | 'event_queue_id' => $queue, | |
332 | 'hash' => $hash, | |
333 | 'body' => $text, | |
334 | 'version' => 3, | |
cdc5c450 | 335 | // Setting is_transactional means it will rollback if |
336 | // it crashes part way through creating the bounce. | |
337 | // If the api were standard & had a create this would be the | |
338 | // default. Adding the standard api & deprecating this one | |
339 | // would probably be the | |
340 | // most consistent way to address this - but this is | |
341 | // a quick hack. | |
342 | 'is_transactional' => 1, | |
be2fb01f | 343 | ]; |
6a488035 TO |
344 | $result = civicrm_api('Mailing', 'event_bounce', $params); |
345 | break; | |
346 | ||
347 | case 'c': | |
348 | case 'confirm': | |
349 | // CRM-7921 | |
be2fb01f | 350 | $params = [ |
6a488035 TO |
351 | 'contact_id' => $job, |
352 | 'subscribe_id' => $queue, | |
353 | 'hash' => $hash, | |
354 | 'version' => 3, | |
be2fb01f | 355 | ]; |
6a488035 TO |
356 | $result = civicrm_api('Mailing', 'event_confirm', $params); |
357 | break; | |
358 | ||
359 | case 'o': | |
360 | case 'optOut': | |
be2fb01f | 361 | $params = [ |
6a488035 TO |
362 | 'job_id' => $job, |
363 | 'event_queue_id' => $queue, | |
364 | 'hash' => $hash, | |
365 | 'version' => 3, | |
be2fb01f | 366 | ]; |
6a488035 TO |
367 | $result = civicrm_api('MailingGroup', 'event_domain_unsubscribe', $params); |
368 | break; | |
369 | ||
370 | case 'r': | |
371 | case 'reply': | |
372 | // instead of text and HTML parts (4th and 6th params) send the whole email as the last param | |
be2fb01f | 373 | $params = [ |
6a488035 TO |
374 | 'job_id' => $job, |
375 | 'event_queue_id' => $queue, | |
376 | 'hash' => $hash, | |
377 | 'bodyTxt' => NULL, | |
378 | 'replyTo' => $replyTo, | |
379 | 'bodyHTML' => NULL, | |
380 | 'fullEmail' => $mail->generate(), | |
381 | 'version' => 3, | |
be2fb01f | 382 | ]; |
6a488035 TO |
383 | $result = civicrm_api('Mailing', 'event_reply', $params); |
384 | break; | |
385 | ||
386 | case 'e': | |
387 | case 're': | |
388 | case 'resubscribe': | |
be2fb01f | 389 | $params = [ |
6a488035 TO |
390 | 'job_id' => $job, |
391 | 'event_queue_id' => $queue, | |
392 | 'hash' => $hash, | |
393 | 'version' => 3, | |
be2fb01f | 394 | ]; |
6a488035 TO |
395 | $result = civicrm_api('MailingGroup', 'event_resubscribe', $params); |
396 | break; | |
397 | ||
398 | case 's': | |
399 | case 'subscribe': | |
be2fb01f | 400 | $params = [ |
6a488035 TO |
401 | 'email' => $mail->from->email, |
402 | 'group_id' => $job, | |
403 | 'version' => 3, | |
be2fb01f | 404 | ]; |
6a488035 TO |
405 | $result = civicrm_api('MailingGroup', 'event_subscribe', $params); |
406 | break; | |
407 | ||
408 | case 'u': | |
409 | case 'unsubscribe': | |
be2fb01f | 410 | $params = [ |
6a488035 TO |
411 | 'job_id' => $job, |
412 | 'event_queue_id' => $queue, | |
413 | 'hash' => $hash, | |
414 | 'version' => 3, | |
be2fb01f | 415 | ]; |
6a488035 TO |
416 | $result = civicrm_api('MailingGroup', 'event_unsubscribe', $params); |
417 | break; | |
418 | } | |
419 | ||
420 | if ($result['is_error']) { | |
421 | echo "Failed Processing: {$mail->subject}, Action: $action, Job ID: $job, Queue ID: $queue, Hash: $hash. Reason: {$result['error_message']}\n"; | |
422 | } | |
423 | else { | |
424 | CRM_Utils_Hook::emailProcessor('mailing', $params, $mail, $result, $action); | |
425 | } | |
426 | } | |
427 | ||
428 | $store->markProcessed($key); | |
429 | } | |
430 | // CRM-7356 – used by IMAP only | |
431 | $store->expunge(); | |
432 | } | |
433 | } | |
96025800 | 434 | |
01467aec EE |
435 | /** |
436 | * @param \ezcMailMultipart $multipart | |
437 | * @param int $recursionLevel | |
438 | * | |
439 | * @return array | |
440 | */ | |
441 | protected static function getTextFromMultipart($multipart, $recursionLevel = 0) { | |
442 | if ($recursionLevel >= self::MIME_MAX_RECURSION) { | |
443 | return NULL; | |
444 | } | |
445 | $recursionLevel += 1; | |
446 | $text = NULL; | |
447 | if ($multipart instanceof ezcMailMultipartReport) { | |
448 | $text = self::getTextFromMulipartReport($multipart, $recursionLevel); | |
449 | } | |
450 | elseif ($multipart instanceof ezcMailMultipartRelated) { | |
451 | $text = self::getTextFromMultipartRelated($multipart, $recursionLevel); | |
452 | } | |
453 | else { | |
454 | foreach ($multipart->getParts() as $part) { | |
455 | if (isset($part->subType) and $part->subType === 'plain') { | |
456 | $text = $part->text; | |
457 | } | |
458 | elseif ($part instanceof ezcMailMultipart) { | |
459 | $text = self::getTextFromMultipart($part, $recursionLevel); | |
460 | } | |
461 | if ($text) { | |
462 | break; | |
463 | } | |
464 | } | |
465 | } | |
466 | return $text; | |
467 | } | |
468 | ||
469 | /** | |
470 | * @param \ezcMailMultipartRelated $related | |
471 | * @param int $recursionLevel | |
472 | * | |
473 | * @return array | |
474 | */ | |
475 | protected static function getTextFromMultipartRelated($related, $recursionLevel) { | |
476 | $text = NULL; | |
477 | foreach ($related->getRelatedParts() as $part) { | |
478 | if (isset($part->subType) and $part->subType === 'plain') { | |
479 | $text = $part->text; | |
480 | } | |
481 | elseif ($part instanceof ezcMailMultipart) { | |
482 | $text = self::getTextFromMultipart($part, $recursionLevel); | |
483 | } | |
484 | if ($text) { | |
485 | break; | |
486 | } | |
487 | } | |
488 | return $text; | |
489 | } | |
490 | ||
491 | /** | |
492 | * @param \ezcMailMultipartReport $multipart | |
493 | * @param $recursionLevel | |
494 | * | |
495 | * @return array | |
496 | */ | |
497 | protected static function getTextFromMulipartReport($multipart, $recursionLevel) { | |
498 | $text = NULL; | |
499 | $part = $multipart->getMachinePart(); | |
500 | if ($part instanceof ezcMailDeliveryStatus) { | |
501 | foreach ($part->recipients as $rec) { | |
502 | if (isset($rec["Diagnostic-Code"])) { | |
503 | $text = $rec["Diagnostic-Code"]; | |
504 | break; | |
505 | } | |
506 | elseif (isset($rec["Description"])) { | |
507 | $text = $rec["Description"]; | |
508 | break; | |
509 | } | |
510 | // no diagnostic info present - try getting the human readable part | |
511 | elseif (isset($rec["Status"])) { | |
512 | $text = $rec["Status"]; | |
513 | $textpart = $multipart->getReadablePart(); | |
514 | if ($textpart !== NULL and isset($textpart->text)) { | |
515 | $text .= " " . $textpart->text; | |
516 | } | |
517 | else { | |
518 | $text .= " Delivery failed but no diagnostic code or description."; | |
519 | } | |
520 | break; | |
521 | } | |
522 | } | |
523 | } | |
524 | elseif ($part !== NULL and isset($part->text)) { | |
525 | $text = $part->text; | |
526 | } | |
527 | elseif (($part = $multipart->getReadablePart()) !== NULL) { | |
528 | if (isset($part->text)) { | |
529 | $text = $part->text; | |
530 | } | |
531 | elseif ($part instanceof ezcMailMultipart) { | |
532 | $text = self::getTextFromMultipart($part, $recursionLevel); | |
533 | } | |
534 | } | |
535 | return $text; | |
536 | } | |
537 | ||
6a488035 | 538 | } |