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' => empty($headers['Date']) ? false : 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 // $overview['date'] is a DateTime/Carbon object when using the PHPIMAP driver, not a timestamp, so it must be converted first
1975 $overviewdate = ($overview['date'] instanceof DateTimeInterface) ? $overview['date']->getTimestamp() : $overview['date'];
1976 $dateformated = dol_print_date($overviewdate, 'dayrfc', 'gmt');
1977 dol_syslog("msgid=".$overview['message_id']." date=".$dateformated." from=".$overview['from']." to=".$overview['to']." subject=".$overview['subject']);
1978
1979 // Removed emojis
1980 $overview['subject'] = removeEmoji($overview['subject'], getDolGlobalInt('MAIN_EMAIL_COLLECTOR_ACCEPT_EMOJIS', 1));
1981 } else {
1982 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);
1983
1984 $overview[0]->subject = $this->decodeSMTPSubject($overview[0]->subject);
1985
1986 $overview[0]->from = $this->decodeSMTPSubject($overview[0]->from);
1987
1988 // Removed emojis
1989 $overview[0]->subject = removeEmoji($overview[0]->subject, getDolGlobalInt('MAIN_EMAIL_COLLECTOR_ACCEPT_EMOJIS', 1));
1990 }
1991 // GET IMAP email structure/content
1992 global $htmlmsg, $plainmsg, $charset, $attachments;
1993
1994 if (getDolGlobalInt('MAIN_IMAP_USE_PHPIMAP')) {
1996 '@phan-var-force Webklex\PHPIMAP\Message $imapemail';
1997 if ($imapemail->hasHTMLBody()) {
1998 $htmlmsg = $imapemail->getHTMLBody();
1999 }
2000 if ($imapemail->hasTextBody() && $imapemail->getTextBody() != "\n") {
2001 $plainmsg = $imapemail->getTextBody();
2002 }
2003 if ($imapemail->hasAttachments()) {
2004 $attachments = $imapemail->getAttachments()->all();
2005 } else {
2006 $attachments = [];
2007 }
2008 } else {
2009 $getMsg = $this->getmsg($connection, $imapemail); // This set global var $charset, $htmlmsg, $plainmsg, $attachments
2010 if ($getMsg < 0) {
2011 $this->errors = array_merge($this->errors, [$this->error]);
2012 return $getMsg;
2013 }
2014 }
2015 '@phan-var-force Webklex\PHPIMAP\Attachment[] $attachments';
2016
2017 //print $plainmsg;
2018 //var_dump($plainmsg); exit;
2019
2020 //$htmlmsg,$plainmsg,$charset,$attachments
2021 $messagetext = $plainmsg ? $plainmsg : dol_string_nohtmltag((string) $htmlmsg, 0);
2022 // Removed emojis
2023
2024 if (utf8_valid($messagetext)) {
2025 $messagetext = removeEmoji($messagetext, getDolGlobalInt('MAIN_EMAIL_COLLECTOR_ACCEPT_EMOJIS', 1));
2026 } else {
2027 $operationslog .= '<br>Discarded - Email body is not valid utf8';
2028 dol_syslog(" Discarded - Email body is not valid utf8");
2029 continue; // Exclude email
2030 }
2031
2032 if (!empty($searchfilterexcludebodyarray)) {
2033 foreach ($searchfilterexcludebodyarray as $searchfilterexcludebody) {
2034 if (preg_match('/'.preg_quote($searchfilterexcludebody, '/').'/ms', $messagetext)) {
2035 $nbemailprocessed++;
2036 $operationslog .= '<br>Discarded - Email body contains string '.$searchfilterexcludebody;
2037 dol_syslog(" Discarded - Email body contains string ".$searchfilterexcludebody);
2038 continue 2; // Exclude email
2039 }
2040 }
2041 }
2042
2043 //var_dump($plainmsg);
2044 //var_dump($htmlmsg);
2045 //var_dump($messagetext);
2046 //var_dump($charset);
2047 //var_dump($attachments);
2048 //exit;
2049
2050 // Parse IMAP email structure
2051 /*
2052 $structure = imap_fetchstructure($connection, $imapemail, FT_UID);
2053
2054 $partplain = $parthtml = -1;
2055 $encodingplain = $encodinghtml = '';
2056
2057 $result = createPartArray($structure, '');
2058
2059 foreach($result as $part)
2060 {
2061 // $part['part_object']->type seems 0 for content
2062 // $part['part_object']->type seems 5 for attachment
2063 if (empty($part['part_object'])) continue;
2064 if ($part['part_object']->subtype == 'HTML')
2065 {
2066 $parthtml=$part['part_number'];
2067 if ($part['part_object']->encoding == 4)
2068 {
2069 $encodinghtml = 'aaa';
2070 }
2071 }
2072 if ($part['part_object']->subtype == 'PLAIN')
2073 {
2074 $partplain=$part['part_number'];
2075 if ($part['part_object']->encoding == 4)
2076 {
2077 $encodingplain = 'rr';
2078 }
2079 }
2080 }
2081 //var_dump($result);
2082 //var_dump($partplain);
2083 //var_dump($parthtml);
2084
2085 //var_dump($structure);
2086 //var_dump($parthtml);
2087 //var_dump($partplain);
2088
2089 $messagetext = imap_fetchbody($connection, $imapemail, ($parthtml != '-1' ? $parthtml : ($partplain != '-1' ? $partplain : 1)), FT_PEEK|FTP_UID);
2090 */
2091
2092 //var_dump($messagetext);
2093 //var_dump($structure->parts[0]->parts);
2094 //print $header;
2095 //print $messagetext;
2096 //exit;
2097
2098 $fromstring = '';
2099 $replytostring = '';
2100
2101 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
2102 $fromstring = $overview['from'];
2103 $replytostring = empty($overview['in_reply-to']) ? $headers['Reply-To'] : $overview['in_reply-to'];
2104
2105 $sender = $overview['sender'];
2106 $to = $overview['to'];
2107 $sendtocc = empty($overview['cc']) ? '' : $overview['cc'];
2108 $sendtobcc = empty($overview['bcc']) ? '' : $overview['bcc'];
2109
2110 $tmpdate = $overview['date']->toDate(); // @phan-suppress-current-line PhanPluginUnknownObjectMethodCall,PhanUndeclaredMethod
2111 $tmptimezone = $tmpdate->getTimezone()->getName(); // @phan-suppress-current-line PhanPluginUnknownObjectMethodCall
2112
2113 $dateemail = dol_stringtotime((string) $overview['date'], 'gmt'); // if $overview['timezone'] is "+00:00"
2114 if (preg_match('/^([+\-])(\d\d):(\d\d)/', $tmptimezone, $reg)) {
2115 if ($reg[1] == '+' && ($reg[2] != '00' || $reg[3] != '00')) {
2116 $dateemail -= (3600 * (int) $reg[2]);
2117 $dateemail -= (60 * (int) $reg[3]);
2118 }
2119 if ($reg[1] == '-' && ($reg[2] != '00' || $reg[3] != '00')) {
2120 $dateemail += (3600 * (int) $reg[2]);
2121 $dateemail += (60 * (int) $reg[3]);
2122 }
2123 }
2124 $subject = $overview['subject'];
2125 } else {
2126 $fromstring = $overview[0]->from;
2127 $replytostring = (!empty($overview['in_reply-to']) ? $overview['in_reply-to'] : (!empty($headers['Reply-To']) ? $headers['Reply-To'] : "")) ;
2128
2129 $sender = !empty($overview[0]->sender) ? $overview[0]->sender : '';
2130 $to = $overview[0]->to;
2131 $sendtocc = !empty($overview[0]->cc) ? $overview[0]->cc : '';
2132 $sendtobcc = !empty($overview[0]->bcc) ? $overview[0]->bcc : '';
2133 $dateemail = dol_stringtotime((string) $overview[0]->udate, 'gmt');
2134 $subject = $overview[0]->subject;
2135 //var_dump($msgid);exit;
2136 }
2137
2138 if (!empty($searchfilterexcludesubjectarray)) {
2139 foreach ($searchfilterexcludesubjectarray as $searchfilterexcludesubject) {
2140 if (preg_match('/'.preg_quote($searchfilterexcludesubject, '/').'/ms', $subject)) {
2141 $nbemailprocessed++;
2142 $operationslog .= '<br>Discarded - Email subject contains string '.$searchfilterexcludesubject;
2143 dol_syslog(" Discarded - Email subject contains string ".$searchfilterexcludesubject);
2144 continue 2; // Exclude email
2145 }
2146 }
2147 }
2148
2149 $reg = array();
2150 if (preg_match('/^(.*)<(.*)>$/', $fromstring, $reg)) {
2151 $from = $reg[2];
2152 $fromtext = $reg[1];
2153 } else {
2154 $from = $fromstring;
2155 $fromtext = '';
2156 }
2157 if (preg_match('/^(.*)<(.*)>$/', $replytostring, $reg)) {
2158 $replyto = $reg[2];
2159 $replytotext = $reg[1];
2160 } else {
2161 $replyto = $replytostring;
2162 $replytotext = '';
2163 }
2164 $fk_element_id = 0;
2165 $fk_element_type = '';
2166
2167
2168 $this->db->begin();
2169
2170 $contactid = 0;
2171 $thirdpartyid = 0;
2172 $projectid = 0;
2173 $ticketid = 0;
2174
2175 // Analyze TrackId in field References (already analyzed previously into the "To:" and "Message-Id").
2176 // For example:
2177 // References: <1542377954.SMTPs-dolibarr-thi649@8f6014fde11ec6cdec9a822234fc557e>
2178 // References: <1542377954.SMTPs-dolibarr-tic649@8f6014fde11ec6cdec9a822234fc557e>
2179 // References: <1542377954.SMTPs-dolibarr-abc649@8f6014fde11ec6cdec9a822234fc557e>
2180 $trackid = '';
2181 $objectid = 0;
2182 $objectemail = null;
2183
2184 $reg = array();
2185 $arrayofreferences = array();
2186 if (!empty($headers['References'])) {
2187 $arrayofreferences = preg_split('/(,|\s+)/', $headers['References']);
2188 }
2189 if (!in_array('<'.$msgid.'>', $arrayofreferences)) {
2190 $arrayofreferences = array_merge($arrayofreferences, array('<'.$msgid.'>'));
2191 }
2192 // var_dump($headers['References']);
2193 // var_dump($arrayofreferences);
2194
2195 // We loop on References, but as soon as we found one that allow us to find an existing object,
2196 // we do a break (See line with comment "Exit loop of references").
2197 foreach ($arrayofreferences as $reference) {
2198 //print "Process mail ".$iforemailloop." email_msgid ".$msgid.", date ".dol_print_date($dateemail, 'dayhour', 'gmt').", subject ".$subject.", reference ".dol_escape_htmltag($reference)."<br>\n";
2199 if (!empty($trackidfoundintorecipienttype)) {
2200 $resultsearchtrackid = -1; // trackid found
2201 $reg[1] = $trackidfoundintorecipienttype;
2202 $reg[2] = $trackidfoundintorecipientid;
2203 } elseif (!empty($trackidfoundintomsgidtype)) {
2204 $resultsearchtrackid = -1; // trackid found
2205 $reg[1] = $trackidfoundintomsgidtype;
2206 $reg[2] = $trackidfoundintomsgidid;
2207 } else {
2208 $resultsearchtrackid = preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote($host, '/').'/', $reference, $reg); // trackid found or not
2209 if (empty($resultsearchtrackid) && getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE')) {
2210 $resultsearchtrackid = preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote(getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE'), '/').'/', $reference, $reg); // trackid found
2211 }
2212 }
2213
2214 if (!empty($resultsearchtrackid)) {
2215 // We found a tracker (in recipient email or msgid or into a Reference matching the Dolibarr server)
2216 $trackid = $reg[1].$reg[2];
2217
2218 $objectid = $reg[2];
2219 // See also list into interface_50_modAgenda_ActionsAuto
2220 if ($reg[1] == 'thi') { // Third-party
2221 $objectemail = new Societe($this->db);
2222 }
2223 if ($reg[1] == 'ctc') { // Contact
2224 $objectemail = new Contact($this->db);
2225 }
2226 if ($reg[1] == 'inv') { // Customer Invoice
2227 $objectemail = new Facture($this->db);
2228 }
2229 if ($reg[1] == 'sinv') { // Supplier Invoice
2230 $objectemail = new FactureFournisseur($this->db);
2231 }
2232 if ($reg[1] == 'pro') { // Customer Proposal
2233 $objectemail = new Propal($this->db);
2234 }
2235 if ($reg[1] == 'ord') { // Sale Order
2236 $objectemail = new Commande($this->db);
2237 }
2238 if ($reg[1] == 'shi') { // Shipment
2239 $objectemail = new Expedition($this->db);
2240 }
2241 if ($reg[1] == 'spro') { // Supplier Proposal
2242 $objectemail = new SupplierProposal($this->db);
2243 }
2244 if ($reg[1] == 'sord') { // Supplier Order
2245 $objectemail = new CommandeFournisseur($this->db);
2246 }
2247 if ($reg[1] == 'rec') { // Reception
2248 $objectemail = new Reception($this->db);
2249 }
2250 if ($reg[1] == 'proj') { // Project
2251 $objectemail = new Project($this->db);
2252 $projectfoundby = 'TrackID dolibarr-'.$trackid.'@...';
2253 }
2254 if ($reg[1] == 'tas') { // Task
2255 $objectemail = new Task($this->db);
2256 }
2257 if ($reg[1] == 'con') { // Contact
2258 $objectemail = new Contact($this->db);
2259 }
2260 if ($reg[1] == 'use') { // User
2261 $objectemail = new User($this->db);
2262 }
2263 if ($reg[1] == 'tic') { // Ticket
2264 $objectemail = new Ticket($this->db);
2265 $ticketfoundby = 'TrackID dolibarr-'.$trackid.'@...';
2266 }
2267 if ($reg[1] == 'recruitmentcandidature') { // Recruiting Candidate
2268 $objectemail = new RecruitmentCandidature($this->db);
2269 $candidaturefoundby = 'TrackID dolibarr-'.$trackid.'@...';
2270 }
2271 if ($reg[1] == 'mem') { // Member
2272 $objectemail = new Adherent($this->db);
2273 }
2274 /*if ($reg[1] == 'leav') { // Leave / Holiday
2275 $objectemail = new Holiday($db);
2276 }
2277 if ($reg[1] == 'exp') { // ExpenseReport
2278 $objectemail = new ExpenseReport($db);
2279 }*/
2280 } elseif (preg_match('/<(.*@.*)>/', $reference, $reg)) {
2281 // This is an external reference, we check if we have it in our database
2282 if (is_null($objectemail) && isModEnabled('ticket')) {
2283 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."ticket";
2284 $sql .= " WHERE email_msgid = '".$this->db->escape($reg[1])."' OR origin_references LIKE '%".$this->db->escape($this->db->escapeforlike($reg[1]))."%'";
2285 $resql = $this->db->query($sql);
2286 if ($resql) {
2287 $obj = $this->db->fetch_object($resql);
2288 if ($obj) {
2289 $objectid = $obj->rowid;
2290 $objectemail = new Ticket($this->db);
2291 $ticketfoundby = $langs->transnoentitiesnoconv("EmailMsgID").' ('.$reg[1].')';
2292 }
2293 } else {
2294 $errorforemail++;
2295 }
2296 }
2297
2298 if (!is_object($objectemail) && isModEnabled('project')) {
2299 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."projet where email_msgid = '".$this->db->escape($reg[1])."'";
2300 $resql = $this->db->query($sql);
2301 if ($resql) {
2302 $obj = $this->db->fetch_object($resql);
2303 if ($obj) {
2304 $objectid = $obj->rowid;
2305 $objectemail = new Project($this->db);
2306 $projectfoundby = $langs->transnoentitiesnoconv("EmailMsgID").' ('.$reg[1].')';
2307 }
2308 } else {
2309 $errorforemail++;
2310 }
2311 }
2312
2313 if (!is_object($objectemail) && isModEnabled('recruitment')) {
2314 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."recruitment_recruitmentcandidature where email_msgid = '".$this->db->escape($reg[1])."'";
2315 $resql = $this->db->query($sql);
2316 if ($resql) {
2317 $obj = $this->db->fetch_object($resql);
2318 if ($obj) {
2319 $objectid = $obj->rowid;
2320 $objectemail = new RecruitmentCandidature($this->db);
2321 $candidaturefoundby = $langs->transnoentitiesnoconv("EmailMsgID").' ('.$reg[1].')';
2322 }
2323 } else {
2324 $errorforemail++;
2325 }
2326 }
2327 }
2328
2329 // Load object linked to email
2330 if (is_object($objectemail)) {
2331 $result = $objectemail->fetch($objectid);
2332 if ($result > 0) {
2333 $fk_element_id = $objectemail->id;
2334 $fk_element_type = $objectemail->element;
2335 // Fix fk_element_type
2336 if ($fk_element_type == 'facture') {
2337 $fk_element_type = 'invoice';
2338 }
2339
2340 if (get_class($objectemail) != 'Societe') {
2341 $thirdpartyid = $objectemail->fk_soc ?? $objectemail->socid;
2342 } else {
2343 $thirdpartyid = $objectemail->id;
2344 }
2345
2346 if (get_class($objectemail) != 'Contact') {
2347 $contactid = $objectemail->fk_socpeople;
2348 } else {
2349 $contactid = $objectemail->id;
2350 }
2351
2352 if (get_class($objectemail) != 'Project') {
2353 $projectid = isset($objectemail->fk_project) ? $objectemail->fk_project : $objectemail->fk_projet;
2354 } else {
2355 $projectid = $objectemail->id;
2356 }
2357
2358 if ($objectemail instanceof Ticket) { // For tickets, we have a column to store all met references, so we complete it if we need to.
2359 $ticketid = $objectemail->id;
2360
2361 $changeonticket_references = false;
2362 if (empty($trackid)) {
2363 $trackid = $objectemail->track_id;
2364 }
2365 if (empty($objectemail->origin_references)) {
2366 $objectemail->origin_references = !empty($headers['References']) ? $headers['References'] : null;
2367 $changeonticket_references = true;
2368 } else {
2369 foreach ($arrayofreferences as $key => $referencetmp) {
2370 if (!str_contains($objectemail->origin_references, $referencetmp)) {
2371 $objectemail->origin_references .= " ".$referencetmp;
2372 $changeonticket_references = true;
2373 }
2374 }
2375 }
2376 if ($changeonticket_references) {
2377 $operationslog .= '<br>We complete ticket ID='.$ticketid.' with property origin_references='.$objectemail->origin_references;
2378 $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
2379 }
2380 }
2381 }
2382 }
2383
2384 // Project
2385 if ($projectid > 0) {
2386 $result = $projectstatic->fetch($projectid);
2387 if ($result <= 0) {
2388 $projectstatic->id = 0;
2389 } else {
2390 $projectid = $projectstatic->id;
2391 if ($trackid) {
2392 $projectfoundby = 'trackid ('.$trackid.')';
2393 }
2394 if (empty($contactid)) {
2395 $contactid = $projectstatic->fk_contact;
2396 }
2397 if (empty($thirdpartyid)) {
2398 $thirdpartyid = $projectstatic->fk_soc;
2399 }
2400 }
2401 }
2402 // Contact
2403 if ($contactid > 0) {
2404 $result = $contactstatic->fetch($contactid);
2405 if ($result <= 0) {
2406 $contactstatic->id = 0;
2407 } else {
2408 $contactid = $contactstatic->id;
2409 if ($trackid) {
2410 $contactfoundby = 'trackid ('.$trackid.')';
2411 }
2412 if (empty($thirdpartyid)) {
2413 $thirdpartyid = $contactstatic->fk_soc;
2414 }
2415 }
2416 }
2417 // Thirdparty
2418 if ($thirdpartyid > 0) {
2419 $result = $thirdpartystatic->fetch($thirdpartyid);
2420 if ($result <= 0) {
2421 $thirdpartystatic->id = 0;
2422 } else {
2423 $thirdpartyid = $thirdpartystatic->id;
2424 if ($trackid) {
2425 $thirdpartyfoundby = 'trackid ('.$trackid.')';
2426 }
2427 }
2428 }
2429
2430 if (is_object($objectemail)) {
2431 break; // Exit loop of references. We already found an accurate reference
2432 }
2433 }
2434
2435 if (empty($contactid)) { // Try to find contact using email
2436 $result = $contactstatic->fetch(0, null, '', $from);
2437
2438 if ($result > 0) {
2439 dol_syslog("We found a contact with the email ".$from);
2440 $contactid = $contactstatic->id;
2441 $contactfoundby = 'email of contact ('.$from.')';
2442 if (empty($thirdpartyid) && $contactstatic->socid > 0) {
2443 $result = $thirdpartystatic->fetch($contactstatic->socid);
2444 if ($result > 0) {
2445 $thirdpartyid = $thirdpartystatic->id;
2446 $thirdpartyfoundby = 'email of contact ('.$from.')';
2447 }
2448 }
2449 }
2450 }
2451
2452 if (empty($thirdpartyid)) { // Try to find thirdparty using email
2453 $result = $thirdpartystatic->fetch(0, '', '', '', '', '', '', '', '', '', $from);
2454 if ($result > 0) {
2455 dol_syslog("We found a thirdparty with the email ".$from);
2456 $thirdpartyid = $thirdpartystatic->id;
2457 $thirdpartyfoundby = 'email ('.$from.')';
2458 }
2459 }
2460
2461 /*
2462 if ($replyto) {
2463 if (empty($contactid)) { // Try to find contact using email
2464 $result = $contactstatic->fetch(0, null, '', $replyto);
2465
2466 if ($result > 0) {
2467 dol_syslog("We found a contact with the email ".$replyto);
2468 $contactid = $contactstatic->id;
2469 $contactfoundby = 'email of contact ('.$replyto.')';
2470 if (empty($thirdpartyid) && $contactstatic->socid > 0) {
2471 $result = $thirdpartystatic->fetch($contactstatic->socid);
2472 if ($result > 0) {
2473 $thirdpartyid = $thirdpartystatic->id;
2474 $thirdpartyfoundby = 'email of contact ('.$replyto.')';
2475 }
2476 }
2477 }
2478 }
2479
2480 if (empty($thirdpartyid)) { // Try to find thirdparty using email
2481 $result = $thirdpartystatic->fetch(0, '', '', '', '', '', '', '', '', '', $replyto);
2482 if ($result > 0) {
2483 dol_syslog("We found a thirdparty with the email ".$replyto);
2484 $thirdpartyid = $thirdpartystatic->id;
2485 $thirdpartyfoundby = 'email ('.$replyto.')';
2486 }
2487 }
2488 }
2489 */
2490
2491
2492 // Now do all operations for the email (extract variables and creating data)
2493 if ($mode < 2) { // 0=Mode production, 1=Mode test (read IMAP and try SQL update then rollback), 2=Mode test with no SQL updates
2494 foreach ($this->actions as $operation) {
2495 $errorforthisaction = 0;
2496 $ticketalreadyexists = 0;
2497 if ($errorforactions) {
2498 break;
2499 }
2500 if (empty($operation['status'])) {
2501 continue;
2502 }
2503
2504 $operationslog .= '<br>* Process operation '.$operation['type'];
2505
2506 // Make Operation
2507 dol_syslog("Execute action ".$operation['type']." actionparam=".$operation['actionparam'].' thirdpartystatic->id='.$thirdpartystatic->id.' contactstatic->id='.$contactstatic->id.' projectstatic->id='.$projectstatic->id);
2508 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
2509
2510 // Try to guess if this is an email in or out.
2511 $actioncode = 'EMAIL_IN';
2512 // If we scan the Sent box, we use the code for out email
2513 if (preg_match('/Sent$/', $sourcedir) || preg_match('/envoyés$/i', $sourcedir)) {
2514 $actioncode = 'EMAIL';
2515 }
2516 // If sender is in the list MAIL_FROM_EMAILS_TO_CONSIDER_SENDING
2517 $arrayofemailtoconsideresender = explode(',', getDolGlobalString('MAIL_FROM_EMAILS_TO_CONSIDER_SENDING'));
2518 foreach ($arrayofemailtoconsideresender as $emailtoconsidersender) {
2519 if (preg_match('/'.preg_quote($emailtoconsidersender, '/').'/', $fromstring)) {
2520 $actioncode = 'EMAIL';
2521 }
2522 }
2523 $operationslog .= '<br>Email will have actioncode='.$actioncode;
2524
2525 $description = $descriptiontitle = $descriptionmeta = $descriptionfull = '';
2526
2527 $descriptiontitle = $langs->transnoentitiesnoconv("RecordCreatedByEmailCollector", $this->ref);
2528
2529 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("EmailMsgID").' : '.dol_escape_htmltag($msgid));
2530 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailTopic").' : '.dol_escape_htmltag($subject));
2531 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailDate").($langs->trans("MailDate") != 'Date' ? ' (Date)' : '').' : '.dol_escape_htmltag(dol_print_date($dateemail, "dayhourtext", "gmt")));
2532 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailFrom").($langs->trans("MailFrom") != 'From' ? ' (From)' : '').' : '.dol_escape_htmltag($fromstring));
2533 if ($sender) {
2534 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("Sender").($langs->trans("Sender") != 'Sender' ? ' (Sender)' : '').' : '.dol_escape_htmltag($sender));
2535 }
2536 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailTo").($langs->trans("MailTo") != 'To' ? ' (To)' : '').' : '.dol_escape_htmltag($to));
2537 if ($replyto) {
2538 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailReply").($langs->trans("MailReply") != 'Reply to' ? ' (Reply to)' : '').' : '.dol_escape_htmltag($replyto));
2539 }
2540 if ($sendtocc) {
2541 $descriptionmeta = dol_concatdesc($descriptionmeta, $langs->trans("MailCC").($langs->trans("MailCC") != 'CC' ? ' (CC)' : '').' : '.dol_escape_htmltag($sendtocc));
2542 }
2543
2544 if ($operation['type'] == 'ticket') {
2545 // Verify if ticket already exists to fall back on the right operation
2546 $tickettocreate = new Ticket($this->db);
2547 $errorfetchticket = 0;
2548 $alreadycreated = 0;
2549 if ($ticketid > 0) {
2550 $alreadycreated = $tickettocreate->fetch($ticketid);
2551 }
2552 if ($alreadycreated == 0 && !empty($trackid)) {
2553 $alreadycreated = $tickettocreate->fetch(0, '', $trackid);
2554 }
2555 if ($alreadycreated == 0 && !empty($msgid)) {
2556 $alreadycreated = $tickettocreate->fetch(0, '', '', $msgid);
2557 }
2558 if ($alreadycreated < 0) {
2559 $errorfetchticket++;
2560 }
2561 if (empty($errorfetchticket)) {
2562 if ($alreadycreated == 0) {
2563 $operationslog .= '<br>Ticket not found using trackid='.$trackid.' or msgid='.$msgid;
2564 $ticketalreadyexists = 0;
2565 } else {
2566 $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
2567 $ticketalreadyexists = 1;
2568 $operation['type'] = 'recordevent';
2569 }
2570 } else {
2571 $ticketalreadyexists = -1;
2572 }
2573 }
2574
2575 // Process now the operation according to its type
2576
2577 // Search and create thirdparty
2578 if ($operation['type'] == 'loadthirdparty' || $operation['type'] == 'loadandcreatethirdparty') {
2579 if ($thirdpartyid > 0) {
2580 // We already have found the thirdparty id to load, so we bypass the action loadthirdparty
2581 $idtouseforthirdparty = $thirdpartyid;
2582 $operationslog .= '<br>We already have found a related thirdparty id, so we bypass the action loadthirdparty and use idtouseforthirdparty='.$idtouseforthirdparty;
2583 } elseif (empty($operation['actionparam'])) {
2584 $errorforactions++;
2585 $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;";
2586 $this->errors[] = $this->error;
2587 } else {
2588 $actionparam = $operation['actionparam'];
2589 $idtouseforthirdparty = '';
2590 $nametouseforthirdparty = '';
2591 $emailtouseforthirdparty = '';
2592 $namealiastouseforthirdparty = '';
2593
2594 $operationslog .= '<br>Loop on each property to set into actionparam';
2595
2596 // $actionparam = 'param=SET:aaa' or 'param=EXTRACT:BODY:....'
2597 $arrayvaluetouse = dolExplodeIntoArray($actionparam, '(\n\r|\r|\n|;)', '=');
2598 foreach ($arrayvaluetouse as $propertytooverwrite => $valueforproperty) {
2599 $sourcestring = '';
2600 $sourcefield = '';
2601 $regexstring = '';
2602 $regforregex = array();
2603
2604 if (preg_match('/^EXTRACT:([a-zA-Z0-9_]+):(.*)$/', $valueforproperty, $regforregex)) {
2605 $sourcefield = $regforregex[1];
2606 $regexstring = $regforregex[2];
2607 }
2608
2609 if (!empty($sourcefield) && !empty($regexstring)) {
2610 if (strtolower($sourcefield) == 'body') {
2611 $sourcestring = $messagetext;
2612 } elseif (strtolower($sourcefield) == 'subject') {
2613 $sourcestring = $subject;
2614 } elseif (strtolower($sourcefield) == 'header') {
2615 $sourcestring = $header;
2616 }
2617
2618 if ($sourcestring) {
2619 $regforval = array();
2620 //var_dump($regexstring);var_dump($sourcestring);
2621 if (preg_match('/'.$regexstring.'/ms', $sourcestring, $regforval)) {
2622 //var_dump($regforval[count($regforval)-1]);exit;
2623 // Overwrite param $tmpproperty
2624 if ($propertytooverwrite == 'id') {
2625 $idtouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2626
2627 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found idtouseforthirdparty='.dol_escape_htmltag($idtouseforthirdparty);
2628 } elseif ($propertytooverwrite == 'email') {
2629 $emailtouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2630
2631 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found emailtouseforthirdparty='.dol_escape_htmltag($emailtouseforthirdparty);
2632 } elseif ($propertytooverwrite == 'name') {
2633 $nametouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2634
2635 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found nametouseforthirdparty='.dol_escape_htmltag($nametouseforthirdparty);
2636 } elseif ($propertytooverwrite == 'name_alias') {
2637 $namealiastouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null;
2638
2639 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found namealiastouseforthirdparty='.dol_escape_htmltag($namealiastouseforthirdparty);
2640 } else {
2641 $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';
2642 }
2643 } else {
2644 // Regex not found
2645 if (in_array($propertytooverwrite, array('id', 'email', 'name', 'name_alias'))) {
2646 $idtouseforthirdparty = null;
2647 $nametouseforthirdparty = null;
2648 $emailtouseforthirdparty = null;
2649 $namealiastouseforthirdparty = null;
2650
2651 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Not found. Property searched is critical so we cancel the search.';
2652 } else {
2653 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Not found';
2654 }
2655 }
2656 //var_dump($object->$tmpproperty);exit;
2657 } else {
2658 // Nothing can be done for this param
2659 $errorforactions++;
2660 $this->error = 'The extract rule to use to load thirdparty for email '.$msgid.' has an unknown source (must be HEADER, SUBJECT or BODY)';
2661 $this->errors[] = $this->error;
2662
2663 $operationslog .= '<br>'.$this->error;
2664 }
2665 } elseif (preg_match('/^(SET|SETIFEMPTY):(.*)$/', $valueforproperty, $reg)) {
2666 //if (preg_match('/^options_/', $tmpproperty)) $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $reg[1];
2667 //else $object->$tmpproperty = $reg[1];
2668 // Example: id=SETIFEMPTY:123
2669 if ($propertytooverwrite == 'id') {
2670 $idtouseforthirdparty = $reg[2];
2671
2672 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property idtouseforthrdparty='.dol_escape_htmltag($idtouseforthirdparty);
2673 } elseif ($propertytooverwrite == 'email') {
2674 $emailtouseforthirdparty = $reg[2];
2675
2676 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property emailtouseforthrdparty='.dol_escape_htmltag($emailtouseforthirdparty);
2677 } elseif ($propertytooverwrite == 'name') {
2678 $nametouseforthirdparty = $reg[2];
2679
2680 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property nametouseforthirdparty='.dol_escape_htmltag($nametouseforthirdparty);
2681 } elseif ($propertytooverwrite == 'name_alias') {
2682 $namealiastouseforthirdparty = $reg[2];
2683
2684 $operationslog .= '<br>propertytooverwrite='.$propertytooverwrite.' We set property namealiastouseforthirdparty='.dol_escape_htmltag($namealiastouseforthirdparty);
2685 }
2686 } else {
2687 $errorforactions++;
2688 $this->error = 'Bad syntax for description of action parameters: '.$actionparam;
2689 $this->errors[] = $this->error;
2690 break;
2691 }
2692 }
2693
2694 if (!$errorforactions && ($idtouseforthirdparty || $emailtouseforthirdparty || $nametouseforthirdparty || $namealiastouseforthirdparty)) {
2695 // We make another search on thirdparty
2696 $operationslog .= '<br>We have this initial main data to search thirdparty: id='.$idtouseforthirdparty.', email='.$emailtouseforthirdparty.', name='.$nametouseforthirdparty.', name_alias='.$namealiastouseforthirdparty.'.';
2697
2698 $tmpobject = new stdClass();
2699 $tmpobject->element = 'generic';
2700 $tmpobject->id = $idtouseforthirdparty;
2701 $tmpobject->name = $nametouseforthirdparty;
2702 $tmpobject->name_alias = $namealiastouseforthirdparty;
2703 $tmpobject->email = $emailtouseforthirdparty;
2704
2705 $this->overwritePropertiesOfObject($tmpobject, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2706
2707 $idtouseforthirdparty = $tmpobject->id;
2708 $nametouseforthirdparty = $tmpobject->name;
2709 $namealiastouseforthirdparty = $tmpobject->name_alias;
2710 $emailtouseforthirdparty = $tmpobject->email;
2711
2712 $operationslog .= '<br>We try to search existing thirdparty with idtouseforthirdparty='.$idtouseforthirdparty.' emailtouseforthirdparty='.$emailtouseforthirdparty.' nametouseforthirdparty='.$nametouseforthirdparty.' namealiastouseforthirdparty='.$namealiastouseforthirdparty;
2713
2714 // Try to find the thirdparty that match the most the information we have
2715 $result = $thirdpartystatic->findNearest((int) $idtouseforthirdparty, (string) $nametouseforthirdparty, '', '', '', '', '', '', '', '', (string) $emailtouseforthirdparty, (string) $namealiastouseforthirdparty);
2716
2717 if ($result < 0) {
2718 $errorforactions++;
2719 $this->error = 'Error when getting thirdparty with name '.$nametouseforthirdparty.' (may be 2 record exists with same name ?)';
2720 $this->errors[] = $this->error;
2721 break;
2722 } elseif ($result == 0) { // No thirdparty found
2723 if ($operation['type'] == 'loadthirdparty') {
2724 dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." name_alias=".$namealiastouseforthirdparty." was not found");
2725
2726 // Search into contacts of thirdparties to try to guess the thirdparty to use
2727 $resultContact = $contactstatic->findNearest(0, '', '', '', (string) $emailtouseforthirdparty, '', 0);
2728 if ($resultContact > 0) {
2729 $contactstatic->fetch($resultContact);
2730 $idtouseforthirdparty = $contactstatic->socid;
2731 $result = $thirdpartystatic->fetch($idtouseforthirdparty);
2732 if ($result > 0) {
2733 dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." name_alias=".$namealiastouseforthirdparty." was found thanks to linked contact search");
2734 } else {
2735 $errorforactions++;
2736 $langs->load("errors");
2737 $this->error = $langs->trans('ErrorFailedToLoadThirdParty', $idtouseforthirdparty, (string) $emailtouseforthirdparty, (string) $nametouseforthirdparty, (string) $namealiastouseforthirdparty);
2738 $this->errors[] = $this->error;
2739 }
2740 } else {
2741 $errorforactions++;
2742 $langs->load("errors");
2743 $this->error = $langs->trans('ErrorFailedToLoadThirdParty', $idtouseforthirdparty, (string) $emailtouseforthirdparty, (string) $nametouseforthirdparty, (string) $namealiastouseforthirdparty);
2744 $this->errors[] = $this->error;
2745 }
2746 } elseif ($operation['type'] == 'loadandcreatethirdparty') {
2747 dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." name_alias=".$namealiastouseforthirdparty." was not found. We try to create it.");
2748
2749 // Create thirdparty
2750 $thirdpartystatic = new Societe($db);
2751 $thirdpartystatic->name = (string) $nametouseforthirdparty;
2752 if (!empty($namealiastouseforthirdparty)) {
2753 if ($namealiastouseforthirdparty != $nametouseforthirdparty) {
2754 $thirdpartystatic->name_alias = $namealiastouseforthirdparty;
2755 }
2756 } else {
2757 $thirdpartystatic->name_alias = (empty($replytostring) ? (empty($fromtext) ? '' : $fromtext) : $replytostring);
2758 }
2759 $thirdpartystatic->email = (empty($emailtouseforthirdparty) ? (empty($replyto) ? (empty($from) ? '' : $from) : $replyto) : $emailtouseforthirdparty);
2760
2761 // Overwrite values with values extracted from source email
2762 $errorforthisaction = $this->overwritePropertiesOfObject($thirdpartystatic, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2763
2764 if ($thirdpartystatic->client && empty($thirdpartystatic->code_client)) {
2765 $thirdpartystatic->code_client = 'auto';
2766 }
2767 if ($thirdpartystatic->fournisseur && empty($thirdpartystatic->code_fournisseur)) {
2768 $thirdpartystatic->code_fournisseur = 'auto';
2769 }
2770
2771 if ($errorforthisaction) {
2772 $errorforactions++;
2773 } else {
2774 $result = $thirdpartystatic->create($user);
2775 if ($result <= 0) {
2776 $errorforactions++;
2777 $this->error = $thirdpartystatic->error;
2778 $this->errors = $thirdpartystatic->errors;
2779 } else {
2780 $operationslog .= '<br>Thirdparty created -> id = '.dol_escape_htmltag((string) $thirdpartystatic->id);
2781 }
2782 }
2783 }
2784 } else { // $result > 0 is ID of thirdparty
2785 dol_syslog("One and only one existing third party has been found");
2786
2787 $thirdpartystatic->fetch($result);
2788
2789 $operationslog .= '<br>Thirdparty already exists with id = '.dol_escape_htmltag((string) $thirdpartystatic->id)." and name ".dol_escape_htmltag($thirdpartystatic->name);
2790 }
2791 }
2792 }
2793 } elseif ($operation['type'] == 'loadandcreatecontact') { // Search and create contact
2794 if (empty($operation['actionparam'])) {
2795 $errorforactions++;
2796 $this->error = "Action loadandcreatecontact has empty parameter. Must be 'SET:xxx' or 'EXTRACT:(body|subject):regex' to define how to extract data";
2797 $this->errors[] = $this->error;
2798 } else {
2799 $contact_static = new Contact($this->db);
2800 // Overwrite values with values extracted from source email
2801 $errorforthisaction = $this->overwritePropertiesOfObject($contact_static, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2802 if ($errorforthisaction) {
2803 $errorforactions++;
2804 } else {
2805 if (!empty($contact_static->email) && $contact_static->email != $from) {
2806 $from = $contact_static->email;
2807 }
2808
2809 $result = $contactstatic->fetch(0, null, '', $from);
2810 if ($result < 0) {
2811 $errorforactions++;
2812 $this->error = 'Error when getting contact with email ' . $from;
2813 $this->errors[] = $this->error;
2814 break;
2815 } elseif ($result == 0) {
2816 dol_syslog("Contact with email " . $from . " was not found. We try to create it.");
2817 $contactstatic = new Contact($this->db);
2818
2819 // Create contact
2820 $contactstatic->email = $from;
2821 $operationslog .= '<br>We set property email='.dol_escape_htmltag($from);
2822
2823 // Overwrite values with values extracted from source email
2824 $errorforthisaction = $this->overwritePropertiesOfObject($contactstatic, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2825
2826 if ($errorforthisaction) {
2827 $errorforactions++;
2828 } else {
2829 // Search country by name or code
2830 if (!empty($contactstatic->country)) {
2831 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
2832 $result = getCountry('', '3', $this->db, null, 1, $contactstatic->country);
2833 if ($result == 'NotDefined') {
2834 $errorforactions++;
2835 $this->error = "Error country not found by this name '" . $contactstatic->country . "'";
2836 } elseif (!($result > 0)) {
2837 $errorforactions++;
2838 $this->error = "Error when search country by this name '" . $contactstatic->country . "'";
2839 $this->errors[] = $this->db->lasterror();
2840 } else {
2841 $contactstatic->country_id = $result;
2842 $operationslog .= '<br>We set property country_id='.dol_escape_htmltag($result);
2843 }
2844 } elseif (!empty($contactstatic->country_code)) {
2845 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
2846 $result = getCountry($contactstatic->country_code, '3', $this->db);
2847 if ($result == 'NotDefined') {
2848 $errorforactions++;
2849 $this->error = "Error country not found by this code '" . $contactstatic->country_code . "'";
2850 } elseif (!($result > 0)) {
2851 $errorforactions++;
2852 $this->error = "Error when search country by this code '" . $contactstatic->country_code . "'";
2853 $this->errors[] = $this->db->lasterror();
2854 } else {
2855 $contactstatic->country_id = $result;
2856 $operationslog .= '<br>We set property country_id='.dol_escape_htmltag($result);
2857 }
2858 }
2859
2860 if (!$errorforactions) {
2861 // Search state by name or code (for country if defined)
2862 if (!empty($contactstatic->state)) {
2863 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
2864 $result = dol_getIdFromCode($this->db, $contactstatic->state, 'c_departements', 'nom', 'rowid');
2865 if (empty($result)) {
2866 $errorforactions++;
2867 $this->error = "Error state not found by this name '" . $contactstatic->state . "'";
2868 } elseif (!($result > 0)) {
2869 $errorforactions++;
2870 $this->error = "Error when search state by this name '" . $contactstatic->state . "'";
2871 $this->errors[] = $this->db->lasterror();
2872 } else {
2873 $contactstatic->state_id = $result;
2874 $operationslog .= '<br>We set property state_id='.dol_escape_htmltag($result);
2875 }
2876 } elseif (!empty($contactstatic->state_code)) {
2877 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
2878 $result = dol_getIdFromCode($this->db, $contactstatic->state_code, 'c_departements', 'code_departement', 'rowid');
2879 if (empty($result)) {
2880 $errorforactions++;
2881 $this->error = "Error state not found by this code '" . $contactstatic->state_code . "'";
2882 } elseif (!($result > 0)) {
2883 $errorforactions++;
2884 $this->error = "Error when search state by this code '" . $contactstatic->state_code . "'";
2885 $this->errors[] = $this->db->lasterror();
2886 } else {
2887 $contactstatic->state_id = $result;
2888 $operationslog .= '<br>We set property state_id='.dol_escape_htmltag($result);
2889 }
2890 }
2891 }
2892
2893 if (!$errorforactions) {
2894 $result = $contactstatic->create($user);
2895 if ($result <= 0) {
2896 $errorforactions++;
2897 $this->error = $contactstatic->error;
2898 $this->errors = $contactstatic->errors;
2899 } else {
2900 $operationslog .= '<br>Contact created -> id = '.dol_escape_htmltag((string) $contactstatic->id);
2901 }
2902 }
2903 }
2904 }
2905 }
2906 }
2907 } elseif ($operation['type'] == 'recordevent') {
2908 // Create event
2909 $actioncomm = new ActionComm($this->db);
2910
2911 $alreadycreated = $actioncomm->fetch(0, '', '', $msgid);
2912 if ($alreadycreated == 0) {
2913 $operationslog .= '<br>We did not find existing actioncomm with msgid='.$msgid;
2914
2915 if ($projectstatic->id > 0) {
2916 if ($projectfoundby) {
2917 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Project found from '.$projectfoundby);
2918 }
2919 }
2920 if ($thirdpartystatic->id > 0) {
2921 if ($thirdpartyfoundby) {
2922 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Third party found from '.$thirdpartyfoundby);
2923 }
2924 }
2925 if ($contactstatic->id > 0) {
2926 if ($contactfoundby) {
2927 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Contact/address found from '.$contactfoundby);
2928 }
2929 }
2930
2931 $description = $descriptiontitle;
2932
2933 $description = dol_concatdesc($description, $descriptionmeta);
2934 $description = dol_concatdesc($description, "-----");
2935 $description = dol_concatdesc($description, $messagetext);
2936
2937 $descriptionfull = $description;
2938 if (!getDolGlobalString('MAIN_EMAILCOLLECTOR_MAIL_WITHOUT_HEADER')) {
2939 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
2940 $descriptionfull = dol_concatdesc($descriptionfull, $header);
2941 }
2942
2943 // Insert record of emails sent
2944 $actioncomm->type_code = 'AC_OTH_AUTO'; // Type of event ('AC_OTH', 'AC_OTH_AUTO', 'AC_XXX'...)
2945 $actioncomm->code = 'AC_'.$actioncode;
2946 $actioncomm->label = $langs->trans("ActionAC_".$actioncode).' - '.$langs->trans("MailFrom").' '.$from;
2947 $actioncomm->note_private = $descriptionfull;
2948 $actioncomm->fk_project = $projectstatic->id;
2949 $actioncomm->datep = $dateemail; // date of email
2950 $actioncomm->datef = $dateemail; // date of email
2951 $actioncomm->percentage = -1; // Not applicable
2952 $actioncomm->socid = $thirdpartystatic->id;
2953 $actioncomm->contact_id = $contactstatic->id;
2954 $actioncomm->socpeopleassigned = (!empty($contactstatic->id) ? array($contactstatic->id) : array());
2955 $actioncomm->authorid = $user->id; // User saving action
2956 $actioncomm->userownerid = $user->id; // Owner of action
2957 // Fields when action is an email (content should be added into note)
2958 $actioncomm->email_msgid = $msgid;
2959 $actioncomm->email_from = $fromstring;
2960 $actioncomm->email_sender = $sender;
2961 $actioncomm->email_to = $to;
2962 $actioncomm->email_tocc = $sendtocc;
2963 $actioncomm->email_tobcc = $sendtobcc;
2964 $actioncomm->email_subject = $subject;
2965 $actioncomm->errors_to = '';
2966
2967 if (!in_array($fk_element_type, array('societe', 'contact', 'project', 'user'))) {
2968 $actioncomm->fk_element = $fk_element_id;
2969 $actioncomm->elementid = $fk_element_id;
2970 $actioncomm->elementtype = $fk_element_type;
2971 if (is_object($objectemail) && $objectemail->module) {
2972 $actioncomm->elementtype .= '@'.$objectemail->module;
2973 }
2974 }
2975
2976 //$actioncomm->extraparams = $extraparams;
2977
2978 // Overwrite values with values extracted from source email
2979 $errorforthisaction = $this->overwritePropertiesOfObject($actioncomm, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
2980
2981 if ($errorforthisaction) {
2982 $errorforactions++;
2983 } else {
2984 $result = $actioncomm->create($user);
2985 if ($result <= 0) {
2986 $errorforactions++;
2987 $this->errors = $actioncomm->errors;
2988 } else {
2989 if ($fk_element_type == "ticket" && is_object($objectemail)) {
2990 if ($objectemail->status == Ticket::STATUS_CLOSED || $objectemail->status == Ticket::STATUS_CANCELED || $objectemail->status == Ticket::STATUS_NEED_MORE_INFO || $objectemail->status == Ticket::STATUS_WAITING) {
2991 if ($objectemail->fk_user_assign != null) {
2992 $res = $objectemail->setStatut(Ticket::STATUS_ASSIGNED);
2993 } else {
2994 $res = $objectemail->setStatut(Ticket::STATUS_NOT_READ);
2995 }
2996
2997 if ($res) {
2998 $operationslog .= '<br>Ticket Re-Opened successfully -> ref='.$objectemail->ref;
2999 } else {
3000 $errorforactions++;
3001 $this->error = 'Error while changing the ticket status -> ref='.$objectemail->ref;
3002 $this->errors[] = $this->error;
3003 }
3004 }
3005 if (!empty($attachments)) {
3006 // There is an attachment for the ticket -> store attachment
3007 $ticket = new Ticket($this->db);
3008 $ticket->fetch($fk_element_id);
3009 $destdir = $conf->ticket->dir_output.'/'.$ticket->ref;
3010 if (!dol_is_dir($destdir)) {
3011 dol_mkdir($destdir);
3012 }
3013 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3014 foreach ($attachments as $attachment) {
3015 $attachment->save($destdir.'/');
3016 }
3017 } else {
3018 $this->getmsg($connection, $imapemail, $destdir);
3019 }
3020 }
3021 }
3022
3023 $operationslog .= '<br>Event created -> id='.dol_escape_htmltag((string) $actioncomm->id);
3024 }
3025 }
3026 } else {
3027 $operationslog .= '<br>An event in actioncomm table already exists for the msgid = '.$msgid.' so we bypass this action.';
3028 }
3029 } elseif ($operation['type'] == 'recordjoinpiece') {
3030 $data = [];
3031 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3032 foreach ($attachments as $attachment) {
3033 if ($attachment->getName() === 'undefined') {
3034 continue;
3035 }
3036 $data[$attachment->getName()] = $attachment->getContent();
3037 }
3038 } else {
3039 $pj = getAttachments($imapemail, $connection);
3040 foreach ($pj as $key => $val) {
3041 $data[$val['filename']] = getFileData($imapemail, (string) $val['pos'], $val['type'], $connection);
3042 }
3043 }
3044 if (count($data) > 0) {
3045 $sql = "SELECT rowid as id FROM ".MAIN_DB_PREFIX."user WHERE email LIKE '%".$this->db->escape($from)."%'";
3046 $resql = $this->db->query($sql);
3047 if ($this->db->num_rows($resql) == 0) {
3048 $this->errors[] = "User Not allowed to add documents ({$from})";
3049 }
3050 $arrayobject = array(
3051 'propale' => array('table' => 'propal',
3052 'fields' => array('ref'),
3053 'class' => 'comm/propal/class/propal.class.php',
3054 'object' => 'Propal'),
3055 'holiday' => array('table' => 'holiday',
3056 'fields' => array('ref'),
3057 'class' => 'holiday/class/holiday.class.php',
3058 'object' => 'Holiday'),
3059 'expensereport' => array('table' => 'expensereport',
3060 'fields' => array('ref'),
3061 'class' => 'expensereport/class/expensereport.class.php',
3062 'object' => 'ExpenseReport'),
3063 'recruitment/recruitmentjobposition' => array('table' => 'recruitment_recruitmentjobposition',
3064 'fields' => array('ref'),
3065 'class' => 'recruitment/class/recruitmentjobposition.class.php',
3066 'object' => 'RecruitmentJobPosition'),
3067 'recruitment/recruitmentcandidature' => array('table' => 'recruitment_recruitmentcandidature',
3068 'fields' => array('ref'),
3069 'class' => 'recruitment/class/recruitmentcandidature.class.php',
3070 'object' => 'RecruitmentCandidature'),
3071 'societe' => array('table' => 'societe',
3072 'fields' => array('code_client', 'code_fournisseur'),
3073 'class' => 'societe/class/societe.class.php',
3074 'object' => 'Societe'),
3075 'commande' => array('table' => 'commande',
3076 'fields' => array('ref'),
3077 'class' => 'commande/class/commande.class.php',
3078 'object' => 'Commande'),
3079 'expedition' => array('table' => 'expedition',
3080 'fields' => array('ref'),
3081 'class' => 'expedition/class/expedition.class.php',
3082 'object' => 'Expedition'),
3083 'contract' => array('table' => 'contrat',
3084 'fields' => array('ref'),
3085 'class' => 'contrat/class/contrat.class.php',
3086 'object' => 'Contrat'),
3087 'fichinter' => array('table' => 'fichinter',
3088 'fields' => array('ref'),
3089 'class' => 'fichinter/class/fichinter.class.php',
3090 'object' => 'Fichinter'),
3091 'ticket' => array('table' => 'ticket',
3092 'fields' => array('ref'),
3093 'class' => 'ticket/class/ticket.class.php',
3094 'object' => 'Ticket'),
3095 'knowledgemanagement' => array('table' => 'knowledgemanagement_knowledgerecord',
3096 'fields' => array('ref'),
3097 'class' => 'knowledgemanagement/class/knowledgemanagement.class.php',
3098 'object' => 'KnowledgeRecord'),
3099 'supplier_proposal' => array('table' => 'supplier_proposal',
3100 'fields' => array('ref'),
3101 'class' => 'supplier_proposal/class/supplier_proposal.class.php',
3102 'object' => 'SupplierProposal'),
3103 'fournisseur/commande' => array('table' => 'commande_fournisseur',
3104 'fields' => array('ref', 'ref_supplier'),
3105 'class' => 'fourn/class/fournisseur.commande.class.php',
3106 'object' => 'SupplierProposal'),
3107 'facture' => array('table' => 'facture',
3108 'fields' => array('ref'),
3109 'class' => 'compta/facture/class/facture.class.php',
3110 'object' => 'Facture'),
3111 'fournisseur/facture' => array('table' => 'facture_fourn',
3112 'fields' => array('ref', 'ref_client'),
3113 'class' => 'fourn/class/fournisseur.facture.class.php',
3114 'object' => 'FactureFournisseur'),
3115 'produit' => array('table' => 'product',
3116 'fields' => array('ref'),
3117 'class' => 'product/class/product.class.php',
3118 'object' => 'Product'),
3119 'productlot' => array('table' => 'product_lot',
3120 'fields' => array('batch'),
3121 'class' => 'product/stock/class/productlot.class.php',
3122 'object' => 'Productlot'),
3123 'projet' => array('table' => 'projet',
3124 'fields' => array('ref'),
3125 'class' => 'projet/class/projet.class.php',
3126 'object' => 'Project'),
3127 'projet_task' => array('table' => 'projet_task',
3128 'fields' => array('ref'),
3129 'class' => 'projet/class/task.class.php',
3130 'object' => 'Task'),
3131 'ressource' => array('table' => 'resource',
3132 'fields' => array('ref'),
3133 'class' => 'ressource/class/dolressource.class.php',
3134 'object' => 'Dolresource'),
3135 'bom' => array('table' => 'bom_bom',
3136 'fields' => array('ref'),
3137 'class' => 'bom/class/bom.class.php',
3138 'object' => 'BOM'),
3139 'mrp' => array('table' => 'mrp_mo',
3140 'fields' => array('ref'),
3141 'class' => 'mrp/class/mo.class.php',
3142 'object' => 'Mo'),
3143 );
3144
3145 if (!is_object($hookmanager)) {
3146 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
3147 $hookmanager = new HookManager($this->db);
3148 }
3149 $hookmanager->initHooks(array('emailcolector'));
3150 $parameters = array('arrayobject' => $arrayobject);
3151 $reshook = $hookmanager->executeHooks('addmoduletoeamailcollectorjoinpiece', $parameters); // Note that $action and $object may have been modified by some hooks
3152 if ($reshook > 0) {
3153 $arrayobject = $hookmanager->resArray;
3154 }
3155
3156 $resultobj = array();
3157
3158 foreach ($arrayobject as $key => $objectdesc) {
3159 $sql = 'SELECT DISTINCT t.rowid ';
3160 $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->db->sanitize($objectdesc['table']) . ' AS t';
3161 $sql .= ' WHERE ';
3162 foreach ($objectdesc['fields'] as $field) {
3163 $sql .= "('" .$this->db->escape($subject) . "' LIKE CONCAT('%', t." . $this->db->sanitize($field) . ", '%') AND t." . $this->db->sanitize($field) . " <> '') OR ";
3164 }
3165 $sql = substr($sql, 0, -4);
3166
3167 $ressqlobj = $this->db->query($sql);
3168 if ($ressqlobj) {
3169 while ($obj = $this->db->fetch_object($ressqlobj)) {
3170 $resultobj[$key][] = $obj->rowid;
3171 }
3172 }
3173 }
3174 $dirs = array();
3175 foreach ($resultobj as $mod => $ids) {
3176 $moddesc = $arrayobject[$mod];
3177 $elementpath = $mod;
3178 dol_include_once($moddesc['class']);
3179 $objectmanaged = new $moddesc['object']($this->db);
3180 '@phan-var-force CommonObject $objectmanaged';
3181 foreach ($ids as $val) {
3182 $res = $objectmanaged->fetch($val);
3183 if ($res) {
3184 $path = ($objectmanaged->entity > 1 ? "/" . $objectmanaged->entity : '');
3185 $dirs[] = DOL_DATA_ROOT . $path . "/" . $elementpath . '/' . dol_sanitizeFileName($objectmanaged->ref) . '/';
3186 } else {
3187 $this->errors[] = 'object not found';
3188 }
3189 }
3190 }
3191 foreach ($dirs as $target) {
3192 $prefix = $this->actions[$this->id]['actionparam'];
3193 foreach ($data as $filename => $content) {
3194 $resr = saveAttachment($target, $prefix . '_' . $filename, $content);
3195 if ($resr == -1) {
3196 $this->errors[] = 'Doc not saved';
3197 }
3198 }
3199 }
3200
3201 $operationslog .= '<br>Save attachment files on disk';
3202 } else {
3203 $this->errors[] = 'no joined piece';
3204
3205 $operationslog .= '<br>No joinded files';
3206 }
3207 } elseif ($operation['type'] == 'project') {
3208 // Create project / lead
3209 $projecttocreate = new Project($this->db);
3210 $alreadycreated = $projecttocreate->fetch(0, '', '', $msgid);
3211 if ($alreadycreated == 0) {
3212 if ($thirdpartystatic->id > 0) {
3213 $projecttocreate->socid = $thirdpartystatic->id;
3214 if ($thirdpartyfoundby) {
3215 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Third party found from '.$thirdpartyfoundby);
3216 }
3217 }
3218 if ($contactstatic->id > 0) {
3219 $projecttocreate->contact_id = $contactstatic->id;
3220 if ($contactfoundby) {
3221 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Contact/address found from '.$contactfoundby);
3222 }
3223 }
3224
3225 $description = $descriptiontitle;
3226
3227 $description = dol_concatdesc($description, $descriptionmeta);
3228 $description = dol_concatdesc($description, "-----");
3229 $description = dol_concatdesc($description, $messagetext);
3230
3231 $descriptionfull = $description;
3232 if (!getDolGlobalString('MAIN_EMAILCOLLECTOR_MAIL_WITHOUT_HEADER')) {
3233 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
3234 $descriptionfull = dol_concatdesc($descriptionfull, $header);
3235 }
3236
3237 $id_opp_status = dol_getIdFromCode($this->db, 'PROSP', 'c_lead_status', 'code', 'rowid');
3238 $percent_opp_status = dol_getIdFromCode($this->db, 'PROSP', 'c_lead_status', 'code', 'percent');
3239
3240 $projecttocreate->title = $subject;
3241 $projecttocreate->date_start = $dateemail; // date of email
3242 $projecttocreate->date_end = 0;
3243 $projecttocreate->opp_status = $id_opp_status;
3244 $projecttocreate->opp_percent = $percent_opp_status;
3245 $projecttocreate->description = dol_concatdesc(dolGetFirstLineOfText(dol_string_nohtmltag($description, 2), 10), '...'.$langs->transnoentities("SeePrivateNote").'...');
3246 $projecttocreate->note_private = $descriptionfull;
3247 $projecttocreate->entity = $conf->entity;
3248 // Fields when action is an email (content should be added into agenda event)
3249 $projecttocreate->email_date = $dateemail;
3250 $projecttocreate->email_msgid = $msgid;
3251 $projecttocreate->email_from = $fromstring;
3252 $projecttocreate->email_sender = $sender;
3253 $projecttocreate->email_to = $to;
3254 $projecttocreate->email_tocc = $sendtocc;
3255 $projecttocreate->email_tobcc = $sendtobcc;
3256 $projecttocreate->email_subject = $subject;
3257 $projecttocreate->errors_to = '';
3258
3259 $savesocid = $projecttocreate->socid;
3260
3261 // Overwrite values with values extracted from source email.
3262 // This may overwrite any $projecttocreate->xxx properties.
3263 $errorforthisaction = $this->overwritePropertiesOfObject($projecttocreate, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
3264 $modele = null;
3265
3266 // Set project ref if not yet defined
3267 if (empty($projecttocreate->ref)) {
3268 // Get next Ref
3269 $defaultref = '';
3270 $modele = getDolGlobalString('PROJECT_ADDON', 'mod_project_simple');
3271
3272 // Search template files
3273 $file = '';
3274 $classname = '';
3275 $reldir = '';
3276 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3277 foreach ($dirmodels as $reldir) {
3278 $file = dol_buildpath($reldir."core/modules/project/".$modele.'.php', 0);
3279 if (file_exists($file)) {
3280 $classname = $modele;
3281 break;
3282 }
3283 }
3284
3285 if ($classname !== '') {
3286 if ($savesocid > 0) {
3287 if ($savesocid != $projecttocreate->socid) {
3288 $errorforactions++;
3289 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');
3290 }
3291 } else {
3292 if ($projecttocreate->socid > 0) {
3293 $thirdpartystatic->fetch($projecttocreate->socid);
3294 }
3295 }
3296
3297 $result = dol_include_once($reldir."core/modules/project/".$modele.'.php');
3298 $modModuleToUseForNextValue = new $classname();
3299 '@phan-var-force ModeleNumRefProjects $modModuleToUseForNextValue';
3300 $defaultref = $modModuleToUseForNextValue->getNextValue(($thirdpartystatic->id > 0 ? $thirdpartystatic : null), $projecttocreate);
3301 }
3302 $projecttocreate->ref = $defaultref;
3303 }
3304
3305
3306 if ($errorforthisaction) {
3307 $errorforactions++;
3308 } else {
3309 if (empty($projecttocreate->ref) || (is_numeric($projecttocreate->ref) && $projecttocreate->ref <= 0)) {
3310 $errorforactions++;
3311 $this->error = 'Failed to create project: Can\'t get a valid value for the field ref with numbering template = '.$modele.', thirdparty id = '.$thirdpartystatic->id;
3312
3313 $operationslog .= '<br>'.$this->error;
3314 } else {
3315 // Create project
3316 $result = $projecttocreate->create($user);
3317 if ($result <= 0) {
3318 $errorforactions++;
3319 $this->error = 'Failed to create project: '.$langs->trans($projecttocreate->error);
3320 $this->errors = $projecttocreate->errors;
3321
3322 $operationslog .= '<br>'.$this->error;
3323 } else {
3324 if ($attachments) {
3325 $destdir = $conf->project->dir_output.'/'.$projecttocreate->ref;
3326 if (!dol_is_dir($destdir)) {
3327 dol_mkdir($destdir);
3328 }
3329 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3330 foreach ($attachments as $attachment) {
3331 // $attachment->save($destdir.'/');
3332 $typeattachment = (string) $attachment->getDisposition();
3333 $filename = $attachment->getFilename();
3334 $content = $attachment->getContent();
3335 $this->saveAttachment($destdir, $filename, $content);
3336 }
3337 } else {
3338 $getMsg = $this->getmsg($connection, $imapemail, $destdir);
3339 if ($getMsg < 0) {
3340 $this->errors = array_merge($this->errors, [$this->error]);
3341 return $getMsg;
3342 }
3343 }
3344
3345 $operationslog .= '<br>Project created with attachments -> id='.dol_escape_htmltag((string) $projecttocreate->id);
3346 } else {
3347 $operationslog .= '<br>Project created without attachments -> id='.dol_escape_htmltag((string) $projecttocreate->id);
3348 }
3349 }
3350 }
3351 }
3352 } else {
3353 dol_syslog("Project already exists for msgid = ".dol_escape_htmltag($msgid).", so we do not recreate it.");
3354
3355 $operationslog .= '<br>Project already exists for msgid ='.dol_escape_htmltag($msgid);
3356 }
3357 } elseif ($operation['type'] == 'ticket') {
3358 // Create ticket
3359 $tickettocreate = new Ticket($this->db);
3360 if ($ticketalreadyexists == 0) {
3361 if ($thirdpartystatic->id > 0) {
3362 $tickettocreate->socid = $thirdpartystatic->id;
3363 $tickettocreate->fk_soc = $thirdpartystatic->id;
3364 if ($thirdpartyfoundby) {
3365 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Third party found from '.$thirdpartyfoundby);
3366 }
3367 }
3368 if ($contactstatic->id > 0) {
3369 $tickettocreate->contact_id = $contactstatic->id;
3370 if ($contactfoundby) {
3371 $descriptionmeta = dol_concatdesc($descriptionmeta, 'Contact/address found from '.$contactfoundby);
3372 }
3373 }
3374
3375 $description = $descriptiontitle;
3376
3377 $description = dol_concatdesc($description, $descriptionmeta);
3378 $description = dol_concatdesc($description, "-----");
3379 $description = dol_concatdesc($description, $messagetext);
3380
3381 $descriptionfull = $description;
3382 if (!getDolGlobalString('MAIN_EMAILCOLLECTOR_MAIL_WITHOUT_HEADER')) {
3383 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
3384 $descriptionfull = dol_concatdesc($descriptionfull, $header);
3385 }
3386
3387 $tickettocreate->subject = $subject;
3388 $tickettocreate->message = $description;
3389 $tickettocreate->type_code = (getDolGlobalString('MAIN_EMAILCOLLECTOR_TICKET_TYPE_CODE', dol_getIdFromCode($this->db, 1, 'c_ticket_type', 'use_default', 'code', 1)));
3390 $tickettocreate->category_code = (getDolGlobalString('MAIN_EMAILCOLLECTOR_TICKET_CATEGORY_CODE', dol_getIdFromCode($this->db, 1, 'c_ticket_category', 'use_default', 'code', 1)));
3391 $tickettocreate->severity_code = (getDolGlobalString('MAIN_EMAILCOLLECTOR_TICKET_SEVERITY_CODE', dol_getIdFromCode($this->db, 1, 'c_ticket_severity', 'use_default', 'code', 1)));
3392 $tickettocreate->origin_email = $from;
3393 $tickettocreate->origin_replyto = (!empty($replyto) ? $replyto : null);
3394 $tickettocreate->origin_references = (!empty($headers['References']) ? $headers['References'] : null);
3395 $tickettocreate->fk_user_create = $user->id;
3396 $tickettocreate->datec = dol_now();
3397 $tickettocreate->fk_project = $projectstatic->id;
3398 $tickettocreate->notify_tiers_at_create = getDolGlobalInt('TICKET_CHECK_NOTIFY_THIRDPARTY_AT_CREATION');
3399 $tickettocreate->note_private = $descriptionfull;
3400 $tickettocreate->entity = $conf->entity;
3401 // Fields when action is an email (content should be added into agenda event)
3402 $tickettocreate->email_date = $dateemail;
3403 $tickettocreate->email_msgid = $msgid;
3404 $tickettocreate->email_from = $fromstring;
3405 $tickettocreate->email_sender = $sender;
3406 $tickettocreate->email_to = $to;
3407 $tickettocreate->email_tocc = $sendtocc;
3408 $tickettocreate->email_tobcc = $sendtobcc;
3409 $tickettocreate->email_subject = $subject;
3410 $tickettocreate->errors_to = '';
3411
3412 //$tickettocreate->fk_contact = $contactstatic->id;
3413
3414 $savesocid = $tickettocreate->socid;
3415
3416 // Overwrite values with values extracted from source email.
3417 // This may overwrite any $projecttocreate->xxx properties.
3418 $errorforthisaction = $this->overwritePropertiesOfObject($tickettocreate, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
3419
3420 $modele = 'UNDEFINED';
3421 // Set ticket ref if not yet defined
3422 if (empty($tickettocreate->ref)) {
3423 // Get next Ref
3424 $defaultref = '';
3425 $modele = getDolGlobalString('TICKET_ADDON', 'mod_ticket_simple');
3426
3427 // Search template files
3428 $file = '';
3429 $classname = '';
3430 $reldir = '';
3431 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3432 foreach ($dirmodels as $reldir) {
3433 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
3434 if (file_exists($file)) {
3435 $classname = $modele;
3436 break;
3437 }
3438 }
3439
3440 if ($classname !== '') {
3441 if ($savesocid > 0) {
3442 if ($savesocid != $tickettocreate->socid) {
3443 $errorforactions++;
3444 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');
3445 }
3446 } else {
3447 if ($tickettocreate->socid > 0) {
3448 $thirdpartystatic->fetch($tickettocreate->socid);
3449 }
3450 }
3451
3452 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
3453 $modModuleToUseForNextValue = new $classname();
3454 '@phan-var-force ModeleNumRefTicket $modModuleToUseForNextValue';
3455 $defaultref = $modModuleToUseForNextValue->getNextValue(($thirdpartystatic->id > 0 ? $thirdpartystatic : null), $tickettocreate);
3456 }
3457 $tickettocreate->ref = $defaultref;
3458 }
3459
3460 if ($errorforthisaction) {
3461 $errorforactions++;
3462 } else {
3463 if (is_numeric($tickettocreate->ref) && $tickettocreate->ref <= 0) {
3464 $errorforactions++;
3465 $this->error = 'Failed to create ticket: Can\'t get a valid value for the field ref with numbering template = '.$modele.', thirdparty id = '.$thirdpartystatic->id;
3466 } else {
3467 // Create ticket
3468 $tickettocreate->context['actionmsg2'] = $langs->trans("ActionAC_EMAIL_IN").' - '.$langs->trans("TICKET_CREATEInDolibarr");
3469 $tickettocreate->context['actionmsg'] = $langs->trans("ActionAC_EMAIL_IN").' - '.$langs->trans("TICKET_CREATEInDolibarr");
3470 //$tickettocreate->email_fields_no_propagate_in_actioncomm = 0;
3471
3472 // Add sender to context array to make sure that confirmation e-mail can be sent by trigger script
3473 $sender_contact = new Contact($this->db);
3474 $sender_contact->fetch(0, null, '', $from);
3475 if (!empty($sender_contact->id)) {
3476 $tickettocreate->context['contact_id'] = $sender_contact->id;
3477 }
3478
3479 $result = $tickettocreate->create($user);
3480 if ($result <= 0) {
3481 $errorforactions++;
3482 $this->error = 'Failed to create ticket: '.$langs->trans($tickettocreate->error);
3483 $this->errors = $tickettocreate->errors;
3484 } else {
3485 if ($attachments) {
3486 $destdir = $conf->ticket->dir_output.'/'.$tickettocreate->ref;
3487 if (!dol_is_dir($destdir)) {
3488 dol_mkdir($destdir);
3489 }
3490 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3491 foreach ($attachments as $attachment) {
3492 // $attachment->save($destdir.'/');
3493 $typeattachment = (string) $attachment->getDisposition();
3494 $filename = $attachment->getName();
3495 $content = $attachment->getContent();
3496 $this->saveAttachment($destdir, $filename, $content);
3497 }
3498 } else {
3499 $getMsg = $this->getmsg($connection, $imapemail, $destdir);
3500 if ($getMsg < 0) {
3501 $this->errors = array_merge($this->errors, [$this->error]);
3502 return $getMsg;
3503 }
3504 }
3505
3506 $operationslog .= '<br>Ticket created with attachments -> id='.dol_escape_htmltag((string) $tickettocreate->id);
3507 } else {
3508 $operationslog .= '<br>Ticket created without attachments -> id='.dol_escape_htmltag((string) $tickettocreate->id);
3509 }
3510 }
3511 }
3512 }
3513 } else {
3514 // Do nothing in action ticket if ticket already exists.
3515 // 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
3516 // already replaced automatically by operation "recordevent".
3517 $operationslog .= '<br>Ticket already exists, so we bypass action "ticket"';
3518 }
3519 } elseif ($operation['type'] == 'candidature') {
3520 // Create candidature
3521 $candidaturetocreate = new RecruitmentCandidature($this->db);
3522
3523 $alreadycreated = $candidaturetocreate->fetch(0, '', $msgid);
3524 if ($alreadycreated == 0) {
3525 $description = $descriptiontitle;
3526 $description = dol_concatdesc($description, "-----");
3527 $description = dol_concatdesc($description, $descriptionmeta);
3528 $description = dol_concatdesc($description, "-----");
3529 $description = dol_concatdesc($description, $messagetext);
3530
3531 $descriptionfull = $description;
3532 $descriptionfull = dol_concatdesc($descriptionfull, "----- Header");
3533 $descriptionfull = dol_concatdesc($descriptionfull, $header);
3534
3535 $candidaturetocreate->subject = $subject;
3536 $candidaturetocreate->message = $description;
3537 $candidaturetocreate->type_code = 0;
3538 $candidaturetocreate->category_code = null;
3539 $candidaturetocreate->severity_code = null;
3540 $candidaturetocreate->email = $from;
3541 //$candidaturetocreate->lastname = $langs->trans("Anonymous").' - '.$from;
3542 $candidaturetocreate->fk_user_creat = $user->id;
3543 $candidaturetocreate->date_creation = dol_now();
3544 $candidaturetocreate->fk_project = $projectstatic->id;
3545 $candidaturetocreate->description = $description;
3546 $candidaturetocreate->note_private = $descriptionfull;
3547 $candidaturetocreate->entity = $conf->entity;
3548 $candidaturetocreate->email_msgid = $msgid;
3549 $candidaturetocreate->email_date = $dateemail; // date of email
3550 $candidaturetocreate->status = $candidaturetocreate::STATUS_DRAFT;
3551 //$candidaturetocreate->fk_contact = $contactstatic->id;
3552
3553 // Overwrite values with values extracted from source email.
3554 // This may overwrite any $projecttocreate->xxx properties.
3555 $errorforthisaction = $this->overwritePropertiesOfObject($candidaturetocreate, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
3556
3557 // Set candidature ref if not yet defined
3558 /*if (empty($candidaturetocreate->ref)) We do not need this because we create object in draft status
3559 {
3560 // Get next Ref
3561 $defaultref = '';
3562 $modele = empty($conf->global->CANDIDATURE_ADDON) ? 'mod_candidature_simple' : $conf->global->CANDIDATURE_ADDON;
3563
3564 // Search template files
3565 $file = ''; $classname = ''; $filefound = 0; $reldir = '';
3566 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3567 foreach ($dirmodels as $reldir)
3568 {
3569 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
3570 if (file_exists($file)) {
3571 $filefound = 1;
3572 $classname = $modele;
3573 break;
3574 }
3575 }
3576
3577 if ($filefound) {
3578 if ($savesocid > 0) {
3579 if ($savesocid != $candidaturetocreate->socid) {
3580 $errorforactions++;
3581 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');
3582 }
3583 } else {
3584 if ($candidaturetocreate->socid > 0)
3585 {
3586 $thirdpartystatic->fetch($candidaturetocreate->socid);
3587 }
3588 }
3589
3590 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
3591 $modModuleToUseForNextValue = new $classname;
3592 '@phan-var-force ModeleNumRefTicket $modModuleToUseForNextValue';
3593 $defaultref = $modModuleToUseForNextValue->getNextValue(($thirdpartystatic->id > 0 ? $thirdpartystatic : null), $tickettocreate);
3594 }
3595 $candidaturetocreate->ref = $defaultref;
3596 }*/
3597
3598 if ($errorforthisaction) {
3599 $errorforactions++;
3600 } else {
3601 // Create project
3602 $result = $candidaturetocreate->create($user);
3603 if ($result <= 0) {
3604 $errorforactions++;
3605 $this->error = 'Failed to create candidature: '.implode(', ', $candidaturetocreate->errors);
3606 $this->errors = $candidaturetocreate->errors;
3607 }
3608
3609 $operationslog .= '<br>Candidature created without attachments -> id='.dol_escape_htmltag((string) $candidaturetocreate->id);
3610 }
3611 }
3612 } elseif (substr($operation['type'], 0, 4) == 'hook') {
3613 // Create event specific on hook
3614 // this code action is hook..... for support this call
3615 if (!is_object($hookmanager)) {
3616 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
3617 $hookmanager = new HookManager($this->db);
3618 }
3619 $hookmanager->initHooks(['emailcolector']);
3620
3621 $parameters = array(
3622 'connection' => $connection,
3623 'imapemail' => $imapemail,
3624 'overview' => $overview,
3625
3626 'from' => $from,
3627 'fromtext' => $fromtext,
3628
3629 'actionparam' => $operation['actionparam'],
3630
3631 'thirdpartyid' => $thirdpartyid,
3632 'objectid' => $objectid,
3633 'objectemail' => $objectemail,
3634
3635 'messagetext' => $messagetext,
3636 'subject' => $subject,
3637 'header' => $header,
3638 'attachments' => $attachments,
3639 );
3640 $reshook = $hookmanager->executeHooks('doCollectImapOneCollector', $parameters, $this, $operation['type']);
3641
3642 if ($reshook < 0) {
3643 $errorforthisaction++;
3644 $this->error = $hookmanager->resPrint;
3645 }
3646 if ($errorforthisaction) {
3647 $errorforactions++;
3648 $operationslog .= '<br>Hook doCollectImapOneCollector executed with error';
3649 } else {
3650 $operationslog .= '<br>Hook doCollectImapOneCollector executed without error';
3651 }
3652 }
3653
3654 if (!$errorforactions) {
3655 $nbactiondoneforemail++;
3656 }
3657 }
3658 }
3659
3660 // Error for email or not ?
3661 if (!$errorforactions) {
3662 if (!empty($targetdir)) {
3663 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3664 // Move mail using PHP-IMAP
3665 dol_syslog("EmailCollector::doCollectOneCollector move message ".($imapemail->getHeader()->get('subject'))." to ".$targetdir, LOG_DEBUG);
3666 $operationslog .= '<br>Move mail '.($this->uidAsString($imapemail)).' - '.$msgid.' - '.$imapemail->getHeader()->get('subject').' to '.$targetdir;
3667
3668 $arrayofemailtodelete[$this->uidAsString($imapemail)] = $imapemail;
3669 // Note: Real move is done later using $arrayofemailtodelete
3670 } else {
3671 dol_syslog("EmailCollector::doCollectOneCollector move message ".($this->uidAsString($imapemail))." to ".$connectstringtarget, LOG_DEBUG);
3672 $operationslog .= '<br>Move mail '.($this->uidAsString($imapemail)).' - '.$msgid;
3673
3674 $arrayofemailtodelete[$imapemail] = $msgid;
3675 // Note: Real move is done later using $arrayofemailtodelete
3676 }
3677 } else {
3678 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3679 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);
3680 } else {
3681 dol_syslog("EmailCollector::doCollectOneCollector message ".($this->uidAsString($imapemail))." to ".$connectstringtarget." was set to read", LOG_DEBUG);
3682 }
3683 }
3684 } else {
3685 $errorforemail++;
3686 }
3687
3688
3689 unset($objectemail);
3690 unset($projectstatic);
3691 unset($thirdpartystatic);
3692 unset($contactstatic);
3693
3694 $nbemailprocessed++;
3695
3696 if (!$errorforemail) {
3697 $nbactiondone += $nbactiondoneforemail;
3698 $nbemailok++;
3699
3700 if (empty($mode)) {
3701 $this->db->commit();
3702 } else {
3703 $this->db->rollback();
3704 }
3705
3706 // Stop the loop to process email if we reach maximum collected per collect
3707 if ($this->maxemailpercollect > 0 && $nbemailok >= $this->maxemailpercollect) {
3708 dol_syslog("EmailCollect::doCollectOneCollector We reach maximum of ".$nbemailok." collected with success, so we stop this collector now.");
3709 $datelastok = empty($headers['Date']) ? false : strtotime($headers['Date']); // Set datetime
3710 break;
3711 }
3712 } else {
3713 $error++;
3714
3715 $this->db->rollback();
3716 }
3717 }
3718
3719 $output = $langs->trans('XEmailsDoneYActionsDone', $nbemailprocessed, $nbemailok, $nbactiondone);
3720
3721 dol_syslog("End of loop on emails", LOG_INFO, -1);
3722 } else {
3723 $langs->load("admin");
3724 $output = $langs->trans('NoNewEmailToProcess');
3725 $output .= ' (defaultlang='.$langs->defaultlang.')';
3726 }
3727
3728 // Disconnect
3729 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3730 // We sort to move/delete array with the more recent first (with higher number) so renumbering does not affect number of others to delete
3731 krsort($arrayofemailtodelete, SORT_NUMERIC);
3732
3733 foreach ($arrayofemailtodelete as $imapemailnum => $imapemail) {
3734 dol_syslog("EmailCollect::doCollectOneCollector delete email ".$imapemailnum);
3735
3736 $operationslog .= "<br> move email ".$imapemailnum.($mode > 0 ? ' (test)' : '');
3737
3738 if (empty($mode) && empty($error)) {
3739 $tmptargetdir = $targetdir;
3740 if (!getDolGlobalString('MAIL_DISABLE_UTF7_ENCODE_OF_DIR')) {
3741 $tmptargetdir = $this->getEncodedUtf7($targetdir);
3742 }
3743
3744 $result = 0;
3745 try {
3746 $result = $imapemail->move($tmptargetdir, false);
3747 } catch (Exception $e) {
3748 // Nothing to do. $result will remain 0
3749 $operationslog .= '<br>Exception !!!! '.$e->getMessage();
3750 }
3751 if (empty($result)) {
3752 dol_syslog("Failed to move email into target directory ".$targetdir);
3753 $operationslog .= '<br>Failed to move email into target directory '.$targetdir;
3754 $error++;
3755 }
3756 }
3757 }
3758
3759 if (empty($mode) && empty($error)) {
3760 dol_syslog("Expunge", LOG_DEBUG);
3761 $operationslog .= "<br>Expunge";
3762
3763 $client->expunge(); // To validate all moves
3764 }
3765
3766 $client->disconnect();
3767 } else {
3768 foreach ($arrayofemailtodelete as $imapemail => $msgid) {
3769 dol_syslog("EmailCollect::doCollectOneCollector delete email ".$imapemail." ".$msgid);
3770
3771 $operationslog .= "<br> delete email ".$imapemail." ".$msgid.($mode > 0 ? ' (test)' : '');
3772
3773 if (empty($mode) && empty($error)) {
3774 $res = imap_mail_move($connection, $imapemail, $targetdir, CP_UID);
3775 if (!$res) {
3776 // $errorforemail++; // Not in loop, not needed, not initialised
3777 $this->error = imap_last_error();
3778 $this->errors[] = $this->error;
3779
3780 $operationslog .= '<br>Error in move '.$this->error;
3781
3782 dol_syslog(imap_last_error());
3783 }
3784 }
3785 }
3786
3787 if (empty($mode) && empty($error)) {
3788 dol_syslog("Expunge", LOG_DEBUG);
3789 $operationslog .= "<br>Expunge";
3790
3791 imap_expunge($connection); // To validate all moves
3792 }
3793 imap_close($connection);
3794 }
3795
3796 $this->datelastresult = $now;
3797 $this->lastresult = $output;
3798 if (getDolGlobalString('MAIN_IMAP_USE_PHPIMAP')) {
3799 $this->debuginfo .= 'IMAP search array used : '.$search;
3800 } else {
3801 $this->debuginfo .= 'IMAP search string used : '.$search;
3802 }
3803 if ($searchhead) {
3804 $this->debuginfo .= '<br>Then search string into email header : '.dol_escape_htmltag($searchhead);
3805 }
3806 if ($operationslog) {
3807 $this->debuginfo .= $operationslog;
3808 }
3809
3810 if (empty($error) && empty($mode)) {
3811 $this->datelastok = $datelastok;
3812 }
3813
3814 if (!empty($this->errors)) {
3815 $this->lastresult .= "<br>".implode("<br>", $this->errors);
3816 }
3817 $this->codelastresult = ($error ? 'KO' : 'OK');
3818
3819 if (empty($mode)) {
3820 $this->update($user);
3821 }
3822
3823 dol_syslog("EmailCollector::doCollectOneCollector end error=".$error, LOG_INFO);
3824
3825 return $error ? -1 : 1;
3826 }
3827
3828
3829
3830 // Loop to get part html and plain. Code found on PHP imap_fetchstructure documentation
3831
3840 private function getmsg($mbox, $mid, $destdir = ''): int
3841 {
3842 // input $mbox = IMAP stream, $mid = message id
3843 // output all the following:
3844 global $charset, $htmlmsg, $plainmsg, $attachments;
3845 $htmlmsg = $plainmsg = $charset = '';
3846 $attachments = array();
3847
3848 // HEADER
3849 //$h = imap_header($mbox,$mid);
3850 // add code here to get date, from, to, cc, subject...
3851
3852 // BODY
3853 $s = imap_fetchstructure($mbox, $mid, FT_UID);
3854 if ($s === false) {
3855 $this->errors = array_merge($this->errors, [imap_last_error()]);
3856 return -1;
3857 }
3858
3859 if (empty($s->parts)) {
3860 // simple
3861 $this->getpart($mbox, $mid, $s, '0'); // pass '0' as part-number
3862 } else {
3863 // multipart: cycle through each part
3864 foreach ($s->parts as $partno0 => $p) {
3865 $this->getpart($mbox, $mid, $p, (string) ($partno0 + 1), $destdir);
3866 }
3867 }
3868
3869 return 1;
3870 }
3871
3872 /* partno string
3873 0 multipart/mixed
3874 1 multipart/alternative
3875 1.1 text/plain
3876 1.2 text/html
3877 2 message/rfc822
3878 2 multipart/mixed
3879 2.1 multipart/alternative
3880 2.1.1 text/plain
3881 2.1.2 text/html
3882 2.2 message/rfc822
3883 2.2 multipart/alternative
3884 2.2.1 text/plain
3885 2.2.2 text/html
3886 */
3887
3898 private function getpart($mbox, $mid, $p, $partno, $destdir = '')
3899 {
3900 // $partno = '1', '2', '2.1', '2.1.3', etc for multipart, 0 if simple
3901 global $htmlmsg, $plainmsg, $charset, $attachments;
3902
3903 // DECODE DATA
3904 $data = ($partno) ?
3905 imap_fetchbody($mbox, $mid, $partno, FT_UID) : // multipart
3906 imap_body($mbox, $mid, FT_UID); // simple
3907 // Any part may be encoded, even plain text messages, so check everything.
3908 if ($p->encoding == 4) {
3909 $data = quoted_printable_decode($data);
3910 } elseif ($p->encoding == 3) {
3911 $data = base64_decode($data);
3912 }
3913
3914 // PARAMETERS
3915 // get all parameters, like charset, filenames of attachments, etc.
3916 $params = array();
3917 if ($p->parameters) {
3918 foreach ($p->parameters as $x) {
3919 $params[strtolower($x->attribute)] = $x->value;
3920 }
3921 }
3922 if (!empty($p->dparameters)) {
3923 foreach ($p->dparameters as $x) {
3924 $params[strtolower($x->attribute)] = $x->value;
3925 }
3926 }
3927 '@phan-var-force array{filename?:string,name?:string,charset?:string} $params';
3928
3929 // ATTACHMENT
3930 // Any part with a filename is an attachment,
3931 // so an attached text file (type 0) is not mistaken as the message.
3932 if (!empty($params['filename']) || !empty($params['name'])) {
3933 // filename may be given as 'Filename' or 'Name' or both
3934 $filename = $params['filename'] ?? $params['name'];
3935 // filename may be encoded, so see imap_mime_header_decode()
3936 $attachments[$filename] = $data; // this is a problem if two files have same name
3937
3938 if (strlen($destdir)) {
3939 if (substr($destdir, -1) != '/') {
3940 $destdir .= '/';
3941 }
3942
3943 // Get file name (with extension)
3944 $file_name_complete = $filename;
3945 $destination = $destdir.$file_name_complete;
3946
3947 // Extract file extension
3948 $extension = pathinfo($file_name_complete, PATHINFO_EXTENSION);
3949
3950 // Extract file name without extension
3951 $file_name = pathinfo($file_name_complete, PATHINFO_FILENAME);
3952
3953 // Save an original file name variable to track while renaming if file already exists
3954 $file_name_original = $file_name;
3955
3956 // Increment file name by 1
3957 $num = 1;
3958
3963 while (file_exists($destdir.$file_name.".".$extension)) {
3964 $file_name = $file_name_original . ' (' . $num . ')';
3965 $file_name_complete = $file_name . "." . $extension;
3966 $destination = $destdir.$file_name_complete;
3967 $num++;
3968 }
3969
3970 $destination = dol_sanitizePathName($destination);
3971
3972 file_put_contents($destination, $data);
3973 }
3974 }
3975
3976 // TEXT
3977 if ($p->type == 0 && $data) {
3978 if (!empty($params['charset'])) {
3979 $data = $this->convertStringEncoding($data, $params['charset']);
3980 }
3981 // Messages may be split in different parts because of inline attachments,
3982 // so append parts together with blank row.
3983 if (strtolower($p->subtype) == 'plain') {
3984 $plainmsg .= trim($data)."\n\n";
3985 } else {
3986 $htmlmsg .= $data."<br><br>";
3987 }
3988 $charset = $params['charset']; // assume all parts are same charset
3989 } elseif ($p->type == 2 && $data) {
3990 // EMBEDDED MESSAGE
3991 // Many bounce notifications embed the original message as type 2,
3992 // but AOL uses type 1 (multipart), which is not handled here.
3993 // There are no PHP functions to parse embedded messages,
3994 // so this just appends the raw source to the main message.
3995 if (!empty($params['charset'])) {
3996 $data = $this->convertStringEncoding($data, $params['charset']);
3997 }
3998 $plainmsg .= $data."\n\n";
3999 }
4000
4001 // SUBPART RECURSION
4002 if (!empty($p->parts)) {
4003 foreach ($p->parts as $partno0 => $p2) {
4004 $this->getpart($mbox, $mid, $p2, $partno.'.'.($partno0 + 1), $destdir); // 1.2, 1.2.1, etc.
4005 }
4006 }
4007 }
4008
4018 protected function convertStringEncoding($string, $fromEncoding, $toEncoding = 'UTF-8')
4019 {
4020 if (!$string || $fromEncoding == $toEncoding) {
4021 return $string;
4022 }
4023 $convertedString = function_exists('iconv') ? @iconv($fromEncoding, $toEncoding.'//IGNORE', $string) : null;
4024 if (!$convertedString && extension_loaded('mbstring')) {
4025 $convertedString = @mb_convert_encoding($string, $toEncoding, $fromEncoding);
4026 }
4027 if (!$convertedString) {
4028 throw new Exception('Mime string encoding conversion failed');
4029 }
4030 return $convertedString;
4031 }
4032
4043 protected function decodeSMTPSubject($subject)
4044 {
4045 // Decode $overview[0]->subject according to RFC2047
4046 // Can use also imap_mime_header_decode($str)
4047 // Can use also mb_decode_mimeheader($str)
4048 // Can use also iconv_mime_decode($str, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8')
4049 if (function_exists('imap_mime_header_decode') && function_exists('iconv_mime_decode')) {
4050 $elements = imap_mime_header_decode($subject);
4051 $newstring = '';
4052 if (!empty($elements)) {
4053 $num = count($elements);
4054 for ($i = 0; $i < $num; $i++) {
4055 $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));
4056 $newstring .= $stringinutf8;
4057 }
4058 $subject = $newstring;
4059 }
4060 } elseif (!function_exists('mb_decode_mimeheader')) {
4061 $subject = mb_decode_mimeheader($subject);
4062 } elseif (function_exists('iconv_mime_decode')) {
4063 $subject = iconv_mime_decode($subject, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8');
4064 }
4065
4066 return $subject;
4067 }
4068
4077 private function saveAttachment($destdir, $filename, $content)
4078 {
4079 require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';
4080
4081 $tmparraysize = getDefaultImageSizes();
4082 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
4083 $maxheightsmall = $tmparraysize['maxheightsmall'];
4084 $maxwidthmini = $tmparraysize['maxwidthmini'];
4085 $maxheightmini = $tmparraysize['maxheightmini'];
4086 $quality = $tmparraysize['quality'];
4087
4088 file_put_contents($destdir.'/'.$filename, $content);
4089 if (image_format_supported($filename) == 1) {
4090 // Create thumbs
4091 vignette($destdir.'/'.$filename, $maxwidthsmall, $maxheightsmall, '_small', $quality, "thumbs");
4092 // Create mini thumbs for image (Ratio is near 16/9)
4093 vignette($destdir.'/'.$filename, $maxwidthmini, $maxheightmini, '_mini', $quality, "thumbs");
4094 }
4095 addFileIntoDatabaseIndex($destdir, $filename);
4096 }
4097
4104 protected function uidAsString($imapemail)
4105 {
4106 if (is_object($imapemail)) {
4107 return $imapemail->getAttributes()["uid"];
4108 } else {
4109 return (string) $imapemail;
4110 }
4111 }
4112}
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.