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