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