Merge pull request #8859 from jitendrapurohit/CRM-19209
[civicrm-core.git] / CRM / Utils / Mail / Incoming.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
fa938177 6 | Copyright CiviCRM LLC (c) 2004-2016 |
6a488035
TO
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
fa938177 31 * @copyright CiviCRM LLC (c) 2004-2016
6a488035
TO
32 */
33class CRM_Utils_Mail_Incoming {
7da04cde 34 const
6a488035
TO
35 EMAILPROCESSOR_CREATE_INDIVIDUAL = 1,
36 EMAILPROCESSOR_OVERRIDE = 2,
37 EMAILPROCESSOR_IGNORE = 3;
38
5bc392e6
EM
39 /**
40 * @param $mail
41 * @param $attachments
42 *
43 * @return string
44 */
00be9182 45 public function formatMail($mail, &$attachments) {
6a488035
TO
46 $t = '';
47 $t .= "From: " . self::formatAddress($mail->from) . "\n";
48 $t .= "To: " . self::formatAddresses($mail->to) . "\n";
49 $t .= "Cc: " . self::formatAddresses($mail->cc) . "\n";
50 $t .= "Bcc: " . self::formatAddresses($mail->bcc) . "\n";
51 $t .= 'Date: ' . date(DATE_RFC822, $mail->timestamp) . "\n";
52 $t .= 'Subject: ' . $mail->subject . "\n";
53 $t .= "MessageId: " . $mail->messageId . "\n";
54 $t .= "\n";
55 $t .= self::formatMailPart($mail->body, $attachments);
56 return $t;
57 }
58
5bc392e6
EM
59 /**
60 * @param $part
61 * @param $attachments
62 *
63 * @throws Exception
64 */
a5989abf 65 public static function formatMailPart($part, &$attachments) {
6a488035
TO
66 if ($part instanceof ezcMail) {
67 return self::formatMail($part, $attachments);
68 }
69
7bc2d958 70 if ($part instanceof ezcMailText) {
6a488035
TO
71 return self::formatMailText($part, $attachments);
72 }
73
74 if ($part instanceof ezcMailFile) {
75 return self::formatMailFile($part, $attachments);
76 }
77
78 if ($part instanceof ezcMailRfc822Digest) {
79 return self::formatMailRfc822Digest($part, $attachments);
80 }
81
82 if ($part instanceof ezcMailMultiPart) {
83 return self::formatMailMultipart($part, $attachments);
84 }
85
7bc2d958
J
86 // CRM-19111 - Handle blank emails with a subject.
87 if (!$part) {
88 return NULL;
89 }
90
6a488035
TO
91 CRM_Core_Error::fatal(ts("No clue about the %1", array(1 => get_class($part))));
92 }
93
5bc392e6
EM
94 /**
95 * @param $part
96 * @param $attachments
97 *
98 * @throws Exception
99 */
00be9182 100 public function formatMailMultipart($part, &$attachments) {
6a488035
TO
101 if ($part instanceof ezcMailMultiPartAlternative) {
102 return self::formatMailMultipartAlternative($part, $attachments);
103 }
104
105 if ($part instanceof ezcMailMultiPartDigest) {
106 return self::formatMailMultipartDigest($part, $attachments);
107 }
108
109 if ($part instanceof ezcMailMultiPartRelated) {
110 return self::formatMailMultipartRelated($part, $attachments);
111 }
112
113 if ($part instanceof ezcMailMultiPartMixed) {
114 return self::formatMailMultipartMixed($part, $attachments);
115 }
116
117 if ($part instanceof ezcMailMultipartReport) {
118 return self::formatMailMultipartReport($part, $attachments);
119 }
120
121 CRM_Core_Error::fatal(ts("No clue about the %1", array(1 => get_class($part))));
122 }
123
5bc392e6
EM
124 /**
125 * @param $part
126 * @param $attachments
127 *
128 * @return string
129 */
00be9182 130 public function formatMailMultipartMixed($part, &$attachments) {
6a488035
TO
131 $t = '';
132 foreach ($part->getParts() as $key => $alternativePart) {
133 $t .= self::formatMailPart($alternativePart, $attachments);
134 }
135 return $t;
136 }
137
5bc392e6
EM
138 /**
139 * @param $part
140 * @param $attachments
141 *
142 * @return string
143 */
00be9182 144 public function formatMailMultipartRelated($part, &$attachments) {
6a488035
TO
145 $t = '';
146 $t .= "-RELATED MAIN PART-\n";
147 $t .= self::formatMailPart($part->getMainPart(), $attachments);
148 foreach ($part->getRelatedParts() as $key => $alternativePart) {
149 $t .= "-RELATED PART $key-\n";
150 $t .= self::formatMailPart($alternativePart, $attachments);
151 }
152 $t .= "-RELATED END-\n";
153 return $t;
154 }
155
5bc392e6
EM
156 /**
157 * @param $part
158 * @param $attachments
159 *
160 * @return string
161 */
00be9182 162 public function formatMailMultipartDigest($part, &$attachments) {
6a488035
TO
163 $t = '';
164 foreach ($part->getParts() as $key => $alternativePart) {
165 $t .= "-DIGEST-$key-\n";
166 $t .= self::formatMailPart($alternativePart, $attachments);
167 }
168 $t .= "-DIGEST END---\n";
169 return $t;
170 }
171
5bc392e6
EM
172 /**
173 * @param $part
174 * @param $attachments
175 *
176 * @return string
177 */
00be9182 178 public function formatMailRfc822Digest($part, &$attachments) {
6a488035
TO
179 $t = '';
180 $t .= "-DIGEST-ITEM-\n";
181 $t .= "Item:\n\n";
182 $t .= self::formatMailpart($part->mail, $attachments);
183 $t .= "-DIGEST ITEM END-\n";
184 return $t;
185 }
186
5bc392e6
EM
187 /**
188 * @param $part
189 * @param $attachments
190 *
191 * @return string
192 */
00be9182 193 public function formatMailMultipartAlternative($part, &$attachments) {
6a488035
TO
194 $t = '';
195 foreach ($part->getParts() as $key => $alternativePart) {
196 $t .= "-ALTERNATIVE ITEM $key-\n";
197 $t .= self::formatMailPart($alternativePart, $attachments);
198 }
199 $t .= "-ALTERNATIVE END-\n";
200 return $t;
201 }
202
5bc392e6
EM
203 /**
204 * @param $part
205 * @param $attachments
206 *
207 * @return string
208 */
a5989abf 209 public static function formatMailText($part, &$attachments) {
6a488035
TO
210 $t = "\n{$part->text}\n";
211 return $t;
212 }
213
5bc392e6
EM
214 /**
215 * @param $part
216 * @param $attachments
217 *
218 * @return string
219 */
00be9182 220 public function formatMailMultipartReport($part, &$attachments) {
6a488035
TO
221 $t = '';
222 foreach ($part->getParts() as $key => $reportPart) {
223 $t .= "-REPORT-$key-\n";
224 $t .= self::formatMailPart($reportPart, $attachments);
225 }
226 $t .= "-REPORT END---\n";
227 return $t;
228 }
229
5bc392e6
EM
230 /**
231 * @param $part
232 * @param $attachments
233 *
234 * @return null
235 */
00be9182 236 public function formatMailFile($part, &$attachments) {
6a488035
TO
237 $attachments[] = array(
238 'dispositionType' => $part->dispositionType,
239 'contentType' => $part->contentType,
240 'mimeType' => $part->mimeType,
241 'contentID' => $part->contentId,
242 'fullName' => $part->fileName,
243 );
244 return NULL;
245 }
246
5bc392e6
EM
247 /**
248 * @param $addresses
249 *
250 * @return string
251 */
00be9182 252 public function formatAddresses($addresses) {
6a488035
TO
253 $fa = array();
254 foreach ($addresses as $address) {
255 $fa[] = self::formatAddress($address);
256 }
257 return implode(', ', $fa);
258 }
259
5bc392e6
EM
260 /**
261 * @param $address
262 *
263 * @return string
264 */
00be9182 265 public function formatAddress($address) {
6a488035
TO
266 $name = '';
267 if (!empty($address->name)) {
268 $name = "{$address->name} ";
269 }
270 return $name . "<{$address->email}>";
271 }
272
5bc392e6
EM
273 /**
274 * @param $file
275 *
276 * @return array
277 * @throws Exception
278 */
00be9182 279 public function &parse(&$file) {
6a488035
TO
280
281 // check that the file exists and has some content
282 if (!file_exists($file) ||
283 !trim(file_get_contents($file))
284 ) {
285 return CRM_Core_Error::createAPIError(ts('%1 does not exists or is empty',
353ffa53
TO
286 array(1 => $file)
287 ));
6a488035
TO
288 }
289
6a488035 290 // explode email to digestable format
353ffa53 291 $set = new ezcMailFileSet(array($file));
6a488035 292 $parser = new ezcMailParser();
353ffa53 293 $mail = $parser->parseMail($set);
6a488035
TO
294
295 if (!$mail) {
296 return CRM_Core_Error::createAPIError(ts('%1 could not be parsed',
353ffa53
TO
297 array(1 => $file)
298 ));
6a488035
TO
299 }
300
301 // since we only have one fileset
302 $mail = $mail[0];
303
304 $mailParams = self::parseMailingObject($mail);
305 return $mailParams;
306 }
307
5bc392e6
EM
308 /**
309 * @param $mail
310 *
311 * @return array
312 */
a5989abf 313 public static function parseMailingObject(&$mail) {
6a488035
TO
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'])) {
408b79bf 327 return NULL;
6a488035
TO
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)) {
85ebb706 346 $date = date('YmdHis');
6a488035
TO
347 $config = CRM_Core_Config::singleton();
348 for ($i = 0; $i < count($attachments); $i++) {
349 $attachNum = $i + 1;
353ffa53
TO
350 $fileName = basename($attachments[$i]['fullName']);
351 $newName = CRM_Utils_File::makeFileName($fileName);
352 $location = $config->uploadDir . $newName;
6a488035
TO
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
5bc392e6
EM
371 /**
372 * @param $address
c490a46a 373 * @param array $params
5bc392e6
EM
374 * @param $subParam
375 * @param $mail
376 */
a5989abf 377 public static function parseAddress(&$address, &$params, &$subParam, &$mail) {
6a488035
TO
378 // CRM-9484
379 if (empty($address->email)) {
380 return;
381 }
382
383 $subParam['email'] = $address->email;
384 $subParam['name'] = $address->name;
385
6a488035
TO
386 $contactID = self::getContactID($subParam['email'],
387 $subParam['name'],
388 TRUE,
389 $mail
390 );
391 $subParam['id'] = $contactID ? $contactID : NULL;
392 }
393
5bc392e6
EM
394 /**
395 * @param $addresses
396 * @param $token
c490a46a 397 * @param array $params
5bc392e6
EM
398 * @param $mail
399 */
a5989abf 400 public static function parseAddresses(&$addresses, $token, &$params, &$mail) {
6a488035
TO
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 /**
fe482240 411 * Retrieve a contact ID and if not present.
ea3ddccf 412 *
413 * Create one with this email
414 *
415 * @param string $email
416 * @param string $name
417 * @param bool $create
418 * @param string $mail
419 *
420 * @return int|null
6a488035 421 */
a5989abf 422 public static function getContactID($email, $name = NULL, $create = TRUE, &$mail) {
6a488035
TO
423 $dao = CRM_Contact_BAO_Contact::matchContactOnEmail($email, 'Individual');
424
425 $contactID = NULL;
426 if ($dao) {
427 $contactID = $dao->contact_id;
428 }
429
430 $result = NULL;
431 CRM_Utils_Hook::emailProcessorContact($email, $contactID, $result);
432
433 if (!empty($result)) {
434 if ($result['action'] == self::EMAILPROCESSOR_IGNORE) {
435 return NULL;
436 }
437 if ($result['action'] == self::EMAILPROCESSOR_OVERRIDE) {
438 return $result['contactID'];
439 }
440
441 // else this is now create individual
442 // so we just fall out and do what we normally do
443 }
444
445 if ($contactID) {
446 return $contactID;
447 }
448
449 if (!$create) {
450 return NULL;
451 }
452
453 // contact does not exist, lets create it
454 $params = array(
455 'contact_type' => 'Individual',
456 'email-Primary' => $email,
457 );
458
459 CRM_Utils_String::extractName($name, $params);
460
461 return CRM_Contact_BAO_Contact::createProfileContact($params,
462 CRM_Core_DAO::$_nullArray
463 );
464 }
96025800 465
6a488035 466}