[ Index ]

PHP Cross Reference of Eventum

title

Body

[close]

/include/ -> class.mail_queue.php (source)

   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  }


Generated: Wed Dec 19 21:21:33 2007 Cross-referenced by PHPXref 0.7