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