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