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