Passed
Pull Request — dev (#6)
by Rafael
79:24 queued 24:08
created

EmailCollector   F

Complexity

Total Complexity 683

Size/Duplication

Total Lines 3809
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 2157
dl 0
loc 3809
rs 0.8
c 0
b 0
f 0
wmc 683

25 Methods

Rating   Name   Duplication   Size   Complexity  
A getmsg() 0 23 3
A getLibStatut() 0 3 1
F overwritePropertiesOfObject() 0 189 45
B fetchAll() 0 44 8
D __construct() 0 49 16
A fetchFilters() 0 24 3
A LibStatut() 0 18 4
B create() 0 46 9
A update() 0 19 3
A saveAttachment() 0 19 2
B getConnectStringIMAP() 0 27 9
B convertStringEncoding() 0 13 7
D getpart() 0 107 22
F doCollectOneCollector() 0 2517 496
A fetch() 0 9 1
A getEncodedUtf7() 0 22 3
A initAsSpecimen() 0 6 1
A fetchActions() 0 25 3
A info() 0 22 4
A uidAsString() 0 6 2
B createFromClone() 0 64 8
B decodeSMTPSubject() 0 24 8
F getNomUrl() 0 63 21
A doCollect() 0 21 3
A delete() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like EmailCollector often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EmailCollector, and based on these observations, apply Extract Interface, too.

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