[ Index ] |
PHP Cross Reference of Eventum |
[Summary view] [Print] [Text view]
1 <?php 2 /* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */ 3 // +----------------------------------------------------------------------+ 4 // | Eventum - Issue Tracking System | 5 // +----------------------------------------------------------------------+ 6 // | Copyright (c) 2003, 2004, 2005, 2006, 2007 MySQL AB | 7 // | | 8 // | This program is free software; you can redistribute it and/or modify | 9 // | it under the terms of the GNU General Public License as published by | 10 // | the Free Software Foundation; either version 2 of the License, or | 11 // | (at your option) any later version. | 12 // | | 13 // | This program is distributed in the hope that it will be useful, | 14 // | but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 16 // | GNU General Public License for more details. | 17 // | | 18 // | You should have received a copy of the GNU General Public License | 19 // | along with this program; if not, write to: | 20 // | | 21 // | Free Software Foundation, Inc. | 22 // | 59 Temple Place - Suite 330 | 23 // | Boston, MA 02111-1307, USA. | 24 // +----------------------------------------------------------------------+ 25 // | Authors: João Prado Maia <jpm@mysql.com> | 26 // +----------------------------------------------------------------------+ 27 // 28 29 30 require_once (APP_INC_PATH . "class.error_handler.php"); 31 require_once (APP_INC_PATH . "class.date.php"); 32 require_once (APP_INC_PATH . "class.mime_helper.php"); 33 require_once (APP_INC_PATH . "class.setup.php"); 34 require_once (APP_INC_PATH . "class.lock.php"); 35 require_once (APP_INC_PATH . "class.user.php"); 36 require_once(APP_PEAR_PATH . 'Mail.php'); 37 38 class Mail_Queue 39 { 40 /** 41 * Checks whether it is safe or not to run the mail queue script. 42 * 43 * @access public 44 * @return boolean 45 */ 46 function isSafeToRun() 47 { 48 return Lock::acquire('process_mail_queue'); 49 } 50 51 52 /** 53 * Clears the lock file for the next time this script runs again. 54 * 55 * @access public 56 * @return void 57 */ 58 function removeProcessFile() 59 { 60 Lock::release('process_mail_queue'); 61 } 62 63 64 /** 65 * Adds an email to the outgoing mail queue. 66 * 67 * @access public 68 * @param string $recipient The recipient of this email 69 * @param array $headers The list of headers that should be sent with this email 70 * @param string $body The body of the message 71 * @param integer $save_email_copy Whether to send a copy of this email to a configurable address or not (eventum_sent@) 72 * @param integer $issue_id The ID of the issue. If false, email will not be associated with issue. 73 * @param string $type The type of message this is. 74 * @param integer $sender_usr_id The id of the user sending this email. 75 * @param integer $type_id The ID of the event that triggered this notification (issue_id, sup_id, not_id, etc) 76 * @return true, or a PEAR_Error object 77 */ 78 function add($recipient, $headers, $body, $save_email_copy = 0, $issue_id = false, $type = '', $sender_usr_id = false, $type_id = false) 79 { 80 // avoid sending emails out to users with inactive status 81 $recipient_email = Mail_API::getEmailAddress($recipient); 82 $usr_id = User::getUserIDByEmail($recipient_email); 83 if (!empty($usr_id)) { 84 $user_status = User::getStatusByEmail($recipient_email); 85 // if user is not set to an active status, then silently ignore 86 if ((!User::isActiveStatus($user_status)) && (!User::isPendingStatus($user_status))) { 87 return false; 88 } 89 } 90 91 $to_usr_id = User::getUserIDByEmail($recipient_email); 92 $recipient = Mail_API::fixAddressQuoting($recipient); 93 94 $reminder_addresses = Reminder::_getReminderAlertAddresses(); 95 96 // add specialized headers 97 if ((!empty($issue_id)) && ((!empty($to_usr_id)) && (User::getRoleByUser($to_usr_id, Issue::getProjectID($issue_id)) > User::getRoleID("Customer"))) || 98 (@in_array(Mail_API::getEmailAddress($to), $reminder_addresses))) { 99 $headers += Mail_API::getSpecializedHeaders($issue_id, $type, $headers, $sender_usr_id); 100 } 101 102 if (empty($issue_id)) { 103 $issue_id = 'null'; 104 } 105 // if the Date: header is missing, add it. 106 if (!in_array('Date', array_keys($headers))) { 107 $headers['Date'] = MIME_Helper::encode(date('D, j M Y H:i:s O')); 108 } 109 if (!empty($headers['To'])) { 110 $headers['To'] = Mail_API::fixAddressQuoting($headers['To']); 111 } 112 113 $res = Mail_API::prepareHeaders($headers); 114 if (PEAR::isError($res)) { 115 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 116 return $res; 117 } 118 list(,$text_headers) = $res; 119 120 $stmt = "INSERT INTO 121 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "mail_queue 122 ( 123 maq_save_copy, 124 maq_queued_date, 125 maq_sender_ip_address, 126 maq_recipient, 127 maq_headers, 128 maq_body, 129 maq_iss_id, 130 maq_subject, 131 maq_type"; 132 if ($sender_usr_id != false) { 133 $stmt .= ",\nmaq_usr_id"; 134 } 135 if ($type_id != false) { 136 $stmt .= ",\nmaq_type_id"; 137 } 138 $stmt .= ") VALUES ( 139 $save_email_copy, 140 '" . Date_API::getCurrentDateGMT() . "', 141 '" . @$_SERVER['REMOTE_ADDR'] . "', 142 '" . Misc::escapeString($recipient) . "', 143 '" . Misc::escapeString($text_headers) . "', 144 '" . Misc::escapeString($body) . "', 145 " . Misc::escapeInteger($issue_id) . ", 146 '" . Misc::escapeString($headers["Subject"]) . "', 147 '$type'"; 148 if ($sender_usr_id != false) { 149 $stmt .= ",\n" . $sender_usr_id; 150 } 151 if ($type_id != false) { 152 $stmt .= ",\n" . $type_id; 153 } 154 $stmt .= ")"; 155 $res = $GLOBALS["db_api"]->dbh->query($stmt); 156 if (PEAR::isError($res)) { 157 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 158 return $res; 159 } else { 160 return true; 161 } 162 } 163 164 165 /** 166 * Sends the queued up messages to their destinations. This can either try 167 * to send emails that couldn't be sent before (status = 'error'), or just 168 * emails just recently queued (status = 'pending'). 169 * 170 * @access public 171 * @param string $status The status of the messages that need to be sent 172 * @param integer $limit The limit of emails that we should send at one time 173 */ 174 function send($status, $limit) 175 { 176 // get list of emails to send 177 $emails = Mail_Queue::_getList($status, $limit); 178 // foreach email 179 for ($i = 0; $i < count($emails); $i++) { 180 $current_status = $status; 181 if ($emails[$i]['maq_type'] == 'error') { 182 $current_status = 'error'; 183 } 184 $result = Mail_Queue::_sendEmail($emails[$i]['recipient'], $emails[$i]['headers'], $emails[$i]['body'], $current_status); 185 if (PEAR::isError($result)) { 186 Mail_Queue::_saveLog($emails[$i]['id'], 'error', Mail_Queue::_getErrorMessage($result)); 187 } else { 188 Mail_Queue::_saveLog($emails[$i]['id'], 'sent', ''); 189 if ($emails[$i]['save_copy']) { 190 // send a copy of this email to eventum_sent@ 191 Mail_API::saveEmailInformation($emails[$i]); 192 } 193 } 194 } 195 } 196 197 198 /** 199 * Connects to the SMTP server and sends the queued message. 200 * 201 * @access private 202 * @param string $recipient The recipient of this message 203 * @param string $text_headers The full headers of this message 204 * @param string $body The full body of this message 205 * @param string $status The status of this message 206 * @return true, or a PEAR_Error object 207 */ 208 function _sendEmail($recipient, $text_headers, $body, $status) 209 { 210 $header_names = Mime_Helper::getHeaderNames($text_headers); 211 $_headers = Mail_Queue::_getHeaders($text_headers, $body); 212 $headers = array(); 213 foreach ($_headers as $lowercase_name => $value) { 214 // need to remove the quotes to avoid a parsing problem 215 // on senders that have extended characters in the first 216 // or last words in their sender name 217 if ($lowercase_name == 'from') { 218 $value = Mime_Helper::removeQuotes($value); 219 } 220 $value = Mime_Helper::encode($value); 221 // add the quotes back 222 if ($lowercase_name == 'from') { 223 $value = Mime_Helper::quoteSender($value); 224 } 225 $headers[$header_names[$lowercase_name]] = $value; 226 } 227 // remove any Reply-To:/Return-Path: values from outgoing messages 228 unset($headers['Reply-To']); 229 unset($headers['Return-Path']); 230 // mutt sucks, so let's remove the broken Mime-Version header and add the proper one 231 if (in_array('Mime-Version', array_keys($headers))) { 232 unset($headers['Mime-Version']); 233 $headers['MIME-Version'] = '1.0'; 234 } 235 $mail =& Mail::factory('smtp', Mail_API::getSMTPSettings()); 236 $res = $mail->send($recipient, $headers, $body); 237 if (PEAR::isError($res)) { 238 // special handling of errors when the mail server is down 239 $msg = $res->getMessage(); 240 $cant_notify = (($status == 'error') || (strstr($msg , 'unable to connect to smtp server')) || (stristr($msg, 'Failed to connect to') !== false)); 241 Error_Handler::logError(array($msg, $res->getDebugInfo()), __FILE__, __LINE__, !$cant_notify); 242 return $res; 243 } else { 244 return true; 245 } 246 } 247 248 249 /** 250 * Parses the full email message and returns an array of the headers 251 * contained in it. 252 * 253 * @access private 254 * @param string $text_headers The full headers of this message 255 * @param string $body The full body of this message 256 * @return array The list of headers 257 */ 258 function _getHeaders($text_headers, $body) 259 { 260 $message = $text_headers . "\n\n" . $body; 261 $structure = Mime_Helper::decode($message, FALSE, FALSE); 262 return $structure->headers; 263 } 264 265 266 /** 267 * Retrieves the list of queued email messages, given a status. 268 * 269 * @access private 270 * @param string $status The status of the messages 271 * @param integer $limit The limit on the number of messages that need to be returned 272 * @return array The list of queued email messages 273 */ 274 function _getList($status, $limit = 50) 275 { 276 $stmt = "SELECT 277 maq_id id, 278 maq_iss_id, 279 maq_save_copy save_copy, 280 maq_recipient recipient, 281 maq_headers headers, 282 maq_body body, 283 maq_type, 284 maq_usr_id 285 FROM 286 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "mail_queue 287 WHERE 288 maq_status='$status' 289 ORDER BY 290 maq_id ASC 291 LIMIT 292 0, $limit"; 293 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 294 if (PEAR::isError($res)) { 295 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 296 return array(); 297 } else { 298 return $res; 299 } 300 } 301 302 303 /** 304 * Saves a log entry about the attempt, successful or otherwise, to send the 305 * queued email message. 306 * 307 * @access private 308 * @param integer $maq_id The queued email message ID 309 * @param string $status The status of the attempt ('sent' or 'error') 310 * @param string $server_message The full message from the SMTP server, in case of an error 311 * @return boolean 312 */ 313 function _saveLog($maq_id, $status, $server_message) 314 { 315 $stmt = "INSERT INTO 316 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "mail_queue_log 317 ( 318 mql_maq_id, 319 mql_created_date, 320 mql_status, 321 mql_server_message 322 ) VALUES ( 323 $maq_id, 324 '" . Date_API::getCurrentDateGMT() . "', 325 '" . Misc::escapeString($status) . "', 326 '" . Misc::escapeString($server_message) . "' 327 )"; 328 $res = $GLOBALS["db_api"]->dbh->query($stmt); 329 if (PEAR::isError($res)) { 330 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 331 return false; 332 } else { 333 $stmt = "UPDATE 334 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "mail_queue 335 SET 336 maq_status='" . Misc::escapeString($status) . "' 337 WHERE 338 maq_id=$maq_id"; 339 $GLOBALS["db_api"]->dbh->query($stmt); 340 return true; 341 } 342 } 343 344 345 /** 346 * Handles the PEAR_Error object returned from the SMTP server, and returns 347 * an appropriate error message string. 348 * 349 * @access private 350 * @param object $error The PEAR_Error object 351 * @return string The error message 352 */ 353 function _getErrorMessage($error) 354 { 355 return $error->getMessage() . "/" . $error->getDebugInfo(); 356 } 357 358 359 /** 360 * Returns the mail queue for a specific issue. 361 * 362 * @access public 363 * @param integer $issue_is The issue ID 364 * @return array An array of emails from the queue 365 */ 366 function getListByIssueID($issue_id) 367 { 368 $issue_id = Misc::escapeInteger($issue_id); 369 $stmt = "SELECT 370 maq_id, 371 maq_queued_date, 372 maq_status, 373 maq_recipient, 374 maq_subject 375 FROM 376 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "mail_queue 377 WHERE 378 maq_iss_id = " . Misc::escapeInteger($issue_id) . " 379 ORDER BY 380 maq_queued_date ASC"; 381 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 382 if (PEAR::isError($res)) { 383 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 384 return false; 385 } 386 if (count($res) > 0) { 387 for ($i = 0; $i < count($res); $i++) { 388 $res[$i]['maq_recipient'] = Mime_Helper::decodeAddress($res[$i]['maq_recipient']); 389 $res[$i]['maq_queued_date'] = Date_API::getFormattedDate(Date_API::getUnixTimestamp($res[$i]['maq_queued_date'], 'GMT')); 390 $res[$i]['maq_subject'] = Mime_Helper::fixEncoding($res[$i]['maq_subject']); 391 } 392 } 393 return $res; 394 } 395 396 397 /** 398 * Returns the mail queue entry based on ID. 399 * 400 * @acess public 401 * @param integer $maq_id The id of the mail queue entry. 402 * @return array An array of information 403 */ 404 function getEntry($maq_id) 405 { 406 $stmt = "SELECT 407 maq_iss_id, 408 maq_queued_date, 409 maq_status, 410 maq_recipient, 411 maq_subject, 412 maq_headers, 413 maq_body 414 FROM 415 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "mail_queue 416 WHERE 417 maq_id = " . Misc::escapeInteger($maq_id); 418 $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC); 419 if (PEAR::isError($res)) { 420 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 421 return false; 422 } else { 423 return $res; 424 } 425 } 426 427 428 function getMessageRecipients($types, $type_id) 429 { 430 if (!is_array($types)) { 431 $types = array($types); 432 } 433 $types = Misc::escapeString($types); 434 $sql = "SELECT 435 maq_recipient 436 FROM 437 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "mail_queue 438 WHERE 439 maq_type IN('" . join("', '", $types) . "') AND 440 maq_type_id = " . Misc::escapeInteger($type_id); 441 $res = $GLOBALS["db_api"]->dbh->getCol($sql); 442 if (PEAR::isError($res)) { 443 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 444 return false; 445 } else { 446 for ($i = 0; $i < count($res); $i++) { 447 $res[$i] = Mime_Helper::decodeAddress(str_replace('"', '', $res[$i])); 448 } 449 return $res; 450 } 451 } 452 } 453 454 // benchmarking the included file (aka setup time) 455 if (APP_BENCHMARK) { 456 $GLOBALS['bench']->setMarker('Included Mail_Queue Class'); 457 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Dec 19 21:21:33 2007 | Cross-referenced by PHPXref 0.7 |