[ Index ]

PHP Cross Reference of Eventum

title

Body

[close]

/include/ -> class.support.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  require_once (APP_INC_PATH . "class.error_handler.php");
  30  require_once (APP_INC_PATH . "class.auth.php");
  31  require_once (APP_INC_PATH . "class.user.php");
  32  require_once (APP_INC_PATH . "class.pager.php");
  33  require_once (APP_INC_PATH . "class.mail.php");
  34  require_once (APP_INC_PATH . "class.note.php");
  35  require_once (APP_INC_PATH . "class.misc.php");
  36  require_once (APP_INC_PATH . "class.mime_helper.php");
  37  require_once (APP_INC_PATH . "class.date.php");
  38  require_once (APP_INC_PATH . "class.history.php");
  39  require_once (APP_INC_PATH . "class.issue.php");
  40  require_once (APP_INC_PATH . "class.email_account.php");
  41  require_once (APP_INC_PATH . "class.search_profile.php");
  42  require_once (APP_INC_PATH . "class.routing.php");
  43  
  44  /**
  45   * Class to handle the business logic related to the email feature of
  46   * the application.
  47   *
  48   * @version 1.0
  49   * @author João Prado Maia <jpm@mysql.com>
  50   */
  51  
  52  class Support
  53  {
  54      /**
  55       * Permanently removes the given support emails from the associated email
  56       * server.
  57       *
  58       * @access  public
  59       * @param   array $sup_ids The list of support emails
  60       * @return  integer 1 if the removal worked, -1 otherwise
  61       */
  62      function expungeEmails($sup_ids)
  63      {
  64          $accounts = array();
  65  
  66          $stmt = "SELECT
  67                      sup_id,
  68                      sup_message_id,
  69                      sup_ema_id
  70                   FROM
  71                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
  72                   WHERE
  73                      sup_id IN (" . implode(', ', Misc::escapeInteger($sup_ids)) . ")";
  74          $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
  75          if (PEAR::isError($res)) {
  76              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
  77              return -1;
  78          } else {
  79              for ($i = 0; $i < count($res); $i++) {
  80                  // don't remove emails from the imap/pop3 server if the email
  81                  // account is set to leave a copy of the messages on the server
  82                  $account_details = Email_Account::getDetails($res[$i]['sup_ema_id']);
  83                  if (!$account_details['leave_copy']) {
  84                      // try to re-use an open connection to the imap server
  85                      if (!in_array($res[$i]['sup_ema_id'], array_keys($accounts))) {
  86                          $accounts[$res[$i]['sup_ema_id']] = Support::connectEmailServer(Email_Account::getDetails($res[$i]['sup_ema_id']));
  87                      }
  88                      $mbox = $accounts[$res[$i]['sup_ema_id']];
  89                      if ($mbox !== FALSE) {
  90                          // now try to find the UID of the current message-id
  91                          $matches = @imap_search($mbox, 'TEXT "' . $res[$i]['sup_message_id'] . '"');
  92                          if (count($matches) > 0) {
  93                              for ($y = 0; $y < count($matches); $y++) {
  94                                  $headers = imap_headerinfo($mbox, $matches[$y]);
  95                                  // if the current message also matches the message-id header, then remove it!
  96                                  if ($headers->message_id == $res[$i]['sup_message_id']) {
  97                                      @imap_delete($mbox, $matches[$y]);
  98                                      @imap_expunge($mbox);
  99                                      break;
 100                                  }
 101                              }
 102                          }
 103                      }
 104                  }
 105                  // remove the email record from the table
 106                  Support::removeEmail($res[$i]['sup_id']);
 107              }
 108              return 1;
 109          }
 110      }
 111  
 112  
 113      /**
 114       * Removes the given support email from the database table.
 115       *
 116       * @access  public
 117       * @param   integer $sup_id The support email ID
 118       * @return  boolean
 119       */
 120      function removeEmail($sup_id)
 121      {
 122          $stmt = "DELETE FROM
 123                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
 124                   WHERE
 125                      sup_id=" . Misc::escapeInteger($sup_id);
 126          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 127          if (PEAR::isError($res)) {
 128              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 129              return false;
 130          } else {
 131              $stmt = "DELETE FROM
 132                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body
 133                       WHERE
 134                          seb_sup_id=" . Misc::escapeInteger($sup_id);
 135              $res = $GLOBALS["db_api"]->dbh->query($stmt);
 136              if (PEAR::isError($res)) {
 137                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 138                  return false;
 139              } else {
 140                  return true;
 141              }
 142          }
 143      }
 144  
 145  
 146      /**
 147       * Method used to get the next and previous messages in order to build
 148       * side links when viewing a particular email.
 149       *
 150       * @access  public
 151       * @param   integer $sup_id The email ID
 152       * @return  array Information on the next and previous messages
 153       */
 154      function getListingSides($sup_id)
 155      {
 156          $options = Support::saveSearchParams();
 157  
 158          $stmt = "SELECT
 159                      sup_id,
 160                      sup_ema_id
 161                   FROM
 162                      (
 163                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
 164                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "email_account
 165                      )
 166                      LEFT JOIN
 167                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
 168                      ON
 169                          sup_iss_id = iss_id";
 170          if (!empty($options['keywords'])) {
 171              $stmt .= "," . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body";
 172          }
 173          $stmt .= Support::buildWhereClause($options);
 174          $stmt .= "
 175                   ORDER BY
 176                      " . $options["sort_by"] . " " . $options["sort_order"];
 177          $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt);
 178          if (PEAR::isError($res)) {
 179              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 180              return "";
 181          } else {
 182              // COMPAT: the next line requires PHP >= 4.0.5
 183              $email_ids = array_keys($res);
 184              $index = array_search($sup_id, $email_ids);
 185              if (!empty($email_ids[$index+1])) {
 186                  $next = $email_ids[$index+1];
 187              }
 188              if (!empty($email_ids[$index-1])) {
 189                  $previous = $email_ids[$index-1];
 190              }
 191              return array(
 192                  "next"     => array(
 193                      'sup_id' => @$next,
 194                      'ema_id' => @$res[$next]
 195                  ),
 196                  "previous" => array(
 197                      'sup_id' => @$previous,
 198                      'ema_id' => @$res[$previous]
 199                  )
 200              );
 201          }
 202      }
 203  
 204  
 205      /**
 206       * Method used to get the next and previous messages in order to build
 207       * side links when viewing a particular email associated with an issue.
 208       *
 209       * @access  public
 210       * @param   integer $issue_id The issue ID
 211       * @param   integer $sup_id The email ID
 212       * @return  array Information on the next and previous messages
 213       */
 214      function getIssueSides($issue_id, $sup_id)
 215      {
 216          $stmt = "SELECT
 217                      sup_id,
 218                      sup_ema_id
 219                   FROM
 220                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
 221                   WHERE
 222                      sup_iss_id=" . Misc::escapeInteger($issue_id) . "
 223                   ORDER BY
 224                      sup_id ASC";
 225          $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt);
 226          if (PEAR::isError($res)) {
 227              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 228              return "";
 229          } else {
 230              // COMPAT: the next line requires PHP >= 4.0.5
 231              $email_ids = array_keys($res);
 232              $index = array_search($sup_id, $email_ids);
 233              if (!empty($email_ids[$index+1])) {
 234                  $next = $email_ids[$index+1];
 235              }
 236              if (!empty($email_ids[$index-1])) {
 237                  $previous = $email_ids[$index-1];
 238              }
 239              return array(
 240                  "next"     => array(
 241                      'sup_id' => @$next,
 242                      'ema_id' => @$res[$next]
 243                  ),
 244                  "previous" => array(
 245                      'sup_id' => @$previous,
 246                      'ema_id' => @$res[$previous]
 247                  )
 248              );
 249          }
 250      }
 251  
 252  
 253      /**
 254       * Method used to save the email note into a backup directory.
 255       *
 256       * @access  public
 257       * @param   string $message The full body of the email
 258       */
 259      function saveRoutedEmail($message)
 260      {
 261          list($usec,) = explode(" ", microtime());
 262          $filename = date('Y-m-d_H-i-s_') . $usec . '.email.txt';
 263          $file = APP_ROUTED_MAILS_SAVEDIR . 'routed_emails/' . $filename;
 264          $fp = @fopen($file, 'w');
 265          @fwrite($fp, $message);
 266          @fclose($fp);
 267          @chmod($file, 0644);
 268      }
 269  
 270  
 271      /**
 272       * Method used to get the sender of a given set of emails.
 273       *
 274       * @access  public
 275       * @param   integer $sup_ids The email IDs
 276       * @return  array The 'From:' headers for those emails
 277       */
 278      function getSender($sup_ids)
 279      {
 280          $stmt = "SELECT
 281                      sup_from
 282                   FROM
 283                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
 284                   WHERE
 285                      sup_id IN (" . implode(", ", Misc::escapeInteger($sup_ids)) . ")";
 286          $res = $GLOBALS["db_api"]->dbh->getCol($stmt);
 287          if (PEAR::isError($res)) {
 288              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 289              return array();
 290          } else {
 291              if (empty($res)) {
 292                  return array();
 293              } else {
 294                  return $res;
 295              }
 296          }
 297      }
 298  
 299  
 300      /**
 301       * Method used to clear the error stack as required by the IMAP PHP extension.
 302       *
 303       * @access  public
 304       * @return  void
 305       */
 306      function clearErrors()
 307      {
 308          @imap_errors();
 309      }
 310  
 311  
 312      /**
 313       * Method used to restore the specified support emails from
 314       * 'removed' to 'active'.
 315       *
 316       * @access  public
 317       * @return  integer 1 if the update worked, -1 otherwise
 318       */
 319      function restoreEmails()
 320      {
 321          $items = @implode(", ", Misc::escapeInteger($_POST["item"]));
 322          $stmt = "UPDATE
 323                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
 324                   SET
 325                      sup_removed=0
 326                   WHERE
 327                      sup_id IN ($items)";
 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 -1;
 332          } else {
 333              return 1;
 334          }
 335      }
 336  
 337  
 338      /**
 339       * Method used to get the list of support email entries that are
 340       * set as 'removed'.
 341       *
 342       * @access  public
 343       * @return  array The list of support emails
 344       */
 345      function getRemovedList()
 346      {
 347          $stmt = "SELECT
 348                      sup_id,
 349                      sup_date,
 350                      sup_subject,
 351                      sup_from
 352                   FROM
 353                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
 354                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "email_account
 355                   WHERE
 356                      ema_prj_id=" . Auth::getCurrentProject() . " AND
 357                      ema_id=sup_ema_id AND
 358                      sup_removed=1";
 359          $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
 360          if (PEAR::isError($res)) {
 361              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 362              return "";
 363          } else {
 364              for ($i = 0; $i < count($res); $i++) {
 365                  $res[$i]["sup_date"] = Date_API::getFormattedDate($res[$i]["sup_date"]);
 366                  $res[$i]["sup_subject"] = Mime_Helper::fixEncoding($res[$i]["sup_subject"]);
 367                  $res[$i]["sup_from"] = Mime_Helper::fixEncoding($res[$i]["sup_from"]);
 368              }
 369              return $res;
 370          }
 371      }
 372  
 373  
 374      /**
 375       * Method used to remove all support email entries associated with
 376       * a specified list of support email accounts.
 377       *
 378       * @access  public
 379       * @param   array $ids The list of support email accounts
 380       * @return  boolean
 381       */
 382      function removeEmailByAccounts($ids)
 383      {
 384          if (count($ids) < 1) {
 385              return true;
 386          }
 387          $items = @implode(", ", Misc::escapeInteger($ids));
 388          $stmt = "DELETE FROM
 389                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
 390                   WHERE
 391                      sup_ema_id IN ($items)";
 392          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 393          if (PEAR::isError($res)) {
 394              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 395              return false;
 396          } else {
 397              return true;
 398          }
 399      }
 400  
 401  
 402      /**
 403       * Method used to build the server URI to connect to.
 404       *
 405       * @access  public
 406       * @param   array $info The email server information
 407       * @param   boolean $tls Whether to use TLS or not
 408       * @return  string The server URI to connect to
 409       */
 410      function getServerURI($info, $tls = FALSE)
 411      {
 412          $server_uri = $info['ema_hostname'] . ':' . $info['ema_port'] . '/' . strtolower($info['ema_type']);
 413          if (stristr($info['ema_type'], 'imap')) {
 414              $folder = $info['ema_folder'];
 415          } else {
 416              $folder = 'INBOX';
 417          }
 418          return '{' . $server_uri . '}' . $folder;
 419      }
 420  
 421  
 422      /**
 423       * Method used to connect to the provided email server.
 424       *
 425       * @access  public
 426       * @param   array $info The email server information
 427       * @return  resource The email server connection
 428       */
 429      function connectEmailServer($info)
 430      {
 431          $mbox = @imap_open(Support::getServerURI($info), $info['ema_username'], $info['ema_password']);
 432          if ($mbox === FALSE) {
 433              $errors = @imap_errors();
 434              if (strstr(strtolower($errors[0]), 'certificate failure')) {
 435                  $mbox = @imap_open(Support::getServerURI($info, TRUE), $info['ema_username'], $info['ema_password']);
 436              } else {
 437                  Error_Handler::logError('Error while connecting to the email server - ' . $errors[0], __FILE__, __LINE__);
 438              }
 439          }
 440          return $mbox;
 441      }
 442  
 443  
 444      /**
 445       * Method used to get the total number of emails in the specified
 446       * mailbox.
 447       *
 448       * @access  public
 449       * @param   resource $mbox The mailbox
 450       * @return  integer The number of emails
 451       */
 452      function getTotalEmails($mbox)
 453      {
 454          return @imap_num_msg($mbox);
 455      }
 456  
 457  
 458      /**
 459       * Method used to get the information about a specific message
 460       * from a given mailbox.
 461       *
 462       * XXX this function does more than that.
 463       *
 464       * @access  public
 465       * @param   resource $mbox The mailbox
 466       * @param   array $info The support email account information
 467       * @param   integer $num The index of the message
 468       * @return  void
 469       */
 470      function getEmailInfo($mbox, $info, $num)
 471      {
 472          Auth::createFakeCookie(APP_SYSTEM_USER_ID);
 473  
 474          // check if the current message was already seen
 475          if ($info['ema_get_only_new']) {
 476              list($overview) = @imap_fetch_overview($mbox, $num);
 477              if (($overview->seen) || ($overview->deleted) || ($overview->answered)) {
 478                  return;
 479              }
 480          }
 481  
 482          $email = @imap_headerinfo($mbox, $num);
 483          $headers = imap_fetchheader($mbox, $num);
 484          $body = imap_body($mbox, $num);
 485          // check for mysterious blank messages
 486          if (empty($body) and empty($headers)) {
 487              // XXX do some error reporting?
 488              return;
 489          }
 490          $message_id = Mail_API::getMessageID($headers, $body);
 491          $message = $headers . $body;
 492          // we don't need $body anymore -- free memory
 493          unset($body);
 494  
 495          // if message_id already exists, return immediately -- nothing to do
 496          if (Support::exists($message_id) || Note::exists($message_id)) {
 497              return;
 498          }
 499  
 500          $structure = Mime_Helper::decode($message, true, true);
 501          $message_body = Mime_Helper::getMessageBody($structure);
 502          if (Mime_Helper::hasAttachments($structure)) {
 503              $has_attachments = 1;
 504          } else {
 505              $has_attachments = 0;
 506          }
 507          // we can't trust the in-reply-to from the imap c-client, so let's
 508          // try to manually parse that value from the full headers
 509          $reference_msg_id = Mail_API::getReferenceMessageID($headers);
 510  
 511          // pass in $email by reference so it can be modified
 512          $workflow = Workflow::preEmailDownload($info['ema_prj_id'], $info, $mbox, $num, $message, $email);
 513          if ($workflow === -1) {
 514              return;
 515          }
 516  
 517          // route emails if neccassary
 518          if ($info['ema_use_routing'] == 1) {
 519              $setup = Setup::load();
 520  
 521              // we create addresses array so it can be reused
 522              $addresses = array();
 523              if (isset($email->to)) {
 524                  foreach ($email->to as $address) {
 525                      $addresses[] = $address->mailbox . '@' . $address->host;
 526                  }
 527              }
 528              if (isset($email->cc)) {
 529                  foreach ($email->cc as $address) {
 530                      $addresses[] = $address->mailbox . '@' . $address->host;
 531                  }
 532              }
 533  
 534              if (@$setup['email_routing']['status'] == 'enabled') {
 535                  $res = Routing::getMatchingIssueIDs($addresses, 'email');
 536                  if ($res != false) {
 537                      $return = Routing::route_emails($message);
 538                      if ($return == true) {
 539                          Support::deleteMessage($info, $mbox, $num);
 540                          return;
 541                      }
 542                      return;
 543                  }
 544              }
 545              if (@$setup['note_routing']['status'] == 'enabled') {
 546                  $res = Routing::getMatchingIssueIDs($addresses, 'note');
 547                  if ($res != false) {
 548                      $return = Routing::route_notes($message);
 549                      if ($return == true) {
 550                          Support::deleteMessage($info, $mbox, $num);
 551                          return;
 552                      }
 553                      return;
 554                  }
 555              }
 556              if (@$setup['draft_routing']['status'] == 'enabled') {
 557                  $res = Routing::getMatchingIssueIDs($addresses, 'draft');
 558                  if ($res != false) {
 559                      $return = Routing::route_drafts($message);
 560                      if ($return == true) {
 561                          Support::deleteMessage($info, $mbox, $num);
 562                          return;
 563                      }
 564                      return;
 565                  }
 566              }
 567              return;
 568          }
 569  
 570          $sender_email = Mail_API::getEmailAddress($email->fromaddress);
 571          if (PEAR::isError($sender_email)) {
 572              $sender_email = 'Error Parsing Email <>';
 573          }
 574  
 575          $t = array(
 576              'ema_id'         => $info['ema_id'],
 577              'message_id'     => $message_id,
 578              'date'           => @Date_API::getDateGMTByTS($email->udate),
 579              'from'           => $sender_email,
 580              'to'             => @$email->toaddress,
 581              'cc'             => @$email->ccaddress,
 582              'subject'        => @$email->subject,
 583              'body'           => @$message_body,
 584              'full_email'     => @$message,
 585              'has_attachment' => $has_attachments,
 586              // the following items are not inserted, but useful in some methods
 587              'headers'        => @$structure->headers
 588          );
 589          $should_create_array = Support::createIssueFromEmail(
 590              $info, $headers, $message_body, $t['date'], $sender_email, Mime_Helper::fixEncoding( @$email->subject), $t['to'], $t['cc']);
 591          $should_create_issue = $should_create_array['should_create_issue'];
 592          $associate_email = $should_create_array['associate_email'];
 593          if (!empty($should_create_array['issue_id'])) {
 594              $t['issue_id'] = $should_create_array['issue_id'];
 595  
 596              // figure out if we should change to a different email account
 597              $iss_prj_id = Issue::getProjectID($t['issue_id']);
 598              if ($info['ema_prj_id'] != $iss_prj_id) {
 599                  $new_ema_id = Email_Account::getEmailAccount($iss_prj_id);
 600                  if (!empty($new_ema_id)) {
 601                      $t['ema_id'] = $new_ema_id;
 602                  }
 603              }
 604          }
 605          if (!empty($should_create_array['customer_id'])) {
 606              $t['customer_id'] = $should_create_array['customer_id'];
 607          }
 608          if (empty($t['issue_id'])) {
 609              $t['issue_id'] = 0;
 610          } else {
 611              $prj_id = Issue::getProjectID($t['issue_id']);
 612              Auth::createFakeCookie(APP_SYSTEM_USER_ID, $prj_id);
 613          }
 614          if ($should_create_array['type'] == 'note') {
 615              // assume that this is not a valid note
 616              $res = -1;
 617  
 618              if ($t['issue_id'] != 0) {
 619                  // check if this is valid user
 620                  $usr_id = User::getUserIDByEmail($sender_email);
 621                  if (!empty($usr_id)) {
 622                      $role_id = User::getRoleByUser($usr_id, $prj_id);
 623                      if ($role_id > User::getRoleID("Customer")) {
 624                          // actually a valid user so insert the note
 625  
 626                          Auth::createFakeCookie($usr_id, $prj_id);
 627  
 628                          $users = Project::getUserEmailAssocList($prj_id, 'active', User::getRoleID('Customer'));
 629                          $user_emails = array_map('strtolower', array_values($users));
 630                          $users = array_flip($users);
 631  
 632                          $addresses = array();
 633                          $to_addresses = Mail_API::getEmailAddresses(@$structure->headers['to']);
 634                          if (count($to_addresses)) {
 635                              $addresses = $to_addresses;
 636                          }
 637                          $cc_addresses = Mail_API::getEmailAddresses(@$structure->headers['cc']);
 638                          if (count($cc_addresses)) {
 639                              $addresses = array_merge($addresses, $cc_addresses);
 640                          }
 641                          $cc_users = array();
 642                          foreach ($addresses as $email) {
 643                              if (in_array(strtolower($email), $user_emails)) {
 644                                  $cc_users[] = $users[$email];
 645                              }
 646                          }
 647  
 648                          // XXX FIXME, this is not nice thing to do
 649                          $_POST = array(
 650                              'title'                => Mail_API::removeExcessRe($t['subject']),
 651                              'note'                 => $t['body'],
 652                              'note_cc'              => $cc_users,
 653                              'add_extra_recipients' => 'yes',
 654                              'message_id'           => $t['message_id'],
 655                              'parent_id'            => $should_create_array['parent_id'],
 656                          );
 657                          $res = Note::insert($usr_id, $t['issue_id']);
 658                      }
 659                  }
 660              }
 661          } else {
 662              // check if we need to block this email
 663              if (($should_create_issue == true) || (!Support::blockEmailIfNeeded($t))) {
 664                  if (!empty($t['issue_id'])) {
 665                      list($t['full_email'], $t['headers']) = Mail_API::rewriteThreadingHeaders($t['issue_id'], $t['full_email'], $t['headers'], 'email');
 666                  }
 667  
 668                  // make variable available for workflow to be able to detect whether this email created new issue
 669                  $t['should_create_issue'] = $should_create_array['should_create_issue'];
 670  
 671                  $res = Support::insertEmail($t, $structure, $sup_id);
 672                  if ($res != -1) {
 673                      // only extract the attachments from the email if we are associating the email to an issue
 674                      if (!empty($t['issue_id'])) {
 675                          Support::extractAttachments($t['issue_id'], $structure);
 676  
 677                          // notifications about new emails are always external
 678                          $internal_only = false;
 679                          $assignee_only = false;
 680                          // special case when emails are bounced back, so we don't want a notification to customers about those
 681                          if (Notification::isBounceMessage($sender_email)) {
 682                              // broadcast this email only to the assignees for this issue
 683                              $internal_only = true;
 684                              $assignee_only = true;
 685                          } elseif ($should_create_issue == true) {
 686                              // if a new issue was created, only send a copy of the email to the assignee (if any), don't resend to the original TO/CC list
 687                              $assignee_only = true;
 688                              $internal_only = true;
 689                          }
 690                          Notification::notifyNewEmail(Auth::getUserID(), $t['issue_id'], $t, $internal_only, $assignee_only, '', $sup_id);
 691                          // try to get usr_id of sender, if not, use system account
 692                          $usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($structure->headers['from']));
 693                          if (!$usr_id) {
 694                              $usr_id = APP_SYSTEM_USER_ID;
 695                          }
 696                          // mark this issue as updated
 697                          if ((!empty($t['customer_id'])) && ($t['customer_id'] != 'NULL')) {
 698                              Issue::markAsUpdated($t['issue_id'], 'customer action');
 699                          } else {
 700                              if ((!empty($usr_id)) && (User::getRoleByUser($usr_id, $prj_id) > User::getRoleID('Customer'))) {
 701                                  Issue::markAsUpdated($t['issue_id'], 'staff response');
 702                              } else {
 703                                  Issue::markAsUpdated($t['issue_id'], 'user response');
 704                              }
 705                          }
 706                          // log routed email
 707                          History::add($t['issue_id'], $usr_id, History::getTypeID('email_routed'), ev_gettext('Email routed from %1$s', $structure->headers['from']));
 708                      }
 709                  }
 710              } else {
 711                  $res = 1;
 712              }
 713          }
 714  
 715          if ($res > 0) {
 716              // need to delete the message from the server?
 717              if (!$info['ema_leave_copy']) {
 718                  @imap_delete($mbox, $num);
 719              } else {
 720                  // mark the message as already read
 721                  @imap_setflag_full($mbox, $num, "\\Seen");
 722              }
 723          }
 724          return;
 725      }
 726  
 727  
 728      /**
 729       * Creates a new issue from an email if appropriate. Also returns if this message is related
 730       * to a previous message.
 731       *
 732       * @access  private
 733       * @param   array   $info An array of info about the email account.
 734       * @param   string  $headers The headers of the email.
 735       * @param   string  $message_body The body of the message.
 736       * @param   string  $date The date this message was sent
 737       * @param   string  $from The name and email address of the sender.
 738       * @param   string  $subject The subject of this message.
 739       * @param   array   $to An array of to addresses
 740       * @param   array   $cc An array of cc addresses
 741       * @return  array   An array of information about the message
 742       */
 743      function createIssueFromEmail($info, $headers, $message_body, $date, $from, $subject, $to, $cc)
 744      {
 745          $should_create_issue = false;
 746          $issue_id = '';
 747          $associate_email = '';
 748          $type = 'email';
 749          $parent_id = '';
 750  
 751          // we can't trust the in-reply-to from the imap c-client, so let's
 752          // try to manually parse that value from the full headers
 753          $references = Mail_API::getAllReferences($headers);
 754  
 755          $message_id = Mail_API::getMessageID($headers, $message_body);
 756  
 757          $setup = Setup::load();
 758          if (@$setup['subject_based_routing']['status'] == 'enabled') {
 759              // Look for issue ID in the subject line
 760  
 761              // look for [#XXXX] in the subject line
 762              if (preg_match("/\[#(\d+)\]( Note| BLOCKED)*/", $subject, $matches)) {
 763                  $should_create_issue = false;
 764                  $issue_id = $matches[1];
 765                  if (!Issue::exists($issue_id, false)) {
 766                      $issue_id = '';
 767                  } elseif (!empty($matches[2])) {
 768                      $type = 'note';
 769                  }
 770              } else {
 771                  $should_create_issue = true;
 772              }
 773          } else {
 774              // - if this email is a reply:
 775              if (count($references) > 0) {
 776                  foreach ($references as $reference_msg_id) {
 777                      //  -> check if the replied email exists in the database:
 778                      if (Note::exists($reference_msg_id)) {
 779                          // note exists
 780                          // get what issue it belongs too.
 781                          $issue_id = Note::getIssueByMessageID($reference_msg_id);
 782                          $should_create_issue = false;
 783                          $type = 'note';
 784                          $parent_id = Note::getIDByMessageID($reference_msg_id);
 785                          break;
 786                      } elseif ((Support::exists($reference_msg_id)) || (Issue::getIssueByRootMessageID($reference_msg_id) != false)) {
 787                          // email or issue exists
 788                          $issue_id = Support::getIssueByMessageID($reference_msg_id);
 789                          if (empty($issue_id)) {
 790                              $issue_id = Issue::getIssueByRootMessageID($reference_msg_id);
 791                          }
 792                          if (empty($issue_id)) {
 793                              // parent email isn't associated with issue.
 794                              //      --> create new issue, associate current email and replied email to this issue
 795                              $should_create_issue = true;
 796                              $associate_email = $reference_msg_id;
 797                          } else {
 798                              // parent email is associated with issue:
 799                              //      --> associate current email with existing issue
 800                              $should_create_issue = false;
 801                          }
 802                          break;
 803                      } else {
 804                          //  no matching note, email or issue:
 805                          //    => create new issue and associate current email with it
 806                          $should_create_issue = true;
 807                      }
 808                  }
 809              } else {
 810                  // - if this email is not a reply:
 811                  //  -> create new issue and associate current email with it
 812                  $should_create_issue = true;
 813              }
 814          }
 815  
 816          $sender_email = Mail_API::getEmailAddress($from);
 817          if (PEAR::isError($sender_email)) {
 818              $sender_email = 'Error Parsing Email <>';
 819          }
 820  
 821          // only create a new issue if this email is coming from a known customer
 822          if (($should_create_issue) && ($info['ema_issue_auto_creation_options']['only_known_customers'] == 'yes') &&
 823                  (Customer::hasCustomerIntegration($info['ema_prj_id']))) {
 824              list($customer_id,) = Customer::getCustomerIDByEmails($info['ema_prj_id'], array($sender_email));
 825              if (empty($customer_id)) {
 826                  $should_create_issue = false;
 827              }
 828          }
 829          // check whether we need to create a new issue or not
 830          if (($info['ema_issue_auto_creation'] == 'enabled') && ($should_create_issue) && (!Notification::isBounceMessage($sender_email))) {
 831              $options = Email_Account::getIssueAutoCreationOptions($info['ema_id']);
 832              Auth::createFakeCookie(APP_SYSTEM_USER_ID, $info['ema_prj_id']);
 833              $issue_id = Issue::createFromEmail($info['ema_prj_id'], APP_SYSTEM_USER_ID,
 834                      $from, Mime_Helper::fixEncoding($subject), $message_body, @$options['category'],
 835                      $options['priority'], @$options['users'], $date, $message_id);
 836  
 837              // add sender to authorized repliers list if they are not a real user
 838              $sender_usr_id = User::getUserIDByEmail($sender_email);
 839              if (empty($sender_usr_id)) {
 840                  Authorized_Replier::manualInsert($issue_id, $sender_email, false);
 841              }
 842              // associate any existing replied-to email with this new issue
 843              if ((!empty($associate_email)) && (!empty($reference_issue_id))) {
 844                  $reference_sup_id = Support::getIDByMessageID($associate_email);
 845                  Support::associate(APP_SYSTEM_USER_ID, $issue_id, array($reference_sup_id));
 846              }
 847  
 848  
 849              // add to and cc addresses to notification list
 850              $prj_id = Auth::getCurrentProject();
 851              $project_details = Project::getDetails($prj_id);
 852              $addresses_not_too_add = array($project_details['prj_outgoing_sender_email']);
 853  
 854              if (!empty($to)) {
 855                  $to_addresses = Mail_API::getAddressInfo($to, true);
 856                  foreach ($to_addresses as $address) {
 857                      if ((in_array($address['email'], $addresses_not_too_add)) || (!Workflow::shouldEmailAddress($prj_id, $address['email']) ||
 858                              (!Workflow::shouldAutoAddToNotificationList($prj_id)))) {
 859                          continue;
 860                      }
 861                      if (empty($address['sender_name'])) {
 862                          $recipient = $address['email'];
 863                      } else {
 864                          $recipient = Mail_API::getFormattedName($address['sender_name'], $address['email']);
 865                      }
 866                      Notification::subscribeEmail(Auth::getUserID(), $issue_id, $address['email'], Notification::getDefaultActions());
 867                      Notification::notifyAutoCreatedIssue($prj_id, $issue_id, $from, $date, $subject, $recipient);
 868                  }
 869              }
 870              if (!empty($cc)) {
 871                  $cc_addresses = Mail_API::getAddressInfo($cc, true);
 872                  foreach ($cc_addresses as $address) {
 873                      if ((in_array($address['email'], $addresses_not_too_add)) || (!Workflow::shouldEmailAddress($prj_id, $address['email']) ||
 874                              (!Workflow::shouldAutoAddToNotificationList($prj_id)))) {
 875                          continue;
 876                      }
 877                      if (empty($address['sender_name'])) {
 878                          $recipient = $address['email'];
 879                      } else {
 880                          $recipient = Mail_API::getFormattedName($address['sender_name'], $address['email']);
 881                      }
 882                      Notification::subscribeEmail(Auth::getUserID(), $issue_id, $address['email'], Notification::getDefaultActions());
 883                      Notification::notifyAutoCreatedIssue($prj_id, $issue_id, $from, $date, $subject, $recipient);
 884                  }
 885              }
 886          }
 887          // need to check crm for customer association
 888          if (!empty($from)) {
 889              $details = Email_Account::getDetails($info['ema_id']);
 890              if (Customer::hasCustomerIntegration($info['ema_prj_id'])) {
 891                  // check for any customer contact association
 892                  @list($customer_id,) = Customer::getCustomerIDByEmails($info['ema_prj_id'], array($sender_email));
 893              }
 894          }
 895          return array(
 896              'should_create_issue'   =>  $should_create_issue,
 897              'associate_email'   =>  $associate_email,
 898              'issue_id'  =>  $issue_id,
 899              'customer_id'   =>  @$customer_id,
 900              'type'      =>  $type,
 901              'parent_id' =>  $parent_id
 902          );
 903      }
 904  
 905  
 906      /**
 907       * Method used to close the existing connection to the email
 908       * server.
 909       *
 910       * @access  public
 911       * @param   resource $mbox The mailbox
 912       * @return  void
 913       */
 914      function closeEmailServer($mbox)
 915      {
 916          @imap_close($mbox);
 917      }
 918  
 919  
 920      /**
 921       * Builds a list of all distinct message-ids available in the provided
 922       * email account.
 923       *
 924       * @access  public
 925       * @param   integer $ema_id The support email account ID
 926       * @return  array The list of message-ids
 927       */
 928      function getMessageIDs($ema_id)
 929      {
 930          $stmt = "SELECT
 931                      DISTINCT sup_message_id
 932                   FROM
 933                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
 934                   WHERE
 935                      sup_ema_id=" . Misc::escapeInteger($ema_id);
 936          $res = $GLOBALS["db_api"]->dbh->getCol($stmt);
 937          if (PEAR::isError($res)) {
 938              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 939              return array();
 940          } else {
 941              return $res;
 942          }
 943      }
 944  
 945  
 946      /**
 947       * Checks if a message already is downloaded.
 948       *
 949       * @access  public
 950       * @param   string $message_id The Message-ID header
 951       * @return  boolean
 952       */
 953      function exists($message_id)
 954      {
 955          $sql = "SELECT
 956                      count(*)
 957                  FROM
 958                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
 959                  WHERE
 960                      sup_message_id = '" . Misc::escapeString($message_id) . "'";
 961          $res = $GLOBALS["db_api"]->dbh->getOne($sql);
 962          if (PEAR::isError($res)) {
 963              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 964              return false;
 965          }
 966          if ($res > 0) {
 967              return true;
 968          } else {
 969              return false;
 970          }
 971      }
 972  
 973  
 974      /**
 975       * Method used to add a new support email to the system.
 976       *
 977       * @access  public
 978       * @param   array $row The support email details
 979       * @param   object $structure The email structure object
 980       * @param   integer $sup_id The support ID to be passed out
 981       * @param   boolean $closing If this email comes from closing the issue
 982       * @return  integer 1 if the insert worked, -1 otherwise
 983       */
 984      function insertEmail($row, &$structure, &$sup_id, $closing = false)
 985      {
 986          // get usr_id from FROM header
 987          $usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($row['from']));
 988          if (!empty($usr_id) && !empty($row["customer_id"])) {
 989              $row["customer_id"] = User::getCustomerID($usr_id);
 990          }
 991          if (empty($row['customer_id'])) {
 992              $row['customer_id'] = "NULL";
 993          }
 994  
 995          // try to get the parent ID
 996          $reference_message_id = Mail_API::getReferenceMessageID($row['full_email']);
 997          $parent_id = '';
 998          if (!empty($reference_message_id)) {
 999              $parent_id = Support::getIDByMessageID($reference_message_id);
1000              // make sure it is in the same issue
1001              if ((!empty($parent_id)) && ((empty($row['issue_id'])) || (@$row['issue_id'] != Support::getIssueFromEmail($parent_id)))) {
1002                  $parent_id = '';
1003              }
1004          }
1005  
1006          $stmt = "INSERT INTO
1007                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
1008                   (
1009                      sup_ema_id,";
1010          if (!empty($parent_id)) {
1011              $stmt .= "\nsup_parent_id,";
1012          }
1013          $stmt .= "
1014                      sup_iss_id,";
1015          if (!empty($usr_id)) {
1016              $stmt .= "\nsup_usr_id,\n";
1017          }
1018          $stmt .= "  sup_customer_id,
1019                      sup_message_id,
1020                      sup_date,
1021                      sup_from,
1022                      sup_to,
1023                      sup_cc,
1024                      sup_subject,
1025                      sup_has_attachment
1026                   ) VALUES (
1027                      " . Misc::escapeInteger($row["ema_id"]) . ",\n";
1028          if (!empty($parent_id)) {
1029              $stmt .= "$parent_id,\n";
1030          }
1031          $stmt .=    Misc::escapeInteger($row["issue_id"]) . ",";
1032          if (!empty($usr_id)) {
1033              $stmt .= "\n$usr_id,\n";
1034          }
1035          $stmt .= "
1036                      " . Misc::escapeInteger($row["customer_id"]) . ",
1037                      '" . Misc::escapeString($row["message_id"]) . "',
1038                      '" . Misc::escapeString($row["date"]) . "',
1039                      '" . Misc::escapeString($row["from"]) . "',
1040                      '" . Misc::escapeString(@$row["to"]) . "',
1041                      '" . Misc::escapeString(@$row["cc"]) . "',
1042                      '" . Misc::escapeString($row["subject"]) . "',
1043                      '" . Misc::escapeString($row["has_attachment"]) . "'
1044                   )";
1045          $res = $GLOBALS["db_api"]->dbh->query($stmt);
1046          if (PEAR::isError($res)) {
1047              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1048              return -1;
1049          } else {
1050              $new_sup_id = $GLOBALS["db_api"]->get_last_insert_id();
1051              $sup_id = $new_sup_id;
1052              // now add the body and full email to the separate table
1053              $stmt = "INSERT INTO
1054                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body
1055                       (
1056                          seb_sup_id,
1057                          seb_body,
1058                          seb_full_email
1059                       ) VALUES (
1060                          $new_sup_id,
1061                          '" . Misc::escapeString($row["body"]) . "',
1062                          '" . Misc::escapeString($row["full_email"]) . "'
1063                       )";
1064              $res = $GLOBALS["db_api"]->dbh->query($stmt);
1065              if (PEAR::isError($res)) {
1066                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1067                  return -1;
1068              } else {
1069                  Workflow::handleNewEmail(Email_Account::getProjectID($row["ema_id"]), @$row["issue_id"], $structure, $row, $closing);
1070                  return 1;
1071              }
1072          }
1073      }
1074  
1075  
1076      /**
1077       * Method used to get a specific parameter in the email listing
1078       * cookie.
1079       *
1080       * @access  public
1081       * @param   string $name The name of the parameter
1082       * @return  mixed The value of the specified parameter
1083       */
1084      function getParam($name)
1085      {
1086          if (isset($_GET[$name])) {
1087              return $_GET[$name];
1088          } elseif (isset($_POST[$name])) {
1089              return $_POST[$name];
1090          } elseif (($profile = Search_Profile::getProfile(Auth::getUserID(), Auth::getCurrentProject(), 'email')) && (isset($profile[$name]))) {
1091              return $profile[$name];
1092          } else {
1093              return "";
1094          }
1095      }
1096  
1097  
1098      /**
1099       * Method used to save the current search parameters in a cookie.
1100       *
1101       * @access  public
1102       * @return  array The search parameters
1103       */
1104      function saveSearchParams()
1105      {
1106          $sort_by = Support::getParam('sort_by');
1107          $sort_order = Support::getParam('sort_order');
1108          $rows = Support::getParam('rows');
1109          $cookie = array(
1110              'rows'             => $rows ? $rows : APP_DEFAULT_PAGER_SIZE,
1111              'pagerRow'         => Support::getParam('pagerRow'),
1112              'hide_associated'  => Support::getParam('hide_associated'),
1113              "sort_by"          => $sort_by ? $sort_by : "sup_date",
1114              "sort_order"       => $sort_order ? $sort_order : "DESC",
1115              // quick filter form options
1116              'keywords'         => Support::getParam('keywords'),
1117              'sender'           => Support::getParam('sender'),
1118              'to'               => Support::getParam('to'),
1119              'ema_id'           => Support::getParam('ema_id'),
1120              'filter'           => Support::getParam('filter')
1121          );
1122          // now do some magic to properly format the date fields
1123          $date_fields = array(
1124              'arrival_date'
1125          );
1126          foreach ($date_fields as $field_name) {
1127              $field = Support::getParam($field_name);
1128              if ((empty($field)) || ($cookie['filter'][$field_name] != 'yes')) {
1129                  continue;
1130              }
1131              $end_field_name = $field_name . '_end';
1132              $end_field = Support::getParam($end_field_name);
1133              @$cookie[$field_name] = array(
1134                  'Year'        => $field['Year'],
1135                  'Month'       => $field['Month'],
1136                  'Day'         => $field['Day'],
1137                  'start'       => $field['Year'] . '-' . $field['Month'] . '-' . $field['Day'],
1138                  'filter_type' => $field['filter_type'],
1139                  'end'         => $end_field['Year'] . '-' . $end_field['Month'] . '-' . $end_field['Day']
1140              );
1141              @$cookie[$end_field_name] = array(
1142                  'Year'        => $end_field['Year'],
1143                  'Month'       => $end_field['Month'],
1144                  'Day'         => $end_field['Day']
1145              );
1146          }
1147          Search_Profile::save(Auth::getUserID(), Auth::getCurrentProject(), 'email', $cookie);
1148          return $cookie;
1149      }
1150  
1151  
1152      /**
1153       * Method used to get the current sorting options used in the grid
1154       * layout of the emails listing page.
1155       *
1156       * @access  public
1157       * @param   array $options The current search parameters
1158       * @return  array The sorting options
1159       */
1160      function getSortingInfo($options)
1161      {
1162          $fields = array(
1163              "sup_from",
1164              "sup_customer_id",
1165              "sup_date",
1166              "sup_to",
1167              "sup_iss_id",
1168              "sup_subject"
1169          );
1170          $items = array(
1171              "links"  => array(),
1172              "images" => array()
1173          );
1174          for ($i = 0; $i < count($fields); $i++) {
1175              if ($options["sort_by"] == $fields[$i]) {
1176                  $items["images"][$fields[$i]] = "images/" . strtolower($options["sort_order"]) . ".gif";
1177                  if (strtolower($options["sort_order"]) == "asc") {
1178                      $sort_order = "desc";
1179                  } else {
1180                      $sort_order = "asc";
1181                  }
1182                  $items["links"][$fields[$i]] = $_SERVER["PHP_SELF"] . "?sort_by=" . $fields[$i] . "&sort_order=" . $sort_order;
1183              } else {
1184                  $items["links"][$fields[$i]] = $_SERVER["PHP_SELF"] . "?sort_by=" . $fields[$i] . "&sort_order=asc";
1185              }
1186          }
1187          return $items;
1188      }
1189  
1190  
1191      /**
1192       * Method used to get the list of emails to be displayed in the
1193       * grid layout.
1194       *
1195       * @access  public
1196       * @param   array $options The search parameters
1197       * @param   integer $current_row The current page number
1198       * @param   integer $max The maximum number of rows per page
1199       * @return  array The list of issues to be displayed
1200       */
1201      function getEmailListing($options, $current_row = 0, $max = 5)
1202      {
1203          $prj_id = Auth::getCurrentProject();
1204          $usr_id = Auth::getUserID();
1205          if ($max == "ALL") {
1206              $max = 9999999;
1207          }
1208          $start = $current_row * $max;
1209  
1210          $stmt = "SELECT
1211                      sup_id,
1212                      sup_ema_id,
1213                      sup_iss_id,
1214                      sup_customer_id,
1215                      sup_from,
1216                      sup_date,
1217                      sup_to,
1218                      sup_subject,
1219                      sup_has_attachment
1220                   FROM
1221                      (
1222                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
1223                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "email_account";
1224          if (!empty($options['keywords'])) {
1225              $stmt .= "," . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body";
1226          }
1227          $stmt .= "
1228                      )
1229                      LEFT JOIN
1230                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
1231                      ON
1232                          sup_iss_id = iss_id";
1233          $stmt .= Support::buildWhereClause($options);
1234          $stmt .= "
1235                   ORDER BY
1236                      " . Misc::escapeString($options["sort_by"]) . " " . Misc::escapeString($options["sort_order"]);
1237          $total_rows = Pager::getTotalRows($stmt);
1238          $stmt .= "
1239                   LIMIT
1240                      " . Misc::escapeInteger($start) . ", " . Misc::escapeInteger($max);
1241          $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
1242          if (PEAR::isError($res)) {
1243              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1244              return array(
1245                  "list" => "",
1246                  "info" => ""
1247              );
1248          } else {
1249              if ((count($res) < 1) && ($current_row > 0)) {
1250                  // if there are no results, and the page is not the first page reset page to one and reload results
1251                  Auth::redirect(APP_RELATIVE_URL . "emails.php?pagerRow=0&rows=$max");
1252              }
1253              if (Customer::hasCustomerIntegration($prj_id)) {
1254                  $customer_ids = array();
1255                  for ($i = 0; $i < count($res); $i++) {
1256                      if ((!empty($res[$i]['sup_customer_id'])) && (!in_array($res[$i]['sup_customer_id'], $customer_ids))) {
1257                          $customer_ids[] = $res[$i]['sup_customer_id'];
1258                      }
1259                  }
1260                  if (count($customer_ids) > 0) {
1261                      $company_titles = Customer::getTitles($prj_id, $customer_ids);
1262                  }
1263              }
1264              for ($i = 0; $i < count($res); $i++) {
1265                  $res[$i]["sup_date"] = Date_API::getFormattedDate($res[$i]["sup_date"]);
1266                  $res[$i]["sup_subject"] = Mime_Helper::fixEncoding($res[$i]["sup_subject"]);
1267                  $res[$i]["sup_from"] = join(', ', Mail_API::getName($res[$i]["sup_from"], true));
1268                  if ((empty($res[$i]["sup_to"])) && (!empty($res[$i]["sup_iss_id"]))) {
1269                      $res[$i]["sup_to"] = "Notification List";
1270                  } else {
1271                      $to = Mail_API::getName($res[$i]["sup_to"]);
1272                      # FIXME: just ignore the unformattable header?
1273                      if (PEAR::isError($to)) {
1274                          Error_Handler::logError(array($to->getMessage(), 'sup_id' . $res[$i]['sup_id'] . "\n" . $res[$i]["sup_to"] . "\n" . $to->getDebugInfo()), __FILE__, __LINE__);
1275                      } else {
1276                          $res[$i]['sup_to'] = Mime_Helper::fixEncoding($to);
1277                      }
1278                  }
1279                  if (Customer::hasCustomerIntegration($prj_id)) {
1280                      @$res[$i]['customer_title'] = $company_titles[$res[$i]['sup_customer_id']];
1281                  }
1282              }
1283              $total_pages = ceil($total_rows / $max);
1284              $last_page = $total_pages - 1;
1285              return array(
1286                  "list" => $res,
1287                  "info" => array(
1288                      "current_page"  => $current_row,
1289                      "start_offset"  => $start,
1290                      "end_offset"    => $start + count($res),
1291                      "total_rows"    => $total_rows,
1292                      "total_pages"   => $total_pages,
1293                      "previous_page" => ($current_row == 0) ? "-1" : ($current_row - 1),
1294                      "next_page"     => ($current_row == $last_page) ? "-1" : ($current_row + 1),
1295                      "last_page"     => $last_page
1296                  )
1297              );
1298          }
1299      }
1300  
1301  
1302      /**
1303       * Method used to get the list of emails to be displayed in the grid layout.
1304       *
1305       * @access  public
1306       * @param   array $options The search parameters
1307       * @return  string The where clause
1308       */
1309      function buildWhereClause($options)
1310      {
1311          $stmt = "
1312                   WHERE
1313                      sup_removed=0 AND
1314                      sup_ema_id=ema_id AND
1315                      ema_prj_id=" . Auth::getCurrentProject();
1316          if (!empty($options["hide_associated"])) {
1317              $stmt .= " AND sup_iss_id = 0";
1318          }
1319          if (!empty($options['keywords'])) {
1320              $stmt .= " AND sup_id=seb_sup_id ";
1321              $stmt .= " AND (" . Misc::prepareBooleanSearch('sup_subject', $options["keywords"]);
1322              $stmt .= " OR " . Misc::prepareBooleanSearch('seb_body', $options["keywords"]) . ")";
1323          }
1324          if (!empty($options['sender'])) {
1325              $stmt .= " AND " . Misc::prepareBooleanSearch('sup_from', $options["sender"]);
1326          }
1327          if (!empty($options['to'])) {
1328              $stmt .= " AND " . Misc::prepareBooleanSearch('sup_to', $options["to"]);
1329          }
1330          if (!empty($options['ema_id'])) {
1331              $stmt .= " AND sup_ema_id=" . $options['ema_id'];
1332          }
1333          if ((!empty($options['filter'])) && ($options['filter']['arrival_date'] == 'yes')) {
1334              switch ($options['arrival_date']['filter_type']) {
1335                  case 'greater':
1336                      $stmt .= " AND sup_date >= '" . $options['arrival_date']['start'] . "'";
1337                      break;
1338                  case 'less':
1339                      $stmt .= " AND sup_date <= '" . $options['arrival_date']['start'] . "'";
1340                      break;
1341                  case 'between':
1342                      $stmt .= " AND sup_date BETWEEN '" . $options['arrival_date']['start'] . "' AND '" . $options['arrival_date']['end'] . "'";
1343                      break;
1344              }
1345          }
1346  
1347          // handle 'private' issues.
1348          if (Auth::getCurrentRole() < User::getRoleID("Manager")) {
1349              $stmt .= " AND (iss_private = 0 OR iss_private IS NULL)";
1350          }
1351          return $stmt;
1352      }
1353  
1354  
1355      /**
1356       * Method used to extract and associate attachments in an email
1357       * to the given issue.
1358       *
1359       * @access  public
1360       * @param   integer $issue_id The issue ID
1361       * @param   mixed   $input The full body of the message or decoded email.
1362       * @param   boolean $internal_only Whether these files are supposed to be internal only or not
1363       * @param   integer $associated_note_id The note ID that these attachments should be associated with
1364       * @return  void
1365       */
1366      function extractAttachments($issue_id, $input, $internal_only = false, $associated_note_id = false)
1367      {
1368          if (!is_object($input)) {
1369              $input = Mime_Helper::decode($input, true, true);
1370          }
1371  
1372          // figure out who should be the 'owner' of this attachment
1373          $sender_email = strtolower(Mail_API::getEmailAddress($input->headers['from']));
1374          $usr_id = User::getUserIDByEmail($sender_email);
1375          $unknown_user = false;
1376          if (empty($usr_id)) {
1377              $prj_id = Issue::getProjectID($issue_id);
1378              if (Customer::hasCustomerIntegration($prj_id)) {
1379                  // try checking if a customer technical contact has this email associated with it
1380                  list(,$contact_id) = Customer::getCustomerIDByEmails($prj_id, array($sender_email));
1381                  if (!empty($contact_id)) {
1382                      $usr_id = User::getUserIDByContactID($contact_id);
1383                  }
1384              }
1385              if (empty($usr_id)) {
1386                  // if we couldn't find a real customer by that email, set the usr_id to be the system user id,
1387                  // and store the actual email address in the unknown_user field.
1388                  $usr_id = APP_SYSTEM_USER_ID;
1389                  $unknown_user = $input->headers['from'];
1390              }
1391          }
1392          // now for the real thing
1393          $attachments = Mime_Helper::getAttachments($input);
1394          if (count($attachments) > 0) {
1395              if (empty($associated_note_id)) {
1396                  $history_log = ev_gettext("Attachment originated from an email");
1397              } else {
1398                  $history_log = ev_gettext("Attachment originated from a note");
1399              }
1400              $attachment_id = Attachment::add($issue_id, $usr_id, $history_log, $internal_only, $unknown_user, $associated_note_id);
1401              for ($i = 0; $i < count($attachments); $i++) {
1402                  Attachment::addFile($attachment_id, $attachments[$i]['filename'], $attachments[$i]['filetype'], $attachments[$i]['blob']);
1403              }
1404              // mark the note as having attachments (poor man's caching system)
1405              if ($associated_note_id != false) {
1406                  Note::setAttachmentFlag($associated_note_id);
1407              }
1408          }
1409      }
1410  
1411  
1412      /**
1413       * Method used to silently associate a support email with an
1414       * existing issue.
1415       *
1416       * @access  public
1417       * @param   integer $usr_id The user ID of the person performing this change
1418       * @param   integer $issue_id The issue ID
1419       * @param   array $items The list of email IDs to associate
1420       * @return  integer 1 if it worked, -1 otherwise
1421       */
1422      function associateEmail($usr_id, $issue_id, $items)
1423      {
1424          $stmt = "UPDATE
1425                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
1426                   SET
1427                      sup_iss_id=$issue_id
1428                   WHERE
1429                      sup_id IN (" . @implode(", ", Misc::escapeInteger($items)) . ")";
1430          $res = $GLOBALS["db_api"]->dbh->query($stmt);
1431          if (PEAR::isError($res)) {
1432              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1433              return -1;
1434          } else {
1435              for ($i = 0; $i < count($items); $i++) {
1436                  $full_email = Support::getFullEmail($items[$i]);
1437                  Support::extractAttachments($issue_id, $full_email);
1438              }
1439              Issue::markAsUpdated($issue_id, "email");
1440              // save a history entry for each email being associated to this issue
1441              $stmt = "SELECT
1442                          sup_subject
1443                       FROM
1444                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
1445                       WHERE
1446                          sup_id IN (" . @implode(", ", Misc::escapeInteger($items)) . ")";
1447              $res = $GLOBALS["db_api"]->dbh->getCol($stmt);
1448              for ($i = 0; $i < count($res); $i++) {
1449                  History::add($issue_id, $usr_id, History::getTypeID('email_associated'),
1450                         ev_gettext('Email (subject: \'%1$s\') associated by %2$s', $res[$i], User::getFullName($usr_id)));
1451              }
1452              return 1;
1453          }
1454      }
1455  
1456  
1457      /**
1458       * Method used to associate a support email with an existing
1459       * issue.
1460       *
1461       * @access  public
1462       * @param   integer $usr_id The user ID of the person performing this change
1463       * @param   integer $issue_id The issue ID
1464       * @param   array $items The list of email IDs to associate
1465       * @param   boolean $authorize If the senders should be added the authorized repliers list
1466       * @return  integer 1 if it worked, -1 otherwise
1467       */
1468      function associate($usr_id, $issue_id, $items, $authorize = false, $add_recipients_to_nl = false)
1469      {
1470          $res = Support::associateEmail($usr_id, $issue_id, $items);
1471          if ($res == 1) {
1472              $stmt = "SELECT
1473                          sup_id,
1474                          seb_full_email
1475                       FROM
1476                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
1477                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body
1478                       WHERE
1479                          sup_id=seb_sup_id AND
1480                          sup_id IN (" . @implode(", ", Misc::escapeInteger($items)) . ")";
1481              $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
1482              for ($i = 0; $i < count($res); $i++) {
1483                  // since downloading email should make the emails 'public', send 'false' below as the 'internal_only' flag
1484                  $structure = Mime_Helper::decode($res[$i]['seb_full_email'], true, false);
1485                  if (Mime_Helper::hasAttachments($structure)) {
1486                      $has_attachments = 1;
1487                  } else {
1488                      $has_attachments = 0;
1489                  }
1490                  $t = array(
1491                      'issue_id'       => $issue_id,
1492                      'message_id'     => @$structure->headers['message-id'],
1493                      'from'           => @$structure->headers['from'],
1494                      'to'             => @$structure->headers['to'],
1495                      'cc'             => @$structure->headers['cc'],
1496                      'subject'        => @$structure->headers['subject'],
1497                      'body'           => Mime_Helper::getMessageBody($structure),
1498                      'full_email'     => $res[$i]['seb_full_email'],
1499                      'has_attachment' => $has_attachments,
1500                      // the following items are not inserted, but useful in some methods
1501                      'headers'        => @$structure->headers
1502                  );
1503                  Notification::notifyNewEmail($usr_id, $issue_id, $t, false, false, '', $res[$i]['sup_id']);
1504                  if ($authorize) {
1505                      Authorized_Replier::manualInsert($issue_id, Mail_API::getEmailAddress(@$structure->headers['from']), false);
1506                  }
1507              }
1508              return 1;
1509          } else {
1510              return -1;
1511          }
1512      }
1513  
1514  
1515      /**
1516       * Method used to get the support email entry details.
1517       *
1518       * @access  public
1519       * @param   integer $ema_id The support email account ID
1520       * @param   integer $sup_id The support email ID
1521       * @return  array The email entry details
1522       */
1523      function getEmailDetails($ema_id, $sup_id)
1524      {
1525          $stmt = "SELECT
1526                      " . APP_TABLE_PREFIX . "support_email.*,
1527                      " . APP_TABLE_PREFIX . "support_email_body.*
1528                   FROM
1529                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
1530                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body
1531                   WHERE
1532                      sup_id=seb_sup_id AND
1533                      sup_id=" . Misc::escapeInteger($sup_id) . " AND
1534                      sup_ema_id=" . Misc::escapeInteger($ema_id);
1535          $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC);
1536          if (PEAR::isError($res)) {
1537              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1538              return "";
1539          } else {
1540              $res["attachments"] = Mime_Helper::getAttachmentCIDs($res["seb_full_email"]);
1541              $res["timestamp"] = Date_API::getUnixTimestamp($res['sup_date'], 'GMT');
1542              $res["sup_date"] = Date_API::getFormattedDate($res["sup_date"]);
1543              $res["sup_subject"] = Mime_Helper::fixEncoding($res["sup_subject"]);
1544              $res['reply_subject'] = Mail_API::removeExcessRe('Re: ' . $res["sup_subject"], true);
1545              $res["sup_from"] = Mime_Helper::fixEncoding($res["sup_from"]);
1546              $res["sup_to"] = Mime_Helper::fixEncoding($res["sup_to"]);
1547  
1548              if (!empty($res['sup_iss_id'])) {
1549                  $res['reply_subject'] = Mail_API::formatSubject($res['sup_iss_id'], $res['reply_subject']);
1550              }
1551  
1552              return $res;
1553          }
1554      }
1555  
1556  
1557      /**
1558       * Returns the nth note for a specific issue. The sequence starts at 1.
1559       *
1560       * @access  public
1561       * @param   integer $issue_id The id of the issue.
1562       * @param   integer $sequence The sequential number of the email.
1563       * @return  array An array of data containing details about the email.
1564       */
1565      function getEmailBySequence($issue_id, $sequence)
1566      {
1567          $stmt = "SELECT
1568                      sup_id,
1569                      sup_ema_id
1570                  FROM
1571                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
1572                  WHERE
1573                      sup_iss_id = " . Misc::escapeInteger($issue_id) . "
1574                  ORDER BY
1575                      sup_id
1576                  LIMIT " . (Misc::escapeInteger($sequence) - 1) . ", 1";
1577          $res = $GLOBALS["db_api"]->dbh->getRow($stmt);
1578          if (PEAR::isError($res)) {
1579              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1580              return array();
1581          } else if (count($res) < 1) {
1582              return array();
1583          } else {
1584              return Support::getEmailDetails($res[1], $res[0]);
1585          }
1586      }
1587  
1588  
1589      /**
1590       * Method used to get the list of support emails associated with
1591       * a given set of issues.
1592       *
1593       * @access  public
1594       * @param   array $items List of issues
1595       * @return  array The list of support emails
1596       */
1597      function getListDetails($items)
1598      {
1599          $items = @implode(", ", Misc::escapeInteger($items));
1600          $stmt = "SELECT
1601                      sup_id,
1602                      sup_from,
1603                      sup_subject
1604                   FROM
1605                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
1606                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "email_account
1607                   WHERE
1608                      ema_id=sup_ema_id AND
1609                      ema_prj_id=" . Auth::getCurrentProject() . " AND
1610                      sup_id IN ($items)";
1611          $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
1612          if (PEAR::isError($res)) {
1613              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1614              return "";
1615          } else {
1616              for ($i = 0; $i < count($res); $i++) {
1617                  $res[$i]["sup_subject"] = Mime_Helper::fixEncoding($res[$i]["sup_subject"]);
1618                  $res[$i]["sup_from"] = Mime_Helper::fixEncoding($res[$i]["sup_from"]);
1619              }
1620              return $res;
1621          }
1622      }
1623  
1624  
1625      /**
1626       * Method used to get the full email message for a given support
1627       * email ID.
1628       *
1629       * @access  public
1630       * @param   integer $sup_id The support email ID
1631       * @return  string The full email message
1632       */
1633      function getFullEmail($sup_id)
1634      {
1635          $stmt = "SELECT
1636                      seb_full_email
1637                   FROM
1638                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body
1639                   WHERE
1640                      seb_sup_id=" . Misc::escapeInteger($sup_id);
1641          $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
1642          if (PEAR::isError($res)) {
1643              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1644              return "";
1645          } else {
1646              return $res;
1647          }
1648      }
1649  
1650  
1651      /**
1652       * Method used to get the email message for a given support
1653       * email ID.
1654       *
1655       * @access  public
1656       * @param   integer $sup_id The support email ID
1657       * @return  string The email message
1658       */
1659      function getEmail($sup_id)
1660      {
1661          $stmt = "SELECT
1662                      seb_body
1663                   FROM
1664                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body
1665                   WHERE
1666                      seb_sup_id=" . Misc::escapeInteger($sup_id);
1667          $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
1668          if (PEAR::isError($res)) {
1669              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1670              return "";
1671          } else {
1672              return $res;
1673          }
1674      }
1675  
1676  
1677      /**
1678       * Method used to get all of the support email entries associated
1679       * with a given issue.
1680       *
1681       * @access  public
1682       * @param   integer $issue_id The issue ID
1683       * @return  array The list of support emails
1684       */
1685      function getEmailsByIssue($issue_id)
1686      {
1687          $usr_id = Auth::getUserID();
1688          $stmt = "SELECT
1689                      sup_id,
1690                      sup_ema_id,
1691                      sup_from,
1692                      sup_to,
1693                      sup_cc,
1694                      sup_date,
1695                      sup_subject,
1696                      seb_body,
1697                      sup_has_attachment,
1698                      CONCAT(sup_ema_id, '-', sup_id) AS composite_id
1699                   FROM
1700                      (
1701                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
1702                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email_body,
1703                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "email_account,
1704                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
1705                      )
1706                   WHERE
1707                      sup_id=seb_sup_id AND
1708                      ema_id=sup_ema_id AND
1709                      iss_id = sup_iss_id AND
1710                      ema_prj_id=iss_prj_id AND
1711                      sup_iss_id=" . Misc::escapeInteger($issue_id) . "
1712                   ORDER BY
1713                      sup_id ASC";
1714          $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
1715          if (PEAR::isError($res)) {
1716              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1717              return "";
1718          } else {
1719              if (count($res) == 0) {
1720                  return "";
1721              } else {
1722                  for ($i = 0; $i < count($res); $i++) {
1723                      $res[$i]["sup_date"] = Date_API::getFormattedDate($res[$i]["sup_date"]);
1724                      $res[$i]["sup_subject"] = Mime_Helper::fixEncoding($res[$i]["sup_subject"]);
1725                      $res[$i]["sup_from"] = Mime_Helper::fixEncoding($res[$i]["sup_from"]);
1726                      $res[$i]["sup_to"] = Mime_Helper::fixEncoding($res[$i]["sup_to"]);
1727                      $res[$i]["sup_cc"] = Mime_Helper::fixEncoding($res[$i]["sup_cc"]);
1728                  }
1729                  return $res;
1730              }
1731          }
1732      }
1733  
1734  
1735      /**
1736       * Method used to update all of the selected support emails as
1737       * 'removed' ones.
1738       *
1739       * @access  public
1740       * @return  integer 1 if it worked, -1 otherwise
1741       */
1742      function removeEmails()
1743      {
1744          $items = @implode(", ", Misc::escapeInteger($_POST["item"]));
1745          $stmt = "UPDATE
1746                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
1747                   SET
1748                      sup_removed=1
1749                   WHERE
1750                      sup_id IN ($items)";
1751          $res = $GLOBALS["db_api"]->dbh->query($stmt);
1752          if (PEAR::isError($res)) {
1753              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1754              return -1;
1755          } else {
1756              return 1;
1757          }
1758      }
1759  
1760  
1761      /**
1762       * Method used to remove the association of all support emails
1763       * for a given issue.
1764       *
1765       * @access  public
1766       * @return  integer 1 if it worked, -1 otherwise
1767       */
1768      function removeAssociation()
1769      {
1770          $items = @implode(", ", Misc::escapeInteger($_POST["item"]));
1771          $stmt = "SELECT
1772                      sup_iss_id
1773                   FROM
1774                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
1775                   WHERE
1776                      sup_id IN ($items)";
1777          $issue_id = $GLOBALS["db_api"]->dbh->getOne($stmt);
1778  
1779          $stmt = "UPDATE
1780                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
1781                   SET
1782                      sup_iss_id=0
1783                   WHERE
1784                      sup_id IN ($items)";
1785          $res = $GLOBALS["db_api"]->dbh->query($stmt);
1786          if (PEAR::isError($res)) {
1787              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1788              return -1;
1789          } else {
1790              Issue::markAsUpdated($issue_id);
1791              // save a history entry for each email being associated to this issue
1792              $stmt = "SELECT
1793                          sup_id,
1794                          sup_subject
1795                       FROM
1796                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
1797                       WHERE
1798                          sup_id IN ($items)";
1799              $subjects = $GLOBALS["db_api"]->dbh->getAssoc($stmt);
1800              for ($i = 0; $i < count($_POST["item"]); $i++) {
1801                  History::add($issue_id, Auth::getUserID(), History::getTypeID('email_disassociated'),
1802                                  ev_gettext('Email (subject: \'%1$s\') disassociated by %2$s', $subjects[$_POST["item"][$i]], User::getFullName(Auth::getUserID())));
1803              }
1804              return 1;
1805          }
1806      }
1807  
1808  
1809      /**
1810       * Checks whether the given email address is allowed to send emails in the
1811       * issue ID.
1812       *
1813       * @access  public
1814       * @param   integer $issue_id The issue ID
1815       * @param   string $sender_email The email address
1816       * @return  boolean
1817       */
1818      function isAllowedToEmail($issue_id, $sender_email)
1819      {
1820          $prj_id = Issue::getProjectID($issue_id);
1821  
1822          // check the workflow
1823          $workflow_can_email = Workflow::canEmailIssue($prj_id, $issue_id, $sender_email);
1824          if ($workflow_can_email != null) {
1825              return $workflow_can_email;
1826          }
1827  
1828          $is_allowed = true;
1829          $sender_usr_id = User::getUserIDByEmail($sender_email);
1830          if (empty($sender_usr_id)) {
1831              if (Customer::hasCustomerIntegration($prj_id)) {
1832                  // check for a customer contact with several email addresses
1833                  $customer_id = Issue::getCustomerID($issue_id);
1834                  $contact_emails = array_keys(Customer::getContactEmailAssocList($prj_id, $customer_id));
1835                  $contact_emails = array_map('strtolower', $contact_emails);
1836                  if ((!in_array(strtolower($sender_email), $contact_emails)) &&
1837                          (!Authorized_Replier::isAuthorizedReplier($issue_id, $sender_email))) {
1838                      $is_allowed = false;
1839                  }
1840              } else {
1841                  if (!Authorized_Replier::isAuthorizedReplier($issue_id, $sender_email)) {
1842                      $is_allowed = false;
1843                  }
1844              }
1845          } else {
1846              // check if this user is not a customer and
1847              // also not in the assignment list for the current issue and
1848              // also not in the authorized repliers list
1849              // also not the reporter
1850              $details = Issue::getDetails($issue_id);
1851              if (!Issue::canAccess($issue_id, $sender_usr_id)) {
1852                  $is_allowed = false;
1853              } if (($sender_usr_id != $details['iss_usr_id']) &&
1854                      (!Authorized_Replier::isUserAuthorizedReplier($issue_id, $sender_usr_id)) &&
1855                      (!Issue::isAssignedToUser($issue_id, $sender_usr_id)) &&
1856                      (User::getRoleByUser($sender_usr_id, Issue::getProjectID($issue_id)) != User::getRoleID('Customer'))) {
1857                  $is_allowed = false;
1858              } elseif ((User::getRoleByUser($sender_usr_id, Issue::getProjectID($issue_id)) == User::getRoleID('Customer')) &&
1859                      (User::getCustomerID($sender_usr_id) != Issue::getCustomerID($issue_id))) {
1860                  $is_allowed = false;
1861              }
1862          }
1863          return $is_allowed;
1864      }
1865  
1866  
1867      /**
1868       * Method used to build the headers of a web-based message.
1869       *
1870       * @access  public
1871       * @param   integer $issue_id The issue ID
1872       * @param   string $message_id The message-id
1873       * @param   string $from The sender of this message
1874       * @param   string $to The primary recipient of this message
1875       * @param   string $cc The extra recipients of this message
1876       * @param   string $body The message body
1877       * @param   string $in_reply_to The message-id that we are replying to
1878       * @return  string The full email
1879       */
1880      function buildFullHeaders($issue_id, $message_id, $from, $to, $cc, $subject, $body, $in_reply_to)
1881      {
1882          // hack needed to get the full headers of this web-based email
1883          $mail = new Mail_API;
1884          $mail->setTextBody($body);
1885          if (!empty($issue_id)) {
1886              $mail->setHeaders(array("Message-Id" => $message_id));
1887          } else {
1888              $issue_id = 0;
1889          }
1890  
1891          // if there is no existing in-reply-to header, get the root message for the issue
1892          if (($in_reply_to == false) && (!empty($issue_id))) {
1893              $in_reply_to = Issue::getRootMessageID($issue_id);
1894          }
1895  
1896          if ($in_reply_to) {
1897              $mail->setHeaders(array("In-Reply-To" => $in_reply_to));
1898          }
1899          $cc = trim($cc);
1900          if (!empty($cc)) {
1901              $cc = str_replace(",", ";", $cc);
1902              $ccs = explode(";", $cc);
1903              for ($i = 0; $i < count($ccs); $i++) {
1904                  if (!empty($ccs[$i])) {
1905                      $mail->addCc($ccs[$i]);
1906                  }
1907              }
1908          }
1909          return $mail->getFullHeaders($from, $to, $subject);
1910      }
1911  
1912  
1913      /**
1914       * Method used to send emails directly from the sender to the
1915       * recipient. This will not re-write the sender's email address
1916       * to issue-xxxx@ or whatever.
1917       *
1918       * @access  public
1919       * @param   integer $issue_id The issue ID
1920       * @param   string $from The sender of this message
1921       * @param   string $to The primary recipient of this message
1922       * @param   string $cc The extra recipients of this message
1923       * @param   string $subject The subject of this message
1924       * @param   string $body The message body
1925       * @param   string $message_id The message-id
1926       * @param   integer $sender_usr_id The ID of the user sending this message.
1927       * @return  void
1928       */
1929      function sendDirectEmail($issue_id, $from, $to, $cc, $subject, $body, $message_id, $sender_usr_id = false)
1930      {
1931          $subject = Mail_API::formatSubject($issue_id, $subject);
1932          $recipients = Support::getRecipientsCC($cc);
1933          $recipients[] = $to;
1934          // send the emails now, one at a time
1935          foreach ($recipients as $recipient) {
1936              $mail = new Mail_API;
1937              if (!empty($issue_id)) {
1938                  // add the warning message to the current message' body, if needed
1939                  $fixed_body = Mail_API::addWarningMessage($issue_id, $recipient, $body, array());
1940                  $mail->setHeaders(array(
1941                      "Message-Id" => $message_id
1942                  ));
1943                  // skip users who don't have access to this issue
1944                  $recipient_usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($recipient));
1945                  if (((!empty($recipient_usr_id)) && (!Issue::canAccess($issue_id, $recipient_usr_id))) ||
1946                          (empty($recipient_usr_id)) && (Issue::isPrivate($issue_id))) {
1947                      continue;
1948                  }
1949              } else {
1950                  $fixed_body = $body;
1951              }
1952              if (User::getRoleByUser(User::getUserIDByEmail(Mail_API::getEmailAddress($from)), Issue::getProjectID($issue_id)) == User::getRoleID("Customer")) {
1953                  $type = 'customer_email';
1954              } else {
1955                  $type = 'other_email';
1956              }
1957              $mail->setTextBody($fixed_body);
1958              $mail->send($from, $recipient, $subject, TRUE, $issue_id, $type, $sender_usr_id);
1959          }
1960      }
1961  
1962  
1963      /**
1964       * Method used to parse the Cc list in a string format and return
1965       * an array of the email addresses contained within.
1966       *
1967       * @access  public
1968       * @param   string $cc The Cc list
1969       * @return  array The list of email addresses
1970       */
1971      function getRecipientsCC($cc)
1972      {
1973          $cc = trim($cc);
1974          if (empty($cc)) {
1975              return array();
1976          } else {
1977              $cc = str_replace(",", ";", $cc);
1978              return explode(";", $cc);
1979          }
1980      }
1981  
1982  
1983      /**
1984       * Method used to send an email from the user interface.
1985       *
1986       * @access  public
1987       * @return  integer 1 if it worked, -1 otherwise
1988       */
1989      function sendEmail($parent_sup_id = FALSE)
1990      {
1991          // if we are replying to an existing email, set the In-Reply-To: header accordingly
1992          if ($parent_sup_id) {
1993              $in_reply_to = Support::getMessageIDByID($parent_sup_id);
1994          } else {
1995              $in_reply_to = false;
1996          }
1997  
1998          // get ID of whoever is sending this.
1999          $sender_usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($_POST["from"]));
2000          if (empty($sender_usr_id)) {
2001              $sender_usr_id = false;
2002          }
2003  
2004          // get type of email this is
2005          if (!empty($_POST['type'])) {
2006              $type = $_POST['type'];
2007          } else {
2008              $type = '';
2009          }
2010  
2011  
2012          // remove extra 'Re: ' from subject
2013          $_POST['subject'] = Mail_API::removeExcessRe($_POST['subject'], true);
2014          $internal_only = false;
2015          $message_id = Mail_API::generateMessageID();
2016          // hack needed to get the full headers of this web-based email
2017          $full_email = Support::buildFullHeaders($_POST["issue_id"], $message_id, $_POST["from"],
2018                  $_POST["to"], $_POST["cc"], $_POST["subject"], $_POST["message"], $in_reply_to);
2019  
2020          // email blocking should only be done if this is an email about an associated issue
2021          if (!empty($_POST['issue_id'])) {
2022              $user_info = User::getNameEmail(Auth::getUserID());
2023              // check whether the current user is allowed to send this email to customers or not
2024              if (!Support::isAllowedToEmail($_POST["issue_id"], $user_info['usr_email'])) {
2025                  // add the message body as a note
2026                  $_POST['blocked_msg'] = $full_email;
2027                  $_POST['title'] = $_POST["subject"];
2028                  $_POST['note'] = Mail_API::getCannedBlockedMsgExplanation() . $_POST["message"];
2029                  Note::insert(Auth::getUserID(), $_POST["issue_id"]);
2030                  Workflow::handleBlockedEmail(Issue::getProjectID($_POST['issue_id']), $_POST['issue_id'], $_POST, 'web');
2031                  return 1;
2032              }
2033          }
2034  
2035          // only send a direct email if the user doesn't want to add the Cc'ed people to the notification list
2036          if (@$_POST['add_unknown'] == 'yes') {
2037              if (!empty($_POST['issue_id'])) {
2038                  // add the recipients to the notification list of the associated issue
2039                  $recipients = array($_POST['to']);
2040                  $recipients = array_merge($recipients, Support::getRecipientsCC($_POST['cc']));
2041                  for ($i = 0; $i < count($recipients); $i++) {
2042                      if ((!empty($recipients[$i])) && (!Notification::isIssueRoutingSender($_POST["issue_id"], $recipients[$i]))) {
2043                          Notification::subscribeEmail(Auth::getUserID(), $_POST["issue_id"], Mail_API::getEmailAddress($recipients[$i]), Notification::getDefaultActions());
2044                      }
2045                  }
2046              }
2047          } else {
2048              // Usually when sending out emails associated to an issue, we would
2049              // simply insert the email in the table and call the Notification::notifyNewEmail() method,
2050              // but on this case we need to actually send the email to the recipients that are not
2051              // already in the notification list for the associated issue, if any.
2052              // In the case of replying to an email that is not yet associated with an issue, then
2053              // we are always directly sending the email, without using any notification list
2054              // functionality.
2055              if (!empty($_POST['issue_id'])) {
2056                  // send direct emails only to the unknown addresses, and leave the rest to be
2057                  // catched by the notification list
2058                  $from = Notification::getFixedFromHeader($_POST['issue_id'], $_POST['from'], 'issue');
2059                  // build the list of unknown recipients
2060                  if (!empty($_POST['to'])) {
2061                      $recipients = array($_POST['to']);
2062                      $recipients = array_merge($recipients, Support::getRecipientsCC($_POST['cc']));
2063                  } else {
2064                      $recipients = Support::getRecipientsCC($_POST['cc']);
2065                  }
2066                  $unknowns = array();
2067                  for ($i = 0; $i < count($recipients); $i++) {
2068                      if (!Notification::isSubscribedToEmails($_POST['issue_id'], $recipients[$i])) {
2069                          $unknowns[] = $recipients[$i];
2070                      }
2071                  }
2072                  if (count($unknowns) > 0) {
2073                      $to = array_shift($unknowns);
2074                      $cc = implode('; ', $unknowns);
2075                      // send direct emails
2076                      Support::sendDirectEmail($_POST['issue_id'], $from, $to, $cc,
2077                              $_POST['subject'], $_POST['message'], $message_id, $sender_usr_id);
2078                  }
2079              } else {
2080                  // send direct emails to all recipients, since we don't have an associated issue
2081                  $project_info = Project::getOutgoingSenderAddress(Auth::getCurrentProject());
2082                  // use the project-related outgoing email address, if there is one
2083                  if (!empty($project_info['email'])) {
2084                      $from = Mail_API::getFormattedName(User::getFullName(Auth::getUserID()), $project_info['email']);
2085                  } else {
2086                      // otherwise, use the real email address for the current user
2087                      $from = User::getFromHeader(Auth::getUserID());
2088                  }
2089                  // send direct emails
2090                  Support::sendDirectEmail($_POST['issue_id'], $from, $_POST['to'], $_POST['cc'],
2091                          $_POST['subject'], $_POST['message'], $message_id);
2092              }
2093          }
2094  
2095          $t = array(
2096              'customer_id'    => 'NULL',
2097              'issue_id'       => $_POST["issue_id"] ? $_POST["issue_id"] : 0,
2098              'ema_id'         => $_POST['ema_id'],
2099              'message_id'     => $message_id,
2100              'date'           => Date_API::getCurrentDateGMT(),
2101              'from'           => $_POST['from'],
2102              'to'             => $_POST['to'],
2103              'cc'             => @$_POST['cc'],
2104              'subject'        => @$_POST['subject'],
2105              'body'           => $_POST['message'],
2106              'full_email'     => $full_email,
2107              'has_attachment' => 0
2108          );
2109          // associate this new email with a customer, if appropriate
2110          if (Auth::getCurrentRole() == User::getRoleID('Customer')) {
2111              $customer_id = User::getCustomerID(Auth::getUserID());
2112              if ((!empty($customer_id)) && ($customer_id != -1)) {
2113                  $t['customer_id'] = $customer_id;
2114              }
2115          }
2116          $structure = Mime_Helper::decode($full_email, true, false);
2117          $t['headers'] = $structure->headers;
2118          $res = Support::insertEmail($t, $structure, $sup_id);
2119          if (!empty($_POST["issue_id"])) {
2120              // need to send a notification
2121              Notification::notifyNewEmail(Auth::getUserID(), $_POST["issue_id"], $t, $internal_only, false, $type, $sup_id);
2122              // mark this issue as updated
2123              if ((!empty($t['customer_id'])) && ($t['customer_id'] != 'NULL')) {
2124                  Issue::markAsUpdated($_POST["issue_id"], 'customer action');
2125              } else {
2126                  if ((!empty($sender_usr_id)) && (User::getRoleByUser($sender_usr_id, Issue::getProjectID($_POST['issue_id'])) > User::getRoleID('Customer'))) {
2127                      Issue::markAsUpdated($_POST["issue_id"], 'staff response');
2128                  } else {
2129                      Issue::markAsUpdated($_POST["issue_id"], 'user response');
2130                  }
2131              }
2132              // save a history entry for this
2133              History::add($_POST["issue_id"], Auth::getUserID(), History::getTypeID('email_sent'),
2134                              ev_gettext('Outgoing email sent by %1$s', User::getFullName(Auth::getUserID())));
2135          }
2136  
2137          return 1;
2138      }
2139  
2140  
2141      /**
2142       * Method used to get the message-id associated with a given support
2143       * email entry.
2144       *
2145       * @access  public
2146       * @param   integer $sup_id The support email ID
2147       * @return  integer The email ID
2148       */
2149      function getMessageIDByID($sup_id)
2150      {
2151          $stmt = "SELECT
2152                      sup_message_id
2153                   FROM
2154                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
2155                   WHERE
2156                      sup_id=" . Misc::escapeInteger($sup_id);
2157          $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
2158          if (PEAR::isError($res)) {
2159              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
2160              return "";
2161          } else {
2162              return $res;
2163          }
2164      }
2165  
2166  
2167      /**
2168       * Method used to get the support ID associated with a given support
2169       * email message-id.
2170       *
2171       * @access  public
2172       * @param   string $message_id The message ID
2173       * @return  integer The email ID
2174       */
2175      function getIDByMessageID($message_id)
2176      {
2177          $stmt = "SELECT
2178                      sup_id
2179                   FROM
2180                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
2181                   WHERE
2182                      sup_message_id='" . Misc::escapeString($message_id) . "'";
2183          $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
2184          if (PEAR::isError($res)) {
2185              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
2186              return false;
2187          } else {
2188              if (empty($res)) {
2189                  return false;
2190              } else {
2191                  return $res;
2192              }
2193          }
2194      }
2195  
2196  
2197      /**
2198       * Method used to get the issue ID associated with a given support
2199       * email message-id.
2200       *
2201       * @access  public
2202       * @param   string $message_id The message ID
2203       * @return  integer The issue ID
2204       */
2205      function getIssueByMessageID($message_id)
2206      {
2207          $stmt = "SELECT
2208                      sup_iss_id
2209                   FROM
2210                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
2211                   WHERE
2212                      sup_message_id='" . Misc::escapeString($message_id) . "'";
2213          $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
2214          if (PEAR::isError($res)) {
2215              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
2216              return "";
2217          } else {
2218              return $res;
2219          }
2220      }
2221  
2222  
2223      /**
2224       * Method used to get the issue ID associated with a given support
2225       * email entry.
2226       *
2227       * @access  public
2228       * @param   integer $sup_id The support email ID
2229       * @return  integer The issue ID
2230       */
2231      function getIssueFromEmail($sup_id)
2232      {
2233          $stmt = "SELECT
2234                      sup_iss_id
2235                   FROM
2236                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
2237                   WHERE
2238                      sup_id=" . Misc::escapeInteger($sup_id);
2239          $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
2240          if (PEAR::isError($res)) {
2241              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
2242              return "";
2243          } else {
2244              return $res;
2245          }
2246      }
2247  
2248  
2249      /**
2250       * Returns the message-id of the parent email.
2251       *
2252       * @access  public
2253       * @param   string $msg_id The message ID
2254       * @return  string The message id of the parent email or false
2255       */
2256      function getParentMessageIDbyMessageID($msg_id)
2257      {
2258          $sql = "SELECT
2259                      parent.sup_message_id
2260                  FROM
2261                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email child,
2262                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email parent
2263                  WHERE
2264                      parent.sup_id = child.sup_parent_id AND
2265                      child.sup_message_id = '" . Misc::escapeString($msg_id) . "'";
2266          $res = $GLOBALS["db_api"]->dbh->getOne($sql);
2267          if (PEAR::isError($res)) {
2268              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
2269              return false;
2270          } else {
2271              if (empty($res)) {
2272                  return false;
2273              }
2274              return $res;
2275          }
2276  
2277      }
2278  
2279  
2280      /**
2281       * Returns the number of emails sent by a user in a time range.
2282       *
2283       * @access  public
2284       * @param   string $usr_id The ID of the user
2285       * @param   integer $start The timestamp of the start date
2286       * @param   integer $end The timestanp of the end date
2287       * @param   boolean $associated If this should return emails associated with issues or non associated emails.
2288       * @return  integer The number of emails sent by the user.
2289       */
2290      function getSentEmailCountByUser($usr_id, $start, $end, $associated)
2291      {
2292          $usr_info = User::getNameEmail($usr_id);
2293          $stmt = "SELECT
2294                      COUNT(sup_id)
2295                   FROM
2296                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email,
2297                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "email_account
2298                   WHERE
2299                      ema_id = sup_ema_id AND
2300                      ema_prj_id = " . Auth::getCurrentProject() . " AND
2301                      sup_date BETWEEN '" . Misc::escapeString($start) . "' AND '" . Misc::escapeString($end) . "' AND
2302                      sup_from LIKE '%" . Misc::escapeString($usr_info["usr_email"]) . "%' AND
2303                      sup_iss_id ";
2304          if ($associated == true) {
2305              $stmt .= "!= 0";
2306          } else {
2307              $stmt .= "= 0";
2308          }
2309          $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
2310          if (PEAR::isError($res)) {
2311              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
2312              return "";
2313          }
2314          return $res;
2315      }
2316  
2317  
2318      /**
2319       * Returns the projectID based on the email account
2320       *
2321       * @access  public
2322       * @param   integer $ema_id The id of the email account.
2323       * @return  integer The ID of the of the project.
2324       */
2325      function getProjectByEmailAccount($ema_id)
2326      {
2327          static $returns;
2328  
2329          if (!empty($returns[$ema_id])) {
2330              return $returns[$ema_id];
2331          }
2332  
2333          $stmt = "SELECT
2334                      ema_prj_id
2335                   FROM
2336                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "email_account
2337                   WHERE
2338                      ema_id = " . Misc::escapeInteger($ema_id);
2339          $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
2340          if (PEAR::isError($res)) {
2341              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
2342              return -1;
2343          }
2344          $returns[$ema_id] = $res;
2345          return $res;
2346      }
2347  
2348  
2349      /**
2350       * Moves an email from one account to another.
2351       *
2352       * @access  public
2353       * @param   integer $sup_id The ID of the message.
2354       * @param   integer $current_ema_id The ID of the account the message is currently in.
2355       * @param   integer $new_ema_id The ID of the account to move the message too.
2356       * @return  integer -1 if there was error moving the message, 1 otherwise.
2357       */
2358      function moveEmail($sup_id, $current_ema_id, $new_ema_id)
2359      {
2360          $usr_id = Auth::getUserID();
2361          $email = Support::getEmailDetails($current_ema_id, $sup_id);
2362          if (!empty($email['sup_iss_id'])) {
2363              return -1;
2364          }
2365  
2366          $info = Email_Account::getDetails($new_ema_id);
2367          $full_email = Support::getFullEmail($sup_id);
2368          $structure = Mime_Helper::decode($full_email, true, true);
2369          $headers = '';
2370          foreach ($structure->headers as $key => $value) {
2371              if (is_array($value)) {
2372                  continue;
2373              }
2374              $headers .= "$key: $value\n";
2375          }
2376  
2377          // handle auto creating issues (if needed)
2378          $should_create_array = Support::createIssueFromEmail($info, $headers, $email['seb_body'], $email['timestamp'], $email['sup_from'], $email['sup_subject']);
2379          $should_create_issue = $should_create_array['should_create_issue'];
2380          $associate_email = $should_create_array['associate_email'];
2381          $issue_id = $should_create_array['issue_id'];
2382          $customer_id = $should_create_array['customer_id'];
2383  
2384          if (empty($issue_id)) {
2385              $issue_id = 0;
2386          }
2387          if (empty($customer_id)) {
2388              $customer_id = 'NULL';
2389          }
2390  
2391          $sql = "UPDATE
2392                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
2393                  SET
2394                      sup_ema_id = " . Misc::escapeInteger($new_ema_id) . ",
2395                      sup_iss_id = " . Misc::escapeInteger($issue_id) . ",
2396                      sup_customer_id = " . Misc::escapeInteger($customer_id) . "
2397                  WHERE
2398                      sup_id = " . Misc::escapeInteger($sup_id) . " AND
2399                      sup_ema_id = " . Misc::escapeInteger($current_ema_id);
2400          $res = $GLOBALS["db_api"]->dbh->query($sql);
2401          if (PEAR::isError($res)) {
2402              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
2403              return -1;
2404          }
2405  
2406          $row = array(
2407              'customer_id'    => $customer_id,
2408              'issue_id'       => $issue_id,
2409              'ema_id'         => $new_ema_id,
2410              'message_id'     => $email['sup_message_id'],
2411              'date'           => $email['timestamp'],
2412              'from'           => $email['sup_from'],
2413              'to'             => $email['sup_to'],
2414              'cc'             => $email['sup_cc'],
2415              'subject'        => $email['sup_subject'],
2416              'body'           => $email['seb_body'],
2417              'full_email'     => $email['seb_full_email'],
2418              'has_attachment' => $email['sup_has_attachment']
2419          );
2420          Workflow::handleNewEmail(Support::getProjectByEmailAccount($new_ema_id), $issue_id, $structure, $row);
2421          return 1;
2422      }
2423  
2424  
2425      /**
2426       * Deletes the specified message from the server
2427       * NOTE: YOU STILL MUST call imap_expunge($mbox) to permanently delete the message.
2428       *
2429       * @param   array $info An array of email account information
2430       * @param   object $mbox The mailbox object
2431       * @param   integer $num The number of the message to delete.
2432       */
2433      function deleteMessage($info, $mbox, $num)
2434      {
2435          // need to delete the message from the server?
2436          if (!$info['ema_leave_copy']) {
2437              @imap_delete($mbox, $num);
2438          } else {
2439              // mark the message as already read
2440              @imap_setflag_full($mbox, $num, "\\Seen");
2441          }
2442      }
2443  
2444  
2445      /**
2446       * Check if this email needs to be blocked and if so, block it.
2447       *
2448       *
2449       */
2450      function blockEmailIfNeeded($email)
2451      {
2452          if (empty($email['issue_id'])) {
2453              return false;
2454          }
2455  
2456          $issue_id = $email['issue_id'];
2457          $prj_id = Issue::getProjectID($issue_id);
2458          $sender_email = strtolower(Mail_API::getEmailAddress($email['headers']['from']));
2459          list($text_headers, $body) = Mime_Helper::splitHeaderBody($email['full_email']);
2460          if ((Mail_API::isVacationAutoResponder($email['headers'])) || (Notification::isBounceMessage($sender_email)) ||
2461                  (!Support::isAllowedToEmail($issue_id, $sender_email))) {
2462              // add the message body as a note
2463              $_POST = array(
2464                  'blocked_msg' => $email['full_email'],
2465                  'title'       => @$email['headers']['subject'],
2466                  'note'        => Mail_API::getCannedBlockedMsgExplanation($issue_id) . $email['body'],
2467                  'message_id'  => Mail_API::getMessageID($text_headers, $body),
2468              );
2469              // avoid having this type of message re-open the issue
2470              if (Mail_API::isVacationAutoResponder($email['headers'])) {
2471                  $closing = true;
2472                  $notify = false;
2473              } else {
2474                  $closing = false;
2475                  $notify = true;
2476              }
2477              $res = Note::insert(Auth::getUserID(), $issue_id, $email['headers']['from'], false, $closing, $notify);
2478              // associate the email attachments as internal-only files on this issue
2479              if ($res != -1) {
2480                  Support::extractAttachments($issue_id, $email['full_email'], true, $res);
2481              }
2482  
2483              $_POST['issue_id'] = $issue_id;
2484              $_POST['from'] = $sender_email;
2485  
2486              // avoid having this type of message re-open the issue
2487              if (Mail_API::isVacationAutoResponder($email['headers'])) {
2488                  $email_type = 'vacation-autoresponder';
2489              } else {
2490                  $email_type = 'routed';
2491              }
2492              Workflow::handleBlockedEmail($prj_id, $issue_id, $_POST, $email_type);
2493  
2494              // try to get usr_id of sender, if not, use system account
2495              $usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($email['from']));
2496              if (!$usr_id) {
2497                  $usr_id = APP_SYSTEM_USER_ID;
2498              }
2499              // log blocked email
2500              History::add($issue_id, $usr_id, History::getTypeID('email_blocked'), ev_gettext('Email from \'%1$s\' blocked', $email['from']));
2501              return true;
2502          }
2503          return false;
2504      }
2505  }
2506  
2507  // benchmarking the included file (aka setup time)
2508  if (APP_BENCHMARK) {
2509      $GLOBALS['bench']->setMarker('Included Support Class');
2510  }


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