dolibarr 18.0.8
emailcollector.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
24// Put here all includes required by your class file
25include_once DOL_DOCUMENT_ROOT .'/emailcollector/lib/emailcollector.lib.php';
26
27require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php';
28require_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
29
30require_once DOL_DOCUMENT_ROOT .'/comm/propal/class/propal.class.php'; // Customer Proposal
31require_once DOL_DOCUMENT_ROOT .'/commande/class/commande.class.php'; // Sale Order
32require_once DOL_DOCUMENT_ROOT .'/compta/facture/class/facture.class.php'; // Customer Invoice
33require_once DOL_DOCUMENT_ROOT .'/contact/class/contact.class.php'; // Contact / Address
34require_once DOL_DOCUMENT_ROOT .'/expedition/class/expedition.class.php'; // Shipping / Delivery
35require_once DOL_DOCUMENT_ROOT .'/fourn/class/fournisseur.commande.class.php'; // Purchase Order
36require_once DOL_DOCUMENT_ROOT .'/fourn/class/fournisseur.facture.class.php'; // Purchase Invoice
37require_once DOL_DOCUMENT_ROOT .'/projet/class/project.class.php'; // Project
38require_once DOL_DOCUMENT_ROOT .'/reception/class/reception.class.php'; // Reception
39require_once DOL_DOCUMENT_ROOT .'/recruitment/class/recruitmentcandidature.class.php'; // Recruiting
40require_once DOL_DOCUMENT_ROOT .'/societe/class/societe.class.php'; // Third-Party
41require_once DOL_DOCUMENT_ROOT .'/supplier_proposal/class/supplier_proposal.class.php'; // Supplier Proposal
42require_once DOL_DOCUMENT_ROOT .'/ticket/class/ticket.class.php'; // Ticket
43//require_once DOL_DOCUMENT_ROOT .'/expensereport/class/expensereport.class.php'; // Expense Report
44//require_once DOL_DOCUMENT_ROOT .'/holiday/class/holiday.class.php'; // Holidays (leave request)
45
46
47use Webklex\PHPIMAP\ClientManager;
48use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
49use Webklex\PHPIMAP\Exceptions\InvalidWhereQueryCriteriaException;
50use Webklex\PHPIMAP\Exceptions\GetMessagesFailedException;
51
52use OAuth\Common\Storage\DoliStorage;
53use OAuth\Common\Consumer\Credentials;
54
55
60{
64 public $element = 'emailcollector';
65
69 public $table_element = 'emailcollector_emailcollector';
70
74 public $ismultientitymanaged = 1;
75
79 public $isextrafieldmanaged = 0;
80
84 public $picto = 'email';
85
89 public $fk_element = 'fk_emailcollector';
90
94 protected $childtables = array();
95
99 protected $childtablesoncascade = array('emailcollector_emailcollectorfilter', 'emailcollector_emailcollectoraction');
100
101
121 // BEGIN MODULEBUILDER PROPERTIES
125 public $fields = array(
126 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'visible'=>2, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1),
127 'entity' => array('type'=>'integer', 'label'=>'Entity', 'enabled'=>1, 'visible'=>0, 'default'=>1, 'notnull'=>1, 'index'=>1, 'position'=>20),
128 'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>10, 'searchall'=>1, 'help'=>'Example: MyCollector1', 'csslist'=>'tdoverflowmax200'),
129 'label' => array('type'=>'varchar(255)', 'label'=>'Label', 'visible'=>1, 'enabled'=>1, 'position'=>30, 'notnull'=>-1, 'searchall'=>1, 'help'=>'Example: My Email collector', 'csslist'=>'tdoverflowmax150'),
130 'description' => array('type'=>'text', 'label'=>'Description', 'visible'=>-1, 'enabled'=>1, 'position'=>60, 'notnull'=>-1, 'cssview'=>'small', 'csslist'=>'small tdoverflowmax200'),
131 'host' => array('type'=>'varchar(255)', 'label'=>'EMailHost', 'visible'=>1, 'enabled'=>1, 'position'=>90, 'notnull'=>1, 'searchall'=>1, 'comment'=>"IMAP server", 'help'=>'Example: imap.gmail.com', 'csslist'=>'tdoverflowmax125'),
132 'port' => array('type'=>'varchar(10)', 'label'=>'EMailHostPort', 'visible'=>1, 'enabled'=>1, 'position'=>91, 'notnull'=>1, 'searchall'=>0, 'comment'=>"IMAP server port", 'help'=>'Example: 993', 'csslist'=>'tdoverflowmax50', 'default'=>'993'),
133 'hostcharset' => array('type'=>'varchar(16)', 'label'=>'HostCharset', 'visible'=>-1, 'enabled'=>1, 'position'=>92, 'notnull'=>0, 'searchall'=>0, 'comment'=>"IMAP server charset", 'help'=>'Example: "UTF-8" (May be "US-ASCII" with some Office365)', 'default'=>'UTF-8'),
134 'imap_encryption' => array('type'=>'varchar(16)', 'label'=>'ImapEncryption', 'visible'=>-1, 'enabled'=>1, 'position'=>93, 'searchall'=>0, 'comment'=>"IMAP encryption", 'help'=>'ImapEncryptionHelp', 'arrayofkeyval'=> array('ssl'=>'SSL', 'tls' => 'TLS', 'notls' => 'NOTLS'), 'default'=>'ssl'),
135 'norsh' => array('type'=>'integer', 'label'=>'NoRSH', 'visible'=>-1, 'enabled'=>"!getDolGlobalInt('MAIN_IMAP_USE_PHPIMAP')", 'position'=>94, 'searchall'=>0, 'help'=>'NoRSHHelp', 'arrayofkeyval'=> array(0 =>'No', 1 => 'Yes'), 'default'=> 0),
136 'acces_type' => array('type'=>'integer', 'label'=>'accessType', 'visible'=>-1, 'enabled'=>"getDolGlobalInt('MAIN_IMAP_USE_PHPIMAP')", 'position'=>101, 'notnull'=>1, 'index'=>1, 'comment'=>"IMAP login type", 'arrayofkeyval'=>array('0'=>'loginPassword', '1'=>'oauthToken'), 'default'=>'0', 'help'=>''),
137 'login' => array('type'=>'varchar(128)', 'label'=>'Login', 'visible'=>-1, 'enabled'=>1, 'position'=>102, 'notnull'=>-1, 'index'=>1, 'comment'=>"IMAP login", 'help'=>'Example: myaccount@gmail.com'),
138 'password' => array('type'=>'password', 'label'=>'Password', 'visible'=>-1, 'enabled'=>"1", 'position'=>103, 'notnull'=>-1, 'comment'=>"IMAP password", 'help'=>'WithGMailYouCanCreateADedicatedPassword'),
139 'oauth_service' => array('type'=>'varchar(128)', 'label'=>'oauthService', 'visible'=>-1, 'enabled'=>"getDolGlobalInt('MAIN_IMAP_USE_PHPIMAP')", 'position'=>104, 'notnull'=>0, 'index'=>1, 'comment'=>"IMAP login oauthService", 'arrayofkeyval'=>array(), 'help'=>'TokenMustHaveBeenCreated'),
140 'source_directory' => array('type'=>'varchar(255)', 'label'=>'MailboxSourceDirectory', 'visible'=>-1, 'enabled'=>1, 'position'=>104, 'notnull'=>1, 'default' => 'Inbox', 'help'=>'Example: INBOX'),
141 'target_directory' => array('type'=>'varchar(255)', 'label'=>'MailboxTargetDirectory', 'visible'=>1, 'enabled'=>1, 'position'=>110, 'notnull'=>0, 'help'=>"EmailCollectorTargetDir"),
142 'maxemailpercollect' => array('type'=>'integer', 'label'=>'MaxEmailCollectPerCollect', 'visible'=>-1, 'enabled'=>1, 'position'=>111, 'default'=>100),
143 'datelastresult' => array('type'=>'datetime', 'label'=>'DateLastCollectResult', 'visible'=>1, 'enabled'=>'$action != "create" && $action != "edit"', 'position'=>121, 'notnull'=>-1, 'csslist'=>'nowraponall'),
144 'codelastresult' => array('type'=>'varchar(16)', 'label'=>'CodeLastResult', 'visible'=>1, 'enabled'=>'$action != "create" && $action != "edit"', 'position'=>122, 'notnull'=>-1,),
145 'lastresult' => array('type'=>'varchar(255)', 'label'=>'LastResult', 'visible'=>1, 'enabled'=>'$action != "create" && $action != "edit"', 'position'=>123, 'notnull'=>-1, 'cssview'=>'small', 'csslist'=>'small tdoverflowmax200'),
146 'datelastok' => array('type'=>'datetime', 'label'=>'DateLastcollectResultOk', 'visible'=>1, 'enabled'=>'$action != "create"', 'position'=>125, 'notnull'=>-1, 'csslist'=>'nowraponall'),
147 'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'visible'=>0, 'enabled'=>1, 'position'=>61, 'notnull'=>-1,),
148 'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'visible'=>0, 'enabled'=>1, 'position'=>62, 'notnull'=>-1,),
149 'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>-2, 'enabled'=>1, 'position'=>500, 'notnull'=>1,),
150 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-2, 'enabled'=>1, 'position'=>501, 'notnull'=>1,),
151 //'date_validation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>502),
152 'fk_user_creat' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'visible'=>-2, 'enabled'=>1, 'position'=>510, 'notnull'=>1,),
153 'fk_user_modif' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'visible'=>-2, 'enabled'=>1, 'position'=>511, 'notnull'=>-1,),
154 //'fk_user_valid' =>array('type'=>'integer', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>512),
155 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'visible'=>-2, 'enabled'=>1, 'position'=>1000, 'notnull'=>-1,),
156 'status' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>1000, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array('0'=>'Inactive', '1'=>'Active'))
157 );
158
159
163 public $rowid;
164
168 public $ref;
169
173 public $entity;
174
178 public $label;
179
180
184 public $status;
185
189 public $date_creation;
190
194 public $tms;
195
199 public $fk_user_creat;
200
204 public $fk_user_modif;
205
209 public $import_key;
210
211 public $host;
212 public $port;
213 public $hostcharset;
214 public $login;
215 public $password;
216 public $acces_type;
217 public $oauth_service;
218 public $imap_encryption;
219 public $norsh;
220 public $source_directory;
221 public $target_directory;
222 public $maxemailpercollect;
223
227 public $datelastresult;
228
229 public $codelastresult;
230 public $lastresult;
231 public $datelastok;
232 // END MODULEBUILDER PROPERTIES
233
234 public $filters;
235 public $actions;
236
237 public $debuginfo;
238
239 const STATUS_DISABLED = 0;
240 const STATUS_ENABLED = 1;
241
242
248 public function __construct(DoliDB $db)
249 {
250 global $conf, $langs;
251
252 $this->db = $db;
253
254 if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) {
255 $this->fields['rowid']['visible'] = 0;
256 }
257 if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
258 $this->fields['entity']['enabled'] = 0;
259 }
260
261 // List of oauth services
262 $oauthservices = array();
263
264 foreach ($conf->global as $key => $val) {
265 if (!empty($val) && preg_match('/^OAUTH_.*_ID$/', $key)) {
266 $key = preg_replace('/^OAUTH_/', '', $key);
267 $key = preg_replace('/_ID$/', '', $key);
268 if (preg_match('/^.*-/', $key)) {
269 $name = preg_replace('/^.*-/', '', $key);
270 } else {
271 $name = $langs->trans("NoName");
272 }
273 $provider = preg_replace('/-.*$/', '', $key);
274 $provider = ucfirst(strtolower($provider));
275
276 $oauthservices[$key] = $name." (".$provider.")";
277 }
278 }
279
280 $this->fields['oauth_service']['arrayofkeyval'] = $oauthservices;
281
282 // Unset fields that are disabled
283 foreach ($this->fields as $key => $val) {
284 if (isset($val['enabled']) && empty($val['enabled'])) {
285 unset($this->fields[$key]);
286 }
287 }
288
289 // Translate some data of arrayofkeyval
290 foreach ($this->fields as $key => $val) {
291 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
292 foreach ($val['arrayofkeyval'] as $key2 => $val2) {
293 $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
294 }
295 }
296 }
297 }
298
306 public function create(User $user, $notrigger = false)
307 {
308 global $langs;
309
310 // Check parameters
311 if ($this->host && preg_match('/^http:/i', trim($this->host))) {
312 $langs->load("errors");
313 $this->error = $langs->trans("ErrorHostMustNotStartWithHttp", $this->host);
314 return -1;
315 }
316
317 include_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php';
318 $this->password = dolEncrypt($this->password);
319
320 $id = $this->createCommon($user, $notrigger);
321
322 $this->password = dolDecrypt($this->password);
323
324 if (is_array($this->filters) && count($this->filters)) {
325 $emailcollectorfilter = new EmailCollectorFilter($this->db);
326
327 foreach ($this->filters as $filter) {
328 $emailcollectorfilter->type = $filter['type'];
329 $emailcollectorfilter->rulevalue = $filter['rulevalue'];
330 $emailcollectorfilter->fk_emailcollector = $this->id;
331 $emailcollectorfilter->status = $filter['status'];
332
333 $emailcollectorfilter->create($user);
334 }
335 }
336
337 if (is_array($this->actions) && count($this->actions)) {
338 $emailcollectoroperation = new EmailCollectorAction($this->db);
339
340 foreach ($this->actions as $operation) {
341 $emailcollectoroperation->type = $operation['type'];
342 $emailcollectoroperation->actionparam = $operation['actionparam'];
343 $emailcollectoroperation->fk_emailcollector = $this->id;
344 $emailcollectoroperation->status = $operation['status'];
345 $emailcollectoroperation->position = $operation['position'];
346
347 $emailcollectoroperation->create($user);
348 }
349 }
350
351 return $id;
352 }
353
361 public function createFromClone(User $user, $fromid)
362 {
363 global $langs, $extrafields;
364 $error = 0;
365
366 dol_syslog(__METHOD__, LOG_DEBUG);
367
368 $object = new self($this->db);
369
370 $this->db->begin();
371
372 // Load source object
373 $object->fetchCommon($fromid);
374
375 $object->fetchFilters(); // Rules
376 $object->fetchActions(); // Operations
377
378 // Reset some properties
379 unset($object->id);
380 unset($object->fk_user_creat);
381 unset($object->import_key);
382 unset($object->password);
383
384 // Clear fields
385 $object->ref = "copy_of_".$object->ref;
386 $object->label = $langs->trans("CopyOf")." ".$object->label;
387 if (empty($object->host)) {
388 $object->host = 'imap.example.com';
389 }
390 // Clear extrafields that are unique
391 if (is_array($object->array_options) && count($object->array_options) > 0) {
392 $extrafields->fetch_name_optionals_label($this->table_element);
393 foreach ($object->array_options as $key => $option) {
394 $shortkey = preg_replace('/options_/', '', $key);
395 if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey])) {
396 //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
397 unset($object->array_options[$key]);
398 }
399 }
400 }
401
402 // Create clone
403 $object->context['createfromclone'] = 'createfromclone';
404 $result = $object->create($user);
405 if ($result < 0) {
406 $error++;
407 $this->error = $object->error;
408 $this->errors = $object->errors;
409 }
410
411 unset($object->context['createfromclone']);
412
413 // End
414 if (!$error) {
415 $this->db->commit();
416 return $object;
417 } else {
418 $this->db->rollback();
419 return -1;
420 }
421 }
422
430 public function fetch($id, $ref = null)
431 {
432 $result = $this->fetchCommon($id, $ref);
433
434 include_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php';
435 $this->password = dolDecrypt($this->password);
436
437 //if ($result > 0 && !empty($this->table_element_line)) $this->fetchLines();
438 return $result;
439 }
440
446 /*
447 public function fetchLines()
448 {
449 $this->lines=array();
450
451 // Load lines with object EmailCollectorLine
452
453 return count($this->lines)?1:0;
454 }
455 */
456
468 public function fetchAll(User $user, $activeOnly = 0, $sortfield = 's.rowid', $sortorder = 'ASC', $limit = 100, $page = 0)
469 {
470 global $langs;
471
472 $obj_ret = array();
473
474 $sql = "SELECT s.rowid";
475 $sql .= " FROM ".MAIN_DB_PREFIX."emailcollector_emailcollector as s";
476 $sql .= ' WHERE s.entity IN ('.getEntity('emailcollector').')';
477 if ($activeOnly) {
478 $sql .= " AND s.status = 1";
479 }
480 $sql .= $this->db->order($sortfield, $sortorder);
481 if ($limit) {
482 if ($page < 0) {
483 $page = 0;
484 }
485 $offset = $limit * $page;
486
487 $sql .= $this->db->plimit($limit + 1, $offset);
488 }
489
490 $result = $this->db->query($sql);
491 if ($result) {
492 $num = $this->db->num_rows($result);
493 $i = 0;
494 while ($i < $num) {
495 $obj = $this->db->fetch_object($result);
496 $emailcollector_static = new EmailCollector($this->db);
497 if ($emailcollector_static->fetch($obj->rowid)) {
498 $obj_ret[] = $emailcollector_static;
499 }
500 $i++;
501 }
502 } else {
503 $this->errors[] = 'EmailCollector::fetchAll Error when retrieve emailcollector list';
504 dol_syslog('EmailCollector::fetchAll Error when retrieve emailcollector list', LOG_ERR);
505 $ret = -1;
506 }
507 if (!count($obj_ret)) {
508 dol_syslog('EmailCollector::fetchAll No emailcollector found', LOG_DEBUG);
509 }
510
511 return $obj_ret;
512 }
513
521 public function update(User $user, $notrigger = false)
522 {
523 global $langs;
524
525 // Check parameters
526 if ($this->host && preg_match('/^http:/i', trim($this->host))) {
527 $langs->load("errors");
528 $this->error = $langs->trans("ErrorHostMustNotStartWithHttp", $this->host);
529 return -1;
530 }
531
532 include_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php';
533 $this->password = dolEncrypt($this->password);
534
535 $result = $this->updateCommon($user, $notrigger);
536
537 $this->password = dolDecrypt($this->password);
538
539 return $result;
540 }
541
549 public function delete(User $user, $notrigger = false)
550 {
551 return $this->deleteCommon($user, $notrigger, 1);
552 }
553
564 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
565 {
566 global $conf, $langs, $action, $hookmanager;
567
568 if (!empty($conf->dol_no_mouse_hover)) {
569 $notooltip = 1; // Force disable tooltips
570 }
571
572 $result = '';
573
574 $label = '<u>'.$langs->trans("EmailCollector").'</u>';
575 $label .= '<br>';
576 $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
577
578 $url = DOL_URL_ROOT.'/admin/emailcollector_card.php?id='.$this->id;
579
580 if ($option != 'nolink') {
581 // Add param to save lastsearch_values or not
582 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
583 if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
584 $add_save_lastsearch_values = 1;
585 }
586 if ($add_save_lastsearch_values) {
587 $url .= '&save_lastsearch_values=1';
588 }
589 }
590
591 $linkclose = '';
592 if (empty($notooltip)) {
593 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
594 $label = $langs->trans("ShowEmailCollector");
595 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
596 }
597 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
598 $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
599 } else {
600 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
601 }
602
603 $linkstart = '<a href="'.$url.'"';
604 $linkstart .= $linkclose.'>';
605 $linkend = '</a>';
606
607 $result .= $linkstart;
608 if ($withpicto) {
609 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
610 }
611 if ($withpicto != 2) {
612 $result .= $this->ref;
613 }
614 $result .= $linkend;
615 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
616
617 $hookmanager->initHooks(array('emailcollectordao'));
618 $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
619 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
620 if ($reshook > 0) {
621 $result = $hookmanager->resPrint;
622 } else {
623 $result .= $hookmanager->resPrint;
624 }
625
626 return $result;
627 }
628
635 public function getLibStatut($mode = 0)
636 {
637 return $this->LibStatut($this->status, $mode);
638 }
639
640 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
648 public function LibStatut($status, $mode = 0)
649 {
650 // phpcs:enable
651 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
652 global $langs;
653 //$langs->load("mymodule");
654 $this->labelStatus[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
655 $this->labelStatus[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
656 $this->labelStatusShort[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
657 $this->labelStatusShort[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
658 }
659
660 $statusType = 'status5';
661 if ($status == self::STATUS_ENABLED) {
662 $statusType = 'status4';
663 }
664
665 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
666 }
667
674 public function info($id)
675 {
676 $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
677 $sql .= ' fk_user_creat, fk_user_modif';
678 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
679 $sql .= ' WHERE t.rowid = '.((int) $id);
680 $result = $this->db->query($sql);
681 if ($result) {
682 if ($this->db->num_rows($result)) {
683 $obj = $this->db->fetch_object($result);
684 $this->id = $obj->rowid;
685
686 $this->user_creation_id = $obj->fk_user_creat;
687 $this->user_modification_id = $obj->fk_user_modif;
688 $this->date_creation = $this->db->jdate($obj->datec);
689 $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
690 }
691
692 $this->db->free($result);
693 } else {
694 dol_print_error($this->db);
695 }
696 }
697
704 public function initAsSpecimen()
705 {
706 $this->host = 'localhost';
707 $this->login = 'alogin';
708
709 $this->initAsSpecimenCommon();
710 }
711
718 public function fetchFilters()
719 {
720 $this->filters = array();
721
722 $sql = 'SELECT rowid, type, rulevalue, status';
723 $sql .= ' FROM '.MAIN_DB_PREFIX.'emailcollector_emailcollectorfilter';
724 $sql .= ' WHERE fk_emailcollector = '.((int) $this->id);
725 //$sql.= ' ORDER BY position';
726
727 $resql = $this->db->query($sql);
728 if ($resql) {
729 $num = $this->db->num_rows($resql);
730 $i = 0;
731 while ($i < $num) {
732 $obj = $this->db->fetch_object($resql);
733 $this->filters[$obj->rowid] = array('id'=>$obj->rowid, 'type'=>$obj->type, 'rulevalue'=>$obj->rulevalue, 'status'=>$obj->status);
734 $i++;
735 }
736 $this->db->free($resql);
737 } else {
738 dol_print_error($this->db);
739 }
740
741 return 1;
742 }
743
750 public function fetchActions()
751 {
752 $this->actions = array();
753
754 $sql = 'SELECT rowid, type, actionparam, status';
755 $sql .= ' FROM '.MAIN_DB_PREFIX.'emailcollector_emailcollectoraction';
756 $sql .= ' WHERE fk_emailcollector = '.((int) $this->id);
757 $sql .= ' ORDER BY position';
758
759 $resql = $this->db->query($sql);
760 if ($resql) {
761 $num = $this->db->num_rows($resql);
762 $i = 0;
763 while ($i < $num) {
764 $obj = $this->db->fetch_object($resql);
765 $this->actions[$obj->rowid] = array('id'=>$obj->rowid, 'type'=>$obj->type, 'actionparam'=>$obj->actionparam, 'status'=>$obj->status);
766 $i++;
767 }
768 $this->db->free($resql);
769
770 return 1;
771 } else {
772 dol_print_error($this->db);
773
774 return -1;
775 }
776 }
777
778
784 public function getConnectStringIMAP()
785 {
786 global $conf;
787
788 // Connect to IMAP
789 $flags = '/service=imap'; // IMAP
790 if (!empty($conf->global->IMAP_FORCE_TLS)) {
791 $flags .= '/tls';
792 } elseif (empty($this->imap_encryption) || ($this->imap_encryption == 'ssl' && !empty($conf->global->IMAP_FORCE_NOSSL))) {
793 $flags .= '';
794 } else {
795 $flags .= '/' . $this->imap_encryption;
796 }
797
798 $flags .= '/novalidate-cert';
799 //$flags.='/readonly';
800 //$flags.='/debug';
801 if (!empty($this->norsh) || !empty($conf->global->IMAP_FORCE_NORSH)) {
802 $flags .= '/norsh';
803 }
804 //Used in shared mailbox from Office365
805 if (strpos($this->login, '/') != false) {
806 $partofauth = explode('/', $this->login);
807 $flags .= '/authuser='.$partofauth[0].'/user='.$partofauth[1];
808 }
809
810 $connectstringserver = '{'.$this->host.':'.$this->port.$flags.'}';
811
812 return $connectstringserver;
813 }
814
821 public function getEncodedUtf7($str)
822 {
823 if (function_exists('mb_convert_encoding')) {
824 // change spaces by entropy because mb_convert fail with spaces
825 $str = preg_replace("/ /", "xyxy", $str);
826 // if mb_convert work
827 if ($str = mb_convert_encoding($str, "UTF-7")) {
828 // change characters
829 $str = preg_replace("/\+A/", "&A", $str);
830 // change to spaces again
831 $str = preg_replace("/xyxy/", " ", $str);
832 return $str;
833 } else {
834 // print error and return false
835 $this->error = "error: is not possible to encode this string '".$str."'";
836 return false;
837 }
838 } else {
839 return $str;
840 }
841 }
842
849 public function doCollect()
850 {
851 global $user;
852
853 $nbErrors = 0;
854
855 $arrayofcollectors = $this->fetchAll($user, 1);
856 // Loop on each collector
857 foreach ($arrayofcollectors as $emailcollector) {
858 $result = $emailcollector->doCollectOneCollector(0);
859 dol_syslog("doCollect result = ".$result." for emailcollector->id = ".$emailcollector->id);
860
861 $this->error .= 'EmailCollector ID '.$emailcollector->id.':'.$emailcollector->error.'<br>';
862 if (!empty($emailcollector->errors)) {
863 $this->error .= join('<br>', $emailcollector->errors);
864 $nbErrors++;
865 }
866 $this->output .= 'EmailCollector ID '.$emailcollector->id.': '.$emailcollector->lastresult.'<br>';
867 }
868
869 return $nbErrors;
870 }
871
883 private function overwritePropertiesOfObject(&$object, $actionparam, $messagetext, $subject, $header, &$operationslog)
884 {
885 global $conf, $langs;
886
887 $errorforthisaction = 0;
888
889 // set output lang
890 $outputlangs = $langs;
891 $newlang = '';
892 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
893 $newlang = GETPOST('lang_id', 'aZ09');
894 }
895 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
896 $newlang = $object->thirdparty->default_lang;
897 }
898 if (!empty($newlang)) {
899 $outputlangs = new Translate('', $conf);
900 $outputlangs->setDefaultLang($newlang);
901 }
902
903 // Overwrite values with values extracted from source email
904 // $this->actionparam = 'opportunity_status=123;abc=EXTRACT:BODY:....'
905 $arrayvaluetouse = dolExplodeIntoArray($actionparam, '(\n\r|\r|\n|;)', '=');
906
907 $tmp = array();
908
909 // Loop on each property set into actionparam
910 foreach ($arrayvaluetouse as $propertytooverwrite => $valueforproperty) {
911 $tmpclass = '';
912 $tmpproperty = '';
913 $tmparray = explode('.', $propertytooverwrite);
914 if (count($tmparray) == 2) {
915 $tmpclass = $tmparray[0];
916 $tmpproperty = $tmparray[1];
917 } else {
918 $tmpproperty = $tmparray[0];
919 }
920 if ($tmpclass && ($tmpclass != $object->element)) {
921 continue; // Property is for another type of object
922 }
923
924 //if (property_exists($object, $tmpproperty) || preg_match('/^options_/', $tmpproperty))
925 if ($tmpproperty) {
926 $sourcestring = '';
927 $sourcefield = '';
928 $regexstring = '';
929 //$transformationstring='';
930 $regforregex = array();
931 if (preg_match('/^EXTRACT:([a-zA-Z0-9_]+):(.*):([^:])$/', $valueforproperty, $regforregex)) {
932 $sourcefield = $regforregex[1];
933 $regexstring = $regforregex[2];
934 //$transofrmationstring=$regforregex[3];
935 } elseif (preg_match('/^EXTRACT:([a-zA-Z0-9_]+):(.*)$/', $valueforproperty, $regforregex)) {
936 $sourcefield = $regforregex[1];
937 $regexstring = $regforregex[2];
938 }
939
940 if (!empty($sourcefield) && !empty($regexstring)) {
941 if (strtolower($sourcefield) == 'body') {
942 $sourcestring = $messagetext;
943 } elseif (strtolower($sourcefield) == 'subject') {
944 $sourcestring = $subject;
945 } elseif (strtolower($sourcefield) == 'header') {
946 $sourcestring = $header;
947 }
948
949 if ($sourcestring) {
950 $regforval = array();
951 $regexoptions = '';
952 if (strtolower($sourcefield) == 'body') {
953 $regexoptions = 'ms'; // The m means ^ and $ char is valid at each new line. The s means the char '.' is valid for new lines char too
954 }
955 if (strtolower($sourcefield) == 'header') {
956 $regexoptions = 'm'; // The m means ^ and $ char is valid at each new line.
957 }
958
959 //var_dump($tmpproperty.' - '.$regexstring.' - '.$regexoptions.' - '.$sourcestring);
960 if (preg_match('/'.$regexstring.'/'.$regexoptions, $sourcestring, $regforval)) {
961 // Overwrite param $tmpproperty
962 $valueextracted = isset($regforval[count($regforval) - 1]) ?trim($regforval[count($regforval) - 1]) : null;
963 if (strtolower($sourcefield) == 'header') { // extract from HEADER
964 if (preg_match('/^options_/', $tmpproperty)) {
965 $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $this->decodeSMTPSubject($valueextracted);
966 } else {
967 if (property_exists($object, $tmpproperty)) {
968 $object->$tmpproperty = $this->decodeSMTPSubject($valueextracted);
969 } else {
970 $tmp[$tmpproperty] = $this->decodeSMTPSubject($valueextracted);
971 }
972 }
973 } else { // extract from BODY
974 if (preg_match('/^options_/', $tmpproperty)) {
975 $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $this->decodeSMTPSubject($valueextracted);
976 } else {
977 if (property_exists($object, $tmpproperty)) {
978 $object->$tmpproperty = $this->decodeSMTPSubject($valueextracted);
979 } else {
980 $tmp[$tmpproperty] = $this->decodeSMTPSubject($valueextracted);
981 }
982 }
983 }
984 if (preg_match('/^options_/', $tmpproperty)) {
985 $operationslog .= '<br>Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> found '.dol_escape_htmltag(dol_trunc($object->array_options[preg_replace('/^options_/', '', $tmpproperty)], 128));
986 } else {
987 if (property_exists($object, $tmpproperty)) {
988 $operationslog .= '<br>Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> found '.dol_escape_htmltag(dol_trunc($object->$tmpproperty, 128));
989 } else {
990 $operationslog .= '<br>Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> found '.dol_escape_htmltag(dol_trunc($tmp[$tmpproperty], 128));
991 }
992 }
993 } else {
994 // Regex not found
995 if (property_exists($object, $tmpproperty)) {
996 $object->$tmpproperty = null;
997 } else {
998 $tmp[$tmpproperty] = null;
999 }
1000
1001 $operationslog .= '<br>Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> not found, so property '.dol_escape_htmltag($tmpproperty).' is set to null.';
1002 }
1003 } else {
1004 // Nothing can be done for this param
1005 $errorforthisaction++;
1006 $this->error = 'The extract rule to use to overwrite properties has on an unknown source (must be HEADER, SUBJECT or BODY)';
1007 $this->errors[] = $this->error;
1008
1009 $operationslog .= '<br>'.$this->error;
1010 }
1011 } elseif (preg_match('/^(SET|SETIFEMPTY):(.*)$/', $valueforproperty, $regforregex)) {
1012 $valuecurrent = '';
1013 if (preg_match('/^options_/', $tmpproperty)) {
1014 $valuecurrent = $object->array_options[preg_replace('/^options_/', '', $tmpproperty)];
1015 } else {
1016 if (property_exists($object, $tmpproperty)) {
1017 $valuecurrent = $object->$tmpproperty;
1018 } else {
1019 $valuecurrent = $tmp[$tmpproperty];
1020 }
1021 }
1022
1023 if ($regforregex[1] == 'SET' || empty($valuecurrent)) {
1024 $valuetouse = $regforregex[2];
1025 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object);
1026 complete_substitutions_array($substitutionarray, $outputlangs, $object);
1027 $matcharray = array();
1028 preg_match_all('/__([a-z0-9]+(?:_[a-z0-9]+)?)__/i', $valuetouse, $matcharray);
1029 //var_dump($tmpproperty.' - '.$object->$tmpproperty.' - '.$valuetouse); var_dump($matcharray);
1030 if (is_array($matcharray[1])) { // $matcharray[1] is an array with the list of substitution key found without the __X__ syntax into the SET entry
1031 foreach ($matcharray[1] as $keytoreplace) {
1032 if ($keytoreplace) {
1033 if (preg_match('/^options_/', $keytoreplace)) {
1034 $substitutionarray['__'.$keytoreplace.'__'] = $object->array_options[preg_replace('/^options_/', '', $keytoreplace)];
1035 } else {
1036 if (property_exists($object, $keytoreplace)) {
1037 $substitutionarray['__'.$keytoreplace.'__'] = $object->$keytoreplace;
1038 } else {
1039 $substitutionarray['__'.$keytoreplace.'__'] = $tmp[$keytoreplace];
1040 }
1041 }
1042 }
1043 }
1044 }
1045 //var_dump($substitutionarray);
1046 //dol_syslog('substitutionarray='.var_export($substitutionarray, true));
1047
1048 $valuetouse = make_substitutions($valuetouse, $substitutionarray);
1049 if (preg_match('/^options_/', $tmpproperty)) {
1050 $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $valuetouse;
1051
1052 $operationslog .= '<br>Set value '.dol_escape_htmltag($valuetouse).' into object->array_options['.dol_escape_htmltag(preg_replace('/^options_/', '', $tmpproperty)).']';
1053 } else {
1054 if (property_exists($object, $tmpproperty)) {
1055 $object->$tmpproperty = $valuetouse;
1056 } else {
1057 $tmp[$tmpproperty] = $valuetouse;
1058 }
1059
1060 $operationslog .= '<br>Set value '.dol_escape_htmltag($valuetouse).' into object->'.dol_escape_htmltag($tmpproperty);
1061 }
1062 }
1063 } else {
1064 $errorforthisaction++;
1065 $this->error = 'Bad syntax for description of action parameters: '.$actionparam;
1066 $this->errors[] = $this->error;
1067 }
1068 }
1069 }
1070
1071 return $errorforthisaction;
1072 }
1073
1080 public function doCollectOneCollector($mode = 0)
1081 {
1082 global $db, $conf, $langs, $user;
1083 global $hookmanager;
1084
1085 //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1086
1087 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1088 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
1089 require_once DOL_DOCUMENT_ROOT.'/includes/webklex/php-imap/vendor/autoload.php';
1090 }
1091
1092 dol_syslog("EmailCollector::doCollectOneCollector start for id=".$this->id." - ".$this->ref, LOG_INFO);
1093
1094 $langs->loadLangs(array("project", "companies", "mails", "errors", "ticket", "agenda", "commercial"));
1095
1096 $error = 0;
1097 $this->output = '';
1098 $this->error = '';
1099 $this->debuginfo = '';
1100
1101 $search = '';
1102 $searchhead = '';
1103 $searchfilterdoltrackid = 0;
1104 $searchfilternodoltrackid = 0;
1105 $searchfilterisanswer = 0;
1106 $searchfilterisnotanswer = 0;
1107 $searchfilterreplyto = 0;
1108 $searchfilterexcludebodyarray = array();
1109 $searchfilterexcludesubjectarray = array();
1110 $operationslog = '';
1111
1112 $now = dol_now();
1113
1114
1115 if (empty($this->host)) {
1116 $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('EMailHost'));
1117 return -1;
1118 }
1119 if (empty($this->login)) {
1120 $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Login'));
1121 return -1;
1122 }
1123 if (empty($this->source_directory)) {
1124 $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('MailboxSourceDirectory'));
1125 return -1;
1126 }
1127
1128 $this->fetchFilters();
1129 $this->fetchActions();
1130
1131 $targetdir = ($this->target_directory ? $this->target_directory : ''); // Can be '[Gmail]/Trash' or 'mytag'
1132
1133 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
1134 if ($this->acces_type == 1) {
1135 // Mode OAUth2 with PHP-IMAP
1136 require_once DOL_DOCUMENT_ROOT.'/core/lib/oauth.lib.php'; // define $supportedoauth2array
1137 $keyforsupportedoauth2array = $this->oauth_service;
1138 if (preg_match('/^.*-/', $keyforsupportedoauth2array)) {
1139 $keyforprovider = preg_replace('/^.*-/', '', $keyforsupportedoauth2array);
1140 } else {
1141 $keyforprovider = '';
1142 }
1143 $keyforsupportedoauth2array = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
1144 $keyforsupportedoauth2array = 'OAUTH_'.$keyforsupportedoauth2array.'_NAME';
1145
1146 $OAUTH_SERVICENAME = (empty($supportedoauth2array[$keyforsupportedoauth2array]['name']) ? 'Unknown' : $supportedoauth2array[$keyforsupportedoauth2array]['name'].($keyforprovider ? '-'.$keyforprovider : ''));
1147
1148 require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php';
1149 //$debugtext = "Host: ".$this->host."<br>Port: ".$this->port."<br>Login: ".$this->login."<br>Password: ".$this->password."<br>access type: ".$this->acces_type."<br>oauth service: ".$this->oauth_service."<br>Max email per collect: ".$this->maxemailpercollect;
1150 //dol_syslog($debugtext);
1151
1152 $token = '';
1153
1154 $storage = new DoliStorage($db, $conf, $keyforprovider);
1155
1156 try {
1157 $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME);
1158
1159 $expire = true;
1160 // Is token expired or will token expire in the next 30 seconds
1161 // if (is_object($tokenobj)) {
1162 // $expire = ($tokenobj->getEndOfLife() !== -9002 && $tokenobj->getEndOfLife() !== -9001 && time() > ($tokenobj->getEndOfLife() - 30));
1163 // }
1164 // Token expired so we refresh it
1165 if (is_object($tokenobj) && $expire) {
1166 $credentials = new Credentials(
1167 getDolGlobalString('OAUTH_'.$this->oauth_service.'_ID'),
1168 getDolGlobalString('OAUTH_'.$this->oauth_service.'_SECRET'),
1169 getDolGlobalString('OAUTH_'.$this->oauth_service.'_URLAUTHORIZE')
1170 );
1171 $serviceFactory = new \OAuth\ServiceFactory();
1172 $oauthname = explode('-', $OAUTH_SERVICENAME);
1173 // ex service is Google-Emails we need only the first part Google
1174 $apiService = $serviceFactory->createService($oauthname[0], $credentials, $storage, array());
1175 // We have to save the token because Google give it only once
1176 $refreshtoken = $tokenobj->getRefreshToken();
1177 $tokenobj = $apiService->refreshAccessToken($tokenobj);
1178 $tokenobj->setRefreshToken($refreshtoken);
1179 $storage->storeAccessToken($OAUTH_SERVICENAME, $tokenobj);
1180 }
1181 $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME);
1182 if (is_object($tokenobj)) {
1183 $token = $tokenobj->getAccessToken();
1184 } else {
1185 $this->error = "Token not found";
1186 return -1;
1187 }
1188 } catch (Exception $e) {
1189 // Return an error if token not found
1190 $this->error = $e->getMessage();
1191 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
1192 return -1;
1193 }
1194
1195 $cm = new ClientManager();
1196 $client = $cm->make([
1197 'host' => $this->host,
1198 'port' => $this->port,
1199 'encryption' => !empty($this->imap_encryption) ? $this->imap_encryption : false,
1200 'validate_cert' => true,
1201 'protocol' => 'imap',
1202 'username' => $this->login,
1203 'password' => $token,
1204 'authentication' => "oauth",
1205 ]);
1206 } else {
1207 // Mode login/pass with PHP-IMAP
1208 $cm = new ClientManager();
1209 $client = $cm->make([
1210 'host' => $this->host,
1211 'port' => $this->port,
1212 'encryption' => !empty($this->imap_encryption) ? $this->imap_encryption : false,
1213 'validate_cert' => true,
1214 'protocol' => 'imap',
1215 'username' => $this->login,
1216 'password' => $this->password,
1217 'authentication' => "login",
1218 ]);
1219 }
1220
1221 try {
1222 $client->connect();
1223 } catch (ConnectionFailedException $e) {
1224 $this->error = $e->getMessage();
1225 $this->errors[] = $this->error;
1226 dol_syslog("EmailCollector::doCollectOneCollector ".$this->error, LOG_ERR);
1227 return -1;
1228 }
1229
1230 $host = dol_getprefix('email');
1231 } else {
1232 // Use native IMAP functions
1233 if (!function_exists('imap_open')) {
1234 $this->error = 'IMAP function not enabled on your PHP';
1235 return -2;
1236 }
1237 $sourcedir = $this->source_directory;
1238
1239 $connectstringserver = $this->getConnectStringIMAP();
1240 $connectstringsource = $connectstringserver.imap_utf7_encode($sourcedir);
1241 $connectstringtarget = $connectstringserver.imap_utf7_encode($targetdir);
1242
1243 $connection = imap_open($connectstringsource, $this->login, $this->password);
1244 if (!$connection) {
1245 $this->error = 'Failed to open IMAP connection '.$connectstringsource.' '.imap_last_error();
1246 return -3;
1247 }
1248 imap_errors(); // Clear stack of errors.
1249
1250 $host = dol_getprefix('email');
1251 //$host = '123456';
1252
1253 // Define the IMAP search string
1254 // See https://tools.ietf.org/html/rfc3501#section-6.4.4 for IMAPv4 (PHP not yet compatible)
1255 // See https://tools.ietf.org/html/rfc1064 page 13 for IMAPv2
1256 //$search='ALL';
1257 }
1258
1259 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
1260 // Use PHPIMAP external library
1261 $criteria = array(array('UNDELETED')); // Seems not supported by some servers
1262 foreach ($this->filters as $rule) {
1263 if (empty($rule['status'])) {
1264 continue;
1265 }
1266
1267 $not = '';
1268 if (strpos($rule['rulevalue'], '!') === 0) {
1269 // The value start with !, so we exclude the criteria
1270 $not = 'NOT ';
1271 // Then remove the ! from the string for next filters
1272 $rule['rulevalue'] = substr($rule['rulevalue'], 1);
1273 }
1274
1275 if ($rule['type'] == 'from') {
1276 $tmprulevaluearray = explode('*', $rule['rulevalue']);
1277 if (count($tmprulevaluearray) >= 2) {
1278 foreach ($tmprulevaluearray as $tmprulevalue) {
1279 array_push($criteria, array($not."FROM" => $tmprulevalue));
1280 }
1281 } else {
1282 array_push($criteria, array($not."FROM" => $rule['rulevalue']));
1283 }
1284 }
1285 if ($rule['type'] == 'to') {
1286 $tmprulevaluearray = explode('*', $rule['rulevalue']);
1287 if (count($tmprulevaluearray) >= 2) {
1288 foreach ($tmprulevaluearray as $tmprulevalue) {
1289 array_push($criteria, array($not."TO" => $tmprulevalue));
1290 }
1291 } else {
1292 array_push($criteria, array($not."TO" => $rule['rulevalue']));
1293 }
1294 }
1295 if ($rule['type'] == 'bcc') {
1296 array_push($criteria, array($not."BCC" => $rule['rulevalue']));
1297 }
1298 if ($rule['type'] == 'cc') {
1299 array_push($criteria, array($not."CC" => $rule['rulevalue']));
1300 }
1301 if ($rule['type'] == 'subject') {
1302 if (strpos($rule['rulevalue'], '!') === 0) {
1303 //array_push($criteria, array("NOT SUBJECT" => $rule['rulevalue']));
1304 $searchfilterexcludesubjectarray[] = preg_replace('/^!/', '', $rule['rulevalue']);
1305 } else {
1306 array_push($criteria, array("SUBJECT" => $rule['rulevalue']));
1307 }
1308 }
1309 if ($rule['type'] == 'body') {
1310 if (strpos($rule['rulevalue'], '!') === 0) {
1311 //array_push($criteria, array("NOT BODY" => $rule['rulevalue']));
1312 $searchfilterexcludebodyarray[] = preg_replace('/^!/', '', $rule['rulevalue']);
1313 } else {
1314 array_push($criteria, array("BODY" => $rule['rulevalue']));
1315 }
1316 }
1317 if ($rule['type'] == 'header') {
1318 array_push($criteria, array($not."HEADER" => $rule['rulevalue']));
1319 }
1320
1321 /* seems not used */
1322 /*
1323 if ($rule['type'] == 'notinsubject') {
1324 array_push($criteria, array($not."SUBJECT NOT" => $rule['rulevalue']));
1325 }
1326 if ($rule['type'] == 'notinbody') {
1327 array_push($criteria, array($not."BODY NOT" => $rule['rulevalue']));
1328 }*/
1329
1330 if ($rule['type'] == 'seen') {
1331 array_push($criteria, array($not."SEEN"));
1332 }
1333 if ($rule['type'] == 'unseen') {
1334 array_push($criteria, array($not."UNSEEN"));
1335 }
1336 if ($rule['type'] == 'unanswered') {
1337 array_push($criteria, array($not."UNANSWERED"));
1338 }
1339 if ($rule['type'] == 'answered') {
1340 array_push($criteria, array($not."ANSWERED"));
1341 }
1342 if ($rule['type'] == 'smaller') {
1343 array_push($criteria, array($not."SMALLER"));
1344 }
1345 if ($rule['type'] == 'larger') {
1346 array_push($criteria, array($not."LARGER"));
1347 }
1348
1349 // Rules to filter after the search imap
1350 if ($rule['type'] == 'withtrackingidinmsgid') {
1351 $searchfilterdoltrackid++; $searchhead .= '/Message-ID.*@'.preg_quote($host, '/').'/';
1352 }
1353 if ($rule['type'] == 'withouttrackingidinmsgid') {
1354 $searchfilterdoltrackid++; $searchhead .= '/Message-ID.*@'.preg_quote($host, '/').'/';
1355 }
1356 if ($rule['type'] == 'withtrackingid') {
1357 $searchfilterdoltrackid++; $searchhead .= '/References.*@'.preg_quote($host, '/').'/';
1358 }
1359 if ($rule['type'] == 'withouttrackingid') {
1360 $searchfilternodoltrackid++; $searchhead .= '! /References.*@'.preg_quote($host, '/').'/';
1361 }
1362
1363 if ($rule['type'] == 'isanswer') {
1364 $searchfilterisanswer++; $searchhead .= '/References.*@.*/';
1365 }
1366 if ($rule['type'] == 'isnotanswer') {
1367 $searchfilterisnotanswer++; $searchhead .= '! /References.*@.*/';
1368 }
1369
1370 if ($rule['type'] == 'replyto') {
1371 $searchfilterreplyto++; $searchhead .= '/Reply-To.*'.preg_quote($rule['rulevalue'], '/').'/';
1372 }
1373 }
1374
1375 if (empty($targetdir)) { // Use last date as filter if there is no targetdir defined.
1376 $fromdate = 0;
1377 if ($this->datelastok) {
1378 $fromdate = $this->datelastok;
1379 }
1380 if ($fromdate > 0) {
1381 // $search .= ($search ? ' ' : '').'SINCE '.date('j-M-Y', $fromdate - 1); // SENTSINCE not supported. Date must be X-Abc-9999 (X on 1 digit if < 10)
1382 array_push($criteria, array("SINCE" => date('j-M-Y', $fromdate - 1)));
1383 }
1384 //$search.=($search?' ':'').'SINCE 8-Apr-2022';
1385 }
1386
1387 dol_syslog("IMAP search string = ".var_export($criteria, true));
1388 $search = var_export($criteria, true);
1389 } else {
1390 // Use native IMAP functions
1391 $search = 'UNDELETED'; // Seems not supported by some servers
1392 foreach ($this->filters as $rule) {
1393 if (empty($rule['status'])) {
1394 continue;
1395 }
1396
1397 // Forge the IMAP search string.
1398 // See https://www.rfc-editor.org/rfc/rfc3501
1399
1400 $not = '';
1401 if (strpos($rule['rulevalue'], '!') === 0) {
1402 // The value start with !, so we exclude the criteria
1403 $not = 'NOT ';
1404 // Then remove the ! from the string for next filters
1405 $rule['rulevalue'] = substr($rule['rulevalue'], 1);
1406 }
1407
1408 if ($rule['type'] == 'from') {
1409 $tmprulevaluearray = explode('*', $rule['rulevalue']); // Search on abc*def means searching on 'abc' and on 'def'
1410 if (count($tmprulevaluearray) >= 2) {
1411 foreach ($tmprulevaluearray as $tmprulevalue) {
1412 $search .= ($search ? ' ' : '').$not.'FROM "'.str_replace('"', '', $tmprulevalue).'"';
1413 }
1414 } else {
1415 $search .= ($search ? ' ' : '').$not.'FROM "'.str_replace('"', '', $rule['rulevalue']).'"';
1416 }
1417 }
1418 if ($rule['type'] == 'to') {
1419 $tmprulevaluearray = explode('*', $rule['rulevalue']); // Search on abc*def means searching on 'abc' and on 'def'
1420 if (count($tmprulevaluearray) >= 2) {
1421 foreach ($tmprulevaluearray as $tmprulevalue) {
1422 $search .= ($search ? ' ' : '').$not.'TO "'.str_replace('"', '', $tmprulevalue).'"';
1423 }
1424 } else {
1425 $search .= ($search ? ' ' : '').$not.'TO "'.str_replace('"', '', $rule['rulevalue']).'"';
1426 }
1427 }
1428 if ($rule['type'] == 'bcc') {
1429 $search .= ($search ? ' ' : '').$not.'BCC';
1430 }
1431 if ($rule['type'] == 'cc') {
1432 $search .= ($search ? ' ' : '').$not.'CC';
1433 }
1434 if ($rule['type'] == 'subject') {
1435 if (strpos($rule['rulevalue'], '!') === 0) {
1436 //$search .= ($search ? ' ' : '').'NOT BODY "'.str_replace('"', '', $rule['rulevalue']).'"';
1437 $searchfilterexcludesubjectarray[] = preg_replace('/^!/', '', $rule['rulevalue']);
1438 } else {
1439 $search .= ($search ? ' ' : '').'SUBJECT "'.str_replace('"', '', $rule['rulevalue']).'"';
1440 }
1441 }
1442 if ($rule['type'] == 'body') {
1443 if (strpos($rule['rulevalue'], '!') === 0) {
1444 //$search .= ($search ? ' ' : '').'NOT BODY "'.str_replace('"', '', $rule['rulevalue']).'"';
1445 $searchfilterexcludebodyarray[] = preg_replace('/^!/', '', $rule['rulevalue']);
1446 } else {
1447 // Warning: Google doesn't implement IMAP properly, and only matches whole words,
1448 $search .= ($search ? ' ' : '').'BODY "'.str_replace('"', '', $rule['rulevalue']).'"';
1449 }
1450 }
1451 if ($rule['type'] == 'header') {
1452 $search .= ($search ? ' ' : '').$not.'HEADER '.$rule['rulevalue'];
1453 }
1454
1455 /* seems not used */
1456 /*
1457 if ($rule['type'] == 'notinsubject') {
1458 $search .= ($search ? ' ' : '').'NOT SUBJECT "'.str_replace('"', '', $rule['rulevalue']).'"';
1459 }
1460 if ($rule['type'] == 'notinbody') {
1461 $search .= ($search ? ' ' : '').'NOT BODY "'.str_replace('"', '', $rule['rulevalue']).'"';
1462 }*/
1463
1464 if ($rule['type'] == 'seen') {
1465 $search .= ($search ? ' ' : '').$not.'SEEN';
1466 }
1467 if ($rule['type'] == 'unseen') {
1468 $search .= ($search ? ' ' : '').$not.'UNSEEN';
1469 }
1470 if ($rule['type'] == 'unanswered') {
1471 $search .= ($search ? ' ' : '').$not.'UNANSWERED';
1472 }
1473 if ($rule['type'] == 'answered') {
1474 $search .= ($search ? ' ' : '').$not.'ANSWERED';
1475 }
1476 if ($rule['type'] == 'smaller') {
1477 $search .= ($search ? ' ' : '').$not.'SMALLER "'.str_replace('"', '', $rule['rulevalue']).'"';
1478 }
1479 if ($rule['type'] == 'larger') {
1480 $search .= ($search ? ' ' : '').$not.'LARGER "'.str_replace('"', '', $rule['rulevalue']).'"';
1481 }
1482
1483 // Rules to filter after the search imap
1484 if ($rule['type'] == 'withtrackingidinmsgid') {
1485 $searchfilterdoltrackid++; $searchhead .= '/Message-ID.*@'.preg_quote($host, '/').'/';
1486 }
1487 if ($rule['type'] == 'withouttrackingidinmsgid') {
1488 $searchfilterdoltrackid++; $searchhead .= '/Message-ID.*@'.preg_quote($host, '/').'/';
1489 }
1490 if ($rule['type'] == 'withtrackingid') {
1491 $searchfilterdoltrackid++; $searchhead .= '/References.*@'.preg_quote($host, '/').'/';
1492 }
1493 if ($rule['type'] == 'withouttrackingid') {
1494 $searchfilternodoltrackid++; $searchhead .= '! /References.*@'.preg_quote($host, '/').'/';
1495 }
1496
1497 if ($rule['type'] == 'isanswer') {
1498 $searchfilterisanswer++; $searchhead .= '/References.*@.*/';
1499 }
1500 if ($rule['type'] == 'isnotanswer') {
1501 $searchfilterisnotanswer++; $searchhead .= '! /References.*@.*/';
1502 }
1503
1504 if ($rule['type'] == 'replyto') {
1505 $searchfilterreplyto++; $searchhead .= '/Reply-To.*'.preg_quote($rule['rulevalue'], '/').'/';
1506 }
1507 }
1508
1509 if (empty($targetdir)) { // Use last date as filter if there is no targetdir defined.
1510 $fromdate = 0;
1511 if ($this->datelastok) {
1512 $fromdate = $this->datelastok;
1513 }
1514 if ($fromdate > 0) {
1515 $search .= ($search ? ' ' : '').'SINCE '.date('j-M-Y', $fromdate - 1); // SENTSINCE not supported. Date must be X-Abc-9999 (X on 1 digit if < 10)
1516 }
1517 //$search.=($search?' ':'').'SINCE 8-Apr-2018';
1518 }
1519
1520 dol_syslog("IMAP search string = ".$search);
1521 //var_dump($search);
1522 }
1523
1524 $nbemailprocessed = 0;
1525 $nbemailok = 0;
1526 $nbactiondone = 0;
1527 $charset = ($this->hostcharset ? $this->hostcharset : "UTF-8");
1528
1529 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
1530 try {
1531 //$criteria = [['ALL']];
1532 //$Query = $client->getFolders()[0]->messages()->where($criteria);
1533 $f = $client->getFolders(false, $this->source_directory);
1534 $Query = $f[0]->messages()->where($criteria);
1535 } catch (InvalidWhereQueryCriteriaException $e) {
1536 $this->error = $e->getMessage();
1537 $this->errors[] = $this->error;
1538 dol_syslog("EmailCollector::doCollectOneCollector ".$this->error, LOG_ERR);
1539 return -1;
1540 } catch (Exception $e) {
1541 $this->error = $e->getMessage();
1542 $this->errors[] = $this->error;
1543 dol_syslog("EmailCollector::doCollectOneCollector ".$this->error, LOG_ERR);
1544 return -1;
1545 }
1546
1547 try {
1548 //var_dump($Query->count());
1549 if ($mode > 0) {
1550 $Query->leaveUnread();
1551 }
1552 $arrayofemail = $Query->limit($this->maxemailpercollect)->setFetchOrder("asc")->get();
1553 //var_dump($arrayofemail);
1554 } catch (Exception $e) {
1555 $this->error = $e->getMessage();
1556 $this->errors[] = $this->error;
1557 dol_syslog("EmailCollector::doCollectOneCollector ".$this->error, LOG_ERR);
1558 return -1;
1559 }
1560 } else {
1561 // Scan IMAP inbox
1562 $arrayofemail = imap_search($connection, $search, SE_UID, $charset);
1563
1564 if ($arrayofemail === false) {
1565 // Nothing found or search string not understood
1566 $mapoferrrors = imap_errors();
1567 if ($mapoferrrors !== false) {
1568 $error++;
1569 $this->error = "Search string not understood - ".join(',', $mapoferrrors);
1570 $this->errors[] = $this->error;
1571 }
1572 }
1573 }
1574
1575 $arrayofemailtodelete = array(); // Track email to delete to make the deletion at end.
1576
1577 // Loop on each email found
1578 if (!$error && !empty($arrayofemail) && count($arrayofemail) > 0) {
1579 // Loop to get part html and plain
1580 /*
1581 0 multipart/mixed
1582 1 multipart/alternative
1583 1.1 text/plain
1584 1.2 text/html
1585 2 message/rfc822
1586 2 multipart/mixed
1587 2.1 multipart/alternative
1588 2.1.1 text/plain
1589 2.1.2 text/html
1590 2.2 message/rfc822
1591 2.2 multipart/alternative
1592 2.2.1 text/plain
1593 2.2.2 text/html
1594 */
1595 dol_syslog("Start of loop on email", LOG_INFO, 1);
1596
1597 $iforemailloop = 0;
1598 foreach ($arrayofemail as $imapemail) {
1599 if ($nbemailprocessed > 1000) {
1600 break; // Do not process more than 1000 email per launch (this is a different protection than maxnbcollectedpercollect)
1601 }
1602 $iforemailloop++;
1603
1604
1605 // GET header and overview datas
1606 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
1607 $header = $imapemail->getHeader()->raw;
1608 $overview = $imapemail->getAttributes();
1609 } else {
1610 $header = imap_fetchheader($connection, $imapemail, FT_UID);
1611 $overview = imap_fetch_overview($connection, $imapemail, FT_UID);
1612 }
1613
1614 $header = preg_replace('/\r\n\s+/m', ' ', $header); // When a header line is on several lines, merge lines
1615
1616 $matches = array();
1617 preg_match_all('/([^: ]+): (.+?(?:\r\n\s(?:.+?))*)\r\n/m', $header, $matches);
1618 $headers = array_combine($matches[1], $matches[2]);
1619 //var_dump($headers);exit;
1620
1621 if (!empty($headers['in-reply-to']) && empty($headers['In-Reply-To'])) {
1622 $headers['In-Reply-To'] = $headers['in-reply-to'];
1623 }
1624 if (!empty($headers['references']) && empty($headers['References'])) {
1625 $headers['References'] = $headers['references'];
1626 }
1627 if (!empty($headers['message-id']) && empty($headers['Message-ID'])) {
1628 $headers['Message-ID'] = $headers['message-id'];
1629 }
1630 if (!empty($headers['subject']) && empty($headers['Subject'])) {
1631 $headers['Subject'] = $headers['subject'];
1632 }
1633
1634 $headers['Subject'] = $this->decodeSMTPSubject($headers['Subject']);
1635
1636 $emailto = $this->decodeSMTPSubject($overview[0]->to);
1637
1638 $operationslog .= '<br>** Process email #'.dol_escape_htmltag($iforemailloop)." - ".dol_escape_htmltag($this->uidAsString($imapemail))." - References: ".dol_escape_htmltag($headers['References'])." - Subject: ".dol_escape_htmltag($headers['Subject']);
1639 dol_syslog("** Process email ".$iforemailloop." References: ".$headers['References']." Subject: ".$headers['Subject']);
1640
1641
1642 $trackidfoundintorecipienttype = '';
1643 $trackidfoundintorecipientid = 0;
1644 $reg = array();
1645 // See also later list of all supported tags...
1646 if (preg_match('/\+(thi|ctc|use|mem|sub|proj|tas|con|tic|pro|ord|inv|spro|sor|sin|leav|stockinv|job|surv|salary)([0-9]+)@/', $emailto, $reg)) {
1647 $trackidfoundintorecipienttype = $reg[1];
1648 $trackidfoundintorecipientid = $reg[2];
1649 } elseif (preg_match('/\+emailing-(\w+)@/', $emailto, $reg)) { // Can be 'emailing-test' or 'emailing-IdMailing-IdRecipient'
1650 $trackidfoundintorecipienttype = 'emailing';
1651 $trackidfoundintorecipientid = $reg[1];
1652 }
1653
1654 // If there is a filter on trackid
1655 if ($searchfilterdoltrackid > 0) {
1656 if (empty($trackidfoundintorecipienttype)) {
1657 if (empty($headers['References']) || !preg_match('/@'.preg_quote($host, '/').'/', $headers['References'])) {
1658 $nbemailprocessed++;
1659 dol_syslog(" Discarded - No suffix in email recipient and no Header References found matching signature of application so with a trackid");
1660 continue; // Exclude email
1661 }
1662 }
1663 }
1664 if ($searchfilternodoltrackid > 0) {
1665 if (!empty($trackidfoundintorecipienttype) || (!empty($headers['References']) && preg_match('/@'.preg_quote($host, '/').'/', $headers['References']))) {
1666 $nbemailprocessed++;
1667 dol_syslog(" Discarded - Suffix found into email or Header References found and matching signature of application so with a trackid");
1668 continue; // Exclude email
1669 }
1670 }
1671
1672 if ($searchfilterisanswer > 0) {
1673 if (empty($headers['In-Reply-To'])) {
1674 $nbemailprocessed++;
1675 dol_syslog(" Discarded - Email is not an answer (no In-Reply-To header)");
1676 continue; // Exclude email
1677 }
1678 // Note: we can have
1679 // Message-ID=A, In-Reply-To=B, References=B and message can BE an answer or NOT (a transfer rewriten)
1680 $isanswer = 0;
1681 if (preg_match('/Re\s*:\s+/i', $headers['Subject'])) {
1682 $isanswer = 1;
1683 }
1684 //if ($headers['In-Reply-To'] != $headers['Message-ID'] && empty($headers['References'])) $isanswer = 1; // If in-reply-to differs of message-id, this is a reply
1685 //if ($headers['In-Reply-To'] != $headers['Message-ID'] && !empty($headers['References']) && strpos($headers['References'], $headers['Message-ID']) !== false) $isanswer = 1;
1686
1687 if (!$isanswer) {
1688 $nbemailprocessed++;
1689 dol_syslog(" Discarded - Email is not an answer (no RE prefix in subject)");
1690 continue; // Exclude email
1691 }
1692 }
1693 if ($searchfilterisnotanswer > 0) {
1694 if (!empty($headers['In-Reply-To'])) {
1695 // Note: we can have
1696 // Message-ID=A, In-Reply-To=B, References=B and message can BE an answer or NOT (a transfer rewriten)
1697 $isanswer = 0;
1698 if (preg_match('/Re\s*:\s+/i', $headers['Subject'])) {
1699 $isanswer = 1;
1700 }
1701 //if ($headers['In-Reply-To'] != $headers['Message-ID'] && empty($headers['References'])) $isanswer = 1; // If in-reply-to differs of message-id, this is a reply
1702 //if ($headers['In-Reply-To'] != $headers['Message-ID'] && !empty($headers['References']) && strpos($headers['References'], $headers['Message-ID']) !== false) $isanswer = 1;
1703 if ($isanswer) {
1704 $nbemailprocessed++;
1705 dol_syslog(" Discarded - Email is an answer");
1706 continue; // Exclude email
1707 }
1708 }
1709 }
1710
1711 //print "Process mail ".$iforemailloop." Subject: ".dol_escape_htmltag($headers['Subject'])." selected<br>\n";
1712
1713 $thirdpartystatic = new Societe($this->db);
1714 $contactstatic = new Contact($this->db);
1715 $projectstatic = new Project($this->db);
1716
1717 $nbactiondoneforemail = 0;
1718 $errorforemail = 0;
1719 $errorforactions = 0;
1720 $thirdpartyfoundby = '';
1721 $contactfoundby = '';
1722 $projectfoundby = '';
1723 $ticketfoundby = '';
1724 $candidaturefoundby = '';
1725
1726
1727 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
1728 dol_syslog("msgid=".$overview['message_id']." date=".dol_print_date($overview['date'], 'dayrfc', 'gmt')." from=".$overview['from']." to=".$overview['to']." subject=".$overview['subject']);
1729
1730 // Removed emojis
1731 $overview['subject'] = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $overview['subject']);
1732 } else {
1733 dol_syslog("msgid=".$overview[0]->message_id." date=".dol_print_date($overview[0]->udate, 'dayrfc', 'gmt')." from=".$overview[0]->from." to=".$overview[0]->to." subject=".$overview[0]->subject);
1734
1735 $overview[0]->subject = $this->decodeSMTPSubject($overview[0]->subject);
1736
1737 $overview[0]->from = $this->decodeSMTPSubject($overview[0]->from);
1738
1739 // Removed emojis
1740 $overview[0]->subject = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $overview[0]->subject);
1741 }
1742 // GET IMAP email structure/content
1743 global $htmlmsg, $plainmsg, $charset, $attachments;
1744
1745 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
1746 if ($imapemail->hasHTMLBody()) {
1747 $htmlmsg = $imapemail->getHTMLBody();
1748 }
1749 if ($imapemail->hasTextBody()) {
1750 $plainmsg = $imapemail->getTextBody();
1751 }
1752 if ($imapemail->hasAttachments()) {
1753 $attachments = $imapemail->getAttachments()->all();
1754 } else {
1755 $attachments = [];
1756 }
1757 } else {
1758 $getMsg = $this->getmsg($connection, $imapemail); // This set global var $charset, $htmlmsg, $plainmsg, $attachments
1759 if ($getMsg < 0) {
1760 $this->errors = array_merge($this->errors, [$this->error]);
1761 return $getMsg;
1762 }
1763 }
1764 //print $plainmsg;
1765 //var_dump($plainmsg); exit;
1766
1767 //$htmlmsg,$plainmsg,$charset,$attachments
1768 $messagetext = $plainmsg ? $plainmsg : dol_string_nohtmltag($htmlmsg, 0);
1769 // Removed emojis
1770
1771 if (utf8_valid($messagetext)) {
1772 //$messagetext = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $messagetext);
1773 $messagetext = $this->removeEmoji($messagetext);
1774 } else {
1775 $operationslog .= '<br>Discarded - Email body is not valid utf8';
1776 dol_syslog(" Discarded - Email body is not valid utf8");
1777 continue; // Exclude email
1778 }
1779
1780 if (!empty($searchfilterexcludebodyarray)) {
1781 foreach ($searchfilterexcludebodyarray as $searchfilterexcludebody) {
1782 if (preg_match('/'.preg_quote($searchfilterexcludebody, '/').'/ms', $messagetext)) {
1783 $nbemailprocessed++;
1784 $operationslog .= '<br>Discarded - Email body contains string '.$searchfilterexcludebody;
1785 dol_syslog(" Discarded - Email body contains string ".$searchfilterexcludebody);
1786 continue 2; // Exclude email
1787 }
1788 }
1789 }
1790
1791 //var_dump($plainmsg);
1792 //var_dump($htmlmsg);
1793 //var_dump($messagetext);
1794 //var_dump($charset);
1795 //var_dump($attachments);
1796 //exit;
1797
1798 // Parse IMAP email structure
1799 /*
1800 $structure = imap_fetchstructure($connection, $imapemail, FT_UID);
1801
1802 $partplain = $parthtml = -1;
1803 $encodingplain = $encodinghtml = '';
1804
1805 $result = createPartArray($structure, '');
1806
1807 foreach($result as $part)
1808 {
1809 // $part['part_object']->type seems 0 for content
1810 // $part['part_object']->type seems 5 for attachment
1811 if (empty($part['part_object'])) continue;
1812 if ($part['part_object']->subtype == 'HTML')
1813 {
1814 $parthtml=$part['part_number'];
1815 if ($part['part_object']->encoding == 4)
1816 {
1817 $encodinghtml = 'aaa';
1818 }
1819 }
1820 if ($part['part_object']->subtype == 'PLAIN')
1821 {
1822 $partplain=$part['part_number'];
1823 if ($part['part_object']->encoding == 4)
1824 {
1825 $encodingplain = 'rr';
1826 }
1827 }
1828 }
1829 //var_dump($result);
1830 //var_dump($partplain);
1831 //var_dump($parthtml);
1832
1833 //var_dump($structure);
1834 //var_dump($parthtml);
1835 //var_dump($partplain);
1836
1837 $messagetext = imap_fetchbody($connection, $imapemail, ($parthtml != '-1' ? $parthtml : ($partplain != '-1' ? $partplain : 1)), FT_PEEK|FTP_UID);
1838 */
1839
1840 //var_dump($messagetext);
1841 //var_dump($structure->parts[0]->parts);
1842 //print $header;
1843 //print $messagetext;
1844 //exit;
1845
1846 $fromstring = '';
1847 $replytostring = '';
1848
1849 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
1850 $fromstring = $overview['from'];
1851 //$replytostring = empty($overview['reply-to']) ? '' : $overview['reply-to'];
1852
1853 $sender = $overview['sender'];
1854 $to = $overview['to'];
1855 $sendtocc = empty($overview['cc']) ? '' : $overview['cc'];
1856 $sendtobcc = empty($overview['bcc']) ? '' : $overview['bcc'];
1857 $date = $overview['date'];
1858 $msgid = str_replace(array('<', '>'), '', $overview['message_id']);
1859 $subject = $overview['subject'];
1860 } else {
1861 $fromstring = $overview[0]->from;
1862 //$replytostring = empty($overview[0]->replyto) ? '' : $overview[0]->replyto;
1863
1864 $sender = $overview[0]->sender;
1865 $to = $overview[0]->to;
1866 $sendtocc = $overview[0]->cc;
1867 $sendtobcc = $overview[0]->bcc;
1868 $date = $overview[0]->udate;
1869 $msgid = str_replace(array('<', '>'), '', $overview[0]->message_id);
1870 $subject = $overview[0]->subject;
1871 //var_dump($msgid);exit;
1872 }
1873
1874 if (!empty($searchfilterexcludesubjectarray)) {
1875 foreach ($searchfilterexcludesubjectarray as $searchfilterexcludesubject) {
1876 if (preg_match('/'.preg_quote($searchfilterexcludesubject, '/').'/ms', $subject)) {
1877 $nbemailprocessed++;
1878 $operationslog .= '<br>Discarded - Email subject contains string '.$searchfilterexcludesubject;
1879 dol_syslog(" Discarded - Email subject contains string ".$searchfilterexcludesubject);
1880 continue 2; // Exclude email
1881 }
1882 }
1883 }
1884
1885 $reg = array();
1886 if (preg_match('/^(.*)<(.*)>$/', $fromstring, $reg)) {
1887 $from = $reg[2];
1888 $fromtext = $reg[1];
1889 } else {
1890 $from = $fromstring;
1891 $fromtext = '';
1892 }
1893 if (preg_match('/^(.*)<(.*)>$/', $replytostring, $reg)) {
1894 $replyto = $reg[2];
1895 $replytotext = $reg[1];
1896 } else {
1897 $replyto = $replytostring;
1898 $replytotext = '';
1899 }
1900 $fk_element_id = 0; $fk_element_type = '';
1901
1902 $this->db->begin();
1903
1904 $contactid = 0; $thirdpartyid = 0; $projectid = 0; $ticketid = 0;
1905
1906 // Analyze TrackId in field References. For example:
1907 // References: <1542377954.SMTPs-dolibarr-thi649@8f6014fde11ec6cdec9a822234fc557e>
1908 // References: <1542377954.SMTPs-dolibarr-tic649@8f6014fde11ec6cdec9a822234fc557e>
1909 // References: <1542377954.SMTPs-dolibarr-abc649@8f6014fde11ec6cdec9a822234fc557e>
1910 $trackid = '';
1911 $objectid = 0;
1912 $objectemail = null;
1913
1914 $reg = array();
1915 if (!empty($headers['References'])) {
1916 $arrayofreferences = preg_split('/(,|\s+)/', $headers['References']);
1917 // var_dump($headers['References']);
1918 // var_dump($arrayofreferences);
1919
1920 foreach ($arrayofreferences as $reference) {
1921 //print "Process mail ".$iforemailloop." email_msgid ".$msgid.", date ".dol_print_date($date, 'dayhour').", subject ".$subject.", reference ".dol_escape_htmltag($reference)."<br>\n";
1922 if (!empty($trackidfoundintorecipienttype)) {
1923 $resultsearchtrackid = -1;
1924 $reg[1] = $trackidfoundintorecipienttype;
1925 $reg[2] = $trackidfoundintorecipientid;
1926 } else {
1927 $resultsearchtrackid = preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote($host, '/').'/', $reference, $reg);
1928 if (empty($resultsearchtrackid) && getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE')) {
1929 $resultsearchtrackid = preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote(getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE'), '/').'/', $reference, $reg);
1930 }
1931 }
1932
1933 if (!empty($resultsearchtrackid)) {
1934 // We found a tracker (in recipient email or into a Reference matching the Dolibarr server)
1935 $trackid = $reg[1].$reg[2];
1936
1937 $objectid = $reg[2];
1938 // See also list into interface_50_modAgenda_ActionsAuto
1939 if ($reg[1] == 'thi') { // Third-party
1940 $objectemail = new Societe($this->db);
1941 }
1942 if ($reg[1] == 'ctc') { // Contact
1943 $objectemail = new Contact($this->db);
1944 }
1945 if ($reg[1] == 'inv') { // Customer Invoice
1946 $objectemail = new Facture($this->db);
1947 }
1948 if ($reg[1] == 'sinv') { // Supplier Invoice
1949 $objectemail = new FactureFournisseur($this->db);
1950 }
1951 if ($reg[1] == 'pro') { // Customer Proposal
1952 $objectemail = new Propal($this->db);
1953 }
1954 if ($reg[1] == 'ord') { // Sale Order
1955 $objectemail = new Commande($this->db);
1956 }
1957 if ($reg[1] == 'shi') { // Shipment
1958 $objectemail = new Expedition($this->db);
1959 }
1960 if ($reg[1] == 'spro') { // Supplier Proposal
1961 $objectemail = new SupplierProposal($this->db);
1962 }
1963 if ($reg[1] == 'sord') { // Supplier Order
1964 $objectemail = new CommandeFournisseur($this->db);
1965 }
1966 if ($reg[1] == 'rec') { // Reception
1967 $objectemail = new Reception($this->db);
1968 }
1969 if ($reg[1] == 'proj') { // Project
1970 $objectemail = new Project($this->db);
1971 }
1972 if ($reg[1] == 'tas') { // Task
1973 $objectemail = new Task($this->db);
1974 }
1975 if ($reg[1] == 'con') { // Contact
1976 $objectemail = new Contact($this->db);
1977 }
1978 if ($reg[1] == 'use') { // User
1979 $objectemail = new User($this->db);
1980 }
1981 if ($reg[1] == 'tic') { // Ticket
1982 $objectemail = new Ticket($this->db);
1983 }
1984 if ($reg[1] == 'recruitmentcandidature') { // Recruiting Candidate
1985 $objectemail = new RecruitmentCandidature($this->db);
1986 }
1987 if ($reg[1] == 'mem') { // Member
1988 $objectemail = new Adherent($this->db);
1989 }
1990 /*if ($reg[1] == 'leav') { // Leave / Holiday
1991 $objectemail = new Holiday($db);
1992 }
1993 if ($reg[1] == 'exp') { // ExpenseReport
1994 $objectemail = new ExpenseReport($db);
1995 }*/
1996 } elseif (preg_match('/<(.*@.*)>/', $reference, $reg)) {
1997 // This is an external reference, we check if we have it in our database
1998 if (!is_object($objectemail)) {
1999 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."ticket where email_msgid = '".$this->db->escape($reg[1])."'";
2000 $resql = $this->db->query($sql);
2001 if ($resql) {
2002 $obj = $this->db->fetch_object($resql);
2003 if ($obj) {
2004 $objectid = $obj->rowid;
2005 $objectemail = new Ticket($this->db);
2006 $ticketfoundby = $langs->transnoentitiesnoconv("EmailMsgID").' ('.$reg[1].')';
2007 }
2008 } else {
2009 $errorforemail++;
2010 }
2011 }
2012
2013 if (!is_object($objectemail)) {
2014 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."projet where email_msgid = '".$this->db->escape($reg[1])."'";
2015 $resql = $this->db->query($sql);
2016 if ($resql) {
2017 $obj = $this->db->fetch_object($resql);
2018 if ($obj) {
2019 $objectid = $obj->rowid;
2020 $objectemail = new Project($this->db);
2021 $projectfoundby = $langs->transnoentitiesnoconv("EmailMsgID").' ('.$reg[1].')';
2022 }
2023 } else {
2024 $errorforemail++;
2025 }
2026 }
2027
2028 if (!is_object($objectemail)) {
2029 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."recruitment_recruitmentcandidature where email_msgid = '".$this->db->escape($reg[1])."'";
2030 $resql = $this->db->query($sql);
2031 if ($resql) {
2032 $obj = $this->db->fetch_object($resql);
2033 if ($obj) {
2034 $objectid = $obj->rowid;
2035 $objectemail = new RecruitmentCandidature($this->db);
2036 $candidaturefoundby = $langs->transnoentitiesnoconv("EmailMsgID").' ('.$reg[1].')';
2037 }
2038 } else {
2039 $errorforemail++;
2040 }
2041 }
2042 }
2043
2044 // Load object linked to email
2045 if (is_object($objectemail)) {
2046 $result = $objectemail->fetch($objectid);
2047 if ($result > 0) {
2048 $fk_element_id = $objectemail->id;
2049 $fk_element_type = $objectemail->element;
2050 // Fix fk_element_type
2051 if ($fk_element_type == 'facture') {
2052 $fk_element_type = 'invoice';
2053 }
2054
2055 if (get_class($objectemail) != 'Societe') {
2056 $thirdpartyid = $objectemail->fk_soc ?? $objectemail->socid;
2057 } else {
2058 $thirdpartyid = $objectemail->id;
2059 }
2060
2061 if (get_class($objectemail) != 'Contact') {
2062 $contactid = $objectemail->fk_socpeople;
2063 } else {
2064 $contactid = $objectemail->id;
2065 }
2066
2067 if (get_class($objectemail) != 'Project') {
2068 $projectid = isset($objectemail->fk_project) ? $objectemail->fk_project : $objectemail->fk_projet;
2069 } else {
2070 $projectid = $objectemail->id;
2071 }
2072 }
2073 }
2074
2075 // Project
2076 if ($projectid > 0) {
2077 $result = $projectstatic->fetch($projectid);
2078 if ($result <= 0) {
2079 $projectstatic->id = 0;
2080 } else {
2081 $projectid = $projectstatic->id;
2082 if ($trackid) {
2083 $projectfoundby = 'trackid ('.$trackid.')';
2084 }
2085 if (empty($contactid)) {
2086 $contactid = $projectstatic->fk_contact;
2087 }
2088 if (empty($thirdpartyid)) {
2089 $thirdpartyid = $projectstatic->fk_soc;
2090 }
2091 }
2092 }
2093 // Contact
2094 if ($contactid > 0) {
2095 $result = $contactstatic->fetch($contactid);
2096 if ($result <= 0) {
2097 $contactstatic->id = 0;
2098 } else {
2099 $contactid = $contactstatic->id;
2100 if ($trackid) {
2101 $contactfoundby = 'trackid ('.$trackid.')';
2102 }
2103 if (empty($thirdpartyid)) {
2104 $thirdpartyid = $contactstatic->fk_soc;
2105 }
2106 }
2107 }
2108 // Thirdparty
2109 if ($thirdpartyid > 0) {
2110 $result = $thirdpartystatic->fetch($thirdpartyid);
2111 if ($result <= 0) {
2112 $thirdpartystatic->id = 0;
2113 } else {
2114 $thirdpartyid = $thirdpartystatic->id;
2115 if ($trackid) {
2116 $thirdpartyfoundby = 'trackid ('.$trackid.')';
2117 }
2118 }
2119 }
2120
2121 if (is_object($objectemail)) {
2122 break; // Exit loop of references. We already found an accurate reference
2123 }
2124 }
2125 }
2126
2127 if (empty($contactid)) { // Try to find contact using email
2128 $result = $contactstatic->fetch(0, null, '', $from);
2129
2130 if ($result > 0) {
2131 dol_syslog("We found a contact with the email ".$from);
2132 $contactid = $contactstatic->id;
2133 $contactfoundby = 'email of contact ('.$from.')';
2134 if (empty($thirdpartyid) && $contactstatic->socid > 0) {
2135 $result = $thirdpartystatic->fetch($contactstatic->socid);
2136 if ($result > 0) {
2137 $thirdpartyid = $thirdpartystatic->id;
2138 $thirdpartyfoundby = 'email of contact ('.$from.')';
2139 }
2140 }
2141 }
2142 }
2143
2144 if (empty($thirdpartyid)) { // Try to find thirdparty using email
2145 $result = $thirdpartystatic->fetch(0, '', '', '', '', '', '', '', '', '', $from);
2146 if ($result > 0) {
2147 dol_syslog("We found a thirdparty with the email ".$from);
2148 $thirdpartyid = $thirdpartystatic->id;
2149 $thirdpartyfoundby = 'email ('.$from.')';
2150 }
2151 }
2152
2153 /*
2154 if ($replyto) {
2155 if (empty($contactid)) { // Try to find contact using email
2156 $result = $contactstatic->fetch(0, null, '', $replyto);
2157
2158 if ($result > 0) {
2159 dol_syslog("We found a contact with the email ".$replyto);
2160 $contactid = $contactstatic->id;
2161 $contactfoundby = 'email of contact ('.$replyto.')';
2162 if (empty($thirdpartyid) && $contactstatic->socid > 0) {
2163 $result = $thirdpartystatic->fetch($contactstatic->socid);
2164 if ($result > 0) {
2165 $thirdpartyid = $thirdpartystatic->id;
2166 $thirdpartyfoundby = 'email of contact ('.$replyto.')';
2167 }
2168 }
2169 }
2170 }
2171
2172 if (empty($thirdpartyid)) { // Try to find thirdparty using email
2173 $result = $thirdpartystatic->fetch(0, '', '', '', '', '', '', '', '', '', $replyto);
2174 if ($result > 0) {
2175 dol_syslog("We found a thirdparty with the email ".$replyto);
2176 $thirdpartyid = $thirdpartystatic->id;
2177 $thirdpartyfoundby = 'email ('.$replyto.')';
2178 }
2179 }
2180 }
2181 */
2182
2183 // Do operations (extract variables and creating data)
2184 if ($mode < 2) { // 0=Mode production, 1=Mode test (read IMAP and try SQL update then rollback), 2=Mode test with no SQL updates
2185 foreach ($this->actions as $operation) {
2186 $errorforthisaction = 0;
2187
2188 if ($errorforactions) {
2189 break;
2190 }
2191 if (empty($operation['status'])) {
2192 continue;
2193 }
2194
2195 $operationslog .= '<br>* Process operation '.$operation['type'];
2196
2197 // Make Operation
2198 dol_syslog("Execute action ".$operation['type']." actionparam=".$operation['actionparam'].' thirdpartystatic->id='.$thirdpartystatic->id.' contactstatic->id='.$contactstatic->id.' projectstatic->id='.$projectstatic->id);
2199 dol_syslog("Execute action fk_element_id=".$fk_element_id." fk_element_type=".$fk_element_type); // If a Dolibarr tracker id is found, we should now the id of object
2200
2201 $actioncode = 'EMAIL_IN';
2202 // If we scan the Sent box, we use the code for out email
2203 if ($this->source_directory == 'Sent') {
2204 $actioncode = 'EMAIL_OUT';
2205 }
2206
2207 $description = $descriptiontitle = $descriptionmeta = $descriptionfull = '';
2208
2209 $descriptiontitle = $langs->trans("RecordCreatedByEmailCollector", $this->ref, $msgid);
2210
2211 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailTopic").' : '.dol_escape_htmltag($subject));
2212 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailFrom").($langs->trans("MailFrom") != 'From' ? ' (From)' : '').' : '.dol_escape_htmltag($fromstring));
2213 if ($sender) {
2214 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("Sender").($langs->trans("Sender") != 'Sender' ? ' (Sender)' : '').' : '.dol_escape_htmltag($sender));
2215 }
2216 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailTo").($langs->trans("MailTo") != 'To' ? ' (To)' : '').' : '.dol_escape_htmltag($to));
2217 if ($sendtocc) {
2218 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailCC").($langs->trans("MailCC") != 'CC' ? ' (CC)' : '').' : '.dol_escape_htmltag($sendtocc));
2219 }
2220
2221 // Search and create thirdparty
2222 if ($operation['type'] == 'loadthirdparty' || $operation['type'] == 'loadandcreatethirdparty') {
2223 if (empty($operation['actionparam'])) {
2224 $errorforactions++;
2225 $this->error = "Action loadthirdparty or loadandcreatethirdparty has empty parameter. Must be a rule like 'name=HEADER:^From:(.*);' or 'name=SET:xxx' or 'name=EXTRACT:(body|subject):regex where 'name' can be replaced with 'id' or 'email' to define how to set or extract data. More properties can also be set, for example client=SET:2;";
2226 $this->errors[] = $this->error;
2227 } else {
2228 $actionparam = $operation['actionparam'];
2229 $idtouseforthirdparty = '';
2230 $nametouseforthirdparty = '';
2231 $emailtouseforthirdparty = '';
2232 $namealiastouseforthirdparty = '';
2233
2234 $operationslog .= '<br>Loop on each property to set into actionparam';
2235
2236 // $actionparam = 'param=SET:aaa' or 'param=EXTRACT:BODY:....'
2237 $arrayvaluetouse = dolExplodeIntoArray($actionparam, '(\n\r|\r|\n|;)', '=');
2238 foreach ($arrayvaluetouse as $propertytooverwrite => $valueforproperty) {
2239 $sourcestring = '';
2240 $sourcefield = '';
2241 $regexstring = '';
2242 $regforregex = array();
2243
2244 if (preg_match('/^EXTRACT:([a-zA-Z0-9_]+):(.*)$/', $valueforproperty, $regforregex)) {
2245 $sourcefield = $regforregex[1];
2246 $regexstring = $regforregex[2];
2247 }
2248
2249 if (!empty($sourcefield) && !empty($regexstring)) {
2250 if (strtolower($sourcefield) == 'body') {
2251 $sourcestring = $messagetext;
2252 } elseif (strtolower($sourcefield) == 'subject') {
2253 $sourcestring = $subject;
2254 } elseif (strtolower($sourcefield) == 'header') {
2255 $sourcestring = $header;
2256 }
2257
2258 if ($sourcestring) {
2259 $regforval = array();
2260 //var_dump($regexstring);var_dump($sourcestring);
2261 if (preg_match('/'.$regexstring.'/ms', $sourcestring, $regforval)) {
2262 //var_dump($regforval[count($regforval)-1]);exit;
2263 // Overwrite param $tmpproperty
2264 if ($propertytooverwrite == 'id') {
2265 $idtouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2266
2267 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found idtouseforthirdparty='.dol_escape_htmltag($idtouseforthirdparty);
2268 } elseif ($propertytooverwrite == 'email') {
2269 $emailtouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2270
2271 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found emailtouseforthirdparty='.dol_escape_htmltag($emailtouseforthirdparty);
2272 } elseif ($propertytooverwrite == 'name') {
2273 $nametouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2274
2275 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found nametouseforthirdparty='.dol_escape_htmltag($nametouseforthirdparty);
2276 } elseif ($propertytooverwrite == 'name_alias') {
2277 $namealiastouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2278
2279 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found namealiastouseforthirdparty='.dol_escape_htmltag($namealiastouseforthirdparty);
2280 } else {
2281 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> We discard this, not a field used to search an existing thirdparty';
2282 }
2283 } else {
2284 // Regex not found
2285 if (in_array($propertytooverwrite, array('id', 'email', 'name', 'name_alias'))) {
2286 $idtouseforthirdparty = null;
2287 $nametouseforthirdparty = null;
2288 $emailtouseforthirdparty = null;
2289 $namealiastouseforthirdparty = null;
2290
2291 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Not found. Property searched is critical so we cancel the search.';
2292 } else {
2293 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Not found';
2294 }
2295 }
2296 //var_dump($object->$tmpproperty);exit;
2297 } else {
2298 // Nothing can be done for this param
2299 $errorforactions++;
2300 $this->error = 'The extract rule to use to load thirdparty for email '.$msgid.' has an unknown source (must be HEADER, SUBJECT or BODY)';
2301 $this->errors[] = $this->error;
2302
2303 $operationslog .= '<br>'.$this->error;
2304 }
2305 } elseif (preg_match('/^(SET|SETIFEMPTY):(.*)$/', $valueforproperty, $reg)) {
2306 //if (preg_match('/^options_/', $tmpproperty)) $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $reg[1];
2307 //else $object->$tmpproperty = $reg[1];
2308 // Example: id=SETIFEMPTY:123
2309 if ($propertytooverwrite == 'id') {
2310 $idtouseforthirdparty = $reg[2];
2311
2312 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property idtouseforthrdparty='.dol_escape_htmltag($idtouseforthirdparty);
2313 } elseif ($propertytooverwrite == 'email') {
2314 $emailtouseforthirdparty = $reg[2];
2315
2316 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property emailtouseforthrdparty='.dol_escape_htmltag($emailtouseforthirdparty);
2317 } elseif ($propertytooverwrite == 'name') {
2318 $nametouseforthirdparty = $reg[2];
2319
2320 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property nametouseforthirdparty='.dol_escape_htmltag($nametouseforthirdparty);
2321 } elseif ($propertytooverwrite == 'name_alias') {
2322 $namealiastouseforthirdparty = $reg[2];
2323
2324 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property namealiastouseforthirdparty='.dol_escape_htmltag($namealiastouseforthirdparty);
2325 }
2326 } else {
2327 $errorforactions++;
2328 $this->error = 'Bad syntax for description of action parameters: '.$actionparam;
2329 $this->errors[] = $this->error;
2330 break;
2331 }
2332 }
2333
2334 if (!$errorforactions && ($idtouseforthirdparty || $emailtouseforthirdparty || $nametouseforthirdparty || $namealiastouseforthirdparty)) {
2335 // We make another search on thirdparty
2336 $operationslog .= '<br>We have this data to search thirdparty: id='.$idtouseforthirdparty.', email='.$emailtouseforthirdparty.', name='.$nametouseforthirdparty.', name_alias='.$namealiastouseforthirdparty;
2337
2338 $tmpobject = new stdClass();
2339 $tmpobject->element == 'generic';
2340 $tmpobject->id = $idtouseforthirdparty;
2341 $tmpobject->name = $nametouseforthirdparty;
2342 $tmpobject->name_alias = $namealiastouseforthirdparty;
2343 $tmpobject->email = $emailtouseforthirdparty;
2344
2345 $this->overwritePropertiesOfObject($tmpobject, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2346
2347 $idtouseforthirdparty = $tmpobject->id;
2348 $nametouseforthirdparty = $tmpobject->name;
2349 $namealiastouseforthirdparty = $tmpobject->name_alias;
2350 $emailtouseforthirdparty = $tmpobject->email;
2351
2352 $operationslog .= '<br>We try to search existing thirdparty with '.$idtouseforthirdparty.' '.$emailtouseforthirdparty.' '.$nametouseforthirdparty.' '.$namealiastouseforthirdparty;
2353
2354 $result = $thirdpartystatic->fetch($idtouseforthirdparty, $nametouseforthirdparty, '', '', '', '', '', '', '', '', $emailtouseforthirdparty, $namealiastouseforthirdparty);
2355 if ($result < 0) {
2356 $errorforactions++;
2357 $this->error = 'Error when getting thirdparty with name '.$nametouseforthirdparty.' (may be 2 record exists with same name ?)';
2358 $this->errors[] = $this->error;
2359 break;
2360 } elseif ($result == 0) {
2361 if ($operation['type'] == 'loadthirdparty') {
2362 dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." name_alias=".$namealiastouseforthirdparty." was not found");
2363
2364 //search into contacts of thirdparty
2365 $resultContact = $contactstatic->fetch('', '', '', $emailtouseforthirdparty);
2366 if ($resultContact > 0) {
2367 $idtouseforthirdparty = $contactstatic->socid;
2368 $result = $thirdpartystatic->fetch($idtouseforthirdparty);
2369 if ($result > 0) {
2370 dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." name_alias=".$namealiastouseforthirdparty." was found thanks to linked contact search");
2371 } else {
2372 $errorforactions++;
2373 $langs->load("errors");
2374 $this->error = $langs->trans('ErrorFailedToLoadThirdParty', $idtouseforthirdparty, $emailtouseforthirdparty, $nametouseforthirdparty, $namealiastouseforthirdparty);
2375 $this->errors[] = $this->error;
2376 }
2377 } else {
2378 $errorforactions++;
2379 $langs->load("errors");
2380 $this->error = $langs->trans('ErrorFailedToLoadThirdParty', $idtouseforthirdparty, $emailtouseforthirdparty, $nametouseforthirdparty, $namealiastouseforthirdparty);
2381 $this->errors[] = $this->error;
2382 }
2383 } elseif ($operation['type'] == 'loadandcreatethirdparty') {
2384 dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." name_alias=".$namealiastouseforthirdparty." was not found. We try to create it.");
2385
2386 // Create thirdparty
2387 $thirdpartystatic = new Societe($db);
2388 $thirdpartystatic->name = $nametouseforthirdparty;
2389 if (!empty($namealiastouseforthirdparty)) {
2390 if ($namealiastouseforthirdparty != $nametouseforthirdparty) {
2391 $thirdpartystatic->name_alias = $namealiastouseforthirdparty;
2392 }
2393 } else {
2394 $thirdpartystatic->name_alias = (empty($replytostring) ? (empty($fromtext) ? '': $fromtext) : $replytostring);
2395 }
2396 $thirdpartystatic->email = (empty($emailtouseforthirdparty) ? (empty($replyto) ? (empty($from) ? '' : $from) : $replyto) : $emailtouseforthirdparty);
2397
2398 // Overwrite values with values extracted from source email
2399 $errorforthisaction = $this->overwritePropertiesOfObject($thirdpartystatic, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2400
2401 if ($thirdpartystatic->client && empty($thirdpartystatic->code_client)) {
2402 $thirdpartystatic->code_client = 'auto';
2403 }
2404 if ($thirdpartystatic->fournisseur && empty($thirdpartystatic->code_fournisseur)) {
2405 $thirdpartystatic->code_fournisseur = 'auto';
2406 }
2407
2408 if ($errorforthisaction) {
2409 $errorforactions++;
2410 } else {
2411 $result = $thirdpartystatic->create($user);
2412 if ($result <= 0) {
2413 $errorforactions++;
2414 $this->error = $thirdpartystatic->error;
2415 $this->errors = $thirdpartystatic->errors;
2416 } else {
2417 $operationslog .= '<br>Thirdparty created -> id = '.dol_escape_htmltag($thirdpartystatic->id);
2418 }
2419 }
2420 }
2421 } else {
2422 dol_syslog("One and only one existing third party has been found");
2423
2424 $operationslog .= '<br>Thirdparty already exists with id = '.dol_escape_htmltag($thirdpartystatic->id);
2425 }
2426 }
2427 }
2428 } elseif ($operation['type'] == 'loadandcreatecontact') { // Search and create contact
2429 if (empty($operation['actionparam'])) {
2430 $errorforactions++;
2431 $this->error = "Action loadandcreatecontact has empty parameter. Must be 'SET:xxx' or 'EXTRACT:(body|subject):regex' to define how to extract data";
2432 $this->errors[] = $this->error;
2433 } else {
2434 $contact_static = new Contact($this->db);
2435 // Overwrite values with values extracted from source email
2436 $errorforthisaction = $this->overwritePropertiesOfObject($contact_static, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2437 if ($errorforthisaction) {
2438 $errorforactions++;
2439 } else {
2440 if (!empty($contact_static->email) && $contact_static->email != $from) $from = $contact_static->email;
2441
2442 $result = $contactstatic->fetch(0, null, '', $from);
2443 if ($result < 0) {
2444 $errorforactions++;
2445 $this->error = 'Error when getting contact with email ' . $from;
2446 $this->errors[] = $this->error;
2447 break;
2448 } elseif ($result == 0) {
2449 dol_syslog("Contact with email " . $from . " was not found. We try to create it.");
2450 $contactstatic = new Contact($this->db);
2451
2452 // Create contact
2453 $contactstatic->email = $from;
2454 $operationslog .= '<br>We set property email='.dol_escape_htmltag($from);
2455
2456 // Overwrite values with values extracted from source email
2457 $errorforthisaction = $this->overwritePropertiesOfObject($contactstatic, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2458
2459 if ($errorforthisaction) {
2460 $errorforactions++;
2461 } else {
2462 // Search country by name or code
2463 if (!empty($contactstatic->country)) {
2464 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
2465 $result = getCountry('', 3, $this->db, '', 1, $contactstatic->country);
2466 if ($result == 'NotDefined') {
2467 $errorforactions++;
2468 $this->error = "Error country not found by this name '" . $contactstatic->country . "'";
2469 } elseif (!($result > 0)) {
2470 $errorforactions++;
2471 $this->error = "Error when search country by this name '" . $contactstatic->country . "'";
2472 $this->errors[] = $this->db->lasterror();
2473 } else {
2474 $contactstatic->country_id = $result;
2475 $operationslog .= '<br>We set property country_id='.dol_escape_htmltag($result);
2476 }
2477 } elseif (!empty($contactstatic->country_code)) {
2478 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
2479 $result = getCountry($contactstatic->country_code, 3, $this->db);
2480 if ($result == 'NotDefined') {
2481 $errorforactions++;
2482 $this->error = "Error country not found by this code '" . $contactstatic->country_code . "'";
2483 } elseif (!($result > 0)) {
2484 $errorforactions++;
2485 $this->error = "Error when search country by this code '" . $contactstatic->country_code . "'";
2486 $this->errors[] = $this->db->lasterror();
2487 } else {
2488 $contactstatic->country_id = $result;
2489 $operationslog .= '<br>We set property country_id='.dol_escape_htmltag($result);
2490 }
2491 }
2492
2493 if (!$errorforactions) {
2494 // Search state by name or code (for country if defined)
2495 if (!empty($contactstatic->state)) {
2496 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
2497 $result = dol_getIdFromCode($this->db, $contactstatic->state, 'c_departements', 'nom', 'rowid');
2498 if (empty($result)) {
2499 $errorforactions++;
2500 $this->error = "Error state not found by this name '" . $contactstatic->state . "'";
2501 } elseif (!($result > 0)) {
2502 $errorforactions++;
2503 $this->error = "Error when search state by this name '" . $contactstatic->state . "'";
2504 $this->errors[] = $this->db->lasterror();
2505 } else {
2506 $contactstatic->state_id = $result;
2507 $operationslog .= '<br>We set property state_id='.dol_escape_htmltag($result);
2508 }
2509 } elseif (!empty($contactstatic->state_code)) {
2510 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
2511 $result = dol_getIdFromCode($this->db, $contactstatic->state_code, 'c_departements', 'code_departement', 'rowid');
2512 if (empty($result)) {
2513 $errorforactions++;
2514 $this->error = "Error state not found by this code '" . $contactstatic->state_code . "'";
2515 } elseif (!($result > 0)) {
2516 $errorforactions++;
2517 $this->error = "Error when search state by this code '" . $contactstatic->state_code . "'";
2518 $this->errors[] = $this->db->lasterror();
2519 } else {
2520 $contactstatic->state_id = $result;
2521 $operationslog .= '<br>We set property state_id='.dol_escape_htmltag($result);
2522 }
2523 }
2524 }
2525
2526 if (!$errorforactions) {
2527 $result = $contactstatic->create($user);
2528 if ($result <= 0) {
2529 $errorforactions++;
2530 $this->error = $contactstatic->error;
2531 $this->errors = $contactstatic->errors;
2532 } else {
2533 $operationslog .= '<br>Contact created -> id = '.dol_escape_htmltag($contactstatic->id);
2534 }
2535 }
2536 }
2537 }
2538 }
2539 }
2540 } elseif ($operation['type'] == 'recordevent') {
2541 // Create event
2542 $actioncomm = new ActionComm($this->db);
2543
2544 $alreadycreated = $actioncomm->fetch(0, '', '', $msgid);
2545 if ($alreadycreated == 0) {
2546 if ($projectstatic->id > 0) {
2547 if ($projectfoundby) {
2548 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Project found from '.$projectfoundby);
2549 }
2550 }
2551 if ($thirdpartystatic->id > 0) {
2552 if ($thirdpartyfoundby) {
2553 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Third party found from '.$thirdpartyfoundby);
2554 }
2555 }
2556 if ($contactstatic->id > 0) {
2557 if ($contactfoundby) {
2558 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Contact/address found from '.$contactfoundby);
2559 }
2560 }
2561
2562 $description = $descriptiontitle;
2563 $description = dol_concatdesc($description, "-----");
2564 $description = dol_concatdesc($description, $descriptionmeta);
2565 $description = dol_concatdesc($description, "-----");
2566 $description = dol_concatdesc($description, $messagetext);
2567
2568 $descriptionfull = $description;
2569 if (empty($conf->global->MAIN_EMAILCOLLECTOR_MAIL_WITHOUT_HEADER)) {
2570 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
2571 $descriptionfull = dol_concatdesc($descriptionfull, $header);
2572 }
2573
2574 // Insert record of emails sent
2575 $actioncomm->type_code = 'AC_OTH_AUTO'; // Type of event ('AC_OTH', 'AC_OTH_AUTO', 'AC_XXX'...)
2576 $actioncomm->code = 'AC_'.$actioncode;
2577 $actioncomm->label = $langs->trans("ActionAC_".$actioncode).' - '.$langs->trans("MailFrom").' '.$from;
2578 $actioncomm->note_private = $descriptionfull;
2579 $actioncomm->fk_project = $projectstatic->id;
2580 $actioncomm->datep = $date; // date of email
2581 $actioncomm->datef = $date; // date of email
2582 $actioncomm->percentage = -1; // Not applicable
2583 $actioncomm->socid = $thirdpartystatic->id;
2584 $actioncomm->contact_id = $contactstatic->id;
2585 $actioncomm->socpeopleassigned = (!empty($contactstatic->id) ? array($contactstatic->id => '') : array());
2586 $actioncomm->authorid = $user->id; // User saving action
2587 $actioncomm->userownerid = $user->id; // Owner of action
2588 // Fields when action is an email (content should be added into note)
2589 $actioncomm->email_msgid = $msgid;
2590 $actioncomm->email_from = $fromstring;
2591 $actioncomm->email_sender = $sender;
2592 $actioncomm->email_to = $to;
2593 $actioncomm->email_tocc = $sendtocc;
2594 $actioncomm->email_tobcc = $sendtobcc;
2595 $actioncomm->email_subject = $subject;
2596 $actioncomm->errors_to = '';
2597
2598 if (!in_array($fk_element_type, array('societe', 'contact', 'project', 'user'))) {
2599 $actioncomm->fk_element = $fk_element_id;
2600 $actioncomm->elementid = $fk_element_id;
2601 $actioncomm->elementtype = $fk_element_type;
2602 if (is_object($objectemail) && $objectemail->module) {
2603 $actioncomm->elementtype .= '@'.$objectemail->module;
2604 }
2605 }
2606
2607 //$actioncomm->extraparams = $extraparams;
2608
2609 // Overwrite values with values extracted from source email
2610 $errorforthisaction = $this->overwritePropertiesOfObject($actioncomm, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2611
2612 //var_dump($fk_element_id);
2613 //var_dump($fk_element_type);
2614 //var_dump($alreadycreated);
2615 //var_dump($operation['type']);
2616 //var_dump($actioncomm);
2617 //exit;
2618
2619 if ($errorforthisaction) {
2620 $errorforactions++;
2621 } else {
2622 $result = $actioncomm->create($user);
2623 if ($result <= 0) {
2624 $errorforactions++;
2625 $this->errors = $actioncomm->errors;
2626 } else {
2627 $operationslog .= '<br>Event created -> id='.dol_escape_htmltag($actioncomm->id);
2628 }
2629 }
2630 }
2631 } elseif ($operation['type'] == 'recordjoinpiece') {
2632 $data = [];
2633 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
2634 foreach ($attachments as $attachment) {
2635 if ($attachment->getName() === 'undefined') {
2636 continue;
2637 }
2638 $data[$attachment->getName()] = $attachment->getContent();
2639 }
2640 } else {
2641 $pj = getAttachments($imapemail, $connection);
2642 foreach ($pj as $key => $val) {
2643 $data[$val['filename']] = getFileData($imapemail, $val['pos'], $val['type'], $connection);
2644 }
2645 }
2646 if (count($data) > 0) {
2647 $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."user WHERE email LIKE '%".$this->db->escape($from)."%'";
2648 $resql = $this->db->query($sql);
2649 if ($this->db->num_rows($resql) == 0) {
2650 $this->errors[] = "User Not allowed to add documents ({$from})";
2651 }
2652 $arrayobject = array(
2653 'propale' => array('table' => 'propal',
2654 'fields' => array('ref'),
2655 'class' => 'comm/propal/class/propal.class.php',
2656 'object' => 'Propal'),
2657 'holiday' => array('table' => 'holiday',
2658 'fields' => array('ref'),
2659 'class' => 'holiday/class/holiday.class.php',
2660 'object' => 'Holiday'),
2661 'expensereport' => array('table' => 'expensereport',
2662 'fields' => array('ref'),
2663 'class' => 'expensereport/class/expensereport.class.php',
2664 'object' => 'ExpenseReport'),
2665 'recruitment/recruitmentjobposition' => array('table' => 'recruitment_recruitmentjobposition',
2666 'fields' => array('ref'),
2667 'class' => 'recruitment/class/recruitmentjobposition.class.php',
2668 'object' => 'RecruitmentJobPosition'),
2669 'recruitment/recruitmentcandidature' => array('table' => 'recruitment_recruitmentcandidature',
2670 'fields' => array('ref'),
2671 'class' => 'recruitment/class/recruitmentcandidature.class.php',
2672 'object' => ' RecruitmentCandidature'),
2673 'societe' => array('table' => 'societe',
2674 'fields' => array('code_client', 'code_fournisseur'),
2675 'class' => 'societe/class/societe.class.php',
2676 'object' => 'Societe'),
2677 'commande' => array('table' => 'commande',
2678 'fields' => array('ref'),
2679 'class' => 'commande/class/commande.class.php',
2680 'object' => 'Commande'),
2681 'expedition' => array('table' => 'expedition',
2682 'fields' => array('ref'),
2683 'class' => 'expedition/class/expedition.class.php',
2684 'object' => 'Expedition'),
2685 'contract' => array('table' => 'contrat',
2686 'fields' => array('ref'),
2687 'class' => 'contrat/class/contrat.class.php',
2688 'object' => 'Contrat'),
2689 'fichinter' => array('table' => 'fichinter',
2690 'fields' => array('ref'),
2691 'class' => 'fichinter/class/fichinter.class.php',
2692 'object' => 'Fichinter'),
2693 'ticket' => array('table' => 'ticket',
2694 'fields' => array('ref'),
2695 'class' => 'ticket/class/ticket.class.php',
2696 'object' => 'Ticket'),
2697 'knowledgemanagement' => array('table' => 'knowledgemanagement_knowledgerecord',
2698 'fields' => array('ref'),
2699 'class' => 'knowledgemanagement/class/knowledgemanagement.class.php',
2700 'object' => 'KnowledgeRecord'),
2701 'supplier_proposal' => array('table' => 'supplier_proposal',
2702 'fields' => array('ref'),
2703 'class' => 'supplier_proposal/class/supplier_proposal.class.php',
2704 'object' => 'SupplierProposal'),
2705 'fournisseur/commande' => array('table' => 'commande_fournisseur',
2706 'fields' => array('ref', 'ref_supplier'),
2707 'class' => 'fourn/class/fournisseur.commande.class.php',
2708 'object' => 'SupplierProposal'),
2709 'facture' => array('table' => 'facture',
2710 'fields' => array('ref'),
2711 'class' => 'compta/facture/class/facture.class.php',
2712 'object' => 'Facture'),
2713 'fournisseur/facture' => array('table' => 'facture_fourn',
2714 'fields' => array('ref', 'ref_client'),
2715 'class' => 'fourn/class/fournisseur.facture.class.php',
2716 'object' => 'FactureFournisseur'),
2717 'produit' => array('table' => 'product',
2718 'fields' => array('ref'),
2719 'class' => 'product/class/product.class.php',
2720 'object' => 'Product'),
2721 'productlot' => array('table' => 'product_lot',
2722 'fields' => array('batch'),
2723 'class' => 'product/stock/class/productlot.class.php',
2724 'object' => 'Productlot'),
2725 'projet' => array('table' => 'projet',
2726 'fields' => array('ref'),
2727 'class' => 'projet/class/projet.class.php',
2728 'object' => 'Project'),
2729 'projet_task' => array('table' => 'projet_task',
2730 'fields' => array('ref'),
2731 'class' => 'projet/class/task.class.php',
2732 'object' => 'Task'),
2733 'ressource' => array('table' => 'resource',
2734 'fields' => array('ref'),
2735 'class' => 'ressource/class/dolressource.class.php',
2736 'object' => 'Dolresource'),
2737 'bom' => array('table' => 'bom_bom',
2738 'fields' => array('ref'),
2739 'class' => 'bom/class/bom.class.php',
2740 'object' => 'BOM'),
2741 'mrp' => array('table' => 'mrp_mo',
2742 'fields' => array('ref'),
2743 'class' => 'mrp/class/mo.class.php',
2744 'object' => 'Mo'),
2745 );
2746
2747 if (!is_object($hookmanager)) {
2748 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
2749 $hookmanager = new HookManager($this->db);
2750 }
2751 $hookmanager->initHooks(array('emailcolector'));
2752 $parameters = array('arrayobject' => $arrayobject);
2753 $reshook = $hookmanager->executeHooks('addmoduletoeamailcollectorjoinpiece', $parameters); // Note that $action and $object may have been modified by some hooks
2754 if ($reshook > 0) {
2755 $arrayobject = $hookmanager->resArray;
2756 }
2757
2758 $resultobj = array();
2759
2760 foreach ($arrayobject as $key => $objectdesc) {
2761 $sql = 'SELECT DISTINCT t.rowid ';
2762 $sql .= ' FROM ' . MAIN_DB_PREFIX . $objectdesc['table'] . ' AS t';
2763 $sql .= ' WHERE ';
2764 foreach ($objectdesc['fields'] as $field) {
2765 $sql .= "('" .$this->db->escape($subject) . "' LIKE CONCAT('%', t." . $field . ", '%') AND t." . $field . "<>'') OR ";
2766 }
2767 $sql = substr($sql, 0, -4);
2768
2769 $ressqlobj = $this->db->query($sql);
2770 if ($ressqlobj) {
2771 while ($obj = $this->db->fetch_object($ressqlobj)) {
2772 $resultobj[$key][] = $obj->rowid;
2773 }
2774 }
2775 }
2776 $dirs = array();
2777 foreach ($resultobj as $mod => $ids) {
2778 $moddesc = $arrayobject[$mod];
2779 $elementpath = $mod;
2780 dol_include_once($moddesc['class']);
2781 $objectmanaged = new $moddesc['object']($this->db);
2782 foreach ($ids as $val) {
2783 $res = $objectmanaged->fetch($val);
2784 if ($res) {
2785 $path = ($objectmanaged->entity > 1 ? "/" . $objectmanaged->entity : '');
2786 $dirs[] = DOL_DATA_ROOT . $path . "/" . $elementpath . '/' . dol_sanitizeFileName($objectmanaged->ref) . '/';
2787 } else {
2788 $this->errors[] = 'object not found';
2789 }
2790 }
2791 }
2792 foreach ($dirs as $target) {
2793 $prefix = $this->actions[$this->id]['actionparam'];
2794 foreach ($data as $filename => $content) {
2795 $resr = saveAttachment($target, $prefix . '_' . $filename, $content);
2796 if ($resr == -1) {
2797 $this->errors[] = 'Doc not saved';
2798 }
2799 }
2800 }
2801
2802 $operationslog .= '<br>Save attachment files on disk';
2803 } else {
2804 $this->errors[] = 'no joined piece';
2805
2806 $operationslog .= '<br>No joinded files';
2807 }
2808 } elseif ($operation['type'] == 'project') {
2809 // Create project / lead
2810 $projecttocreate = new Project($this->db);
2811 $alreadycreated = $projecttocreate->fetch(0, '', '', $msgid);
2812 if ($alreadycreated == 0) {
2813 if ($thirdpartystatic->id > 0) {
2814 $projecttocreate->socid = $thirdpartystatic->id;
2815 if ($thirdpartyfoundby) {
2816 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Third party found from '.$thirdpartyfoundby);
2817 }
2818 }
2819 if ($contactstatic->id > 0) {
2820 $projecttocreate->contact_id = $contactstatic->id;
2821 if ($contactfoundby) {
2822 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Contact/address found from '.$contactfoundby);
2823 }
2824 }
2825
2826 $description = $descriptiontitle;
2827 $description = dol_concatdesc($description, "-----");
2828 $description = dol_concatdesc($description, $descriptionmeta);
2829 $description = dol_concatdesc($description, "-----");
2830 $description = dol_concatdesc($description, $messagetext);
2831
2832 $descriptionfull = $description;
2833 if (empty($conf->global->MAIN_EMAILCOLLECTOR_MAIL_WITHOUT_HEADER)) {
2834 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
2835 $descriptionfull = dol_concatdesc($descriptionfull, $header);
2836 }
2837
2838 $id_opp_status = dol_getIdFromCode($this->db, 'PROSP', 'c_lead_status', 'code', 'rowid');
2839 $percent_opp_status = dol_getIdFromCode($this->db, 'PROSP', 'c_lead_status', 'code', 'percent');
2840
2841 $projecttocreate->title = $subject;
2842 $projecttocreate->date_start = $date; // date of email
2843 $projecttocreate->date_end = '';
2844 $projecttocreate->opp_status = $id_opp_status;
2845 $projecttocreate->opp_percent = $percent_opp_status;
2846 $projecttocreate->description = dol_concatdesc(dolGetFirstLineOfText(dol_string_nohtmltag($description, 2), 10), '...'.$langs->transnoentities("SeePrivateNote").'...');
2847 $projecttocreate->note_private = $descriptionfull;
2848 $projecttocreate->entity = $conf->entity;
2849 $projecttocreate->email_msgid = $msgid;
2850
2851 $savesocid = $projecttocreate->socid;
2852
2853 // Overwrite values with values extracted from source email.
2854 // This may overwrite any $projecttocreate->xxx properties.
2855 $errorforthisaction = $this->overwritePropertiesOfObject($projecttocreate, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2856
2857 // Set project ref if not yet defined
2858 if (empty($projecttocreate->ref)) {
2859 // Get next Ref
2860 $defaultref = '';
2861 $modele = empty($conf->global->PROJECT_ADDON) ? 'mod_project_simple' : $conf->global->PROJECT_ADDON;
2862
2863 // Search template files
2864 $file = ''; $classname = ''; $filefound = 0; $reldir = '';
2865 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2866 foreach ($dirmodels as $reldir) {
2867 $file = dol_buildpath($reldir."core/modules/project/".$modele.'.php', 0);
2868 if (file_exists($file)) {
2869 $filefound = 1;
2870 $classname = $modele;
2871 break;
2872 }
2873 }
2874
2875 if ($filefound) {
2876 if ($savesocid > 0) {
2877 if ($savesocid != $projecttocreate->socid) {
2878 $errorforactions++;
2879 setEventMessages('You loaded a thirdparty (id='.$savesocid.') and you force another thirdparty id (id='.$projecttocreate->socid.') by setting socid in operation with a different value', null, 'errors');
2880 }
2881 } else {
2882 if ($projecttocreate->socid > 0) {
2883 $thirdpartystatic->fetch($projecttocreate->socid);
2884 }
2885 }
2886
2887 $result = dol_include_once($reldir."core/modules/project/".$modele.'.php');
2888 $modModuleToUseForNextValue = new $classname;
2889 $defaultref = $modModuleToUseForNextValue->getNextValue(($thirdpartystatic->id > 0 ? $thirdpartystatic : null), $projecttocreate);
2890 }
2891 $projecttocreate->ref = $defaultref;
2892 }
2893
2894
2895 if ($errorforthisaction) {
2896 $errorforactions++;
2897 } else {
2898 if (empty($projecttocreate->ref) || (is_numeric($projecttocreate->ref) && $projecttocreate->ref <= 0)) {
2899 $errorforactions++;
2900 $this->error = 'Failed to create project: Can\'t get a valid value for the field ref with numbering template = '.$modele.', thirdparty id = '.$thirdpartystatic->id;
2901
2902 $operationslog .= '<br>'.$this->error;
2903 } else {
2904 // Create project
2905 $result = $projecttocreate->create($user);
2906 if ($result <= 0) {
2907 $errorforactions++;
2908 $this->error = 'Failed to create project: '.$langs->trans($projecttocreate->error);
2909 $this->errors = $projecttocreate->errors;
2910
2911 $operationslog .= '<br>'.$this->error;
2912 } else {
2913 if ($attachments) {
2914 $destdir = $conf->project->dir_output.'/'.$projecttocreate->ref;
2915 if (!dol_is_dir($destdir)) {
2916 dol_mkdir($destdir);
2917 }
2918 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
2919 foreach ($attachments as $attachment) {
2920 $attachment->save($destdir.'/');
2921 }
2922 } else {
2923 $getMsg = $this->getmsg($connection, $imapemail, $destdir);
2924 if ($getMsg < 0) {
2925 $this->errors = array_merge($this->errors, [$this->error]);
2926 return $getMsg;
2927 }
2928 }
2929
2930 $operationslog .= '<br>Project created with attachments -> id='.dol_escape_htmltag($projecttocreate->id);
2931 } else {
2932 $operationslog .= '<br>Project created without attachments -> id='.dol_escape_htmltag($projecttocreate->id);
2933 }
2934 }
2935 }
2936 }
2937 } else {
2938 dol_syslog("Project already exists for msgid = ".dol_escape_htmltag($msgid).", so we do not recreate it.");
2939
2940 $operationslog .= '<br>Project already exists for msgid ='.dol_escape_htmltag($msgid);
2941 }
2942 } elseif ($operation['type'] == 'ticket') {
2943 // Create ticket
2944 $tickettocreate = new Ticket($this->db);
2945
2946 $alreadycreated = $tickettocreate->fetch(0, '', '', $msgid);
2947 if ($alreadycreated == 0) {
2948 if ($thirdpartystatic->id > 0) {
2949 $tickettocreate->socid = $thirdpartystatic->id;
2950 $tickettocreate->fk_soc = $thirdpartystatic->id;
2951 if ($thirdpartyfoundby) {
2952 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Third party found from '.$thirdpartyfoundby);
2953 }
2954 }
2955 if ($contactstatic->id > 0) {
2956 $tickettocreate->contact_id = $contactstatic->id;
2957 if ($contactfoundby) {
2958 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Contact/address found from '.$contactfoundby);
2959 }
2960 }
2961
2962 $description = $descriptiontitle;
2963 $description = dol_concatdesc($description, "-----");
2964 $description = dol_concatdesc($description, $descriptionmeta);
2965 $description = dol_concatdesc($description, "-----");
2966 $description = dol_concatdesc($description, $messagetext);
2967
2968 $descriptionfull = $description;
2969 if (empty($conf->global->MAIN_EMAILCOLLECTOR_MAIL_WITHOUT_HEADER)) {
2970 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
2971 $descriptionfull = dol_concatdesc($descriptionfull, $header);
2972 }
2973
2974 $tickettocreate->subject = $subject;
2975 $tickettocreate->message = $description;
2976 $tickettocreate->type_code = (!empty($conf->global->MAIN_EMAILCOLLECTOR_TICKET_TYPE_CODE) ? $conf->global->MAIN_EMAILCOLLECTOR_TICKET_TYPE_CODE : dol_getIdFromCode($this->db, 1, 'c_ticket_type', 'use_default', 'code', 1));
2977 $tickettocreate->category_code = (!empty($conf->global->MAIN_EMAILCOLLECTOR_TICKET_CATEGORY_CODE) ? $conf->global->MAIN_EMAILCOLLECTOR_TICKET_CATEGORY_CODE : dol_getIdFromCode($this->db, 1, 'c_ticket_category', 'use_default', 'code', 1));
2978 $tickettocreate->severity_code = (!empty($conf->global->MAIN_EMAILCOLLECTOR_TICKET_SEVERITY_CODE) ? $conf->global->MAIN_EMAILCOLLECTOR_TICKET_SEVERITY_CODE : dol_getIdFromCode($this->db, 1, 'c_ticket_severity', 'use_default', 'code', 1));
2979 $tickettocreate->origin_email = $from;
2980 $tickettocreate->fk_user_create = $user->id;
2981 $tickettocreate->datec = dol_now();
2982 $tickettocreate->fk_project = $projectstatic->id;
2983 $tickettocreate->notify_tiers_at_create = 0;
2984 $tickettocreate->note_private = $descriptionfull;
2985 $tickettocreate->entity = $conf->entity;
2986 $tickettocreate->email_msgid = $msgid;
2987 $tickettocreate->email_date = $date;
2988 //$tickettocreate->fk_contact = $contactstatic->id;
2989
2990 $savesocid = $tickettocreate->socid;
2991
2992 // Overwrite values with values extracted from source email.
2993 // This may overwrite any $projecttocreate->xxx properties.
2994 $errorforthisaction = $this->overwritePropertiesOfObject($tickettocreate, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2995
2996 // Set ticket ref if not yet defined
2997 if (empty($tickettocreate->ref)) {
2998 // Get next Ref
2999 $defaultref = '';
3000 $modele = empty($conf->global->TICKET_ADDON) ? 'mod_ticket_simple' : $conf->global->TICKET_ADDON;
3001
3002 // Search template files
3003 $file = ''; $classname = ''; $filefound = 0; $reldir = '';
3004 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3005 foreach ($dirmodels as $reldir) {
3006 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
3007 if (file_exists($file)) {
3008 $filefound = 1;
3009 $classname = $modele;
3010 break;
3011 }
3012 }
3013
3014 if ($filefound) {
3015 if ($savesocid > 0) {
3016 if ($savesocid != $tickettocreate->socid) {
3017 $errorforactions++;
3018 setEventMessages('You loaded a thirdparty (id='.$savesocid.') and you force another thirdparty id (id='.$tickettocreate->socid.') by setting socid in operation with a different value', null, 'errors');
3019 }
3020 } else {
3021 if ($tickettocreate->socid > 0) {
3022 $thirdpartystatic->fetch($tickettocreate->socid);
3023 }
3024 }
3025
3026 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
3027 $modModuleToUseForNextValue = new $classname;
3028 $defaultref = $modModuleToUseForNextValue->getNextValue(($thirdpartystatic->id > 0 ? $thirdpartystatic : null), $tickettocreate);
3029 }
3030 $tickettocreate->ref = $defaultref;
3031 }
3032
3033 if ($errorforthisaction) {
3034 $errorforactions++;
3035 } else {
3036 if (is_numeric($tickettocreate->ref) && $tickettocreate->ref <= 0) {
3037 $errorforactions++;
3038 $this->error = 'Failed to create ticket: Can\'t get a valid value for the field ref with numbering template = '.$modele.', thirdparty id = '.$thirdpartystatic->id;
3039 } else {
3040 // Create project
3041 $result = $tickettocreate->create($user);
3042 if ($result <= 0) {
3043 $errorforactions++;
3044 $this->error = 'Failed to create ticket: '.$langs->trans($tickettocreate->error);
3045 $this->errors = $tickettocreate->errors;
3046 } else {
3047 if ($attachments) {
3048 $destdir = $conf->ticket->dir_output.'/'.$tickettocreate->ref;
3049 if (!dol_is_dir($destdir)) {
3050 dol_mkdir($destdir);
3051 }
3052 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
3053 foreach ($attachments as $attachment) {
3054 $attachment->save($destdir.'/');
3055 }
3056 } else {
3057 $getMsg = $this->getmsg($connection, $imapemail, $destdir);
3058 if ($getMsg < 0) {
3059 $this->errors = array_merge($this->errors, [$this->error]);
3060 return $getMsg;
3061 }
3062 }
3063
3064 $operationslog .= '<br>Ticket created with attachments -> id='.dol_escape_htmltag($tickettocreate->id);
3065 } else {
3066 $operationslog .= '<br>Ticket created without attachments -> id='.dol_escape_htmltag($tickettocreate->id);
3067 }
3068 }
3069 }
3070 }
3071 }
3072 } elseif ($operation['type'] == 'candidature') {
3073 // Create candidature
3074 $candidaturetocreate = new RecruitmentCandidature($this->db);
3075
3076 $alreadycreated = $candidaturetocreate->fetch(0, '', $msgid);
3077 if ($alreadycreated == 0) {
3078 $description = $descriptiontitle;
3079 $description = dol_concatdesc($description, "-----");
3080 $description = dol_concatdesc($description, $descriptionmeta);
3081 $description = dol_concatdesc($description, "-----");
3082 $description = dol_concatdesc($description, $messagetext);
3083
3084 $descriptionfull = $description;
3085 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
3086 $descriptionfull = dol_concatdesc($descriptionfull, $header);
3087
3088 $candidaturetocreate->subject = $subject;
3089 $candidaturetocreate->message = $description;
3090 $candidaturetocreate->type_code = 0;
3091 $candidaturetocreate->category_code = null;
3092 $candidaturetocreate->severity_code = null;
3093 $candidaturetocreate->email = $from;
3094 //$candidaturetocreate->lastname = $langs->trans("Anonymous").' - '.$from;
3095 $candidaturetocreate->fk_user_creat = $user->id;
3096 $candidaturetocreate->date_creation = dol_now();
3097 $candidaturetocreate->fk_project = $projectstatic->id;
3098 $candidaturetocreate->description = $description;
3099 $candidaturetocreate->note_private = $descriptionfull;
3100 $candidaturetocreate->entity = $conf->entity;
3101 $candidaturetocreate->email_msgid = $msgid;
3102 $candidaturetocreate->email_date = $date; // date of email
3103 $candidaturetocreate->status = $candidaturetocreate::STATUS_DRAFT;
3104 //$candidaturetocreate->fk_contact = $contactstatic->id;
3105
3106 // Overwrite values with values extracted from source email.
3107 // This may overwrite any $projecttocreate->xxx properties.
3108 $errorforthisaction = $this->overwritePropertiesOfObject($candidaturetocreate, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
3109
3110 // Set candidature ref if not yet defined
3111 /*if (empty($candidaturetocreate->ref)) We do not need this because we create object in draft status
3112 {
3113 // Get next Ref
3114 $defaultref = '';
3115 $modele = empty($conf->global->CANDIDATURE_ADDON) ? 'mod_candidature_simple' : $conf->global->CANDIDATURE_ADDON;
3116
3117 // Search template files
3118 $file = ''; $classname = ''; $filefound = 0; $reldir = '';
3119 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3120 foreach ($dirmodels as $reldir)
3121 {
3122 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
3123 if (file_exists($file)) {
3124 $filefound = 1;
3125 $classname = $modele;
3126 break;
3127 }
3128 }
3129
3130 if ($filefound) {
3131 if ($savesocid > 0) {
3132 if ($savesocid != $candidaturetocreate->socid) {
3133 $errorforactions++;
3134 setEventMessages('You loaded a thirdparty (id='.$savesocid.') and you force another thirdparty id (id='.$candidaturetocreate->socid.') by setting socid in operation with a different value', null, 'errors');
3135 }
3136 } else {
3137 if ($candidaturetocreate->socid > 0)
3138 {
3139 $thirdpartystatic->fetch($candidaturetocreate->socid);
3140 }
3141 }
3142
3143 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
3144 $modModuleToUseForNextValue = new $classname;
3145 $defaultref = $modModuleToUseForNextValue->getNextValue(($thirdpartystatic->id > 0 ? $thirdpartystatic : null), $tickettocreate);
3146 }
3147 $candidaturetocreate->ref = $defaultref;
3148 }*/
3149
3150 if ($errorforthisaction) {
3151 $errorforactions++;
3152 } else {
3153 // Create project
3154 $result = $candidaturetocreate->create($user);
3155 if ($result <= 0) {
3156 $errorforactions++;
3157 $this->error = 'Failed to create candidature: '.join(', ', $candidaturetocreate->errors);
3158 $this->errors = $candidaturetocreate->errors;
3159 }
3160
3161 $operationslog .= '<br>Candidature created without attachments -> id='.dol_escape_htmltag($candidaturetocreate->id);
3162 }
3163 }
3164 } elseif (substr($operation['type'], 0, 4) == 'hook') {
3165 // Create event specific on hook
3166 // this code action is hook..... for support this call
3167 if (!is_object($hookmanager)) {
3168 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
3169 $hookmanager = new HookManager($this->db);
3170 }
3171
3172 $parameters = array(
3173 'connection'=> $connection,
3174 'imapemail'=>$imapemail,
3175 'overview'=>$overview,
3176
3177 'from' => $from,
3178 'fromtext' => $fromtext,
3179
3180 'actionparam'=> $operation['actionparam'],
3181
3182 'thirdpartyid' => $thirdpartyid,
3183 'objectid'=> $objectid,
3184 'objectemail'=> $objectemail,
3185
3186 'messagetext'=>$messagetext,
3187 'subject'=>$subject,
3188 'header'=>$header,
3189 'attachments'=>$attachments,
3190 );
3191 $reshook = $hookmanager->executeHooks('doCollectImapOneCollector', $parameters, $this, $operation['type']);
3192
3193 if ($reshook < 0) {
3194 $errorforthisaction++;
3195 $this->error = $hookmanager->resPrint;
3196 }
3197 if ($errorforthisaction) {
3198 $errorforactions++;
3199 $operationslog .= '<br>Hook doCollectImapOneCollector executed with error';
3200 } else {
3201 $operationslog .= '<br>Hook doCollectImapOneCollector executed without error';
3202 }
3203 }
3204
3205 if (!$errorforactions) {
3206 $nbactiondoneforemail++;
3207 }
3208 }
3209 }
3210
3211 // Error for email or not ?
3212 if (!$errorforactions) {
3213 if (!empty($targetdir)) {
3214 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
3215 // Move mail using PHP-IMAP
3216 dol_syslog("EmailCollector::doCollectOneCollector move message ".($imapemail->getHeader()->get('subject'))." to ".$targetdir, LOG_DEBUG);
3217 if (empty($mode)) {
3218 $imapemail->move($targetdir);
3219 }
3220 } else {
3221 dol_syslog("EmailCollector::doCollectOneCollector move message ".($this->uidAsString($imapemail))." to ".$connectstringtarget, LOG_DEBUG);
3222 $operationslog .= '<br>Move mail '.($this->uidAsString($imapemail)).' - '.$msgid;
3223
3224 $arrayofemailtodelete[$imapemail] = $msgid;
3225 }
3226 } else {
3227 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
3228 dol_syslog("EmailCollector::doCollectOneCollector message '".($imapemail->getHeader()->get('subject'))."' using this->host=".$this->host.", this->access_type=".$this->acces_type." was set to read", LOG_DEBUG);
3229 } else {
3230 dol_syslog("EmailCollector::doCollectOneCollector message ".($this->uidAsString($imapemail))." to ".$connectstringtarget." was set to read", LOG_DEBUG);
3231 }
3232 }
3233 } else {
3234 $errorforemail++;
3235 }
3236
3237
3238 unset($objectemail);
3239 unset($projectstatic);
3240 unset($thirdpartystatic);
3241 unset($contactstatic);
3242
3243 $nbemailprocessed++;
3244
3245 if (!$errorforemail) {
3246 $nbactiondone += $nbactiondoneforemail;
3247 $nbemailok++;
3248
3249 if (empty($mode)) {
3250 $this->db->commit();
3251 } else {
3252 $this->db->rollback();
3253 }
3254
3255 // Stop the loop to process email if we reach maximum collected per collect
3256 if ($this->maxemailpercollect > 0 && $nbemailok >= $this->maxemailpercollect) {
3257 dol_syslog("EmailCollect::doCollectOneCollector We reach maximum of ".$nbemailok." collected with success, so we stop this collector now.");
3258 break;
3259 }
3260 } else {
3261 $error++;
3262
3263 $this->db->rollback();
3264 }
3265 }
3266
3267 $output = $langs->trans('XEmailsDoneYActionsDone', $nbemailprocessed, $nbemailok, $nbactiondone);
3268
3269 dol_syslog("End of loop on emails", LOG_INFO, -1);
3270 } else {
3271 $langs->load("admin");
3272 $output = $langs->trans('NoNewEmailToProcess');
3273 }
3274
3275 // Disconnect
3276 if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
3277 $client->disconnect();
3278 } else {
3279 foreach ($arrayofemailtodelete as $imapemail => $msgid) {
3280 dol_syslog("EmailCollect::doCollectOneCollector delete email ".$imapemail." ".$msgid);
3281
3282 $operationslog .= "<br> delete email ".$imapemail." ".$msgid;
3283
3284 if (empty($mode) && empty($error)) {
3285 $res = imap_mail_move($connection, $imapemail, $targetdir, CP_UID);
3286 if ($res == false) {
3287 $errorforemail++;
3288 $this->error = imap_last_error();
3289 $this->errors[] = $this->error;
3290
3291 $operationslog .= '<br>Error in move '.$this->error;
3292
3293 dol_syslog(imap_last_error());
3294 }
3295 }
3296 }
3297
3298 if (empty($mode) && empty($error)) {
3299 dol_syslog("Expunge", LOG_DEBUG);
3300 $operationslog .= "<br>Expunge";
3301
3302 imap_expunge($connection); // To validate all moves
3303 }
3304 imap_close($connection);
3305 }
3306
3307 $this->datelastresult = $now;
3308 $this->lastresult = $output;
3309 $this->debuginfo .= 'IMAP search string used : '.$search;
3310 if ($searchhead) {
3311 $this->debuginfo .= '<br>Then search string into email header : '.dol_escape_htmltag($searchhead);
3312 }
3313 if ($operationslog) {
3314 $this->debuginfo .= $operationslog;
3315 }
3316
3317 if (empty($error) && empty($mode)) {
3318 $this->datelastok = $now;
3319 }
3320
3321 if (!empty($this->errors)) {
3322 $this->lastresult .= "<br>".join("<br>", $this->errors);
3323 }
3324 $this->codelastresult = ($error ? 'KO' : 'OK');
3325
3326 if (empty($mode)) {
3327 $this->update($user);
3328 }
3329
3330 dol_syslog("EmailCollector::doCollectOneCollector end", LOG_INFO);
3331
3332 return $error ? -1 : 1;
3333 }
3334
3335
3336
3337 // Loop to get part html and plain. Code found on PHP imap_fetchstructure documentation
3338
3347 private function getmsg($mbox, $mid, $destdir = ''): int
3348 {
3349 // input $mbox = IMAP stream, $mid = message id
3350 // output all the following:
3351 global $charset, $htmlmsg, $plainmsg, $attachments;
3352 $htmlmsg = $plainmsg = $charset = '';
3353 $attachments = array();
3354
3355 // HEADER
3356 //$h = imap_header($mbox,$mid);
3357 // add code here to get date, from, to, cc, subject...
3358
3359 // BODY
3360 $s = imap_fetchstructure($mbox, $mid, FT_UID);
3361 if ($s === false) {
3362 $this->errors = array_merge($this->errors, [imap_last_error()]);
3363 return -1;
3364 }
3365
3366 if (empty($s->parts)) {
3367 // simple
3368 $this->getpart($mbox, $mid, $s, 0); // pass 0 as part-number
3369 } else {
3370 // multipart: cycle through each part
3371 foreach ($s->parts as $partno0 => $p) {
3372 $this->getpart($mbox, $mid, $p, $partno0 + 1, $destdir);
3373 }
3374 }
3375
3376 return 1;
3377 }
3378
3379 /* partno string
3380 0 multipart/mixed
3381 1 multipart/alternative
3382 1.1 text/plain
3383 1.2 text/html
3384 2 message/rfc822
3385 2 multipart/mixed
3386 2.1 multipart/alternative
3387 2.1.1 text/plain
3388 2.1.2 text/html
3389 2.2 message/rfc822
3390 2.2 multipart/alternative
3391 2.2.1 text/plain
3392 2.2.2 text/html
3393 */
3394
3405 private function getpart($mbox, $mid, $p, $partno, $destdir = '')
3406 {
3407 // $partno = '1', '2', '2.1', '2.1.3', etc for multipart, 0 if simple
3408 global $htmlmsg, $plainmsg, $charset, $attachments;
3409
3410 // DECODE DATA
3411 $data = ($partno) ?
3412 imap_fetchbody($mbox, $mid, $partno, FT_UID) : // multipart
3413 imap_body($mbox, $mid, FT_UID); // simple
3414 // Any part may be encoded, even plain text messages, so check everything.
3415 if ($p->encoding == 4) {
3416 $data = quoted_printable_decode($data);
3417 } elseif ($p->encoding == 3) {
3418 $data = base64_decode($data);
3419 }
3420
3421 // PARAMETERS
3422 // get all parameters, like charset, filenames of attachments, etc.
3423 $params = array();
3424 if ($p->parameters) {
3425 foreach ($p->parameters as $x) {
3426 $params[strtolower($x->attribute)] = $x->value;
3427 }
3428 }
3429 if ($p->dparameters) {
3430 foreach ($p->dparameters as $x) {
3431 $params[strtolower($x->attribute)] = $x->value;
3432 }
3433 }
3434
3435 // ATTACHMENT
3436 // Any part with a filename is an attachment,
3437 // so an attached text file (type 0) is not mistaken as the message.
3438 if ($params['filename'] || $params['name']) {
3439 // filename may be given as 'Filename' or 'Name' or both
3440 $filename = ($params['filename']) ? $params['filename'] : $params['name'];
3441 // filename may be encoded, so see imap_mime_header_decode()
3442 $attachments[$filename] = $data; // this is a problem if two files have same name
3443
3444 // Get file name (with extension)
3445 $file_name_complete = $params['filename'];
3446
3447
3448 $destination = $destdir.'/'.$file_name_complete;
3449
3450 // Extract file extension
3451 $extension = pathinfo($file_name_complete, PATHINFO_EXTENSION);
3452
3453 // Extract file name without extension
3454 $file_name = pathinfo($file_name_complete, PATHINFO_FILENAME);
3455
3456 // Save an original file name variable to track while renaming if file already exists
3457 $file_name_original = $file_name;
3458
3459 // Increment file name by 1
3460 $num = 1;
3461
3466 while (file_exists($destdir."/".$file_name.".".$extension)) {
3467 $file_name = $file_name_original . ' (' . $num . ')';
3468 $file_name_complete = $file_name . "." . $extension;
3469 $destination = $destdir.'/'.$file_name_complete;
3470 $num++;
3471 }
3472
3473 $destination = dol_sanitizePathName($destination);
3474
3475 file_put_contents($destination, $data);
3476 }
3477
3478 // TEXT
3479 if ($p->type == 0 && $data) {
3480 if (!empty($params['charset'])) {
3481 $data = $this->convertStringEncoding($data, $params['charset']);
3482 }
3483 // Messages may be split in different parts because of inline attachments,
3484 // so append parts together with blank row.
3485 if (strtolower($p->subtype) == 'plain') {
3486 $plainmsg .= trim($data)."\n\n";
3487 } else {
3488 $htmlmsg .= $data."<br><br>";
3489 }
3490 $charset = $params['charset']; // assume all parts are same charset
3491 } elseif ($p->type == 2 && $data) {
3492 // EMBEDDED MESSAGE
3493 // Many bounce notifications embed the original message as type 2,
3494 // but AOL uses type 1 (multipart), which is not handled here.
3495 // There are no PHP functions to parse embedded messages,
3496 // so this just appends the raw source to the main message.
3497 if (!empty($params['charset'])) {
3498 $data = $this->convertStringEncoding($data, $params['charset']);
3499 }
3500 $plainmsg .= $data."\n\n";
3501 }
3502
3503 // SUBPART RECURSION
3504 if ($p->parts) {
3505 foreach ($p->parts as $partno0 => $p2) {
3506 $this->getpart($mbox, $mid, $p2, $partno.'.'.($partno0 + 1)); // 1.2, 1.2.1, etc.
3507 }
3508 }
3509 }
3510
3520 protected function convertStringEncoding($string, $fromEncoding, $toEncoding = 'UTF-8')
3521 {
3522 if (!$string || $fromEncoding == $toEncoding) {
3523 return $string;
3524 }
3525 $convertedString = function_exists('iconv') ? @iconv($fromEncoding, $toEncoding.'//IGNORE', $string) : null;
3526 if (!$convertedString && extension_loaded('mbstring')) {
3527 $convertedString = @mb_convert_encoding($string, $toEncoding, $fromEncoding);
3528 }
3529 if (!$convertedString) {
3530 throw new Exception('Mime string encoding conversion failed');
3531 }
3532 return $convertedString;
3533 }
3534
3545 protected function decodeSMTPSubject($subject)
3546 {
3547 // Decode $overview[0]->subject according to RFC2047
3548 // Can use also imap_mime_header_decode($str)
3549 // Can use also mb_decode_mimeheader($str)
3550 // Can use also iconv_mime_decode($str, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8')
3551 if (function_exists('imap_mime_header_decode') && function_exists('iconv_mime_decode')) {
3552 $elements = imap_mime_header_decode($subject);
3553 $newstring = '';
3554 if (!empty($elements)) {
3555 $num = count($elements);
3556 for ($i = 0; $i < $num; $i++) {
3557 $stringinutf8 = (in_array(strtoupper($elements[$i]->charset), array('DEFAULT', 'UTF-8')) ? $elements[$i]->text : iconv_mime_decode($elements[$i]->text, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, $elements[$i]->charset));
3558 $newstring .= $stringinutf8;
3559 }
3560 $subject = $newstring;
3561 }
3562 } elseif (!function_exists('mb_decode_mimeheader')) {
3563 $subject = mb_decode_mimeheader($subject);
3564 } elseif (function_exists('iconv_mime_decode')) {
3565 $subject = iconv_mime_decode($subject, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8');
3566 }
3567
3568 return $subject;
3569 }
3570
3577 protected function removeEmoji($text)
3578 {
3579 // Supprimer les caractères emoji en utilisant une expression régulière
3580 $text = preg_replace('/[\x{1F600}-\x{1F64F}]/u', '', $text);
3581 $text = preg_replace('/[\x{1F300}-\x{1F5FF}]/u', '', $text);
3582 $text = preg_replace('/[\x{1F680}-\x{1F6FF}]/u', '', $text);
3583 $text = preg_replace('/[\x{2600}-\x{26FF}]/u', '', $text);
3584 $text = preg_replace('/[\x{2700}-\x{27BF}]/u', '', $text);
3585 $text = preg_replace('/[\x{1F900}-\x{1F9FF}]/u', '', $text);
3586 $text = preg_replace('/[\x{1F1E0}-\x{1F1FF}]/u', '', $text);
3587
3588 return $text;
3589 }
3590
3597 protected function uidAsString($imapemail)
3598 {
3599 if (is_object($imapemail)) {
3600 return $imapemail->getAttributes()["uid"];
3601 } else {
3602 return (string) $imapemail;
3603 }
3604 }
3605}
$object ref
Definition info.php:78
Class to manage agenda events (actions)
Class to manage members of a foundation.
Class to manage predefined suppliers products.
Class to manage customers orders.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetchCommon($id, $ref=null, $morewhere='')
Load object in memory from the database.
createCommon(User $user, $notrigger=false)
Create object into database.
deleteCommon(User $user, $notrigger=false, $forcechilddeletion=0)
Delete object in database.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
updateCommon(User $user, $notrigger=false)
Update object into database.
Class to manage contact/addresses.
Class to manage Dolibarr database access.
Class for EmailCollectorAction.
Class for EmailCollectorFilter.
Class for EmailCollector.
fetch($id, $ref=null)
Load object in memory from the database.
createFromClone(User $user, $fromid)
Clone and object into another one.
create(User $user, $notrigger=false)
Create object into database.
update(User $user, $notrigger=false)
Update object into database.
convertStringEncoding($string, $fromEncoding, $toEncoding='UTF-8')
Converts a string from one encoding to another.
overwritePropertiesOfObject(&$object, $actionparam, $messagetext, $subject, $header, &$operationslog)
overwitePropertiesOfObject
__construct(DoliDB $db)
Constructor.
getpart($mbox, $mid, $p, $partno, $destdir='')
Sub function for getpart().
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
removeEmoji($text)
Remove EMoji from email content.
getEncodedUtf7($str)
Convert str to UTF-7 imap default mailbox names.
getConnectStringIMAP()
Return the connectstring to use with IMAP connection function.
decodeSMTPSubject($subject)
Decode a subject string according to RFC2047 Example: '=?Windows-1252?Q?RE=A0:_ABC?...
fetchFilters()
Fetch filters.
uidAsString($imapemail)
Get UID of message as a string.
LibStatut($status, $mode=0)
Return the status.
fetchActions()
Fetch actions.
fetchAll(User $user, $activeOnly=0, $sortfield='s.rowid', $sortorder='ASC', $limit=100, $page=0)
Load object lines in memory from the database.
info($id)
Charge les informations d'ordre info dans l'objet commande.
getmsg($mbox, $mid, $destdir='')
getmsg
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionaly the picto)
doCollectOneCollector($mode=0)
Execute collect for current collector loaded previously with fetch.
doCollect()
Action executed by scheduler CAN BE A CRON TASK.
getLibStatut($mode=0)
Return label of the status.
Class to manage shipments.
Class to manage suppliers invoices.
Class to manage invoices.
Class to manage hooks.
Class to manage projects.
Class to manage proposals.
Class to manage receptions.
Class for RecruitmentCandidature.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage price ask supplier.
Class to manage tasks.
Class to manage translations.
Class to manage Dolibarr users.
getCountry($searchkey, $withcode='', $dbtouse=0, $outputlangs='', $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
saveAttachment($path, $filename, $data)
Save joined file into a directory with a given name.
getFileData($jk, $fpos, $type, $mbox)
Get content of a joined file from its position into a given email.
getAttachments($jk, $mbox)
Get attachments of a given mail.
dol_is_dir($folder)
Test if filename is a directory.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
dolExplodeIntoArray($string, $delimiter=';', $kv='=')
Split a string with 2 keys into key array.
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
utf8_valid($str)
Check if a string is in UTF8.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null, $include=null)
Return array of possible common substitutions.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_sanitizePathName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a path name.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
Class to generate the form for creating a new ticket.
dolEncrypt($chain, $key='', $ciphering='AES-256-CTR', $forceseed='')
Encode a string with a symetric encryption.
dolDecrypt($chain, $key='')
Decode a string with a symetric encryption.