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