Merge pull request #4958 from pratikshad/code-cleanup-batch-19
[civicrm-core.git] / CRM / Utils / Mail / Incoming.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
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. |
13 | |
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. |
18 | |
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 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35 class CRM_Utils_Mail_Incoming {
36 const
37 EMAILPROCESSOR_CREATE_INDIVIDUAL = 1,
38 EMAILPROCESSOR_OVERRIDE = 2,
39 EMAILPROCESSOR_IGNORE = 3;
40
41 /**
42 * @param $mail
43 * @param $attachments
44 *
45 * @return string
46 */
47 public function formatMail($mail, &$attachments) {
48 $t = '';
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";
56 $t .= "\n";
57 $t .= self::formatMailPart($mail->body, $attachments);
58 return $t;
59 }
60
61 /**
62 * @param $part
63 * @param $attachments
64 *
65 * @throws Exception
66 */
67 public static function formatMailPart($part, &$attachments) {
68 if ($part instanceof ezcMail) {
69 return self::formatMail($part, $attachments);
70 }
71
72 if ($part instanceof ezcMailText) {
73 return self::formatMailText($part, $attachments);
74 }
75
76 if ($part instanceof ezcMailFile) {
77 return self::formatMailFile($part, $attachments);
78 }
79
80 if ($part instanceof ezcMailRfc822Digest) {
81 return self::formatMailRfc822Digest($part, $attachments);
82 }
83
84 if ($part instanceof ezcMailMultiPart) {
85 return self::formatMailMultipart($part, $attachments);
86 }
87
88 CRM_Core_Error::fatal(ts("No clue about the %1", array(1 => get_class($part))));
89 }
90
91 /**
92 * @param $part
93 * @param $attachments
94 *
95 * @throws Exception
96 */
97 public function formatMailMultipart($part, &$attachments) {
98 if ($part instanceof ezcMailMultiPartAlternative) {
99 return self::formatMailMultipartAlternative($part, $attachments);
100 }
101
102 if ($part instanceof ezcMailMultiPartDigest) {
103 return self::formatMailMultipartDigest($part, $attachments);
104 }
105
106 if ($part instanceof ezcMailMultiPartRelated) {
107 return self::formatMailMultipartRelated($part, $attachments);
108 }
109
110 if ($part instanceof ezcMailMultiPartMixed) {
111 return self::formatMailMultipartMixed($part, $attachments);
112 }
113
114 if ($part instanceof ezcMailMultipartReport) {
115 return self::formatMailMultipartReport($part, $attachments);
116 }
117
118 CRM_Core_Error::fatal(ts("No clue about the %1", array(1 => get_class($part))));
119 }
120
121 /**
122 * @param $part
123 * @param $attachments
124 *
125 * @return string
126 */
127 public function formatMailMultipartMixed($part, &$attachments) {
128 $t = '';
129 foreach ($part->getParts() as $key => $alternativePart) {
130 $t .= self::formatMailPart($alternativePart, $attachments);
131 }
132 return $t;
133 }
134
135 /**
136 * @param $part
137 * @param $attachments
138 *
139 * @return string
140 */
141 public function formatMailMultipartRelated($part, &$attachments) {
142 $t = '';
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);
148 }
149 $t .= "-RELATED END-\n";
150 return $t;
151 }
152
153 /**
154 * @param $part
155 * @param $attachments
156 *
157 * @return string
158 */
159 public function formatMailMultipartDigest($part, &$attachments) {
160 $t = '';
161 foreach ($part->getParts() as $key => $alternativePart) {
162 $t .= "-DIGEST-$key-\n";
163 $t .= self::formatMailPart($alternativePart, $attachments);
164 }
165 $t .= "-DIGEST END---\n";
166 return $t;
167 }
168
169 /**
170 * @param $part
171 * @param $attachments
172 *
173 * @return string
174 */
175 public function formatMailRfc822Digest($part, &$attachments) {
176 $t = '';
177 $t .= "-DIGEST-ITEM-\n";
178 $t .= "Item:\n\n";
179 $t .= self::formatMailpart($part->mail, $attachments);
180 $t .= "-DIGEST ITEM END-\n";
181 return $t;
182 }
183
184 /**
185 * @param $part
186 * @param $attachments
187 *
188 * @return string
189 */
190 public function formatMailMultipartAlternative($part, &$attachments) {
191 $t = '';
192 foreach ($part->getParts() as $key => $alternativePart) {
193 $t .= "-ALTERNATIVE ITEM $key-\n";
194 $t .= self::formatMailPart($alternativePart, $attachments);
195 }
196 $t .= "-ALTERNATIVE END-\n";
197 return $t;
198 }
199
200 /**
201 * @param $part
202 * @param $attachments
203 *
204 * @return string
205 */
206 public static function formatMailText($part, &$attachments) {
207 $t = "\n{$part->text}\n";
208 return $t;
209 }
210
211 /**
212 * @param $part
213 * @param $attachments
214 *
215 * @return string
216 */
217 public function formatMailMultipartReport($part, &$attachments) {
218 $t = '';
219 foreach ($part->getParts() as $key => $reportPart) {
220 $t .= "-REPORT-$key-\n";
221 $t .= self::formatMailPart($reportPart, $attachments);
222 }
223 $t .= "-REPORT END---\n";
224 return $t;
225 }
226
227 /**
228 * @param $part
229 * @param $attachments
230 *
231 * @return null
232 */
233 public 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,
240 );
241 return NULL;
242 }
243
244 /**
245 * @param $addresses
246 *
247 * @return string
248 */
249 public function formatAddresses($addresses) {
250 $fa = array();
251 foreach ($addresses as $address) {
252 $fa[] = self::formatAddress($address);
253 }
254 return implode(', ', $fa);
255 }
256
257 /**
258 * @param $address
259 *
260 * @return string
261 */
262 public function formatAddress($address) {
263 $name = '';
264 if (!empty($address->name)) {
265 $name = "{$address->name} ";
266 }
267 return $name . "<{$address->email}>";
268 }
269
270 /**
271 * @param $file
272 *
273 * @return array
274 * @throws Exception
275 */
276 public function &parse(&$file) {
277
278 // check that the file exists and has some content
279 if (!file_exists($file) ||
280 !trim(file_get_contents($file))
281 ) {
282 return CRM_Core_Error::createAPIError(ts('%1 does not exists or is empty',
283 array(1 => $file)
284 ));
285 }
286
287 require_once 'ezc/Base/src/ezc_bootstrap.php';
288 require_once 'ezc/autoload/mail_autoload.php';
289
290 // explode email to digestable format
291 $set = new ezcMailFileSet(array($file));
292 $parser = new ezcMailParser();
293 $mail = $parser->parseMail($set);
294
295 if (!$mail) {
296 return CRM_Core_Error::createAPIError(ts('%1 could not be parsed',
297 array(1 => $file)
298 ));
299 }
300
301 // since we only have one fileset
302 $mail = $mail[0];
303
304 $mailParams = self::parseMailingObject($mail);
305 return $mailParams;
306 }
307
308 /**
309 * @param $mail
310 *
311 * @return array
312 */
313 public static function parseMailingObject(&$mail) {
314
315 $config = CRM_Core_Config::singleton();
316
317 // get ready for collecting data about this email
318 // and put it in a standardized format
319 $params = array('is_error' => 0);
320
321 $params['from'] = array();
322 self::parseAddress($mail->from, $field, $params['from'], $mail);
323
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'])) {
327 return NULL;
328 }
329
330 $emailFields = array('to', 'cc', 'bcc');
331 foreach ($emailFields as $field) {
332 $value = $mail->$field;
333 self::parseAddresses($value, $field, $params, $mail);
334 }
335
336 // define other parameters
337 $params['subject'] = $mail->subject;
338 $params['date'] = date("YmdHi00",
339 strtotime($mail->getHeader("Date"))
340 );
341 $attachments = array();
342 $params['body'] = self::formatMailPart($mail->body, $attachments);
343
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++) {
349 $attachNum = $i + 1;
350 $fileName = basename($attachments[$i]['fullName']);
351 $newName = CRM_Utils_File::makeFileName($fileName);
352 $location = $config->uploadDir . $newName;
353
354 // move file to the civicrm upload directory
355 rename($attachments[$i]['fullName'], $location);
356
357 $mimeType = "{$attachments[$i]['contentType']}/{$attachments[$i]['mimeType']}";
358
359 $params["attachFile_$attachNum"] = array(
360 'uri' => $fileName,
361 'type' => $mimeType,
362 'upload_date' => $date,
363 'location' => $location,
364 );
365 }
366 }
367
368 return $params;
369 }
370
371 /**
372 * @param $address
373 * @param array $params
374 * @param $subParam
375 * @param $mail
376 */
377 public static function parseAddress(&$address, &$params, &$subParam, &$mail) {
378 // CRM-9484
379 if (empty($address->email)) {
380 return;
381 }
382
383 $subParam['email'] = $address->email;
384 $subParam['name'] = $address->name;
385
386 $contactID = self::getContactID($subParam['email'],
387 $subParam['name'],
388 TRUE,
389 $mail
390 );
391 $subParam['id'] = $contactID ? $contactID : NULL;
392 }
393
394 /**
395 * @param $addresses
396 * @param $token
397 * @param array $params
398 * @param $mail
399 */
400 public static function parseAddresses(&$addresses, $token, &$params, &$mail) {
401 $params[$token] = array();
402
403 foreach ($addresses as $address) {
404 $subParam = array();
405 self::parseAddress($address, $params, $subParam, $mail);
406 $params[$token][] = $subParam;
407 }
408 }
409
410 /**
411 * Retrieve a contact ID and if not present
412 * create one with this email
413 */
414 public static function getContactID($email, $name = NULL, $create = TRUE, &$mail) {
415 $dao = CRM_Contact_BAO_Contact::matchContactOnEmail($email, 'Individual');
416
417 $contactID = NULL;
418 if ($dao) {
419 $contactID = $dao->contact_id;
420 }
421
422 $result = NULL;
423 CRM_Utils_Hook::emailProcessorContact($email, $contactID, $result);
424
425 if (!empty($result)) {
426 if ($result['action'] == self::EMAILPROCESSOR_IGNORE) {
427 return NULL;
428 }
429 if ($result['action'] == self::EMAILPROCESSOR_OVERRIDE) {
430 return $result['contactID'];
431 }
432
433 // else this is now create individual
434 // so we just fall out and do what we normally do
435 }
436
437 if ($contactID) {
438 return $contactID;
439 }
440
441 if (!$create) {
442 return NULL;
443 }
444
445 // contact does not exist, lets create it
446 $params = array(
447 'contact_type' => 'Individual',
448 'email-Primary' => $email,
449 );
450
451 CRM_Utils_String::extractName($name, $params);
452
453 return CRM_Contact_BAO_Contact::createProfileContact($params,
454 CRM_Core_DAO::$_nullArray
455 );
456 }
457 }