/** * Sets the current selected project for the user session. * * @access public * @param integer $project The project ID * @param integer $remember Whether to automatically remember the setting or not * @return void */ function setCurrentProject($project, $remember) { $cookie = array("prj_id" => $project, "remember" => $remember); $cookie = base64_encode(serialize($cookie)); setcookie(APP_PROJECT_COOKIE, $cookie, APP_PROJECT_COOKIE_EXPIRE, APP_COOKIE_URL, APP_COOKIE_DOMAIN); Auth::createFakeCookie(Auth::getUserID(), $project); }
/** * Creates a new issue from an email if appropriate. Also returns if this message is related * to a previous message. * * @param array $info An array of info about the email account. * @param string $headers The headers of the email. * @param string $message_body The body of the message. * @param string $date The date this message was sent * @param string $from The name and email address of the sender. * @param string $subject The subject of this message. * @param array $to An array of to addresses * @param array $cc An array of cc addresses * @return array An array of information about the message */ public function createIssueFromEmail($info, $headers, $message_body, $date, $from, $subject, $to, $cc) { $should_create_issue = false; $issue_id = ''; $associate_email = ''; $type = 'email'; $parent_id = ''; $customer_id = false; $contact_id = false; $contract_id = false; $severity = false; // we can't trust the in-reply-to from the imap c-client, so let's // try to manually parse that value from the full headers $references = Mail_Helper::getAllReferences($headers); $message_id = Mail_Helper::getMessageID($headers, $message_body); $workflow = Workflow::getIssueIDforNewEmail($info['ema_prj_id'], $info, $headers, $message_body, $date, $from, $subject, $to, $cc); if (is_array($workflow)) { if (isset($workflow['customer_id'])) { $customer_id = $workflow['customer_id']; } if (isset($workflow['contract_id'])) { $contract_id = $workflow['contract_id']; } if (isset($workflow['contact_id'])) { $contact_id = $workflow['contact_id']; } if (isset($workflow['severity'])) { $severity = $workflow['severity']; } if (isset($workflow['should_create_issue'])) { $should_create_issue = $workflow['should_create_issue']; } else { $should_create_issue = true; } } elseif ($workflow == 'new') { $should_create_issue = true; } elseif (is_numeric($workflow)) { $issue_id = $workflow; } else { $setup = Setup::load(); if (@$setup['subject_based_routing']['status'] == 'enabled') { // Look for issue ID in the subject line // look for [#XXXX] in the subject line if (preg_match("/\\[#(\\d+)\\]( Note| BLOCKED)*/", $subject, $matches)) { $should_create_issue = false; $issue_id = $matches[1]; if (!Issue::exists($issue_id, false)) { $issue_id = ''; } elseif (!empty($matches[2])) { $type = 'note'; } } else { $should_create_issue = true; } } else { // - if this email is a reply: if (count($references) > 0) { foreach ($references as $reference_msg_id) { // -> check if the replied email exists in the database: if (Note::exists($reference_msg_id)) { // note exists // get what issue it belongs too. $issue_id = Note::getIssueByMessageID($reference_msg_id); $should_create_issue = false; $type = 'note'; $parent_id = Note::getIDByMessageID($reference_msg_id); break; } elseif (self::exists($reference_msg_id) || Issue::getIssueByRootMessageID($reference_msg_id) != false) { // email or issue exists $issue_id = self::getIssueByMessageID($reference_msg_id); if (empty($issue_id)) { $issue_id = Issue::getIssueByRootMessageID($reference_msg_id); } if (empty($issue_id)) { // parent email isn't associated with issue. // --> create new issue, associate current email and replied email to this issue $should_create_issue = true; $associate_email = $reference_msg_id; } else { // parent email is associated with issue: // --> associate current email with existing issue $should_create_issue = false; } break; } else { // no matching note, email or issue: // => create new issue and associate current email with it $should_create_issue = true; } } } else { // - if this email is not a reply: // -> create new issue and associate current email with it $should_create_issue = true; } } } $sender_email = Mail_Helper::getEmailAddress($from); if (Misc::isError($sender_email)) { $sender_email = 'Error Parsing Email <>'; } // only create a new issue if this email is coming from a known customer if ($should_create_issue && $info['ema_issue_auto_creation_options']['only_known_customers'] == 'yes' && CRM::hasCustomerIntegration($info['ema_prj_id']) && !$customer_id) { try { $crm = CRM::getInstance($info['ema_prj_id']); $should_create_issue = true; } catch (CRMException $e) { $should_create_issue = false; } } // check whether we need to create a new issue or not if ($info['ema_issue_auto_creation'] == 'enabled' && $should_create_issue && !Notification::isBounceMessage($sender_email)) { $options = Email_Account::getIssueAutoCreationOptions($info['ema_id']); Auth::createFakeCookie(APP_SYSTEM_USER_ID, $info['ema_prj_id']); $issue_id = Issue::createFromEmail($info['ema_prj_id'], APP_SYSTEM_USER_ID, $from, Mime_Helper::decodeQuotedPrintable($subject), $message_body, @$options['category'], @$options['priority'], @$options['users'], $date, $message_id, $severity, $customer_id, $contact_id, $contract_id); // add sender to authorized repliers list if they are not a real user $sender_usr_id = User::getUserIDByEmail($sender_email, true); if (empty($sender_usr_id)) { Authorized_Replier::manualInsert($issue_id, $sender_email, false); } // associate any existing replied-to email with this new issue if (!empty($associate_email) && !empty($reference_issue_id)) { $reference_sup_id = self::getIDByMessageID($associate_email); self::associate(APP_SYSTEM_USER_ID, $issue_id, array($reference_sup_id)); } } // need to check crm for customer association if (!empty($from)) { if (CRM::hasCustomerIntegration($info['ema_prj_id']) && !$customer_id) { // check for any customer contact association try { $crm = CRM::getInstance($info['ema_prj_id']); $contact = $crm->getContactByEmail($sender_email); $contact_id = $contact->getContactID(); $contracts = $contact->getContracts(array(CRM_EXCLUDE_EXPIRED)); $contract = $contracts[0]; $customer_id = $contract->getCustomerID(); } catch (CRMException $e) { $customer_id = null; $contact_id = null; } } } return array('should_create_issue' => $should_create_issue, 'associate_email' => $associate_email, 'issue_id' => $issue_id, 'customer_id' => $customer_id, 'contact_id' => $contact_id, 'type' => $type, 'parent_id' => $parent_id); }
/** * Creates a new issue from an email if appropriate. Also returns if this message is related * to a previous message. * * @access private * @param array $info An array of info about the email account. * @param string $headers The headers of the email. * @param string $message_body The body of the message. * @param string $date The date this message was sent * @param string $from The name and email address of the sender. * @param string $subject The subject of this message. * @return array An array of information about the message */ function createIssueFromEmail($info, $headers, $message_body, $date, $from, $subject) { $should_create_issue = false; $issue_id = ''; $associate_email = ''; $type = 'email'; $parent_id = ''; // we can't trust the in-reply-to from the imap c-client, so let's // try to manually parse that value from the full headers $references = Mail_API::getAllReferences($headers); $message_id = Mail_API::getMessageID($headers, $message_body); $setup = Setup::load(); if (@$setup['subject_based_routing']['status'] == 'enabled' && preg_match("/\\[#(\\d+)\\]( Note| BLOCKED)*/", $subject, $matches)) { $should_create_issue = false; $issue_id = $matches[1]; if (!Issue::exists($issue_id, false)) { $issue_id = ''; } elseif (!empty($matches[2])) { $type = 'note'; } } else { // - if this email is a reply: if (count($references) > 0) { foreach ($references as $reference_msg_id) { // -> check if the replied email exists in the database: if (Note::exists($reference_msg_id)) { // note exists // get what issue it belongs too. $issue_id = Note::getIssueByMessageID($reference_msg_id); $should_create_issue = false; $type = 'note'; $parent_id = Note::getIDByMessageID($reference_msg_id); break; } elseif (Support::exists($reference_msg_id) || Issue::getIssueByRootMessageID($reference_msg_id) != false) { // email or issue exists $issue_id = Support::getIssueByMessageID($reference_msg_id); if (empty($issue_id)) { $issue_id = Issue::getIssueByRootMessageID($reference_msg_id); } if (empty($issue_id)) { // parent email isn't associated with issue. // --> create new issue, associate current email and replied email to this issue $should_create_issue = true; $associate_email = $reference_msg_id; } else { // parent email is associated with issue: // --> associate current email with existing issue $should_create_issue = false; } break; } else { // no matching note, email or issue: // => create new issue and associate current email with it $should_create_issue = true; } } } else { // - if this email is not a reply: // -> create new issue and associate current email with it $should_create_issue = true; } if (empty($issue_id)) { $issue_id = Issue::getIssueBy($subject, 'iss_summary'); if (!empty($issue_id)) { $should_create_issue = false; } } } $sender_email = Mail_API::getEmailAddress($from); // only create a new issue if this email is coming from a known customer if ($should_create_issue && $info['ema_issue_auto_creation_options']['only_known_customers'] == 'yes' && Customer::hasCustomerIntegration($info['ema_prj_id'])) { list($customer_id, ) = Customer::getCustomerIDByEmails($info['ema_prj_id'], array($sender_email)); if (empty($customer_id)) { $should_create_issue = false; } } // check whether we need to create a new issue or not if ($info['ema_issue_auto_creation'] == 'enabled' && $should_create_issue) { $options = Email_Account::getIssueAutoCreationOptions($info['ema_id']); Auth::createFakeCookie(APP_SYSTEM_USER_ID, $info['ema_prj_id']); $issue_id = Issue::createFromEmail($info['ema_prj_id'], APP_SYSTEM_USER_ID, $from, Mime_Helper::fixEncoding($subject), $message_body, @$options['category'], $options['priority'], @$options['users'], $date, $message_id); // associate any existing replied-to email with this new issue if (!empty($associate_email) && !empty($reference_issue_id)) { $reference_sup_id = Support::getIDByMessageID($associate_email); Support::associate(APP_SYSTEM_USER_ID, $issue_id, array($reference_sup_id)); } } // need to check crm for customer association if (!empty($from)) { $details = Email_Account::getDetails($info['ema_id']); if (Customer::hasCustomerIntegration($info['ema_prj_id'])) { // check for any customer contact association @(list($customer_id, ) = Customer::getCustomerIDByEmails($info['ema_prj_id'], array($sender_email))); } } return array('should_create_issue' => $should_create_issue, 'associate_email' => $associate_email, 'issue_id' => $issue_id, 'customer_id' => @$customer_id, 'type' => $type, 'parent_id' => $parent_id); }
/** * Authorize request. * TODO: translations * TODO: ip based control */ function authorizeRequest() { // try current auth cookie $usr_id = Auth::getUserID(); if (!$usr_id) { // otherwise setup HTTP Auth headers $authData = getAuthData(); if ($authData === null) { sendAuthenticateHeader(); echo 'Error: You are required to authenticate in order to access the requested RSS feed.'; exit; } list($authUser, $authPassword) = $authData; // check the authentication if (Validation::isWhitespace($authUser)) { sendAuthenticateHeader(); echo 'Error: Please provide your email address.'; exit; } if (Validation::isWhitespace($authPassword)) { sendAuthenticateHeader(); echo 'Error: Please provide your password.'; exit; } // check if user exists if (!Auth::userExists($authUser)) { sendAuthenticateHeader(); echo 'Error: The user specified does not exist.'; exit; } // check if the password matches if (!Auth::isCorrectPassword($authUser, $authPassword)) { sendAuthenticateHeader(); echo 'Error: The provided email address/password combo is not correct.'; exit; } // check if this user did already confirm his account if (Auth::isPendingUser($authUser)) { sendAuthenticateHeader(); echo 'Error: The provided user still needs to have its account confirmed.'; exit; } // check if this user is really an active one if (!Auth::isActiveUser($authUser)) { sendAuthenticateHeader(); echo 'Error: The provided user is currently set as an inactive user.'; exit; } $usr_id = User::getUserIDByEmail($authUser); Auth::createFakeCookie($usr_id); } // check if the required parameter 'custom_id' is really being passed if (empty($_GET['custom_id'])) { rssError("Error: The required 'custom_id' parameter was not provided."); exit; } // check if the passed 'custom_id' parameter is associated with the usr_id if (!Filter::isGlobal($_GET['custom_id']) && !Filter::isOwner($_GET['custom_id'], $usr_id)) { rssError('Error: The provided custom filter ID is not associated with the given email address.'); exit; } }
exit; } // check if the required parameter 'custom_id' is really being passed if (empty($HTTP_GET_VARS['custom_id'])) { returnError("Error: The required 'custom_id' parameter was not provided."); exit; } $usr_id = User::getUserIDByEmail($HTTP_SERVER_VARS['PHP_AUTH_USER']); // check if the passed 'custom_id' parameter is associated with the usr_id if (!Filter::isGlobal($HTTP_GET_VARS['custom_id']) && !Filter::isOwner($HTTP_GET_VARS['custom_id'], $usr_id)) { returnError('Error: The provided custom filter ID is not associated with the given email address.'); exit; } } $filter = Filter::getDetails($HTTP_GET_VARS["custom_id"], FALSE); Auth::createFakeCookie(User::getUserIDByEmail($HTTP_SERVER_VARS['PHP_AUTH_USER']), $filter['cst_prj_id']); $options = array('users' => $filter['cst_users'], 'keywords' => $filter['cst_keywords'], 'priority' => $filter['cst_iss_pri_id'], 'category' => $filter['cst_iss_prc_id'], 'status' => $filter['cst_iss_sta_id'], 'hide_closed' => $filter['cst_hide_closed'], 'hide_answered' => $filter['cst_hide_answered'], 'sort_by' => $filter['cst_sort_by'], 'sort_order' => $filter['cst_sort_order']); $issues = Issue::getListing($filter['cst_prj_id'], $options, 0, 'ALL', TRUE); $issues = $issues['list']; $project_title = Project::getName($filter['cst_prj_id']); Issue::getDescriptionByIssues($issues); Header("Content-Type: text/xml; charset=" . APP_CHARSET); echo '<?xml version="1.0" encoding="' . APP_CHARSET . '"?>' . "\n"; ?> <rss version="2.0" > <channel> <title><?php echo htmlspecialchars($setup['tool_caption']); ?> - <?php
/** * Routes a draft to the correct issue. * * @param string $full_message The complete draft. * @return mixed true or array(ERROR_CODE, ERROR_STRING) in case of failure */ public static function route_drafts($full_message) { // 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(self::EX_NOINPUT, ev_gettext('Error: The email message was empty.') . "\n"); } // 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(self::EX_CONFIG, ev_gettext('Error: The email draft interface is disabled.') . "\n"); } if (empty($setup['draft_routing']['address_prefix'])) { return array(self::EX_CONFIG, ev_gettext('Error: Please configure the email address prefix.') . "\n"); } if (empty($setup['draft_routing']['address_host'])) { return array(self::EX_CONFIG, ev_gettext('Error: Please configure the email address domain.') . "\n"); } $structure = Mime_Helper::decode($full_message, true, false); // find which issue ID this email refers to if (isset($structure->headers['to'])) { $issue_id = self::getMatchingIssueIDs($structure->headers['to'], 'draft'); } // validation is always a good idea if (empty($issue_id) and isset($structure->headers['cc'])) { // we need to try the Cc header as well $issue_id = self::getMatchingIssueIDs($structure->headers['cc'], 'draft'); } if (empty($issue_id)) { return array(self::EX_DATAERR, ev_gettext('Error: The routed email 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 $sender_email = strtolower(Mail_Helper::getEmailAddress($structure->headers['from'])); $sender_usr_id = User::getUserIDByEmail($sender_email, true); if (!empty($sender_usr_id)) { $sender_role = User::getRoleByUser($sender_usr_id, $prj_id); if ($sender_role < User::getRoleID('Standard User')) { return array(self::EX_NOPERM, ev_gettext("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 = $structure->body; 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? $usr_id = Auth::getUserID(); History::add($issue_id, $usr_id, 'draft_routed', 'Draft routed from {from}', array('from' => $structure->headers['from'])); return true; }
/** * 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; }