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