--- eventum-1.7.1/include/class.mail_queue.php Thu Mar 30 14:13:13 2006 +++ /usr/local/www/eventum/include/class.mail_queue.php Sat May 31 16:37:25 2008 @@ -111,6 +111,10 @@ if (!empty($headers['To'])) { $headers['To'] = Mail_API::fixAddressQuoting($headers['To']); } + if (in_array('Content-Transfer-Encoding', array_keys($headers)) && + $headers['Content-Transfer-Encoding'] == 'base64') { + unset($headers['Content-Transfer-Encoding']); + } list(,$text_headers) = Mail_API::prepareHeaders($headers); @@ -445,4 +449,4 @@ if (APP_BENCHMARK) { $GLOBALS['bench']->setMarker('Included Mail_Queue Class'); } -?> \ No newline at end of file +?> --- eventum-1.7.1/include/class.notification.php Thu Mar 30 14:13:14 2006 +++ /usr/local/www/eventum/include/class.notification.php Mon Jan 19 20:38:36 2009 @@ -44,11 +44,13 @@ include_once(APP_INC_PATH . "class.custom_field.php"); include_once(APP_INC_PATH . "class.template.php"); include_once(APP_INC_PATH . "class.mail.php"); +include_once(APP_INC_PATH . "class.mime_helper.php"); include_once(APP_INC_PATH . "class.date.php"); include_once(APP_INC_PATH . "class.project.php"); include_once(APP_INC_PATH . "class.history.php"); include_once(APP_INC_PATH . "class.issue.php"); include_once(APP_INC_PATH . "class.priority.php"); +include_once(APP_PEAR_PATH . "Mail/mimePart.php"); class Notification { @@ -77,6 +79,251 @@ } } + function createSubPartParams($mimePart) + { + $params['content_type'] = $mimePart->headers['content-type']; + + if (isset($mimePart->headers['content-transfer-encoding'])) { + $params['encoding'] = $mimePart->headers['content-transfer-encoding']; + } + + if (isset($mimePart->headers['content-disposition'])) { + $params['disposition'] = $mimePart->headers['content-disposition']; + } + + if (isset($mimePart->d_parameters['filename'])) { + $params['dfilename'] = $mimePart->d_parameters['filename']; + } + + return $params; + } + + function createNewBody($headers, $oldBody, $header) + { + if (stristr($oldBody, "
/i", "$0\n\n$header\n", $oldBody); + } elseif (stristr($headers['content-type'], "text/") && + !stristr($headers['content-disposition'], "attachment")) { + // Don't insert the header into text attachments! + if (stristr($headers['content-type'], "text/html")) { + // HTML email needs a bit more formatting to prevent + // display problems + $newBody = "
\n" . $header . "\n" . $oldBody; + } else { + $newBody = $header . $oldBody; + } + } else { + $newBody = $oldBody; + } + + return $newBody; + } + + function replaceMimePartBoundary($oldBoundaryStr, $newBoundaryStr, $message) + { + // Gah - fix up the boundary because there's no way to + // set it manually in the Mail_mimePart ctor + $boundaryRegexp = "boundary=\"?([^\"]*)\"?"; + + Error_Handler::logToFile("oldBoundaryStr = [$oldBoundaryStr]", __FILE__, __LINE__); + Error_Handler::logToFile("newBoundaryStr = [$newBoundaryStr]", __FILE__, __LINE__); + + $numMatches = preg_match("/$boundaryRegexp/i", $oldBoundaryStr, $matches); + Error_Handler::logToFile("numMatches (old boundary) = $numMatches", __FILE__, __LINE__); + $old_boundary = $matches[1]; + Error_Handler::logToFile("old_boundary = $old_boundary", __FILE__, __LINE__); + + $numMatches = preg_match("/$boundaryRegexp/i", $newBoundaryStr, $matches); + Error_Handler::logToFile("numMatches (new boundary) = $numMatches", __FILE__, __LINE__); + $new_boundary = $matches[1]; + Error_Handler::logToFile("new_boundary = $new_boundary", __FILE__, __LINE__); + + return str_replace($new_boundary, $old_boundary, $message); + } + + function ©MimeParts($sourceMessage, &$sourceBoundary, $header) + { + if (Notification::messageHasHeaders($sourceMessage)) { + // Since we cannot specify our own boundary characters, make + // sure to strip them out when setting the content type + if (preg_match("/^(.*?);\s+boundary=/", $sourceMessage->headers['content-type'], $matches)) { + $destParams['content_type'] = $matches[1]; + } else { + $destParams['content_type'] = $sourceMessage->headers['content-type']; + } + } else { + $destParams['content_type'] = 'multipart/mixed'; + } + + $destMessage = new Mail_mimePart('', $destParams); + + if (Notification::messageHasParts($sourceMessage)) { + $numSourceParts = count($sourceMessage->parts); + + for ($i = 0; $i < $numSourceParts; $i++) { + $currentPart = $sourceMessage->parts[$i]; + + if (Notification::messageHasParts($currentPart)) { + if (Notification::messageHasBoundary($currentPart)) { + $currentBoundary = $currentPart->ctype_parameters['boundary']; + } else { + $currentBoundary = ''; + } + + $subPart =& Notification::copyMimeParts($currentPart, $currentBoundary, $header); + $encodedSubPart = $subPart->encode(); + $subPartBody = $encodedSubPart['body']; + + $subPartBody = Notification::replaceMimePartBoundary($currentPart->headers['content-type'], $encodedSubPart['headers']['Content-Type'], $subPartBody); + + $subPart =& $destMessage->addSubPart($subPartBody, Notification::createSubPartParams($currentPart)); + } + + if (Notification::messageHasBody($currentPart)) { + $newBody = Notification::createNewBody($currentPart->headers, $currentPart->body, $header); + + $subPartParams = Notification::createSubPartParams($currentPart); + $subPart =& $destMessage->addSubPart($newBody, $subPartParams); + } + } + } + + return $destMessage; + } + + function messageHasBoundary($message) + { + $hasCtype = Notification::messageHasElement($message, 'ctype_parameters'); + if (!$hasCtype) { + return false; + } else { + $objectVars = get_object_vars($message); + $ctypeArray = $objectVars['ctype_parameters']; + return array_key_exists('boundary', $ctypeArray); + } + } + + function messageHasParts($message) + { + return Notification::messageHasElement($message, 'parts'); + } + + function messageHasBody($message) + { + return Notification::messageHasElement($message, 'body'); + } + + function messageHasHeaders($message) + { + return Notification::messageHasElement($message, 'headers'); + } + + function messageHasElement($message, $element) + { + $objectVars = get_object_vars($message); + if (!empty($objectVars)) { + return array_key_exists($element, $objectVars); + } else { + return false; + } + } + + /** + * This method is used to create an information header + * that can be added to emails to describe who the message and + * future replies will be sent to. The recipients are + * partitioned into a list of staff members and a list of + * customers. + */ + function insertInformationalEmailHeader($issue_id, $recips, $sender, $message) + { + if (!empty($message)) { + list($headers, $body) = Mime_Helper::splitBodyHeader($message); + $decoded_message = Mime_Helper::decode($message, TRUE, TRUE); + + if (Notification::messageHasBoundary($decoded_message)) { + $boundary = $decoded_message->ctype_parameters['boundary']; + } else { + $boundary = ''; + } + + if (Notification::messageHasParts($decoded_message)) { + $num_parts = count($decoded_message->parts); + } else { + $num_parts = -1; + } + } else { + $decoded_message = $message; + $num_parts = 0; + } + + // Add the sender to the array of recipients, if specified + // and not already present. This is a bit of a hack, because + // it's only used to create the header down below. An email isn't + // necessarily going to be sent to the sender right now, but + // an email will be sent when someone replies to this message. + // Since the header is supposed to show who *would* get a message + // if someone replies, we manually add the sender to the list + // of recips. + if (!empty($sender) && !in_array($sender, $recips)) { + array_push($recips, $sender); + } + + $cust_role_id = User::getRoleID("Customer"); + $header = "========================================================\n"; + $header .= "This message and your reply sent to\n"; + $staff_header = "Staff: "; + $cust_header = "Customers: "; + + for ($i = 0; $i < count($recips); $i++) { + // We have to extract the email address from inside the <...> + // if present + if (preg_match("/<(.*?)>/", $recips[$i], $matches)) { + $user_email = $matches[1]; + } else { + $user_email = $recips[$i]; + } + + Error_Handler::logToFile("Processing email: $user_email", __FILE__, __LINE__); + + $usr_id = User::getUserIDByEmail($user_email); + Error_Handler::logToFile("Email has usr_id $usr_id", __FILE__, __LINE__); + $user_role_id = User::getRoleByUser($usr_id, Issue::getProjectID($issue_id)); + Error_Handler::logToFile("Email has user_role_id $user_role_id", __FILE__, __LINE__); + if (empty($user_role_id) || User::getRoleByUser($usr_id, Issue::getProjectID($issue_id)) == $cust_role_id) { + $cust_header .= $user_email . ", "; + } else { + $staff_header .= $user_email . ", "; + } + } + + $cust_header = rtrim($cust_header, ", ") . "\n"; + $staff_header = rtrim($staff_header, ", ") . "\n"; + + $header .= $staff_header; + $header .= $cust_header; + $header .= "========================================================\n\n"; + + $new_body = ''; + if ($num_parts > 0) { + // Recursively build the new message + $mimeMessage =& Notification::copyMimeParts($decoded_message, $boundary, $header); + + $encoded_message = $mimeMessage->encode(); + $new_body = $encoded_message['body']; + + $new_body = Notification::replaceMimePartBoundary($decoded_message->headers['content-type'], $encoded_message['headers']['Content-Type'], $new_body); + } elseif ($num_parts == -1) { + $new_body = $header . $decoded_message->body; + } + + if ($new_body == '') { + return $header; + } else { + return $new_body; + } + } + /** * Method used to get the list of email addresses currently @@ -256,6 +503,7 @@ $sender = $message['from']; $sender_email = strtolower(Mail_API::getEmailAddress($sender)); + Error_Handler::logToFile("sender_email: $sender_email", __FILE__, __LINE__); // get ID of whoever is sending this. $sender_usr_id = User::getUserIDByEmail($sender_email); if (empty($sender_usr_id)) { @@ -348,6 +596,8 @@ } } + $body = Notification::insertInformationalEmailHeader($issue_id, $emails, $sender_email, $full_message); + @include_once(APP_PEAR_PATH . 'Mail/mime.php'); foreach ($emails as $to) { $recipient_usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($to)); @@ -873,6 +1123,8 @@ $final_type = $type; $sender_usr_id = false; $threading_headers = Mail_API::getBaseThreadingHeaders($issue_id); + $message_header = Notification::insertInformationalEmailHeader($issue_id, $emails, '', ''); + for ($i = 0; $i < count($emails); $i++) { $can_access = true; @@ -901,7 +1153,7 @@ // send email (use PEAR's classes) $mail = new Mail_API; - $mail->setTextBody($tpl->getTemplateContents()); + $mail->setTextBody($message_header . $tpl->getTemplateContents()); if ($headers != false) { $mail->setHeaders($headers); } @@ -1413,6 +1665,7 @@ "data" => $data )); $text_message = $tpl->getTemplateContents(); + $message_header = Notification::insertInformationalEmailHeader($issue_id, $assignees, '', ''); for ($i = 0; $i < count($assignees); $i++) { if (!Workflow::shouldEmailAddress($prj_id, Mail_API::getEmailAddress(User::getFromHeader($assignees[$i])))) { @@ -1420,7 +1673,7 @@ } // send email (use PEAR's classes) $mail = new Mail_API; - $mail->setTextBody($text_message); + $mail->setTextBody($message_header . $text_message); $mail->setHeaders(Mail_API::getBaseThreadingHeaders($issue_id)); $mail->send(Notification::getFixedFromHeader($issue_id, '', 'issue'), User::getFromHeader($assignees[$i]), "[#$issue_id] $title: " . $issue['iss_summary'], TRUE, $issue_id, $type); } @@ -1541,6 +1794,10 @@ $stmt .= " AND\npru_role >= " . Misc::escapeInteger($min_role); } $users = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); + if (PEAR::isError($users)) { + Error_Handler::logError(array($users->getMessage(), $users->getDebugInfo()), __FILE__, __LINE__); + return ""; + } for ($i = 0; $i < count($users); $i++) { if ($users[$i]['pru_role'] != User::getRoleID('Customer')) { $subscribers['staff'][] = $users[$i]['usr_full_name']; @@ -2089,4 +2346,4 @@ if (APP_BENCHMARK) { $GLOBALS['bench']->setMarker('Included Notification Class'); } -?> \ No newline at end of file +?> --- eventum-1.7.1/include/class.support.php Thu Mar 30 14:13:13 2006 +++ /usr/local/www/eventum/include/class.support.php Wed Feb 4 14:07:23 2009 @@ -490,7 +490,7 @@ } $message_id = Mail_API::getMessageID($headers, $body); if ((!Support::exists($message_id)) && (!Note::exists($message_id))) { - $structure = Mime_Helper::decode($message, true, true); + $structure = Mime_Helper::decode($message, true, false); $message_body = Mime_Helper::getMessageBody(&$structure); if (Mime_Helper::hasAttachments($message)) { $has_attachments = 1; @@ -746,11 +746,10 @@ $message_id = Mail_API::getMessageID($headers, $message_body); $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)) { + if ((@$setup['subject_based_routing']['status'] == 'enabled') && + // look for issue ID as [#XXXX] in the subject line + (preg_match("/\[#(\d+)\]( Note| BLOCKED)*/", $subject, $matches))) + { $should_create_issue = false; $issue_id = $matches[1]; if (!Issue::exists($issue_id, false)) { @@ -758,9 +757,7 @@ } elseif (!empty($matches[2])) { $type = 'note'; } - } else { - $should_create_issue = true; - } + # if subject has no issue ID, try using MessageID } else { // - if this email is a reply: if (count($references) > 0) { @@ -1876,6 +1873,7 @@ { $recipients = Support::getRecipientsCC($cc); $recipients[] = $to; + // send the emails now, one at a time foreach ($recipients as $recipient) { $mail = new Mail_API; --- eventum-1.7.1/include/class.mail.php Thu Mar 30 14:13:13 2006 +++ /usr/local/www/eventum/include/class.mail.php Tue Jan 6 14:17:08 2009 @@ -245,6 +245,11 @@ $address = Mime_Helper::encodeValue($address); include_once(APP_PEAR_PATH . "Mail/RFC822.php"); $t = Mail_RFC822::parseAddressList($address, null, null, false); + if (PEAR::isError($t)) { + Error_Handler::logToFile("Error parsing: $address", __FILE__, __LINE__); + echo "
";echo "Error parsing address [" . htmlspecialchars($address) . "]\n";echo ""; + echo "
";print_r(debug_backtrace());echo ""; + } if ($multiple) { $returns = array(); for ($i = 0; $i < count($t); $i++) { @@ -992,4 +997,4 @@ if (APP_BENCHMARK) { $GLOBALS['bench']->setMarker('Included Mail_API Class'); } -?> \ No newline at end of file +?>