3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2014
35 class CRM_Utils_Mail_Incoming
{
37 EMAILPROCESSOR_CREATE_INDIVIDUAL
= 1,
38 EMAILPROCESSOR_OVERRIDE
= 2,
39 EMAILPROCESSOR_IGNORE
= 3;
47 function formatMail($mail, &$attachments) {
49 $t .= "From: " . self
::formatAddress($mail->from
) . "\n";
50 $t .= "To: " . self
::formatAddresses($mail->to
) . "\n";
51 $t .= "Cc: " . self
::formatAddresses($mail->cc
) . "\n";
52 $t .= "Bcc: " . self
::formatAddresses($mail->bcc
) . "\n";
53 $t .= 'Date: ' . date(DATE_RFC822
, $mail->timestamp
) . "\n";
54 $t .= 'Subject: ' . $mail->subject
. "\n";
55 $t .= "MessageId: " . $mail->messageId
. "\n";
57 $t .= self
::formatMailPart($mail->body
, $attachments);
67 public static function formatMailPart($part, &$attachments) {
68 if ($part instanceof ezcMail
) {
69 return self
::formatMail($part, $attachments);
72 if ($part instanceof ezcMailText
) {
73 return self
::formatMailText($part, $attachments);
76 if ($part instanceof ezcMailFile
) {
77 return self
::formatMailFile($part, $attachments);
80 if ($part instanceof ezcMailRfc822Digest
) {
81 return self
::formatMailRfc822Digest($part, $attachments);
84 if ($part instanceof ezcMailMultiPart
) {
85 return self
::formatMailMultipart($part, $attachments);
88 CRM_Core_Error
::fatal(ts("No clue about the %1", array(1 => get_class($part))));
97 function formatMailMultipart($part, &$attachments) {
98 if ($part instanceof ezcMailMultiPartAlternative
) {
99 return self
::formatMailMultipartAlternative($part, $attachments);
102 if ($part instanceof ezcMailMultiPartDigest
) {
103 return self
::formatMailMultipartDigest($part, $attachments);
106 if ($part instanceof ezcMailMultiPartRelated
) {
107 return self
::formatMailMultipartRelated($part, $attachments);
110 if ($part instanceof ezcMailMultiPartMixed
) {
111 return self
::formatMailMultipartMixed($part, $attachments);
114 if ($part instanceof ezcMailMultipartReport
) {
115 return self
::formatMailMultipartReport($part, $attachments);
118 CRM_Core_Error
::fatal(ts("No clue about the %1", array(1 => get_class($part))));
123 * @param $attachments
127 function formatMailMultipartMixed($part, &$attachments) {
129 foreach ($part->getParts() as $key => $alternativePart) {
130 $t .= self
::formatMailPart($alternativePart, $attachments);
137 * @param $attachments
141 function formatMailMultipartRelated($part, &$attachments) {
143 $t .= "-RELATED MAIN PART-\n";
144 $t .= self
::formatMailPart($part->getMainPart(), $attachments);
145 foreach ($part->getRelatedParts() as $key => $alternativePart) {
146 $t .= "-RELATED PART $key-\n";
147 $t .= self
::formatMailPart($alternativePart, $attachments);
149 $t .= "-RELATED END-\n";
155 * @param $attachments
159 function formatMailMultipartDigest($part, &$attachments) {
161 foreach ($part->getParts() as $key => $alternativePart) {
162 $t .= "-DIGEST-$key-\n";
163 $t .= self
::formatMailPart($alternativePart, $attachments);
165 $t .= "-DIGEST END---\n";
171 * @param $attachments
175 function formatMailRfc822Digest($part, &$attachments) {
177 $t .= "-DIGEST-ITEM-\n";
179 $t .= self
::formatMailpart($part->mail
, $attachments);
180 $t .= "-DIGEST ITEM END-\n";
186 * @param $attachments
190 function formatMailMultipartAlternative($part, &$attachments) {
192 foreach ($part->getParts() as $key => $alternativePart) {
193 $t .= "-ALTERNATIVE ITEM $key-\n";
194 $t .= self
::formatMailPart($alternativePart, $attachments);
196 $t .= "-ALTERNATIVE END-\n";
202 * @param $attachments
206 public static function formatMailText($part, &$attachments) {
207 $t = "\n{$part->text}\n";
213 * @param $attachments
217 function formatMailMultipartReport($part, &$attachments) {
219 foreach ($part->getParts() as $key => $reportPart) {
220 $t .= "-REPORT-$key-\n";
221 $t .= self
::formatMailPart($reportPart, $attachments);
223 $t .= "-REPORT END---\n";
229 * @param $attachments
233 function formatMailFile($part, &$attachments) {
234 $attachments[] = array(
235 'dispositionType' => $part->dispositionType
,
236 'contentType' => $part->contentType
,
237 'mimeType' => $part->mimeType
,
238 'contentID' => $part->contentId
,
239 'fullName' => $part->fileName
,
249 function formatAddresses($addresses) {
251 foreach ($addresses as $address) {
252 $fa[] = self
::formatAddress($address);
254 return implode(', ', $fa);
262 function formatAddress($address) {
264 if (!empty($address->name
)) {
265 $name = "{$address->name} ";
267 return $name . "<{$address->email}>";
276 function &parse(&$file) {
278 // check that the file exists and has some content
279 if (!file_exists($file) ||
280 !trim(file_get_contents($file))
282 return CRM_Core_Error
::createAPIError(ts('%1 does not exists or is empty',
287 require_once 'ezc/Base/src/ezc_bootstrap.php';
288 require_once 'ezc/autoload/mail_autoload.php';
290 // explode email to digestable format
291 $set = new ezcMailFileSet(array($file));
292 $parser = new ezcMailParser();
293 $mail = $parser->parseMail($set);
296 return CRM_Core_Error
::createAPIError(ts('%1 could not be parsed',
301 // since we only have one fileset
304 $mailParams = self
::parseMailingObject($mail);
313 public static function parseMailingObject(&$mail) {
315 $config = CRM_Core_Config
::singleton();
317 // get ready for collecting data about this email
318 // and put it in a standardized format
319 $params = array('is_error' => 0);
321 $params['from'] = array();
322 self
::parseAddress($mail->from
, $field, $params['from'], $mail);
324 // we definitely need a contact id for the from address
325 // if we dont have one, skip this email
326 if (empty($params['from']['id'])) {
330 $emailFields = array('to', 'cc', 'bcc');
331 foreach ($emailFields as $field) {
332 $value = $mail->$field;
333 self
::parseAddresses($value, $field, $params, $mail);
336 // define other parameters
337 $params['subject'] = $mail->subject
;
338 $params['date'] = date("YmdHi00",
339 strtotime($mail->getHeader("Date"))
341 $attachments = array();
342 $params['body'] = self
::formatMailPart($mail->body
, $attachments);
344 // format and move attachments to the civicrm area
345 if (!empty($attachments)) {
346 $date = date('Ymdhis');
347 $config = CRM_Core_Config
::singleton();
348 for ($i = 0; $i < count($attachments); $i++
) {
350 $fileName = basename($attachments[$i]['fullName']);
351 $newName = CRM_Utils_File
::makeFileName($fileName);
352 $location = $config->uploadDir
. $newName;
354 // move file to the civicrm upload directory
355 rename($attachments[$i]['fullName'], $location);
357 $mimeType = "{$attachments[$i]['contentType']}/{$attachments[$i]['mimeType']}";
359 $params["attachFile_$attachNum"] = array(
362 'upload_date' => $date,
363 'location' => $location,
373 * @param array $params
377 public static function parseAddress(&$address, &$params, &$subParam, &$mail) {
379 if (empty($address->email
)) {
383 $subParam['email'] = $address->email
;
384 $subParam['name'] = $address->name
;
387 $contactID = self
::getContactID($subParam['email'],
392 $subParam['id'] = $contactID ?
$contactID : NULL;
398 * @param array $params
401 public static function parseAddresses(&$addresses, $token, &$params, &$mail) {
402 $params[$token] = array();
404 foreach ($addresses as $address) {
406 self
::parseAddress($address, $params, $subParam, $mail);
407 $params[$token][] = $subParam;
412 * retrieve a contact ID and if not present
413 * create one with this email
415 public static function getContactID($email, $name = NULL, $create = TRUE, &$mail) {
416 $dao = CRM_Contact_BAO_Contact
::matchContactOnEmail($email, 'Individual');
420 $contactID = $dao->contact_id
;
424 CRM_Utils_Hook
::emailProcessorContact($email, $contactID, $result);
426 if (!empty($result)) {
427 if ($result['action'] == self
::EMAILPROCESSOR_IGNORE
) {
430 if ($result['action'] == self
::EMAILPROCESSOR_OVERRIDE
) {
431 return $result['contactID'];
434 // else this is now create individual
435 // so we just fall out and do what we normally do
446 // contact does not exist, lets create it
448 'contact_type' => 'Individual',
449 'email-Primary' => $email,
452 CRM_Utils_String
::extractName($name, $params);
454 return CRM_Contact_BAO_Contact
::createProfileContact($params,
455 CRM_Core_DAO
::$_nullArray