dolibarr 22.0.5
emailcollector.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
26// Put here all includes required by your class file
27include_once DOL_DOCUMENT_ROOT .'/emailcollector/lib/emailcollector.lib.php';
28
29require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php';
30require_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
31require_once DOL_DOCUMENT_ROOT .'/core/lib/functions2.lib.php';
32
33require_once DOL_DOCUMENT_ROOT .'/comm/propal/class/propal.class.php'; // Customer Proposal
34require_once DOL_DOCUMENT_ROOT .'/commande/class/commande.class.php'; // Sale Order
35require_once DOL_DOCUMENT_ROOT .'/compta/facture/class/facture.class.php'; // Customer Invoice
36require_once DOL_DOCUMENT_ROOT .'/contact/class/contact.class.php'; // Contact / Address
37require_once DOL_DOCUMENT_ROOT .'/expedition/class/expedition.class.php'; // Shipping / Delivery
38require_once DOL_DOCUMENT_ROOT .'/fourn/class/fournisseur.commande.class.php'; // Purchase Order
39require_once DOL_DOCUMENT_ROOT .'/fourn/class/fournisseur.facture.class.php'; // Purchase Invoice
40require_once DOL_DOCUMENT_ROOT .'/projet/class/project.class.php'; // Project
41require_once DOL_DOCUMENT_ROOT .'/reception/class/reception.class.php'; // Reception
42require_once DOL_DOCUMENT_ROOT .'/recruitment/class/recruitmentcandidature.class.php'; // Recruiting
43require_once DOL_DOCUMENT_ROOT .'/societe/class/societe.class.php'; // Third-Party
44require_once DOL_DOCUMENT_ROOT .'/supplier_proposal/class/supplier_proposal.class.php'; // Supplier Proposal
45require_once DOL_DOCUMENT_ROOT .'/ticket/class/ticket.class.php'; // Ticket
46//require_once DOL_DOCUMENT_ROOT .'/expensereport/class/expensereport.class.php'; // Expense Report
47//require_once DOL_DOCUMENT_ROOT .'/holiday/class/holiday.class.php'; // Holidays (leave request)
48
49
50use Webklex\PHPIMAP\ClientManager;
51use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
52use Webklex\PHPIMAP\Exceptions\InvalidWhereQueryCriteriaException;
53use OAuth\Common\Storage\DoliStorage;
54use OAuth\Common\Consumer\Credentials;
55
60{
64 public $element = 'emailcollector';
65
69 public $table_element = 'emailcollector_emailcollector';
70
74 public $picto = 'email';
75
79 public $fk_element = 'fk_emailcollector';
80
84 protected $childtables = array();
85
89 protected $childtablesoncascade = array('emailcollector_emailcollectorfilter', 'emailcollector_emailcollectoraction');
90
91
111 // BEGIN MODULEBUILDER PROPERTIES
115 public $fields = array(
116 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'visible' => -2, 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'index' => 1),
117 'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'default' => '1', 'notnull' => 1, 'index' => 1, 'position' => 20),
118 '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'),
119 'label' => array('type' => 'varchar(255)', 'label' => 'Label', 'visible' => 1, 'enabled' => 1, 'position' => 30, 'notnull' => -1, 'searchall' => 1, 'help' => 'Example: My Email collector', 'csslist' => 'tdoverflowmax150', 'tdcss' => 'titlefieldmiddle'),
120 'description' => array('type' => 'text', 'label' => 'Description', 'visible' => -1, 'enabled' => 1, 'position' => 60, 'notnull' => -1, 'cssview' => 'small', 'csslist' => 'small tdoverflowmax200'),
121 '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'),
122 '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'),
123 'imap_encryption' => array('type' => 'varchar(16)', 'label' => 'ImapEncryption', 'visible' => -1, 'enabled' => 1, 'position' => 92, 'searchall' => 0, 'comment' => "IMAP encryption", 'help' => 'ImapEncryptionHelp', 'arrayofkeyval' => array('ssl' => 'SSL', 'tls' => 'TLS', 'notls' => 'NOTLS'), 'default' => 'ssl'),
124 'hostcharset' => array('type' => 'varchar(16)', 'label' => 'HostCharset', 'visible' => -1, 'enabled' => 1, 'position' => 93, 'notnull' => 0, 'searchall' => 0, 'comment' => "IMAP server charset", 'help' => 'Example: "UTF-8" (May be "US-ASCII" with some Office365)', 'default' => 'UTF-8'),
125 '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'),
126 'acces_type' => array('type' => 'integer', 'label' => 'AuthenticationMethod', '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' => ''),
127 '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'),
128 'password' => array('type' => 'password', 'label' => 'Password', 'visible' => -1, 'enabled' => "1", 'position' => 103, 'notnull' => -1, 'comment' => "IMAP password", 'help' => 'WithGMailYouCanCreateADedicatedPassword'),
129 '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'),
130 'source_directory' => array('type' => 'varchar(255)', 'label' => 'MailboxSourceDirectory', 'visible' => -1, 'enabled' => 1, 'position' => 109, 'notnull' => 1, 'default' => 'Inbox', 'csslist' => 'tdoverflowmax100', 'help' => 'Example: INBOX, [Gmail]/Spam, [Gmail]/Draft, [Gmail]/Brouillons, [Gmail]/Sent Mail, [Gmail]/Messages envoyés, ...'),
131 'target_directory' => array('type' => 'varchar(255)', 'label' => 'MailboxTargetDirectory', 'visible' => 1, 'enabled' => 1, 'position' => 110, 'notnull' => 0, 'csslist' => 'tdoverflowmax100', 'help' => "EmailCollectorTargetDir"),
132 'maxemailpercollect' => array('type' => 'integer', 'label' => 'MaxEmailCollectPerCollect', 'visible' => -1, 'enabled' => 1, 'position' => 111, 'default' => '50'),
133 'datelastresult' => array('type' => 'datetime', 'label' => 'DateLastCollectResult', 'visible' => 1, 'enabled' => '$action != "create" && $action != "edit"', 'position' => 121, 'notnull' => -1, 'csslist' => 'nowraponall'),
134 'codelastresult' => array('type' => 'varchar(16)', 'label' => 'CodeLastResult', 'visible' => 1, 'enabled' => '$action != "create" && $action != "edit"', 'position' => 122, 'notnull' => -1,),
135 'lastresult' => array('type' => 'varchar(255)', 'label' => 'LastResult', 'visible' => 1, 'enabled' => '$action != "create" && $action != "edit"', 'position' => 123, 'notnull' => -1, 'cssview' => 'small', 'csslist' => 'small tdoverflowmax200'),
136 'datelastok' => array('type' => 'datetime', 'label' => 'DateLastcollectResultOk', 'visible' => 1, 'enabled' => '$action != "create"', 'position' => 125, 'notnull' => -1, 'csslist' => 'nowraponall'),
137 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'visible' => 0, 'enabled' => 1, 'position' => 61, 'notnull' => -1,),
138 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'visible' => 0, 'enabled' => 1, 'position' => 62, 'notnull' => -1,),
139 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'visible' => -2, 'enabled' => 1, 'position' => 500, 'notnull' => 1,),
140 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'visible' => -2, 'enabled' => 1, 'position' => 501, 'notnull' => 1,),
141 //'date_validation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>502),
142 'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'visible' => -2, 'enabled' => 1, 'position' => 510, 'notnull' => 1,),
143 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'visible' => -2, 'enabled' => 1, 'position' => 511, 'notnull' => -1,),
144 //'fk_user_valid' =>array('type'=>'integer', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>512),
145 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'visible' => -2, 'enabled' => 1, 'position' => 1000, 'notnull' => -1,),
146 'status' => array('type' => 'integer', 'label' => 'Status', 'visible' => 1, 'enabled' => 1, 'position' => 1000, 'notnull' => 1, 'default' => '0', 'index' => 1, 'arrayofkeyval' => array(0 => 'Inactive', 1 => 'Active'))
147 );
148
149
153 public $rowid;
154
158 public $ref;
159
163 public $entity;
164
168 public $label;
169
173 public $description;
174
178 public $status;
179
183 public $fk_user_creat;
184
188 public $fk_user_modif;
189
193 public $import_key;
194
198 public $host;
202 public $port;
206 public $hostcharset;
210 public $login;
214 public $password;
218 public $acces_type;
222 public $oauth_service;
226 public $imap_encryption;
230 public $norsh;
234 public $source_directory;
238 public $target_directory;
242 public $maxemailpercollect;
243
247 public $datelastresult;
248
252 public $codelastresult;
256 public $lastresult;
260 public $datelastok;
261 // END MODULEBUILDER PROPERTIES
262
266 public $filters;
270 public $actions;
271
275 public $debuginfo;
276
277 const STATUS_DISABLED = 0;
278 const STATUS_ENABLED = 1;
279
280
286 public function __construct(DoliDB $db)
287 {
288 global $conf, $langs;
289
290 $this->db = $db;
291
292 $this->ismultientitymanaged = 1;
293 $this->isextrafieldmanaged = 0;
294
295 if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
296 $this->fields['rowid']['visible'] = 0;
297 }
298 if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
299 $this->fields['entity']['enabled'] = 0;
300 }
301
302 // List of oauth services
303 $oauthservices = array();
304
305 foreach ($conf->global as $key => $val) {
306 if (!empty($val) && preg_match('/^OAUTH_.*_ID$/', $key)) {
307 $key = preg_replace('/^OAUTH_/', '', $key);
308 $key = preg_replace('/_ID$/', '', $key);
309 if (preg_match('/^.*-/', $key)) {
310 $name = preg_replace('/^.*-/', '', $key);
311 } else {
312 $name = $langs->trans("NoName");
313 }
314 $provider = preg_replace('/-.*$/', '', $key);
315 $provider = ucfirst(strtolower($provider));
316
317 $oauthservices[$key] = $name." (".$provider.")";
318 }
319 }
320
321 $this->fields['oauth_service']['arrayofkeyval'] = $oauthservices;
322
323 // Unset fields that are disabled
324 foreach ($this->fields as $key => $val) {
325 if (isset($val['enabled']) && empty($val['enabled'])) {
326 unset($this->fields[$key]);
327 }
328 }
329
330 // Translate some data of arrayofkeyval
331 foreach ($this->fields as $key => $val) {
332 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
333 foreach ($val['arrayofkeyval'] as $key2 => $val2) {
334 $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
335 }
336 }
337 }
338 }
339
347 public function create(User $user, $notrigger = 0)
348 {
349 global $langs;
350
351 // Check parameters
352 if ($this->host && preg_match('/^http:/i', trim($this->host))) {
353 $langs->load("errors");
354 $this->error = $langs->trans("ErrorHostMustNotStartWithHttp", $this->host);
355 return -1;
356 }
357
358 include_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php';
359 $this->password = dolEncrypt($this->password);
360
361 $id = $this->createCommon($user, $notrigger);
362
363 $this->password = dolDecrypt($this->password);
364
365 if (is_array($this->filters) && count($this->filters)) {
366 $emailcollectorfilter = new EmailCollectorFilter($this->db);
367
368 foreach ($this->filters as $filter) {
369 $emailcollectorfilter->type = $filter['type'];
370 $emailcollectorfilter->rulevalue = $filter['rulevalue'];
371 $emailcollectorfilter->fk_emailcollector = $this->id;
372 $emailcollectorfilter->status = $filter['status'];
373
374 $emailcollectorfilter->create($user);
375 }
376 }
377
378 if (is_array($this->actions) && count($this->actions)) {
379 $emailcollectoroperation = new EmailCollectorAction($this->db);
380
381 foreach ($this->actions as $operation) {
382 $emailcollectoroperation->type = $operation['type'];
383 $emailcollectoroperation->actionparam = $operation['actionparam'];
384 $emailcollectoroperation->fk_emailcollector = $this->id;
385 $emailcollectoroperation->status = $operation['status'];
386 $emailcollectoroperation->position = $operation['position'];
387
388 $emailcollectoroperation->create($user);
389 }
390 }
391
392 return $id;
393 }
394
402 public function createFromClone(User $user, $fromid)
403 {
404 global $langs, $extrafields;
405 $error = 0;
406
407 dol_syslog(__METHOD__, LOG_DEBUG);
408
409 $object = new self($this->db);
410
411 $this->db->begin();
412
413 // Load source object
414 $object->fetchCommon($fromid);
415
416 $object->fetchFilters(); // Rules
417 $object->fetchActions(); // Operations
418
419 // Reset some properties
420 unset($object->id);
421 unset($object->fk_user_creat);
422 unset($object->import_key);
423 unset($object->password);
424 unset($object->lastresult);
425 unset($object->codelastresult);
426 unset($object->datelastresult);
427 unset($object->datelastok);
428 unset($object->debuginfo);
429
430 // Clear fields
431 $object->ref = "copy_of_".$object->ref;
432 $object->label = $langs->trans("CopyOf")." ".$object->label;
433 if (empty($object->host)) {
434 $object->host = 'imap.example.com';
435 }
436 // Clear extrafields that are unique
437 if (is_array($object->array_options) && count($object->array_options) > 0) {
438 $extrafields->fetch_name_optionals_label($this->table_element);
439 foreach ($object->array_options as $key => $option) {
440 $shortkey = preg_replace('/options_/', '', $key);
441 if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey])) {
442 //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
443 unset($object->array_options[$key]);
444 }
445 }
446 }
447
448 // Create clone
449 $object->context['createfromclone'] = 'createfromclone';
450 $result = $object->create($user);
451 if ($result < 0) {
452 $error++;
453 $this->error = $object->error;
454 $this->errors = $object->errors;
455 }
456
457 unset($object->context['createfromclone']);
458
459 // End
460 if (!$error) {
461 $this->db->commit();
462 return $object;
463 } else {
464 $this->db->rollback();
465 return -1;
466 }
467 }
468
476 public function fetch($id, $ref = null)
477 {
478 $result = $this->fetchCommon($id, $ref);
479
480 include_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php';
481 $this->password = dolDecrypt($this->password);
482
483 //if ($result > 0 && !empty($this->table_element_line)) $this->fetchLines();
484 return $result;
485 }
486
492 /*
493 public function fetchLines()
494 {
495 $this->lines=array();
496
497 // Load lines with object EmailCollectorLine
498
499 return count($this->lines)?1:0;
500 }
501 */
502
514 public function fetchAll(User $user, $activeOnly = 0, $sortfield = 's.rowid', $sortorder = 'ASC', $limit = 100, $page = 0)
515 {
516 $obj_ret = array();
517
518 $sql = "SELECT s.rowid";
519 $sql .= " FROM ".MAIN_DB_PREFIX."emailcollector_emailcollector as s";
520 $sql .= ' WHERE s.entity IN ('.getEntity('emailcollector').')';
521 if ($activeOnly) {
522 $sql .= " AND s.status = 1";
523 }
524 $sql .= $this->db->order($sortfield, $sortorder);
525 if ($limit) {
526 if ($page < 0) {
527 $page = 0;
528 }
529 $offset = $limit * $page;
530
531 $sql .= $this->db->plimit($limit + 1, $offset);
532 }
533
534 $result = $this->db->query($sql);
535 if ($result) {
536 $num = $this->db->num_rows($result);
537 $i = 0;
538 while ($i < $num) {
539 $obj = $this->db->fetch_object($result);
540 $emailcollector_static = new EmailCollector($this->db);
541 if ($emailcollector_static->fetch($obj->rowid)) {
542 $obj_ret[] = $emailcollector_static;
543 }
544 $i++;
545 }
546 } else {
547 $this->errors[] = 'EmailCollector::fetchAll Error when retrieve emailcollector list';
548 dol_syslog('EmailCollector::fetchAll Error when retrieve emailcollector list', LOG_ERR);
549 }
550 if (!count($obj_ret)) {
551 dol_syslog('EmailCollector::fetchAll No emailcollector found', LOG_DEBUG);
552 }
553
554 return $obj_ret;
555 }
556
564 public function update(User $user, $notrigger = 0)
565 {
566 global $langs;
567
568 // Check parameters
569 if ($this->host && preg_match('/^http:/i', trim($this->host))) {
570 $langs->load("errors");
571 $this->error = $langs->trans("ErrorHostMustNotStartWithHttp", $this->host);
572 return -1;
573 }
574
575 include_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php';
576 $this->password = dolEncrypt($this->password);
577
578 $result = $this->updateCommon($user, $notrigger);
579
580 $this->password = dolDecrypt($this->password);
581
582 return $result;
583 }
584
592 public function delete(User $user, $notrigger = 0)
593 {
594 return $this->deleteCommon($user, $notrigger, 1);
595 }
596
607 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
608 {
609 global $conf, $langs, $action, $hookmanager;
610
611 if (!empty($conf->dol_no_mouse_hover)) {
612 $notooltip = 1; // Force disable tooltips
613 }
614
615 $result = '';
616
617 $label = '<u>'.$langs->trans("EmailCollector").'</u>';
618 $label .= '<br>';
619 $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
620
621 $url = DOL_URL_ROOT.'/admin/emailcollector_card.php?id='.$this->id;
622
623 if ($option != 'nolink') {
624 // Add param to save lastsearch_values or not
625 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
626 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
627 $add_save_lastsearch_values = 1;
628 }
629 if ($add_save_lastsearch_values) {
630 $url .= '&save_lastsearch_values=1';
631 }
632 }
633
634 $linkclose = '';
635 if (empty($notooltip)) {
636 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
637 $label = $langs->trans("ShowEmailCollector");
638 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
639 }
640 $linkclose .= ' title="'.dolPrintHTMLForAttribute($label).'"';
641 $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
642 } else {
643 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
644 }
645
646 $linkstart = '<a href="'.$url.'"';
647 $linkstart .= $linkclose.'>';
648 $linkend = '</a>';
649
650 $result .= $linkstart;
651 if ($withpicto) {
652 $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);
653 }
654 if ($withpicto != 2) {
655 $result .= $this->ref;
656 }
657 $result .= $linkend;
658 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
659
660 $hookmanager->initHooks(array('emailcollectordao'));
661 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
662 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
663 if ($reshook > 0) {
664 $result = $hookmanager->resPrint;
665 } else {
666 $result .= $hookmanager->resPrint;
667 }
668
669 return $result;
670 }
671
678 public function getLibStatut($mode = 0)
679 {
680 return $this->LibStatut($this->status, $mode);
681 }
682
683 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
691 public function LibStatut($status, $mode = 0)
692 {
693 // phpcs:enable
694 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
695 global $langs;
696 //$langs->load("mymodule");
697 $this->labelStatus[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
698 $this->labelStatus[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
699 $this->labelStatusShort[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
700 $this->labelStatusShort[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
701 }
702
703 $statusType = 'status5';
704 if ($status == self::STATUS_ENABLED) {
705 $statusType = 'status4';
706 }
707
708 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
709 }
710
717 public function info($id)
718 {
719 $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
720 $sql .= ' fk_user_creat, fk_user_modif';
721 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
722 $sql .= ' WHERE t.rowid = '.((int) $id);
723 $result = $this->db->query($sql);
724 if ($result) {
725 if ($this->db->num_rows($result)) {
726 $obj = $this->db->fetch_object($result);
727
728 $this->id = $obj->rowid;
729
730 $this->user_creation_id = $obj->fk_user_creat;
731 $this->user_modification_id = $obj->fk_user_modif;
732 $this->date_creation = $this->db->jdate($obj->datec);
733 $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
734 }
735
736 $this->db->free($result);
737 } else {
738 dol_print_error($this->db);
739 }
740 }
741
748 public function initAsSpecimen()
749 {
750 $this->host = 'localhost';
751 $this->login = 'alogin';
752
753 return $this->initAsSpecimenCommon();
754 }
755
762 public function fetchFilters()
763 {
764 $this->filters = array();
765
766 $sql = 'SELECT rowid, type, rulevalue, status';
767 $sql .= ' FROM '.MAIN_DB_PREFIX.'emailcollector_emailcollectorfilter';
768 $sql .= ' WHERE fk_emailcollector = '.((int) $this->id);
769 //$sql.= ' ORDER BY position';
770
771 $resql = $this->db->query($sql);
772 if ($resql) {
773 $num = $this->db->num_rows($resql);
774 $i = 0;
775 while ($i < $num) {
776 $obj = $this->db->fetch_object($resql);
777 $this->filters[$obj->rowid] = array('id' => $obj->rowid, 'type' => $obj->type, 'rulevalue' => $obj->rulevalue, 'status' => $obj->status);
778 $i++;
779 }
780 $this->db->free($resql);
781 } else {
782 dol_print_error($this->db);
783 }
784
785 return 1;
786 }
787
794 public function fetchActions()
795 {
796 $this->actions = array();
797
798 $sql = 'SELECT rowid, type, actionparam, status';
799 $sql .= ' FROM '.MAIN_DB_PREFIX.'emailcollector_emailcollectoraction';
800 $sql .= ' WHERE fk_emailcollector = '.((int) $this->id);
801 $sql .= ' ORDER BY position';
802
803 $resql = $this->db->query($sql);
804 if ($resql) {
805 $num = $this->db->num_rows($resql);
806 $i = 0;
807 while ($i < $num) {
808 $obj = $this->db->fetch_object($resql);
809 $this->actions[$obj->rowid] = array('id' => $obj->rowid, 'type' => $obj->type, 'actionparam' => $obj->actionparam, 'status' => $obj->status);
810 $i++;
811 }
812 $this->db->free($resql);
813
814 return 1;
815 } else {
816 dol_print_error($this->db);
817
818 return -1;
819 }
820 }
821
822
828 public function getConnectStringIMAP()
829 {
830 // Connect to IMAP
831 $flags = '/service=imap'; // IMAP
832 if (getDolGlobalString('IMAP_FORCE_TLS')) {
833 $flags .= '/tls';
834 } elseif (empty($this->imap_encryption) || ($this->imap_encryption == 'ssl' && getDolGlobalString('IMAP_FORCE_NOSSL'))) {
835 $flags .= '';
836 } else {
837 $flags .= '/' . $this->imap_encryption;
838 }
839
840 $flags .= '/novalidate-cert';
841 //$flags.='/readonly';
842 //$flags.='/debug';
843 if (!empty($this->norsh) || getDolGlobalString('IMAP_FORCE_NORSH')) {
844 $flags .= '/norsh';
845 }
846 //Used in shared mailbox from Office365
847 if (!empty($this->login) && strpos($this->login, '/') != false) {
848 $partofauth = explode('/', $this->login);
849 $flags .= '/authuser='.$partofauth[0].'/user='.$partofauth[1];
850 }
851
852 $connectstringserver = '{'.$this->host.':'.$this->port.$flags.'}';
853
854 return $connectstringserver;
855 }
856
863 public function getEncodedUtf7($str)
864 {
865 if (function_exists('mb_convert_encoding')) {
866 // change spaces by entropy because mb_convert fail with spaces
867 $str = preg_replace("/ /", "xxxSPACExxx", $str); // the replacement string must be valid in utf7 so _ can't be used
868 $str = preg_replace("/_/", "xxxUNDERSCORExxx", $str); // encode underscore to avoid encoding issues with mb_convert
869 $str = preg_replace("/\[Gmail\]/", "xxxGMAILxxx", $str); // the replacement string must be valid in utf7 so _ can't be used
870 // if mb_convert work
871 if ($str = mb_convert_encoding($str, "UTF-7")) {
872 // change characters
873 $str = preg_replace("/\+A/", "&A", $str);
874 $str = preg_replace("/xxxUNDERSCORExxx/", "_", $str);
875 // change to spaces again
876 $str = preg_replace("/xxxSPACExxx/", " ", $str);
877 // change to [Gmail] again
878 $str = preg_replace("/xxxGMAILxxx/", "[Gmail]", $str);
879 return $str;
880 } else {
881 // print error and return false
882 $this->error = "error: is not possible to encode this string '".$str."'";
883 return false;
884 }
885 } else {
886 return $str;
887 }
888 }
889
896 public function doCollect()
897 {
898 global $user;
899
900 $nbErrors = 0;
901
902 $arrayofcollectors = $this->fetchAll($user, 1);
903 // Loop on each collector
904 foreach ($arrayofcollectors as $emailcollector) {
905 $result = $emailcollector->doCollectOneCollector(0);
906
907 dol_syslog("doCollect result = ".$result." for emailcollector->id = ".$emailcollector->id);
908
909 $this->error .= 'EmailCollector ID '.$emailcollector->id.':'.$emailcollector->error.'<br>';
910 if (!empty($emailcollector->errors)) {
911 $this->error .= implode('<br>', $emailcollector->errors);
912 $nbErrors++;
913 }
914 $this->output .= 'EmailCollector ID '.$emailcollector->id.': '.$emailcollector->lastresult.'<br>';
915
916 if ($result < 0) {
917 $nbErrors++;
918 }
919 }
920
921 return $nbErrors;
922 }
923
935 private function overwritePropertiesOfObject(&$object, $actionparam, $messagetext, $subject, $header, &$operationslog)
936 {
937 global $conf, $langs;
938
939 $errorforthisaction = 0;
940
941 // set output lang
942 $outputlangs = $langs;
943 $newlang = '';
944 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
945 $newlang = GETPOST('lang_id', 'aZ09');
946 }
947 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
948 $newlang = !empty($object->thirdparty->default_lang) ? $object->thirdparty->default_lang : $newlang;
949 }
950 if (!empty($newlang)) {
951 $outputlangs = new Translate('', $conf);
952 $outputlangs->setDefaultLang($newlang);
953 }
954
955 // Overwrite values with values extracted from source email
956 // $this->actionparam = 'opportunity_status=123;abc=EXTRACT:BODY:....'
957 $arrayvaluetouse = dolExplodeIntoArray($actionparam, '(\n\r|\r|\n|;)', '=');
958
959 $tmp = array();
960
961 // Loop on each property set into actionparam
962 foreach ($arrayvaluetouse as $propertytooverwrite => $valueforproperty) {
963 $tmpclass = '';
964 $tmpproperty = '';
965 $tmparray = explode('.', $propertytooverwrite);
966 if (count($tmparray) == 2) {
967 $tmpclass = $tmparray[0];
968 $tmpproperty = $tmparray[1];
969 } else {
970 $tmpproperty = $tmparray[0];
971 }
972 if ($tmpclass && ($tmpclass != $object->element)) {
973 continue; // Property is for another type of object
974 }
975
976 //if (property_exists($object, $tmpproperty) || preg_match('/^options_/', $tmpproperty))
977 if ($tmpproperty) {
978 $sourcestring = '';
979 $sourcefield = '';
980 $regexstring = '';
981 //$transformationstring='';
982 $regforregex = array();
983 if (preg_match('/^EXTRACT:([a-zA-Z0-9_]+):(.*):([^:])$/', $valueforproperty, $regforregex)) {
984 $sourcefield = $regforregex[1];
985 $regexstring = $regforregex[2];
986 //$transofrmationstring=$regforregex[3];
987 } elseif (preg_match('/^EXTRACT:([a-zA-Z0-9_]+):(.*)$/', $valueforproperty, $regforregex)) {
988 $sourcefield = $regforregex[1];
989 $regexstring = $regforregex[2];
990 }
991
992 if (!empty($sourcefield) && !empty($regexstring)) {
993 if (strtolower($sourcefield) == 'body') {
994 $sourcestring = $messagetext;
995 } elseif (strtolower($sourcefield) == 'subject') {
996 $sourcestring = $subject;
997 } elseif (strtolower($sourcefield) == 'header') {
998 $sourcestring = $header;
999 }
1000
1001 if ($sourcestring) {
1002 $regforval = array();
1003 $regexoptions = '';
1004 if (strtolower($sourcefield) == 'body') {
1005 $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
1006 }
1007 if (strtolower($sourcefield) == 'header') {
1008 $regexoptions = 'm'; // The m means ^ and $ char is valid at each new line.
1009 }
1010
1011 //var_dump($tmpproperty.' - '.$regexstring.' - '.$regexoptions.' - '.$sourcestring);
1012 if (preg_match('/'.$regexstring.'/'.$regexoptions, $sourcestring, $regforval)) {
1013 // Overwrite param $tmpproperty
1014 $valueextracted = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
1015 if (strtolower($sourcefield) == 'header') { // extract from HEADER
1016 if (preg_match('/^options_/', $tmpproperty)) {
1017 $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $this->decodeSMTPSubject($valueextracted);
1018 } else {
1019 if (property_exists($object, $tmpproperty)) {
1020 $object->$tmpproperty = $this->decodeSMTPSubject($valueextracted);
1021 } else {
1022 $tmp[$tmpproperty] = $this->decodeSMTPSubject($valueextracted);
1023 }
1024 }
1025 } else { // extract from BODY
1026 if (preg_match('/^options_/', $tmpproperty)) {
1027 $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $this->decodeSMTPSubject($valueextracted);
1028 } else {
1029 if (property_exists($object, $tmpproperty)) {
1030 $object->$tmpproperty = $this->decodeSMTPSubject($valueextracted);
1031 } else {
1032 $tmp[$tmpproperty] = $this->decodeSMTPSubject($valueextracted);
1033 }
1034 }
1035 }
1036 if (preg_match('/^options_/', $tmpproperty)) {
1037 $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));
1038 } else {
1039 if (property_exists($object, $tmpproperty)) {
1040 $operationslog .= '<br>Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> found '.dol_escape_htmltag(dol_trunc($object->$tmpproperty, 128));
1041 } else {
1042 $operationslog .= '<br>Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> found '.dol_escape_htmltag(dol_trunc($tmp[$tmpproperty], 128));
1043 }
1044 }
1045 } else {
1046 // Regex not found
1047 if (property_exists($object, $tmpproperty)) {
1048 $object->$tmpproperty = null;
1049 } else {
1050 $tmp[$tmpproperty] = null;
1051 }
1052
1053 $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.';
1054 }
1055 } else {
1056 // Nothing can be done for this param
1057 $errorforthisaction++;
1058 $this->error = 'The extract rule to use to overwrite properties has on an unknown source (must be HEADER, SUBJECT or BODY)';
1059 $this->errors[] = $this->error;
1060
1061 $operationslog .= '<br>'.$this->error;
1062 }
1063 } elseif (preg_match('/^(SET|SETIFEMPTY):(.*)$/', $valueforproperty, $regforregex)) {
1064 $valuecurrent = '';
1065 if (preg_match('/^options_/', $tmpproperty)) {
1066 $valuecurrent = $object->array_options[preg_replace('/^options_/', '', $tmpproperty)];
1067 } else {
1068 if (property_exists($object, $tmpproperty)) {
1069 $valuecurrent = $object->$tmpproperty;
1070 } else {
1071 // False positive @phan-suppress-next-line PhanTypeInvalidDimOffset
1072 $valuecurrent = $tmp[$tmpproperty];
1073 }
1074 }
1075
1076 if ($regforregex[1] == 'SET' || empty($valuecurrent)) {
1077 $valuetouse = $regforregex[2];
1078 $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object);
1079 complete_substitutions_array($substitutionarray, $outputlangs, $object);
1080 $matcharray = array();
1081 preg_match_all('/__([a-z0-9]+(?:_[a-z0-9]+)?)__/i', $valuetouse, $matcharray);
1082 //var_dump($tmpproperty.' - '.$object->$tmpproperty.' - '.$valuetouse); var_dump($matcharray);
1083 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
1084 foreach ($matcharray[1] as $keytoreplace) {
1085 if ($keytoreplace) {
1086 if (preg_match('/^options_/', $keytoreplace)) {
1087 $substitutionarray['__'.$keytoreplace.'__'] = $object->array_options[preg_replace('/^options_/', '', $keytoreplace)];
1088 } else {
1089 if (property_exists($object, $keytoreplace)) {
1090 $substitutionarray['__'.$keytoreplace.'__'] = $object->$keytoreplace;
1091 } else {
1092 // False positive @phan-suppress-next-line PhanTypeInvalidDimOffset
1093 $substitutionarray['__'.$keytoreplace.'__'] = $tmp[$keytoreplace];
1094 }
1095 }
1096 }
1097 }
1098 }
1099 //var_dump($substitutionarray);
1100 //dol_syslog('substitutionarray='.var_export($substitutionarray, true));
1101
1102 $valuetouse = make_substitutions($valuetouse, $substitutionarray);
1103 if (preg_match('/^options_/', $tmpproperty)) {
1104 $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $valuetouse;
1105
1106 $operationslog .= '<br>Set value '.dol_escape_htmltag($valuetouse).' into object->array_options['.dol_escape_htmltag(preg_replace('/^options_/', '', $tmpproperty)).']';
1107 } else {
1108 if (property_exists($object, $tmpproperty)) {
1109 $object->$tmpproperty = $valuetouse;
1110 } else {
1111 $tmp[$tmpproperty] = $valuetouse;
1112 }
1113
1114 $operationslog .= '<br>Set value '.dol_escape_htmltag($valuetouse).' into object->'.dol_escape_htmltag($tmpproperty);
1115 }
1116 }
1117 } else {
1118 $errorforthisaction++;
1119 $this->error = 'Bad syntax for description of action parameters: '.$actionparam;
1120 $this->errors[] = $this->error;
1121 }
1122 }
1123 }
1124
1125 return $errorforthisaction;
1126 }
1127
1134 public function doCollectOneCollector($mode = 0)
1135 {
1136 global $db, $conf, $langs, $user;
1137 global $hookmanager;
1138
1139 //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1140
1141 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1142 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
1143 require_once DOL_DOCUMENT_ROOT.'/includes/webklex/php-imap/vendor/autoload.php';
1144 }
1145
1146 dol_syslog("EmailCollector::doCollectOneCollector start for id=".$this->id." - ".$this->ref, LOG_INFO);
1147
1148 $langs->loadLangs(array("project", "companies", "mails", "errors", "ticket", "agenda", "commercial"));
1149
1150 $error = 0;
1151 $this->output = '';
1152 $this->error = '';
1153 $this->debuginfo = '';
1154
1155 $search = '';
1156 $searchhead = '';
1157 $searchfilterdoltrackid = 0;
1158 $searchfilternodoltrackid = 0;
1159 $searchfilterisanswer = 0;
1160 $searchfilterisnotanswer = 0;
1161 $searchfilterreplyto = 0;
1162 $searchfilterexcludebodyarray = array();
1163 $searchfilterexcludesubjectarray = array();
1164 $operationslog = '';
1165 $rulesreplyto = array();
1166 $connectstringsource = '';
1167 $connectstringtarget = '';
1168 $connection = false;
1169 $arrayofemail = array();
1170
1171 $now = dol_now();
1172 $datelastok = $now;
1173
1174 if (empty($this->host)) {
1175 $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('EMailHost'));
1176 return -1;
1177 }
1178 if (empty($this->login)) {
1179 $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Login'));
1180 return -1;
1181 }
1182 if (empty($this->source_directory)) {
1183 $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('MailboxSourceDirectory'));
1184 return -1;
1185 }
1186
1187 $client = null;
1188
1189 $sourcedir = $this->source_directory;
1190 $targetdir = ($this->target_directory ? $this->target_directory : ''); // Can be '[Gmail]/Trash' or 'mytag'
1191
1192 $this->fetchFilters();
1193 $this->fetchActions();
1194
1195 $sourcedir = $this->source_directory;
1196 $targetdir = ($this->target_directory ? $this->target_directory : ''); // Can be '[Gmail]/Trash' or 'mytag'
1197
1198 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
1199 if ($this->acces_type == 1) {
1200 // Mode OAUth2 (access_type == 1) with PHP-IMAP
1201 $this->debuginfo .= 'doCollectOneCollector is using method MAIN_IMAP_USE_PHPIMAP=1, access_type=1 (OAUTH2)<br>';
1202
1203 require_once DOL_DOCUMENT_ROOT.'/core/lib/oauth.lib.php';
1204
1205 $supportedoauth2array = getSupportedOauth2Array();
1206
1207 $keyforsupportedoauth2array = $this->oauth_service;
1208 if (preg_match('/^.*-/', $keyforsupportedoauth2array)) {
1209 $keyforprovider = preg_replace('/^.*-/', '', $keyforsupportedoauth2array);
1210 } else {
1211 $keyforprovider = '';
1212 }
1213 $keyforsupportedoauth2array = preg_replace('/-.*$/', '', strtoupper($keyforsupportedoauth2array));
1214 $keyforsupportedoauth2array = 'OAUTH_'.$keyforsupportedoauth2array.'_NAME';
1215
1216 if (!empty($supportedoauth2array)) {
1217 $nameofservice = ucfirst(strtolower(empty($supportedoauth2array[$keyforsupportedoauth2array]['callbackfile']) ? 'Unknown' : $supportedoauth2array[$keyforsupportedoauth2array]['callbackfile']));
1218 $nameofservice .= ($keyforprovider ? '-'.$keyforprovider : '');
1219 $OAUTH_SERVICENAME = $nameofservice;
1220 } else {
1221 $OAUTH_SERVICENAME = 'Unknown';
1222 }
1223
1224 $keyforparamtenant = 'OAUTH_'.strtoupper(empty($supportedoauth2array[$keyforsupportedoauth2array]['callbackfile']) ? 'Unknown' : $supportedoauth2array[$keyforsupportedoauth2array]['callbackfile']).($keyforprovider ? '-'.$keyforprovider : '').'_TENANT';
1225
1226 require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php';
1227 //$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;
1228 //dol_syslog($debugtext);
1229
1230 $token = '';
1231
1232 $storage = new DoliStorage($db, $conf, $keyforprovider, getDolGlobalString($keyforparamtenant));
1233
1234 try {
1235 $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME);
1236
1237 $expire = true;
1238 // TODO
1239 // Is token expired or will token expire in the next 30 seconds
1240 // if (is_object($tokenobj)) {
1241 // $expire = ($tokenobj->getEndOfLife() !== -9002 && $tokenobj->getEndOfLife() !== -9001 && time() > ($tokenobj->getEndOfLife() - 30));
1242 // }
1243 // Token expired so we refresh it
1244 if (is_object($tokenobj) && $expire) {
1245 $this->debuginfo .= 'Refresh token '.$OAUTH_SERVICENAME.'<br>';
1246 $credentials = new Credentials(
1247 getDolGlobalString('OAUTH_'.$this->oauth_service.'_ID'),
1248 getDolGlobalString('OAUTH_'.$this->oauth_service.'_SECRET'),
1249 getDolGlobalString('OAUTH_'.$this->oauth_service.'_URLCALLBACK')
1250 );
1251 $serviceFactory = new \OAuth\ServiceFactory();
1252 $oauthname = explode('-', $OAUTH_SERVICENAME);
1253 // ex service is Google-Emails we need only the first part Google
1254
1255 $scopes = array();
1256 if (preg_match('/^Microsoft/', $OAUTH_SERVICENAME)) {
1257 //$extraparams = $tokenobj->getExtraParams();
1258 $tmp = explode('-', $OAUTH_SERVICENAME);
1259 $scopes = explode(',', getDolGlobalString('OAUTH_'.strtoupper($tmp[0]).(empty($tmp[1]) ? '' : '-'.$tmp[1]).'_SCOPE'));
1260 }
1261
1262 $apiService = $serviceFactory->createService($oauthname[0], $credentials, $storage, $scopes);
1263
1264 '@phan-var-force OAuth\OAuth2\Service\AbstractService|OAuth\OAuth1\Service\AbstractService $apiService'; // createService is only ServiceInterface
1265
1266 $refreshtoken = $tokenobj->getRefreshToken();
1267 $tokenobj = $apiService->refreshAccessToken($tokenobj);
1268
1269 // We have to save the token because answer give it only once
1270 $tokenobj->setRefreshToken($refreshtoken);
1271 $storage->storeAccessToken($OAUTH_SERVICENAME, $tokenobj);
1272 }
1273 $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME);
1274 if (is_object($tokenobj)) {
1275 $token = $tokenobj->getAccessToken();
1276 } else {
1277 $this->error = "Token not found";
1278 return -1;
1279 }
1280 } catch (Exception $e) {
1281 // Return an error if token not found
1282 $this->error = $e->getMessage();
1283 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
1284 return -1;
1285 }
1286
1287 $cm = new ClientManager();
1288 $client = $cm->make([
1289 'host' => $this->host,
1290 'port' => $this->port,
1291 'encryption' => !empty($this->imap_encryption) ? $this->imap_encryption : false,
1292 'validate_cert' => true,
1293 'protocol' => 'imap',
1294 'username' => $this->login,
1295 'password' => $token,
1296 'authentication' => "oauth",
1297 ]);
1298 } else {
1299 // Mode LOGIN (login/pass) with PHP-IMAP
1300 $this->debuginfo .= 'doCollectOneCollector is using method MAIN_IMAP_USE_PHPIMAP=1, access_type=0 (LOGIN)<br>';
1301
1302 $cm = new ClientManager();
1303 $client = $cm->make([
1304 'host' => $this->host,
1305 'port' => $this->port,
1306 'encryption' => !empty($this->imap_encryption) ? $this->imap_encryption : false,
1307 'validate_cert' => true,
1308 'protocol' => 'imap',
1309 'username' => $this->login,
1310 'password' => $this->password,
1311 'authentication' => "login",
1312 ]);
1313 }
1314
1315 try {
1316 $client->connect();
1317
1318 $connection = true;
1319 } catch (ConnectionFailedException $e) {
1320 $this->error = $e->getMessage();
1321 $this->errors[] = $this->error;
1322 dol_syslog("EmailCollector::doCollectOneCollector ".$this->error, LOG_ERR);
1323 return -1;
1324 }
1325
1326 $host = dol_getprefix('email');
1327 } else {
1328 // Use native IMAP functions
1329 $this->debuginfo .= 'doCollectOneCollector is using method MAIN_IMAP_USE_PHPIMAP=0 (native PHP imap, LOGIN)<br>';
1330
1331 if (!function_exists('imap_open')) {
1332 $this->error = 'IMAP function not enabled on your PHP';
1333 return -2;
1334 }
1335
1336 $connectstringserver = $this->getConnectStringIMAP();
1337 if (!getDolGlobalString('MAIL_DISABLE_UTF7_ENCODE_OF_DIR')) {
1338 $connectstringsource = $connectstringserver.$this->getEncodedUtf7($sourcedir);
1339 $connectstringtarget = $connectstringserver.$this->getEncodedUtf7($targetdir);
1340 } else {
1341 $connectstringsource = $connectstringserver.$sourcedir;
1342 $connectstringtarget = $connectstringserver.$targetdir;
1343 }
1344
1345 $this->debuginfo .= 'connectstringsource = '.$connectstringsource.', $connectstringtarget='.$connectstringtarget.'<br>';
1346
1347 $connection = imap_open($connectstringsource, $this->login, $this->password);
1348 if ($connection === false) {
1349 $this->error = 'Failed to open IMAP connection '.$connectstringsource.' '.imap_last_error();
1350 return -3;
1351 }
1352 '@phan-var-force resource|IMAP\Connection $connection';
1353 imap_errors(); // Clear stack of errors.
1354
1355 $host = dol_getprefix('email');
1356 //$host = '123456';
1357
1358 // Define the IMAP search string
1359 // See https://tools.ietf.org/html/rfc3501#section-6.4.4 for IMAPv4 (PHP not yet compatible)
1360 // See https://tools.ietf.org/html/rfc1064 page 13 for IMAPv2
1361 //$search='ALL';
1362 }
1363
1364 $criteria = array();
1365 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
1366 // Use PHPIMAP external library
1367 $criteria = array(array('UNDELETED')); // Seems not supported by some servers
1368 foreach ($this->filters as $rule) {
1369 if (empty($rule['status'])) {
1370 continue;
1371 }
1372
1373 $not = '';
1374 if (strpos($rule['rulevalue'], '!') === 0) {
1375 // The value start with !, so we exclude the criteria
1376 $not = 'NOT ';
1377 // Then remove the ! from the string for next filters
1378 $rule['rulevalue'] = substr($rule['rulevalue'], 1);
1379 }
1380
1381 if ($rule['type'] == 'from') {
1382 $tmprulevaluearray = explode('*', $rule['rulevalue']);
1383 if (count($tmprulevaluearray) >= 2) {
1384 foreach ($tmprulevaluearray as $tmprulevalue) {
1385 array_push($criteria, array($not."FROM" => $tmprulevalue));
1386 }
1387 } else {
1388 array_push($criteria, array($not."FROM" => $rule['rulevalue']));
1389 }
1390 }
1391 if ($rule['type'] == 'to') {
1392 $tmprulevaluearray = explode('*', $rule['rulevalue']);
1393 if (count($tmprulevaluearray) >= 2) {
1394 foreach ($tmprulevaluearray as $tmprulevalue) {
1395 array_push($criteria, array($not."TO" => $tmprulevalue));
1396 }
1397 } else {
1398 array_push($criteria, array($not."TO" => $rule['rulevalue']));
1399 }
1400 }
1401 if ($rule['type'] == 'bcc') {
1402 array_push($criteria, array($not."BCC" => $rule['rulevalue']));
1403 }
1404 if ($rule['type'] == 'cc') {
1405 array_push($criteria, array($not."CC" => $rule['rulevalue']));
1406 }
1407 if ($rule['type'] == 'subject') {
1408 if (strpos($rule['rulevalue'], '!') === 0) {
1409 //array_push($criteria, array("NOT SUBJECT" => $rule['rulevalue']));
1410 $searchfilterexcludesubjectarray[] = preg_replace('/^!/', '', $rule['rulevalue']);
1411 } else {
1412 array_push($criteria, array("SUBJECT" => $rule['rulevalue']));
1413 }
1414 }
1415 if ($rule['type'] == 'body') {
1416 if (strpos($rule['rulevalue'], '!') === 0) {
1417 //array_push($criteria, array("NOT BODY" => $rule['rulevalue']));
1418 $searchfilterexcludebodyarray[] = preg_replace('/^!/', '', $rule['rulevalue']);
1419 } else {
1420 array_push($criteria, array("BODY" => $rule['rulevalue']));
1421 }
1422 }
1423 if ($rule['type'] == 'header') {
1424 array_push($criteria, array($not."HEADER" => $rule['rulevalue']));
1425 }
1426
1427 /* seems not used */
1428 /*
1429 if ($rule['type'] == 'notinsubject') {
1430 array_push($criteria, array($not."SUBJECT NOT" => $rule['rulevalue']));
1431 }
1432 if ($rule['type'] == 'notinbody') {
1433 array_push($criteria, array($not."BODY NOT" => $rule['rulevalue']));
1434 }*/
1435
1436 if ($rule['type'] == 'seen') {
1437 array_push($criteria, array($not."SEEN"));
1438 }
1439 if ($rule['type'] == 'unseen') {
1440 array_push($criteria, array($not."UNSEEN"));
1441 }
1442 if ($rule['type'] == 'unanswered') {
1443 array_push($criteria, array($not."UNANSWERED"));
1444 }
1445 if ($rule['type'] == 'answered') {
1446 array_push($criteria, array($not."ANSWERED"));
1447 }
1448 if ($rule['type'] == 'smaller') {
1449 array_push($criteria, array($not."SMALLER"));
1450 }
1451 if ($rule['type'] == 'larger') {
1452 array_push($criteria, array($not."LARGER"));
1453 }
1454
1455 // Rules to filter after the search imap
1456 if ($rule['type'] == 'withtrackingidinmsgid') {
1457 $searchfilterdoltrackid++;
1458 $searchhead .= '/Message-ID.*@'.preg_quote($host, '/').'/';
1459 }
1460 if ($rule['type'] == 'withouttrackingidinmsgid') {
1461 $searchfilterdoltrackid++;
1462 $searchhead .= '/Message-ID.*@'.preg_quote($host, '/').'/';
1463 }
1464 if ($rule['type'] == 'withtrackingid') {
1465 $searchfilterdoltrackid++;
1466 $searchhead .= '/References.*@'.preg_quote($host, '/').'/';
1467 }
1468 if ($rule['type'] == 'withouttrackingid') {
1469 $searchfilternodoltrackid++;
1470 $searchhead .= '! /References.*@'.preg_quote($host, '/').'/';
1471 }
1472
1473 if ($rule['type'] == 'isanswer') {
1474 $searchfilterisanswer++;
1475 $searchhead .= '/References.*@.*/';
1476 }
1477 if ($rule['type'] == 'isnotanswer') {
1478 $searchfilterisnotanswer++;
1479 $searchhead .= '! /References.*@.*/';
1480 }
1481
1482 if ($rule['type'] == 'replyto') {
1483 $searchfilterreplyto++;
1484 $rulesreplyto[] = $rule['rulevalue'];
1485 $searchhead .= '/Reply-To.*'.preg_quote($rule['rulevalue'], '/').'/';
1486 }
1487 }
1488
1489 if (empty($targetdir) || !getDolGlobalString('EMAILCOLLECTOR_NO_FILTER_ON_DATE_IF_THERE_IS_A_TARGETDIR')) { // Use the last date of successful check as a filter if there is no targetdir defined.
1490 $fromdate = 0;
1491 if ($this->datelastok) {
1492 $fromdate = $this->datelastok;
1493 }
1494 if ($fromdate > 0) {
1495 // $search .= ($search ? ' ' : '').'SINCE '.date('j-M-Y', $fromdate - 1); // SENTSINCE not supported. Date must be X-Abc-9999 (X on 1 digit if < 10)
1496 array_push($criteria, array("SINCE" => date('j-M-Y', $fromdate - 1))); // -1 is to add a security to no forgot some email
1497 }
1498 //$search.=($search?' ':'').'SINCE 8-Apr-2022';
1499 }
1500
1501 dol_syslog("IMAP search string = ".var_export($criteria, true));
1502 $search = var_export($criteria, true);
1503 } else {
1504 // Use native IMAP functions
1505 $search = 'UNDELETED'; // Seems not supported by some servers
1506 foreach ($this->filters as $rule) {
1507 if (empty($rule['status'])) {
1508 continue;
1509 }
1510
1511 // Forge the IMAP search string.
1512 // See https://www.rfc-editor.org/rfc/rfc3501
1513
1514 $not = '';
1515 if (!empty($rule['rulevalue']) && strpos($rule['rulevalue'], '!') === 0) {
1516 // The value start with !, so we exclude the criteria
1517 $not = 'NOT ';
1518 // Then remove the ! from the string for next filters
1519 $rule['rulevalue'] = substr($rule['rulevalue'], 1);
1520 }
1521
1522 if ($rule['type'] == 'from') {
1523 $tmprulevaluearray = explode('*', $rule['rulevalue']); // Search on abc*def means searching on 'abc' and on 'def'
1524 if (count($tmprulevaluearray) >= 2) {
1525 foreach ($tmprulevaluearray as $tmprulevalue) {
1526 $search .= ($search ? ' ' : '').$not.'FROM "'.str_replace('"', '', $tmprulevalue).'"';
1527 }
1528 } else {
1529 $search .= ($search ? ' ' : '').$not.'FROM "'.str_replace('"', '', $rule['rulevalue']).'"';
1530 }
1531 }
1532 if ($rule['type'] == 'to') {
1533 $tmprulevaluearray = explode('*', $rule['rulevalue']); // Search on abc*def means searching on 'abc' and on 'def'
1534 if (count($tmprulevaluearray) >= 2) {
1535 foreach ($tmprulevaluearray as $tmprulevalue) {
1536 $search .= ($search ? ' ' : '').$not.'TO "'.str_replace('"', '', $tmprulevalue).'"';
1537 }
1538 } else {
1539 $search .= ($search ? ' ' : '').$not.'TO "'.str_replace('"', '', $rule['rulevalue']).'"';
1540 }
1541 }
1542 if ($rule['type'] == 'bcc') {
1543 $search .= ($search ? ' ' : '').$not.'BCC';
1544 }
1545 if ($rule['type'] == 'cc') {
1546 $search .= ($search ? ' ' : '').$not.'CC';
1547 }
1548 if ($rule['type'] == 'subject') {
1549 if ($not) {
1550 //$search .= ($search ? ' ' : '').'NOT BODY "'.str_replace('"', '', $rule['rulevalue']).'"';
1551 $searchfilterexcludesubjectarray[] = $rule['rulevalue'];
1552 } else {
1553 $search .= ($search ? ' ' : '').'SUBJECT "'.str_replace('"', '', $rule['rulevalue']).'"';
1554 }
1555 }
1556 if ($rule['type'] == 'body') {
1557 if ($not) {
1558 //$search .= ($search ? ' ' : '').'NOT BODY "'.str_replace('"', '', $rule['rulevalue']).'"';
1559 $searchfilterexcludebodyarray[] = $rule['rulevalue'];
1560 } else {
1561 // Warning: Google doesn't implement IMAP properly, and only matches whole words,
1562 $search .= ($search ? ' ' : '').'BODY "'.str_replace('"', '', $rule['rulevalue']).'"';
1563 }
1564 }
1565 if ($rule['type'] == 'header') {
1566 $search .= ($search ? ' ' : '').$not.'HEADER '.$rule['rulevalue'];
1567 }
1568
1569 /* seems not used */
1570 /*
1571 if ($rule['type'] == 'notinsubject') {
1572 $search .= ($search ? ' ' : '').'NOT SUBJECT "'.str_replace('"', '', $rule['rulevalue']).'"';
1573 }
1574 if ($rule['type'] == 'notinbody') {
1575 $search .= ($search ? ' ' : '').'NOT BODY "'.str_replace('"', '', $rule['rulevalue']).'"';
1576 }*/
1577
1578 if ($rule['type'] == 'seen') {
1579 $search .= ($search ? ' ' : '').$not.'SEEN';
1580 }
1581 if ($rule['type'] == 'unseen') {
1582 $search .= ($search ? ' ' : '').$not.'UNSEEN';
1583 }
1584 if ($rule['type'] == 'unanswered') {
1585 $search .= ($search ? ' ' : '').$not.'UNANSWERED';
1586 }
1587 if ($rule['type'] == 'answered') {
1588 $search .= ($search ? ' ' : '').$not.'ANSWERED';
1589 }
1590 if ($rule['type'] == 'smaller') {
1591 $search .= ($search ? ' ' : '').$not.'SMALLER "'.str_replace('"', '', $rule['rulevalue']).'"';
1592 }
1593 if ($rule['type'] == 'larger') {
1594 $search .= ($search ? ' ' : '').$not.'LARGER "'.str_replace('"', '', $rule['rulevalue']).'"';
1595 }
1596
1597 // Rules to filter after the search imap
1598 if ($rule['type'] == 'withtrackingidinmsgid') {
1599 $searchfilterdoltrackid++;
1600 $searchhead .= '/Message-ID.*@'.preg_quote($host, '/').'/';
1601 }
1602 if ($rule['type'] == 'withouttrackingidinmsgid') {
1603 $searchfilterdoltrackid++;
1604 $searchhead .= '/Message-ID.*@'.preg_quote($host, '/').'/';
1605 }
1606 if ($rule['type'] == 'withtrackingid') {
1607 $searchfilterdoltrackid++;
1608 $searchhead .= '/References.*@'.preg_quote($host, '/').'/';
1609 }
1610 if ($rule['type'] == 'withouttrackingid') {
1611 $searchfilternodoltrackid++;
1612 $searchhead .= '! /References.*@'.preg_quote($host, '/').'/';
1613 }
1614
1615 if ($rule['type'] == 'isanswer') {
1616 $searchfilterisanswer++;
1617 $searchhead .= '/References.*@.*/';
1618 }
1619 if ($rule['type'] == 'isnotanswer') {
1620 $searchfilterisnotanswer++;
1621 $searchhead .= '! /References.*@.*/';
1622 }
1623
1624 if ($rule['type'] == 'replyto') {
1625 $searchfilterreplyto++;
1626 $rulesreplyto[] = $rule['rulevalue'];
1627 $searchhead .= '/Reply-To.*'.preg_quote($rule['rulevalue'], '/').'/';
1628 }
1629 }
1630
1631 if (empty($targetdir)) { // Use last date as filter if there is no targetdir defined.
1632 $fromdate = 0;
1633 if ($this->datelastok) {
1634 $fromdate = $this->datelastok;
1635 }
1636 if ($fromdate > 0) {
1637 $search .= ($search ? ' ' : '').'SINCE '.date('j-M-Y', $fromdate - 1); // SENTSINCE not supported. Date must be X-Abc-9999 (X on 1 digit if < 10)
1638 }
1639 //$search.=($search?' ':'').'SINCE 8-Apr-2018';
1640 }
1641
1642 dol_syslog("IMAP search string = ".$search);
1643 //var_dump($search);
1644 }
1645
1646 $nbemailprocessed = 0;
1647 $nbemailok = 0;
1648 $nbactiondone = 0;
1649 $charset = ($this->hostcharset ? $this->hostcharset : "UTF-8");
1650 $arrayofemail = array();
1651
1652 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP') && is_object($client)) {
1653 try {
1654 // Uncomment this to output debug info
1655 //$client->getConnection()->enableDebug();
1656
1657 $tmpsourcedir = $sourcedir;
1658 if (!getDolGlobalString('MAIL_DISABLE_UTF7_ENCODE_OF_DIR')) {
1659 $tmpsourcedir = $this->getEncodedUtf7($sourcedir);
1660 }
1661
1662 $f = $client->getFolders(false, $tmpsourcedir); // Note the search of directory do a search on sourcedir*
1663 if ($f) {
1664 $folder = $f[0];
1665 if ($folder instanceof Webklex\PHPIMAP\Folder) {
1666 $Query = $folder->messages()->where($criteria); // @phan-suppress-current-line PhanPluginUnknownObjectMethodCall
1667 } else {
1668 $error++;
1669 $this->error = "Source directory ".$sourcedir." not found";
1670 $this->errors[] = $this->error;
1671 dol_syslog("EmailCollector::doCollectOneCollector ".$this->error, LOG_WARNING);
1672 return -1;
1673 }
1674 } else {
1675 $error++;
1676 $this->error = "Failed to execute getfolders";
1677 $this->errors[] = $this->error;
1678 dol_syslog("EmailCollector::doCollectOneCollector ".$this->error, LOG_ERR);
1679 return -1;
1680 }
1681 } catch (InvalidWhereQueryCriteriaException $e) {
1682 $this->error = $e->getMessage();
1683 $this->errors[] = $this->error;
1684 dol_syslog("EmailCollector::doCollectOneCollector ".$this->error, LOG_ERR);
1685 return -1;
1686 } catch (Exception $e) {
1687 $this->error = $e->getMessage();
1688 $this->errors[] = $this->error;
1689 dol_syslog("EmailCollector::doCollectOneCollector ".$this->error, LOG_ERR);
1690 return -1;
1691 }
1692
1693 '@phan-var-force Webklex\PHPIMAP\Query\Query $Query';
1694 try {
1695 //var_dump($Query->count());
1696 if ($mode > 0) {
1697 $Query->leaveUnread();
1698 }
1699 $arrayofemail = $Query->limit($this->maxemailpercollect)->setFetchOrder("asc")->get();
1700 dol_syslog("EmailCollector::doCollectOneCollector nb arrayofemail ".(is_array($arrayofemail) ? count($arrayofemail) : 'Not array')); // @phpstan-ignore-line
1701 //var_dump($arrayofemail);
1702 } catch (Exception $e) {
1703 $this->error = $e->getMessage();
1704 $this->errors[] = $this->error;
1705 dol_syslog("EmailCollector::doCollectOneCollector ".$this->error, LOG_ERR);
1706 return -1;
1707 }
1708 } elseif ($connection !== false) {
1709 // Scan IMAP dir (for native IMAP, the source dir is inside the $connection variable)
1710 $arrayofemail = imap_search($connection, $search, SE_UID, $charset);
1711
1712 if ($arrayofemail === false) {
1713 // Nothing found or search string not understood
1714 $mapoferrrors = imap_errors();
1715 if ($mapoferrrors !== false) {
1716 $error++;
1717 $this->error = "Search string not understood - ".implode(',', $mapoferrrors);
1718 $this->errors[] = $this->error;
1719 }
1720 }
1721 }
1722
1723 $arrayofemailtodelete = array(); // Track email to delete to make the deletion at end.
1724
1725 // Loop on each email found
1726 if (!$error && !empty($arrayofemail) && count($arrayofemail) > 0 && $connection !== false) {
1727 // Loop to get part html and plain
1728 /*
1729 0 multipart/mixed
1730 1 multipart/alternative
1731 1.1 text/plain
1732 1.2 text/html
1733 2 message/rfc822
1734 2 multipart/mixed
1735 2.1 multipart/alternative
1736 2.1.1 text/plain
1737 2.1.2 text/html
1738 2.2 message/rfc822
1739 2.2 multipart/alternative
1740 2.2.1 text/plain
1741 2.2.2 text/html
1742 */
1743 dol_syslog("Start of loop on email", LOG_INFO, 1);
1744
1745 $richarrayofemail = array();
1746
1747 foreach ($arrayofemail as $imapemail) {
1748 if ($nbemailprocessed > 1000) {
1749 break; // Do not process more than 1000 email per launch (this is a different protection than maxnbcollectedpercollect)
1750 }
1751
1752 // GET header and overview datas
1753 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
1754 '@phan-var-force Webklex\PHPIMAP\Message $imapemail';
1755 $header = $imapemail->getHeader()->raw; // @phan-suppress-current-line PhanPluginUnknownObjectMethodCall // @phan-suppress-current-line PhanPluginUnknownObjectMethodCall
1756 $overview = $imapemail->getAttributes();
1757 } else {
1758 $header = imap_fetchheader($connection, $imapemail, FT_UID);
1759 $overview = imap_fetch_overview($connection, $imapemail, FT_UID);
1760 }
1761
1762 $header = preg_replace('/\r\n\s+/m', ' ', $header); // When a header line is on several lines, merge lines
1763
1764 $matches = array();
1765 preg_match_all('/([^: ]+): (.+?(?:\r\n\s(?:.+?))*)(\r\n|\s$)/m', $header, $matches);
1766 $headers = array_combine($matches[1], $matches[2]);
1767
1768
1769 $richarrayofemail[] = array('imapemail' => $imapemail, 'header' => $header, 'headers' => $headers, 'overview' => $overview, 'date' => strtotime($headers['Date']));
1770 }
1771
1772
1773 // Sort email found by ascending date
1774 $richarrayofemail = dol_sort_array($richarrayofemail, 'date', 'asc');
1775
1776
1777 $iforemailloop = 0;
1778 foreach ($richarrayofemail as $tmpval) {
1779 $iforemailloop++;
1780
1781 $imapemail = $tmpval['imapemail'];
1782 $header = $tmpval['header'];
1783 $overview = $tmpval['overview'];
1784 $headers = $tmpval['headers'];
1785
1786 if (!empty($headers['in-reply-to']) && empty($headers['In-Reply-To'])) {
1787 $headers['In-Reply-To'] = $headers['in-reply-to'];
1788 }
1789 if (!empty($headers['references']) && empty($headers['References'])) {
1790 $headers['References'] = $headers['references'];
1791 }
1792 if (!empty($headers['message-id']) && empty($headers['Message-ID'])) {
1793 $headers['Message-ID'] = $headers['message-id'];
1794 }
1795 if (!empty($headers['subject']) && empty($headers['Subject'])) {
1796 $headers['Subject'] = $headers['subject'];
1797 }
1798
1799 $headers['Subject'] = $this->decodeSMTPSubject($headers['Subject']);
1800
1801 if (getDolGlobalInt('MAIN_IMAP_USE_PHPIMAP')) {
1802 $emailto = (string) $overview['to'];
1803 } else {
1804 $emailto = $this->decodeSMTPSubject($overview[0]->to);
1805 }
1806 //var_dump($headers);
1807 //var_dump($overview);exit;
1808
1809 $operationslog .= '<br>** Process email #'.dol_escape_htmltag((string) $iforemailloop);
1810
1811 if (getDolGlobalInt('MAIN_IMAP_USE_PHPIMAP')) {
1813 '@phan-var-force Webklex\PHPIMAP\Message $imapemail';
1814 // $operationslog .= " - ".dol_escape_htmltag((string) $imapemail);
1815 $msgid = str_replace(array('<', '>'), '', $overview['message_id']);
1816 } else {
1817 $operationslog .= " - ".dol_escape_htmltag((string) $imapemail);
1818 $msgid = str_replace(array('<', '>'), '', $overview[0]->message_id);
1819 }
1820 $operationslog .= " - MsgId: ".$msgid;
1821 $operationslog .= " - Date: ".($headers['Date'] ?? $langs->transnoentitiesnoconv("NotFound"));
1822 $operationslog .= " - References: ".dol_escape_htmltag($headers['References'] ?? $langs->transnoentitiesnoconv("NotFound"))." - Subject: ".dol_escape_htmltag($headers['Subject']);
1823
1824 dol_syslog("-- Process email #".$iforemailloop.", MsgId: ".$msgid.", Date: ".($headers['Date'] ?? '').", References: ".($headers['References'] ?? '').", Subject: ".$headers['Subject']);
1825
1826
1827 $trackidfoundintorecipienttype = '';
1828 $trackidfoundintorecipientid = 0;
1829 $reg = array();
1830 // See also later list of all supported tags...
1831 // Note: "th[i]" to avoid matching a codespell suggestion to convert to "this".
1832 // TODO Add host after the @'.preg_quote($host, '/')
1833 if (preg_match('/\+(th[i]|ctc|use|mem|sub|proj|tas|con|tic|pro|ord|inv|spro|sor|sin|leav|stockinv|job|surv|salary)([0-9]+)@/', $emailto, $reg)) {
1834 $trackidfoundintorecipienttype = $reg[1];
1835 $trackidfoundintorecipientid = $reg[2];
1836 } elseif (preg_match('/\+emailing-(\w+)@/', $emailto, $reg)) { // Can be 'emailing-test' or 'emailing-IdMailing-IdRecipient'
1837 $trackidfoundintorecipienttype = 'emailing';
1838 $trackidfoundintorecipientid = $reg[1];
1839 }
1840
1841 $trackidfoundintomsgidtype = '';
1842 $trackidfoundintomsgidid = 0;
1843 $reg = array();
1844 // See also later list of all supported tags...
1845 // Note: "th[i]" to avoid matching a codespell suggestion to convert to "this".
1846 // TODO Add host after the @
1847 if (preg_match('/(?:[\+\-])(th[i]|ctc|use|mem|sub|proj|tas|con|tic|pro|ord|inv|spro|sor|sin|leav|stockinv|job|surv|salary)([0-9]+)@/', $msgid, $reg)) {
1848 $trackidfoundintomsgidtype = $reg[1];
1849 $trackidfoundintomsgidid = $reg[2];
1850 } elseif (preg_match('/(?:[\+\-])emailing-(\w+)@/', $msgid, $reg)) { // Can be 'emailing-test' or 'emailing-IdMailing-IdRecipient'
1851 $trackidfoundintomsgidtype = 'emailing';
1852 $trackidfoundintomsgidid = $reg[1];
1853 }
1854
1855 // Now apply some filters.
1856
1857 //If there is an emailcollector filter on trackid
1858 if ($searchfilterdoltrackid > 0) {
1859 if (empty($trackidfoundintorecipienttype) && empty($trackidfoundintomsgidtype)) {
1860 if (empty($headers['References']) || !preg_match('/@'.preg_quote($host, '/').'/', $headers['References'])) {
1861 $nbemailprocessed++;
1862 dol_syslog(" Discarded - No suffix in email recipient and no Header References found matching the signature of the application, so with a trackid coming from the application");
1863 continue; // Exclude email
1864 }
1865 }
1866 }
1867 if ($searchfilternodoltrackid > 0) {
1868 if (!empty($trackidfoundintorecipienttype) || !empty($trackidfoundintomsgidtype) || (!empty($headers['References']) && preg_match('/@'.preg_quote($host, '/').'/', $headers['References']))) {
1869 $nbemailprocessed++;
1870 dol_syslog(" Discarded - Suffix found into email or Header References found and matching signature of application so with a trackid");
1871 continue; // Exclude email
1872 }
1873 }
1874
1875 if ($searchfilterisanswer > 0) {
1876 $referencesforanswer = '';
1877 if (!empty($headers['References'])) {
1878 $referencesforanswer .= $headers['References'].' ';
1879 }
1880 if (!empty($headers['In-Reply-To'])) {
1881 $referencesforanswer .= $headers['In-Reply-To'];
1882 }
1883
1884 $hasdolibarrreference = 0;
1885 if (!empty($referencesforanswer)) {
1886 if (preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote($host, '/').'/', $referencesforanswer)) {
1887 $hasdolibarrreference = 1;
1888 } elseif (getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE')) {
1889 if (preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote(getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE'), '/').'/', $referencesforanswer)) {
1890 $hasdolibarrreference = 1;
1891 }
1892 }
1893 }
1894
1895 if (empty($headers['In-Reply-To']) && empty($hasdolibarrreference)) {
1896 $nbemailprocessed++;
1897 dol_syslog(" Discarded - Email is not an answer (no In-Reply-To header and no Dolibarr reference)");
1898 continue; // Exclude email
1899 }
1900 $isanswer = 0;
1901 if (preg_match('/^(Re|AW)\s*:\s+/i', $headers['Subject'])) {
1902 $isanswer = 1;
1903 }
1904 if (getDolGlobalString('EMAILCOLLECTOR_USE_IN_REPLY_TO_TO_DETECT_ANSWERS')) {
1905 // Note: "In-Reply-To" to detect if mail is an answer of another mail is not reliable because we can have:
1906 // Message-ID=A, In-Reply-To=B, References=B and message can BE an answer but may be NOT (for example a transfer of an email rewritten)
1907 if (!empty($headers['In-Reply-To'])) {
1908 $isanswer = 1;
1909 }
1910 }
1911 if ($hasdolibarrreference) {
1912 $isanswer = 1;
1913 }
1914 //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
1915 //if ($headers['In-Reply-To'] != $headers['Message-ID'] && !empty($headers['References']) && strpos($headers['References'], $headers['Message-ID']) !== false) $isanswer = 1;
1916
1917 if (!$isanswer) {
1918 $nbemailprocessed++;
1919 dol_syslog(" Discarded - Email is not an answer (no RE prefix in subject)");
1920 continue; // Exclude email
1921 }
1922 }
1923 if ($searchfilterisnotanswer > 0) {
1924 if (!empty($headers['In-Reply-To'])) {
1925 // Note: we can have
1926 // Message-ID=A, In-Reply-To=B, References=B and message can BE an answer or NOT (a transfer rewritten)
1927 $isanswer = 0;
1928 if (preg_match('/^(回复|回覆|SV|Antw|VS|RE|Re|AW|Aw|ΑΠ|השב| תשובה | הועבר|Vá|R|RIF|BLS|Atb|RES|Odp|பதில்|YNT|ATB)\s*:\s+/i', $headers['Subject'])) {
1929 $isanswer = 1;
1930 }
1931 //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
1932 //if ($headers['In-Reply-To'] != $headers['Message-ID'] && !empty($headers['References']) && strpos($headers['References'], $headers['Message-ID']) !== false) $isanswer = 1;
1933 if ($isanswer) {
1934 $nbemailprocessed++;
1935 dol_syslog(" Discarded - Email is an answer");
1936 continue; // Exclude email
1937 }
1938 }
1939 }
1940 if ($searchfilterreplyto > 0) {
1941 if (!empty($headers['Reply-To'])) {
1942 $isreplytook = 0;
1943 foreach ($rulesreplyto as $key => $rulereplyto) {
1944 if (preg_match('/'.preg_quote($rulereplyto, '/').'/', $headers['Reply-To'])) {
1945 $isreplytook++;
1946 }
1947 }
1948
1949 if (!$isreplytook || $isreplytook != count($rulesreplyto)) {
1950 $nbemailprocessed++;
1951 dol_syslog(" Discarded - Reply-to does not match");
1952 continue; // Exclude email
1953 }
1954 }
1955 }
1956
1957 //print "Process mail ".$iforemailloop." Subject: ".dol_escape_htmltag($headers['Subject'])." selected<br>\n";
1958
1959 $thirdpartystatic = new Societe($this->db);
1960 $contactstatic = new Contact($this->db);
1961 $projectstatic = new Project($this->db);
1962
1963 $nbactiondoneforemail = 0;
1964 $errorforemail = 0;
1965 $errorforactions = 0;
1966 $thirdpartyfoundby = '';
1967 $contactfoundby = '';
1968 $projectfoundby = '';
1969 $ticketfoundby = '';
1970 $candidaturefoundby = '';
1971
1972
1973 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
1974 $dateformated = dol_print_date($overview['date'], 'dayrfc', 'gmt'); // May generate a warning "dol_print_date($overview['date'], 'dayrfc', 'gmt')" in log
1975 dol_syslog("msgid=".$overview['message_id']." date=".$dateformated." from=".$overview['from']." to=".$overview['to']." subject=".$overview['subject']);
1976
1977 // Removed emojis
1978 $overview['subject'] = removeEmoji($overview['subject'], getDolGlobalInt('MAIN_EMAIL_COLLECTOR_ACCEPT_EMOJIS', 1));
1979 } else {
1980 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);
1981
1982 $overview[0]->subject = $this->decodeSMTPSubject($overview[0]->subject);
1983
1984 $overview[0]->from = $this->decodeSMTPSubject($overview[0]->from);
1985
1986 // Removed emojis
1987 $overview[0]->subject = removeEmoji($overview[0]->subject, getDolGlobalInt('MAIN_EMAIL_COLLECTOR_ACCEPT_EMOJIS', 1));
1988 }
1989 // GET IMAP email structure/content
1990 global $htmlmsg, $plainmsg, $charset, $attachments;
1991
1992 if (getDolGlobalInt('MAIN_IMAP_USE_PHPIMAP')) {
1994 '@phan-var-force Webklex\PHPIMAP\Message $imapemail';
1995 if ($imapemail->hasHTMLBody()) {
1996 $htmlmsg = $imapemail->getHTMLBody();
1997 }
1998 if ($imapemail->hasTextBody() && $imapemail->getTextBody() != "\n") {
1999 $plainmsg = $imapemail->getTextBody();
2000 }
2001 if ($imapemail->hasAttachments()) {
2002 $attachments = $imapemail->getAttachments()->all();
2003 } else {
2004 $attachments = [];
2005 }
2006 } else {
2007 $getMsg = $this->getmsg($connection, $imapemail); // This set global var $charset, $htmlmsg, $plainmsg, $attachments
2008 if ($getMsg < 0) {
2009 $this->errors = array_merge($this->errors, [$this->error]);
2010 return $getMsg;
2011 }
2012 }
2013 '@phan-var-force Webklex\PHPIMAP\Attachment[] $attachments';
2014
2015 //print $plainmsg;
2016 //var_dump($plainmsg); exit;
2017
2018 //$htmlmsg,$plainmsg,$charset,$attachments
2019 $messagetext = $plainmsg ? $plainmsg : dol_string_nohtmltag((string) $htmlmsg, 0);
2020 // Removed emojis
2021
2022 if (utf8_valid($messagetext)) {
2023 $messagetext = removeEmoji($messagetext, getDolGlobalInt('MAIN_EMAIL_COLLECTOR_ACCEPT_EMOJIS', 1));
2024 } else {
2025 $operationslog .= '<br>Discarded - Email body is not valid utf8';
2026 dol_syslog(" Discarded - Email body is not valid utf8");
2027 continue; // Exclude email
2028 }
2029
2030 if (!empty($searchfilterexcludebodyarray)) {
2031 foreach ($searchfilterexcludebodyarray as $searchfilterexcludebody) {
2032 if (preg_match('/'.preg_quote($searchfilterexcludebody, '/').'/ms', $messagetext)) {
2033 $nbemailprocessed++;
2034 $operationslog .= '<br>Discarded - Email body contains string '.$searchfilterexcludebody;
2035 dol_syslog(" Discarded - Email body contains string ".$searchfilterexcludebody);
2036 continue 2; // Exclude email
2037 }
2038 }
2039 }
2040
2041 //var_dump($plainmsg);
2042 //var_dump($htmlmsg);
2043 //var_dump($messagetext);
2044 //var_dump($charset);
2045 //var_dump($attachments);
2046 //exit;
2047
2048 // Parse IMAP email structure
2049 /*
2050 $structure = imap_fetchstructure($connection, $imapemail, FT_UID);
2051
2052 $partplain = $parthtml = -1;
2053 $encodingplain = $encodinghtml = '';
2054
2055 $result = createPartArray($structure, '');
2056
2057 foreach($result as $part)
2058 {
2059 // $part['part_object']->type seems 0 for content
2060 // $part['part_object']->type seems 5 for attachment
2061 if (empty($part['part_object'])) continue;
2062 if ($part['part_object']->subtype == 'HTML')
2063 {
2064 $parthtml=$part['part_number'];
2065 if ($part['part_object']->encoding == 4)
2066 {
2067 $encodinghtml = 'aaa';
2068 }
2069 }
2070 if ($part['part_object']->subtype == 'PLAIN')
2071 {
2072 $partplain=$part['part_number'];
2073 if ($part['part_object']->encoding == 4)
2074 {
2075 $encodingplain = 'rr';
2076 }
2077 }
2078 }
2079 //var_dump($result);
2080 //var_dump($partplain);
2081 //var_dump($parthtml);
2082
2083 //var_dump($structure);
2084 //var_dump($parthtml);
2085 //var_dump($partplain);
2086
2087 $messagetext = imap_fetchbody($connection, $imapemail, ($parthtml != '-1' ? $parthtml : ($partplain != '-1' ? $partplain : 1)), FT_PEEK|FTP_UID);
2088 */
2089
2090 //var_dump($messagetext);
2091 //var_dump($structure->parts[0]->parts);
2092 //print $header;
2093 //print $messagetext;
2094 //exit;
2095
2096 $fromstring = '';
2097 $replytostring = '';
2098
2099 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
2100 $fromstring = $overview['from'];
2101 $replytostring = empty($overview['in_reply-to']) ? $headers['Reply-To'] : $overview['in_reply-to'];
2102
2103 $sender = $overview['sender'];
2104 $to = $overview['to'];
2105 $sendtocc = empty($overview['cc']) ? '' : $overview['cc'];
2106 $sendtobcc = empty($overview['bcc']) ? '' : $overview['bcc'];
2107
2108 $tmpdate = $overview['date']->toDate(); // @phan-suppress-current-line PhanPluginUnknownObjectMethodCall
2109 $tmptimezone = $tmpdate->getTimezone()->getName(); // @phan-suppress-current-line PhanPluginUnknownObjectMethodCall
2110
2111 $dateemail = dol_stringtotime((string) $overview['date'], 'gmt'); // if $overview['timezone'] is "+00:00"
2112 if (preg_match('/^([+\-])(\d\d):(\d\d)/', $tmptimezone, $reg)) {
2113 if ($reg[1] == '+' && ($reg[2] != '00' || $reg[3] != '00')) {
2114 $dateemail -= (3600 * (int) $reg[2]);
2115 $dateemail -= (60 * (int) $reg[3]);
2116 }
2117 if ($reg[1] == '-' && ($reg[2] != '00' || $reg[3] != '00')) {
2118 $dateemail += (3600 * (int) $reg[2]);
2119 $dateemail += (60 * (int) $reg[3]);
2120 }
2121 }
2122 $subject = $overview['subject'];
2123 } else {
2124 $fromstring = $overview[0]->from;
2125 $replytostring = (!empty($overview['in_reply-to']) ? $overview['in_reply-to'] : (!empty($headers['Reply-To']) ? $headers['Reply-To'] : "")) ;
2126
2127 $sender = !empty($overview[0]->sender) ? $overview[0]->sender : '';
2128 $to = $overview[0]->to;
2129 $sendtocc = !empty($overview[0]->cc) ? $overview[0]->cc : '';
2130 $sendtobcc = !empty($overview[0]->bcc) ? $overview[0]->bcc : '';
2131 $dateemail = dol_stringtotime((string) $overview[0]->udate, 'gmt');
2132 $subject = $overview[0]->subject;
2133 //var_dump($msgid);exit;
2134 }
2135
2136 if (!empty($searchfilterexcludesubjectarray)) {
2137 foreach ($searchfilterexcludesubjectarray as $searchfilterexcludesubject) {
2138 if (preg_match('/'.preg_quote($searchfilterexcludesubject, '/').'/ms', $subject)) {
2139 $nbemailprocessed++;
2140 $operationslog .= '<br>Discarded - Email subject contains string '.$searchfilterexcludesubject;
2141 dol_syslog(" Discarded - Email subject contains string ".$searchfilterexcludesubject);
2142 continue 2; // Exclude email
2143 }
2144 }
2145 }
2146
2147 $reg = array();
2148 if (preg_match('/^(.*)<(.*)>$/', $fromstring, $reg)) {
2149 $from = $reg[2];
2150 $fromtext = $reg[1];
2151 } else {
2152 $from = $fromstring;
2153 $fromtext = '';
2154 }
2155 if (preg_match('/^(.*)<(.*)>$/', $replytostring, $reg)) {
2156 $replyto = $reg[2];
2157 $replytotext = $reg[1];
2158 } else {
2159 $replyto = $replytostring;
2160 $replytotext = '';
2161 }
2162 $fk_element_id = 0;
2163 $fk_element_type = '';
2164
2165
2166 $this->db->begin();
2167
2168 $contactid = 0;
2169 $thirdpartyid = 0;
2170 $projectid = 0;
2171 $ticketid = 0;
2172
2173 // Analyze TrackId in field References (already analyzed previously into the "To:" and "Message-Id").
2174 // For example:
2175 // References: <1542377954.SMTPs-dolibarr-thi649@8f6014fde11ec6cdec9a822234fc557e>
2176 // References: <1542377954.SMTPs-dolibarr-tic649@8f6014fde11ec6cdec9a822234fc557e>
2177 // References: <1542377954.SMTPs-dolibarr-abc649@8f6014fde11ec6cdec9a822234fc557e>
2178 $trackid = '';
2179 $objectid = 0;
2180 $objectemail = null;
2181
2182 $reg = array();
2183 $arrayofreferences = array();
2184 if (!empty($headers['References'])) {
2185 $arrayofreferences = preg_split('/(,|\s+)/', $headers['References']);
2186 }
2187 if (!in_array('<'.$msgid.'>', $arrayofreferences)) {
2188 $arrayofreferences = array_merge($arrayofreferences, array('<'.$msgid.'>'));
2189 }
2190 // var_dump($headers['References']);
2191 // var_dump($arrayofreferences);
2192
2193 // We loop on References, but as soon as we found one that allow us to find an existing object,
2194 // we do a break (See line with comment "Exit loop of references").
2195 foreach ($arrayofreferences as $reference) {
2196 //print "Process mail ".$iforemailloop." email_msgid ".$msgid.", date ".dol_print_date($dateemail, 'dayhour', 'gmt').", subject ".$subject.", reference ".dol_escape_htmltag($reference)."<br>\n";
2197 if (!empty($trackidfoundintorecipienttype)) {
2198 $resultsearchtrackid = -1; // trackid found
2199 $reg[1] = $trackidfoundintorecipienttype;
2200 $reg[2] = $trackidfoundintorecipientid;
2201 } elseif (!empty($trackidfoundintomsgidtype)) {
2202 $resultsearchtrackid = -1; // trackid found
2203 $reg[1] = $trackidfoundintomsgidtype;
2204 $reg[2] = $trackidfoundintomsgidid;
2205 } else {
2206 $resultsearchtrackid = preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote($host, '/').'/', $reference, $reg); // trackid found or not
2207 if (empty($resultsearchtrackid) && getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE')) {
2208 $resultsearchtrackid = preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote(getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE'), '/').'/', $reference, $reg); // trackid found
2209 }
2210 }
2211
2212 if (!empty($resultsearchtrackid)) {
2213 // We found a tracker (in recipient email or msgid or into a Reference matching the Dolibarr server)
2214 $trackid = $reg[1].$reg[2];
2215
2216 $objectid = $reg[2];
2217 // See also list into interface_50_modAgenda_ActionsAuto
2218 if ($reg[1] == 'thi') { // Third-party
2219 $objectemail = new Societe($this->db);
2220 }
2221 if ($reg[1] == 'ctc') { // Contact
2222 $objectemail = new Contact($this->db);
2223 }
2224 if ($reg[1] == 'inv') { // Customer Invoice
2225 $objectemail = new Facture($this->db);
2226 }
2227 if ($reg[1] == 'sinv') { // Supplier Invoice
2228 $objectemail = new FactureFournisseur($this->db);
2229 }
2230 if ($reg[1] == 'pro') { // Customer Proposal
2231 $objectemail = new Propal($this->db);
2232 }
2233 if ($reg[1] == 'ord') { // Sale Order
2234 $objectemail = new Commande($this->db);
2235 }
2236 if ($reg[1] == 'shi') { // Shipment
2237 $objectemail = new Expedition($this->db);
2238 }
2239 if ($reg[1] == 'spro') { // Supplier Proposal
2240 $objectemail = new SupplierProposal($this->db);
2241 }
2242 if ($reg[1] == 'sord') { // Supplier Order
2243 $objectemail = new CommandeFournisseur($this->db);
2244 }
2245 if ($reg[1] == 'rec') { // Reception
2246 $objectemail = new Reception($this->db);
2247 }
2248 if ($reg[1] == 'proj') { // Project
2249 $objectemail = new Project($this->db);
2250 $projectfoundby = 'TrackID dolibarr-'.$trackid.'@...';
2251 }
2252 if ($reg[1] == 'tas') { // Task
2253 $objectemail = new Task($this->db);
2254 }
2255 if ($reg[1] == 'con') { // Contact
2256 $objectemail = new Contact($this->db);
2257 }
2258 if ($reg[1] == 'use') { // User
2259 $objectemail = new User($this->db);
2260 }
2261 if ($reg[1] == 'tic') { // Ticket
2262 $objectemail = new Ticket($this->db);
2263 $ticketfoundby = 'TrackID dolibarr-'.$trackid.'@...';
2264 }
2265 if ($reg[1] == 'recruitmentcandidature') { // Recruiting Candidate
2266 $objectemail = new RecruitmentCandidature($this->db);
2267 $candidaturefoundby = 'TrackID dolibarr-'.$trackid.'@...';
2268 }
2269 if ($reg[1] == 'mem') { // Member
2270 $objectemail = new Adherent($this->db);
2271 }
2272 /*if ($reg[1] == 'leav') { // Leave / Holiday
2273 $objectemail = new Holiday($db);
2274 }
2275 if ($reg[1] == 'exp') { // ExpenseReport
2276 $objectemail = new ExpenseReport($db);
2277 }*/
2278 } elseif (preg_match('/<(.*@.*)>/', $reference, $reg)) {
2279 // This is an external reference, we check if we have it in our database
2280 if (is_null($objectemail) && isModEnabled('ticket')) {
2281 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."ticket";
2282 $sql .= " WHERE email_msgid = '".$this->db->escape($reg[1])."' OR origin_references LIKE '%".$this->db->escape($this->db->escapeforlike($reg[1]))."%'";
2283 $resql = $this->db->query($sql);
2284 if ($resql) {
2285 $obj = $this->db->fetch_object($resql);
2286 if ($obj) {
2287 $objectid = $obj->rowid;
2288 $objectemail = new Ticket($this->db);
2289 $ticketfoundby = $langs->transnoentitiesnoconv("EmailMsgID").' ('.$reg[1].')';
2290 }
2291 } else {
2292 $errorforemail++;
2293 }
2294 }
2295
2296 if (!is_object($objectemail) && isModEnabled('project')) {
2297 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."projet where email_msgid = '".$this->db->escape($reg[1])."'";
2298 $resql = $this->db->query($sql);
2299 if ($resql) {
2300 $obj = $this->db->fetch_object($resql);
2301 if ($obj) {
2302 $objectid = $obj->rowid;
2303 $objectemail = new Project($this->db);
2304 $projectfoundby = $langs->transnoentitiesnoconv("EmailMsgID").' ('.$reg[1].')';
2305 }
2306 } else {
2307 $errorforemail++;
2308 }
2309 }
2310
2311 if (!is_object($objectemail) && isModEnabled('recruitment')) {
2312 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."recruitment_recruitmentcandidature where email_msgid = '".$this->db->escape($reg[1])."'";
2313 $resql = $this->db->query($sql);
2314 if ($resql) {
2315 $obj = $this->db->fetch_object($resql);
2316 if ($obj) {
2317 $objectid = $obj->rowid;
2318 $objectemail = new RecruitmentCandidature($this->db);
2319 $candidaturefoundby = $langs->transnoentitiesnoconv("EmailMsgID").' ('.$reg[1].')';
2320 }
2321 } else {
2322 $errorforemail++;
2323 }
2324 }
2325 }
2326
2327 // Load object linked to email
2328 if (is_object($objectemail)) {
2329 $result = $objectemail->fetch($objectid);
2330 if ($result > 0) {
2331 $fk_element_id = $objectemail->id;
2332 $fk_element_type = $objectemail->element;
2333 // Fix fk_element_type
2334 if ($fk_element_type == 'facture') {
2335 $fk_element_type = 'invoice';
2336 }
2337
2338 if (get_class($objectemail) != 'Societe') {
2339 $thirdpartyid = $objectemail->fk_soc ?? $objectemail->socid;
2340 } else {
2341 $thirdpartyid = $objectemail->id;
2342 }
2343
2344 if (get_class($objectemail) != 'Contact') {
2345 $contactid = $objectemail->fk_socpeople;
2346 } else {
2347 $contactid = $objectemail->id;
2348 }
2349
2350 if (get_class($objectemail) != 'Project') {
2351 $projectid = isset($objectemail->fk_project) ? $objectemail->fk_project : $objectemail->fk_projet;
2352 } else {
2353 $projectid = $objectemail->id;
2354 }
2355
2356 if ($objectemail instanceof Ticket) { // For tickets, we have a column to store all met references, so we complete it if we need to.
2357 $ticketid = $objectemail->id;
2358
2359 $changeonticket_references = false;
2360 if (empty($trackid)) {
2361 $trackid = $objectemail->track_id;
2362 }
2363 if (empty($objectemail->origin_references)) {
2364 $objectemail->origin_references = !empty($headers['References']) ? $headers['References'] : null;
2365 $changeonticket_references = true;
2366 } else {
2367 foreach ($arrayofreferences as $key => $referencetmp) {
2368 if (!str_contains($objectemail->origin_references, $referencetmp)) {
2369 $objectemail->origin_references .= " ".$referencetmp;
2370 $changeonticket_references = true;
2371 }
2372 }
2373 }
2374 if ($changeonticket_references) {
2375 $operationslog .= '<br>We complete ticket ID='.$ticketid.' with property origin_references='.$objectemail->origin_references;
2376 $objectemail->update($user, 1); // We complete the references field with all references mentioned into this email. This field is for technical tracking purpose, not a user field, so no need to execute triggers
2377 }
2378 }
2379 }
2380 }
2381
2382 // Project
2383 if ($projectid > 0) {
2384 $result = $projectstatic->fetch($projectid);
2385 if ($result <= 0) {
2386 $projectstatic->id = 0;
2387 } else {
2388 $projectid = $projectstatic->id;
2389 if ($trackid) {
2390 $projectfoundby = 'trackid ('.$trackid.')';
2391 }
2392 if (empty($contactid)) {
2393 $contactid = $projectstatic->fk_contact;
2394 }
2395 if (empty($thirdpartyid)) {
2396 $thirdpartyid = $projectstatic->fk_soc;
2397 }
2398 }
2399 }
2400 // Contact
2401 if ($contactid > 0) {
2402 $result = $contactstatic->fetch($contactid);
2403 if ($result <= 0) {
2404 $contactstatic->id = 0;
2405 } else {
2406 $contactid = $contactstatic->id;
2407 if ($trackid) {
2408 $contactfoundby = 'trackid ('.$trackid.')';
2409 }
2410 if (empty($thirdpartyid)) {
2411 $thirdpartyid = $contactstatic->fk_soc;
2412 }
2413 }
2414 }
2415 // Thirdparty
2416 if ($thirdpartyid > 0) {
2417 $result = $thirdpartystatic->fetch($thirdpartyid);
2418 if ($result <= 0) {
2419 $thirdpartystatic->id = 0;
2420 } else {
2421 $thirdpartyid = $thirdpartystatic->id;
2422 if ($trackid) {
2423 $thirdpartyfoundby = 'trackid ('.$trackid.')';
2424 }
2425 }
2426 }
2427
2428 if (is_object($objectemail)) {
2429 break; // Exit loop of references. We already found an accurate reference
2430 }
2431 }
2432
2433 if (empty($contactid)) { // Try to find contact using email
2434 $result = $contactstatic->fetch(0, null, '', $from);
2435
2436 if ($result > 0) {
2437 dol_syslog("We found a contact with the email ".$from);
2438 $contactid = $contactstatic->id;
2439 $contactfoundby = 'email of contact ('.$from.')';
2440 if (empty($thirdpartyid) && $contactstatic->socid > 0) {
2441 $result = $thirdpartystatic->fetch($contactstatic->socid);
2442 if ($result > 0) {
2443 $thirdpartyid = $thirdpartystatic->id;
2444 $thirdpartyfoundby = 'email of contact ('.$from.')';
2445 }
2446 }
2447 }
2448 }
2449
2450 if (empty($thirdpartyid)) { // Try to find thirdparty using email
2451 $result = $thirdpartystatic->fetch(0, '', '', '', '', '', '', '', '', '', $from);
2452 if ($result > 0) {
2453 dol_syslog("We found a thirdparty with the email ".$from);
2454 $thirdpartyid = $thirdpartystatic->id;
2455 $thirdpartyfoundby = 'email ('.$from.')';
2456 }
2457 }
2458
2459 /*
2460 if ($replyto) {
2461 if (empty($contactid)) { // Try to find contact using email
2462 $result = $contactstatic->fetch(0, null, '', $replyto);
2463
2464 if ($result > 0) {
2465 dol_syslog("We found a contact with the email ".$replyto);
2466 $contactid = $contactstatic->id;
2467 $contactfoundby = 'email of contact ('.$replyto.')';
2468 if (empty($thirdpartyid) && $contactstatic->socid > 0) {
2469 $result = $thirdpartystatic->fetch($contactstatic->socid);
2470 if ($result > 0) {
2471 $thirdpartyid = $thirdpartystatic->id;
2472 $thirdpartyfoundby = 'email of contact ('.$replyto.')';
2473 }
2474 }
2475 }
2476 }
2477
2478 if (empty($thirdpartyid)) { // Try to find thirdparty using email
2479 $result = $thirdpartystatic->fetch(0, '', '', '', '', '', '', '', '', '', $replyto);
2480 if ($result > 0) {
2481 dol_syslog("We found a thirdparty with the email ".$replyto);
2482 $thirdpartyid = $thirdpartystatic->id;
2483 $thirdpartyfoundby = 'email ('.$replyto.')';
2484 }
2485 }
2486 }
2487 */
2488
2489
2490 // Now do all operations for the email (extract variables and creating data)
2491 if ($mode < 2) { // 0=Mode production, 1=Mode test (read IMAP and try SQL update then rollback), 2=Mode test with no SQL updates
2492 foreach ($this->actions as $operation) {
2493 $errorforthisaction = 0;
2494 $ticketalreadyexists = 0;
2495 if ($errorforactions) {
2496 break;
2497 }
2498 if (empty($operation['status'])) {
2499 continue;
2500 }
2501
2502 $operationslog .= '<br>* Process operation '.$operation['type'];
2503
2504 // Make Operation
2505 dol_syslog("Execute action ".$operation['type']." actionparam=".$operation['actionparam'].' thirdpartystatic->id='.$thirdpartystatic->id.' contactstatic->id='.$contactstatic->id.' projectstatic->id='.$projectstatic->id);
2506 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
2507
2508 // Try to guess if this is an email in or out.
2509 $actioncode = 'EMAIL_IN';
2510 // If we scan the Sent box, we use the code for out email
2511 if (preg_match('/Sent$/', $sourcedir) || preg_match('/envoyés$/i', $sourcedir)) {
2512 $actioncode = 'EMAIL';
2513 }
2514 // If sender is in the list MAIL_FROM_EMAILS_TO_CONSIDER_SENDING
2515 $arrayofemailtoconsideresender = explode(',', getDolGlobalString('MAIL_FROM_EMAILS_TO_CONSIDER_SENDING'));
2516 foreach ($arrayofemailtoconsideresender as $emailtoconsidersender) {
2517 if (preg_match('/'.preg_quote($emailtoconsidersender, '/').'/', $fromstring)) {
2518 $actioncode = 'EMAIL';
2519 }
2520 }
2521 $operationslog .= '<br>Email will have actioncode='.$actioncode;
2522
2523 $description = $descriptiontitle = $descriptionmeta = $descriptionfull = '';
2524
2525 $descriptiontitle = $langs->transnoentitiesnoconv("RecordCreatedByEmailCollector", $this->ref);
2526
2527 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("EmailMsgID").' : '.dol_escape_htmltag($msgid));
2528 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailTopic").' : '.dol_escape_htmltag($subject));
2529 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailDate").($langs->trans("MailDate") != 'Date' ? ' (Date)' : '').' : '.dol_escape_htmltag(dol_print_date($dateemail, "dayhourtext", "gmt")));
2530 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailFrom").($langs->trans("MailFrom") != 'From' ? ' (From)' : '').' : '.dol_escape_htmltag($fromstring));
2531 if ($sender) {
2532 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("Sender").($langs->trans("Sender") != 'Sender' ? ' (Sender)' : '').' : '.dol_escape_htmltag($sender));
2533 }
2534 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailTo").($langs->trans("MailTo") != 'To' ? ' (To)' : '').' : '.dol_escape_htmltag($to));
2535 if ($replyto) {
2536 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailReply").($langs->trans("MailReply") != 'Reply to' ? ' (Reply to)' : '').' : '.dol_escape_htmltag($replyto));
2537 }
2538 if ($sendtocc) {
2539 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailCC").($langs->trans("MailCC") != 'CC' ? ' (CC)' : '').' : '.dol_escape_htmltag($sendtocc));
2540 }
2541
2542 if ($operation['type'] == 'ticket') {
2543 // Verify if ticket already exists to fall back on the right operation
2544 $tickettocreate = new Ticket($this->db);
2545 $errorfetchticket = 0;
2546 $alreadycreated = 0;
2547 if ($ticketid > 0) {
2548 $alreadycreated = $tickettocreate->fetch($ticketid);
2549 }
2550 if ($alreadycreated == 0 && !empty($trackid)) {
2551 $alreadycreated = $tickettocreate->fetch(0, '', $trackid);
2552 }
2553 if ($alreadycreated == 0 && !empty($msgid)) {
2554 $alreadycreated = $tickettocreate->fetch(0, '', '', $msgid);
2555 }
2556 if ($alreadycreated < 0) {
2557 $errorfetchticket++;
2558 }
2559 if (empty($errorfetchticket)) {
2560 if ($alreadycreated == 0) {
2561 $operationslog .= '<br>Ticket not found using trackid='.$trackid.' or msgid='.$msgid;
2562 $ticketalreadyexists = 0;
2563 } else {
2564 $operationslog .= '<br>Ticket already found using trackid='.$trackid.' or msgid='.$msgid.", we replace operation 'ticket' with 'recordevent' to add a new message"; // We change the operation type to do
2565 $ticketalreadyexists = 1;
2566 $operation['type'] = 'recordevent';
2567 }
2568 } else {
2569 $ticketalreadyexists = -1;
2570 }
2571 }
2572
2573 // Process now the operation according to its type
2574
2575 // Search and create thirdparty
2576 if ($operation['type'] == 'loadthirdparty' || $operation['type'] == 'loadandcreatethirdparty') {
2577 if ($thirdpartyid > 0) {
2578 // We already have found the thirdparty id to load, so we bypass the action loadthirdparty
2579 $idtouseforthirdparty = $thirdpartyid;
2580 $operationslog .= '<br>We already have found a related thirdparty id, so we bypass the action loadthirdparty and use idtouseforthirdparty='.$idtouseforthirdparty;
2581 } elseif (empty($operation['actionparam'])) {
2582 $errorforactions++;
2583 $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;";
2584 $this->errors[] = $this->error;
2585 } else {
2586 $actionparam = $operation['actionparam'];
2587 $idtouseforthirdparty = '';
2588 $nametouseforthirdparty = '';
2589 $emailtouseforthirdparty = '';
2590 $namealiastouseforthirdparty = '';
2591
2592 $operationslog .= '<br>Loop on each property to set into actionparam';
2593
2594 // $actionparam = 'param=SET:aaa' or 'param=EXTRACT:BODY:....'
2595 $arrayvaluetouse = dolExplodeIntoArray($actionparam, '(\n\r|\r|\n|;)', '=');
2596 foreach ($arrayvaluetouse as $propertytooverwrite => $valueforproperty) {
2597 $sourcestring = '';
2598 $sourcefield = '';
2599 $regexstring = '';
2600 $regforregex = array();
2601
2602 if (preg_match('/^EXTRACT:([a-zA-Z0-9_]+):(.*)$/', $valueforproperty, $regforregex)) {
2603 $sourcefield = $regforregex[1];
2604 $regexstring = $regforregex[2];
2605 }
2606
2607 if (!empty($sourcefield) && !empty($regexstring)) {
2608 if (strtolower($sourcefield) == 'body') {
2609 $sourcestring = $messagetext;
2610 } elseif (strtolower($sourcefield) == 'subject') {
2611 $sourcestring = $subject;
2612 } elseif (strtolower($sourcefield) == 'header') {
2613 $sourcestring = $header;
2614 }
2615
2616 if ($sourcestring) {
2617 $regforval = array();
2618 //var_dump($regexstring);var_dump($sourcestring);
2619 if (preg_match('/'.$regexstring.'/ms', $sourcestring, $regforval)) {
2620 //var_dump($regforval[count($regforval)-1]);exit;
2621 // Overwrite param $tmpproperty
2622 if ($propertytooverwrite == 'id') {
2623 $idtouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2624
2625 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found idtouseforthirdparty='.dol_escape_htmltag($idtouseforthirdparty);
2626 } elseif ($propertytooverwrite == 'email') {
2627 $emailtouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2628
2629 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found emailtouseforthirdparty='.dol_escape_htmltag($emailtouseforthirdparty);
2630 } elseif ($propertytooverwrite == 'name') {
2631 $nametouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2632
2633 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found nametouseforthirdparty='.dol_escape_htmltag($nametouseforthirdparty);
2634 } elseif ($propertytooverwrite == 'name_alias') {
2635 $namealiastouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2636
2637 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found namealiastouseforthirdparty='.dol_escape_htmltag($namealiastouseforthirdparty);
2638 } else {
2639 $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';
2640 }
2641 } else {
2642 // Regex not found
2643 if (in_array($propertytooverwrite, array('id', 'email', 'name', 'name_alias'))) {
2644 $idtouseforthirdparty = null;
2645 $nametouseforthirdparty = null;
2646 $emailtouseforthirdparty = null;
2647 $namealiastouseforthirdparty = null;
2648
2649 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Not found. Property searched is critical so we cancel the search.';
2650 } else {
2651 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Not found';
2652 }
2653 }
2654 //var_dump($object->$tmpproperty);exit;
2655 } else {
2656 // Nothing can be done for this param
2657 $errorforactions++;
2658 $this->error = 'The extract rule to use to load thirdparty for email '.$msgid.' has an unknown source (must be HEADER, SUBJECT or BODY)';
2659 $this->errors[] = $this->error;
2660
2661 $operationslog .= '<br>'.$this->error;
2662 }
2663 } elseif (preg_match('/^(SET|SETIFEMPTY):(.*)$/', $valueforproperty, $reg)) {
2664 //if (preg_match('/^options_/', $tmpproperty)) $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $reg[1];
2665 //else $object->$tmpproperty = $reg[1];
2666 // Example: id=SETIFEMPTY:123
2667 if ($propertytooverwrite == 'id') {
2668 $idtouseforthirdparty = $reg[2];
2669
2670 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property idtouseforthrdparty='.dol_escape_htmltag($idtouseforthirdparty);
2671 } elseif ($propertytooverwrite == 'email') {
2672 $emailtouseforthirdparty = $reg[2];
2673
2674 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property emailtouseforthrdparty='.dol_escape_htmltag($emailtouseforthirdparty);
2675 } elseif ($propertytooverwrite == 'name') {
2676 $nametouseforthirdparty = $reg[2];
2677
2678 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property nametouseforthirdparty='.dol_escape_htmltag($nametouseforthirdparty);
2679 } elseif ($propertytooverwrite == 'name_alias') {
2680 $namealiastouseforthirdparty = $reg[2];
2681
2682 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property namealiastouseforthirdparty='.dol_escape_htmltag($namealiastouseforthirdparty);
2683 }
2684 } else {
2685 $errorforactions++;
2686 $this->error = 'Bad syntax for description of action parameters: '.$actionparam;
2687 $this->errors[] = $this->error;
2688 break;
2689 }
2690 }
2691
2692 if (!$errorforactions && ($idtouseforthirdparty || $emailtouseforthirdparty || $nametouseforthirdparty || $namealiastouseforthirdparty)) {
2693 // We make another search on thirdparty
2694 $operationslog .= '<br>We have this initial main data to search thirdparty: id='.$idtouseforthirdparty.', email='.$emailtouseforthirdparty.', name='.$nametouseforthirdparty.', name_alias='.$namealiastouseforthirdparty.'.';
2695
2696 $tmpobject = new stdClass();
2697 $tmpobject->element = 'generic';
2698 $tmpobject->id = $idtouseforthirdparty;
2699 $tmpobject->name = $nametouseforthirdparty;
2700 $tmpobject->name_alias = $namealiastouseforthirdparty;
2701 $tmpobject->email = $emailtouseforthirdparty;
2702
2703 $this->overwritePropertiesOfObject($tmpobject, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2704
2705 $idtouseforthirdparty = $tmpobject->id;
2706 $nametouseforthirdparty = $tmpobject->name;
2707 $namealiastouseforthirdparty = $tmpobject->name_alias;
2708 $emailtouseforthirdparty = $tmpobject->email;
2709
2710 $operationslog .= '<br>We try to search existing thirdparty with idtouseforthirdparty='.$idtouseforthirdparty.' emailtouseforthirdparty='.$emailtouseforthirdparty.' nametouseforthirdparty='.$nametouseforthirdparty.' namealiastouseforthirdparty='.$namealiastouseforthirdparty;
2711
2712 // Try to find the thirdparty that match the most the information we have
2713 $result = $thirdpartystatic->findNearest((int) $idtouseforthirdparty, (string) $nametouseforthirdparty, '', '', '', '', '', '', '', '', (string) $emailtouseforthirdparty, (string) $namealiastouseforthirdparty);
2714
2715 if ($result < 0) {
2716 $errorforactions++;
2717 $this->error = 'Error when getting thirdparty with name '.$nametouseforthirdparty.' (may be 2 record exists with same name ?)';
2718 $this->errors[] = $this->error;
2719 break;
2720 } elseif ($result == 0) { // No thirdparty found
2721 if ($operation['type'] == 'loadthirdparty') {
2722 dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." name_alias=".$namealiastouseforthirdparty." was not found");
2723
2724 // Search into contacts of thirdparties to try to guess the thirdparty to use
2725 $resultContact = $contactstatic->findNearest(0, '', '', '', (string) $emailtouseforthirdparty, '', 0);
2726 if ($resultContact > 0) {
2727 $contactstatic->fetch($resultContact);
2728 $idtouseforthirdparty = $contactstatic->socid;
2729 $result = $thirdpartystatic->fetch($idtouseforthirdparty);
2730 if ($result > 0) {
2731 dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." name_alias=".$namealiastouseforthirdparty." was found thanks to linked contact search");
2732 } else {
2733 $errorforactions++;
2734 $langs->load("errors");
2735 $this->error = $langs->trans('ErrorFailedToLoadThirdParty', $idtouseforthirdparty, (string) $emailtouseforthirdparty, (string) $nametouseforthirdparty, (string) $namealiastouseforthirdparty);
2736 $this->errors[] = $this->error;
2737 }
2738 } else {
2739 $errorforactions++;
2740 $langs->load("errors");
2741 $this->error = $langs->trans('ErrorFailedToLoadThirdParty', $idtouseforthirdparty, (string) $emailtouseforthirdparty, (string) $nametouseforthirdparty, (string) $namealiastouseforthirdparty);
2742 $this->errors[] = $this->error;
2743 }
2744 } elseif ($operation['type'] == 'loadandcreatethirdparty') {
2745 dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." name_alias=".$namealiastouseforthirdparty." was not found. We try to create it.");
2746
2747 // Create thirdparty
2748 $thirdpartystatic = new Societe($db);
2749 $thirdpartystatic->name = (string) $nametouseforthirdparty;
2750 if (!empty($namealiastouseforthirdparty)) {
2751 if ($namealiastouseforthirdparty != $nametouseforthirdparty) {
2752 $thirdpartystatic->name_alias = $namealiastouseforthirdparty;
2753 }
2754 } else {
2755 $thirdpartystatic->name_alias = (empty($replytostring) ? (empty($fromtext) ? '' : $fromtext) : $replytostring);
2756 }
2757 $thirdpartystatic->email = (empty($emailtouseforthirdparty) ? (empty($replyto) ? (empty($from) ? '' : $from) : $replyto) : $emailtouseforthirdparty);
2758
2759 // Overwrite values with values extracted from source email
2760 $errorforthisaction = $this->overwritePropertiesOfObject($thirdpartystatic, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2761
2762 if ($thirdpartystatic->client && empty($thirdpartystatic->code_client)) {
2763 $thirdpartystatic->code_client = 'auto';
2764 }
2765 if ($thirdpartystatic->fournisseur && empty($thirdpartystatic->code_fournisseur)) {
2766 $thirdpartystatic->code_fournisseur = 'auto';
2767 }
2768
2769 if ($errorforthisaction) {
2770 $errorforactions++;
2771 } else {
2772 $result = $thirdpartystatic->create($user);
2773 if ($result <= 0) {
2774 $errorforactions++;
2775 $this->error = $thirdpartystatic->error;
2776 $this->errors = $thirdpartystatic->errors;
2777 } else {
2778 $operationslog .= '<br>Thirdparty created -> id = '.dol_escape_htmltag((string) $thirdpartystatic->id);
2779 }
2780 }
2781 }
2782 } else { // $result > 0 is ID of thirdparty
2783 dol_syslog("One and only one existing third party has been found");
2784
2785 $thirdpartystatic->fetch($result);
2786
2787 $operationslog .= '<br>Thirdparty already exists with id = '.dol_escape_htmltag((string) $thirdpartystatic->id)." and name ".dol_escape_htmltag($thirdpartystatic->name);
2788 }
2789 }
2790 }
2791 } elseif ($operation['type'] == 'loadandcreatecontact') { // Search and create contact
2792 if (empty($operation['actionparam'])) {
2793 $errorforactions++;
2794 $this->error = "Action loadandcreatecontact has empty parameter. Must be 'SET:xxx' or 'EXTRACT:(body|subject):regex' to define how to extract data";
2795 $this->errors[] = $this->error;
2796 } else {
2797 $contact_static = new Contact($this->db);
2798 // Overwrite values with values extracted from source email
2799 $errorforthisaction = $this->overwritePropertiesOfObject($contact_static, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2800 if ($errorforthisaction) {
2801 $errorforactions++;
2802 } else {
2803 if (!empty($contact_static->email) && $contact_static->email != $from) {
2804 $from = $contact_static->email;
2805 }
2806
2807 $result = $contactstatic->fetch(0, null, '', $from);
2808 if ($result < 0) {
2809 $errorforactions++;
2810 $this->error = 'Error when getting contact with email ' . $from;
2811 $this->errors[] = $this->error;
2812 break;
2813 } elseif ($result == 0) {
2814 dol_syslog("Contact with email " . $from . " was not found. We try to create it.");
2815 $contactstatic = new Contact($this->db);
2816
2817 // Create contact
2818 $contactstatic->email = $from;
2819 $operationslog .= '<br>We set property email='.dol_escape_htmltag($from);
2820
2821 // Overwrite values with values extracted from source email
2822 $errorforthisaction = $this->overwritePropertiesOfObject($contactstatic, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2823
2824 if ($errorforthisaction) {
2825 $errorforactions++;
2826 } else {
2827 // Search country by name or code
2828 if (!empty($contactstatic->country)) {
2829 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
2830 $result = getCountry('', '3', $this->db, null, 1, $contactstatic->country);
2831 if ($result == 'NotDefined') {
2832 $errorforactions++;
2833 $this->error = "Error country not found by this name '" . $contactstatic->country . "'";
2834 } elseif (!($result > 0)) {
2835 $errorforactions++;
2836 $this->error = "Error when search country by this name '" . $contactstatic->country . "'";
2837 $this->errors[] = $this->db->lasterror();
2838 } else {
2839 $contactstatic->country_id = $result;
2840 $operationslog .= '<br>We set property country_id='.dol_escape_htmltag($result);
2841 }
2842 } elseif (!empty($contactstatic->country_code)) {
2843 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
2844 $result = getCountry($contactstatic->country_code, '3', $this->db);
2845 if ($result == 'NotDefined') {
2846 $errorforactions++;
2847 $this->error = "Error country not found by this code '" . $contactstatic->country_code . "'";
2848 } elseif (!($result > 0)) {
2849 $errorforactions++;
2850 $this->error = "Error when search country by this code '" . $contactstatic->country_code . "'";
2851 $this->errors[] = $this->db->lasterror();
2852 } else {
2853 $contactstatic->country_id = $result;
2854 $operationslog .= '<br>We set property country_id='.dol_escape_htmltag($result);
2855 }
2856 }
2857
2858 if (!$errorforactions) {
2859 // Search state by name or code (for country if defined)
2860 if (!empty($contactstatic->state)) {
2861 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
2862 $result = dol_getIdFromCode($this->db, $contactstatic->state, 'c_departements', 'nom', 'rowid');
2863 if (empty($result)) {
2864 $errorforactions++;
2865 $this->error = "Error state not found by this name '" . $contactstatic->state . "'";
2866 } elseif (!($result > 0)) {
2867 $errorforactions++;
2868 $this->error = "Error when search state by this name '" . $contactstatic->state . "'";
2869 $this->errors[] = $this->db->lasterror();
2870 } else {
2871 $contactstatic->state_id = $result;
2872 $operationslog .= '<br>We set property state_id='.dol_escape_htmltag($result);
2873 }
2874 } elseif (!empty($contactstatic->state_code)) {
2875 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
2876 $result = dol_getIdFromCode($this->db, $contactstatic->state_code, 'c_departements', 'code_departement', 'rowid');
2877 if (empty($result)) {
2878 $errorforactions++;
2879 $this->error = "Error state not found by this code '" . $contactstatic->state_code . "'";
2880 } elseif (!($result > 0)) {
2881 $errorforactions++;
2882 $this->error = "Error when search state by this code '" . $contactstatic->state_code . "'";
2883 $this->errors[] = $this->db->lasterror();
2884 } else {
2885 $contactstatic->state_id = $result;
2886 $operationslog .= '<br>We set property state_id='.dol_escape_htmltag($result);
2887 }
2888 }
2889 }
2890
2891 if (!$errorforactions) {
2892 $result = $contactstatic->create($user);
2893 if ($result <= 0) {
2894 $errorforactions++;
2895 $this->error = $contactstatic->error;
2896 $this->errors = $contactstatic->errors;
2897 } else {
2898 $operationslog .= '<br>Contact created -> id = '.dol_escape_htmltag((string) $contactstatic->id);
2899 }
2900 }
2901 }
2902 }
2903 }
2904 }
2905 } elseif ($operation['type'] == 'recordevent') {
2906 // Create event
2907 $actioncomm = new ActionComm($this->db);
2908
2909 $alreadycreated = $actioncomm->fetch(0, '', '', $msgid);
2910 if ($alreadycreated == 0) {
2911 $operationslog .= '<br>We did not find existing actioncomm with msgid='.$msgid;
2912
2913 if ($projectstatic->id > 0) {
2914 if ($projectfoundby) {
2915 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Project found from '.$projectfoundby);
2916 }
2917 }
2918 if ($thirdpartystatic->id > 0) {
2919 if ($thirdpartyfoundby) {
2920 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Third party found from '.$thirdpartyfoundby);
2921 }
2922 }
2923 if ($contactstatic->id > 0) {
2924 if ($contactfoundby) {
2925 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Contact/address found from '.$contactfoundby);
2926 }
2927 }
2928
2929 $description = $descriptiontitle;
2930
2931 $description = dol_concatdesc($description, $descriptionmeta);
2932 $description = dol_concatdesc($description, "-----");
2933 $description = dol_concatdesc($description, $messagetext);
2934
2935 $descriptionfull = $description;
2936 if (!getDolGlobalString('MAIN_EMAILCOLLECTOR_MAIL_WITHOUT_HEADER')) {
2937 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
2938 $descriptionfull = dol_concatdesc($descriptionfull, $header);
2939 }
2940
2941 // Insert record of emails sent
2942 $actioncomm->type_code = 'AC_OTH_AUTO'; // Type of event ('AC_OTH', 'AC_OTH_AUTO', 'AC_XXX'...)
2943 $actioncomm->code = 'AC_'.$actioncode;
2944 $actioncomm->label = $langs->trans("ActionAC_".$actioncode).' - '.$langs->trans("MailFrom").' '.$from;
2945 $actioncomm->note_private = $descriptionfull;
2946 $actioncomm->fk_project = $projectstatic->id;
2947 $actioncomm->datep = $dateemail; // date of email
2948 $actioncomm->datef = $dateemail; // date of email
2949 $actioncomm->percentage = -1; // Not applicable
2950 $actioncomm->socid = $thirdpartystatic->id;
2951 $actioncomm->contact_id = $contactstatic->id;
2952 $actioncomm->socpeopleassigned = (!empty($contactstatic->id) ? array($contactstatic->id) : array());
2953 $actioncomm->authorid = $user->id; // User saving action
2954 $actioncomm->userownerid = $user->id; // Owner of action
2955 // Fields when action is an email (content should be added into note)
2956 $actioncomm->email_msgid = $msgid;
2957 $actioncomm->email_from = $fromstring;
2958 $actioncomm->email_sender = $sender;
2959 $actioncomm->email_to = $to;
2960 $actioncomm->email_tocc = $sendtocc;
2961 $actioncomm->email_tobcc = $sendtobcc;
2962 $actioncomm->email_subject = $subject;
2963 $actioncomm->errors_to = '';
2964
2965 if (!in_array($fk_element_type, array('societe', 'contact', 'project', 'user'))) {
2966 $actioncomm->fk_element = $fk_element_id;
2967 $actioncomm->elementid = $fk_element_id;
2968 $actioncomm->elementtype = $fk_element_type;
2969 if (is_object($objectemail) && $objectemail->module) {
2970 $actioncomm->elementtype .= '@'.$objectemail->module;
2971 }
2972 }
2973
2974 //$actioncomm->extraparams = $extraparams;
2975
2976 // Overwrite values with values extracted from source email
2977 $errorforthisaction = $this->overwritePropertiesOfObject($actioncomm, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2978
2979 if ($errorforthisaction) {
2980 $errorforactions++;
2981 } else {
2982 $result = $actioncomm->create($user);
2983 if ($result <= 0) {
2984 $errorforactions++;
2985 $this->errors = $actioncomm->errors;
2986 } else {
2987 if ($fk_element_type == "ticket" && is_object($objectemail)) {
2988 if ($objectemail->status == Ticket::STATUS_CLOSED || $objectemail->status == Ticket::STATUS_CANCELED || $objectemail->status == Ticket::STATUS_NEED_MORE_INFO || $objectemail->status == Ticket::STATUS_WAITING) {
2989 if ($objectemail->fk_user_assign != null) {
2990 $res = $objectemail->setStatut(Ticket::STATUS_ASSIGNED);
2991 } else {
2992 $res = $objectemail->setStatut(Ticket::STATUS_NOT_READ);
2993 }
2994
2995 if ($res) {
2996 $operationslog .= '<br>Ticket Re-Opened successfully -> ref='.$objectemail->ref;
2997 } else {
2998 $errorforactions++;
2999 $this->error = 'Error while changing the ticket status -> ref='.$objectemail->ref;
3000 $this->errors[] = $this->error;
3001 }
3002 }
3003 if (!empty($attachments)) {
3004 // There is an attachment for the ticket -> store attachment
3005 $ticket = new Ticket($this->db);
3006 $ticket->fetch($fk_element_id);
3007 $destdir = $conf->ticket->dir_output.'/'.$ticket->ref;
3008 if (!dol_is_dir($destdir)) {
3009 dol_mkdir($destdir);
3010 }
3011 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3012 foreach ($attachments as $attachment) {
3013 $attachment->save($destdir.'/');
3014 }
3015 } else {
3016 $this->getmsg($connection, $imapemail, $destdir);
3017 }
3018 }
3019 }
3020
3021 $operationslog .= '<br>Event created -> id='.dol_escape_htmltag((string) $actioncomm->id);
3022 }
3023 }
3024 } else {
3025 $operationslog .= '<br>An event in actioncomm table already exists for the msgid = '.$msgid.' so we bypass this action.';
3026 }
3027 } elseif ($operation['type'] == 'recordjoinpiece') {
3028 $data = [];
3029 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3030 foreach ($attachments as $attachment) {
3031 if ($attachment->getName() === 'undefined') {
3032 continue;
3033 }
3034 $data[$attachment->getName()] = $attachment->getContent();
3035 }
3036 } else {
3037 $pj = getAttachments($imapemail, $connection);
3038 foreach ($pj as $key => $val) {
3039 $data[$val['filename']] = getFileData($imapemail, (string) $val['pos'], $val['type'], $connection);
3040 }
3041 }
3042 if (count($data) > 0) {
3043 $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."user WHERE email LIKE '%".$this->db->escape($from)."%'";
3044 $resql = $this->db->query($sql);
3045 if ($this->db->num_rows($resql) == 0) {
3046 $this->errors[] = "User Not allowed to add documents ({$from})";
3047 }
3048 $arrayobject = array(
3049 'propale' => array('table' => 'propal',
3050 'fields' => array('ref'),
3051 'class' => 'comm/propal/class/propal.class.php',
3052 'object' => 'Propal'),
3053 'holiday' => array('table' => 'holiday',
3054 'fields' => array('ref'),
3055 'class' => 'holiday/class/holiday.class.php',
3056 'object' => 'Holiday'),
3057 'expensereport' => array('table' => 'expensereport',
3058 'fields' => array('ref'),
3059 'class' => 'expensereport/class/expensereport.class.php',
3060 'object' => 'ExpenseReport'),
3061 'recruitment/recruitmentjobposition' => array('table' => 'recruitment_recruitmentjobposition',
3062 'fields' => array('ref'),
3063 'class' => 'recruitment/class/recruitmentjobposition.class.php',
3064 'object' => 'RecruitmentJobPosition'),
3065 'recruitment/recruitmentcandidature' => array('table' => 'recruitment_recruitmentcandidature',
3066 'fields' => array('ref'),
3067 'class' => 'recruitment/class/recruitmentcandidature.class.php',
3068 'object' => 'RecruitmentCandidature'),
3069 'societe' => array('table' => 'societe',
3070 'fields' => array('code_client', 'code_fournisseur'),
3071 'class' => 'societe/class/societe.class.php',
3072 'object' => 'Societe'),
3073 'commande' => array('table' => 'commande',
3074 'fields' => array('ref'),
3075 'class' => 'commande/class/commande.class.php',
3076 'object' => 'Commande'),
3077 'expedition' => array('table' => 'expedition',
3078 'fields' => array('ref'),
3079 'class' => 'expedition/class/expedition.class.php',
3080 'object' => 'Expedition'),
3081 'contract' => array('table' => 'contrat',
3082 'fields' => array('ref'),
3083 'class' => 'contrat/class/contrat.class.php',
3084 'object' => 'Contrat'),
3085 'fichinter' => array('table' => 'fichinter',
3086 'fields' => array('ref'),
3087 'class' => 'fichinter/class/fichinter.class.php',
3088 'object' => 'Fichinter'),
3089 'ticket' => array('table' => 'ticket',
3090 'fields' => array('ref'),
3091 'class' => 'ticket/class/ticket.class.php',
3092 'object' => 'Ticket'),
3093 'knowledgemanagement' => array('table' => 'knowledgemanagement_knowledgerecord',
3094 'fields' => array('ref'),
3095 'class' => 'knowledgemanagement/class/knowledgemanagement.class.php',
3096 'object' => 'KnowledgeRecord'),
3097 'supplier_proposal' => array('table' => 'supplier_proposal',
3098 'fields' => array('ref'),
3099 'class' => 'supplier_proposal/class/supplier_proposal.class.php',
3100 'object' => 'SupplierProposal'),
3101 'fournisseur/commande' => array('table' => 'commande_fournisseur',
3102 'fields' => array('ref', 'ref_supplier'),
3103 'class' => 'fourn/class/fournisseur.commande.class.php',
3104 'object' => 'SupplierProposal'),
3105 'facture' => array('table' => 'facture',
3106 'fields' => array('ref'),
3107 'class' => 'compta/facture/class/facture.class.php',
3108 'object' => 'Facture'),
3109 'fournisseur/facture' => array('table' => 'facture_fourn',
3110 'fields' => array('ref', 'ref_client'),
3111 'class' => 'fourn/class/fournisseur.facture.class.php',
3112 'object' => 'FactureFournisseur'),
3113 'produit' => array('table' => 'product',
3114 'fields' => array('ref'),
3115 'class' => 'product/class/product.class.php',
3116 'object' => 'Product'),
3117 'productlot' => array('table' => 'product_lot',
3118 'fields' => array('batch'),
3119 'class' => 'product/stock/class/productlot.class.php',
3120 'object' => 'Productlot'),
3121 'projet' => array('table' => 'projet',
3122 'fields' => array('ref'),
3123 'class' => 'projet/class/projet.class.php',
3124 'object' => 'Project'),
3125 'projet_task' => array('table' => 'projet_task',
3126 'fields' => array('ref'),
3127 'class' => 'projet/class/task.class.php',
3128 'object' => 'Task'),
3129 'ressource' => array('table' => 'resource',
3130 'fields' => array('ref'),
3131 'class' => 'ressource/class/dolressource.class.php',
3132 'object' => 'Dolresource'),
3133 'bom' => array('table' => 'bom_bom',
3134 'fields' => array('ref'),
3135 'class' => 'bom/class/bom.class.php',
3136 'object' => 'BOM'),
3137 'mrp' => array('table' => 'mrp_mo',
3138 'fields' => array('ref'),
3139 'class' => 'mrp/class/mo.class.php',
3140 'object' => 'Mo'),
3141 );
3142
3143 if (!is_object($hookmanager)) {
3144 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
3145 $hookmanager = new HookManager($this->db);
3146 }
3147 $hookmanager->initHooks(array('emailcolector'));
3148 $parameters = array('arrayobject' => $arrayobject);
3149 $reshook = $hookmanager->executeHooks('addmoduletoeamailcollectorjoinpiece', $parameters); // Note that $action and $object may have been modified by some hooks
3150 if ($reshook > 0) {
3151 $arrayobject = $hookmanager->resArray;
3152 }
3153
3154 $resultobj = array();
3155
3156 foreach ($arrayobject as $key => $objectdesc) {
3157 $sql = 'SELECT DISTINCT t.rowid ';
3158 $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->db->sanitize($objectdesc['table']) . ' AS t';
3159 $sql .= ' WHERE ';
3160 foreach ($objectdesc['fields'] as $field) {
3161 $sql .= "('" .$this->db->escape($subject) . "' LIKE CONCAT('%', t." . $this->db->sanitize($field) . ", '%') AND t." . $this->db->sanitize($field) . " <> '') OR ";
3162 }
3163 $sql = substr($sql, 0, -4);
3164
3165 $ressqlobj = $this->db->query($sql);
3166 if ($ressqlobj) {
3167 while ($obj = $this->db->fetch_object($ressqlobj)) {
3168 $resultobj[$key][] = $obj->rowid;
3169 }
3170 }
3171 }
3172 $dirs = array();
3173 foreach ($resultobj as $mod => $ids) {
3174 $moddesc = $arrayobject[$mod];
3175 $elementpath = $mod;
3176 dol_include_once($moddesc['class']);
3177 $objectmanaged = new $moddesc['object']($this->db);
3178 '@phan-var-force CommonObject $objectmanaged';
3179 foreach ($ids as $val) {
3180 $res = $objectmanaged->fetch($val);
3181 if ($res) {
3182 $path = ($objectmanaged->entity > 1 ? "/" . $objectmanaged->entity : '');
3183 $dirs[] = DOL_DATA_ROOT . $path . "/" . $elementpath . '/' . dol_sanitizeFileName($objectmanaged->ref) . '/';
3184 } else {
3185 $this->errors[] = 'object not found';
3186 }
3187 }
3188 }
3189 foreach ($dirs as $target) {
3190 $prefix = $this->actions[$this->id]['actionparam'];
3191 foreach ($data as $filename => $content) {
3192 $resr = saveAttachment($target, $prefix . '_' . $filename, $content);
3193 if ($resr == -1) {
3194 $this->errors[] = 'Doc not saved';
3195 }
3196 }
3197 }
3198
3199 $operationslog .= '<br>Save attachment files on disk';
3200 } else {
3201 $this->errors[] = 'no joined piece';
3202
3203 $operationslog .= '<br>No joinded files';
3204 }
3205 } elseif ($operation['type'] == 'project') {
3206 // Create project / lead
3207 $projecttocreate = new Project($this->db);
3208 $alreadycreated = $projecttocreate->fetch(0, '', '', $msgid);
3209 if ($alreadycreated == 0) {
3210 if ($thirdpartystatic->id > 0) {
3211 $projecttocreate->socid = $thirdpartystatic->id;
3212 if ($thirdpartyfoundby) {
3213 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Third party found from '.$thirdpartyfoundby);
3214 }
3215 }
3216 if ($contactstatic->id > 0) {
3217 $projecttocreate->contact_id = $contactstatic->id;
3218 if ($contactfoundby) {
3219 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Contact/address found from '.$contactfoundby);
3220 }
3221 }
3222
3223 $description = $descriptiontitle;
3224
3225 $description = dol_concatdesc($description, $descriptionmeta);
3226 $description = dol_concatdesc($description, "-----");
3227 $description = dol_concatdesc($description, $messagetext);
3228
3229 $descriptionfull = $description;
3230 if (!getDolGlobalString('MAIN_EMAILCOLLECTOR_MAIL_WITHOUT_HEADER')) {
3231 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
3232 $descriptionfull = dol_concatdesc($descriptionfull, $header);
3233 }
3234
3235 $id_opp_status = dol_getIdFromCode($this->db, 'PROSP', 'c_lead_status', 'code', 'rowid');
3236 $percent_opp_status = dol_getIdFromCode($this->db, 'PROSP', 'c_lead_status', 'code', 'percent');
3237
3238 $projecttocreate->title = $subject;
3239 $projecttocreate->date_start = $dateemail; // date of email
3240 $projecttocreate->date_end = 0;
3241 $projecttocreate->opp_status = $id_opp_status;
3242 $projecttocreate->opp_percent = $percent_opp_status;
3243 $projecttocreate->description = dol_concatdesc(dolGetFirstLineOfText(dol_string_nohtmltag($description, 2), 10), '...'.$langs->transnoentities("SeePrivateNote").'...');
3244 $projecttocreate->note_private = $descriptionfull;
3245 $projecttocreate->entity = $conf->entity;
3246 // Fields when action is an email (content should be added into agenda event)
3247 $projecttocreate->email_date = $dateemail;
3248 $projecttocreate->email_msgid = $msgid;
3249 $projecttocreate->email_from = $fromstring;
3250 $projecttocreate->email_sender = $sender;
3251 $projecttocreate->email_to = $to;
3252 $projecttocreate->email_tocc = $sendtocc;
3253 $projecttocreate->email_tobcc = $sendtobcc;
3254 $projecttocreate->email_subject = $subject;
3255 $projecttocreate->errors_to = '';
3256
3257 $savesocid = $projecttocreate->socid;
3258
3259 // Overwrite values with values extracted from source email.
3260 // This may overwrite any $projecttocreate->xxx properties.
3261 $errorforthisaction = $this->overwritePropertiesOfObject($projecttocreate, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
3262 $modele = null;
3263
3264 // Set project ref if not yet defined
3265 if (empty($projecttocreate->ref)) {
3266 // Get next Ref
3267 $defaultref = '';
3268 $modele = getDolGlobalString('PROJECT_ADDON', 'mod_project_simple');
3269
3270 // Search template files
3271 $file = '';
3272 $classname = '';
3273 $reldir = '';
3274 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3275 foreach ($dirmodels as $reldir) {
3276 $file = dol_buildpath($reldir."core/modules/project/".$modele.'.php', 0);
3277 if (file_exists($file)) {
3278 $classname = $modele;
3279 break;
3280 }
3281 }
3282
3283 if ($classname !== '') {
3284 if ($savesocid > 0) {
3285 if ($savesocid != $projecttocreate->socid) {
3286 $errorforactions++;
3287 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');
3288 }
3289 } else {
3290 if ($projecttocreate->socid > 0) {
3291 $thirdpartystatic->fetch($projecttocreate->socid);
3292 }
3293 }
3294
3295 $result = dol_include_once($reldir."core/modules/project/".$modele.'.php');
3296 $modModuleToUseForNextValue = new $classname();
3297 '@phan-var-force ModeleNumRefProjects $modModuleToUseForNextValue';
3298 $defaultref = $modModuleToUseForNextValue->getNextValue(($thirdpartystatic->id > 0 ? $thirdpartystatic : null), $projecttocreate);
3299 }
3300 $projecttocreate->ref = $defaultref;
3301 }
3302
3303
3304 if ($errorforthisaction) {
3305 $errorforactions++;
3306 } else {
3307 if (empty($projecttocreate->ref) || (is_numeric($projecttocreate->ref) && $projecttocreate->ref <= 0)) {
3308 $errorforactions++;
3309 $this->error = 'Failed to create project: Can\'t get a valid value for the field ref with numbering template = '.$modele.', thirdparty id = '.$thirdpartystatic->id;
3310
3311 $operationslog .= '<br>'.$this->error;
3312 } else {
3313 // Create project
3314 $result = $projecttocreate->create($user);
3315 if ($result <= 0) {
3316 $errorforactions++;
3317 $this->error = 'Failed to create project: '.$langs->trans($projecttocreate->error);
3318 $this->errors = $projecttocreate->errors;
3319
3320 $operationslog .= '<br>'.$this->error;
3321 } else {
3322 if ($attachments) {
3323 $destdir = $conf->project->dir_output.'/'.$projecttocreate->ref;
3324 if (!dol_is_dir($destdir)) {
3325 dol_mkdir($destdir);
3326 }
3327 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3328 foreach ($attachments as $attachment) {
3329 // $attachment->save($destdir.'/');
3330 $typeattachment = (string) $attachment->getDisposition();
3331 $filename = $attachment->getFilename();
3332 $content = $attachment->getContent();
3333 $this->saveAttachment($destdir, $filename, $content);
3334 }
3335 } else {
3336 $getMsg = $this->getmsg($connection, $imapemail, $destdir);
3337 if ($getMsg < 0) {
3338 $this->errors = array_merge($this->errors, [$this->error]);
3339 return $getMsg;
3340 }
3341 }
3342
3343 $operationslog .= '<br>Project created with attachments -> id='.dol_escape_htmltag((string) $projecttocreate->id);
3344 } else {
3345 $operationslog .= '<br>Project created without attachments -> id='.dol_escape_htmltag((string) $projecttocreate->id);
3346 }
3347 }
3348 }
3349 }
3350 } else {
3351 dol_syslog("Project already exists for msgid = ".dol_escape_htmltag($msgid).", so we do not recreate it.");
3352
3353 $operationslog .= '<br>Project already exists for msgid ='.dol_escape_htmltag($msgid);
3354 }
3355 } elseif ($operation['type'] == 'ticket') {
3356 // Create ticket
3357 $tickettocreate = new Ticket($this->db);
3358 if ($ticketalreadyexists == 0) {
3359 if ($thirdpartystatic->id > 0) {
3360 $tickettocreate->socid = $thirdpartystatic->id;
3361 $tickettocreate->fk_soc = $thirdpartystatic->id;
3362 if ($thirdpartyfoundby) {
3363 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Third party found from '.$thirdpartyfoundby);
3364 }
3365 }
3366 if ($contactstatic->id > 0) {
3367 $tickettocreate->contact_id = $contactstatic->id;
3368 if ($contactfoundby) {
3369 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Contact/address found from '.$contactfoundby);
3370 }
3371 }
3372
3373 $description = $descriptiontitle;
3374
3375 $description = dol_concatdesc($description, $descriptionmeta);
3376 $description = dol_concatdesc($description, "-----");
3377 $description = dol_concatdesc($description, $messagetext);
3378
3379 $descriptionfull = $description;
3380 if (!getDolGlobalString('MAIN_EMAILCOLLECTOR_MAIL_WITHOUT_HEADER')) {
3381 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
3382 $descriptionfull = dol_concatdesc($descriptionfull, $header);
3383 }
3384
3385 $tickettocreate->subject = $subject;
3386 $tickettocreate->message = $description;
3387 $tickettocreate->type_code = (getDolGlobalString('MAIN_EMAILCOLLECTOR_TICKET_TYPE_CODE', dol_getIdFromCode($this->db, 1, 'c_ticket_type', 'use_default', 'code', 1)));
3388 $tickettocreate->category_code = (getDolGlobalString('MAIN_EMAILCOLLECTOR_TICKET_CATEGORY_CODE', dol_getIdFromCode($this->db, 1, 'c_ticket_category', 'use_default', 'code', 1)));
3389 $tickettocreate->severity_code = (getDolGlobalString('MAIN_EMAILCOLLECTOR_TICKET_SEVERITY_CODE', dol_getIdFromCode($this->db, 1, 'c_ticket_severity', 'use_default', 'code', 1)));
3390 $tickettocreate->origin_email = $from;
3391 $tickettocreate->origin_replyto = (!empty($replyto) ? $replyto : null);
3392 $tickettocreate->origin_references = (!empty($headers['References']) ? $headers['References'] : null);
3393 $tickettocreate->fk_user_create = $user->id;
3394 $tickettocreate->datec = dol_now();
3395 $tickettocreate->fk_project = $projectstatic->id;
3396 $tickettocreate->notify_tiers_at_create = getDolGlobalInt('TICKET_CHECK_NOTIFY_THIRDPARTY_AT_CREATION');
3397 $tickettocreate->note_private = $descriptionfull;
3398 $tickettocreate->entity = $conf->entity;
3399 // Fields when action is an email (content should be added into agenda event)
3400 $tickettocreate->email_date = $dateemail;
3401 $tickettocreate->email_msgid = $msgid;
3402 $tickettocreate->email_from = $fromstring;
3403 $tickettocreate->email_sender = $sender;
3404 $tickettocreate->email_to = $to;
3405 $tickettocreate->email_tocc = $sendtocc;
3406 $tickettocreate->email_tobcc = $sendtobcc;
3407 $tickettocreate->email_subject = $subject;
3408 $tickettocreate->errors_to = '';
3409
3410 //$tickettocreate->fk_contact = $contactstatic->id;
3411
3412 $savesocid = $tickettocreate->socid;
3413
3414 // Overwrite values with values extracted from source email.
3415 // This may overwrite any $projecttocreate->xxx properties.
3416 $errorforthisaction = $this->overwritePropertiesOfObject($tickettocreate, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
3417
3418 $modele = 'UNDEFINED';
3419 // Set ticket ref if not yet defined
3420 if (empty($tickettocreate->ref)) {
3421 // Get next Ref
3422 $defaultref = '';
3423 $modele = getDolGlobalString('TICKET_ADDON', 'mod_ticket_simple');
3424
3425 // Search template files
3426 $file = '';
3427 $classname = '';
3428 $reldir = '';
3429 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3430 foreach ($dirmodels as $reldir) {
3431 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
3432 if (file_exists($file)) {
3433 $classname = $modele;
3434 break;
3435 }
3436 }
3437
3438 if ($classname !== '') {
3439 if ($savesocid > 0) {
3440 if ($savesocid != $tickettocreate->socid) {
3441 $errorforactions++;
3442 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');
3443 }
3444 } else {
3445 if ($tickettocreate->socid > 0) {
3446 $thirdpartystatic->fetch($tickettocreate->socid);
3447 }
3448 }
3449
3450 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
3451 $modModuleToUseForNextValue = new $classname();
3452 '@phan-var-force ModeleNumRefTicket $modModuleToUseForNextValue';
3453 $defaultref = $modModuleToUseForNextValue->getNextValue(($thirdpartystatic->id > 0 ? $thirdpartystatic : null), $tickettocreate);
3454 }
3455 $tickettocreate->ref = $defaultref;
3456 }
3457
3458 if ($errorforthisaction) {
3459 $errorforactions++;
3460 } else {
3461 if (is_numeric($tickettocreate->ref) && $tickettocreate->ref <= 0) {
3462 $errorforactions++;
3463 $this->error = 'Failed to create ticket: Can\'t get a valid value for the field ref with numbering template = '.$modele.', thirdparty id = '.$thirdpartystatic->id;
3464 } else {
3465 // Create ticket
3466 $tickettocreate->context['actionmsg2'] = $langs->trans("ActionAC_EMAIL_IN").' - '.$langs->trans("TICKET_CREATEInDolibarr");
3467 $tickettocreate->context['actionmsg'] = $langs->trans("ActionAC_EMAIL_IN").' - '.$langs->trans("TICKET_CREATEInDolibarr");
3468 //$tickettocreate->email_fields_no_propagate_in_actioncomm = 0;
3469
3470 // Add sender to context array to make sure that confirmation e-mail can be sent by trigger script
3471 $sender_contact = new Contact($this->db);
3472 $sender_contact->fetch(0, null, '', $from);
3473 if (!empty($sender_contact->id)) {
3474 $tickettocreate->context['contact_id'] = $sender_contact->id;
3475 }
3476
3477 $result = $tickettocreate->create($user);
3478 if ($result <= 0) {
3479 $errorforactions++;
3480 $this->error = 'Failed to create ticket: '.$langs->trans($tickettocreate->error);
3481 $this->errors = $tickettocreate->errors;
3482 } else {
3483 if ($attachments) {
3484 $destdir = $conf->ticket->dir_output.'/'.$tickettocreate->ref;
3485 if (!dol_is_dir($destdir)) {
3486 dol_mkdir($destdir);
3487 }
3488 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3489 foreach ($attachments as $attachment) {
3490 // $attachment->save($destdir.'/');
3491 $typeattachment = (string) $attachment->getDisposition();
3492 $filename = $attachment->getName();
3493 $content = $attachment->getContent();
3494 $this->saveAttachment($destdir, $filename, $content);
3495 }
3496 } else {
3497 $getMsg = $this->getmsg($connection, $imapemail, $destdir);
3498 if ($getMsg < 0) {
3499 $this->errors = array_merge($this->errors, [$this->error]);
3500 return $getMsg;
3501 }
3502 }
3503
3504 $operationslog .= '<br>Ticket created with attachments -> id='.dol_escape_htmltag((string) $tickettocreate->id);
3505 } else {
3506 $operationslog .= '<br>Ticket created without attachments -> id='.dol_escape_htmltag((string) $tickettocreate->id);
3507 }
3508 }
3509 }
3510 }
3511 } else {
3512 // Do nothing in action ticket if ticket already exists.
3513 // Note that if we want to add a new event to a ticket, we should have a collector with action "Record event in agenda" or hope the type of action 'ticket' was
3514 // already replaced automatically by operation "recordevent".
3515 $operationslog .= '<br>Ticket already exists, so we bypass action "ticket"';
3516 }
3517 } elseif ($operation['type'] == 'candidature') {
3518 // Create candidature
3519 $candidaturetocreate = new RecruitmentCandidature($this->db);
3520
3521 $alreadycreated = $candidaturetocreate->fetch(0, '', $msgid);
3522 if ($alreadycreated == 0) {
3523 $description = $descriptiontitle;
3524 $description = dol_concatdesc($description, "-----");
3525 $description = dol_concatdesc($description, $descriptionmeta);
3526 $description = dol_concatdesc($description, "-----");
3527 $description = dol_concatdesc($description, $messagetext);
3528
3529 $descriptionfull = $description;
3530 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
3531 $descriptionfull = dol_concatdesc($descriptionfull, $header);
3532
3533 $candidaturetocreate->subject = $subject;
3534 $candidaturetocreate->message = $description;
3535 $candidaturetocreate->type_code = 0;
3536 $candidaturetocreate->category_code = null;
3537 $candidaturetocreate->severity_code = null;
3538 $candidaturetocreate->email = $from;
3539 //$candidaturetocreate->lastname = $langs->trans("Anonymous").' - '.$from;
3540 $candidaturetocreate->fk_user_creat = $user->id;
3541 $candidaturetocreate->date_creation = dol_now();
3542 $candidaturetocreate->fk_project = $projectstatic->id;
3543 $candidaturetocreate->description = $description;
3544 $candidaturetocreate->note_private = $descriptionfull;
3545 $candidaturetocreate->entity = $conf->entity;
3546 $candidaturetocreate->email_msgid = $msgid;
3547 $candidaturetocreate->email_date = $dateemail; // date of email
3548 $candidaturetocreate->status = $candidaturetocreate::STATUS_DRAFT;
3549 //$candidaturetocreate->fk_contact = $contactstatic->id;
3550
3551 // Overwrite values with values extracted from source email.
3552 // This may overwrite any $projecttocreate->xxx properties.
3553 $errorforthisaction = $this->overwritePropertiesOfObject($candidaturetocreate, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
3554
3555 // Set candidature ref if not yet defined
3556 /*if (empty($candidaturetocreate->ref)) We do not need this because we create object in draft status
3557 {
3558 // Get next Ref
3559 $defaultref = '';
3560 $modele = empty($conf->global->CANDIDATURE_ADDON) ? 'mod_candidature_simple' : $conf->global->CANDIDATURE_ADDON;
3561
3562 // Search template files
3563 $file = ''; $classname = ''; $filefound = 0; $reldir = '';
3564 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3565 foreach ($dirmodels as $reldir)
3566 {
3567 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
3568 if (file_exists($file)) {
3569 $filefound = 1;
3570 $classname = $modele;
3571 break;
3572 }
3573 }
3574
3575 if ($filefound) {
3576 if ($savesocid > 0) {
3577 if ($savesocid != $candidaturetocreate->socid) {
3578 $errorforactions++;
3579 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');
3580 }
3581 } else {
3582 if ($candidaturetocreate->socid > 0)
3583 {
3584 $thirdpartystatic->fetch($candidaturetocreate->socid);
3585 }
3586 }
3587
3588 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
3589 $modModuleToUseForNextValue = new $classname;
3590 '@phan-var-force ModeleNumRefTicket $modModuleToUseForNextValue';
3591 $defaultref = $modModuleToUseForNextValue->getNextValue(($thirdpartystatic->id > 0 ? $thirdpartystatic : null), $tickettocreate);
3592 }
3593 $candidaturetocreate->ref = $defaultref;
3594 }*/
3595
3596 if ($errorforthisaction) {
3597 $errorforactions++;
3598 } else {
3599 // Create project
3600 $result = $candidaturetocreate->create($user);
3601 if ($result <= 0) {
3602 $errorforactions++;
3603 $this->error = 'Failed to create candidature: '.implode(', ', $candidaturetocreate->errors);
3604 $this->errors = $candidaturetocreate->errors;
3605 }
3606
3607 $operationslog .= '<br>Candidature created without attachments -> id='.dol_escape_htmltag((string) $candidaturetocreate->id);
3608 }
3609 }
3610 } elseif (substr($operation['type'], 0, 4) == 'hook') {
3611 // Create event specific on hook
3612 // this code action is hook..... for support this call
3613 if (!is_object($hookmanager)) {
3614 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
3615 $hookmanager = new HookManager($this->db);
3616 }
3617 $hookmanager->initHooks(['emailcolector']);
3618
3619 $parameters = array(
3620 'connection' => $connection,
3621 'imapemail' => $imapemail,
3622 'overview' => $overview,
3623
3624 'from' => $from,
3625 'fromtext' => $fromtext,
3626
3627 'actionparam' => $operation['actionparam'],
3628
3629 'thirdpartyid' => $thirdpartyid,
3630 'objectid' => $objectid,
3631 'objectemail' => $objectemail,
3632
3633 'messagetext' => $messagetext,
3634 'subject' => $subject,
3635 'header' => $header,
3636 'attachments' => $attachments,
3637 );
3638 $reshook = $hookmanager->executeHooks('doCollectImapOneCollector', $parameters, $this, $operation['type']);
3639
3640 if ($reshook < 0) {
3641 $errorforthisaction++;
3642 $this->error = $hookmanager->resPrint;
3643 }
3644 if ($errorforthisaction) {
3645 $errorforactions++;
3646 $operationslog .= '<br>Hook doCollectImapOneCollector executed with error';
3647 } else {
3648 $operationslog .= '<br>Hook doCollectImapOneCollector executed without error';
3649 }
3650 }
3651
3652 if (!$errorforactions) {
3653 $nbactiondoneforemail++;
3654 }
3655 }
3656 }
3657
3658 // Error for email or not ?
3659 if (!$errorforactions) {
3660 if (!empty($targetdir)) {
3661 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3662 // Move mail using PHP-IMAP
3663 dol_syslog("EmailCollector::doCollectOneCollector move message ".($imapemail->getHeader()->get('subject'))." to ".$targetdir, LOG_DEBUG);
3664 $operationslog .= '<br>Move mail '.($this->uidAsString($imapemail)).' - '.$msgid.' - '.$imapemail->getHeader()->get('subject').' to '.$targetdir;
3665
3666 $arrayofemailtodelete[$this->uidAsString($imapemail)] = $imapemail;
3667 // Note: Real move is done later using $arrayofemailtodelete
3668 } else {
3669 dol_syslog("EmailCollector::doCollectOneCollector move message ".($this->uidAsString($imapemail))." to ".$connectstringtarget, LOG_DEBUG);
3670 $operationslog .= '<br>Move mail '.($this->uidAsString($imapemail)).' - '.$msgid;
3671
3672 $arrayofemailtodelete[$imapemail] = $msgid;
3673 // Note: Real move is done later using $arrayofemailtodelete
3674 }
3675 } else {
3676 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3677 dol_syslog("EmailCollector::doCollectOneCollector message ".($this->uidAsString($imapemail))." '".($imapemail->getHeader()->get('subject'))."' using this->host=".$this->host.", this->access_type=".$this->acces_type." was set to read", LOG_DEBUG);
3678 } else {
3679 dol_syslog("EmailCollector::doCollectOneCollector message ".($this->uidAsString($imapemail))." to ".$connectstringtarget." was set to read", LOG_DEBUG);
3680 }
3681 }
3682 } else {
3683 $errorforemail++;
3684 }
3685
3686
3687 unset($objectemail);
3688 unset($projectstatic);
3689 unset($thirdpartystatic);
3690 unset($contactstatic);
3691
3692 $nbemailprocessed++;
3693
3694 if (!$errorforemail) {
3695 $nbactiondone += $nbactiondoneforemail;
3696 $nbemailok++;
3697
3698 if (empty($mode)) {
3699 $this->db->commit();
3700 } else {
3701 $this->db->rollback();
3702 }
3703
3704 // Stop the loop to process email if we reach maximum collected per collect
3705 if ($this->maxemailpercollect > 0 && $nbemailok >= $this->maxemailpercollect) {
3706 dol_syslog("EmailCollect::doCollectOneCollector We reach maximum of ".$nbemailok." collected with success, so we stop this collector now.");
3707 $datelastok = strtotime($headers['Date']); // Set datetime
3708 break;
3709 }
3710 } else {
3711 $error++;
3712
3713 $this->db->rollback();
3714 }
3715 }
3716
3717 $output = $langs->trans('XEmailsDoneYActionsDone', $nbemailprocessed, $nbemailok, $nbactiondone);
3718
3719 dol_syslog("End of loop on emails", LOG_INFO, -1);
3720 } else {
3721 $langs->load("admin");
3722 $output = $langs->trans('NoNewEmailToProcess');
3723 $output .= ' (defaultlang='.$langs->defaultlang.')';
3724 }
3725
3726 // Disconnect
3727 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3728 // We sort to move/delete array with the more recent first (with higher number) so renumbering does not affect number of others to delete
3729 krsort($arrayofemailtodelete, SORT_NUMERIC);
3730
3731 foreach ($arrayofemailtodelete as $imapemailnum => $imapemail) {
3732 dol_syslog("EmailCollect::doCollectOneCollector delete email ".$imapemailnum);
3733
3734 $operationslog .= "<br> move email ".$imapemailnum.($mode > 0 ? ' (test)' : '');
3735
3736 if (empty($mode) && empty($error)) {
3737 $tmptargetdir = $targetdir;
3738 if (!getDolGlobalString('MAIL_DISABLE_UTF7_ENCODE_OF_DIR')) {
3739 $tmptargetdir = $this->getEncodedUtf7($targetdir);
3740 }
3741
3742 $result = 0;
3743 try {
3744 $result = $imapemail->move($tmptargetdir, false);
3745 } catch (Exception $e) {
3746 // Nothing to do. $result will remain 0
3747 $operationslog .= '<br>Exception !!!! '.$e->getMessage();
3748 }
3749 if (empty($result)) {
3750 dol_syslog("Failed to move email into target directory ".$targetdir);
3751 $operationslog .= '<br>Failed to move email into target directory '.$targetdir;
3752 $error++;
3753 }
3754 }
3755 }
3756
3757 if (empty($mode) && empty($error)) {
3758 dol_syslog("Expunge", LOG_DEBUG);
3759 $operationslog .= "<br>Expunge";
3760
3761 $client->expunge(); // To validate all moves
3762 }
3763
3764 $client->disconnect();
3765 } else {
3766 foreach ($arrayofemailtodelete as $imapemail => $msgid) {
3767 dol_syslog("EmailCollect::doCollectOneCollector delete email ".$imapemail." ".$msgid);
3768
3769 $operationslog .= "<br> delete email ".$imapemail." ".$msgid.($mode > 0 ? ' (test)' : '');
3770
3771 if (empty($mode) && empty($error)) {
3772 $res = imap_mail_move($connection, $imapemail, $targetdir, CP_UID);
3773 if (!$res) {
3774 // $errorforemail++; // Not in loop, not needed, not initialised
3775 $this->error = imap_last_error();
3776 $this->errors[] = $this->error;
3777
3778 $operationslog .= '<br>Error in move '.$this->error;
3779
3780 dol_syslog(imap_last_error());
3781 }
3782 }
3783 }
3784
3785 if (empty($mode) && empty($error)) {
3786 dol_syslog("Expunge", LOG_DEBUG);
3787 $operationslog .= "<br>Expunge";
3788
3789 imap_expunge($connection); // To validate all moves
3790 }
3791 imap_close($connection);
3792 }
3793
3794 $this->datelastresult = $now;
3795 $this->lastresult = $output;
3796 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3797 $this->debuginfo .= 'IMAP search array used : '.$search;
3798 } else {
3799 $this->debuginfo .= 'IMAP search string used : '.$search;
3800 }
3801 if ($searchhead) {
3802 $this->debuginfo .= '<br>Then search string into email header : '.dol_escape_htmltag($searchhead);
3803 }
3804 if ($operationslog) {
3805 $this->debuginfo .= $operationslog;
3806 }
3807
3808 if (empty($error) && empty($mode)) {
3809 $this->datelastok = $datelastok;
3810 }
3811
3812 if (!empty($this->errors)) {
3813 $this->lastresult .= "<br>".implode("<br>", $this->errors);
3814 }
3815 $this->codelastresult = ($error ? 'KO' : 'OK');
3816
3817 if (empty($mode)) {
3818 $this->update($user);
3819 }
3820
3821 dol_syslog("EmailCollector::doCollectOneCollector end error=".$error, LOG_INFO);
3822
3823 return $error ? -1 : 1;
3824 }
3825
3826
3827
3828 // Loop to get part html and plain. Code found on PHP imap_fetchstructure documentation
3829
3838 private function getmsg($mbox, $mid, $destdir = ''): int
3839 {
3840 // input $mbox = IMAP stream, $mid = message id
3841 // output all the following:
3842 global $charset, $htmlmsg, $plainmsg, $attachments;
3843 $htmlmsg = $plainmsg = $charset = '';
3844 $attachments = array();
3845
3846 // HEADER
3847 //$h = imap_header($mbox,$mid);
3848 // add code here to get date, from, to, cc, subject...
3849
3850 // BODY
3851 $s = imap_fetchstructure($mbox, $mid, FT_UID);
3852 if ($s === false) {
3853 $this->errors = array_merge($this->errors, [imap_last_error()]);
3854 return -1;
3855 }
3856
3857 if (empty($s->parts)) {
3858 // simple
3859 $this->getpart($mbox, $mid, $s, '0'); // pass '0' as part-number
3860 } else {
3861 // multipart: cycle through each part
3862 foreach ($s->parts as $partno0 => $p) {
3863 $this->getpart($mbox, $mid, $p, (string) ($partno0 + 1), $destdir);
3864 }
3865 }
3866
3867 return 1;
3868 }
3869
3870 /* partno string
3871 0 multipart/mixed
3872 1 multipart/alternative
3873 1.1 text/plain
3874 1.2 text/html
3875 2 message/rfc822
3876 2 multipart/mixed
3877 2.1 multipart/alternative
3878 2.1.1 text/plain
3879 2.1.2 text/html
3880 2.2 message/rfc822
3881 2.2 multipart/alternative
3882 2.2.1 text/plain
3883 2.2.2 text/html
3884 */
3885
3896 private function getpart($mbox, $mid, $p, $partno, $destdir = '')
3897 {
3898 // $partno = '1', '2', '2.1', '2.1.3', etc for multipart, 0 if simple
3899 global $htmlmsg, $plainmsg, $charset, $attachments;
3900
3901 // DECODE DATA
3902 $data = ($partno) ?
3903 imap_fetchbody($mbox, $mid, $partno, FT_UID) : // multipart
3904 imap_body($mbox, $mid, FT_UID); // simple
3905 // Any part may be encoded, even plain text messages, so check everything.
3906 if ($p->encoding == 4) {
3907 $data = quoted_printable_decode($data);
3908 } elseif ($p->encoding == 3) {
3909 $data = base64_decode($data);
3910 }
3911
3912 // PARAMETERS
3913 // get all parameters, like charset, filenames of attachments, etc.
3914 $params = array();
3915 if ($p->parameters) {
3916 foreach ($p->parameters as $x) {
3917 $params[strtolower($x->attribute)] = $x->value;
3918 }
3919 }
3920 if (!empty($p->dparameters)) {
3921 foreach ($p->dparameters as $x) {
3922 $params[strtolower($x->attribute)] = $x->value;
3923 }
3924 }
3925 '@phan-var-force array{filename?:string,name?:string,charset?:string} $params';
3926
3927 // ATTACHMENT
3928 // Any part with a filename is an attachment,
3929 // so an attached text file (type 0) is not mistaken as the message.
3930 if (!empty($params['filename']) || !empty($params['name'])) {
3931 // filename may be given as 'Filename' or 'Name' or both
3932 $filename = $params['filename'] ?? $params['name'];
3933 // filename may be encoded, so see imap_mime_header_decode()
3934 $attachments[$filename] = $data; // this is a problem if two files have same name
3935
3936 if (strlen($destdir)) {
3937 if (substr($destdir, -1) != '/') {
3938 $destdir .= '/';
3939 }
3940
3941 // Get file name (with extension)
3942 $file_name_complete = $filename;
3943 $destination = $destdir.$file_name_complete;
3944
3945 // Extract file extension
3946 $extension = pathinfo($file_name_complete, PATHINFO_EXTENSION);
3947
3948 // Extract file name without extension
3949 $file_name = pathinfo($file_name_complete, PATHINFO_FILENAME);
3950
3951 // Save an original file name variable to track while renaming if file already exists
3952 $file_name_original = $file_name;
3953
3954 // Increment file name by 1
3955 $num = 1;
3956
3961 while (file_exists($destdir.$file_name.".".$extension)) {
3962 $file_name = $file_name_original . ' (' . $num . ')';
3963 $file_name_complete = $file_name . "." . $extension;
3964 $destination = $destdir.$file_name_complete;
3965 $num++;
3966 }
3967
3968 $destination = dol_sanitizePathName($destination);
3969
3970 file_put_contents($destination, $data);
3971 }
3972 }
3973
3974 // TEXT
3975 if ($p->type == 0 && $data) {
3976 if (!empty($params['charset'])) {
3977 $data = $this->convertStringEncoding($data, $params['charset']);
3978 }
3979 // Messages may be split in different parts because of inline attachments,
3980 // so append parts together with blank row.
3981 if (strtolower($p->subtype) == 'plain') {
3982 $plainmsg .= trim($data)."\n\n";
3983 } else {
3984 $htmlmsg .= $data."<br><br>";
3985 }
3986 $charset = $params['charset']; // assume all parts are same charset
3987 } elseif ($p->type == 2 && $data) {
3988 // EMBEDDED MESSAGE
3989 // Many bounce notifications embed the original message as type 2,
3990 // but AOL uses type 1 (multipart), which is not handled here.
3991 // There are no PHP functions to parse embedded messages,
3992 // so this just appends the raw source to the main message.
3993 if (!empty($params['charset'])) {
3994 $data = $this->convertStringEncoding($data, $params['charset']);
3995 }
3996 $plainmsg .= $data."\n\n";
3997 }
3998
3999 // SUBPART RECURSION
4000 if (!empty($p->parts)) {
4001 foreach ($p->parts as $partno0 => $p2) {
4002 $this->getpart($mbox, $mid, $p2, $partno.'.'.($partno0 + 1), $destdir); // 1.2, 1.2.1, etc.
4003 }
4004 }
4005 }
4006
4016 protected function convertStringEncoding($string, $fromEncoding, $toEncoding = 'UTF-8')
4017 {
4018 if (!$string || $fromEncoding == $toEncoding) {
4019 return $string;
4020 }
4021 $convertedString = function_exists('iconv') ? @iconv($fromEncoding, $toEncoding.'//IGNORE', $string) : null;
4022 if (!$convertedString && extension_loaded('mbstring')) {
4023 $convertedString = @mb_convert_encoding($string, $toEncoding, $fromEncoding);
4024 }
4025 if (!$convertedString) {
4026 throw new Exception('Mime string encoding conversion failed');
4027 }
4028 return $convertedString;
4029 }
4030
4041 protected function decodeSMTPSubject($subject)
4042 {
4043 // Decode $overview[0]->subject according to RFC2047
4044 // Can use also imap_mime_header_decode($str)
4045 // Can use also mb_decode_mimeheader($str)
4046 // Can use also iconv_mime_decode($str, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8')
4047 if (function_exists('imap_mime_header_decode') && function_exists('iconv_mime_decode')) {
4048 $elements = imap_mime_header_decode($subject);
4049 $newstring = '';
4050 if (!empty($elements)) {
4051 $num = count($elements);
4052 for ($i = 0; $i < $num; $i++) {
4053 $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));
4054 $newstring .= $stringinutf8;
4055 }
4056 $subject = $newstring;
4057 }
4058 } elseif (!function_exists('mb_decode_mimeheader')) {
4059 $subject = mb_decode_mimeheader($subject);
4060 } elseif (function_exists('iconv_mime_decode')) {
4061 $subject = iconv_mime_decode($subject, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8');
4062 }
4063
4064 return $subject;
4065 }
4066
4075 private function saveAttachment($destdir, $filename, $content)
4076 {
4077 require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';
4078
4079 $tmparraysize = getDefaultImageSizes();
4080 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
4081 $maxheightsmall = $tmparraysize['maxheightsmall'];
4082 $maxwidthmini = $tmparraysize['maxwidthmini'];
4083 $maxheightmini = $tmparraysize['maxheightmini'];
4084 $quality = $tmparraysize['quality'];
4085
4086 file_put_contents($destdir.'/'.$filename, $content);
4087 if (image_format_supported($filename) == 1) {
4088 // Create thumbs
4089 vignette($destdir.'/'.$filename, $maxwidthsmall, $maxheightsmall, '_small', $quality, "thumbs");
4090 // Create mini thumbs for image (Ratio is near 16/9)
4091 vignette($destdir.'/'.$filename, $maxwidthmini, $maxheightmini, '_mini', $quality, "thumbs");
4092 }
4093 addFileIntoDatabaseIndex($destdir, $filename);
4094 }
4095
4102 protected function uidAsString($imapemail)
4103 {
4104 if (is_object($imapemail)) {
4105 return $imapemail->getAttributes()["uid"];
4106 } else {
4107 return (string) $imapemail;
4108 }
4109 }
4110}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:67
$object ref
Definition info.php:90
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,...
createCommon(User $user, $notrigger=0)
Create object in the database.
updateCommon(User $user, $notrigger=0)
Update object into database.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
fetchCommon($id, $ref=null, $morewhere='', $noextrafields=0)
Load object in memory from the database.
deleteCommon(User $user, $notrigger=0, $forcechilddeletion=0)
Delete object in 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.
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.
getEncodedUtf7($str)
Convert str to UTF-7 imap.
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.
update(User $user, $notrigger=0)
Update object into database.
saveAttachment($destdir, $filename, $content)
saveAttachment
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 information 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 optionally the picto)
create(User $user, $notrigger=0)
Create object into database.
doCollect()
Action executed by scheduler CAN BE A CRON TASK.
getLibStatut($mode=0)
Return label of the status.
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.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
getCountry($searchkey, $withcode='', $dbtouse=null, $outputlangs=null, $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
dol_stringtotime($string, $gm=1)
Convert a string date into a GM Timestamps date Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not s...
Definition date.lib.php:431
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.
addFileIntoDatabaseIndex($dir, $file, $fullpathorig='', $mode='uploaded', $setsharekey=0, $object=null, $forceFullTextIndexation='')
Add a file into database index.
dol_is_dir($folder)
Test if filename is a directory.
removeEmoji($text, $allowedemoji=1)
Remove EMoji from email content.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
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.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
utf8_valid($str)
Check if a string is in UTF8.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
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...
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
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.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return a 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...
vignette($file, $maxWidth=160, $maxHeight=120, $extName='_small', $quality=50, $outdir='thumbs', $targetformat=0)
Create a thumbnail from an image file (Supported extensions are gif, jpg, png and bmp).
if(!defined( 'IMAGETYPE_WEBP')) getDefaultImageSizes()
Return default values for image sizes.
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
Class to generate the form for creating a new ticket.
getSupportedOauth2Array()
Return array of tabs to use on pages to setup cron module.
dolEncrypt($chain, $key='', $ciphering='', $forceseed='')
Encode a string with a symmetric encryption.
dolDecrypt($chain, $key='')
Decode a string with a symmetric encryption.