dolibarr  18.0.0-beta
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  }
3123 
3124  $parameters = array(
3125  'connection'=> $connection,
3126  'imapemail'=>$imapemail,
3127  'overview'=>$overview,
3128 
3129  'from' => $from,
3130  'fromtext' => $fromtext,
3131 
3132  'actionparam'=> $operation['actionparam'],
3133 
3134  'thirdpartyid' => $thirdpartyid,
3135  'objectid'=> $objectid,
3136  'objectemail'=> $objectemail,
3137 
3138  'messagetext'=>$messagetext,
3139  'subject'=>$subject,
3140  'header'=>$header,
3141  'attachments'=>$attachments,
3142  );
3143  $reshook = $hookmanager->executeHooks('doCollectImapOneCollector', $parameters, $this, $operation['type']);
3144 
3145  if ($reshook < 0) {
3146  $errorforthisaction++;
3147  $this->error = $hookmanager->resPrint;
3148  }
3149  if ($errorforthisaction) {
3150  $errorforactions++;
3151  $operationslog .= '<br>Hook doCollectImapOneCollector executed with error';
3152  } else {
3153  $operationslog .= '<br>Hook doCollectImapOneCollector executed without error';
3154  }
3155  }
3156 
3157  if (!$errorforactions) {
3158  $nbactiondoneforemail++;
3159  }
3160  }
3161  }
3162 
3163  // Error for email or not ?
3164  if (!$errorforactions) {
3165  if (!empty($targetdir)) {
3166  if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
3167  // Move mail using PHP-IMAP
3168  dol_syslog("EmailCollector::doCollectOneCollector move message ".($imapemail->getHeader()->get('subject'))." to ".$targetdir, LOG_DEBUG);
3169  if (empty($mode)) {
3170  $imapemail->move($targetdir);
3171  }
3172  } else {
3173  dol_syslog("EmailCollector::doCollectOneCollector move message ".((string) $imapemail)." to ".$connectstringtarget, LOG_DEBUG);
3174  $operationslog .= '<br>Move mail '.((string) $imapemail).' - '.$msgid;
3175 
3176  $arrayofemailtodelete[$imapemail] = $msgid;
3177  }
3178  } else {
3179  if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
3180  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);
3181  } else {
3182  dol_syslog("EmailCollector::doCollectOneCollector message ".((string) $imapemail)." to ".$connectstringtarget." was set to read", LOG_DEBUG);
3183  }
3184  }
3185  } else {
3186  $errorforemail++;
3187  }
3188 
3189 
3190  unset($objectemail);
3191  unset($projectstatic);
3192  unset($thirdpartystatic);
3193  unset($contactstatic);
3194 
3195  $nbemailprocessed++;
3196 
3197  if (!$errorforemail) {
3198  $nbactiondone += $nbactiondoneforemail;
3199  $nbemailok++;
3200 
3201  if (empty($mode)) {
3202  $this->db->commit();
3203  } else {
3204  $this->db->rollback();
3205  }
3206 
3207  // Stop the loop to process email if we reach maximum collected per collect
3208  if ($this->maxemailpercollect > 0 && $nbemailok >= $this->maxemailpercollect) {
3209  dol_syslog("EmailCollect::doCollectOneCollector We reach maximum of ".$nbemailok." collected with success, so we stop this collector now.");
3210  break;
3211  }
3212  } else {
3213  $error++;
3214 
3215  $this->db->rollback();
3216  }
3217  }
3218 
3219  $output = $langs->trans('XEmailsDoneYActionsDone', $nbemailprocessed, $nbemailok, $nbactiondone);
3220 
3221  dol_syslog("End of loop on emails", LOG_INFO, -1);
3222  } else {
3223  $langs->load("admin");
3224  $output = $langs->trans('NoNewEmailToProcess');
3225  }
3226 
3227  // Disconnect
3228  if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) {
3229  $client->disconnect();
3230  } else {
3231  foreach ($arrayofemailtodelete as $imapemail => $msgid) {
3232  dol_syslog("EmailCollect::doCollectOneCollector delete email ".$imapemail." ".$msgid);
3233 
3234  $operationslog .= "<br> delete email ".$imapemail." ".$msgid;
3235 
3236  if (empty($mode) && empty($error)) {
3237  $res = imap_mail_move($connection, $imapemail, $targetdir, CP_UID);
3238  if ($res == false) {
3239  $errorforemail++;
3240  $this->error = imap_last_error();
3241  $this->errors[] = $this->error;
3242 
3243  $operationslog .= '<br>Error in move '.$this->error;
3244 
3245  dol_syslog(imap_last_error());
3246  }
3247  }
3248  }
3249 
3250  if (empty($mode) && empty($error)) {
3251  dol_syslog("Expunge", LOG_DEBUG);
3252  $operationslog .= "<br>Expunge";
3253 
3254  imap_expunge($connection); // To validate all moves
3255  }
3256  imap_close($connection);
3257  }
3258 
3259  $this->datelastresult = $now;
3260  $this->lastresult = $output;
3261  $this->debuginfo .= 'IMAP search string used : '.$search;
3262  if ($searchhead) {
3263  $this->debuginfo .= '<br>Then search string into email header : '.dol_escape_htmltag($searchhead);
3264  }
3265  if ($operationslog) {
3266  $this->debuginfo .= $operationslog;
3267  }
3268 
3269  if (empty($error) && empty($mode)) {
3270  $this->datelastok = $now;
3271  }
3272 
3273  if (!empty($this->errors)) {
3274  $this->lastresult .= "<br>".join("<br>", $this->errors);
3275  }
3276  $this->codelastresult = ($error ? 'KO' : 'OK');
3277 
3278  if (empty($mode)) {
3279  $this->update($user);
3280  }
3281 
3282  dol_syslog("EmailCollector::doCollectOneCollector end", LOG_INFO);
3283 
3284  return $error ? -1 : 1;
3285  }
3286 
3287 
3288 
3289  // Loop to get part html and plain. Code found on PHP imap_fetchstructure documentation
3290 
3299  private function getmsg($mbox, $mid, $destdir = '')
3300  {
3301  // input $mbox = IMAP stream, $mid = message id
3302  // output all the following:
3303  global $charset, $htmlmsg, $plainmsg, $attachments;
3304  $htmlmsg = $plainmsg = $charset = '';
3305  $attachments = array();
3306 
3307  // HEADER
3308  //$h = imap_header($mbox,$mid);
3309  // add code here to get date, from, to, cc, subject...
3310 
3311  // BODY
3312  $s = imap_fetchstructure($mbox, $mid, FT_UID);
3313 
3314 
3315  if (!$s->parts) {
3316  // simple
3317  $this->getpart($mbox, $mid, $s, 0); // pass 0 as part-number
3318  } else {
3319  // multipart: cycle through each part
3320  foreach ($s->parts as $partno0 => $p) {
3321  $this->getpart($mbox, $mid, $p, $partno0 + 1, $destdir);
3322  }
3323  }
3324  }
3325 
3326  /* partno string
3327  0 multipart/mixed
3328  1 multipart/alternative
3329  1.1 text/plain
3330  1.2 text/html
3331  2 message/rfc822
3332  2 multipart/mixed
3333  2.1 multipart/alternative
3334  2.1.1 text/plain
3335  2.1.2 text/html
3336  2.2 message/rfc822
3337  2.2 multipart/alternative
3338  2.2.1 text/plain
3339  2.2.2 text/html
3340  */
3341 
3352  private function getpart($mbox, $mid, $p, $partno, $destdir = '')
3353  {
3354  // $partno = '1', '2', '2.1', '2.1.3', etc for multipart, 0 if simple
3355  global $htmlmsg, $plainmsg, $charset, $attachments;
3356 
3357  // DECODE DATA
3358  $data = ($partno) ?
3359  imap_fetchbody($mbox, $mid, $partno, FT_UID) : // multipart
3360  imap_body($mbox, $mid, FT_UID); // simple
3361  // Any part may be encoded, even plain text messages, so check everything.
3362  if ($p->encoding == 4) {
3363  $data = quoted_printable_decode($data);
3364  } elseif ($p->encoding == 3) {
3365  $data = base64_decode($data);
3366  }
3367 
3368  // PARAMETERS
3369  // get all parameters, like charset, filenames of attachments, etc.
3370  $params = array();
3371  if ($p->parameters) {
3372  foreach ($p->parameters as $x) {
3373  $params[strtolower($x->attribute)] = $x->value;
3374  }
3375  }
3376  if ($p->dparameters) {
3377  foreach ($p->dparameters as $x) {
3378  $params[strtolower($x->attribute)] = $x->value;
3379  }
3380  }
3381 
3382  // ATTACHMENT
3383  // Any part with a filename is an attachment,
3384  // so an attached text file (type 0) is not mistaken as the message.
3385  if ($params['filename'] || $params['name']) {
3386  // filename may be given as 'Filename' or 'Name' or both
3387  $filename = ($params['filename']) ? $params['filename'] : $params['name'];
3388  // filename may be encoded, so see imap_mime_header_decode()
3389  $attachments[$filename] = $data; // this is a problem if two files have same name
3390 
3391  // Get file name (with extension)
3392  $file_name_complete = $params['filename'];
3393 
3394 
3395  $destination = $destdir.'/'.$file_name_complete;
3396 
3397  // Extract file extension
3398  $extension = pathinfo($file_name_complete, PATHINFO_EXTENSION);
3399 
3400  // Extract file name without extension
3401  $file_name = pathinfo($file_name_complete, PATHINFO_FILENAME);
3402 
3403  // Save an original file name variable to track while renaming if file already exists
3404  $file_name_original = $file_name;
3405 
3406  // Increment file name by 1
3407  $num = 1;
3408 
3413  while (file_exists($destdir."/".$file_name.".".$extension)) {
3414  $file_name = $file_name_original . ' (' . $num . ')';
3415  $file_name_complete = $file_name . "." . $extension;
3416  $destination = $destdir.'/'.$file_name_complete;
3417  $num++;
3418  }
3419 
3420  $destination = dol_sanitizePathName($destination);
3421 
3422  file_put_contents($destination, $data);
3423  }
3424 
3425  // TEXT
3426  if ($p->type == 0 && $data) {
3427  if (!empty($params['charset'])) {
3428  $data = $this->convertStringEncoding($data, $params['charset']);
3429  }
3430  // Messages may be split in different parts because of inline attachments,
3431  // so append parts together with blank row.
3432  if (strtolower($p->subtype) == 'plain') {
3433  $plainmsg .= trim($data)."\n\n";
3434  } else {
3435  $htmlmsg .= $data."<br><br>";
3436  }
3437  $charset = $params['charset']; // assume all parts are same charset
3438  } elseif ($p->type == 2 && $data) {
3439  // EMBEDDED MESSAGE
3440  // Many bounce notifications embed the original message as type 2,
3441  // but AOL uses type 1 (multipart), which is not handled here.
3442  // There are no PHP functions to parse embedded messages,
3443  // so this just appends the raw source to the main message.
3444  if (!empty($params['charset'])) {
3445  $data = $this->convertStringEncoding($data, $params['charset']);
3446  }
3447  $plainmsg .= $data."\n\n";
3448  }
3449 
3450  // SUBPART RECURSION
3451  if ($p->parts) {
3452  foreach ($p->parts as $partno0 => $p2) {
3453  $this->getpart($mbox, $mid, $p2, $partno.'.'.($partno0 + 1)); // 1.2, 1.2.1, etc.
3454  }
3455  }
3456  }
3457 
3467  protected function convertStringEncoding($string, $fromEncoding, $toEncoding = 'UTF-8')
3468  {
3469  if (!$string || $fromEncoding == $toEncoding) {
3470  return $string;
3471  }
3472  $convertedString = function_exists('iconv') ? @iconv($fromEncoding, $toEncoding.'//IGNORE', $string) : null;
3473  if (!$convertedString && extension_loaded('mbstring')) {
3474  $convertedString = @mb_convert_encoding($string, $toEncoding, $fromEncoding);
3475  }
3476  if (!$convertedString) {
3477  throw new Exception('Mime string encoding conversion failed');
3478  }
3479  return $convertedString;
3480  }
3481 
3492  protected function decodeSMTPSubject($subject)
3493  {
3494  // Decode $overview[0]->subject according to RFC2047
3495  // Can use also imap_mime_header_decode($str)
3496  // Can use also mb_decode_mimeheader($str)
3497  // Can use also iconv_mime_decode($str, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8')
3498  if (function_exists('imap_mime_header_decode') && function_exists('iconv_mime_decode')) {
3499  $elements = imap_mime_header_decode($subject);
3500  $newstring = '';
3501  if (!empty($elements)) {
3502  $num = count($elements);
3503  for ($i = 0; $i < $num; $i++) {
3504  $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));
3505  $newstring .= $stringinutf8;
3506  }
3507  $subject = $newstring;
3508  }
3509  } elseif (!function_exists('mb_decode_mimeheader')) {
3510  $subject = mb_decode_mimeheader($subject);
3511  } elseif (function_exists('iconv_mime_decode')) {
3512  $subject = iconv_mime_decode($subject, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8');
3513  }
3514 
3515  return $subject;
3516  }
3517 
3524  protected function removeEmoji($text)
3525  {
3526  // Supprimer les caractères emoji en utilisant une expression régulière
3527  $text = preg_replace('/[\x{1F600}-\x{1F64F}]/u', '', $text);
3528  $text = preg_replace('/[\x{1F300}-\x{1F5FF}]/u', '', $text);
3529  $text = preg_replace('/[\x{1F680}-\x{1F6FF}]/u', '', $text);
3530  $text = preg_replace('/[\x{2600}-\x{26FF}]/u', '', $text);
3531  $text = preg_replace('/[\x{2700}-\x{27BF}]/u', '', $text);
3532  $text = preg_replace('/[\x{1F900}-\x{1F9FF}]/u', '', $text);
3533  $text = preg_replace('/[\x{1F1E0}-\x{1F1FF}]/u', '', $text);
3534 
3535  return $text;
3536  }
3537 }
make_substitutions
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
Definition: functions.lib.php:8337
CommonObject\deleteCommon
deleteCommon(User $user, $notrigger=false, $forcechilddeletion=0)
Delete object in database.
Definition: commonobject.class.php:9539
Societe
Class to manage third parties objects (customers, suppliers, prospects...)
Definition: societe.class.php:51
EmailCollector\convertStringEncoding
convertStringEncoding($string, $fromEncoding, $toEncoding='UTF-8')
Converts a string from one encoding to another.
Definition: emailcollector.class.php:3467
dol_sanitizePathName
dol_sanitizePathName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a path name.
Definition: functions.lib.php:1348
dol_escape_htmltag
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
Definition: functions.lib.php:1594
CommonObject\fetchCommon
fetchCommon($id, $ref=null, $morewhere='')
Load object in memory from the database.
Definition: commonobject.class.php:9329
dol_trunc
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.
Definition: functions.lib.php:4049
Reception
Class to manage receptions.
Definition: reception.class.php:50
EmailCollector\info
info($id)
Charge les informations d'ordre info dans l'objet commande.
Definition: emailcollector.class.php:674
dol_sanitizeFileName
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
Definition: functions.lib.php:1322
Expedition
Class to manage shipments.
Definition: expedition.class.php:52
Project
Class to manage projects.
Definition: project.class.php:36
ActionComm
Class to manage agenda events (actions)
Definition: actioncomm.class.php:38
EmailCollector\fetchAll
fetchAll(User $user, $activeOnly=0, $sortfield='s.rowid', $sortorder='ASC', $limit=100, $page=0)
Load object lines in memory from the database.
Definition: emailcollector.class.php:468
DoliDB
Class to manage Dolibarr database access.
Definition: DoliDB.class.php:30
EmailCollectorAction
Class for EmailCollectorAction.
Definition: emailcollectoraction.class.php:31
GETPOST
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
Definition: functions.lib.php:607
FactureFournisseur
Class to manage suppliers invoices.
Definition: fournisseur.facture.class.php:51
dol_print_error
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
Definition: functions.lib.php:5096
EmailCollector\getpart
getpart($mbox, $mid, $p, $partno, $destdir='')
Sub function for getpart().
Definition: emailcollector.class.php:3352
getAttachments
getAttachments($jk, $mbox)
Get attachments of a given mail.
Definition: emailcollector.lib.php:118
dol_include_once
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
Definition: functions.lib.php:1128
Translate
Class to manage translations.
Definition: translate.class.php:30
dol_buildpath
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
Definition: functions.lib.php:1157
EmailCollector\fetch
fetch($id, $ref=null)
Load object in memory from the database.
Definition: emailcollector.class.php:430
EmailCollectorFilter
Class for EmailCollectorFilter.
Definition: emailcollectorfilter.class.php:33
Task
Class to manage tasks.
Definition: task.class.php:39
EmailCollector\initAsSpecimen
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: emailcollector.class.php:704
EmailCollector\doCollectOneCollector
doCollectOneCollector($mode=0)
Execute collect for current collector loaded previously with fetch.
Definition: emailcollector.class.php:1080
utf8_valid
utf8_valid($str)
Check if a string is in UTF8.
Definition: functions.lib.php:8986
CommonObject\initAsSpecimenCommon
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: commonobject.class.php:9867
EmailCollector\fetchFilters
fetchFilters()
Fetch filters.
Definition: emailcollector.class.php:718
Facture
Class to manage invoices.
Definition: facture.class.php:60
CommonObject
Parent class of all other business classes (invoices, contracts, proposals, orders,...
Definition: commonobject.class.php:45
EmailCollector\LibStatut
LibStatut($status, $mode=0)
Return the status.
Definition: emailcollector.class.php:648
EmailCollector\getNomUrl
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionaly the picto)
Definition: emailcollector.class.php:564
EmailCollector\createFromClone
createFromClone(User $user, $fromid)
Clone and object into another one.
Definition: emailcollector.class.php:361
dol_print_date
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
Definition: functions.lib.php:2665
dolExplodeIntoArray
dolExplodeIntoArray($string, $delimiter=';', $kv='=')
Split a string with 2 keys into key array.
Definition: functions.lib.php:9914
dol_concatdesc
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...
Definition: functions.lib.php:7705
CommonObject\createCommon
createCommon(User $user, $notrigger=false)
Create object into database.
Definition: commonobject.class.php:9162
Exception
EmailCollector\getLibStatut
getLibStatut($mode=0)
Return label of the status.
Definition: emailcollector.class.php:635
EmailCollector\getConnectStringIMAP
getConnectStringIMAP()
Return the connectstring to use with IMAP connection function.
Definition: emailcollector.class.php:784
dol_getIdFromCode
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='')
Return an id or code from a code or id.
Definition: functions.lib.php:9062
EmailCollector\doCollect
doCollect()
Action executed by scheduler CAN BE A CRON TASK.
Definition: emailcollector.class.php:849
Commande
Class to manage customers orders.
Definition: commande.class.php:47
EmailCollector\getmsg
getmsg($mbox, $mid, $destdir='')
getmsg
Definition: emailcollector.class.php:3299
$sql
if(isModEnabled('facture') &&!empty($user->rights->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
getFileData
getFileData($jk, $fpos, $type, $mbox)
Get content of a joined file from its position into a given email.
Definition: emailcollector.lib.php:154
RecruitmentCandidature
Class for RecruitmentCandidature.
Definition: recruitmentcandidature.class.php:32
dol_string_nohtmltag
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
Definition: functions.lib.php:7034
dol_syslog
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
Definition: functions.lib.php:1732
Contact
Class to manage contact/addresses.
Definition: contact.class.php:42
dolEncrypt
dolEncrypt($chain, $key='', $ciphering='AES-256-CTR', $forceseed='')
Encode a string with a symetric encryption.
Definition: security.lib.php:121
setEventMessages
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
Definition: functions.lib.php:8644
EmailCollector\__construct
__construct(DoliDB $db)
Constructor.
Definition: emailcollector.class.php:248
EmailCollector\decodeSMTPSubject
decodeSMTPSubject($subject)
Decode a subject string according to RFC2047 Example: '=?Windows-1252?Q?RE=A0:_ABC?...
Definition: emailcollector.class.php:3492
Adherent
Class to manage members of a foundation.
Definition: adherent.class.php:47
CommonObject\updateCommon
updateCommon(User $user, $notrigger=false)
Update object into database.
Definition: commonobject.class.php:9435
isModEnabled
isModEnabled($module)
Is Dolibarr module enabled.
Definition: functions.lib.php:207
ref
$object ref
Definition: info.php:78
getDolGlobalString
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
Definition: functions.lib.php:142
CommandeFournisseur
Class to manage predefined suppliers products.
Definition: fournisseur.commande.class.php:48
User
Class to manage Dolibarr users.
Definition: user.class.php:47
EmailCollector
Class for EmailCollector.
Definition: emailcollector.class.php:59
dolGetStatus
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
Definition: functions.lib.php:10932
EmailCollector\update
update(User $user, $notrigger=false)
Update object into database.
Definition: emailcollector.class.php:521
saveAttachment
saveAttachment($path, $filename, $data)
Save joined file into a directory with a given name.
Definition: emailcollector.lib.php:170
img_object
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
Definition: functions.lib.php:4462
EmailCollector\create
create(User $user, $notrigger=false)
Create object into database.
Definition: emailcollector.class.php:306
EmailCollector\overwritePropertiesOfObject
overwritePropertiesOfObject(&$object, $actionparam, $messagetext, $subject, $header, &$operationslog)
overwitePropertiesOfObject
Definition: emailcollector.class.php:883
EmailCollector\fetchActions
fetchActions()
Fetch actions.
Definition: emailcollector.class.php:750
dol_now
dol_now($mode='auto')
Return date for now.
Definition: functions.lib.php:3046
dolDecrypt
dolDecrypt($chain, $key='')
Decode a string with a symetric encryption.
Definition: security.lib.php:175
EmailCollector\getEncodedUtf7
getEncodedUtf7($str)
Convert str to UTF-7 imap default mailbox names.
Definition: emailcollector.class.php:821
Ticket
Class to generate the form for creating a new ticket.
Definition: html.formticket.class.php:32
EmailCollector\removeEmoji
removeEmoji($text)
Remove EMoji from email content.
Definition: emailcollector.class.php:3524
getCountry
getCountry($searchkey, $withcode='', $dbtouse=0, $outputlangs='', $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
Definition: company.lib.php:515
dol_is_dir
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:452
dolGetFirstLineOfText
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
Definition: functions.lib.php:7258
Propal
Class to manage proposals.
Definition: propal.class.php:52
dol_mkdir
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
Definition: functions.lib.php:6924
SupplierProposal
Class to manage price ask supplier.
Definition: supplier_proposal.class.php:50
getDolGlobalInt
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
Definition: functions.lib.php:156
getCommonSubstitutionArray
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null, $include=null)
Return array of possible common substitutions.
Definition: functions.lib.php:7733
HookManager
Class to manage hooks.
Definition: hookmanager.class.php:30
complete_substitutions_array
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...
Definition: functions.lib.php:8460