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