Exemplo n.º 1
0
 /**
  * Create an activity for a case via email
  *
  * @param int $file email sent
  *
  * @return array|void $activity object of newly creted activity via email@access public
  */
 static function recordActivityViaEmail($file)
 {
     if (!file_exists($file) || !is_readable($file)) {
         return CRM_Core_Error::fatal(ts('File %1 does not exist or is not readable', array(1 => $file)));
     }
     $result = CRM_Utils_Mail_Incoming::parse($file);
     if ($result['is_error']) {
         return $result;
     }
     foreach ($result['to'] as $to) {
         $caseId = NULL;
         $emailPattern = '/^([A-Z0-9._%+-]+)\\+([\\d]+)@[A-Z0-9.-]+\\.[A-Z]{2,4}$/i';
         $replacement = preg_replace($emailPattern, '$2', $to['email']);
         if ($replacement !== $to['email']) {
             $caseId = $replacement;
             //if caseId is invalid, return as error file
             if (!CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseId, 'id')) {
                 return CRM_Core_Error::createAPIError(ts('Invalid case ID ( %1 ) in TO: field.', array(1 => $caseId)));
             }
         } else {
             continue;
         }
         // TODO: May want to replace this with a call to getRelatedAndGlobalContacts() when this feature is revisited.
         // (Or for efficiency call the global one outside the loop and then union with this each time.)
         $contactDetails = self::getRelatedContacts($caseId, TRUE);
         if (!empty($contactDetails[$result['from']['id']])) {
             $params = array();
             $params['subject'] = $result['subject'];
             $params['activity_date_time'] = $result['date'];
             $params['details'] = $result['body'];
             $params['source_contact_id'] = $result['from']['id'];
             $params['status_id'] = CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name');
             $details = CRM_Case_PseudoConstant::caseActivityType();
             $matches = array();
             preg_match('/^\\W+([a-zA-Z0-9_ ]+)(\\W+)?\\n/i', $result['body'], $matches);
             if (!empty($matches) && isset($matches[1])) {
                 $activityType = trim($matches[1]);
                 if (isset($details[$activityType])) {
                     $params['activity_type_id'] = $details[$activityType]['id'];
                 }
             }
             if (!isset($params['activity_type_id'])) {
                 $params['activity_type_id'] = CRM_Core_OptionGroup::getValue('activity_type', 'Inbound Email', 'name');
             }
             // create activity
             $activity = CRM_Activity_BAO_Activity::create($params);
             $caseParams = array('activity_id' => $activity->id, 'case_id' => $caseId);
             self::processCaseActivity($caseParams);
         } else {
             return CRM_Core_Error::createAPIError(ts('FROM email contact %1 doesn\'t have a relationship to the referenced case.', array(1 => $result['from']['email'])));
         }
     }
 }
