*/ include_once "../../../config.inc.php"; include_once APP_INC_PATH . "db_access.php"; include_once APP_INC_PATH . "class.issue.php"; $stmt = "SELECT\n iss_id\n FROM\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue\n WHERE\n iss_root_message_id IS NULL"; $issues = $GLOBALS["db_api"]->dbh->getCol($stmt); foreach ($issues as $issue_id) { $sql = "SELECT\n sup_message_id\n FROM\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email\n WHERE\n sup_iss_id = {$issue_id}\n ORDER BY\n sup_date ASC\n LIMIT 1"; $res = $GLOBALS["db_api"]->dbh->getOne($sql); if (PEAR::isError($res)) { echo "<pre>"; print_r($res); echo "</pre>"; exit; } if (empty($res)) { $msg_id = Mail_API::generateMessageID(); } else { $msg_id = $res; } $sql = "UPDATE\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue\n SET\n iss_root_message_id = '" . Misc::escapeString($msg_id) . "'\n WHERE\n iss_id = {$issue_id}"; $res = $GLOBALS["db_api"]->dbh->query($sql); if (PEAR::isError($res)) { echo "<pre>"; print_r($res); echo "</pre>"; exit; } } ?> done
/** * Method used to get the associated customer and customer contact from * a given set of support emails. This is especially useful to automatically * associate an issue to the appropriate customer contact that sent a * support email. * * @access public * @param array $sup_ids The list of support email IDs * @return array The customer and customer contact ID */ function getCustomerInfoFromEmails($sup_ids) { $senders = Support::getSender($sup_ids); if (count($senders) > 0) { $emails = array(); for ($i = 0; $i < count($senders); $i++) { $emails[] = Mail_API::getEmailAddress($senders[$i]); } list($customer_id, $contact_id) = $this->getCustomerIDByEmails($emails); $company = $this->getDetails($customer_id); $contact = $this->getContactDetails($contact_id); return array('customer_id' => $customer_id, 'customer_name' => $company['customer_name'], 'contact_id' => $contact_id, 'contact_name' => $contact['first_name'] . " " . $contact['last_name'], 'contacts' => $this->getContactEmailAssocList($customer_id)); } else { return array('customer_id' => '', 'customer_name' => '', 'contact_id' => '', 'contact_name' => '', 'contacts' => ''); } }
/** * Check if this email needs to be blocked and if so, block it. * * */ function blockEmailIfNeeded($email) { global $HTTP_POST_VARS; if (empty($email['issue_id'])) { return false; } $issue_id = $email['issue_id']; $prj_id = Issue::getProjectID($issue_id); $sender_email = strtolower(Mail_API::getEmailAddress($email['headers']['from'])); if (Mail_API::isVacationAutoResponder($email['headers']) || Notification::isBounceMessage($sender_email) || !Support::isAllowedToEmail($issue_id, $sender_email)) { // add the message body as a note $HTTP_POST_VARS = array('blocked_msg' => $email['full_email'], 'title' => @$email['headers']['subject'], 'note' => Mail_API::getCannedBlockedMsgExplanation($issue_id) . $email['body']); // avoid having this type of message re-open the issue if (Mail_API::isVacationAutoResponder($email['headers'])) { $closing = true; } else { $closing = false; } $res = Note::insert(Auth::getUserID(), $issue_id, $email['headers']['from'], false, $closing); // associate the email attachments as internal-only files on this issue if ($res != -1) { Support::extractAttachments($issue_id, $email['full_email'], true, $res); } $HTTP_POST_VARS['issue_id'] = $issue_id; $HTTP_POST_VARS['from'] = $sender_email; // avoid having this type of message re-open the issue if (Mail_API::isVacationAutoResponder($email['headers'])) { $email_type = 'vacation-autoresponder'; } else { $email_type = 'routed'; } Workflow::handleBlockedEmail($prj_id, $issue_id, $HTTP_POST_VARS, $email_type); // try to get usr_id of sender, if not, use system account $usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($email['from'])); if (!$usr_id) { $usr_id = APP_SYSTEM_USER_ID; } // log blocked email History::add($issue_id, $usr_id, History::getTypeID('email_blocked'), "Email from '" . $email['from'] . "' blocked."); return true; } return false; }
/** * Converts a note to a draft or an email * * @access public * @param $note_id The id of the note * @param $target What the not should be converted too * @param $authorize_sender If the sender should be added to authorized senders list. */ function convertNote($note_id, $target, $authorize_sender = false) { $note_id = Misc::escapeInteger($note_id); $issue_id = Note::getIssueID($note_id); $email_account_id = Email_Account::getEmailAccount(); $blocked_message = Note::getBlockedMessage($note_id); $unknown_user = Note::getUnknownUser($note_id); $structure = Mime_Helper::decode($blocked_message, true, true); $body = Mime_Helper::getMessageBody($structure); $sender_email = strtolower(Mail_API::getEmailAddress($structure->headers['from'])); if ($target == 'email') { if (Mime_Helper::hasAttachments($blocked_message)) { $has_attachments = 1; } else { $has_attachments = 0; } list($blocked_message, $headers) = Mail_API::rewriteThreadingHeaders($issue_id, $blocked_message, @$structure->headers); $t = array('issue_id' => $issue_id, 'ema_id' => $email_account_id, 'message_id' => @$structure->headers['message-id'], 'date' => Date_API::getCurrentDateGMT(), 'from' => @$structure->headers['from'], 'to' => @$structure->headers['to'], 'cc' => @$structure->headers['cc'], 'subject' => @$structure->headers['subject'], 'body' => @$body, 'full_email' => @$blocked_message, 'has_attachment' => $has_attachments, 'headers' => $headers); // need to check for a possible customer association if (!empty($structure->headers['from'])) { $details = Email_Account::getDetails($email_account_id); // check from the associated project if we need to lookup any customers by this email address if (Customer::hasCustomerIntegration($details['ema_prj_id'])) { // check for any customer contact association list($customer_id, ) = Customer::getCustomerIDByEmails($details['ema_prj_id'], array($sender_email)); if (!empty($customer_id)) { $t['customer_id'] = $customer_id; } } } if (empty($t['customer_id'])) { $update_type = 'staff response'; $t['customer_id'] = "NULL"; } else { $update_type = 'customer action'; } $res = Support::insertEmail($t, $structure, $sup_id); if ($res != -1) { Support::extractAttachments($issue_id, $blocked_message); // notifications about new emails are always external $internal_only = false; // special case when emails are bounced back, so we don't want to notify the customer about those if (Notification::isBounceMessage($sender_email)) { $internal_only = true; } Notification::notifyNewEmail(Auth::getUserID(), $issue_id, $t, $internal_only, false, '', $sup_id); Issue::markAsUpdated($issue_id, $update_type); Note::remove($note_id, false); History::add($issue_id, Auth::getUserID(), History::getTypeID('note_converted_email'), "Note converted to e-mail (from: " . @$structure->headers['from'] . ") by " . User::getFullName(Auth::getUserID())); // now add sender as an authorized replier if ($authorize_sender) { Authorized_Replier::manualInsert($issue_id, @$structure->headers['from']); } } return $res; } else { // save message as a draft $res = Draft::saveEmail($issue_id, $structure->headers['to'], $structure->headers['cc'], $structure->headers['subject'], $body, false, $unknown_user); // remove the note, if the draft was created successfully if ($res) { Note::remove($note_id, false); History::add($issue_id, Auth::getUserID(), History::getTypeID('note_converted_draft'), "Note converted to draft (from: " . @$structure->headers['from'] . ") by " . User::getFullName(Auth::getUserID())); } return $res; } }
/** * Method used to send an alert to a set of email addresses when * a reminder action was triggered, but no action was really * taken because no recipients could be found. * * @access private * @param integer $issue_id The issue ID * @param string $type Which reminder are we trying to send, email or sms * @param array $reminder The reminder details * @param array $action The action details * @return void */ function _recordNoRecipientError($issue_id, $type, $reminder, $action) { $to = Reminder::_getReminderAlertAddresses(); if (count($to) > 0) { $tpl = new Template_API(); $tpl->setTemplate('reminders/alert_no_recipients.tpl.text'); $tpl->bulkAssign(array("type" => $type, "data" => $data, "reminder" => $reminder, "action" => $action, "conditions" => $conditions, "has_customer_integration" => Customer::hasCustomerIntegration(Issue::getProjectID($issue_id)))); $text_message = $tpl->getTemplateContents(); foreach ($to as $address) { // send email (use PEAR's classes) $mail = new Mail_API(); $mail->setTextBody($text_message); $setup = $mail->getSMTPSettings(); $mail->send($setup["from"], $address, "[#{$issue_id}] Reminder Not Triggered: " . $action['rma_title'], 0, $issue_id); } } }
/** * Method used to get a list of emails that are associated with a given * project and issue. * * @access public * @param integer $prj_id The project ID * @param integer $issue_id The issue ID * @return array List of emails */ function getAddressBookEmails($prj_id, $issue_id) { $list = Project::getAddressBook($prj_id, $issue_id); $emails = array(); foreach ($list as $address => $name) { $emails[] = Mail_API::getEmailAddress($address); } return $emails; }
/** * Notifies site administrators of the error condition * * @access private * @param mixed $error_msg The error message * @param string $script The script name where the error happened * @param integer $line The line number where the error happened */ function _notify($error_msg = "unknown", $script = "unknown", $line = "unknown") { global $HTTP_SERVER_VARS; $setup = Setup::load(); $notify_list = trim($setup['email_error']['addresses']); if (empty($notify_list)) { return false; } $notify_list = str_replace(';', ',', $notify_list); $notify_list = explode(',', $notify_list); $subject = APP_SITE_NAME . " - Error found! - " . date("m/d/Y H:i:s"); $msg = "Hello,\n\n"; $msg .= "An error was found at " . date("m/d/Y H:i:s") . " (" . time() . ") on line '" . $line . "' of script " . "'{$script}'.\n\n"; $msg .= "The error message passed to us was:\n\n"; if (is_array($error_msg) && count($error_msg) > 1) { $msg .= "'" . $error_msg[0] . "'\n\n"; $msg .= "A more detailed error message follows:\n\n"; $msg .= "'" . $error_msg[1] . "'\n\n"; } else { $msg .= "'{$error_msg}'\n\n"; } @($msg .= "That happened on page '" . $HTTP_SERVER_VARS["PHP_SELF"] . "' from IP Address '" . getenv("REMOTE_ADDR") . "' coming from the page (referrer) '" . getenv("HTTP_REFERER") . "'.\n\n"); @($msg .= "The user agent given was '" . $HTTP_SERVER_VARS['HTTP_USER_AGENT'] . "'.\n\n"); $msg .= "Sincerely yours,\nAutomated Error_Handler Class"; // only try to include the backtrace if we are on PHP 4.3.0 or later if (version_compare(phpversion(), "4.3.0", ">=")) { $msg .= "\n\nA backtrace is available:\n\n"; ob_start(); $backtrace = debug_backtrace(); // remove the two entries related to the error handling stuff itself array_shift($backtrace); array_shift($backtrace); // now we can print it out print_r($backtrace); $contents = ob_get_contents(); $msg .= $contents; ob_end_clean(); } // avoid triggering an email notification about a query that // was bigger than max_allowed_packet (usually 16 megs on 3.23 // client libraries) if (strlen($msg) > 16777216) { return false; } foreach ($notify_list as $notify_email) { $mail = new Mail_API(); $mail->setTextBody($msg); $mail->send($setup['smtp']['from'], $notify_email, $subject); } }
/** * Connects to the SMTP server and sends the queued message. * * @access private * @param string $recipient The recipient of this message * @param string $text_headers The full headers of this message * @param string $body The full body of this message * @return true, or a PEAR_Error object */ function _sendEmail($recipient, $text_headers, $body) { $header_names = Mime_Helper::getHeaderNames($text_headers); $_headers = Mail_Queue::_getHeaders($text_headers, $body); $headers = array(); foreach ($_headers as $lowercase_name => $value) { // need to remove the quotes to avoid a parsing problem // on senders that have extended characters in the first // or last words in their sender name if ($lowercase_name == 'from') { $value = Mime_Helper::removeQuotes($value); } $value = Mime_Helper::encode($value); // add the quotes back if ($lowercase_name == 'from') { $value = Mime_Helper::quoteSender($value); } $headers[$header_names[$lowercase_name]] = $value; } // remove any Reply-To:/Return-Path: values from outgoing messages unset($headers['Reply-To']); unset($headers['Return-Path']); // mutt sucks, so let's remove the broken Mime-Version header and add the proper one if (in_array('Mime-Version', array_keys($headers))) { unset($headers['Mime-Version']); $headers['MIME-Version'] = '1.0'; } $mail =& Mail::factory('smtp', Mail_API::getSMTPSettings()); $res = $mail->send($recipient, $headers, $body); if (PEAR::isError($res)) { // special handling of errors when the mail server is down if (strstr($res->getMessage(), 'unable to connect to smtp server')) { Error_Handler::logToFile(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); } else { Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); } return $res; } else { return true; } }
$res = Support::getListDetails($HTTP_GET_VARS["item"]); $tpl->assign("emails", $res); $tpl->assign("attached_emails", @implode(",", $HTTP_GET_VARS["item"])); if (Customer::hasCustomerIntegration($prj_id)) { // also need to guess the contact_id from any attached emails $info = Customer::getCustomerInfoFromEmails($prj_id, $HTTP_GET_VARS["item"]); $tpl->assign(array("customer_id" => $info['customer_id'], 'customer_name' => $info['customer_name'], "contact_id" => $info['contact_id'], 'contact_name' => $info['contact_name'], 'contacts' => $info['contacts'])); } // if we are dealing with just one message, use the subject line as the // summary for the issue, and the body as the description if (count($HTTP_GET_VARS["item"]) == 1) { $email_details = Support::getEmailDetails(Email_Account::getAccountByEmail($HTTP_GET_VARS["item"][0]), $HTTP_GET_VARS["item"][0]); $tpl->assign(array('issue_summary' => $email_details['sup_subject'], 'issue_description' => $email_details['message'])); // also auto pre-fill the customer contact text fields if (Customer::hasCustomerIntegration($prj_id)) { $sender_email = Mail_API::getEmailAddress($email_details['sup_from']); list(, $contact_id) = Customer::getCustomerIDByEmails($prj_id, array($sender_email)); if (!empty($contact_id)) { $tpl->assign("contact_details", Customer::getContactDetails($prj_id, $contact_id)); } } } } } $tpl->assign(array("cats" => Category::getAssocList($prj_id), "priorities" => Priority::getAssocList($prj_id), "users" => Project::getUserAssocList($prj_id, 'active', User::getRoleID('Customer')), "releases" => Release::getAssocList($prj_id), "custom_fields" => Custom_Field::getListByProject($prj_id, 'report_form'), "max_attachment_size" => Attachment::getMaxAttachmentSize(), "field_display_settings" => Project::getFieldDisplaySettings($prj_id), "groups" => Group::getAssocList($prj_id))); $setup = Setup::load(); $tpl->assign("allow_unassigned_issues", @$setup["allow_unassigned_issues"]); $prefs = Prefs::get($usr_id); $tpl->assign("user_prefs", $prefs); $tpl->assign("zones", Date_API::getTimezoneList()); if (User::getRole(Auth::getCurrentRole()) == "Customer") {
/** * Method used to add a new issue using the normal report form. * * @access public * @return integer The new issue ID */ function insert() { global $HTTP_POST_VARS, $HTTP_POST_FILES, $insert_errors; $usr_id = Auth::getUserID(); $prj_id = Auth::getCurrentProject(); $initial_status = Project::getInitialStatus($prj_id); $insert_errors = array(); $missing_fields = array(); if ($HTTP_POST_VARS["category"] == '-1') { $missing_fields[] = "Category"; } if ($HTTP_POST_VARS["priority"] == '-1') { $missing_fields[] = "Priority"; } if ($HTTP_POST_VARS["estimated_dev_time"] == '') { $HTTP_POST_VARS["estimated_dev_time"] = 0; } // add new issue $stmt = "INSERT INTO\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue\n (\n iss_prj_id,\n"; if (!empty($HTTP_POST_VARS["group"])) { $stmt .= "iss_grp_id,\n"; } if (!empty($HTTP_POST_VARS["category"])) { $stmt .= "iss_prc_id,\n"; } if (!empty($HTTP_POST_VARS["release"])) { $stmt .= "iss_pre_id,\n"; } if (!empty($HTTP_POST_VARS["priority"])) { $stmt .= "iss_pri_id,\n"; } $stmt .= "iss_usr_id,"; if (!empty($initial_status)) { $stmt .= "iss_sta_id,"; } if (Customer::hasCustomerIntegration($prj_id)) { $stmt .= "\n iss_customer_id,\n iss_customer_contact_id,\n iss_contact_person_lname,\n iss_contact_person_fname,\n iss_contact_email,\n iss_contact_phone,\n iss_contact_timezone,"; } $stmt .= "\n iss_created_date,\n iss_last_public_action_date,\n iss_last_public_action_type,\n iss_summary,\n iss_description,\n iss_dev_time,\n iss_private,\n iss_root_message_id\n ) VALUES (\n " . $prj_id . ",\n"; if (!empty($HTTP_POST_VARS["group"])) { $stmt .= Misc::escapeInteger($HTTP_POST_VARS["group"]) . ",\n"; } if (!empty($HTTP_POST_VARS["category"])) { $stmt .= Misc::escapeInteger($HTTP_POST_VARS["category"]) . ",\n"; } if (!empty($HTTP_POST_VARS["release"])) { $stmt .= Misc::escapeInteger($HTTP_POST_VARS["release"]) . ",\n"; } if (!empty($HTTP_POST_VARS["priority"])) { $stmt .= Misc::escapeInteger($HTTP_POST_VARS["priority"]) . ","; } // if we are creating an issue for a customer, put the // main customer contact as the reporter for it if (Customer::hasCustomerIntegration($prj_id)) { $contact_usr_id = User::getUserIDByContactID($HTTP_POST_VARS['contact']); if (empty($contact_usr_id)) { $contact_usr_id = $usr_id; } $stmt .= Misc::escapeInteger($contact_usr_id) . ","; } else { $stmt .= $usr_id . ","; } if (!empty($initial_status)) { $stmt .= Misc::escapeInteger($initial_status) . ","; } if (Customer::hasCustomerIntegration($prj_id)) { $stmt .= "\n " . Misc::escapeInteger($HTTP_POST_VARS['customer']) . ",\n " . Misc::escapeInteger($HTTP_POST_VARS['contact']) . ",\n '" . Misc::escapeString($HTTP_POST_VARS["contact_person_lname"]) . "',\n '" . Misc::escapeString($HTTP_POST_VARS["contact_person_fname"]) . "',\n '" . Misc::escapeString($HTTP_POST_VARS["contact_email"]) . "',\n '" . Misc::escapeString($HTTP_POST_VARS["contact_phone"]) . "',\n '" . Misc::escapeString($HTTP_POST_VARS["contact_timezone"]) . "',"; } $stmt .= "\n '" . Date_API::getCurrentDateGMT() . "',\n '" . Date_API::getCurrentDateGMT() . "',\n 'created',\n '" . Misc::escapeString($HTTP_POST_VARS["summary"]) . "',\n '" . Misc::escapeString($HTTP_POST_VARS["description"]) . "',\n " . Misc::escapeString($HTTP_POST_VARS["estimated_dev_time"]) . ",\n " . Misc::escapeInteger($HTTP_POST_VARS["private"]) . " ,\n '" . Misc::escapeString(Mail_API::generateMessageID()) . "'\n )"; $res = $GLOBALS["db_api"]->dbh->query($stmt); if (PEAR::isError($res)) { Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); return -1; } else { $new_issue_id = $GLOBALS["db_api"]->get_last_insert_id(); $has_TAM = false; $has_RR = false; $info = User::getNameEmail($usr_id); // log the creation of the issue History::add($new_issue_id, Auth::getUserID(), History::getTypeID('issue_opened'), 'Issue opened by ' . User::getFullName(Auth::getUserID())); $emails = array(); if (Customer::hasCustomerIntegration($prj_id)) { if (@count($HTTP_POST_VARS['contact_extra_emails']) > 0) { $emails = $HTTP_POST_VARS['contact_extra_emails']; } // add the primary contact to the notification list if ($HTTP_POST_VARS['add_primary_contact'] == 'yes') { $contact_email = User::getEmailByContactID($HTTP_POST_VARS['contact']); if (!empty($contact_email)) { $emails[] = $contact_email; } } // if there are any technical account managers associated with this customer, add these users to the notification list $managers = Customer::getAccountManagers($prj_id, $HTTP_POST_VARS['customer']); $manager_usr_ids = array_keys($managers); $manager_emails = array_values($managers); $emails = array_merge($emails, $manager_emails); } // add the reporter to the notification list $emails[] = $info['usr_email']; $emails = array_unique($emails); // COMPAT: version >= 4.0.1 $actions = Notification::getDefaultActions(); foreach ($emails as $address) { Notification::subscribeEmail($usr_id, $new_issue_id, $address, $actions); } // only assign the issue to an user if the associated customer has any technical account managers $users = array(); $has_TAM = false; if (Customer::hasCustomerIntegration($prj_id) && count($manager_usr_ids) > 0) { foreach ($manager_usr_ids as $manager_usr_id) { $users[] = $manager_usr_id; Issue::addUserAssociation($usr_id, $new_issue_id, $manager_usr_id, false); History::add($new_issue_id, $usr_id, History::getTypeID('issue_auto_assigned'), 'Issue auto-assigned to ' . User::getFullName($manager_usr_id) . ' (TAM)'); } $has_TAM = true; } // now add the user/issue association (aka assignments) if (@count($HTTP_POST_VARS["users"]) > 0) { for ($i = 0; $i < count($HTTP_POST_VARS["users"]); $i++) { Notification::subscribeUser($usr_id, $new_issue_id, $HTTP_POST_VARS["users"][$i], $actions); Issue::addUserAssociation($usr_id, $new_issue_id, $HTTP_POST_VARS["users"][$i]); if ($HTTP_POST_VARS["users"][$i] != $usr_id) { $users[] = $HTTP_POST_VARS["users"][$i]; } } } else { // only use the round-robin feature if this new issue was not // already assigned to a customer account manager if (@count($manager_usr_ids) < 1) { $assignee = Round_Robin::getNextAssignee($prj_id); // assign the issue to the round robin person if (!empty($assignee)) { $users[] = $assignee; Issue::addUserAssociation($usr_id, $new_issue_id, $assignee, false); History::add($new_issue_id, APP_SYSTEM_USER_ID, History::getTypeID('rr_issue_assigned'), 'Issue auto-assigned to ' . User::getFullName($assignee) . ' (RR)'); $has_RR = true; } } } // now process any files being uploaded $found = 0; for ($i = 0; $i < count(@$HTTP_POST_FILES["file"]["name"]); $i++) { if (!@empty($HTTP_POST_FILES["file"]["name"][$i])) { $found = 1; break; } } if ($found) { $files = array(); for ($i = 0; $i < count($HTTP_POST_FILES["file"]["name"]); $i++) { $filename = @$HTTP_POST_FILES["file"]["name"][$i]; if (empty($filename)) { continue; } $blob = Misc::getFileContents($HTTP_POST_FILES["file"]["tmp_name"][$i]); if (empty($blob)) { // error reading a file $insert_errors["file[{$i}]"] = "There was an error uploading the file '{$filename}'."; continue; } $files[] = array("filename" => $filename, "type" => $HTTP_POST_FILES['file']['type'][$i], "blob" => $blob); } if (count($files) > 0) { $attachment_id = Attachment::add($new_issue_id, $usr_id, 'Files uploaded at issue creation time'); foreach ($files as $file) { Attachment::addFile($attachment_id, $new_issue_id, $file["filename"], $file["type"], $file["blob"]); } } } // need to associate any emails ? if (!empty($HTTP_POST_VARS["attached_emails"])) { $items = explode(",", $HTTP_POST_VARS["attached_emails"]); Support::associate($usr_id, $new_issue_id, $items, true); } // need to notify any emails being converted into issues ? if (@count($HTTP_POST_VARS["notify_senders"]) > 0) { $recipients = Notification::notifyEmailConvertedIntoIssue($prj_id, $new_issue_id, $HTTP_POST_VARS["notify_senders"], $customer_id); } else { $recipients = array(); } // need to process any custom fields ? if (@count($HTTP_POST_VARS["custom_fields"]) > 0) { foreach ($HTTP_POST_VARS["custom_fields"] as $fld_id => $value) { Custom_Field::associateIssue($new_issue_id, $fld_id, $value); } } // also send a special confirmation email to the customer contact if (@$HTTP_POST_VARS['notify_customer'] == 'yes' && !empty($HTTP_POST_VARS['contact'])) { // also need to pass the list of sender emails already notified, // so we can avoid notifying the same person again $contact_email = User::getEmailByContactID($HTTP_POST_VARS['contact']); if (@(!in_array($contact_email, $recipients))) { Customer::notifyCustomerIssue($prj_id, $new_issue_id, $HTTP_POST_VARS['contact']); } } Workflow::handleNewIssue($prj_id, $new_issue_id, $has_TAM, $has_RR); // also notify any users that want to receive emails anytime a new issue is created Notification::notifyNewIssue($prj_id, $new_issue_id); return $new_issue_id; } }
/** * Splits the full email into headers and body * * @access public * @param string $message The full email message * @param boolean $unfold If headers should be unfolded * @return array An array containing the headers and body */ function splitHeaderBody($message, $unfold = true) { if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $message, $match)) { return array($unfold ? Mail_API::unfold($match[1]) : $match[1], $match[2]); } return array(); }
if (!@empty($HTTP_GET_VARS["id"])) { $email = Support::getEmailDetails($HTTP_GET_VARS["ema_id"], $HTTP_GET_VARS["id"]); $date = Misc::formatReplyDate($email["timestamp"]); $header = "To " . $email["sup_from"] . ",\n\n\nThank you, \n" . Auth::getCurrentProjectName() . "\n\nOn {$date}, " . $email["sup_from"] . " wrote:\n>\n"; $email["seb_body"] = $header . Misc::formatReply($email["message"]); $tpl->bulkAssign(array("email" => $email, "parent_email_id" => $HTTP_GET_VARS["id"])); } } // special handling when someone tries to 'reply' to an issue if (@$HTTP_GET_VARS["cat"] == 'reply') { $details = Issue::getReplyDetails($HTTP_GET_VARS['issue_id']); if ($details != '') { $date = Misc::formatReplyDate($details['created_date_ts']); $header = "To " . $details["reporter"] . ",\n\n\nThank you, \n" . Auth::getCurrentProjectName() . "\n\nOn {$date}, " . $details['reporter'] . " wrote:\n>\n"; $details['seb_body'] = $header . Misc::formatReply($details['description']); $details['sup_from'] = Mail_API::getFormattedName($details['reporter'], $details['reporter_email']); $tpl->bulkAssign(array("email" => $details, "parent_email_id" => 0, "extra_title" => "Issue #" . $HTTP_GET_VARS['issue_id'] . ": Reply")); } } if (!empty($issue_id)) { // list the available statuses $tpl->assign("statuses", Status::getAssocStatusList($prj_id, false)); $tpl->assign("current_issue_status", Issue::getStatusID($issue_id)); // set if the current user is allowed to send emails on this issue or not $sender_details = User::getDetails($usr_id); $tpl->assign("can_send_email", Support::isAllowedToEmail($issue_id, $sender_details["usr_email"])); } if (!@empty($HTTP_GET_VARS["ema_id"]) || !@empty($HTTP_POST_VARS["ema_id"])) { @$tpl->assign("ema_id", $HTTP_GET_VARS["ema_id"] ? $HTTP_GET_VARS["ema_id"] : $HTTP_POST_VARS["ema_id"]); } $tpl->assign("from", User::getFromHeader($usr_id));
/** * Method used to send a confirmation email to the user that is associated * to the email address. * * @access public * @param string $usr_id The user ID * @return void */ function sendPasswordConfirmationEmail($usr_id) { $info = User::getDetails($usr_id); // send confirmation email to user $hash = md5($info["usr_full_name"] . md5($info["usr_email"]) . $GLOBALS["private_key"]); $msg = "Hello,\n\n"; $msg .= "We just received a request to create a new random password for your account in our issue tracking system. "; $msg .= "For security reasons we need you to confirm this request so we can finish the password creation process.\n\n"; $msg .= "If this is not a real request from you, or if you don't need a new password anymore, "; $msg .= "please disregard this email.\n\n"; $msg .= "However, if you would like to confirm this request, please do so by visiting the URL below:\n\n"; $msg .= APP_BASE_URL . "confirm.php?cat=password&email=" . $info["usr_email"] . "&hash=" . $hash . "\n\n"; $setup = Setup::load(); $mail = new Mail_API(); // need to make this message MIME based $mail->setTextBody($msg); $mail->send($setup["smtp"]["from"], $info["usr_email"], APP_SHORT_NAME . ": New Password - Confirmation Required"); }
/** * Method used to update the details of a given subscription. * * @access public * @param integer $sub_id The subscription ID * @return integer 1 if the update worked, -1 otherwise */ function update($sub_id) { global $HTTP_POST_VARS; $sub_id = Misc::escapeInteger($sub_id); $stmt = "SELECT\r\n sub_iss_id,\r\n sub_usr_id\r\n FROM\r\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription\r\n WHERE\r\n sub_id={$sub_id}"; list($issue_id, $usr_id) = $GLOBALS["db_api"]->dbh->getRow($stmt); $email = strtolower(Mail_API::getEmailAddress($HTTP_POST_VARS["email"])); $usr_id = User::getUserIDByEmail($email); if (!empty($usr_id)) { $email = ''; } else { $usr_id = 0; $email = Misc::escapeString($HTTP_POST_VARS["email"]); } $prj_id = Issue::getProjectID($issue_id); // call workflow to modify actions or cancel adding this user. $actions = array(); $subscriber_usr_id = false; $workflow = Workflow::handleSubscription($prj_id, $issue_id, $subscriber_usr_id, $email, $actions); if ($workflow === false) { // cancel subscribing the user return -2; } // always set the type of notification to issue-level $stmt = "UPDATE\r\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription\r\n SET\r\n sub_level='issue',\r\n sub_email='" . Misc::escapeString($email) . "',\r\n sub_usr_id={$usr_id}\r\n WHERE\r\n sub_id={$sub_id}"; $res = $GLOBALS["db_api"]->dbh->query($stmt); if (PEAR::isError($res)) { Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); return -1; } else { $stmt = "DELETE FROM\r\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription_type\r\n WHERE\r\n sbt_sub_id={$sub_id}"; $GLOBALS["db_api"]->dbh->query($stmt); // now add them all again for ($i = 0; $i < count($HTTP_POST_VARS["actions"]); $i++) { Notification::addType($sub_id, $HTTP_POST_VARS["actions"][$i]); } // need to mark the issue as updated Issue::markAsUpdated($issue_id); // need to save a history entry for this History::add($issue_id, Auth::getUserID(), History::getTypeID('notification_updated'), "Notification list entry ('" . Notification::getSubscriber($sub_id) . "') updated by " . User::getFullName(Auth::getUserID())); return 1; } }
<?php /* * Runonce script to set the sup_usr_id field in support_email */ include_once "../../../config.inc.php"; include_once APP_INC_PATH . "db_access.php"; $stmt = "SELECT\n sup_id,\n sup_from\n FROM\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email\n WHERE\n sup_usr_id IS NULL AND\n sup_iss_id != 0"; $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt); foreach ($res as $sup_id => $email) { $usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($email)); if (!empty($usr_id)) { $stmt = "UPDATE\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email\n SET\n sup_usr_id = {$usr_id}\n WHERE\n sup_id = {$sup_id}"; $update = $GLOBALS["db_api"]->dbh->query($stmt); if (PEAR::isError($update)) { echo "<pre>"; var_dump($update); echo "</pre>"; exit(1); } } } echo "complete";
function getBaseThreadingHeaders($issue_id) { $root_msg_id = Issue::getRootMessageID($issue_id); return array("Message-ID" => Mail_API::generateMessageID(), "In-Reply-To" => $root_msg_id, "References" => $root_msg_id); }
Support::removeEmail($HTTP_POST_VARS['item'][$i]); } } $tpl->assign("associate_result", $res); } @$tpl->assign('total_emails', count($HTTP_POST_VARS['item'])); } else { @$tpl->assign('emails', $HTTP_GET_VARS['item']); @$tpl->assign('total_emails', count($HTTP_GET_VARS['item'])); $prj_id = Issue::getProjectID($HTTP_GET_VARS['issue']); if (Customer::hasCustomerIntegration($prj_id)) { // check if the selected emails all have sender email addresses that are associated with the issue' customer $senders = Support::getSender($HTTP_GET_VARS['item']); $sender_emails = array(); for ($i = 0; $i < count($senders); $i++) { $email = Mail_API::getEmailAddress($senders[$i]); $sender_emails[$email] = $senders[$i]; } $customer_id = Issue::getCustomerID($HTTP_GET_VARS['issue']); if (!empty($customer_id)) { $contact_emails = array_keys(Customer::getContactEmailAssocList($prj_id, $customer_id)); $unknown_contacts = array(); foreach ($sender_emails as $email => $address) { if (!@in_array($email, $contact_emails)) { $usr_id = User::getUserIDByEmail($email); if (empty($usr_id)) { $unknown_contacts[] = $address; } else { // if we got a real user ID, check if the customer user is the correct one // (i.e. a contact from the customer associated with the selected issue) if (User::getRoleByUser($usr_id, $prj_id) == User::getRoleID('Customer')) {
/** * Returns if the specified user is authorized to reply to this issue. * * @access public * @param integer $issue_id The id of the issue. * @param string $email The email address to check. * @return boolean If the specified user is allowed to reply to the issue. */ function isAuthorizedReplier($issue_id, $email) { $email = strtolower(Mail_API::getEmailAddress($email)); // first check if this is an actual user or just an email address $user_emails = User::getAssocEmailList(); if (in_array($email, array_keys($user_emails))) { // real user, get id $usr_id = User::getUserIDByEmail($email); return Authorized_Replier::isUserAuthorizedReplier($issue_id, $usr_id); } else { // not a real user $stmt = "SELECT\n COUNT(*) AS total\n FROM\n " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user_replier\n WHERE\n iur_iss_id=" . Misc::escapeInteger($issue_id) . " AND\n iur_email='" . Misc::escapeString($email) . "'"; $res = $GLOBALS["db_api"]->dbh->getOne($stmt); if (PEAR::isError($res)) { Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); return false; } else { if ($res > 0) { return true; } else { return false; } } } }
if (!@empty($HTTP_POST_VARS['new_status'])) { $res = Issue::setStatus($issue_id, $HTTP_POST_VARS['new_status']); if ($res != -1) { $new_status = Status::getStatusTitle($HTTP_POST_VARS['new_status']); History::add($issue_id, $usr_id, History::getTypeID('status_changed'), "Status changed to '{$new_status}' by " . User::getFullName($usr_id)); } } $res = Note::insert($usr_id, $issue_id); $tpl->assign("post_result", $res); // enter the time tracking entry about this phone support entry if (!empty($HTTP_POST_VARS['time_spent'])) { $HTTP_POST_VARS['issue_id'] = $issue_id; $HTTP_POST_VARS['category'] = $HTTP_POST_VARS['time_category']; $HTTP_POST_VARS['summary'] = 'Time entry inserted when sending an internal note.'; Time_Tracking::insertEntry(); } } elseif (@$HTTP_GET_VARS["cat"] == "reply") { if (!@empty($HTTP_GET_VARS["id"])) { $note = Note::getDetails($HTTP_GET_VARS["id"]); $date = Misc::formatReplyDate($note["timestamp"]); $header = "\n\n\nOn {$date}, " . $note["not_from"] . " wrote:\n>\n"; $note["not_body"] = $header . Misc::formatReply($note["not_note"]); $tpl->bulkAssign(array("note" => $note, "parent_note_id" => $HTTP_GET_VARS["id"])); $reply_subject = Mail_API::removeExcessRe($note['not_title']); } } if (empty($reply_subject)) { $reply_subject = 'Re: ' . $details['iss_summary']; } $tpl->assign(array('from' => User::getFromHeader($usr_id), 'users' => Project::getUserAssocList($prj_id, 'active', User::getRoleID('Customer')), 'current_user_prefs' => Prefs::get($usr_id), 'subscribers' => Notification::getSubscribers($issue_id, false, User::getRoleID("Standard User")), 'statuses' => Status::getAssocStatusList($prj_id, false), 'current_issue_status' => Issue::getStatusID($issue_id), 'time_categories' => Time_Tracking::getAssocCategories(), 'note_category_id' => Time_Tracking::getCategoryID('Note Discussion'), 'reply_subject' => $reply_subject)); $tpl->displayTemplate();
/** * Routes a draft to the correct issue. * * @param string $full_message The complete draft. */ function route_drafts($full_message) { global $HTTP_POST_VARS; // save the full message for logging purposes Draft::saveRoutedMessage($full_message); if (preg_match("/^(boundary=).*/m", $full_message)) { $pattern = "/(Content-Type: multipart\\/)(.+); ?\r?\n(boundary=)(.*)\$/im"; $replacement = '$1$2; $3$4'; $full_message = preg_replace($pattern, $replacement, $full_message); } // need some validation here if (empty($full_message)) { return array(66, "Error: The email message was empty.\n"); } // // DON'T EDIT ANYTHING BELOW THIS LINE // // remove the reply-to: header if (preg_match("/^(reply-to:).*/im", $full_message)) { $full_message = preg_replace("/^(reply-to:).*\n/im", '', $full_message, 1); } // check if the draft interface is even supposed to be enabled $setup = Setup::load(); if (@$setup['draft_routing']['status'] != 'enabled') { return array(78, "Error: The email draft interface is disabled.\n"); } $prefix = $setup['draft_routing']['address_prefix']; // escape plus signs so '*****@*****.**' becomes a valid address $prefix = str_replace('+', '\\+', $prefix); $mail_domain = quotemeta($setup['draft_routing']['address_host']); if (empty($prefix)) { return array(78, "Error: Please configure the email address prefix.\n"); } if (empty($mail_domain)) { return array(78, "Error: Please configure the email address domain.\n"); } $structure = Mime_Helper::decode($full_message, true, false); // find which issue ID this email refers to @preg_match("/{$prefix}(\\d*)@{$mail_domain}/i", $structure->headers['to'], $matches); @($issue_id = $matches[1]); // validation is always a good idea if (empty($issue_id)) { // we need to try the Cc header as well @preg_match("/{$prefix}(\\d*)@{$mail_domain}/i", $structure->headers['cc'], $matches); if (!empty($matches[1])) { $issue_id = $matches[1]; } else { return array(65, "Error: The routed draft had no associated Eventum issue ID or had an invalid recipient address.\n"); } } $prj_id = Issue::getProjectID($issue_id); // check if the sender is allowed in this issue' project and if it is an internal user $users = Project::getUserEmailAssocList($prj_id, 'active', User::getRoleID('Customer')); $sender_email = strtolower(Mail_API::getEmailAddress($structure->headers['from'])); $user_emails = array_map('strtolower', array_values($users)); if (!in_array($sender_email, $user_emails)) { return array(77, "Error: The sender of this email is not allowed in the project associated with issue #{$issue_id}.\n"); } Auth::createFakeCookie(User::getUserIDByEmail($sender_email), $prj_id); $body = Mime_Helper::getMessageBody($structure); Draft::saveEmail($issue_id, @$structure->headers['to'], @$structure->headers['cc'], @$structure->headers['subject'], $body, false, false, false); // XXX: need to handle attachments coming from drafts as well? History::add($issue_id, Auth::getUserID(), History::getTypeID('draft_routed'), "Draft routed from " . $structure->headers['from']); return true; }