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