Exemplo n.º 2
0
 /**
  * @param $civiMail
  * @param CRM_Core_DAO $dao
  *
  * @throws Exception
  */
 public static function _process($civiMail, $dao)
 {
     // 0 = activities; 1 = bounce;
     $usedfor = $dao->is_default;
     $emailActivityTypeId = defined('EMAIL_ACTIVITY_TYPE_ID') && EMAIL_ACTIVITY_TYPE_ID ? EMAIL_ACTIVITY_TYPE_ID : CRM_Core_OptionGroup::getValue('activity_type', 'Inbound Email', 'name');
     if (!$emailActivityTypeId) {
         CRM_Core_Error::fatal(ts('Could not find a valid Activity Type ID for Inbound Email'));
     }
     $config = CRM_Core_Config::singleton();
     $verpSeperator = preg_quote($config->verpSeparator);
     $twoDigitStringMin = $verpSeperator . '(\\d+)' . $verpSeperator . '(\\d+)';
     $twoDigitString = $twoDigitStringMin . $verpSeperator;
     $threeDigitString = $twoDigitString . '(\\d+)' . $verpSeperator;
     // FIXME: legacy regexen to handle CiviCRM 2.1 address patterns, with domain id and possible VERP part
     $commonRegex = '/^' . preg_quote($dao->localpart) . '(b|bounce|c|confirm|o|optOut|r|reply|re|e|resubscribe|u|unsubscribe)' . $threeDigitString . '([0-9a-f]{16})(-.*)?@' . preg_quote($dao->domain) . '$/';
     $subscrRegex = '/^' . preg_quote($dao->localpart) . '(s|subscribe)' . $twoDigitStringMin . '@' . preg_quote($dao->domain) . '$/';
     // a common-for-all-actions regex to handle CiviCRM 2.2 address patterns
     $regex = '/^' . preg_quote($dao->localpart) . '(b|c|e|o|r|u)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '$/';
     // a tighter regex for finding bounce info in soft bounces’ mail bodies
     $rpRegex = '/Return-Path:\\s*' . preg_quote($dao->localpart) . '(b)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '/';
     // a regex for finding bound info X-Header
     $rpXheaderRegex = '/X-CiviMail-Bounce: ' . preg_quote($dao->localpart) . '(b)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '/i';
     // CiviMail in regex and Civimail in header !!!
     // retrieve the emails
     try {
         $store = CRM_Mailing_MailStore::getStore($dao->name);
     } catch (Exception $e) {
         $message = ts('Could not connect to MailStore for ') . $dao->username . '@' . $dao->server . '<p>';
         $message .= ts('Error message: ');
         $message .= '<pre>' . $e->getMessage() . '</pre><p>';
         CRM_Core_Error::fatal($message);
     }
     // process fifty at a time, CRM-4002
     while ($mails = $store->fetchNext(MAIL_BATCH_SIZE)) {
         foreach ($mails as $key => $mail) {
             // for every addressee: match address elements if it's to CiviMail
             $matches = array();
             $action = NULL;
             if ($usedfor == 1) {
                 foreach ($mail->to as $address) {
                     if (preg_match($regex, $address->email, $matches)) {
                         list($match, $action, $job, $queue, $hash) = $matches;
                         break;
                         // FIXME: the below elseifs should be dropped when we drop legacy support
                     } elseif (preg_match($commonRegex, $address->email, $matches)) {
                         list($match, $action, $_, $job, $queue, $hash) = $matches;
                         break;
                     } elseif (preg_match($subscrRegex, $address->email, $matches)) {
                         list($match, $action, $_, $job) = $matches;
                         break;
                     }
                 }
                 // CRM-5471: if $matches is empty, it still might be a soft bounce sent
                 // to another address, so scan the body for ‘Return-Path: …bounce-pattern…’
                 if (!$matches and preg_match($rpRegex, $mail->generateBody(), $matches)) {
                     list($match, $action, $job, $queue, $hash) = $matches;
                 }
                 // if $matches is still empty, look for the X-CiviMail-Bounce header
                 // CRM-9855
                 if (!$matches and preg_match($rpXheaderRegex, $mail->generateBody(), $matches)) {
                     list($match, $action, $job, $queue, $hash) = $matches;
                 }
                 // With Mandrilla, the X-CiviMail-Bounce header is produced by generateBody
                 // is base64 encoded
                 // Check all parts
                 if (!$matches) {
                     $all_parts = $mail->fetchParts();
                     foreach ($all_parts as $k_part => $v_part) {
                         if ($v_part instanceof ezcMailFile) {
                             $p_file = $v_part->__get('fileName');
                             $c_file = file_get_contents($p_file);
                             if (preg_match($rpXheaderRegex, $c_file, $matches)) {
                                 list($match, $action, $job, $queue, $hash) = $matches;
                             }
                         }
                     }
                 }
                 // if all else fails, check Delivered-To for possible pattern
                 if (!$matches and preg_match($regex, $mail->getHeader('Delivered-To'), $matches)) {
                     list($match, $action, $job, $queue, $hash) = $matches;
                 }
             }
             // preseve backward compatibility
             if ($usedfor == 0 || !$civiMail) {
                 // if its the activities that needs to be processed ..
                 try {
                     $mailParams = CRM_Utils_Mail_Incoming::parseMailingObject($mail);
                 } catch (Exception $e) {
                     echo $e->getMessage();
                     $store->markIgnored($key);
                     continue;
                 }
                 require_once 'CRM/Utils/DeprecatedUtils.php';
                 $params = _civicrm_api3_deprecated_activity_buildmailparams($mailParams, $emailActivityTypeId);
                 $params['version'] = 3;
                 $result = civicrm_api('activity', 'create', $params);
                 if ($result['is_error']) {
                     $matches = FALSE;
                     echo "Failed Processing: {$mail->subject}. Reason: {$result['error_message']}\n";
                 } else {
                     $matches = TRUE;
                     echo "Processed as Activity: {$mail->subject}\n";
                 }
                 CRM_Utils_Hook::emailProcessor('activity', $params, $mail, $result);
             }
             // if $matches is empty, this email is not CiviMail-bound
             if (!$matches) {
                 $store->markIgnored($key);
                 continue;
             }
             // get $replyTo from either the Reply-To header or from From
             // FIXME: make sure it works with Reply-Tos containing non-email stuff
             $replyTo = $mail->getHeader('Reply-To') ? $mail->getHeader('Reply-To') : $mail->from->email;
             // handle the action by passing it to the proper API call
             // FIXME: leave only one-letter cases when dropping legacy support
             if (!empty($action)) {
                 $result = NULL;
                 switch ($action) {
                     case 'b':
                     case 'bounce':
                         $text = '';
                         if ($mail->body instanceof ezcMailText) {
                             $text = $mail->body->text;
                         } elseif ($mail->body instanceof ezcMailMultipart) {
                             if ($mail->body instanceof ezcMailMultipartReport) {
                                 $part = $mail->body->getMachinePart();
                                 if ($part instanceof ezcMailDeliveryStatus) {
                                     foreach ($part->recipients as $rec) {
                                         if (isset($rec["Diagnostic-Code"])) {
                                             $text = $rec["Diagnostic-Code"];
                                             break;
                                         } elseif (isset($rec["Description"])) {
                                             $text = $rec["Description"];
                                             break;
                                         } elseif (isset($rec["Status"])) {
                                             $text = $rec["Status"];
                                             $textpart = $mail->body->getReadablePart();
                                             if ($textpart != NULL and isset($textpart->text)) {
                                                 $text .= " " . $textpart->text;
                                             } else {
                                                 $text .= " Delivery failed but no diagnostic code or description.";
                                             }
                                             break;
                                         }
                                     }
                                 } elseif ($part != NULL and isset($part->text)) {
                                     $text = $part->text;
                                 } elseif (($part = $mail->body->getReadablePart()) != NULL) {
                                     $text = $part->text;
                                 }
                             } elseif ($mail->body instanceof ezcMailMultipartRelated) {
                                 foreach ($mail->body->getRelatedParts() as $part) {
                                     if (isset($part->subType) and $part->subType == 'plain') {
                                         $text = $part->text;
                                         break;
                                     }
                                 }
                             } else {
                                 foreach ($mail->body->getParts() as $part) {
                                     if (isset($part->subType) and $part->subType == 'plain') {
                                         $text = $part->text;
                                         break;
                                     }
                                 }
                             }
                         }
                         if (empty($text) && $mail->subject == "Delivery Status Notification (Failure)") {
                             // Exchange error - CRM-9361
                             foreach ($mail->body->getParts() as $part) {
                                 if ($part instanceof ezcMailDeliveryStatus) {
                                     foreach ($part->recipients as $rec) {
                                         if ($rec["Status"] == "5.1.1") {
                                             if (isset($rec["Description"])) {
                                                 $text = $rec["Description"];
                                             } else {
                                                 $text = $rec["Status"] . " Delivery to the following recipients failed";
                                             }
                                             break;
                                         }
                                     }
                                 }
                             }
                         }
                         if (empty($text)) {
                             // If bounce processing fails, just take the raw body. Cf. CRM-11046
                             $text = $mail->generateBody();
                             // if text is still empty, lets fudge a blank text so the api call below will succeed
                             if (empty($text)) {
                                 $text = ts('We could not extract the mail body from this bounce message.');
                             }
                         }
                         $params = array('job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash, 'body' => $text, 'version' => 3);
                         $result = civicrm_api('Mailing', 'event_bounce', $params);
                         break;
                     case 'c':
                     case 'confirm':
                         // CRM-7921
                         $params = array('contact_id' => $job, 'subscribe_id' => $queue, 'hash' => $hash, 'version' => 3);
                         $result = civicrm_api('Mailing', 'event_confirm', $params);
                         break;
                     case 'o':
                     case 'optOut':
                         $params = array('job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash, 'version' => 3);
                         $result = civicrm_api('MailingGroup', 'event_domain_unsubscribe', $params);
                         break;
                     case 'r':
                     case 'reply':
                         // instead of text and HTML parts (4th and 6th params) send the whole email as the last param
                         $params = array('job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash, 'bodyTxt' => NULL, 'replyTo' => $replyTo, 'bodyHTML' => NULL, 'fullEmail' => $mail->generate(), 'version' => 3);
                         $result = civicrm_api('Mailing', 'event_reply', $params);
                         break;
                     case 'e':
                     case 're':
                     case 'resubscribe':
                         $params = array('job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash, 'version' => 3);
                         $result = civicrm_api('MailingGroup', 'event_resubscribe', $params);
                         break;
                     case 's':
                     case 'subscribe':
                         $params = array('email' => $mail->from->email, 'group_id' => $job, 'version' => 3);
                         $result = civicrm_api('MailingGroup', 'event_subscribe', $params);
                         break;
                     case 'u':
                     case 'unsubscribe':
                         $params = array('job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash, 'version' => 3);
                         $result = civicrm_api('MailingGroup', 'event_unsubscribe', $params);
                         break;
                 }
                 if ($result['is_error']) {
                     echo "Failed Processing: {$mail->subject}, Action: {$action}, Job ID: {$job}, Queue ID: {$queue}, Hash: {$hash}. Reason: {$result['error_message']}\n";
                 } else {
                     CRM_Utils_Hook::emailProcessor('mailing', $params, $mail, $result, $action);
                 }
             }
             $store->markProcessed($key);
         }
         // CRM-7356 – used by IMAP only
         $store->expunge();
     }
 }
Exemplo n.º 3
0
/**
 * Convert an email file to an activity
 */
function civicrm_activity_processemail($file, $activityTypeID, $result = array())
{
    // do not parse if result array already passed (towards EmailProcessor..)
    if (empty($result)) {
        // might want to check that email is ok here
        if (!file_exists($file) || !is_readable($file)) {
            return CRM_Core_Error::createAPIError(ts('File %1 does not exist or is not readable', array(1 => $file)));
        }
    }
    require_once 'CRM/Utils/Mail/Incoming.php';
    $result = CRM_Utils_Mail_Incoming::parse($file);
    if ($result['is_error']) {
        return $result;
    }
    $params = _civicrm_activity_buildmailparams($result, $activityTypeID);
    return civicrm_activity_create($params);
}
Exemplo n.º 4
0
 static function _process($civiMail, $dao)
 {
     // 0 = activities; 1 = bounce;
     $usedfor = $dao->is_default;
     require_once 'CRM/Core/OptionGroup.php';
     $emailActivityTypeId = defined('EMAIL_ACTIVITY_TYPE_ID') && EMAIL_ACTIVITY_TYPE_ID ? EMAIL_ACTIVITY_TYPE_ID : CRM_Core_OptionGroup::getValue('activity_type', 'Inbound Email', 'name');
     if (!$emailActivityTypeId) {
         CRM_Core_Error::fatal(ts('Could not find a valid Activity Type ID for Inbound Email'));
     }
     // FIXME: legacy regexen to handle CiviCRM 2.1 address patterns, with domain id and possible VERP part
     $commonRegex = '/^' . preg_quote($dao->localpart) . '(b|bounce|c|confirm|o|optOut|r|reply|re|e|resubscribe|u|unsubscribe)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.([0-9a-f]{16})(-.*)?@' . preg_quote($dao->domain) . '$/';
     $subscrRegex = '/^' . preg_quote($dao->localpart) . '(s|subscribe)\\.(\\d+)\\.(\\d+)@' . preg_quote($dao->domain) . '$/';
     // a common-for-all-actions regex to handle CiviCRM 2.2 address patterns
     $regex = '/^' . preg_quote($dao->localpart) . '(b|c|e|o|r|u)\\.(\\d+)\\.(\\d+)\\.([0-9a-f]{16})@' . preg_quote($dao->domain) . '$/';
     // retrieve the emails
     require_once 'CRM/Mailing/MailStore.php';
     $store = CRM_Mailing_MailStore::getStore($dao->name);
     require_once 'api/v2/Mailer.php';
     // process fifty at a time, CRM-4002
     while ($mails = $store->fetchNext(MAIL_BATCH_SIZE)) {
         foreach ($mails as $key => $mail) {
             // for every addressee: match address elements if it's to CiviMail
             $matches = array();
             if ($usedfor == 1) {
                 foreach ($mail->to as $address) {
                     if (preg_match($regex, $address->email, $matches)) {
                         list($match, $action, $job, $queue, $hash) = $matches;
                         break;
                         // FIXME: the below elseifs should be dropped when we drop legacy support
                     } elseif (preg_match($commonRegex, $address->email, $matches)) {
                         list($match, $action, $_, $job, $queue, $hash) = $matches;
                         break;
                     } elseif (preg_match($subscrRegex, $address->email, $matches)) {
                         list($match, $action, $_, $job) = $matches;
                         break;
                     }
                 }
             }
             // preseve backward compatibility
             if ($usedfor == 0 || !$civiMail) {
                 // if its the activities that needs to be processed ..
                 require_once 'CRM/Utils/Mail/Incoming.php';
                 $mailParams = CRM_Utils_Mail_Incoming::parseMailingObject($mail, $dao->name);
                 require_once 'api/v2/Activity.php';
                 $params = _civicrm_activity_buildmailparams($mailParams, $emailActivityTypeId);
                 $result = civicrm_activity_create($params);
                 if ($result['is_error']) {
                     $matches = false;
                     echo "Failed Processing: {$mail->subject}. Reason: {$result['error_message']}\n";
                 } else {
                     $matches = true;
                     echo "Processed as Activity: {$mail->subject}\n";
                 }
             }
             // if $matches is empty, this email is not CiviMail-bound
             if (!$matches) {
                 $store->markIgnored($key);
                 continue;
             }
             // get $replyTo from either the Reply-To header or from From
             // FIXME: make sure it works with Reply-Tos containing non-email stuff
             $replyTo = $mail->getHeader('Reply-To') ? $mail->getHeader('Reply-To') : $mail->from->email;
             // handle the action by passing it to the proper API call
             // FIXME: leave only one-letter cases when dropping legacy support
             if (!empty($action)) {
                 switch ($action) {
                     case 'b':
                     case 'bounce':
                         $text = '';
                         if ($mail->body instanceof ezcMailText) {
                             $text = $mail->body->text;
                         } elseif ($mail->body instanceof ezcMailMultipart) {
                             foreach ($mail->body->getParts() as $part) {
                                 if (isset($part->subType) and $part->subType == 'plain') {
                                     $text = $part->text;
                                     break;
                                 }
                             }
                         }
                         $params = array('job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash, 'body' => $text);
                         civicrm_mailer_event_bounce($params);
                         break;
                     case 'c':
                     case 'confirm':
                         $params = array('job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash);
                         civicrm_mailer_event_confirm($params);
                         break;
                     case 'o':
                     case 'optOut':
                         $params = array('job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash);
                         civicrm_mailer_event_domain_unsubscribe($params);
                         break;
                     case 'r':
                     case 'reply':
                         // instead of text and HTML parts (4th and 6th params) send the whole email as the last param
                         $params = array('job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash, 'bodyTxt' => null, 'replyTo' => $replyTo, 'bodyHTML' => null, 'fullEmail' => $mail->generate());
                         civicrm_mailer_event_reply($params);
                         break;
                     case 'e':
                     case 're':
                     case 'resubscribe':
                         $params = array('job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash);
                         civicrm_mailer_event_resubscribe($params);
                         break;
                     case 's':
                     case 'subscribe':
                         $params = array('email' => $mail->from->email, 'group_id' => $job);
                         civicrm_mailer_event_subscribe($params);
                         break;
                     case 'u':
                     case 'unsubscribe':
                         civicrm_mailer_event_unsubscribe($job, $queue, $hash);
                         break;
                 }
             }
             $store->markProcessed($key);
         }
     }
 }