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