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