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