Passed
Push — master ( de5a80...ddace9 )
by Nils
06:13 queued 14s
created

getItemRestrictedUsersList()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 9
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 18
rs 9.9666
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This file is part of the TeamPass project.
9
 * 
10
 * TeamPass is free software: you can redistribute it and/or modify it
11
 * under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, version 3 of the License.
13
 * 
14
 * TeamPass is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 * 
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 * 
22
 * Certain components of this file may be under different licenses. For
23
 * details, see the `licenses` directory or individual file headers.
24
 * ---
25
 * @file      items.queries.php
26
 * @author    Nils Laumaillé ([email protected])
27
 * @copyright 2009-2025 Teampass.net
28
 * @license   GPL-3.0
29
 * @see       https://www.teampass.net
30
 */
31
32
33
use voku\helper\AntiXSS;
34
use TeampassClasses\NestedTree\NestedTree;
35
use TeampassClasses\SessionManager\SessionManager;
36
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
37
use TeampassClasses\Language\Language;
38
use EZimuel\PHPSecureSession;
39
use TeampassClasses\PerformChecks\PerformChecks;
40
use TeampassClasses\ConfigManager\ConfigManager;
41
use OTPHP\TOTP;
42
use TeampassClasses\EmailService\EmailService;
43
use TeampassClasses\EmailService\EmailSettings;
44
45
// Load functions
46
require_once 'main.functions.php';
47
48
// init
49
loadClasses('DB');
50
$session = SessionManager::getSession();
51
$request = SymfonyRequest::createFromGlobals();
52
$lang = new Language($session->get('user-language') ?? 'english');
53
54
// Load config
55
$configManager = new ConfigManager();
56
$SETTINGS = $configManager->getAllSettings();
57
58
// Do checks
59
// Instantiate the class with posted data
60
$checkUserAccess = new PerformChecks(
61
    dataSanitizer(
62
        [
63
            'type' => null !== $request->request->get('type') ? htmlspecialchars($request->request->get('type')) : '',
64
        ],
65
        [
66
            'type' => 'trim|escape',
67
        ],
68
    ),
69
    [
70
        'user_id' => returnIfSet($session->get('user-id'), null),
71
        'user_key' => returnIfSet($session->get('key', 'SESSION'), null),
72
    ]
73
);
74
// Handle the case
75
echo $checkUserAccess->caseHandler();
76
if (
77
    $checkUserAccess->userAccessPage('items') === false ||
78
    $checkUserAccess->checkSession() === false
79
) {
80
    // Not allowed page
81
    $session->set('system-error_code', ERR_NOT_ALLOWED);
82
    include $SETTINGS['cpassman_dir'] . '/error.php';
83
    exit;
84
}
85
86
// Define Timezone
87
date_default_timezone_set($SETTINGS['timezone'] ?? 'UTC');
88
89
// Set header properties
90
header('Content-type: text/html; charset=utf-8');
91
header('Cache-Control: no-cache, no-store, must-revalidate');
92
error_reporting(E_ERROR);
93
set_time_limit(0);
94
95
// --------------------------------- //
96
97
98
/*
99
 * Define Timezone
100
*/
101
if (isset($SETTINGS['timezone']) === true) {
102
    date_default_timezone_set($SETTINGS['timezone']);
103
} else {
104
    date_default_timezone_set('UTC');
105
}
106
107
require_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $session->get('user-language') . '.php';
108
header('Content-type: text/html; charset=utf-8');
109
header('Cache-Control: no-cache, must-revalidate');
110
111
112
// Prepare nestedTree
113
$tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
114
115
// Load AntiXSS
116
$antiXss = new AntiXSS();
117
118
// Ensure Complexity levels are translated
119
if (defined('TP_PW_COMPLEXITY') === false) {
120
    define(
121
        'TP_PW_COMPLEXITY',
122
        array(
123
            TP_PW_STRENGTH_1 => array(TP_PW_STRENGTH_1, $lang->get('complex_level1'), 'fas fa-thermometer-empty text-danger'),
124
            TP_PW_STRENGTH_2 => array(TP_PW_STRENGTH_2, $lang->get('complex_level2'), 'fas fa-thermometer-quarter text-warning'),
125
            TP_PW_STRENGTH_3 => array(TP_PW_STRENGTH_3, $lang->get('complex_level3'), 'fas fa-thermometer-half text-warning'),
126
            TP_PW_STRENGTH_4 => array(TP_PW_STRENGTH_4, $lang->get('complex_level4'), 'fas fa-thermometer-three-quarters text-success'),
127
            TP_PW_STRENGTH_5 => array(TP_PW_STRENGTH_5, $lang->get('complex_level5'), 'fas fa-thermometer-full text-success'),
128
        )
129
    );
130
}
131
132
// Prepare POST variables
133
$data = [
134
    'type' => $request->request->filter('type', '', FILTER_SANITIZE_SPECIAL_CHARS),
135
    'data' => $request->request->filter('data', '', FILTER_SANITIZE_SPECIAL_CHARS),
136
    'key' => $request->request->filter('key', '', FILTER_SANITIZE_SPECIAL_CHARS),
137
    'label' => $request->request->filter('label', '', FILTER_SANITIZE_SPECIAL_CHARS),
138
    'status' => $request->request->filter('status', '', FILTER_SANITIZE_SPECIAL_CHARS),
139
    'cat' => $request->request->filter('cat', '', FILTER_SANITIZE_SPECIAL_CHARS),
140
    'receipt' => $request->request->filter('receipt', '', FILTER_SANITIZE_SPECIAL_CHARS),
141
    'itemId' => $request->request->filter('item_id', '', FILTER_SANITIZE_SPECIAL_CHARS),
142
    'folderId' => $request->request->filter('folder_id', '', FILTER_SANITIZE_SPECIAL_CHARS),
143
    'id' => $request->request->filter('id', '', FILTER_SANITIZE_SPECIAL_CHARS),
144
    'destination' => $request->request->filter('destination', '', FILTER_SANITIZE_SPECIAL_CHARS),
145
    'source' => $request->request->filter('source', '', FILTER_SANITIZE_SPECIAL_CHARS),
146
    'userId' => $request->request->filter('user_id', '', FILTER_SANITIZE_SPECIAL_CHARS),
147
    'getType' => $request->query->get('type', ''),
148
    'getTerm' => $request->query->get('term', ''),
149
    'option' => $request->request->filter('option', '', FILTER_SANITIZE_SPECIAL_CHARS),
150
    'fileSuffix' => $request->request->filter('file_suffix', '', FILTER_SANITIZE_SPECIAL_CHARS),
151
    'context' => $request->request->filter('context', '', FILTER_SANITIZE_SPECIAL_CHARS),
152
    'notifyType' => $request->request->filter('notify_type', '', FILTER_SANITIZE_SPECIAL_CHARS),
153
    'timestamp' => $request->request->filter('timestamp', '', FILTER_SANITIZE_SPECIAL_CHARS),
154
    'itemKey' => $request->request->filter('item_key', '', FILTER_SANITIZE_SPECIAL_CHARS),
155
    'action' => $request->request->filter('action', '', FILTER_SANITIZE_SPECIAL_CHARS),
156
];
157
158
$filters = [
159
    'type' => 'trim|escape',
160
    'data' => 'trim|escape',
161
    'key' => 'trim|escape',
162
    'label' => 'trim|escape',
163
    'status' => 'trim|escape',
164
    'cat' => 'trim|escape',
165
    'receipt' => 'trim|escape',
166
    'itemId' => 'cast:integer',
167
    'folderId' => 'cast:integer',
168
    'id' => 'cast:integer',
169
    'destination' => 'cast:integer',
170
    'source' => 'cast:integer',
171
    'userId' => 'cast:integer',
172
    'getType' => 'trim|escape',
173
    'getTerm' => 'trim|escape',
174
    'option' => 'trim|escape',
175
    'fileSuffix' => 'trim|escape',
176
    'context' => 'trim|escape',
177
    'notifyType' => 'trim|escape',
178
    'timestamp' => 'cast:integer',
179
    'itemKey' => 'trim|escape',
180
    'action' => 'trim|escape',
181
];
182
183
$inputData = dataSanitizer(
184
    $data,
185
    $filters
186
);
187
188
// List of teampass users ids (and current user id).
189
$tpUsersIDs = [
190
    OTV_USER_ID,
191
    SSH_USER_ID,
192
    API_USER_ID,
193
    $session->get('user-id'),
194
];
195
196
// Do asked action
197
switch ($inputData['type']) {
198
    /*
199
     * CASE
200
     * creating a new ITEM
201
     */
202
    case 'new_item':
203
        // Check KEY and rights
204
        if ($inputData['key'] !== $session->get('key')) {
205
            echo (string) prepareExchangedData(
206
                array(
207
                    'error' => true,
208
                    'message' => $lang->get('key_is_not_correct'),
209
                ),
210
                'encode'
211
            );
212
            break;
213
        }
214
        if ($session->get('user-read_only') === 1) {
215
            echo (string) prepareExchangedData(
216
                array(
217
                    'error' => true,
218
                    'message' => $lang->get('error_not_allowed_to'),
219
                ),
220
                'encode'
221
            );
222
            break;
223
        }
224
225
        // init
226
        $returnValues = [];
227
        $arrData = [];
228
        // decrypt and retreive data in JSON format
229
        $dataReceived = prepareExchangedData(
230
            $inputData['data'],
231
            'decode'
232
        );
233
234
        if (is_array($dataReceived) === true && count($dataReceived) > 0) {
235
            // Prepare variables
236
            $post_anyone_can_modify = filter_var($dataReceived['anyone_can_modify'], FILTER_SANITIZE_NUMBER_INT);
237
            $post_complexity_level = filter_var($dataReceived['complexity_level'], FILTER_SANITIZE_NUMBER_INT);
238
            $post_description = $antiXss->xss_clean($dataReceived['description']);
239
            $post_diffusion_list = filter_var_array(
240
                $dataReceived['diffusion_list'],
241
                FILTER_SANITIZE_FULL_SPECIAL_CHARS
242
            );
243
            $post_diffusion_list_names = filter_var_array(
244
                $dataReceived['diffusion_list_names'],
245
                FILTER_SANITIZE_FULL_SPECIAL_CHARS
246
            );
247
            $post_email = filter_var(htmlspecialchars_decode($dataReceived['email']), FILTER_SANITIZE_EMAIL);
248
            $post_fields = filter_var_array(
249
                $dataReceived['fields'],
250
                FILTER_SANITIZE_FULL_SPECIAL_CHARS
251
            );
252
            $inputData['folderId'] = filter_var($dataReceived['folder'], FILTER_SANITIZE_NUMBER_INT);
253
            $post_folder_is_personal = filter_var($dataReceived['folder_is_personal'], FILTER_SANITIZE_NUMBER_INT);
254
            $inputData['label'] = filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
255
            $post_login = filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
256
            $post_password = htmlspecialchars_decode($dataReceived['pw']);
257
            $post_tags = htmlspecialchars($dataReceived['tags']);
258
            $post_template_id = filter_var($dataReceived['template_id'], FILTER_SANITIZE_NUMBER_INT);
259
            $post_url = filter_var(htmlspecialchars_decode($dataReceived['url']), FILTER_SANITIZE_URL);
260
            $post_uploaded_file_id = filter_var($dataReceived['uploaded_file_id'], FILTER_SANITIZE_NUMBER_INT);
261
            $inputData['userId'] = filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT);
262
            $post_to_be_deleted_after_date = isset($dataReceived['to_be_deleted_after_date']) === true ? filter_var($dataReceived['to_be_deleted_after_date'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '';
263
            $post_to_be_deleted_after_x_views = filter_var($dataReceived['to_be_deleted_after_x_views'], FILTER_SANITIZE_NUMBER_INT);
264
            $post_fa_icon = isset($dataReceived['fa_icon']) === true ? filter_var($dataReceived['fa_icon'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '';
265
266
            // Restricted to users
267
            $post_restricted_to = is_array($dataReceived['restricted_to'])
268
                ? filter_var_array($dataReceived['restricted_to'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)
269
                : '';
270
271
            // Restricted to roles
272
            $post_restricted_to_roles = is_array($dataReceived['restricted_to_roles'])
273
                ? filter_var_array($dataReceived['restricted_to_roles'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)
274
                : '';
275
276
            //-> DO A SET OF CHECKS
277
            // Perform a check in case of Read-Only user creating an item in his PF
278
            if ($session->get('user-read_only') === 1
279
                && (in_array($inputData['folderId'], $session->get('user-personal_folders')) === false
280
                || $post_folder_is_personal !== 1)
281
            ) {
282
                echo (string) prepareExchangedData(
283
                    array(
284
                        'error' => true,
285
                        'message' => $lang->get('error_not_allowed_to_access_this_folder'),
286
                    ),
287
                    'encode'
288
                );
289
                break;
290
            }
291
292
            // Is author authorized to create in this folder
293
            if (count($session->get('user-list_folders_limited')) > 0) {
294
                if (in_array($inputData['folderId'], array_keys($session->get('user-list_folders_limited'))) === false
295
                    && in_array($inputData['folderId'], $session->get('user-accessible_folders')) === false
296
                    && in_array($inputData['folderId'], $session->get('user-personal_folders')) === false
297
                ) {
298
                    echo (string) prepareExchangedData(
299
                        array(
300
                            'error' => true,
301
                            'message' => $lang->get('error_not_allowed_to_access_this_folder'),
302
                        ),
303
                        'encode'
304
                    );
305
                    break;
306
                }
307
            } else {
308
                if (in_array($inputData['folderId'], $session->get('user-accessible_folders')) === false) {
309
                    echo (string) prepareExchangedData(
310
                        array(
311
                            'error' => true,
312
                            'message' => $lang->get('error_not_allowed_to_access_this_folder'),
313
                        ),
314
                        'encode'
315
                    );
316
                    break;
317
                }
318
            }
319
320
            // perform a check in case of Read-Only user creating an item in his PF
321
            if (
322
                $session->get('user-read_only') === 1
323
                && in_array($inputData['folderId'], $session->get('user-personal_folders')) === false
324
            ) {
325
                echo (string) prepareExchangedData(
326
                    array(
327
                        'error' => true,
328
                        'message' => $lang->get('error_not_allowed_to_access_this_folder'),
329
                    ),
330
                    'encode'
331
                );
332
                break;
333
            }
334
335
            // is pwd empty?
336
            if (
337
                empty($post_password) === true
338
                && $session->has('user-create_item_without_password') && null !== $session->get('user-create_item_without_password')
339
                && (int) $session->get('user-create_item_without_password') !== 1
340
            ) {
341
                echo (string) prepareExchangedData(
342
                    array(
343
                        'error' => true,
344
                        'message' => $lang->get('password_cannot_be_empty'),
345
                    ),
346
                    'encode'
347
                );
348
                break;
349
            }
350
351
            // Check length
352
            $strlen_post_password = strlen($post_password);
353
            if ($strlen_post_password > $SETTINGS['pwd_maximum_length']) {
354
                echo (string) prepareExchangedData(
355
                    array(
356
                        'error' => true,
357
                        'message' => $lang->get('password_too_long'),
358
                    ),
359
                    'encode'
360
                );
361
                break;
362
            }
363
364
            // Need info in DB
365
            // About special settings
366
            $dataFolderSettings = DB::queryFirstRow(
367
                'SELECT bloquer_creation, bloquer_modification, personal_folder
368
                FROM ' . prefixTable('nested_tree') . ' 
369
                WHERE id = %i',
370
                $inputData['folderId']
371
            );
372
            $itemInfos = [];
373
            $itemInfos['personal_folder'] = $dataFolderSettings['personal_folder'];
374
            if ((int) $itemInfos['personal_folder'] === 1) {
375
                $itemInfos['no_complex_check_on_modification'] = 1;
376
                $itemInfos['no_complex_check_on_creation'] = 1;
377
            } else {
378
                $itemInfos['no_complex_check_on_modification'] = (int) $dataFolderSettings['bloquer_modification'];
379
                $itemInfos['no_complex_check_on_creation'] = (int) $dataFolderSettings['bloquer_creation'];
380
            }
381
382
            // Get folder complexity
383
            $folderComplexity = DB::queryFirstRow(
384
                'SELECT valeur
385
                FROM ' . prefixTable('misc') . '
386
                WHERE type = %s AND intitule = %i',
387
                'complex',
388
                $inputData['folderId']
389
            );
390
            $itemInfos['requested_folder_complexity'] = $folderComplexity !== null ? (int) $folderComplexity['valeur'] : 0;
391
392
            // Check COMPLEXITY
393
            if ($post_complexity_level < $itemInfos['requested_folder_complexity'] && $itemInfos['no_complex_check_on_creation'] === 0) {
394
                echo (string) prepareExchangedData(
395
                    array(
396
                        'error' => true,
397
                        'message' => $lang->get('error_security_level_not_reached'),
398
                    ),
399
                    'encode'
400
                );
401
                break;
402
            }
403
404
            // ./ END
405
406
            // check if element doesn't already exist
407
            $itemExists = 0;
408
            $newID = '';
409
            $data = DB::queryFirstRow(
410
                'SELECT * FROM ' . prefixTable('items') . '
411
                WHERE label = %s AND inactif = %i',
412
                $inputData['label'],
413
                0
414
            );
415
            $counter = DB::count();
416
            if ($counter > 0) {
417
                $itemExists = 1;
418
            } else {
419
                $itemExists = 0;
420
            }
421
422
            // Manage case where item is personal.
423
            // In this case, duplication is allowed
424
            if (
425
                isset($SETTINGS['duplicate_item']) === true
426
                && (int) $SETTINGS['duplicate_item'] === 0
427
                && (int) $post_folder_is_personal === 1
428
            ) {
429
                $itemExists = 0;
430
            }
431
432
            if ((isset($SETTINGS['duplicate_item']) === true
433
                    && (int) $SETTINGS['duplicate_item'] === 0
434
                    && (int) $itemExists === 0)
435
                || (isset($SETTINGS['duplicate_item']) === true
436
                    && (int) $SETTINGS['duplicate_item'] === 1)
437
            ) {
438
                // Handle case where pw is empty
439
                // if not allowed then warn user
440
                if (($session->has('user-create_item_without_password') && $session->has('user-create_item_without_password') && null !== $session->get('user-create_item_without_password')
441
                        && (int) $session->get('user-create_item_without_password') !== 1) ||
442
                    empty($post_password) === false ||
443
                    (int) $post_folder_is_personal === 1
444
                ) {
445
                    // NEW ENCRYPTION
446
                    $cryptedStuff = doDataEncryption($post_password);
447
                } else {
448
                    $cryptedStuff['encrypted'] = '';
449
                    $cryptedStuff['objectKey'] = '';
450
                }
451
452
                $post_password = $cryptedStuff['encrypted'];
453
                $post_password_key = $cryptedStuff['objectKey'];
454
                $itemFilesForTasks = [];
455
                $itemFieldsForTasks = [];
456
                
457
                // ADD item
458
                DB::insert(
459
                    prefixTable('items'),
460
                    array(
461
                        'label' => $inputData['label'],
462
                        'description' => $post_description,
463
                        'pw' => $post_password,
464
                        'pw_iv' => '',
465
                        'pw_len' => $strlen_post_password,
466
                        'email' => $post_email,
467
                        'url' => $post_url,
468
                        'id_tree' => $inputData['folderId'],
469
                        'login' => $post_login,
470
                        'inactif' => 0,
471
                        'restricted_to' => empty($post_restricted_to) === true ?
472
                            '' : (is_array($post_restricted_to) === true ? implode(';', $post_restricted_to) : $post_restricted_to),
473
                        'perso' => ((int) $post_folder_is_personal === 1) ?
474
                            1 : 0,
475
                        'anyone_can_modify' => ($post_anyone_can_modify === 'on') ? 1 : 0,
476
                        'complexity_level' => $post_complexity_level,
477
                        'encryption_type' => 'teampass_aes',
478
                        'fa_icon' => $post_fa_icon,
479
                        'item_key' => uniqidReal(50),
480
                        'created_at' => time(),
481
                    )
482
                );
483
                $newID = DB::insertId();
484
485
                // Create sharekeys for the user itself
486
                storeUsersShareKey(
487
                    prefixTable('sharekeys_items'),
488
                    (int) $post_folder_is_personal,
489
                    (int) $newID,
490
                    $cryptedStuff['objectKey'],
491
                    true,   // only for the item creator
492
                    false,  // no delete all
493
                );
494
495
                // update fields
496
                if (
497
                    isset($SETTINGS['item_extra_fields']) === true
498
                    && (int) $SETTINGS['item_extra_fields'] === 1
499
                ) {
500
                    foreach ($post_fields as $field) {
501
                        if (empty($field['value']) === false) {
502
                            // should we encrypt the data
503
                            $dataTmp = DB::queryFirstRow(
504
                                'SELECT encrypted_data
505
                                FROM ' . prefixTable('categories') . '
506
                                WHERE id = %i',
507
                                $field['id']
508
                            );
509
510
                            // Should we encrypt the data
511
                            if ((int) $dataTmp['encrypted_data'] === 1) {
512
                                // Create sharekeys for users
513
                                $cryptedStuff = doDataEncryption($field['value']);
514
515
                                // Store value
516
                                DB::insert(
517
                                    prefixTable('categories_items'),
518
                                    array(
519
                                        'item_id' => $newID,
520
                                        'field_id' => $field['id'],
521
                                        'data' => $cryptedStuff['encrypted'],
522
                                        'data_iv' => '',
523
                                        'encryption_type' => TP_ENCRYPTION_NAME,
524
                                    )
525
                                );
526
                                $newObjectId = DB::insertId();
527
            
528
                                // Create sharekeys for user
529
                                storeUsersShareKey(
530
                                    prefixTable('sharekeys_fields'),
531
                                    (int) $post_folder_is_personal,
532
                                    (int) $newObjectId,
533
                                    $cryptedStuff['objectKey'],
534
                                    true,   // only for the item creator
535
                                    false,  // no delete all
536
                                );
537
538
                                array_push(
539
                                    $itemFieldsForTasks,
540
                                    [
541
                                        'object_id' => $newObjectId,
542
                                        'object_key' => $cryptedStuff['objectKey'],
543
                                    ]
544
                                );
545
                                
546
                            } else {
547
                                // update value
548
                                DB::insert(
549
                                    prefixTable('categories_items'),
550
                                    array(
551
                                        'item_id' => $newID,
552
                                        'field_id' => $field['id'],
553
                                        'data' => $field['value'],
554
                                        'data_iv' => '',
555
                                        'encryption_type' => 'not_set',
556
                                    )
557
                                );
558
                            }
559
                        }
560
                    }
561
                }
562
563
                // If template enable, is there a main one selected?
564
                if (
565
                    isset($SETTINGS['item_creation_templates']) === true
566
                    && (int) $SETTINGS['item_creation_templates'] === 1
567
                    && empty($post_template_id) === false
568
                ) {
569
                    DB::queryFirstRow(
570
                        'SELECT *
571
                        FROM ' . prefixTable('templates') . '
572
                        WHERE item_id = %i',
573
                        $newID
574
                    );
575
                    if (DB::count() === 0) {
576
                        // store field text
577
                        DB::insert(
578
                            prefixTable('templates'),
579
                            array(
580
                                'item_id' => $newID,
581
                                'category_id' => $post_template_id,
582
                            )
583
                        );
584
                    } else {
585
                        // Delete if empty
586
                        if (empty($post_template_id) === true) {
587
                            DB::delete(
588
                                prefixTable('templates'),
589
                                'item_id = %i',
590
                                $newID
591
                            );
592
                        } else {
593
                            // Update value
594
                            DB::update(
595
                                prefixTable('templates'),
596
                                array(
597
                                    'category_id' => $post_template_id,
598
                                ),
599
                                'item_id = %i',
600
                                $newID
601
                            );
602
                        }
603
                    }
604
                }
605
606
                // If automatic deletion asked
607
                if (
608
                    isset($SETTINGS['enable_delete_after_consultation']) === true
609
                    && (int) $SETTINGS['enable_delete_after_consultation'] === 1
610
                    && is_null($post_to_be_deleted_after_x_views) === false
611
                    && is_null($post_to_be_deleted_after_date) === false
612
                ) {
613
                    if (
614
                        empty($post_to_be_deleted_after_date) === false
615
                        || $post_to_be_deleted_after_x_views > 0
616
                    ) {
617
                        // Automatic deletion to be added
618
                        DB::insert(
619
                            prefixTable('automatic_del'),
620
                            array(
621
                                'item_id' => $newID,
622
                                'del_enabled' => 1,
623
                                'del_type' => $post_to_be_deleted_after_x_views > 0 ? 1 : 2, //1 = numeric : 2 = date
624
                                'del_value' => $post_to_be_deleted_after_x_views > 0 ? $post_to_be_deleted_after_x_views : dateToStamp($post_to_be_deleted_after_date, $SETTINGS['date_format']),
625
                            )
626
                        );
627
                    }
628
                }
629
630
                // Get readable list of restriction
631
                $listOfRestricted = $oldRestrictionList = '';
632
                if (
633
                    is_array($post_restricted_to) === true
634
                    && count($post_restricted_to) > 0
635
                    && isset($SETTINGS['restricted_to']) === true
636
                    && (int) $SETTINGS['restricted_to'] === 1
637
                ) {
638
                    foreach ($post_restricted_to as $userRest) {
639
                        if (empty($userRest) === false) {
640
                            $dataTmp = DB::queryFirstRow('SELECT login FROM ' . prefixTable('users') . ' WHERE id= %i', $userRest);
641
                            if (empty($listOfRestricted)) {
642
                                $listOfRestricted = $dataTmp['login'];
643
                            } else {
644
                                $listOfRestricted .= ';' . $dataTmp['login'];
645
                            }
646
                        }
647
                    }
648
                }
649
                if (
650
                    $post_restricted_to !== null
651
                    && $data !== null
652
                    && $data['restricted_to'] !== $post_restricted_to
653
                    && (int) $SETTINGS['restricted_to'] === 1
654
                ) {
655
                    if (empty($data['restricted_to']) === false) {
656
                        foreach (explode(';', $data['restricted_to']) as $userRest) {
657
                            if (empty($userRest) === false) {
658
                                $dataTmp = DB::queryFirstRow(
659
                                    'SELECT login
660
                                    FROM ' . prefixTable('users') . '
661
                                    WHERE id= %i',
662
                                    $userRest
663
                                );
664
665
                                if (empty($oldRestrictionList) === true) {
666
                                    $oldRestrictionList = $dataTmp['login'];
667
                                } else {
668
                                    $oldRestrictionList .= ';' . $dataTmp['login'];
669
                                }
670
                            }
671
                        }
672
                    }
673
                }
674
                // Manage retriction_to_roles
675
                if (
676
                    is_array($post_restricted_to_roles) === true
677
                    && count($post_restricted_to_roles) > 0
678
                    && isset($SETTINGS['restricted_to_roles']) === true
679
                    && (int) $SETTINGS['restricted_to_roles'] === 1
680
                ) {
681
                    // add roles for item
682
                    if (
683
                        is_array($post_restricted_to_roles) === true
684
                        && count($post_restricted_to_roles) > 0
685
                    ) {
686
                        foreach ($post_restricted_to_roles as $role) {
687
                            if (count($role) > 1) {
688
                                $role = $role[1];
689
                            } else {
690
                                $role = $role[0];
691
                            }
692
                            DB::insert(
693
                                prefixTable('restriction_to_roles'),
694
                                array(
695
                                    'role_id' => $role,
696
                                    'item_id' => $inputData['itemId'],
697
                                )
698
                            );
699
                        }
700
                    }
701
                }
702
703
                // log
704
                logItems(
705
                    $SETTINGS,
706
                    (int) $newID,
707
                    $inputData['label'],
708
                    $session->get('user-id'),
709
                    'at_creation',
710
                    $session->get('user-login')
711
                );
712
713
                // Add tags
714
                $tags = explode(' ', $post_tags);
715
                foreach ($tags as $tag) {
716
                    if (empty($tag) === false) {
717
                        DB::insert(
718
                            prefixTable('tags'),
719
                            array(
720
                                'item_id' => $newID,
721
                                'tag' => strtolower($tag),
722
                            )
723
                        );
724
                    }
725
                }
726
727
                // Check if any files have been added
728
                if (empty($post_uploaded_file_id) === false) {
729
                    $rows = DB::query(
730
                        'SELECT id
731
                        FROM ' . prefixTable('files') . '
732
                        WHERE id_item = %s',
733
                        $post_uploaded_file_id
734
                    );
735
                    foreach ($rows as $record) {
736
                        // update item_id in files table
737
                        DB::update(
738
                            prefixTable('files'),
739
                            array(
740
                                'id_item' => $newID,
741
                                'confirmed' => 1,
742
                            ),
743
                            'id=%i',
744
                            $record['id']
745
                        );
746
                    }
747
                }
748
749
                // Create new task for the new item
750
                // If it is not a personnal one
751
                if ((int) $post_folder_is_personal === 0) {
752
                    storeTask(
753
                        'new_item',
754
                        $session->get('user-id'),
755
                        0,
756
                        (int) $inputData['folderId'],
757
                        (int) $newID,
758
                        $post_password_key,
759
                        $itemFieldsForTasks,
760
                        $itemFilesForTasks,
761
                    );
762
                }
763
764
                // Announce by email?
765
                if (empty($post_diffusion_list) === false) {
766
                    // get links url
767
                    if (empty($SETTINGS['email_server_url'])) {
768
                        $SETTINGS['email_server_url'] = $SETTINGS['cpassman_url'];
769
                    }
770
771
                    // Get path
772
                    $path = geItemReadablePath(
773
                        (int) $inputData['folderId'],
774
                        $inputData['label'],
775
                        $SETTINGS
776
                    );
777
778
                    // send email
779
                    if (is_array($post_diffusion_list) === true && count($post_diffusion_list) > 0) {
780
                        $cpt = 0;
781
                        foreach ($post_diffusion_list as $emailAddress) {
782
                            if (empty($emailAddress) === false) {
783
                                prepareSendingEmail(
784
                                    $lang->get('email_subject_item_updated'),
785
                                    str_replace(
786
                                        array('#label', '#link'),
787
                                            array($path, $SETTINGS['email_server_url'] . '/index.php?page=items&group=' . $inputData['folderId'] . '&id=' . $newID . $lang['email_body3']),
788
                                            $lang->get('new_item_email_body')
789
                                    ),
790
                                    $emailAddress,
791
                                    $post_diffusion_list_names[$cpt]
792
                                );
793
                            }
794
                            $cpt++;
795
                        }
796
                    }
797
                }
798
            } elseif (
799
                isset($SETTINGS['duplicate_item']) === true
800
                && (int) $SETTINGS['duplicate_item'] === 0
801
                && (int) $itemExists === 1
802
            ) {
803
                // Encrypt data to return
804
                echo (string) prepareExchangedData(
805
                    array(
806
                        'error' => true,
807
                        'message' => $lang->get('error_item_exists'),
808
                    ),
809
                    'encode'
810
                );
811
                break;
812
            }
813
814
            // Add item to CACHE table if new item has been created
815
            if (isset($newID) === true) {
816
                updateCacheTable('add_value', (int) $newID);
817
            }
818
819
            $arrData = array(
820
                'error' => false,
821
                'item_id' => $newID,
822
            );
823
        } else {
824
            // an error appears on JSON format
825
            echo (string) prepareExchangedData(
826
                array(
827
                    'error' => true,
828
                    'message' => $lang->get('json_error_format'),
829
                ),
830
                'encode'
831
            );
832
        }
833
834
        // Encrypt data to return
835
        echo (string) prepareExchangedData(
836
            $arrData,
837
            'encode'
838
        );
839
        break;
840
841
    /*
842
     * CASE
843
     * update an ITEM
844
     */
845
    case 'update_item':
846
        // Check KEY and rights
847
        if ($inputData['key'] !== $session->get('key')) {
848
            echo (string) prepareExchangedData(
849
                array(
850
                    'error' => true,
851
                    'message' => $lang->get('key_is_not_correct'),
852
                ),
853
                'encode'
854
            );
855
            break;
856
        }
857
        if ($session->get('user-read_only') === 1) {
858
            echo (string) prepareExchangedData(
859
                array(
860
                    'error' => true,
861
                    'message' => $lang->get('error_not_allowed_to'),
862
                ),
863
                'encode'
864
            );
865
            break;
866
        }
867
868
        // init
869
        $returnValues = array();
870
        // decrypt and retreive data in JSON format
871
        $dataReceived = prepareExchangedData(
872
            $inputData['data'],
873
            'decode'
874
        );
875
876
        // Error if not expected data
877
        if (is_array($dataReceived) === false || count($dataReceived) === 0) {
878
            echo (string) prepareExchangedData(
879
                array(
880
                    'error' => true,
881
                    'message' => $lang->get('json_error_format'),
882
                ),
883
                'encode'
884
            );
885
            break;
886
        }
887
888
        // Prepare variables
889
        $itemInfos = array();
890
        $inputData['label'] = isset($dataReceived['label']) && is_string($dataReceived['label']) ? filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '';
891
        $post_url = isset($dataReceived['url'])=== true ? filter_var(htmlspecialchars_decode($dataReceived['url']), FILTER_SANITIZE_URL) : '';
892
        $post_password = $original_pw = isset($dataReceived['pw']) && is_string($dataReceived['pw']) ? htmlspecialchars_decode($dataReceived['pw']) : '';
893
        $post_login = isset($dataReceived['login']) && is_string($dataReceived['login']) ? filter_var(htmlspecialchars_decode($dataReceived['login']), FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '';
894
        $post_tags = isset($dataReceived['tags'])=== true ? htmlspecialchars($dataReceived['tags']) : '';
895
        $post_email = isset($dataReceived['email'])=== true ? filter_var(htmlspecialchars_decode($dataReceived['email']), FILTER_SANITIZE_EMAIL) : '';
896
        $post_template_id = (int) filter_var($dataReceived['template_id'], FILTER_SANITIZE_NUMBER_INT);
897
        $inputData['itemId'] = (int) filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
898
        $post_anyone_can_modify = (int) filter_var($dataReceived['anyone_can_modify'], FILTER_SANITIZE_NUMBER_INT);
899
        $post_complexity_level = (int) filter_var($dataReceived['complexity_level'], FILTER_SANITIZE_NUMBER_INT);
900
        $inputData['folderId'] = (int) filter_var($dataReceived['folder'], FILTER_SANITIZE_NUMBER_INT);
901
        $post_folder_is_personal = (int) filter_var($dataReceived['folder_is_personal'], FILTER_SANITIZE_NUMBER_INT);
902
        $post_restricted_to = filter_var_array(
903
            $dataReceived['restricted_to'],
904
            FILTER_SANITIZE_FULL_SPECIAL_CHARS
905
        );
906
        $post_restricted_to_roles = filter_var_array(
907
            $dataReceived['restricted_to_roles'],
908
            FILTER_SANITIZE_FULL_SPECIAL_CHARS
909
        );
910
        $post_diffusion_list = filter_var_array(
911
            $dataReceived['diffusion_list'],
912
            FILTER_SANITIZE_FULL_SPECIAL_CHARS
913
        );
914
        $post_diffusion_list_names = filter_var_array(
915
            $dataReceived['diffusion_list_names'],
916
            FILTER_SANITIZE_FULL_SPECIAL_CHARS
917
        );
918
        //$post_diffusion_list_names = $post_diffusion_list_names !== false ? json_decode($post_diffusion_list_names) : '';
919
        $post_to_be_deleted_after_x_views = filter_var(
920
            $dataReceived['to_be_deleted_after_x_views'],
921
            FILTER_SANITIZE_NUMBER_INT
922
        );
923
        $post_to_be_deleted_after_date = isset($dataReceived['to_be_deleted_after_date']) === true ? filter_var(
924
                $dataReceived['to_be_deleted_after_date'],
925
                FILTER_SANITIZE_FULL_SPECIAL_CHARS
926
            ) :
927
            '';
928
        $post_fields = (filter_var_array(
929
            $dataReceived['fields'],
930
            FILTER_SANITIZE_FULL_SPECIAL_CHARS
931
        ));
932
        $post_description = $antiXss->xss_clean($dataReceived['description']);
933
        $post_fa_icon = isset($dataReceived['fa_icon']) === true ? filter_var(($dataReceived['fa_icon']), FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '';
934
        $post_otp_is_enabled = (int) filter_var($dataReceived['otp_is_enabled'], FILTER_SANITIZE_NUMBER_INT);
935
        $post_otp_phone_number = (int) filter_var($dataReceived['otp_phone_number'], FILTER_SANITIZE_NUMBER_INT);
936
        $post_otp_secret = isset($dataReceived['otp_secret']) === true ? filter_var(($dataReceived['otp_secret']), FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '';
937
938
        //-> DO A SET OF CHECKS
939
        // Perform a check in case of Read-Only user creating an item in his PF
940
        if (
941
            $session->get('user-read_only') === 1
942
            && (in_array($inputData['folderId'], $session->get('user-personal_folders')) === false
943
                || $post_folder_is_personal !== 1)
944
        ) {
945
            echo (string) prepareExchangedData(
946
                array(
947
                    'error' => true,
948
                    'message' => $lang->get('error_not_allowed_to_access_this_folder'),
949
                ),
950
                'encode'
951
            );
952
            break;
953
        }
954
955
        $dataCheck = validateDataFields(prefixTable('items'), $dataReceived);
956
        if ($dataCheck['state'] !== true) {
957
            echo (string) prepareExchangedData(
958
                array(
959
                    'error' => true,
960
                    'message' => $lang->get('error_data_not_valid').' - '.$lang->get('field').' '.strtoupper($dataCheck['field']).' '.$lang->get('exceeds_maximum_length_of').' '.$dataCheck['maxLength'].' ('.$dataCheck['currentLength'].')',
961
                ),
962
                'encode'
963
            );
964
            break;
965
        }
966
967
        // Check PWD EMPTY
968
        if (
969
            empty($pw)
970
            && $session->has('user-create_item_without_password') && null !== $session->get('user-create_item_without_password')
971
            && (int) $session->get('user-create_item_without_password') !== 1
972
        ) {
973
            echo (string) prepareExchangedData(
974
                array(
975
                    'error' => true,
976
                    'message' => $lang->get('error_pw'),
977
                ),
978
                'encode'
979
            );
980
            break;
981
        }
982
983
        // Need info in DB
984
        // About special settings
985
        $dataFolderSettings = DB::queryFirstRow(
986
            'SELECT bloquer_creation, bloquer_modification, personal_folder, title
987
            FROM ' . prefixTable('nested_tree') . ' 
988
            WHERE id = %i',
989
            $inputData['folderId']
990
        );
991
        $itemInfos['personal_folder'] = (int) $dataFolderSettings['personal_folder'];
992
        if ((int) $itemInfos['personal_folder'] === 1) {
993
            $itemInfos['no_complex_check_on_modification'] = 1;
994
            $itemInfos['no_complex_check_on_creation'] = 1;
995
        } else {
996
            $itemInfos['no_complex_check_on_modification'] = (int) $dataFolderSettings['bloquer_modification'];
997
            $itemInfos['no_complex_check_on_creation'] = (int) $dataFolderSettings['bloquer_creation'];
998
        }
999
1000
        // Get folder complexity
1001
        $folderComplexity = DB::queryFirstRow(
1002
            'SELECT valeur
1003
            FROM ' . prefixTable('misc') . '
1004
            WHERE type = %s AND intitule = %i',
1005
            'complex',
1006
            $inputData['folderId']
1007
        );
1008
        $itemInfos['requested_folder_complexity'] = is_null($folderComplexity) === false ? (int) $folderComplexity['valeur'] : 0;
1009
        // Check COMPLEXITY
1010
        if ($post_complexity_level < $itemInfos['requested_folder_complexity'] && $itemInfos['no_complex_check_on_modification'] === 0) {
1011
            echo (string) prepareExchangedData(
1012
                array(
1013
                    'error' => true,
1014
                    'message' => $lang->get('error_security_level_not_reached'),
1015
                ),
1016
                'encode'
1017
            );
1018
            break;
1019
        }
1020
1021
        // Check password length
1022
        $strlen_post_password = strlen($post_password);
1023
        if ($strlen_post_password > $SETTINGS['pwd_maximum_length']) {
1024
            echo (string) prepareExchangedData(
1025
                array(
1026
                    'error' => true,
1027
                    'message' => $lang->get('error_pw_too_long'),
1028
                ),
1029
                'encode'
1030
            );
1031
            break;
1032
        }
1033
1034
        // ./ END
1035
1036
        // Init
1037
        $arrayOfChanges = array();
1038
        $encryptionTaskIsRequested = false;
1039
        $itemFilesForTasks = [];
1040
        $itemFieldsForTasks = [];
1041
        $tasksToBePerformed = [];
1042
        $encrypted_password = '';
1043
        $encrypted_password_key = '';
1044
1045
        // Get all informations for this item
1046
        $dataItem = DB::queryFirstRow(
1047
            'SELECT *
1048
            FROM ' . prefixTable('items') . ' as i
1049
            INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
1050
            WHERE i.id=%i AND l.action = %s',
1051
            $inputData['itemId'],
1052
            'at_creation'
1053
        );
1054
1055
        // Always check what rights user has on requested folder
1056
        $checkRights = getCurrentAccessRights(
1057
            $session->get('user-id'),
1058
            $inputData['itemId'],
1059
            $inputData['folderId'],
1060
        );
1061
1062
        // If source and destination folder are different -> move item
1063
        if ((int) $dataItem['id_tree'] !== $inputData['folderId']) {
1064
            // Check that user can delete on old folder
1065
            if ($checkRights['error'] || !$checkRights['delete']) {
1066
                echo (string) prepareExchangedData(
1067
                    array(
1068
                        'error' => true,
1069
                        'message' => $lang->get('error_not_allowed_to'),
1070
                    ),
1071
                    'encode'
1072
                );
1073
                break;
1074
            }
1075
        }
1076
1077
        if ($checkRights['error'] || !$checkRights['edit']) {
1078
            echo (string) prepareExchangedData(
1079
                array(
1080
                    'error' => true,
1081
                    'message' => $lang->get('error_not_allowed_to'),
1082
                ),
1083
                'encode'
1084
            );
1085
            break;
1086
        }
1087
1088
        // Does the user has the sharekey
1089
        //db::debugmode(true);
1090
        DB::query(
1091
            'SELECT *
1092
            FROM ' . prefixTable('sharekeys_items') . '
1093
            WHERE object_id = %i AND user_id = %s',
1094
            $inputData['itemId'],
1095
            $session->get('user-id')
1096
        );
1097
        if (DB::count() === 0) {
1098
            if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) {
1099
                error_log('TEAMPASS | user '.$session->get('user-id').' has no sharekey for item '.$inputData['itemId']);
1100
            }
1101
            echo (string) prepareExchangedData(
1102
                array(
1103
                    'error' => true,
1104
                    'message' => $lang->get('error_not_allowed_to'),
1105
                ),
1106
                'encode'
1107
            );
1108
            break;
1109
        }
1110
1111
        // check that actual user can access this item
1112
        $restrictionActive = true;
1113
        $restrictedTo = is_null($dataItem['restricted_to']) === false ? array_filter(explode(';', $dataItem['restricted_to'])) : [];
1114
        if (in_array($session->get('user-id'), $restrictedTo) === true) {
1115
            $restrictionActive = false;
1116
        }
1117
        if (empty($dataItem['restricted_to']) === true) {
1118
            $restrictionActive = false;
1119
        }
1120
1121
        // DO init        
1122
        $listOfRestricted = $oldRestrictionList = '';
1123
        $arrayOfUsersRestriction = [];
1124
        $arrayOfUsersIdRestriction = [];
1125
        $diffUsersRestiction = [];
1126
        $diffRolesRestiction = [];
1127
        $arrayOfRestrictionRoles = [];
1128
1129
        $session__list_restricted_folders_for_items = $session->get('system-list_restricted_folders_for_items') ?? [];
1130
        if ((in_array($dataItem['id_tree'], $session->get('user-accessible_folders')) === true
1131
                && ((int) $dataItem['perso'] === 0
1132
                    || ((int) $dataItem['perso'] === 1
1133
                        //&& (int) $session->get('user-id') === (int) $dataItem['id_user']))
1134
                    ))
1135
                && $restrictionActive === false)
1136
            || (isset($SETTINGS['anyone_can_modify']) === true
1137
                && (int) $SETTINGS['anyone_can_modify'] === 1
1138
                && (int) $dataItem['anyone_can_modify'] === 1
1139
                && (in_array($dataItem['id_tree'], $session->get('user-accessible_folders')) === true
1140
                    || (int) $session->get('user-admin') === 1)
1141
                && $restrictionActive === false)
1142
            || (null !== $inputData['folderId']
1143
                && count($session__list_restricted_folders_for_items) > 0
1144
                && in_array($inputData['id'], $session__list_restricted_folders_for_items[$inputData['folderId']]) === true
1145
                && $restrictionActive === false)
1146
        ) {
1147
            // Get existing values
1148
            $data = DB::queryFirstRow(
1149
                'SELECT i.id as id, i.label as label, i.description as description, i.pw as pw, i.url as url, i.id_tree as id_tree, i.perso as perso, i.login as login, 
1150
                i.inactif as inactif, i.restricted_to as restricted_to, i.anyone_can_modify as anyone_can_modify, i.email as email, i.notification as notification,
1151
                u.login as user_login, u.email as user_email
1152
                FROM ' . prefixTable('items') . ' as i
1153
                INNER JOIN ' . prefixTable('log_items') . ' as l ON (i.id=l.id_item)
1154
                INNER JOIN ' . prefixTable('users') . ' as u ON (u.id=l.id_user)
1155
                WHERE i.id=%i',
1156
                $inputData['itemId']
1157
            );
1158
1159
            // Should we log a password change?
1160
            $userKey = DB::queryFirstRow(
1161
                'SELECT share_key
1162
                FROM ' . prefixTable('sharekeys_items') . '
1163
                WHERE user_id = %i AND object_id = %i',
1164
                $session->get('user-id'),
1165
                $inputData['itemId']
1166
            );
1167
            if (DB::count() === 0 || empty($data['pw']) === true) {
1168
                // No share key found
1169
                $pw = '';
1170
            } else {
1171
                $pw = base64_decode(doDataDecryption(
1172
                    $data['pw'],
1173
                    decryptUserObjectKey(
1174
                        $userKey['share_key'],
1175
                        $session->get('user-private_key')
1176
                    )
1177
                ));
1178
            }
1179
1180
            if ($post_password !== $pw) {
1181
                // Encrypt previous pw
1182
                $previousValue = cryption(
1183
                    $pw,
1184
                    '',
1185
                    'encrypt'
1186
                );
1187
1188
                // log the change of PW
1189
                logItems(
1190
                    $SETTINGS,
1191
                    (int) $inputData['itemId'],
1192
                    $inputData['label'],
1193
                    $session->get('user-id'),
1194
                    'at_modification',
1195
                    $session->get('user-login'),
1196
                    'at_pw',
1197
                    TP_ENCRYPTION_NAME,
1198
                    NULL,
1199
                    isset($previousValue['string']) === true ? $previousValue['string'] : '',
1200
                );
1201
            }
1202
            
1203
            // encrypt PW on if it has changed, or if it is empty
1204
            if (
1205
                (
1206
                    (
1207
                        $session->has('user-create_item_without_password')
1208
                        && (int) $session->get('user-create_item_without_password') !== 1
1209
                    )
1210
                    || !empty($post_password)
1211
                )
1212
                && $post_password !== $pw
1213
            ) {
1214
                //-----
1215
                // NEW ENCRYPTION
1216
                $cryptedStuff = doDataEncryption($post_password);
1217
                $encrypted_password = $cryptedStuff['encrypted'];
1218
                $encrypted_password_key = $cryptedStuff['objectKey'];
1219
1220
                // Create sharekeys for users
1221
                storeUsersShareKey(
1222
                    prefixTable('sharekeys_items'),
1223
                    (int) $post_folder_is_personal,
1224
                    (int) $inputData['itemId'],
1225
                    $encrypted_password_key,
1226
                    true,   // only for the item creator
1227
                    true,   // delete all
1228
                );
1229
1230
                // Create a task to create sharekeys for users
1231
                if (WIP=== true) error_log('createTaskForItem - new password for this item - '.$post_password ." -- ". $pw);
1232
                $tasksToBePerformed = ['item_password'];
1233
                $encryptionTaskIsRequested = true;
1234
            } else {
1235
                $encrypted_password = $data['pw'];
1236
            }
1237
1238
            // ---Manage tags
1239
            // Get list of tags
1240
            $itemTags = DB::queryFirstColumn(
1241
                'SELECT tag
1242
                FROM ' . prefixTable('tags') . '
1243
                WHERE item_id = %i',
1244
                $inputData['itemId']
1245
            );
1246
1247
            // deleting existing tags for this item
1248
            DB::delete(
1249
                prefixTable('tags'),
1250
                'item_id = %i',
1251
                $inputData['itemId']
1252
            );
1253
1254
            // Add new tags
1255
            $postArrayTags = [];
1256
            if (empty($post_tags) === false) {
1257
                $postArrayTags = explode(' ', $post_tags);
1258
                foreach ($postArrayTags as $tag) {
1259
                    if (empty($tag) === false) {
1260
                    // save in DB
1261
                        DB::insert(
1262
                            prefixTable('tags'),
1263
                            array(
1264
                                'item_id' => $inputData['itemId'],
1265
                                'tag' => strtolower($tag),
1266
                            )
1267
                        );
1268
                    }
1269
                }
1270
            }
1271
1272
            // Store LOG
1273
            if (count(array_diff($postArrayTags, $itemTags)) > 0) {
1274
                // Store updates performed
1275
                array_push(
1276
                    $arrayOfChanges,
1277
                    'tags'
1278
                );
1279
1280
                // update LOG
1281
                logItems(
1282
                    $SETTINGS,
1283
                    (int) $inputData['itemId'],
1284
                    $inputData['label'],
1285
                    $session->get('user-id'),
1286
                    'at_modification',
1287
                    $session->get('user-login'),
1288
                    'at_tag : ' . implode(' ', $itemTags) . ' => ' . $post_tags
1289
                );
1290
            }
1291
1292
            // update item
1293
            DB::update(
1294
                prefixTable('items'),
1295
                array(
1296
                    'label' => $inputData['label'],
1297
                    'description' => $post_description,
1298
                    'pw' => $encrypted_password,
1299
                    'pw_len' => $strlen_post_password,
1300
                    'email' => $post_email,
1301
                    'login' => $post_login,
1302
                    'url' => $post_url,
1303
                    'id_tree' => $inputData['folderId'],
1304
                    'restricted_to' => empty($post_restricted_to) === true || count($post_restricted_to) === 0 ? '' : implode(';', $post_restricted_to),
1305
                    'anyone_can_modify' => (int) $post_anyone_can_modify,
1306
                    'complexity_level' => (int) $post_complexity_level,
1307
                    'encryption_type' => TP_ENCRYPTION_NAME,
1308
                    'perso' => in_array($inputData['folderId'], $session->get('user-personal_folders')) === true ? 1 : 0,
1309
                    'fa_icon' => $post_fa_icon,
1310
                    'updated_at' => time(),
1311
                ),
1312
                'id=%i',
1313
                $inputData['itemId']
1314
            );
1315
1316
            // update fields
1317
            if (
1318
                isset($SETTINGS['item_extra_fields']) === true
1319
                && (int) $SETTINGS['item_extra_fields'] === 1
1320
                && empty($post_fields) === false
1321
            ) {                
1322
                foreach ($post_fields as $field) {
1323
                    if (empty($field['value']) === false) {
1324
                        $dataTmpCat = DB::queryFirstRow(
1325
                            'SELECT c.id AS id, c.title AS title, i.data AS data, i.data_iv AS data_iv,
1326
                            i.encryption_type AS encryption_type, c.encrypted_data AS encrypted_data,
1327
                            c.masked AS masked, i.id AS field_item_id
1328
                            FROM ' . prefixTable('categories_items') . ' AS i
1329
                            INNER JOIN ' . prefixTable('categories') . ' AS c ON (i.field_id=c.id)
1330
                            WHERE i.field_id = %i AND i.item_id = %i',
1331
                            $field['id'],
1332
                            $inputData['itemId']
1333
                        );
1334
                        $cryptedStuff = [];
1335
                        $encryptedFieldIsChanged = false;
1336
1337
                        // store Field text in DB
1338
                        if (DB::count() === 0) {
1339
                            // The data for this field doesn't exist
1340
                            // It has to be added
1341
1342
                            // Perform new query
1343
                            $dataTmpCat = DB::queryFirstRow(
1344
                                'SELECT id, title, encrypted_data, masked
1345
                                FROM ' . prefixTable('categories') . '
1346
                                WHERE id = %i',
1347
                                $field['id']
1348
                            );
1349
1350
                            // store field text
1351
                            DB::insert(
1352
                                prefixTable('categories_items'),
1353
                                array(
1354
                                    'item_id' => $inputData['itemId'],
1355
                                    'field_id' => $field['id'],
1356
                                    'data' => $field['value'],
1357
                                    'data_iv' => '',
1358
                                    'encryption_type' => 'not_set',
1359
                                )
1360
                            );
1361
1362
                            $newId = DB::insertId();
1363
                            $dataTmpCat['field_item_id'] = $newId;
1364
1365
                            // Should we encrypt the data
1366
                            if ((int) $dataTmpCat['encrypted_data'] === 1) {
1367
                                $cryptedStuff = doDataEncryption($field['value']);
1368
1369
                                // Create sharekeys for users
1370
                                storeUsersShareKey(
1371
                                    prefixTable('sharekeys_fields'),
1372
                                    (int) $post_folder_is_personal,
1373
                                    (int) $newId,
1374
                                    $cryptedStuff['objectKey'],
1375
                                    true,   // only for the item creator
1376
                                    true,   // delete all
1377
                                );
1378
1379
                                // update value
1380
                                DB::update(
1381
                                    prefixTable('categories_items'),
1382
                                    array(
1383
                                        'data' => $cryptedStuff['encrypted'],
1384
                                        'data_iv' => '',
1385
                                        'encryption_type' => TP_ENCRYPTION_NAME,
1386
                                    ),
1387
                                    'id = %i',
1388
                                    $newId
1389
                                );
1390
1391
                                if ($encryptedFieldIsChanged === false) {
1392
                                    array_push(
1393
                                        $tasksToBePerformed,
1394
                                        'item_field'
1395
                                    );
1396
                                    $encryptedFieldIsChanged = true;
1397
                                }
1398
                            } else {
1399
                                // update value
1400
                                DB::update(
1401
                                    prefixTable('categories_items'),
1402
                                    array(
1403
                                        'data' => $field['value'],
1404
                                        'data_iv' => '',
1405
                                        'encryption_type' => 'not_set',
1406
                                    ),
1407
                                    'id = %i',
1408
                                    $newId
1409
                                );
1410
                            }
1411
1412
                            // Store updates performed
1413
                            array_push(
1414
                                $arrayOfChanges,
1415
                                $dataTmpCat['title']
1416
                            );
1417
1418
                            // update LOG
1419
                            logItems(
1420
                                $SETTINGS,
1421
                                (int) $inputData['itemId'],
1422
                                $inputData['label'],
1423
                                $session->get('user-id'),
1424
                                'at_modification',
1425
                                $session->get('user-login'),
1426
                                'at_field : ' . $dataTmpCat['title'] . ' : ' . $field['value']
1427
                            );
1428
                        } else {
1429
                            // Case where the field already exists
1430
                            // compare the old and new value
1431
                            if ($dataTmpCat['encryption_type'] !== 'not_set') {
1432
                                // Get user sharekey for this field
1433
                                $userKey = DB::queryFirstRow(
1434
                                    'SELECT share_key
1435
                                    FROM ' . prefixTable('sharekeys_fields') . '
1436
                                    WHERE user_id = %i AND object_id = %i',
1437
                                    $session->get('user-id'),
1438
                                    $dataTmpCat['field_item_id']
1439
                                );
1440
1441
                                // Decrypt the current value
1442
                                if (DB::count() > 0) {
1443
                                    $oldVal = base64_decode(doDataDecryption(
1444
                                        $dataTmpCat['data'],
1445
                                        decryptUserObjectKey(
1446
                                            $userKey['share_key'],
1447
                                            $session->get('user-private_key')
1448
                                        )
1449
                                    ));
1450
                                } else {
1451
                                    $oldVal = '';
1452
                                }
1453
                            } else {
1454
                                $oldVal = $dataTmpCat['data'];
1455
                            }
1456
1457
                            // Compare both values to see if any change was done
1458
                            if ($field['value'] !== $oldVal) {
1459
                                // The strings are different
1460
                                $encrypt = [];
1461
                                
1462
                                // Should we encrypt the data
1463
                                if ((int) $dataTmpCat['encrypted_data'] === 1) {
1464
                                    $cryptedStuff = doDataEncryption($field['value']);
1465
                                    $encrypt['string'] = $cryptedStuff['encrypted'];
1466
                                    $encrypt['type'] = TP_ENCRYPTION_NAME;
1467
1468
                                    // Create sharekeys for users
1469
                                    storeUsersShareKey(
1470
                                        prefixTable('sharekeys_fields'),
1471
                                        (int) $post_folder_is_personal,
1472
                                        (int) $dataTmpCat['field_item_id'],
1473
                                        $cryptedStuff['objectKey'],
1474
                                        true,   // only for the item creator
1475
                                        true,   // delete all
1476
                                    );
1477
1478
                                    if ($encryptedFieldIsChanged === false) {
1479
                                        array_push(
1480
                                            $tasksToBePerformed,
1481
                                            'item_field'
1482
                                        );
1483
                                        $encryptedFieldIsChanged = true;
1484
                                    }
1485
                                } else {
1486
                                    $encrypt['string'] = $field['value'];
1487
                                    $encrypt['type'] = 'not_set';
1488
                                }
1489
1490
                                // update value
1491
                                DB::update(
1492
                                    prefixTable('categories_items'),
1493
                                    array(
1494
                                        'data' => $encrypt['string'],
1495
                                        'data_iv' => '',
1496
                                        'encryption_type' => $encrypt['type'],
1497
                                    ),
1498
                                    'item_id = %i AND field_id = %i',
1499
                                    $inputData['itemId'],
1500
                                    $field['id']
1501
                                );
1502
1503
                                // Store updates performed
1504
                                array_push(
1505
                                    $arrayOfChanges,
1506
                                    $dataTmpCat['title']
1507
                                );
1508
1509
                                // update LOG
1510
                                logItems(
1511
                                    $SETTINGS,
1512
                                    (int) $inputData['itemId'],
1513
                                    $inputData['label'],
1514
                                    $session->get('user-id'),
1515
                                    'at_modification',
1516
                                    $session->get('user-login'),
1517
                                    'at_field : ' . $dataTmpCat['title'] . ' => ' . $oldVal
1518
                                );
1519
                            }
1520
                        }
1521
1522
                        // Create a task to create sharekeys for this field for users
1523
                        // If this field is encrypted
1524
                        if ((int) $dataTmpCat['encrypted_data'] === 1 && $encryptedFieldIsChanged === true) {
1525
                            array_push(
1526
                                $itemFieldsForTasks,
1527
                                [
1528
                                    'object_id' => $dataTmpCat['field_item_id'],
1529
                                    'object_key' => $cryptedStuff['objectKey'],
1530
                                ]
1531
                            );
1532
                            $encryptionTaskIsRequested = true;
1533
                        }
1534
                    } else {
1535
                        // Case where field new value is empty
1536
                        // then delete field
1537
                        if (empty($field_data[1]) === true) {
1538
                            DB::delete(
1539
                                prefixTable('categories_items'),
1540
                                'item_id = %i AND field_id = %s',
1541
                                $inputData['itemId'],
1542
                                $field['id']
1543
                            );
1544
                        }
1545
                    }
1546
                }
1547
            }
1548
1549
            // create a task for all fields updated
1550
            if ($encryptionTaskIsRequested === true) {
1551
                if (WIP === true) error_log('createTaskForItem - '.print_r($tasksToBePerformed, true));
1552
                createTaskForItem(
1553
                    'item_update_create_keys',
1554
                    array_unique($tasksToBePerformed),
1555
                    (int) $inputData['itemId'],
1556
                    (int) $session->get('user-id'),
1557
                    $encrypted_password_key,
1558
                    (int) $inputData['itemId'],
1559
                    $itemFieldsForTasks,
1560
                    []
1561
                );
1562
            }
1563
1564
            // If template enable, is there a main one selected?
1565
            if (
1566
                isset($SETTINGS['item_creation_templates']) === true
1567
                && (int) $SETTINGS['item_creation_templates'] === 1
1568
            ) {
1569
                DB::queryFirstRow(
1570
                    'SELECT *
1571
                    FROM ' . prefixTable('templates') . '
1572
                    WHERE item_id = %i',
1573
                    $inputData['itemId']
1574
                );
1575
                if (DB::count() === 0 && empty($post_template_id) === false) {
1576
                    // store field text
1577
                    DB::insert(
1578
                        prefixTable('templates'),
1579
                        array(
1580
                            'item_id' => $inputData['itemId'],
1581
                            'category_id' => $post_template_id,
1582
                        )
1583
                    );
1584
                } else {
1585
                    // Delete if empty
1586
                    if (empty($post_template_id) === true) {
1587
                        DB::delete(
1588
                            prefixTable('templates'),
1589
                            'item_id = %i',
1590
                            $inputData['itemId']
1591
                        );
1592
                    } else {
1593
                        // Update value
1594
                        DB::update(
1595
                            prefixTable('templates'),
1596
                            array(
1597
                                'category_id' => $post_template_id,
1598
                            ),
1599
                            'item_id = %i',
1600
                            $inputData['itemId']
1601
                        );
1602
                    }
1603
                }
1604
            }
1605
1606
            // Update automatic deletion - Only by the creator of the Item
1607
            if (
1608
                isset($SETTINGS['enable_delete_after_consultation']) === true
1609
                && (int) $SETTINGS['enable_delete_after_consultation'] === 1
1610
            ) {
1611
                // check if elem exists in Table. If not add it or update it.
1612
                DB::query(
1613
                    'SELECT *
1614
                    FROM ' . prefixTable('automatic_del') . '
1615
                    WHERE item_id = %i',
1616
                    $inputData['itemId']
1617
                );
1618
1619
                if (DB::count() === 0) {
1620
                    // No automatic deletion for this item
1621
                    if (
1622
                        empty($post_to_be_deleted_after_date) === false
1623
                        || (int) $post_to_be_deleted_after_x_views > 0
1624
                    ) {
1625
                        // Automatic deletion to be added
1626
                        DB::insert(
1627
                            prefixTable('automatic_del'),
1628
                            array(
1629
                                'item_id' => $inputData['itemId'],
1630
                                'del_enabled' => 1,
1631
                                'del_type' => empty($post_to_be_deleted_after_x_views) === false ?
1632
                                    1 : 2, //1 = numeric : 2 = date
1633
                                'del_value' => empty($post_to_be_deleted_after_x_views) === false ?
1634
                                    (int) $post_to_be_deleted_after_x_views : dateToStamp($post_to_be_deleted_after_date, $SETTINGS['date_format']),
1635
                            )
1636
                        );
1637
1638
                        // Store updates performed
1639
                        array_push(
1640
                            $arrayOfChanges,
1641
                            $lang->get('automatic_deletion_engaged') . ': ' . $lang->get('enabled')
1642
                        );
1643
1644
                        // update LOG
1645
                        logItems(
1646
                            $SETTINGS,
1647
                            (int) $inputData['itemId'],
1648
                            $inputData['label'],
1649
                            $session->get('user-id'),
1650
                            'at_modification',
1651
                            $session->get('user-login'),
1652
                            'at_automatic_del : enabled'
1653
                        );
1654
                    }
1655
                } else {
1656
                    // Automatic deletion exists for this item
1657
                    if (
1658
                        empty($post_to_be_deleted_after_date) === false
1659
                        || (int) $post_to_be_deleted_after_x_views > 0
1660
                    ) {
1661
                        // Update automatic deletion
1662
                        DB::update(
1663
                            prefixTable('automatic_del'),
1664
                            array(
1665
                                'del_type' => empty($post_to_be_deleted_after_x_views) === false ?
1666
                                    1 : 2, //1 = numeric : 2 = date
1667
                                'del_value' => empty($post_to_be_deleted_after_x_views) === false ?
1668
                                    $post_to_be_deleted_after_x_views : dateToStamp($post_to_be_deleted_after_date, $SETTINGS['date_format']),
1669
                            ),
1670
                            'item_id = %i',
1671
                            $inputData['itemId']
1672
                        );
1673
                    } else {
1674
                        // delete automatic deleteion for this item
1675
                        DB::delete(
1676
                            prefixTable('automatic_del'),
1677
                            'item_id = %i',
1678
                            $inputData['itemId']
1679
                        );
1680
1681
                        // Store updates performed
1682
                        array_push(
1683
                            $arrayOfChanges,
1684
                            $lang->get('automatic_deletion_engaged') . ': ' . $lang->get('disabled')
1685
                        );
1686
1687
                        // update LOG
1688
                        logItems(
1689
                            $SETTINGS,
1690
                            (int) $inputData['itemId'],
1691
                            $inputData['label'],
1692
                            $session->get('user-id'),
1693
                            'at_modification',
1694
                            $session->get('user-login'),
1695
                            'at_automatic_del : disabled'
1696
                        );
1697
                    }
1698
                }
1699
            }
1700
1701
            // get readable list of restriction
1702
            if (
1703
                is_array($post_restricted_to) === true
1704
                && count($post_restricted_to) > 0
1705
                && isset($SETTINGS['restricted_to']) === true
1706
                && (int) $SETTINGS['restricted_to'] === 1
1707
            ) {
1708
                foreach ($post_restricted_to as $userId) {
1709
                    if (empty($userId) === false) {
1710
                        $dataTmp = DB::queryFirstRow(
1711
                            'SELECT id, name, lastname
1712
                            FROM ' . prefixTable('users') . '
1713
                            WHERE id= %i',
1714
                            $userId
1715
                        );
1716
1717
                        // Add to array
1718
                        array_push(
1719
                            $arrayOfUsersRestriction,
1720
                            $dataTmp['name'] . ' ' . $dataTmp['lastname']
1721
                        );
1722
                        array_push(
1723
                            $arrayOfUsersIdRestriction,
1724
                            $dataTmp['id']
1725
                        );
1726
                    }
1727
                }
1728
            }
1729
            if ((int) $SETTINGS['restricted_to'] === 1) {
1730
                $diffUsersRestiction = array_diff(
1731
                    empty($data['restricted_to']) === false ?
1732
                        explode(';', $data['restricted_to']) : array(),
1733
                    $arrayOfUsersIdRestriction
1734
                );
1735
            }
1736
1737
            // Manage retriction_to_roles
1738
            if (
1739
                is_array($post_restricted_to_roles) === true
1740
                && count($post_restricted_to_roles) > 0
1741
                && isset($SETTINGS['restricted_to_roles']) === true
1742
                && (int) $SETTINGS['restricted_to_roles'] === 1
1743
            ) {
1744
                // Init
1745
                $arrayOfRestrictionRolesOld = array();
1746
                $arrayOfRestrictionRoles = array();
1747
1748
                // get values before deleting them
1749
                $rows = DB::query(
1750
                    'SELECT t.title, t.id AS id
1751
                    FROM ' . prefixTable('roles_title') . ' as t
1752
                    INNER JOIN ' . prefixTable('restriction_to_roles') . ' as r ON (t.id=r.role_id)
1753
                    WHERE r.item_id = %i
1754
                    ORDER BY t.title ASC',
1755
                    $inputData['itemId']
1756
                );
1757
                foreach ($rows as $record) {
1758
                    // Add to array
1759
                    array_push(
1760
                        $arrayOfRestrictionRolesOld,
1761
                        $record['title']
1762
                    );
1763
                }
1764
                // delete previous values
1765
                DB::delete(
1766
                    prefixTable('restriction_to_roles'),
1767
                    'item_id = %i',
1768
                    $inputData['itemId']
1769
                );
1770
1771
                // add roles for item
1772
                if (
1773
                    is_array($post_restricted_to_roles) === true
1774
                    && count($post_restricted_to_roles) > 0
1775
                ) {
1776
                    foreach ($post_restricted_to_roles as $role) {
1777
                        DB::insert(
1778
                            prefixTable('restriction_to_roles'),
1779
                            array(
1780
                                'role_id' => $role,
1781
                                'item_id' => $inputData['itemId'],
1782
                            )
1783
                        );
1784
                        $dataTmp = DB::queryFirstRow(
1785
                            'SELECT title
1786
                            FROM ' . prefixTable('roles_title') . '
1787
                            WHERE id = %i',
1788
                            $role
1789
                        );
1790
1791
                        // Add to array
1792
                        array_push(
1793
                            $arrayOfRestrictionRoles,
1794
                            $dataTmp['title']
1795
                        );
1796
                    }
1797
1798
                    if ((int) $SETTINGS['restricted_to'] === 1) {
1799
                        $diffRolesRestiction = array_diff(
1800
                            $arrayOfRestrictionRoles,
1801
                            $arrayOfRestrictionRolesOld
1802
                        );
1803
                    }
1804
                }
1805
            }
1806
            // Update CACHE table
1807
            updateCacheTable('update_value', (int) $inputData['itemId']);
1808
1809
1810
            // Manage OTP status
1811
            // Get current status
1812
            $otpStatus = DB::queryFirstRow(
1813
                'SELECT enabled as otp_is_enabled, phone_number, secret
1814
                FROM ' . prefixTable('items_otp') . '
1815
                WHERE item_id = %i',
1816
                $inputData['itemId']
1817
            );
1818
1819
            // If previous OTP secret is not empty, decrypt it
1820
            if (DB::count() > 0 && $otpStatus['secret'] !== '') {
1821
                // Get current secret
1822
                $currentsecret = cryption(
1823
                    $otpStatus['secret'],
1824
                    '',
1825
                    'decrypt'
1826
                )['string'];
1827
            } else {
1828
                $currentsecret='';
1829
            }
1830
1831
            // If OTP secret provided then encrypt it
1832
            if (empty($post_otp_secret) === false) {
1833
                // Encrypt secret
1834
                $encryptedSecret = cryption(
1835
                    $post_otp_secret,
1836
                    '',
1837
                    'encrypt'
1838
                );
1839
           }
1840
1841
            // Check if status or secret or phone number has changed
1842
            if (DB::count() > 0
1843
                && (
1844
                    ((int) $otpStatus['otp_is_enabled'] !== (int) $post_otp_is_enabled)
1845
                    || ($otpStatus['phone_number'] !== $post_otp_phone_number)
1846
                    || ($currentsecret !== $post_otp_secret)
1847
                )
1848
                && isset($encryptedSecret['string']) === true
1849
            ) {
1850
                // Update status
1851
                DB::update(
1852
                    prefixTable('items_otp'),
1853
                    array(
1854
                        'enabled' => (int) $post_otp_is_enabled,
1855
                        'secret' => $encryptedSecret['string'],
1856
                        'phone_number' => $post_otp_phone_number,
1857
                        'timestamp' => time(),
1858
                    ),
1859
                    'item_id = %i',
1860
                    $inputData['itemId']
1861
                );
1862
1863
                // Store updates performed
1864
                array_push(
1865
                    $arrayOfChanges,
1866
                    $lang->get('otp_status')
1867
                );
1868
1869
                // update LOG
1870
                if ((int) $otpStatus['otp_is_enabled'] !== (int) $post_otp_is_enabled) {
1871
                    logItems(
1872
                        $SETTINGS,
1873
                        (int) $inputData['itemId'],
1874
                        $inputData['label'],
1875
                        $session->get('user-id'),
1876
                        'at_modification',
1877
                        $session->get('user-login'),
1878
                        'at_otp_status:' . ((int) $post_otp_is_enabled === 0 ? 'disabled' : 'enabled')
1879
                    );
1880
                }
1881
                if ($otpStatus['phone_number'] !== $post_otp_phone_number) {
1882
                    logItems(
1883
                        $SETTINGS,
1884
                        (int) $inputData['itemId'],
1885
                        $inputData['label'],
1886
                        $session->get('user-id'),
1887
                        'at_modification',
1888
                        $session->get('user-login'),
1889
                        'at_phone_number:' . $otpStatus['phone_number'] . ' => ' . $post_otp_phone_number
1890
                    );
1891
                }
1892
                if ($currentsecret !== $post_otp_secret) {
1893
                    logItems(
1894
                        $SETTINGS,
1895
                        (int) $inputData['itemId'],
1896
                        $inputData['label'],
1897
                        $session->get('user-id'),
1898
                        'at_modification',
1899
                        $session->get('user-login'),
1900
                        'at_otp_secret:'.$currentsecret
1901
                    );
1902
                }
1903
            } elseif (DB::count() === 0 && empty($post_otp_secret) === false) {
1904
                // Create the entry in items_otp table
1905
                // OTP doesn't exist then create it
1906
                
1907
                // insert in table
1908
                DB::insert(
1909
                    prefixTable('items_otp'),
1910
                    array(
1911
                        'item_id' => $inputData['itemId'],
1912
                        'secret' => $encryptedSecret['string'],
1913
                        'phone_number' => $post_otp_phone_number,
1914
                        'timestamp' => time(),
1915
                        'enabled' => 1,
1916
                    )
1917
                );
1918
            }
1919
1920
            //---- Log all modifications done ----
1921
1922
            // RESTRICTIONS
1923
            if (count($diffRolesRestiction) > 0 || count($diffUsersRestiction) > 0) {
1924
                // Store updates performed
1925
                array_push(
1926
                    $arrayOfChanges,
1927
                    $lang->get('at_restriction')
1928
                );
1929
1930
                // Log
1931
                logItems(
1932
                    $SETTINGS,
1933
                    (int) $inputData['itemId'],
1934
                    $inputData['label'],
1935
                    $session->get('user-id'),
1936
                    'at_modification',
1937
                    $session->get('user-login'),
1938
                    'at_restriction : ' . (count($diffUsersRestiction) > 0 ?
1939
                        implode(', ', $arrayOfUsersRestriction) . (count($diffRolesRestiction) > 0 ? ', ' : '') : '') . (count($diffRolesRestiction) > 0 ? implode(', ', $arrayOfRestrictionRoles) : '')
1940
                );
1941
            }
1942
1943
            // LABEL
1944
            if ($data['label'] !== $inputData['label']) {
1945
                // Store updates performed
1946
                array_push(
1947
                    $arrayOfChanges,
1948
                    $lang->get('at_label')
1949
                );
1950
1951
                // Log
1952
                logItems(
1953
                    $SETTINGS,
1954
                    (int) $inputData['itemId'],
1955
                    $inputData['label'],
1956
                    $session->get('user-id'),
1957
                    'at_modification',
1958
                    $session->get('user-login'),
1959
                    'at_label : ' . $data['label'] . ' => ' . $inputData['label']
1960
                );
1961
            }
1962
            // LOGIN
1963
            if ($data['login'] !== $post_login) {
1964
                // Store updates performed
1965
                array_push(
1966
                    $arrayOfChanges,
1967
                    $lang->get('at_login')
1968
                );
1969
1970
                // Log
1971
                logItems(
1972
                    $SETTINGS,
1973
                    (int) $inputData['itemId'],
1974
                    $inputData['label'],
1975
                    $session->get('user-id'),
1976
                    'at_modification',
1977
                    $session->get('user-login'),
1978
                    'at_login : ' . $data['login'] . ' => ' . $post_login
1979
                );
1980
            }
1981
            // EMAIL
1982
            if ($post_email !== null && $data['email'] !== null && strcmp($data['email'], $post_email) !== 0) {
1983
                // Store updates performed
1984
                array_push(
1985
                    $arrayOfChanges,
1986
                    $lang->get('at_email')
1987
                );
1988
1989
                // Log
1990
                logItems(
1991
                    $SETTINGS,
1992
                    (int) $inputData['itemId'],
1993
                    $inputData['label'],
1994
                    $session->get('user-id'),
1995
                    'at_modification',
1996
                    $session->get('user-login'),
1997
                    'at_email : ' . $data['email'] . ' => ' . $post_email
1998
                );
1999
            }
2000
            // URL
2001
            if ($data['url'] !== $post_url && $post_url !== 'http://') {
2002
                // Store updates performed
2003
                array_push(
2004
                    $arrayOfChanges,
2005
                    $lang->get('at_url')
2006
                );
2007
2008
                // Log
2009
                logItems(
2010
                    $SETTINGS,
2011
                    (int) $inputData['itemId'],
2012
                    $inputData['label'],
2013
                    $session->get('user-id'),
2014
                    'at_modification',
2015
                    $session->get('user-login'),
2016
                    'at_url : ' . $data['url'] . ' => ' . $post_url
2017
                );
2018
            }
2019
            // DESCRIPTION
2020
            // deepcode ignore InsecureHash: md5 is used just to perform a string encrypted comparison
2021
            if (strcmp(md5(strip_tags($data['description'])), md5(strip_tags($post_description))) !== 0) {
2022
                // Store updates performed
2023
                array_push(
2024
                    $arrayOfChanges,
2025
                    $lang->get('at_description')
2026
                );
2027
2028
                // Log
2029
                logItems(
2030
                    $SETTINGS,
2031
                    (int) $inputData['itemId'],
2032
                    $inputData['label'],
2033
                    $session->get('user-id'),
2034
                    'at_modification',
2035
                    $session->get('user-login'),
2036
                    'at_description'
2037
                );
2038
            }
2039
            // FOLDER
2040
            if ((int) $data['id_tree'] !== (int) $inputData['folderId']) {
2041
                // Get name of folders
2042
                $dataTmp = DB::query('SELECT title FROM ' . prefixTable('nested_tree') . ' WHERE id IN %li', array($data['id_tree'], $inputData['folderId']));
2043
2044
                // Store updates performed
2045
                array_push(
2046
                    $arrayOfChanges,
2047
                    $lang->get('at_category')
2048
                );
2049
2050
                // Log
2051
                logItems(
2052
                    $SETTINGS,
2053
                    (int) $inputData['itemId'],
2054
                    $inputData['label'],
2055
                    $session->get('user-id'),
2056
                    'at_modification',
2057
                    $session->get('user-login'),
2058
                    'at_category : ' . $dataTmp[0]['title'] . ' => ' . $dataTmp[1]['title']
2059
                );
2060
            }
2061
            // ANYONE_CAN_MODIFY
2062
            if ((int) $post_anyone_can_modify !== (int) $data['anyone_can_modify']) {
2063
                // Store updates performed
2064
                array_push(
2065
                    $arrayOfChanges,
2066
                    $lang->get('at_anyoneconmodify') . ': ' . ((int) $post_anyone_can_modify === 0 ? $lang->get('disabled') : $lang->get('enabled'))
2067
                );
2068
2069
                // Log
2070
                logItems(
2071
                    $SETTINGS,
2072
                    (int) $inputData['itemId'],
2073
                    $inputData['label'],
2074
                    $session->get('user-id'),
2075
                    'at_modification',
2076
                    $session->get('user-login'),
2077
                    'at_anyoneconmodify : ' . ((int) $post_anyone_can_modify === 0 ? 'disabled' : 'enabled')
2078
                );
2079
            }
2080
2081
            // Reload new values
2082
            $dataItem = DB::queryFirstRow(
2083
                'SELECT *
2084
                FROM ' . prefixTable('items') . ' as i
2085
                INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
2086
                WHERE i.id = %i AND l.action = %s',
2087
                $inputData['itemId'],
2088
                'at_creation'
2089
            );
2090
            // Reload History
2091
            $history = '';
2092
            $rows = DB::query(
2093
                'SELECT l.date as date, l.action as action, l.raison as raison, u.login as login
2094
                FROM ' . prefixTable('log_items') . ' as l
2095
                LEFT JOIN ' . prefixTable('users') . ' as u ON (l.id_user=u.id)
2096
                WHERE l.action <> %s AND id_item=%s',
2097
                'at_shown',
2098
                $inputData['itemId']
2099
            );
2100
            foreach ($rows as $record) {
2101
                if ($record['raison'] === NULL) continue;
2102
                $reason = explode(':', $record['raison']);
2103
                if (count($reason) > 0) {
2104
                    $sentence = date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['date']) . ' - '
2105
                        . $record['login'] . ' - ' . $lang->get($record['action']) . ' - '
2106
                        . (empty($record['raison']) === false ? (count($reason) > 1 ? $lang->get(trim($reason[0])) . ' : ' . $reason[1]
2107
                            : $lang->get(trim($reason[0]))) : '');
2108
                    if (empty($history)) {
2109
                        $history = $sentence;
2110
                    } else {
2111
                        $history .= '<br />' . $sentence;
2112
                    }
2113
                }
2114
            }
2115
2116
            // generate 2d key
2117
            $session->set('user-key_tmp', bin2hex(GenerateCryptKey(16, false, true, true, false, true)));
2118
2119
            // Send email
2120
            if (is_array($post_diffusion_list) === true && count($post_diffusion_list) > 0) {
2121
                $cpt = 0;
2122
                foreach ($post_diffusion_list as $emailAddress) {
2123
                    if (empty($emailAddress) === false) {
2124
                        prepareSendingEmail(
2125
                            $lang->get('email_subject_item_updated'),
2126
                            str_replace(
2127
                                array('#item_label#', '#item_category#', '#item_id#', '#url#', '#name#', '#lastname#', '#folder_name#'),
2128
                                array($inputData['label'], (string) $inputData['folderId'], (string) $inputData['itemId'], $SETTINGS['cpassman_url'], $session->get('user-name'), $session->get('user-lastname'), $dataFolderSettings['title']),
2129
                                $lang->get('email_body_item_updated')
2130
                            ),
2131
                            $emailAddress,
2132
                            $post_diffusion_list_names[$cpt]
2133
                        );
2134
                        $cpt++;
2135
                    }
2136
                }
2137
            }
2138
2139
            // Remove the edition lock if no  encryption steps are needed
2140
            if ($encryptionTaskIsRequested === false) {
2141
                if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) {
2142
                    error_log('Remove the edition lock if no  encryption steps are needed');
2143
                }
2144
                DB::delete(
2145
                    prefixTable('items_edition'), 
2146
                    'item_id = %i AND user_id = %i', 
2147
                    $inputData['itemId'],
2148
                    $session->get('user-id')
2149
                );
2150
            }
2151
2152
            // Notifiy changes to the users
2153
            notifyChangesToSubscribers($inputData['itemId'], $inputData['label'], $arrayOfChanges, $SETTINGS);
2154
2155
            // Prepare some stuff to return
2156
            $arrData = array(
2157
                'error' => false,
2158
                'message' => '',
2159
            );
2160
        } else {
2161
            echo (string) prepareExchangedData(
2162
                array(
2163
                    'error' => true,
2164
                    'message' => $lang->get('error_not_allowed_to_edit_item'),
2165
                ),
2166
                'encode'
2167
            );
2168
            break;
2169
        }
2170
        
2171
        // return data
2172
        echo (string) prepareExchangedData(
2173
            $arrData,
2174
            'encode'
2175
        );
2176
        break;
2177
2178
    /*
2179
     * CASE
2180
     * Copy an Item
2181
     */
2182
    case 'copy_item':
2183
        // Check KEY and rights
2184
        if ($inputData['key'] !== $session->get('key')) {
2185
            echo (string) prepareExchangedData(
2186
                array(
2187
                    'error' => true,
2188
                    'message' => $lang->get('key_is_not_correct'),
2189
                ),
2190
                'encode'
2191
            );
2192
            break;
2193
        }
2194
        if ($session->get('user-read_only') === 1) {
2195
            echo (string) prepareExchangedData(
2196
                array(
2197
                    'error' => true,
2198
                    'message' => $lang->get('error_not_allowed_to'),
2199
                ),
2200
                'encode'
2201
            );
2202
            break;
2203
        }
2204
2205
        // decrypt and retreive data in JSON format
2206
        $dataReceived = prepareExchangedData(
2207
            $inputData['data'],
2208
            'decode'
2209
        );
2210
2211
        // Prepare POST variables
2212
        $post_new_label = (string) filter_var($dataReceived['new_label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2213
        $post_source_id = (int) filter_var($dataReceived['source_id'], FILTER_SANITIZE_NUMBER_INT);
2214
        $post_dest_id = (int) filter_var($dataReceived['dest_id'], FILTER_SANITIZE_NUMBER_INT);
2215
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
2216
2217
        // perform a check in case of Read-Only user creating an item in his PF
2218
        if (
2219
            (int) $session->get('user-read_only') === 1
2220
            && (in_array($post_source_id, $session->get('user-personal_folders')) === false
2221
                || in_array($post_dest_id, $session->get('user-personal_folders')) === false)
2222
        ) {
2223
            echo (string) prepareExchangedData(
2224
                array(
2225
                    'error' => true,
2226
                    'message' => $lang->get('error_not_allowed_to'),
2227
                ),
2228
                'encode'
2229
            );
2230
            break;
2231
        }
2232
2233
        // Init
2234
        $returnValues = '';
2235
        $pw = '';
2236
        $is_perso = 0;
2237
        $itemDataArray = array(
2238
            'pwd' => '',
2239
            'fields' => [],
2240
            'files' => [],
2241
        );
2242
2243
        if (
2244
            empty($inputData['itemId']) === false
2245
            && empty($post_dest_id) === false
2246
        ) {
2247
            // load the original record into an array
2248
            $originalRecord = DB::queryFirstRow(
2249
                'SELECT * FROM ' . prefixTable('items') . '
2250
                WHERE id = %i',
2251
                $inputData['itemId']
2252
            );
2253
2254
            // Check if the folder where this item is accessible to the user
2255
            if (in_array($originalRecord['id_tree'], $session->get('user-accessible_folders')) === false) {
2256
                echo (string) prepareExchangedData(
2257
                    array(
2258
                        'error' => true,
2259
                        'message' => $lang->get('error_not_allowed_to'),
2260
                    ),
2261
                    'encode'
2262
                );
2263
                break;
2264
            }
2265
2266
            // Load the destination folder record into an array
2267
            $dataDestination = DB::queryFirstRow(
2268
                'SELECT personal_folder FROM ' . prefixTable('nested_tree') . '
2269
                WHERE id = %i',
2270
                $post_dest_id
2271
            );
2272
2273
            // Get the ITEM object key for the user
2274
            $userKey = DB::queryFirstRow(
2275
                'SELECT share_key
2276
                FROM ' . prefixTable('sharekeys_items') . '
2277
                WHERE user_id = %i AND object_id = %i',
2278
                $session->get('user-id'),
2279
                $inputData['itemId']
2280
            );
2281
            if (DB::count() === 0) {
2282
                // ERROR - No sharekey found for this item and user
2283
                echo (string) prepareExchangedData(
2284
                    array(
2285
                        'error' => true,
2286
                        'message' => $lang->get('error_not_allowed_to'),
2287
                    ),
2288
                    'encode'
2289
                );
2290
                break;
2291
            }
2292
2293
            // Decrypt / Encrypt the password
2294
            $cryptedStuff = doDataEncryption(
2295
                base64_decode(
2296
                    doDataDecryption(
2297
                        $originalRecord['pw'],
2298
                        decryptUserObjectKey(
2299
                            $userKey['share_key'],
2300
                            $session->get('user-private_key')
2301
                        )
2302
                    )
2303
                )
2304
            );
2305
            // reaffect pw
2306
            $originalRecord['pw'] = $cryptedStuff['encrypted'];
2307
2308
            // store pwd object key
2309
            $itemDataArray['pwd'] = $cryptedStuff['objectKey'];
2310
2311
            // generate the query to update the new record with the previous values
2312
            $aSet = array();
2313
            foreach ($originalRecord as $key => $value) {
2314
                $aSet['item_key'] = uniqidReal(50);
2315
                $aSet['created_at'] = time();
2316
                if ($key === 'id_tree') {
2317
                    $aSet['id_tree'] = $post_dest_id;
2318
                } elseif ($key === 'label') {
2319
                    $aSet[$key] = $post_new_label;
2320
                } elseif ($key === 'viewed_no') {
2321
                    $aSet['viewed_no'] = '0';
2322
                } elseif ($key === 'pw') {
2323
                    $aSet['pw'] = $originalRecord['pw'];
2324
                    $aSet['pw_iv'] = '';
2325
                } elseif ($key === 'perso') {
2326
                    $aSet['perso'] = $is_perso;
2327
                } elseif ($key !== 'id' && $key !== 'key') {
2328
                    $aSet[$key] = $value;
2329
                }
2330
            }
2331
2332
            // insert the new record and get the new auto_increment id
2333
            DB::insert(
2334
                prefixTable('items'),
2335
                $aSet
2336
            );
2337
            $newItemId = DB::insertId();
2338
2339
            // Create sharekeys for users of this new ITEM
2340
            storeUsersShareKey(
2341
                prefixTable('sharekeys_items'),
2342
                (int) $dataDestination['personal_folder'],
2343
                (int) $newItemId,
2344
                $itemDataArray['pwd'],
2345
                true,
2346
                false,
2347
            );
2348
2349
            // --------------------
2350
            // Manage Custom Fields
2351
            $rows = DB::query(
2352
                'SELECT ci.id AS id, ci.data AS data, ci.field_id AS field_id, c.encrypted_data AS encrypted_data
2353
                FROM ' . prefixTable('categories_items') . ' AS ci
2354
                INNER JOIN ' . prefixTable('categories') . ' AS c ON (c.id = ci.field_id)
2355
                WHERE ci.item_id = %i',
2356
                $inputData['itemId']
2357
            );
2358
            foreach ($rows as $field) {
2359
                // Create the entry for the new item
2360
2361
                // Is the data encrypted
2362
                if ((int) $field['encrypted_data'] === 1) {
2363
                    // Get user key
2364
                    $userKey = DB::queryFirstRow(
2365
                        'SELECT share_key
2366
                        FROM ' . prefixTable('sharekeys_fields') . '
2367
                        WHERE user_id = %i AND object_id = %i',
2368
                        $session->get('user-id'),
2369
                        $field['id']
2370
                    );
2371
                    // Then decrypt original field value and encrypt with new key
2372
                    $cryptedStuff = doDataEncryption(
2373
                        base64_decode(
2374
                            doDataDecryption(
2375
                                $field['data'],
2376
                                decryptUserObjectKey(
2377
                                    $userKey['share_key'] ?? '',
2378
                                    $session->get('user-private_key')
2379
                                )
2380
                            )
2381
                        )
2382
                    );
2383
                    // reaffect pw
2384
                    $field['data'] = $cryptedStuff['encrypted'];
2385
                }
2386
2387
                // store field text
2388
                DB::insert(
2389
                    prefixTable('categories_items'),
2390
                    array(
2391
                        'item_id' => $newItemId,
2392
                        'field_id' => $field['field_id'],
2393
                        'data' => (int) $field['encrypted_data'] === 1 ?
2394
                            $cryptedStuff['encrypted'] : $field['data'],
2395
                        'data_iv' => '',
2396
                        'encryption_type' => (int) $field['encrypted_data'] === 1 ?
2397
                            TP_ENCRYPTION_NAME : 'not_set',
2398
                    )
2399
                );
2400
                $newFieldId = DB::insertId();
2401
2402
                // Create sharekeys for current user
2403
                if ((int) $field['encrypted_data'] === 1) {
2404
                    // Create sharekeys for user
2405
                    storeUsersShareKey(
2406
                        prefixTable('sharekeys_fields'),
2407
                        (int) $dataDestination['personal_folder'],
2408
                        (int) $newFieldId,
2409
                        $cryptedStuff['objectKey'],
2410
                        true,
2411
                        false,
2412
                    );
2413
2414
                    // Build list of fields
2415
                    array_push(
2416
                        $itemDataArray['fields'],
2417
                        array(
2418
                            'object_id' => $newFieldId,
2419
                            'object_key' => $cryptedStuff['objectKey'],
2420
                        )
2421
                    );
2422
                }
2423
            }
2424
            // <---
2425
2426
            // ------------------
2427
            // Manage attachments
2428
2429
            // get file key
2430
            $rows = DB::query(
2431
                'SELECT f.id AS id, f.file AS file, f.name AS name, f.status AS status, f.extension AS extension,
2432
                f.size AS size, f.type AS type, s.share_key AS share_key
2433
                FROM ' . prefixTable('files') . ' AS f
2434
                INNER JOIN ' . prefixTable('sharekeys_files') . ' AS s ON (f.id = s.object_id)
2435
                WHERE s.user_id = %i AND f.id_item = %i',
2436
                $session->get('user-id'),
2437
                $inputData['itemId']
2438
            );
2439
            foreach ($rows as $record) {
2440
                // Check if file still exists
2441
                if (file_exists($SETTINGS['path_to_upload_folder'] . DIRECTORY_SEPARATOR . TP_FILE_PREFIX . base64_decode($record['file'])) === true) {
2442
                    // Step1 - decrypt the file
2443
                    // deepcode ignore PT: path is sanitized inside decryptFile()
2444
                    $fileContent = decryptFile(
2445
                        $record['file'],
2446
                        $SETTINGS['path_to_upload_folder'],
2447
                        decryptUserObjectKey($record['share_key'] ?? '', $session->get('user-private_key'))
2448
                    );
2449
2450
                    // Step2 - create file
2451
                    // deepcode ignore InsecureHash: md5 is used jonly for file name in order to get a hashed value in database
2452
                    $newFileName = md5(time() . '_' . $record['id']) . '.' . $record['extension'];
2453
                    $outstream = fopen($SETTINGS['path_to_upload_folder'] . DIRECTORY_SEPARATOR . $newFileName, 'ab');
2454
                    if ($outstream === false) {
2455
                        echo prepareExchangedData(
2456
                            array(
2457
                                'error' => true,
2458
                                'message' => $lang->get('error_cannot_open_file'),
2459
                            ),
2460
                            'encode'
2461
                        );
2462
                        break;
2463
                    }
2464
                    fwrite(
2465
                        $outstream,
2466
                        base64_decode($fileContent)
2467
                    );
2468
2469
                    // Step3 - encrypt the file
2470
                    $newFile = encryptFile($newFileName, $SETTINGS['path_to_upload_folder']);
2471
2472
                    // Step4 - store in database
2473
                    DB::insert(
2474
                        prefixTable('files'),
2475
                        array(
2476
                            'id_item' => $newItemId,
2477
                            'name' => $record['name'],
2478
                            'size' => $record['size'],
2479
                            'extension' => $record['extension'],
2480
                            'type' => $record['type'],
2481
                            'file' => $newFile['fileHash'],
2482
                            'status' => TP_ENCRYPTION_NAME,
2483
                            'confirmed' => 1,
2484
                        )
2485
                    );
2486
                    $newFileId = DB::insertId();
2487
2488
                    // Step5 - create sharekeys
2489
                    // Build list of fields
2490
                    array_push(
2491
                        $itemDataArray['files'],
2492
                        array(
2493
                            'object_id' => $newFileId,
2494
                            'object_key' => $newFile['objectKey'],
2495
                        )
2496
                    );
2497
2498
                    storeUsersShareKey(
2499
                        prefixTable('sharekeys_files'),
2500
                        (int) $dataDestination['personal_folder'],
2501
                        (int) $newFileId,
2502
                        $newFile['objectKey'],
2503
                        true
2504
                    );
2505
                }
2506
            }
2507
            // <---
2508
2509
            // Create new task for the new item
2510
            // If it is not a personnal one
2511
            if ((int) $dataDestination['personal_folder'] !== 1) {
2512
                storeTask(
2513
                    'item_copy',
2514
                    $session->get('user-id'),
2515
                    0,
2516
                    (int) $post_dest_id,
2517
                    (int) $newItemId,
2518
                    $itemDataArray['pwd'],
2519
                    $itemDataArray['fields'],
2520
                    $itemDataArray['files'],
2521
                );
2522
            }
2523
2524
            // -------------------------
2525
            // Add specific restrictions
2526
            $rows = DB::query('SELECT * FROM ' . prefixTable('restriction_to_roles') . ' WHERE item_id = %i', $inputData['itemId']);
2527
            foreach ($rows as $record) {
2528
                DB::insert(
2529
                    prefixTable('restriction_to_roles'),
2530
                    array(
2531
                        'item_id' => $newItemId,
2532
                        'role_id' => $record['role_id'],
2533
                    )
2534
                );
2535
            }
2536
2537
            // Add Tags
2538
            $rows = DB::query('SELECT * FROM ' . prefixTable('tags') . ' WHERE item_id = %i', $inputData['itemId']);
2539
            foreach ($rows as $record) {
2540
                DB::insert(
2541
                    prefixTable('tags'),
2542
                    array(
2543
                        'item_id' => $newItemId,
2544
                        'tag' => $record['tag'],
2545
                    )
2546
                );
2547
            }
2548
2549
            // Add this duplicate in logs
2550
            logItems(
2551
                $SETTINGS,
2552
                (int) $newItemId,
2553
                $originalRecord['label'],
2554
                $session->get('user-id'),
2555
                'at_creation',
2556
                $session->get('user-login')
2557
            );
2558
            // Add the fact that item has been copied in logs
2559
            logItems(
2560
                $SETTINGS,
2561
                (int) $newItemId,
2562
                $originalRecord['label'],
2563
                $session->get('user-id'),
2564
                'at_copy',
2565
                $session->get('user-login')
2566
            );
2567
2568
            echo (string) prepareExchangedData(
2569
                array(
2570
                    'error' => false,
2571
                    'message' => '',
2572
                    'new_id' => $newItemId
2573
                ),
2574
                'encode'
2575
            );
2576
2577
            // Add new item to cache table.
2578
            updateCacheTable('add_value', (int) $newItemId);
2579
        } else {
2580
            // no item
2581
            echo (string) prepareExchangedData(
2582
                array(
2583
                    'error' => true,
2584
                    'message' => $lang->get('error_missing_id'),
2585
                ),
2586
                'encode'
2587
            );
2588
        }
2589
        break;
2590
2591
    /*
2592
     * CASE
2593
     * Display informations of selected item
2594
     */
2595
    case 'show_details_item':
2596
        // Check KEY and rights
2597
        if ($inputData['key'] !== $session->get('key')) {
2598
            echo (string) prepareExchangedData(
2599
                array(
2600
                    'error' => true,
2601
                    'message' => $lang->get('key_is_not_correct'),
2602
                ),
2603
                'encode'
2604
            );
2605
            break;
2606
        }
2607
2608
        // Step #1
2609
        $session->set('system-show_step2', false);
2610
2611
        // Decrypt and retreive data in JSON format
2612
        $dataReceived = prepareExchangedData(
2613
            $inputData['data'],
2614
            'decode'
2615
        );
2616
2617
        // Init post variables
2618
        $inputData['id'] = filter_var(($dataReceived['id']), FILTER_SANITIZE_NUMBER_INT);
2619
        $inputData['folderId'] = filter_var(($dataReceived['folder_id']), FILTER_SANITIZE_NUMBER_INT);
2620
        $post_expired_item = filter_var(($dataReceived['expired_item']), FILTER_SANITIZE_NUMBER_INT);
2621
        $post_restricted = filter_var(($dataReceived['restricted']), FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2622
        $post_folder_access_level = isset($dataReceived['folder_access_level']) === true ?
2623
            filter_var(($dataReceived['folder_access_level']), FILTER_SANITIZE_FULL_SPECIAL_CHARS)
2624
            : '';
2625
        $post_item_rights = filter_var($dataReceived['rights'], FILTER_SANITIZE_NUMBER_INT);
2626
2627
        $pwIsEmptyNormal = false;
2628
        $arrData = array();
2629
        // return ID
2630
        $arrData['id'] = (int) $inputData['id'];
2631
        $arrData['id_user'] = API_USER_ID;
2632
        $arrData['author'] = 'API';
2633
2634
        // Check if item is deleted
2635
        // taking into account that item can be restored.
2636
        // so if restoration timestamp is higher than the deletion one
2637
        // then we can show it
2638
        $item_deleted = DB::queryFirstRow(
2639
            'SELECT *
2640
            FROM ' . prefixTable('log_items') . '
2641
            WHERE id_item = %i AND action = %s
2642
            ORDER BY date DESC
2643
            LIMIT 0, 1',
2644
            $inputData['id'],
2645
            'at_delete'
2646
        );
2647
        $dataDeleted = DB::count();
2648
2649
        $item_restored = DB::queryFirstRow(
2650
            'SELECT *
2651
            FROM ' . prefixTable('log_items') . '
2652
            WHERE id_item = %i AND action = %s
2653
            ORDER BY date DESC
2654
            LIMIT 0, 1',
2655
            $inputData['id'],
2656
            'at_restored'
2657
        );
2658
2659
        if ($dataDeleted !== 0 && intval($item_deleted['date']) > intval($item_restored['date'])) {
2660
            // This item is deleted => exit
2661
            echo (string) prepareExchangedData(
2662
                array(
2663
                    'error' => true,
2664
                    'message' => $lang->get('not_allowed_to_see_pw'),
2665
                    'show_detail_option' => 2,
2666
                ),
2667
                'encode'
2668
            );
2669
            break;
2670
        }
2671
2672
        // Get all informations for this item
2673
        $dataItem = DB::queryFirstRow(
2674
            'SELECT *
2675
            FROM ' . prefixTable('items') . ' as i
2676
            INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
2677
            WHERE i.id = %i AND l.action = %s',
2678
            $inputData['id'],
2679
            'at_creation'
2680
        );
2681
2682
        // Notification
2683
        DB::queryFirstRow(
2684
            'SELECT *
2685
            FROM ' . prefixTable('notification') . '
2686
            WHERE item_id = %i AND user_id = %i',
2687
            $inputData['id'],
2688
            $session->get('user-id')
2689
        );
2690
        if (DB::count() > 0) {
2691
            $arrData['notification_status'] = true;
2692
        } else {
2693
            $arrData['notification_status'] = false;
2694
        }
2695
2696
        // Get all USERS infos
2697
        $listeRestriction = is_null($dataItem['restricted_to']) === false ? array_filter(explode(';', $dataItem['restricted_to'])) : [];
2698
        $session->set('system-emails_list_for_notif', '');
2699
2700
        $user_in_restricted_list_of_item = in_array($session->get('user-id'), $listeRestriction) === true ? true : false;
2701
2702
        // manage case of API user
2703
        if ($dataItem['id_user'] === API_USER_ID) {
2704
            $arrData['author'] = 'API [' . $dataItem['description'] . ']';
2705
            $arrData['id_user'] = API_USER_ID;
2706
            $arrData['author_email'] = '';
2707
            $arrData['notification_status'] = false;
2708
        }
2709
2710
        // Get all tags for this item
2711
        $tags = array();
2712
        $rows = DB::query(
2713
            'SELECT tag
2714
            FROM ' . prefixTable('tags') . '
2715
            WHERE item_id = %i',
2716
            $inputData['id']
2717
        );
2718
        foreach ($rows as $record) {
2719
            array_push($tags, $record['tag']);
2720
        }
2721
2722
        // TODO -> improve this check
2723
        // check that actual user can access this item
2724
        $restrictionActive = true;
2725
        $restrictedTo = is_null($dataItem['restricted_to']) === false ? array_filter(explode(';', $dataItem['restricted_to'])) : [];
2726
        if (
2727
            in_array($session->get('user-id'), $restrictedTo) === true
2728
            || ((int) $session->get('user-manager') === 1 && (int) $SETTINGS['manager_edit'] === 1)
2729
        ) {
2730
            $restrictionActive = false;
2731
        }
2732
        if (empty($dataItem['restricted_to']) === true) {
2733
            $restrictionActive = false;
2734
        }
2735
2736
        // Check if user has a role that is accepted
2737
        $rows_tmp = DB::query(
2738
            'SELECT role_id
2739
            FROM ' . prefixTable('restriction_to_roles') . '
2740
            WHERE item_id=%i',
2741
            $inputData['id']
2742
        );
2743
        foreach ($rows_tmp as $rec_tmp) {
2744
            if (in_array($rec_tmp['role_id'], explode(';', $session->get('user-roles')))) {
2745
                $restrictionActive = false;
2746
            }
2747
        }
2748
2749
        // Uncrypt PW
2750
        // Get the object key for the user
2751
        $userKey = DB::queryFirstRow(
2752
            'SELECT share_key
2753
            FROM ' . prefixTable('sharekeys_items') . '
2754
            WHERE user_id = %i AND object_id = %i',
2755
            $session->get('user-id'),
2756
            $inputData['id']
2757
        );
2758
        if (DB::count() === 0 || empty($dataItem['pw']) === true) {
2759
            // No share key found
2760
            $pwIsEmptyNormally = false;
2761
            // Is this a personal and defuse password?
2762
            if ((int) $dataItem['perso'] === 1 && substr($dataItem['pw'], 0, 3) === 'def') {
2763
                // Yes, then ask for decryption with old personal salt key
2764
                echo (string) prepareExchangedData(
2765
                    array(
2766
                        'error' => true,
2767
                        'message' => $lang->get('error'),
2768
                        'show_detail_option' => 2,
2769
                        'error_type' => 'private_items_to_encrypt',
2770
                    ),
2771
                    'encode'
2772
                );
2773
                break;
2774
            } else {
2775
                $pw = '';
2776
            }
2777
        } else {
2778
            $pwIsEmptyNormal = true;
2779
            $decryptedObject = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
2780
            // if null then we have an error.
2781
            // suspecting bad password
2782
            if (empty($decryptedObject) === false) {
2783
                $pw = doDataDecryption(
2784
                    $dataItem['pw'],
2785
                    $decryptedObject
2786
                );
2787
                $arrData['pwd_encryption_error'] = false;
2788
                $arrData['pwd_encryption_error_message'] = '';
2789
            } else {
2790
                $pw = '';
2791
                $arrData['pwd_encryption_error'] = 'inconsistent_password';
2792
                $arrData['pwd_encryption_error_message'] = $lang->get('error_new_ldap_password_detected');
2793
            }
2794
        }
2795
2796
        // check user is admin
2797
        $session__list_restricted_folders_for_items = $session->get('system-list_restricted_folders_for_items') ?? [];
2798
        if (
2799
            (int) $session->get('user-admin') === 1
2800
            && (int) $dataItem['perso'] !== 1
2801
        ) {
2802
            $arrData['show_details'] = 0;
2803
            // ---
2804
            // ---
2805
        } elseif ((
2806
                (in_array($dataItem['id_tree'], $session->get('user-accessible_folders')) === true || (int) $session->get('user-admin') === 1)
2807
                && ((int) $dataItem['perso'] === 0 || ((int) $dataItem['perso'] === 1 && in_array($dataItem['id_tree'], $session->get('user-personal_folders')) === true))
2808
                && $restrictionActive === false)
2809
            || (isset($SETTINGS['anyone_can_modify']) && (int) $SETTINGS['anyone_can_modify'] === 1
2810
                && (int) $dataItem['anyone_can_modify'] === 1
2811
                && (in_array($dataItem['id_tree'], $session->get('user-accessible_folders')) || (int) $session->get('user-admin') === 1)
2812
                && $restrictionActive === false)
2813
            || (null !== $inputData['folderId']
2814
                && isset($session__list_restricted_folders_for_items[$inputData['folderId']])
2815
                && in_array($inputData['id'], $session__list_restricted_folders_for_items[$inputData['folderId']])
2816
                && (int) $post_restricted === 1
2817
                && $user_in_restricted_list_of_item === true)
2818
            || (isset($SETTINGS['restricted_to_roles']) && (int) $SETTINGS['restricted_to_roles'] === 1
2819
                && $restrictionActive === false)
2820
        ) {
2821
            // Check if actual USER can see this ITEM
2822
            // Allow show details
2823
            $arrData['show_details'] = 1;
2824
2825
            // Display menu icon for deleting if user is allowed
2826
            if (
2827
                (int) $dataItem['id_user'] === (int) $session->get('user-id')
2828
                || (int) $session->get('user-admin') === 1
2829
                || ((int) $session->get('user-manager') === 1 && (int) $SETTINGS['manager_edit'] === 1)
2830
                || (int) $dataItem['anyone_can_modify'] === 1
2831
                || in_array($dataItem['id_tree'], $session->get('system-list_folders_editable_by_role')) === true
2832
                || in_array($session->get('user-id'), $restrictedTo) === true
2833
                //|| count($restrictedTo) === 0
2834
                || (int) $post_folder_access_level === 30
2835
                || (int) $post_item_rights >= 40
2836
            ) {
2837
                $arrData['user_can_modify'] = 1;
2838
                $user_is_allowed_to_modify = true;
2839
            } else {
2840
                $arrData['user_can_modify'] = 0;
2841
                $user_is_allowed_to_modify = false;
2842
            }
2843
2844
            // Get restriction list for roles
2845
            $listRestrictionRoles = array();
2846
            if (isset($SETTINGS['restricted_to_roles']) && (int) $SETTINGS['restricted_to_roles'] === 1) {
2847
                // Add restriction if item is restricted to roles
2848
                $rows = DB::query(
2849
                    'SELECT t.title, t.id
2850
                    FROM ' . prefixTable('roles_title') . ' AS t
2851
                    INNER JOIN ' . prefixTable('restriction_to_roles') . ' AS r ON (t.id=r.role_id)
2852
                    WHERE r.item_id = %i
2853
                    ORDER BY t.title ASC',
2854
                    $inputData['id']
2855
                );
2856
                foreach ($rows as $record) {
2857
                    if (!in_array($record['title'], $listRestrictionRoles)) {
2858
                        array_push($listRestrictionRoles, $record['id']);
2859
                    }
2860
                }
2861
            }
2862
            // Check if any KB is linked to this item
2863
            if (isset($SETTINGS['enable_kb']) && (int) $SETTINGS['enable_kb'] === 1) {
2864
                $tmp = array();
2865
                $rows = DB::query(
2866
                    'SELECT k.label, k.id
2867
                    FROM ' . prefixTable('kb_items') . ' as i
2868
                    INNER JOIN ' . prefixTable('kb') . ' as k ON (i.kb_id=k.id)
2869
                    WHERE i.item_id = %i
2870
                    ORDER BY k.label ASC',
2871
                    $inputData['id']
2872
                );
2873
                foreach ($rows as $record) {
2874
                    array_push(
2875
                        $tmp,
2876
                        array(
2877
                            'id' => $record['id'],
2878
                            'label' => $record['label'],
2879
                        )
2880
                    );
2881
                }
2882
                $arrData['links_to_kbs'] = $tmp;
2883
            }
2884
            // Prepare DIalogBox data
2885
            if ((int) $post_expired_item === 0) {
2886
                $arrData['show_detail_option'] = 0;
2887
            } elseif ($user_is_allowed_to_modify === true && (int) $post_expired_item === 1) {
2888
                $arrData['show_detail_option'] = 1;
2889
            } else {
2890
                $arrData['show_detail_option'] = 2;
2891
            }
2892
2893
            $arrData['label'] = $dataItem['label'] === '' ? '' : $dataItem['label'];
2894
            $arrData['pw_length'] = strlen($pw);
2895
            $arrData['pw_decrypt_info'] = empty($pw) === true && $pwIsEmptyNormal === false ? 'error_no_sharekey_yet' : '';
2896
            $arrData['email'] = empty($dataItem['email']) === true || $dataItem['email'] === null ? '' : $dataItem['email'];
2897
            $arrData['url'] = empty($dataItem['url']) === true ? '' : $dataItem['url'];
2898
            $arrData['folder'] = $dataItem['id_tree'];
2899
            $arrData['description'] = $dataItem['description'];
2900
            $arrData['login'] = $dataItem['login'];
2901
            $arrData['id_restricted_to'] = $listeRestriction;
2902
            $arrData['id_restricted_to_roles'] = $listRestrictionRoles;
2903
            $arrData['tags'] = $tags;
2904
            $arrData['folder'] = (int) $dataItem['id_tree'];
2905
            $arrData['fa_icon'] = $dataItem['fa_icon'];
2906
            $arrData['item_key'] = $dataItem['item_key'];
2907
2908
            if (
2909
                isset($SETTINGS['enable_server_password_change'])
2910
                && (int) $SETTINGS['enable_server_password_change'] === 1
2911
            ) {
2912
                $arrData['auto_update_pwd_frequency'] = $dataItem['auto_update_pwd_frequency'];
2913
            } else {
2914
                $arrData['auto_update_pwd_frequency'] = '0';
2915
            }
2916
2917
            $arrData['anyone_can_modify'] = (int) $dataItem['anyone_can_modify'];
2918
2919
            // Add the fact that item has been viewed in logs
2920
            if (isset($SETTINGS['log_accessed']) && (int) $SETTINGS['log_accessed'] === 1) {
2921
                logItems(
2922
                    $SETTINGS,
2923
                    (int) $inputData['id'],
2924
                    $dataItem['label'],
2925
                    (int) $session->get('user-id'),
2926
                    'at_shown',
2927
                    $session->get('user-login')
2928
                );
2929
            }
2930
2931
            // statistics
2932
            DB::update(
2933
                prefixTable('items'),
2934
                array(
2935
                    'viewed_no' => $dataItem['viewed_no'] + 1,
2936
                    'updated_at' => time(),
2937
                ),
2938
                'id = %i',
2939
                $inputData['id']
2940
            );
2941
            $arrData['viewed_no'] = $dataItem['viewed_no'] + 1;
2942
2943
            // get fields
2944
            $fieldsTmp = array();
2945
            $arrCatList = $template_id = '';
2946
            if (isset($SETTINGS['item_extra_fields']) && (int) $SETTINGS['item_extra_fields'] === 1) {
2947
                // get list of associated Categories
2948
                $arrCatList = array();
2949
                $rows_tmp = DB::query(
2950
                    'SELECT id_category
2951
                    FROM ' . prefixTable('categories_folders') . '
2952
                    WHERE id_folder=%i',
2953
                    $inputData['folderId']
2954
                );
2955
                
2956
                if (DB::count() > 0) {
2957
                    foreach ($rows_tmp as $row) {
2958
                        array_push($arrCatList, (int) $row['id_category']);
2959
                    }
2960
2961
                    // get fields for this Item
2962
                    $rows_tmp = DB::query(
2963
                        'SELECT i.id AS id, i.field_id AS field_id, i.data AS data, i.item_id AS item_id,
2964
                        i.encryption_type AS encryption_type, c.encrypted_data AS encrypted_data, c.parent_id AS parent_id,
2965
                        c.type as field_type, c.masked AS field_masked, c.role_visibility AS role_visibility
2966
                        FROM ' . prefixTable('categories_items') . ' AS i
2967
                        INNER JOIN ' . prefixTable('categories') . ' AS c ON (i.field_id=c.id)
2968
                        WHERE i.item_id=%i AND c.parent_id IN %ls',
2969
                        $inputData['id'],
2970
                        $arrCatList
2971
                    );
2972
                    foreach ($rows_tmp as $row) {
2973
                        // Uncrypt data
2974
                        // Get the object key for the user
2975
                        //db::debugmode(true);
2976
                        $userKey = DB::queryFirstRow(
2977
                            'SELECT share_key
2978
                            FROM ' . prefixTable('sharekeys_fields') . '
2979
                            WHERE user_id = %i AND object_id = %i',
2980
                            $session->get('user-id'),
2981
                            $row['id']
2982
                        );
2983
                        //db::debugmode(false);
2984
                        $fieldText = [];
2985
                        if (DB::count() === 0 && (int) $row['encrypted_data'] === 1) {
2986
                            // Data should be encrypted but no key yet
2987
                            // System is currently creating the keys
2988
                            $fieldText = [
2989
                                'string' => '',
2990
                                'encrypted' => false,
2991
                                'error' => 'error_no_sharekey_yet',
2992
                            ];
2993
                        } else if (DB::count() === 0 && (int) $row['encrypted_data'] === 0) {
2994
                            // Data is not encrypted in DB
2995
                            $fieldText = [
2996
                                'string' => $row['data'],//#3945 - isBase64($row['data']) === true ? base64_decode($row['data']) : 
2997
                                'encrypted' => false,
2998
                                'error' => false,
2999
                            ];
3000
                        } else {
3001
                            // Data is encrypted in DB and we have a key
3002
                            $fieldText = [
3003
                                'string' => doDataDecryption(
3004
                                    $row['data'],
3005
                                    decryptUserObjectKey(
3006
                                        $userKey['share_key'],
3007
                                        $session->get('user-private_key')
3008
                                    )
3009
                                ),
3010
                                'encrypted' => true,
3011
                                'error' => '',
3012
                            ];
3013
                        }
3014
3015
                        // Manage textarea string
3016
                        /*if ($row['field_type'] === 'textarea') {
3017
                            $fieldText = $fieldText;
3018
                        }*/
3019
3020
                        // build returned list of Fields text
3021
                        array_push(
3022
                            $fieldsTmp,
3023
                            array(
3024
                                'id' => (int) $row['field_id'],
3025
                                'value' => $fieldText['string'],
3026
                                'encrypted' => (int) $fieldText['encrypted'],
3027
                                'parent_id' => (int) $row['parent_id'],
3028
                                'type' => $row['field_type'],
3029
                                'masked' => (int) $row['field_masked'],
3030
                                'error' => (string) $fieldText['error'],
3031
                            )
3032
                        );
3033
                    }
3034
                }
3035
            }
3036
3037
            // Now get the selected template (if exists)
3038
            if (isset($SETTINGS['item_creation_templates']) && (int) $SETTINGS['item_creation_templates'] === 1) {
3039
                $rows_tmp = DB::queryFirstRow(
3040
                    'SELECT category_id
3041
                    FROM ' . prefixTable('templates') . '
3042
                    WHERE item_id = %i',
3043
                    $inputData['id']
3044
                );
3045
                if (DB::count() > 0) {
3046
                    $template_id = $rows_tmp['category_id'];
3047
                }
3048
            }
3049
            //}
3050
            $arrData['fields'] = $fieldsTmp;
3051
            $arrData['categories'] = $arrCatList;
3052
            $arrData['template_id'] = (int) $template_id;
3053
            $arrData['to_be_deleted'] = '';
3054
3055
            // Evaluate if item is ready for all users
3056
            $rows_tmp = DB::queryFirstRow(
3057
                'SELECT finished_at
3058
                FROM ' . prefixTable('background_tasks') . '
3059
                WHERE item_id = %i',
3060
                $inputData['id']
3061
            );
3062
            $arrData['item_ready'] = DB::count() === 0 ? true : (DB::count() > 0 && empty($rows_tmp['finished_at']) === true ? false : true);
3063
3064
            // Manage user restriction
3065
            if (null !== $post_restricted) {
3066
                $arrData['restricted'] = $post_restricted;
3067
            } else {
3068
                $arrData['restricted'] = '';
3069
            }
3070
            // Decrement the number before being deleted
3071
            if (isset($SETTINGS['enable_delete_after_consultation']) && (int) $SETTINGS['enable_delete_after_consultation'] === 1) {
3072
                // Is the Item to be deleted?
3073
                $dataDelete = DB::queryFirstRow(
3074
                    'SELECT * 
3075
                    FROM ' . prefixTable('automatic_del') . '
3076
                    WHERE item_id = %i',
3077
                    $inputData['id']
3078
                );
3079
                if (DB::count() > 0) {
3080
                    $arrData['to_be_deleted'] = $dataDelete['del_value'];
3081
                    $arrData['to_be_deleted_type'] = (int) $dataDelete['del_type'];
3082
                }
3083
3084
                // Now delete if required
3085
                if ($dataDelete !== null && ((int) $dataDelete['del_enabled'] === 1
3086
                    || intval($arrData['id_user']) !== intval($session->get('user-id'))))
3087
                {
3088
                    if ((int) $dataDelete['del_type'] === 1 && $dataDelete['del_value'] >= 1) {
3089
                        // decrease counter
3090
                        DB::update(
3091
                            prefixTable('automatic_del'),
3092
                            array(
3093
                                'del_value' => $dataDelete['del_value'] - 1,
3094
                            ),
3095
                            'item_id = %i',
3096
                            $inputData['id']
3097
                        );
3098
                        // store value
3099
                        $arrData['to_be_deleted'] = $dataDelete['del_value'] - 1;
3100
                    } elseif (
3101
                        (int) $dataDelete['del_type'] === 1
3102
                        && $dataDelete['del_value'] <= 1
3103
                        || (int) $dataDelete['del_type'] === 2
3104
                        && $dataDelete['del_value'] < time()
3105
                    ) {
3106
                        $arrData['show_details'] = 0;
3107
                        // delete item
3108
                        DB::delete(prefixTable('automatic_del'), 'item_id = %i', $inputData['id']);
3109
                        // make inactive object
3110
                        DB::update(
3111
                            prefixTable('items'),
3112
                            array(
3113
                                'inactif' => 1,
3114
                                'deleted_at' => time(),
3115
                            ),
3116
                            'id = %i',
3117
                            $inputData['id']
3118
                        );
3119
3120
                        // log
3121
                        logItems(
3122
                            $SETTINGS,
3123
                            (int) $inputData['id'],
3124
                            $dataItem['label'],
3125
                            (int) $session->get('user-id'),
3126
                            'at_delete',
3127
                            $session->get('user-login'),
3128
                            'at_automatically_deleted'
3129
                        );
3130
3131
                        // Update cache table
3132
                        updateCacheTable('delete_value', (int) $inputData['id']);
3133
3134
                        $arrData['show_detail_option'] = 1;
3135
                        $arrData['to_be_deleted'] = 0;
3136
                    } elseif ($dataDelete['del_type'] === '2') {
3137
                        $arrData['to_be_deleted'] = date($SETTINGS['date_format'], (int) $dataDelete['del_value']);
3138
                    }
3139
                } else {
3140
                    $arrData['to_be_deleted'] = '';
3141
                }
3142
            } else {
3143
                $arrData['to_be_deleted'] = $lang->get('no');
3144
            }
3145
            // ---
3146
            // ---
3147
        } else {
3148
            $arrData['show_details'] = 0;
3149
            // get readable list of restriction
3150
            $listOfRestricted = '';
3151
            if (empty($dataItem['restricted_to']) === false) {
3152
                foreach (explode(';', $dataItem['restricted_to']) as $userRest) {
3153
                    if (empty($userRest) === false) {
3154
                        $dataTmp = DB::queryFirstRow(
3155
                            'SELECT login
3156
                            FROM ' . prefixTable('users') . '
3157
                            WHERE id= %i',
3158
                            $userRest
3159
                        );
3160
                        if (empty($listOfRestricted)) {
3161
                            $listOfRestricted = $dataTmp['login'];
3162
                        } else {
3163
                            $listOfRestricted .= ';' . $dataTmp['login'];
3164
                        }
3165
                    }
3166
                }
3167
            }
3168
            $arrData['restricted_to'] = $listOfRestricted;
3169
            $arrData['notification_list'] = '';
3170
            $arrData['notification_status'] = '';
3171
        }
3172
3173
        // Set a timestamp
3174
        $arrData['timestamp'] = time();
3175
3176
        // Set temporary session variable to allow step2
3177
        $session->set('system-show_step2', true);
3178
3179
        // Error
3180
        $arrData['error'] = '';
3181
3182
        // Encrypt data to return
3183
        echo (string) prepareExchangedData(
3184
            $arrData, 
3185
            'encode'
3186
        );
3187
        break;
3188
3189
    /*
3190
     * CASE
3191
     * Display History of the selected Item
3192
     */
3193
    case 'showDetailsStep2':
3194
        // Is this query expected (must be run after a step1 and not standalone)
3195
        if ($session->get('system-show_step2') !== true) {
3196
            // Check KEY and rights
3197
            if ($inputData['key'] !== $session->get('key')) {
3198
                echo (string) prepareExchangedData(
3199
                    array(
3200
                        'error' => true,
3201
                        'message' => $lang->get('key_is_not_correct'),
3202
                    ),
3203
                    'encode'
3204
                );
3205
                break;
3206
            }
3207
            if ($session->get('user-read_only') === 1) {
3208
                echo (string) prepareExchangedData(
3209
                    array(
3210
                        'error' => true,
3211
                        'message' => $lang->get('error_not_allowed_to'),
3212
                    ),
3213
                    'encode'
3214
                );
3215
                break;
3216
            }
3217
        }
3218
3219
        // prepare return array
3220
        $returnArray = [
3221
            'show_details' => 0,
3222
            'attachments' => [],
3223
            'favourite' => 0,
3224
            'otp_for_item_enabled' => 0,
3225
            'otp_phone_number' => '',
3226
            'otp_secret' => '',
3227
            'users_list' => [],
3228
            'roles_list' => [],
3229
            'has_change_proposal' => 0,
3230
            'setting_restricted_to_roles' => 0,
3231
            'otv_links' => 0,
3232
        ];
3233
3234
        // Load item data
3235
        $dataItem = DB::queryFirstRow(
3236
            'SELECT i.*, n.title AS folder_title, o.enabled AS otp_for_item_enabled, o.phone_number AS otp_phone_number, o.secret AS otp_secret
3237
            FROM ' . prefixTable('items') . ' AS i
3238
            INNER JOIN ' . prefixTable('nested_tree') . ' AS n ON (i.id_tree = n.id)
3239
            LEFT JOIN ' . prefixTable('items_otp') . ' AS o ON (o.item_id = i.id)
3240
            WHERE i.id = %i',
3241
            $inputData['id']
3242
        );
3243
3244
        // check that actual user can access this item
3245
        $restrictionActive = true;
3246
        $restrictedTo = is_null($dataItem['restricted_to']) === false ? array_filter(explode(';', $dataItem['restricted_to'])) : [];
3247
        if (
3248
            in_array($session->get('user-id'), $restrictedTo)
3249
            || (((int) $session->get('user-manager') === 1 || (int) $session->get('user-can_manage_all_users') === 1)
3250
                && (int) $SETTINGS['manager_edit'] === 1)
3251
        ) {
3252
            $restrictionActive = false;
3253
        }
3254
        if (empty($dataItem['restricted_to'])) {
3255
            $restrictionActive = false;
3256
        }
3257
3258
        // Check if user has a role that is accepted
3259
        $rows_tmp = DB::query(
3260
            'SELECT role_id
3261
            FROM ' . prefixTable('restriction_to_roles') . '
3262
            WHERE item_id=%i',
3263
            $inputData['id']
3264
        );
3265
        foreach ($rows_tmp as $rec_tmp) {
3266
            if (in_array($rec_tmp['role_id'], explode(';', $session->get('user-roles')))) {
3267
                $restrictionActive = false;
3268
            }
3269
        }
3270
3271
        // check user is admin
3272
        $session__list_restricted_folders_for_items = $session->get('system-list_restricted_folders_for_items') ?? [];
3273
        if (
3274
            (int) $session->get('user-admin') === 1
3275
            && (int) $dataItem['perso'] === 0
3276
        ) {
3277
            $returnArray['show_details'] = 0;
3278
            echo (string) prepareExchangedData(
3279
                $returnArray,
3280
                'encode'
3281
            );
3282
        // Get all expected data about this ITEM
3283
        } else {
3284
            // generate 2d key
3285
            $session->set('user-key_tmp', bin2hex(GenerateCryptKey(16, false, true, true, false, true)));
3286
3287
            // Prepare files listing
3288
            $attachments = [];
3289
            
3290
            // launch query
3291
            $rows = DB::query(
3292
                'SELECT id, name, file, extension, size
3293
                FROM ' . prefixTable('files') . '
3294
                WHERE id_item = %i AND confirmed = 1',
3295
                $inputData['id']
3296
            );
3297
            foreach ($rows as $record) {
3298
                $filename = basename($record['name'], '.' . $record['extension']);
3299
                $filename = isBase64($filename) === true ? base64_decode($filename) : $filename;
3300
3301
                array_push(
3302
                    $attachments,
3303
                    array(
3304
                        'icon' => fileFormatImage(strtolower($record['extension'])),
3305
                        'filename' => $filename,
3306
                        'extension' => $record['extension'],
3307
                        'size' => formatSizeUnits((int) $record['size']),
3308
                        'is_image' => in_array(strtolower($record['extension']), TP_IMAGE_FILE_EXT) === true ? 1 : 0,
3309
                        'id' => $record['id'],
3310
                        'key' => $session->get('user-key_tmp'),
3311
                        'internalFilename' => basename($record['name'], '.' . $record['extension']),
3312
                    )
3313
                );
3314
            }
3315
            $returnArray['attachments'] = $attachments;
3316
3317
            // disable add bookmark if alread bookmarked
3318
            $returnArray['favourite'] = in_array($inputData['id'], $session->get('user-favorites')) === true ? 1 : 0;
3319
            
3320
            // get OTP enabled for item
3321
            $returnArray['otp_for_item_enabled'] = (int) $dataItem['otp_for_item_enabled'];
3322
            $returnArray['otp_phone_number'] = (string) $dataItem['otp_phone_number'];
3323
            if (empty($dataItem['otp_secret']) === false) {
3324
                $secret = cryption(
3325
                    $dataItem['otp_secret'],
3326
                    '',
3327
                    'decrypt'
3328
                )['string'];
3329
            } else {
3330
                $secret = '';
3331
            }
3332
            $returnArray['otp_secret'] = (string) $secret;
3333
3334
            // Add this item to the latests list
3335
            if ($session->has('user-latest_items') && $session->has('user-latest_items') && null !== $session->get('user-latest_items') && isset($SETTINGS['max_latest_items']) && 
3336
                in_array($dataItem['id'], $session->get('user-latest_items')) === false
3337
            ) {
3338
                if (count($session->get('user-latest_items')) >= $SETTINGS['max_latest_items']) {
3339
                    // delete last items
3340
                    SessionManager::specificOpsOnSessionArray('user-latest_items', 'pop');
3341
                }
3342
                SessionManager::specificOpsOnSessionArray('user-latest_items', 'unshift', $dataItem['id']);
3343
                // update DB
3344
                DB::update(
3345
                    prefixTable('users'),
3346
                    array(
3347
                        'latest_items' => implode(';', $session->get('user-latest_items')),
3348
                    ),
3349
                    'id=' . $session->get('user-id')
3350
                );
3351
            }
3352
3353
            // get list of roles
3354
            $listOptionsForUsers = array();
3355
            $listOptionsForRoles = array();
3356
            $rows = DB::query(
3357
                'SELECT r.role_id AS role_id, t.title AS title
3358
                FROM ' . prefixTable('roles_values') . ' AS r
3359
                INNER JOIN ' . prefixTable('roles_title') . ' AS t ON (r.role_id = t.id)
3360
                WHERE r.folder_id = %i',
3361
                $dataItem['id_tree']
3362
            );
3363
            foreach ($rows as $record) {
3364
                array_push(
3365
                    $listOptionsForRoles,
3366
                    array(
3367
                        'id' => (int) $record['role_id'],
3368
                        'title' => $record['title'],
3369
                    )
3370
                );
3371
                $rows2 = DB::query(
3372
                    'SELECT id, login, fonction_id, email, name, lastname
3373
                    FROM ' . prefixTable('users') . '
3374
                    WHERE fonction_id LIKE %s',
3375
                    '%' . $record['role_id'] . '%'
3376
                );
3377
                foreach ($rows2 as $record2) {
3378
                    foreach (explode(';', $record2['fonction_id']) as $role) {
3379
                        if (
3380
                            array_search($record2['id'], array_column($listOptionsForUsers, 'id')) === false
3381
                            && $role === $record['role_id']
3382
                        ) {
3383
                            array_push(
3384
                                $listOptionsForUsers,
3385
                                array(
3386
                                    'id' => (int) $record2['id'],
3387
                                    'login' => $record2['login'],
3388
                                    'name' => $record2['name'] . ' ' . $record2['lastname'],
3389
                                    'email' => $record2['email'],
3390
                                )
3391
                            );
3392
                        }
3393
                    }
3394
                }
3395
            }
3396
3397
            $returnArray['users_list'] = $listOptionsForUsers;
3398
            $returnArray['roles_list'] = $listOptionsForRoles;
3399
3400
            // send notification if enabled
3401
            if (isset($SETTINGS['enable_email_notification_on_item_shown']) === true && (int) $SETTINGS['enable_email_notification_on_item_shown'] === 1) {
3402
                // Get path
3403
                $arbo = $tree->getPath($dataItem['id_tree'], true);
3404
                $path = '';
3405
                foreach ($arbo as $elem) {
3406
                    if (empty($path) === true) {
3407
                        $path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' ';
3408
                    } else {
3409
                        $path .= '&#8594; ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
3410
                    }
3411
                }
3412
                // Build text to show user
3413
                if (empty($path) === true) {
3414
                    $path = addslashes($dataItem['label']);
3415
                } else {
3416
                    $path = addslashes($dataItem['label']) . ' (' . $path . ')';
3417
                }
3418
3419
                // Add Admins to notification list if expected
3420
                $reveivers = [];
3421
                $rows = DB::query(
3422
                    'SELECT email
3423
                    FROM ' . prefixTable('users').'
3424
                    WHERE admin = %i',
3425
                    1
3426
                );
3427
                foreach ($rows as $user) {
3428
                    array_push($reveivers, $user['email']);
3429
                }
3430
3431
                // prepare sending email
3432
                prepareSendingEmail(
3433
                    $lang->get('email_on_open_notification_subject'),
3434
                    str_replace(
3435
                        array('#tp_user#', '#tp_item#', '#tp_link#'),
3436
                        array(
3437
                            addslashes($session->get('user-login')),
3438
                            $path,
3439
                            $SETTINGS['cpassman_url'] . '/index.php?page=items&group=' . $dataItem['id_tree'] . '&id=' . $dataItem['id'],
3440
                        ),
3441
                        $lang->get('email_on_open_notification_mail')
3442
                    ),
3443
                    implode(",", $reveivers),
3444
                    ""
3445
                );
3446
            }
3447
3448
            // has this item a change proposal
3449
            DB::query('SELECT * FROM ' . prefixTable('items_change') . ' WHERE item_id = %i', $inputData['id']);
3450
            $returnArray['has_change_proposal'] = DB::count();
3451
3452
            // Setting
3453
            $returnArray['setting_restricted_to_roles'] = isset($SETTINGS['restricted_to_roles']) === true
3454
                && (int) $SETTINGS['restricted_to_roles'] === 1 ? 1 : 0;
3455
3456
            // get OTV links
3457
            if (isset($SETTINGS['otv_is_enabled']) === true && (int) $SETTINGS['otv_is_enabled'] === 1) {
3458
                DB::query(
3459
                    'SELECT *
3460
                    FROM ' . prefixTable('otv') . '
3461
                    WHERE item_id = %i
3462
                    AND time_limit > %i',
3463
                    $inputData['id'],
3464
                    time()
3465
                );
3466
                $returnArray['otv_links'] = (int) DB::count();
3467
            }
3468
3469
            $session->set('system-show_step2', false);
3470
            
3471
            // deepcode ignore ServerLeak: Data is encrypted before being sent
3472
            echo (string) prepareExchangedData(
3473
                $returnArray,
3474
                'encode'
3475
            );
3476
        }
3477
        break;
3478
3479
    /*
3480
     * CASE
3481
     * Delete an item
3482
     */
3483
    case 'delete_item':
3484
        // Check KEY and rights
3485
        if ($inputData['key'] !== $session->get('key')) {
3486
            echo (string) prepareExchangedData(
3487
                array(
3488
                    'error' => true,
3489
                    'message' => $lang->get('key_is_not_correct'),
3490
                ),
3491
                'encode'
3492
            );
3493
            break;
3494
        }
3495
        if ($session->get('user-read_only') === 1) {
3496
            echo (string) prepareExchangedData(
3497
                array(
3498
                    'error' => true,
3499
                    'message' => $lang->get('error_not_allowed_to'),
3500
                ),
3501
                'encode'
3502
            );
3503
            break;
3504
        }
3505
3506
        // decrypt and retreive data in JSON format
3507
        $dataReceived = prepareExchangedData(
3508
            $inputData['data'],
3509
            'decode'
3510
        );
3511
        
3512
        // Prepare POST variables
3513
        $data = [
3514
            'itemId' => isset($dataReceived['item_id']) === true ? $dataReceived['item_id'] : '',
3515
            'folderId' => isset($dataReceived['folder_id']) === true ? $dataReceived['folder_id'] : '',
3516
            'accessLevel' => isset($dataReceived['access_level']) === true ? $dataReceived['access_level'] : '',
3517
            'itemKey' => isset($dataReceived['item_key']) === true ? $dataReceived['item_key'] : '',
3518
        ];
3519
        
3520
        $filters = [
3521
            'itemId' => 'cast:integer',
3522
            'folderId' => 'cast:integer',
3523
            'accessLevel' => 'cast:integer',
3524
            'itemKey' => 'trim|escape',
3525
        ];
3526
        
3527
        $inputData = dataSanitizer(
3528
            $data,
3529
            $filters
3530
        );
3531
        
3532
        if (empty($inputData['itemId']) === true && (empty($inputData['itemKey']) === true || is_null($inputData['itemKey']) === true)) {
3533
            echo (string) prepareExchangedData(
3534
                array(
3535
                    'error' => true,
3536
                    'message' => $lang->get('nothing_to_do'),
3537
                ),
3538
                'encode'
3539
            );
3540
            break;
3541
        }
3542
3543
        // Check that user can access this item
3544
        $granted = accessToItemIsGranted($inputData['itemId'], $SETTINGS);
3545
        if ($granted !== true) {
3546
            echo (string) prepareExchangedData(
3547
                array(
3548
                    'error' => true,
3549
                    'message' => $granted,
3550
                ),
3551
                'encode'
3552
            );
3553
            break;
3554
        }
3555
3556
        // Load item data
3557
        $data = DB::queryFirstRow(
3558
            'SELECT id_tree, id, label
3559
            FROM ' . prefixTable('items') . '
3560
            WHERE id = %i OR item_key = %s',
3561
            $inputData['itemId'],
3562
            $inputData['itemKey']
3563
        );
3564
        if (empty($inputData['itemId']) === true) {
3565
            $inputData['itemId'] = $data['id'];
3566
        }
3567
        $inputData['label'] = $data['label'];
3568
3569
        // Check that user can delete on this folder
3570
        $checkRights = getCurrentAccessRights(
3571
            $session->get('user-id'),
3572
            $inputData['itemId'],
3573
            (int) $data['id_tree'],
3574
        );
3575
3576
        if ($checkRights['error'] || !$checkRights['delete']) {
3577
            echo (string) prepareExchangedData(
3578
                array(
3579
                    'error' => true,
3580
                    'message' => $lang->get('error_not_allowed_to'),
3581
                ),
3582
                'encode'
3583
            );
3584
        }
3585
3586
        // delete item consists in disabling it
3587
        DB::update(
3588
            prefixTable('items'),
3589
            array(
3590
                'inactif' => '1',
3591
                'deleted_at' => time(),
3592
            ),
3593
            'id = %i OR item_key = %s',
3594
            $inputData['itemId'],
3595
            $inputData['itemKey']
3596
        );
3597
3598
        // log
3599
        logItems(
3600
            $SETTINGS,
3601
            (int) $inputData['itemId'],
3602
            $inputData['label'],
3603
            $session->get('user-id'),
3604
            'at_delete',
3605
            $session->get('user-login')
3606
        );
3607
        // Update CACHE table
3608
        updateCacheTable('delete_value', (int) $inputData['itemId']);
3609
3610
        echo (string) prepareExchangedData(
3611
            array(
3612
                'error' => false,
3613
                'message' => '',
3614
            ),
3615
            'encode'
3616
        );
3617
        break;
3618
3619
        
3620
    /*
3621
     * CASE
3622
     * Display OTP of the selected Item
3623
     */
3624
    case 'show_opt_code':
3625
        // Check KEY and rights
3626
        if ($inputData['key'] !== $session->get('key')) {
3627
            echo (string) prepareExchangedData(
3628
                array(
3629
                    'error' => true,
3630
                    'message' => $lang->get('key_is_not_correct'),
3631
                ),
3632
                'encode'
3633
            );
3634
            break;
3635
        }
3636
        if ($session->get('user-read_only') === 1) {
3637
            echo (string) prepareExchangedData(
3638
                array(
3639
                    'error' => true,
3640
                    'message' => $lang->get('error_not_allowed_to'),
3641
                ),
3642
                'encode'
3643
            );
3644
            break;
3645
        }
3646
3647
        // Load item data
3648
        $dataItem = DB::queryFirstRow(
3649
            'SELECT secret, enabled
3650
            FROM ' . prefixTable('items_otp') . '
3651
            WHERE item_id = %i',
3652
            $inputData['id']
3653
        );
3654
3655
        if (DB::count() > 0) {
3656
            // OTP exists then display it
3657
            $secret = cryption(
3658
                $dataItem['secret'],
3659
                '',
3660
                'decrypt'
3661
            )['string'];
3662
        }
3663
        
3664
        // Generate OTP code
3665
        if (empty($secret) === false) {
3666
            try {
3667
                $otp = TOTP::createFromSecret($secret);
3668
                $otpCode = $otp->now();
3669
                $otpExpiresIn = $otp->expiresIn();
3670
            } catch (RuntimeException $e) {
3671
                $error = true;
3672
                $otpCode = '';
3673
                $otpExpiresIn = '';
3674
                $message = $e->getMessage();
3675
            }
3676
        } else {
3677
            $otpCode = '';
3678
            $otpExpiresIn = '';
3679
        }
3680
        
3681
        // deepcode ignore ServerLeak: Data is encrypted before being sent
3682
        echo (string) prepareExchangedData(
3683
            array(
3684
                'error' => isset($error) === true ? $error : false,
3685
                'message' => isset($message) === true ? $message : '',
3686
                'otp_code' => $otpCode,
3687
                'otp_expires_in' => $otpExpiresIn,
3688
                'otp_enabled' => $dataItem['enabled'],
3689
            ),
3690
            'encode'
3691
        );
3692
        break;
3693
3694
    /*
3695
     * CASE
3696
     * Update a Group
3697
     */
3698
    case 'update_folder':
3699
        // Check KEY and rights
3700
        if ($inputData['key'] !== $session->get('key')) {
3701
            echo (string) prepareExchangedData(
3702
                array(
3703
                    'error' => true,
3704
                    'message' => $lang->get('key_is_not_correct'),
3705
                ),
3706
                'encode'
3707
            );
3708
            break;
3709
        }
3710
        if ($session->get('user-read_only') === 1) {
3711
            echo (string) prepareExchangedData(
3712
                array(
3713
                    'error' => true,
3714
                    'message' => $lang->get('error_not_allowed_to'),
3715
                ),
3716
                'encode'
3717
            );
3718
            break;
3719
        }
3720
        // decrypt and retreive data in JSON format
3721
        $dataReceived = prepareExchangedData(
3722
            $inputData['data'],
3723
            'decode'
3724
        );
3725
3726
        // Prepare variables
3727
        $title = filter_var(htmlspecialchars_decode($dataReceived['title'], ENT_QUOTES), FILTER_SANITIZE_FULL_SPECIAL_CHARS);
3728
        $inputData['folderId'] = filter_var(htmlspecialchars_decode($dataReceived['folder']), FILTER_SANITIZE_NUMBER_INT);
3729
3730
        // Check if user is allowed to access this folder
3731
        if (!in_array($inputData['folderId'], $session->get('user-accessible_folders'))) {
3732
            echo '[{"error" : "' . $lang->get('error_not_allowed_to') . '"}]';
3733
            break;
3734
        }
3735
3736
        // Check if title doesn't contains html codes
3737
        if (preg_match_all('|<[^>]+>(.*)</[^>]+>|U', $title, $out)) {
3738
            echo '[ { "error" : "' . $lang->get('error_html_codes') . '" } ]';
3739
            break;
3740
        }
3741
        // check that title is not numeric
3742
        if (is_numeric($title) === true) {
3743
            echo '[{"error" : "ERR_TITLE_ONLY_WITH_NUMBERS"}]';
3744
            break;
3745
        }
3746
3747
        // Check if duplicate folders name are allowed
3748
        if (isset($SETTINGS['duplicate_folder']) && $SETTINGS['duplicate_folder'] === '0') {
3749
            $data = DB::queryFirstRow('SELECT id, title FROM ' . prefixTable('nested_tree') . ' WHERE title = %s', $title);
3750
            if (empty($data['id']) === false && $dataReceived['folder'] !== $data['id']) {
3751
                echo '[ { "error" : "' . $lang->get('error_group_exist') . '" } ]';
3752
                break;
3753
            }
3754
        }
3755
3756
        // query on folder
3757
        $data = DB::queryFirstRow(
3758
            'SELECT parent_id, personal_folder
3759
            FROM ' . prefixTable('nested_tree') . '
3760
            WHERE id = %i',
3761
            $inputData['folderId']
3762
        );
3763
3764
        // check if complexity level is good
3765
        // if manager or admin don't care
3766
        if ($session->get('user-admin') !== 1 && $session->get('user-manager') !== 1 && $data['personal_folder'] === '0') {
3767
            $data = DB::queryFirstRow(
3768
                'SELECT valeur
3769
                FROM ' . prefixTable('misc') . '
3770
                WHERE intitule = %i AND type = %s',
3771
                $data['parent_id'],
3772
                'complex'
3773
            );
3774
            if (intval($dataReceived['complexity']) < intval($data['valeur'])) {
3775
                echo '[ { "error" : "' . $lang->get('error_folder_complexity_lower_than_top_folder') . ' [<b>' . TP_PW_COMPLEXITY[$data['valeur']][1] . '</b>]"} ]';
3776
                break;
3777
            }
3778
        }
3779
3780
        // update Folders table
3781
        $tmp = DB::queryFirstRow(
3782
            'SELECT title, parent_id, personal_folder FROM ' . prefixTable('nested_tree') . ' WHERE id = %i',
3783
            $dataReceived['folder']
3784
        );
3785
        if ($tmp['parent_id'] !== 0 || $tmp['title'] !== $session->get('user-id') || $tmp['personal_folder'] !== 1) {
3786
            DB::update(
3787
                prefixTable('nested_tree'),
3788
                array(
3789
                    'title' => $title,
3790
                ),
3791
                'id=%s',
3792
                $inputData['folderId']
3793
            );
3794
            // update complixity value
3795
            DB::update(
3796
                prefixTable('misc'),
3797
                array(
3798
                    'valeur' => $dataReceived['complexity'],
3799
                    'updated_at' => time(),
3800
                ),
3801
                'intitule = %s AND type = %s',
3802
                $inputData['folderId'],
3803
                'complex'
3804
            );
3805
            // rebuild fuild tree folder
3806
            $tree->rebuild();
3807
        }
3808
        // send data
3809
        echo '[{"error" : ""}]';
3810
        break;
3811
3812
    /*
3813
     * CASE
3814
     * List items of a group
3815
     */
3816
    case 'do_items_list_in_folder':
3817
        // Check KEY and rights
3818
        if ($inputData['key'] !== $session->get('key')) {
3819
            echo (string) prepareExchangedData(
3820
                array(
3821
                    'error' => true,
3822
                    'message' => $lang->get('error_not_allowed_to')." BOOH 1",
3823
                ),
3824
                'encode'
3825
            );
3826
            break;
3827
        }
3828
3829
        if (count($session->get('user-roles_array')) === 0) {
3830
            echo (string) prepareExchangedData(
3831
                array(
3832
                    'error' => true,
3833
                    'message' => $lang->get('error_not_allowed_to'),
3834
                ),
3835
                'encode'
3836
            );
3837
            break;
3838
        }
3839
3840
        // decrypt and retreive data in JSON format
3841
        $dataReceived = prepareExchangedData(
3842
            $inputData['data'],
3843
            'decode'
3844
        );
3845
3846
        if (is_array($dataReceived) === true && array_key_exists('id', $dataReceived) === false) {
3847
            echo (string) prepareExchangedData(
3848
                array(
3849
                    'error' => true,
3850
                    'message' => $lang->get('error_unknown'),
3851
                ),
3852
                'encode'
3853
            );
3854
            break;
3855
        }
3856
3857
        // Prepare POST variables
3858
        $inputData['id'] = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
3859
        $post_restricted = filter_var($dataReceived['restricted'], FILTER_SANITIZE_NUMBER_INT);
3860
        $post_start = filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT);
3861
        $post_nb_items_to_display_once = filter_var($dataReceived['nb_items_to_display_once'], FILTER_SANITIZE_NUMBER_INT);
3862
3863
        $arr_arbo = [];
3864
        $folderIsPf = in_array($inputData['id'], $session->get('user-personal_folders')) === true ? true : false;
3865
        $showError = 0;
3866
        $itemsIDList = $rights = $returnedData = $uniqueLoadData = $html_json = array();
3867
        // Build query limits
3868
        if (empty($post_start) === true) {
3869
            $start = 0;
3870
        } else {
3871
            $start = $post_start;
3872
        }
3873
3874
        // to do only on 1st iteration
3875
        if ((int) $start === 0) {
3876
            // Prepare tree
3877
            $arbo = $tree->getPath($inputData['id'], true);
3878
            foreach ($arbo as $elem) {
3879
                // Personnal folder
3880
                if ((int) $elem->title === (int) $session->get('user-id') && (int) $elem->nlevel === 1) {
3881
                    $elem->title = $session->get('user-login');
3882
                }
3883
                // Store path elements
3884
                array_push(
3885
                    $arr_arbo,
3886
                    array(
3887
                        'id' => $elem->id,
3888
                        'title' => htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES),
3889
                        'visible' => in_array($elem->id, $session->get('user-accessible_folders')) ? 1 : 0,
3890
                    )
3891
                );
3892
            }
3893
            $uniqueLoadData['path'] = $arr_arbo;
3894
3895
            // store last folder accessed in cookie
3896
            $arr_cookie_options = array (
3897
                'expires' => time() + TP_ONE_DAY_SECONDS * 5,
3898
                'path' => '/', 
3899
                'secure' => true,
3900
                'httponly' => true,
3901
                'samesite' => 'Lax' // None || Lax  || Strict
3902
            );
3903
            // deepcode ignore WebCookieSecureDisabledByDefault: defined in $arr_cookie_options, deepcode ignore WebCookieHttpOnlyDisabledByDefault: defined in $arr_cookie_options
3904
            setcookie('jstree_select', $inputData['id'], $arr_cookie_options);
3905
3906
            // CHeck if roles have 'allow_pw_change' set to true
3907
            $forceItemEditPrivilege = false;
3908
            foreach ($session->get('user-roles_array') as $role) {
3909
                $roleQ = DB::queryFirstRow(
3910
                    'SELECT allow_pw_change
3911
                    FROM ' . prefixTable('roles_title') . '
3912
                    WHERE id = %i',
3913
                    $role
3914
                );
3915
                if ((int) $roleQ['allow_pw_change'] === 1) {
3916
                    $forceItemEditPrivilege = true;
3917
                    break;
3918
                }
3919
            }
3920
3921
            // is this folder a personal one
3922
            $folder_is_personal = in_array($inputData['id'], $session->get('user-personal_folders'));
3923
            $uniqueLoadData['folder_is_personal'] = $folder_is_personal;
3924
3925
            $folder_is_in_personal = in_array(
3926
                $inputData['id'],
3927
                array_merge(
3928
                    $session->get('user-personal_visible_folders'),
3929
                    $session->get('user-personal_folders')
3930
                )
3931
            );
3932
            $uniqueLoadData['folder_is_in_personal'] = $folder_is_in_personal;
3933
3934
3935
            // check role access on this folder (get the most restrictive) (2.1.23)
3936
            if ((int) $folder_is_personal === 0) {
3937
                $accessLevel = 20;
3938
                $arrTmp = [];
3939
                
3940
                foreach ($session->get('user-roles_array') as $role) {
3941
                    $access = DB::queryFirstRow(
3942
                        'SELECT type FROM ' . prefixTable('roles_values') . ' WHERE role_id = %i AND folder_id = %i',
3943
                        $role,
3944
                        $inputData['id']
3945
                    );
3946
                    if (DB::count()>0) {
3947
                        if ($access['type'] === 'R') {
3948
                            array_push($arrTmp, 10);
3949
                        } elseif ($access['type'] === 'W') {
3950
                            array_push($arrTmp, 30);
3951
                        } elseif (
3952
                            $access['type'] === 'ND'
3953
                            || ($forceItemEditPrivilege === true && $access['type'] === 'NDNE')
3954
                        ) {
3955
                            array_push($arrTmp, 20);
3956
                        } elseif ($access['type'] === 'NE') {
3957
                            array_push($arrTmp, 10);
3958
                        } elseif ($access['type'] === 'NDNE') {
3959
                            array_push($arrTmp, 15);
3960
                        } else {
3961
                            // Ensure to give access Right if allowed folder
3962
                            if (in_array($inputData['id'], $session->get('user-accessible_folders')) === true) {
3963
                                array_push($arrTmp, 30);
3964
                            } else {
3965
                                array_push($arrTmp, 0);
3966
                            }
3967
                        }
3968
                    } else {
3969
                        // Ensure to give access Right if allowed folder
3970
                        if (in_array($inputData['id'], $session->get('user-accessible_folders')) === true) {
3971
                            array_push($arrTmp, 50);
3972
                        } else {
3973
                            array_push($arrTmp, 0);
3974
                        }
3975
                    }
3976
                }
3977
                // 3.0.0.0 - changed  MIN to MAX
3978
                $accessLevel = count($arrTmp) > 0 ? max($arrTmp) : $accessLevel;
3979
            } else {
3980
                $accessLevel = 30;
3981
            }
3982
            $uniqueLoadData['accessLevel'] = $accessLevel;
3983
            $uniqueLoadData['showError'] = $showError;
3984
3985
            // check if items exist
3986
            $where = new WhereClause('and');
3987
            $session__user_list_folders_limited = $session->get('user-list_folders_limited');
3988
            if (null !== $post_restricted && (int) $post_restricted === 1 && empty($session__user_list_folders_limited[$inputData['id']]) === false) {
3989
                $counter = count($session__user_list_folders_limited[$inputData['id']]);
3990
                $uniqueLoadData['counter'] = $counter;
3991
                // check if this folder is visible
3992
            } elseif (!in_array(
3993
                $inputData['id'],
3994
                array_merge(
3995
                    $session->get('user-accessible_folders'),
3996
                    array_keys($session->get('system-list_restricted_folders_for_items')),
3997
                    array_keys($session->get('user-list_folders_limited'))
3998
                )
3999
            )) {
4000
                echo (string) prepareExchangedData(
4001
                    array(
4002
                        'error' => 'not_authorized',
4003
                        'arborescence' => $arr_arbo,
4004
                    ),
4005
                    'encode'
4006
                );
4007
                break;
4008
            } else {
4009
                DB::query(
4010
                    'SELECT *
4011
                    FROM ' . prefixTable('items') . '
4012
                    WHERE inactif = %i',
4013
                    0
4014
                );
4015
                $counter = DB::count();
4016
                $uniqueLoadData['counter'] = $counter;
4017
            }
4018
4019
            // Get folder complexity
4020
            $folderComplexity = DB::queryFirstRow(
4021
                'SELECT valeur FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %i',
4022
                'complex',
4023
                $inputData['id']
4024
            );
4025
            $folderComplexity = $folderComplexity !== null ? (int) $folderComplexity['valeur'] : 0;
4026
            $uniqueLoadData['folderComplexity'] = $folderComplexity;
4027
4028
            // Has this folder some categories to be displayed?
4029
            $categoriesStructure = array();
4030
            if (isset($SETTINGS['item_extra_fields']) && (int) $SETTINGS['item_extra_fields'] === 1) {
4031
                $folderRow = DB::query(
4032
                    'SELECT id_category
4033
                    FROM ' . prefixTable('categories_folders') . '
4034
                    WHERE id_folder = %i',
4035
                    $inputData['id']
4036
                );
4037
                foreach ($folderRow as $category) {
4038
                    array_push(
4039
                        $categoriesStructure,
4040
                        $category['id_category']
4041
                    );
4042
                }
4043
            }
4044
            $uniqueLoadData['categoriesStructure'] = $categoriesStructure;
4045
4046
            /*$categoriesStructure = array();
4047
            if (isset($SETTINGS['item_extra_fields']) && (int) $SETTINGS['item_extra_fields'] === 1) {
4048
                $folderRow = DB::query(
4049
                    'SELECT f.id_category, c.title AS title
4050
                    FROM '.prefixTable('categories_folders').' AS f
4051
                    INNER JOIN '.prefixTable('categories').' AS c ON (c.id = f.id_category)
4052
                    WHERE f.id_folder = %i',
4053
                    $inputData['id']
4054
                );
4055
                foreach ($folderRow as $category) {
4056
                    $arrFields = array();
4057
                    // Get each category definition with fields
4058
                    $categoryRow = DB::query(
4059
                        "SELECT *
4060
                        FROM ".prefixTable("categories")."
4061
                        WHERE parent_id=%i
4062
                        ORDER BY `order` ASC",
4063
                        $category['id_category']
4064
                    );
4065
4066
                    if (DB::count() > 0) {
4067
                        foreach ($categoryRow as $field) {
4068
                            // Is this Field visibile by user?
4069
                            if ($field['role_visibility'] === 'all'
4070
                                || count(
4071
                                    array_intersect(
4072
                                        explode(';', $session->get('user-roles')),
4073
                                        explode(',', $field['role_visibility'])
4074
                                    )
4075
                                ) > 0
4076
                            ) {
4077
                                array_push(
4078
                                    $arrFields,
4079
                                    array(
4080
                                        $field['id'],
4081
                                        $field['title'],
4082
                                        $field['encrypted_data'],
4083
                                        $field['type'],
4084
                                        $field['masked'],
4085
                                        $field['is_mandatory']
4086
                                    )
4087
                                );
4088
                            }
4089
                        }
4090
                    }
4091
4092
                    // store the categories
4093
                    array_push(
4094
                        $categoriesStructure,
4095
                        array(
4096
                            $category['id_category'],
4097
                            $category['title'],
4098
                            $arrFields
4099
                        )
4100
                    );
4101
                }
4102
            }
4103
            $uniqueLoadData['categoriesStructure'] = $categoriesStructure;
4104
            */
4105
4106
            if ($session->has('system-list_folders_editable_by_role') && $session->has('system-list_folders_editable_by_role') && null !== $session->get('system-list_folders_editable_by_role')) {
4107
                $list_folders_editable_by_role = in_array($inputData['id'], $session->get('system-list_folders_editable_by_role'));
4108
            } else {
4109
                $list_folders_editable_by_role = '';
4110
            }
4111
            $uniqueLoadData['list_folders_editable_by_role'] = $list_folders_editable_by_role;
4112
        } else {
4113
            $uniqueLoadData = json_decode(
4114
                filter_var($dataReceived['uniqueLoadData'], FILTER_UNSAFE_RAW),
4115
                true
4116
            );
4117
4118
            // initialize main variables
4119
            $showError = $uniqueLoadData['showError'];
4120
            $accessLevel = $uniqueLoadData['accessLevel'];
4121
            $counter = $uniqueLoadData['counter'];
4122
            $counter_full = $uniqueLoadData['counter_full'];
4123
            $categoriesStructure = $uniqueLoadData['categoriesStructure'];
4124
            $folderComplexity = $uniqueLoadData['folderComplexity'];
4125
            $folder_is_personal = $uniqueLoadData['folder_is_personal'];
4126
            $folder_is_in_personal = $uniqueLoadData['folder_is_in_personal'];
4127
            //$list_folders_editable_by_role = $uniqueLoadData['list_folders_editable_by_role'];
4128
        }
4129
        
4130
        // prepare query WHere conditions
4131
        $where = new WhereClause('and');
4132
        $session__user_list_folders_limited = $session->get('user-list_folders_limited');
4133
        if (null !== $post_restricted && (int) $post_restricted === 1 && empty($session__user_list_folders_limited[$inputData['id']]) === false) {
4134
            $where->add('i.id IN %ls', $session__user_list_folders_limited[$inputData['id']]);
4135
        } else {
4136
            $where->add('i.id_tree=%i', $inputData['id']);
4137
        }
4138
4139
        // build the HTML for this set of Items
4140
        if ($counter > 0 && empty($showError)) {
4141
            // init variables
4142
            $expired_item = false;
4143
            $limited_to_items = '';
4144
4145
            // List all ITEMS
4146
            if ($folderIsPf === false) {
4147
                $where->add('i.inactif=%i', 0);
4148
                $sql_e='(SELECT date FROM ' . prefixTable('log_items') 
4149
                    . " WHERE action = 'at_creation' AND id_item=i.id " 
4150
                    . 'union all SELECT date FROM '. prefixTable('log_items') 
4151
                    . " WHERE action = 'at_modification' AND raison = 'at_pw'
4152
                    AND id_item=i.id ORDER BY date DESC LIMIT 1)";
4153
                $where->add('l.date=%l', $sql_e);
4154
4155
                $query_limit = ' LIMIT ' .
4156
                    $start . ',' .
4157
                    $post_nb_items_to_display_once;
4158
                //db::debugmode(true);
4159
                $rows = DB::query(
4160
                    'SELECT i.id AS id, i.item_key AS item_key, MIN(i.restricted_to) AS restricted_to, MIN(i.perso) AS perso,
4161
                    MIN(i.label) AS label, MIN(i.description) AS description, MIN(i.pw) AS pw, MIN(i.login) AS login,
4162
                    MIN(i.anyone_can_modify) AS anyone_can_modify, l.date AS date, i.id_tree AS tree_id, i.fa_icon AS fa_icon,
4163
                    MIN(n.renewal_period) AS renewal_period,
4164
                    MIN(l.action) AS log_action,
4165
                    l.id_user AS log_user,
4166
                    i.url AS link,
4167
                    i.email AS email
4168
                    FROM ' . prefixTable('items') . ' AS i
4169
                    INNER JOIN ' . prefixTable('nested_tree') . ' AS n ON (i.id_tree = n.id)
4170
                    INNER JOIN ' . prefixTable('log_items') . ' AS l ON (i.id = l.id_item)
4171
                    WHERE %l
4172
                    GROUP BY i.id, l.date, l.id_user, l.action
4173
                    ORDER BY i.label ASC, l.date DESC' . $query_limit,
4174
                    $where
4175
                );
4176
                //db::debugmode(false);
4177
            } else {
4178
                $post_nb_items_to_display_once = 'max';
4179
                $where->add('i.inactif=%i', 0);
4180
4181
                $rows = DB::query(
4182
                    'SELECT i.id AS id, i.item_key AS item_key, MIN(i.restricted_to) AS restricted_to, MIN(i.perso) AS perso,
4183
                    MIN(i.label) AS label, MIN(i.description) AS description, MIN(i.pw) AS pw, MIN(i.login) AS login,
4184
                    MIN(i.anyone_can_modify) AS anyone_can_modify,l.date AS date, i.id_tree AS tree_id, i.fa_icon AS fa_icon,
4185
                    MIN(n.renewal_period) AS renewal_period,
4186
                    MIN(l.action) AS log_action,
4187
                    l.id_user AS log_user,
4188
                    i.url AS link,
4189
                    i.email AS email
4190
                    FROM ' . prefixTable('items') . ' AS i
4191
                    INNER JOIN ' . prefixTable('nested_tree') . ' AS n ON (i.id_tree = n.id)
4192
                    INNER JOIN ' . prefixTable('log_items') . ' AS l ON (i.id = l.id_item)
4193
                    WHERE %l
4194
                    GROUP BY i.id, l.date, l.id_user, l.action
4195
                    ORDER BY i.label ASC, l.date DESC',
4196
                    $where
4197
                );
4198
            }
4199
4200
            $idManaged = '';
4201
4202
            foreach ($rows as $record) {
4203
                // exclude all results except the first one returned by query
4204
                if (empty($idManaged) === true || $idManaged !== $record['id']) {
4205
                    // Fix a bug on Personal Item creation - field `perso` must be set to `1`
4206
                    if ((int) $record['perso'] !== 1 && (int) $folder_is_personal === 1) {
4207
                        DB::update(
4208
                            prefixTable('items'),
4209
                            array(
4210
                                'perso' => 1,
4211
                                'updated_at' => time(),
4212
                            ),
4213
                            'id=%i',
4214
                            $record['id']
4215
                        );
4216
                        $record['perso'] = 1;
4217
                    }
4218
4219
                    // Does this item has restriction to groups of users?
4220
                    $item_is_restricted_to_role = false;
4221
                    DB::queryFirstRow(
4222
                        'SELECT role_id
4223
                        FROM ' . prefixTable('restriction_to_roles') . '
4224
                        WHERE item_id = %i',
4225
                        $record['id']
4226
                    );
4227
                    if (DB::count() > 0) {
4228
                        $item_is_restricted_to_role = true;
4229
                    }
4230
4231
                    // Has this item a restriction to Groups of Users
4232
                    $user_is_included_in_role = false;
4233
                    DB::query(
4234
                        'SELECT role_id
4235
                        FROM ' . prefixTable('restriction_to_roles') . '
4236
                        WHERE item_id = %i AND role_id IN %ls',
4237
                        $record['id'],
4238
                        $session->get('user-roles_array')
4239
                    );
4240
                    if (DB::count() > 0) {
4241
                        $user_is_included_in_role = true;
4242
                    }
4243
4244
                    // Is user in restricted list of users
4245
                    if (empty($record['restricted_to']) === false) {
4246
                        if (
4247
                            in_array($session->get('user-id'), explode(';', $record['restricted_to'])) === true
4248
                            || (((int) $session->get('user-manager') === 1 || (int) $session->get('user-can_manage_all_users') === 1)
4249
                                && (int) $SETTINGS['manager_edit'] === 1)
4250
                        ) {
4251
                            $user_is_in_restricted_list = true;
4252
                        } else {
4253
                            $user_is_in_restricted_list = false;
4254
                        }
4255
                    } else {
4256
                        $user_is_in_restricted_list = false;
4257
                    }
4258
4259
                    // Get Expiration date
4260
                    $expired_item = 0;
4261
                    if (
4262
                        (int) $SETTINGS['activate_expiration'] === 1
4263
                        && $record['renewal_period'] > 0
4264
                        && ($record['date'] + ($record['renewal_period'] * TP_ONE_MONTH_SECONDS)) < time()
4265
                    ) {
4266
                        $expired_item = 1;
4267
                    }
4268
                    // Init
4269
                    $html_json[$record['id']]['expired'] = (int) $expired_item;
4270
                    $html_json[$record['id']]['item_id'] = (int) $record['id'];
4271
                    $html_json[$record['id']]['item_key'] = (string) $record['item_key'];
4272
                    $html_json[$record['id']]['tree_id'] = (int) $record['tree_id'];
4273
                    $html_json[$record['id']]['label'] = strip_tags($record['label']);
4274
                    if (isset($SETTINGS['show_description']) === true && (int) $SETTINGS['show_description'] === 1 && is_null($record['description']) === false && empty($record['description']) === false) {
4275
                        $html_json[$record['id']]['desc'] = mb_substr(preg_replace('#<[^>]+>#', ' ', $record['description']), 0, 200);
4276
                    } else {
4277
                        $html_json[$record['id']]['desc'] = '';
4278
                    }
4279
                    $html_json[$record['id']]['login'] = $record['login'];
4280
                    $html_json[$record['id']]['anyone_can_modify'] = (int) $record['anyone_can_modify'];
4281
                    $html_json[$record['id']]['is_result_of_search'] = 0;
4282
                    $html_json[$record['id']]['is_favourited'] = in_array($record['id'], $session->get('user-favorites')) === true ? 1 : 0;
4283
                    $html_json[$record['id']]['link'] = $record['link'];
4284
                    $html_json[$record['id']]['email'] = $record['email'] ?? '';
4285
                    $html_json[$record['id']]['fa_icon'] = $record['fa_icon'];
4286
                    $html_json[$record['id']]['user_restriction_allowed_for_user'] = ((!empty($record['restricted_to']) && $user_is_in_restricted_list === true) || empty($record['restricted_to'])) ? true : false;
4287
4288
                    // Possible values:
4289
                    // 0 -> no access to item
4290
                    // 10 -> appears in list but no view
4291
                    // 20 -> can view without edit (no copy) or move
4292
                    // 30 -> can view without edit (no copy) but can move
4293
                    // 40 -> can edit but not move
4294
                    // 50 -> can edit and move
4295
                    $itemIsPersonal = false;
4296
4297
                    // Let's identify the rights belonging to this ITEM
4298
                    if (
4299
                        (int) $record['perso'] === 1
4300
                        && $record['log_action'] === 'at_creation'
4301
                        && $record['log_user'] === $session->get('user-id')
4302
                        && (int) $folder_is_in_personal === 1
4303
                        && (int) $folder_is_personal === 1
4304
                    ) {
4305
                        // Case 1 - Is this item personal and user its owner?
4306
                        // If yes then allow
4307
                        // If no then continue
4308
                        $itemIsPersonal = true;
4309
                        $right = 70;
4310
                        // ---
4311
                        // ----- END CASE 1 -----
4312
                    } elseif ((($session->has('user-manager') && (int) $session->get('user-manager') && $session->has('user-manager') && (int) $session->get('user-manager') && null !== $session->get('user-manager') && (int) $session->get('user-manager') === 1)
4313
                            || ($session->has('user-can_manage_all_users') && (int) $session->get('user-can_manage_all_users') && $session->has('user-can_manage_all_users') && (int) $session->get('user-can_manage_all_users') && null !== $session->get('user-can_manage_all_users') && (int) $session->get('user-can_manage_all_users') === 1))
4314
                        && (isset($SETTINGS['manager_edit']) === true && (int) $SETTINGS['manager_edit'] === 1)
4315
                        && (int) $record['perso'] !== 1
4316
                        && $user_is_in_restricted_list === true
4317
                    ) {
4318
                        // Case 2 - Is user manager and option "manager_edit" set to true?
4319
                        // Allow all rights
4320
                        $right = 70;
4321
                        // ---
4322
                        // ----- END CASE 2 -----
4323
                    } elseif (
4324
                        (int) $record['anyone_can_modify'] === 1
4325
                        && (int) $record['perso'] !== 1
4326
                        && (int) $session->get('user-read_only') === 0
4327
                    ) {
4328
                        // Case 3 - Has this item the setting "anyone can modify" set to true?
4329
                        // Allow all rights
4330
                        $right = 70;
4331
                        // ---
4332
                        // ----- END CASE 3 -----
4333
                    } elseif (
4334
                        $user_is_in_restricted_list === true
4335
                        && (int) $record['perso'] !== 1
4336
                        && (int) $session->get('user-read_only') === 0
4337
                    ) {
4338
                        // Case 4 - Is this item limited to Users? Is current user in this list?
4339
                        // Allow all rights
4340
                        $right = 70;
4341
                        // ---
4342
                        // ----- END CASE 4 -----
4343
                    } elseif (
4344
                        $user_is_included_in_role === true
4345
                        && (int) $record['perso'] !== 1
4346
                        && (int) $session->get('user-read_only') === 0
4347
                    ) {
4348
                        // Case 5 - Is this item limited to group of users? Is current user in one of those groups?
4349
                        // Allow all rights
4350
                        $right = 60;
4351
                        // ---
4352
                        // ----- END CASE 5 -----
4353
                    } elseif (
4354
                        (int) $record['perso'] !== 1
4355
                        && (int) $session->get('user-read_only') === 1
4356
                    ) {
4357
                        // Case 6 - Is user readonly?
4358
                        // Allow limited rights
4359
                        $right = 10;
4360
                        // ---
4361
                        // ----- END CASE 6 -----
4362
                    } elseif (
4363
                        (int) $record['perso'] !== 1
4364
                        && in_array($record['tree_id'], $session->get('user-allowed_folders_by_definition'))
4365
                    ) {
4366
                        // Case 7 - Is folder allowed by definition for this user?
4367
                        // Allow limited rights
4368
                        $right = 70;
4369
                        // ---
4370
                        // ----- END CASE 7 -----
4371
                    } elseif (
4372
                        (int) $record['perso'] !== 1
4373
                        && (int) $session->get('user-read_only') === 1
4374
                    ) {
4375
                        // Case 8 - Is user allowed to access?
4376
                        // Allow rights
4377
                        $right = 10;
4378
                        // ---
4379
                        // ----- END CASE 8 -----
4380
                    } elseif (($user_is_included_in_role === false && $item_is_restricted_to_role === true)
4381
                        && (int) $record['perso'] !== 1
4382
                        && (int) $session->get('user-read_only') === 0
4383
                    ) {
4384
                        // Case 9 - Is this item limited to Users or Groups? Is current user in this list?
4385
                        // If no then Allow none
4386
                        $right = 10;
4387
                        // ---
4388
                        // ----- END CASE 9 -----
4389
                    } else {
4390
                        // Define the access based upon setting on folder
4391
                        // 0 -> no access to item
4392
                        // 10 -> appears in list but no view
4393
                        // 20 -> can view without edit (no copy) or move or delete
4394
                        // 30 -> can view without edit (no copy) or delete but can move
4395
                        // 40 -> can edit but not move and not delete
4396
                        // 50 -> can edit and delete but not move
4397
                        // 60 -> can edit and move but not delete
4398
                        // 70 -> can edit and move
4399
                        if ((int) $accessLevel === 0) {
4400
                            $right = 0;
4401
                        } elseif ((10 <= (int) $accessLevel) && ((int) $accessLevel < 20)) {
4402
                            $right = 20;
4403
                        } elseif ((20 <= (int) $accessLevel) && ((int) $accessLevel < 30)) {
4404
                            $right = 60;
4405
                        } elseif ((int) $accessLevel >= 30) {
4406
                            $right = 70;
4407
                        } else {
4408
                            $right = 10;
4409
                        }
4410
                    }
4411
4412
                    // Is drag and drop enabled?
4413
                    $dragDrop = (int) $SETTINGS['disable_drag_drop'] !== 1;
4414
4415
                    // Now finalize the data to send back
4416
                    $html_json[$record['id']]['rights'] = $right;
4417
                    $html_json[$record['id']]['perso'] = 'fa-tag mi-red';
4418
                    $html_json[$record['id']]['sk'] = $itemIsPersonal === true ? 1 : 0;
4419
                    $html_json[$record['id']]['display'] = $right > 0 ? 1 : 0;
4420
                    $html_json[$record['id']]['open_edit'] = in_array($right, array(40, 50, 60, 70)) === true ? 1 : 0;
4421
                    $html_json[$record['id']]['canMove'] = in_array($right, array(30, 60, 70)) === true ? (int) $dragDrop : 0;
4422
4423
                    //*************** */
4424
4425
                    // Build array with items
4426
                    array_push(
4427
                        $itemsIDList,
4428
                        array(
4429
                            'id' => (int) $record['id'],
4430
                            //'display' => $displayItem,
4431
                            'edit' => $html_json[$record['id']]['open_edit'],
4432
                        )
4433
                    );
4434
                }
4435
                $idManaged = $record['id'];
4436
            }
4437
4438
            $rights = recupDroitCreationSansComplexite((int) $inputData['id']);
4439
        }
4440
4441
        // DELETE - 2.1.19 - AND (l.action = 'at_creation' OR (l.action = 'at_modification' AND l.raison LIKE 'at_pw :%'))
4442
        // count
4443
        if ((int) $start === 0) {
4444
            DB::query(
4445
                'SELECT i.id
4446
                FROM ' . prefixTable('items') . ' as i
4447
                INNER JOIN ' . prefixTable('nested_tree') . ' as n ON (i.id_tree = n.id)
4448
                INNER JOIN ' . prefixTable('log_items') . ' as l ON (i.id = l.id_item)
4449
                WHERE %l
4450
                ORDER BY i.label ASC, l.date DESC',
4451
                $where
4452
            );
4453
            $counter_full = DB::count();
4454
            $uniqueLoadData['counter_full'] = $counter_full;
4455
        }
4456
4457
        // Check list to be continued status
4458
        if ($post_nb_items_to_display_once !== 'max' && ($post_nb_items_to_display_once + $start) < $counter_full) {
4459
            $listToBeContinued = 'yes';
4460
        } else {
4461
            $listToBeContinued = 'end';
4462
        }
4463
4464
        // Prepare returned values
4465
        $returnValues = array(
4466
            'html_json' => $html_json,
4467
            //'folder_requests_psk' => $findPfGroup,
4468
            'arborescence' => $arr_arbo,
4469
            'array_items' => $itemsIDList,
4470
            'error' => $showError,
4471
            //'saltkey_is_required' => $folderIsPf === true ? 1 : 0,
4472
            'show_clipboard_small_icons' => isset($SETTINGS['copy_to_clipboard_small_icons']) && (int) $SETTINGS['copy_to_clipboard_small_icons'] === 1 ? 1 : 0,
4473
            'next_start' => intval($post_nb_items_to_display_once) + intval($start),
4474
            'list_to_be_continued' => $listToBeContinued,
4475
            'items_count' => $counter,
4476
            'counter_full' => $counter_full,
4477
            'folder_complexity' => (int) $folderComplexity,
4478
            'categoriesStructure' => $categoriesStructure,
4479
            'access_level' => $accessLevel,
4480
            'IsPersonalFolder' => $folderIsPf === true ? 1 : 0,
4481
            'uniqueLoadData' => json_encode($uniqueLoadData),
4482
        );
4483
        // Check if $rights is not null
4484
        if (count($rights) > 0) {
4485
            $returnValues = array_merge($returnValues, $rights);
4486
        }
4487
4488
        // Encrypt data to return
4489
        echo (string) prepareExchangedData(
4490
            $returnValues,
4491
            'encode'
4492
        );
4493
4494
        break;
4495
4496
    case 'get_item_password':
4497
        // Check KEY
4498
        if ($inputData['key'] !== $session->get('key')) {
4499
            echo (string) prepareExchangedData(
4500
                array(
4501
                    'error' => true,
4502
                    'message' => $lang->get('key_is_not_correct'),
4503
                ),
4504
                'encode'
4505
            );
4506
            break;
4507
        }
4508
4509
        // Get item details and its sharekey
4510
        $dataItem = DB::queryFirstRow(
4511
            'SELECT i.pw AS pw, s.share_key AS share_key, i.id AS id,
4512
                    i.label AS label, i.id_tree AS id_tree
4513
            FROM ' . prefixTable('items') . ' AS i
4514
            INNER JOIN ' . prefixTable('sharekeys_items') . ' AS s ON (s.object_id = i.id)
4515
            WHERE user_id = %i AND (i.item_key = %s OR i.id = %i)',
4516
            $session->get('user-id'),
4517
            $inputData['itemKey'] ?? '',
4518
            $inputData['itemId'] ?? 0
4519
        );
4520
4521
        // Check if password item exists
4522
        if (DB::count() === 0) {
4523
            echo (string) prepareExchangedData(
4524
                [
4525
                    'error' => true,
4526
                    'password' => '',
4527
                    'password_error' => $lang->get('password_is_empty'),
4528
                ],
4529
                'encode'
4530
            );
4531
            break;
4532
        }
4533
4534
        // Get user access rights
4535
        $userAccess = getCurrentAccessRights(
4536
            (int) $session->get('user-id'),
4537
            (int) $dataItem['id'],
4538
            (int) $dataItem['id_tree']
4539
        )['access'];
4540
4541
        // List of allowed actions
4542
        $allowedActions = [
4543
            'at_password_copied',
4544
            'at_password_shown',
4545
            'at_password_shown_edit_form',
4546
        ];
4547
4548
        // User not allowed to see this password or invalid action provided
4549
        if ($userAccess !== true
4550
            || empty($inputData['action'])
4551
            || !in_array($inputData['action'], $allowedActions, true)) {
4552
4553
            echo (string) prepareExchangedData(
4554
                [
4555
                    'error' => true,
4556
                    'password' => '',
4557
                    'password_error' => $lang->get('not_allowed_to_see_pw'),
4558
                ],
4559
                'encode'
4560
            );
4561
            break;
4562
        }
4563
4564
        // Log the action on password
4565
        logItems(
4566
            $SETTINGS,
4567
            (int) $dataItem['id'],
4568
            $dataItem['label'],
4569
            (int) $session->get('user-id'),
4570
            $inputData['action'], // Filtered by array of allowed values
4571
            $session->get('user-login')
4572
        );
4573
4574
        // Uncrypt PW if sharekey is available (empty password otherwise)
4575
        $pw = '';
4576
        if (!empty($dataItem['share_key'])) {
4577
            $pw = doDataDecryption(
4578
                $dataItem['pw'],
4579
                decryptUserObjectKey(
4580
                    $dataItem['share_key'],
4581
                    $session->get('user-private_key')
4582
                )
4583
            );
4584
        }
4585
4586
        $returnValues = array(
4587
            'error' => false,
4588
            'password' => $pw,
4589
            'password_error' => '',
4590
        );
4591
4592
        // Encrypt data to return
4593
        echo (string) prepareExchangedData(
4594
            $returnValues,
4595
            'encode'
4596
        );
4597
        break;
4598
4599
    /*
4600
     * CASE
4601
     * Get complexity level of a group
4602
     */
4603
    case 'get_complixity_level':
4604
        // get some info about ITEM
4605
        if (null !== $inputData['itemId'] && empty($inputData['itemId']) === false) {
4606
            // Is this item locked?
4607
            $itemIsLocked = isItemLocked((int) $inputData['itemId'], $session, (int) $session->get('user-id'));
4608
            if ($itemIsLocked['status'] === true) {
4609
                $returnValues = array(
4610
                    'error' => true,
4611
                    'message' => $lang->get('error_no_edition_possible_locked'),
4612
                    'delay' => $itemIsLocked['delay'],
4613
                );
4614
                echo (string) prepareExchangedData(
4615
                    $returnValues,
4616
                    'encode'
4617
                );
4618
                break;
4619
            }
4620
        }
4621
4622
        // do query on this folder
4623
        $data_this_folder = DB::queryFirstRow(
4624
            'SELECT id, personal_folder, title
4625
            FROM ' . prefixTable('nested_tree') . '
4626
            WHERE id = %s',
4627
            $inputData['folderId']
4628
        );
4629
4630
        // check if user can perform this action
4631
        if (
4632
            null !== $inputData['context']
4633
            && empty($inputData['context']) === false
4634
        ) {
4635
            if (
4636
                $inputData['context'] === 'create_folder'
4637
                || $inputData['context'] === 'edit_folder'
4638
                || $inputData['context'] === 'delete_folder'
4639
                || $inputData['context'] === 'copy_folder'
4640
            ) {
4641
                if (
4642
                    (int) $session->get('user-admin') !== 1
4643
                    && ((int) $session->get('user-manager') !== 1)
4644
                    && (isset($SETTINGS['enable_user_can_create_folders'])
4645
                        && (int) $SETTINGS['enable_user_can_create_folders'] !== 1)
4646
                    && ((int) $data_this_folder['personal_folder'] !== 1 && $data_this_folder['title'] !== $session->get('user-id'))   // take into consideration if this is a personal folder
4647
                ) {
4648
                    $returnValues = array(
4649
                        'error' => true,
4650
                        'message' => $lang->get('error_not_allowed_to'),
4651
                    );
4652
                    echo (string) prepareExchangedData(
4653
                        $returnValues,
4654
                        'encode'
4655
                    );
4656
                    break;
4657
                }
4658
            }
4659
        }
4660
4661
        // Get required Complexity for this Folder
4662
        $visibilite = '';
4663
        $data = DB::queryFirstRow(
4664
            'SELECT m.valeur, n.personal_folder
4665
            FROM ' . prefixTable('misc') . ' AS m
4666
            INNER JOIN ' . prefixTable('nested_tree') . ' AS n ON (m.intitule = n.id)
4667
            WHERE type=%s AND intitule = %s',
4668
            'complex',
4669
            $inputData['folderId']
4670
        );
4671
4672
        if (isset($data['valeur']) === true && (empty($data['valeur']) === false || $data['valeur'] === '0')) {
4673
            $complexity = TP_PW_COMPLEXITY[$data['valeur']][1];
4674
            $folder_is_personal = (int) $data['personal_folder'];
4675
4676
            // Prepare Item actual visibility (what Users/Roles can see it)
4677
            $rows = DB::query(
4678
                'SELECT t.title
4679
                FROM ' . prefixTable('roles_values') . ' as v
4680
                INNER JOIN ' . prefixTable('roles_title') . ' as t ON (v.role_id = t.id)
4681
                WHERE v.folder_id = %i
4682
                GROUP BY title',
4683
                $inputData['folderId']
4684
            );
4685
            foreach ($rows as $record) {
4686
                if (empty($visibilite)) {
4687
                    $visibilite = $record['title'];
4688
                } else {
4689
                    $visibilite .= ' - ' . $record['title'];
4690
                }
4691
            }
4692
        } else {
4693
            $complexity = $lang->get('not_defined');
4694
4695
            // if not defined, then previous query failed and personal_folder is null
4696
            // do new query to know if current folder is pf
4697
            $data_pf = DB::queryFirstRow(
4698
                'SELECT personal_folder
4699
                FROM ' . prefixTable('nested_tree') . '
4700
                WHERE id = %s',
4701
                $inputData['folderId']
4702
            );
4703
            
4704
            $folder_is_personal = $data_pf !== null ? (int) $data_pf['personal_folder'] : 0;
4705
            
4706
            $visibilite = $session->get('user-name') . ' ' . $session->get('user-lastname') . ' (' . $session->get('user-login') . ')';
4707
        }
4708
4709
        recupDroitCreationSansComplexite($inputData['folderId']);
4710
4711
        // get list of roles
4712
        $listOptionsForUsers = array();
4713
        $listOptionsForRoles = array();
4714
        $rows = DB::query(
4715
            'SELECT r.role_id AS role_id, t.title AS title
4716
            FROM ' . prefixTable('roles_values') . ' AS r
4717
            INNER JOIN ' . prefixTable('roles_title') . ' AS t ON (r.role_id = t.id)
4718
            WHERE r.folder_id = %i',
4719
            $inputData['folderId']
4720
        );
4721
        foreach ($rows as $record) {
4722
            array_push(
4723
                $listOptionsForRoles,
4724
                array(
4725
                    'id' => $record['role_id'],
4726
                    'title' => $record['title'],
4727
                )
4728
            );
4729
            $rows2 = DB::query(
4730
                'SELECT id, login, fonction_id, email, name, lastname
4731
                FROM ' . prefixTable('users') . '
4732
                WHERE admin = 0 AND fonction_id is not null'
4733
            );
4734
            foreach ($rows2 as $record2) {
4735
                foreach (explode(';', $record2['fonction_id']) as $role) {
4736
                    if (
4737
                        array_search($record2['id'], array_column($listOptionsForUsers, 'id')) === false
4738
                        && $role === $record['role_id']
4739
                    ) {
4740
                        array_push(
4741
                            $listOptionsForUsers,
4742
                            array(
4743
                                'id' => $record2['id'],
4744
                                'login' => $record2['login'],
4745
                                'name' => $record2['name'] . ' ' . $record2['lastname'],
4746
                                'email' => $record2['email'],
4747
                            )
4748
                        );
4749
                    }
4750
                }
4751
            }
4752
        }
4753
        
4754
        // Get access level for this folder
4755
        $accessLevel = 0;
4756
        if ($folder_is_personal === 0) {
4757
            $arrTmp = [];
4758
            $session->set('user-roles_array', explode(';', $session->get('user-roles')));
4759
            foreach ($session->get('user-roles_array') as $role) {
4760
                $access = DB::queryFirstRow(
4761
                    'SELECT type
4762
                    FROM ' . prefixTable('roles_values') . '
4763
                    WHERE role_id = %i AND folder_id = %i',
4764
                    $role,
4765
                    $inputData['folderId']
4766
                );
4767
                if (DB::count()>0) {
4768
                    if ($access['type'] === 'R') {
4769
                        array_push($arrTmp, 10);
4770
                    } elseif ($access['type'] === 'W') {
4771
                        array_push($arrTmp, 30);
4772
                    } elseif ($access['type'] === 'ND') {
4773
                        array_push($arrTmp, 20);
4774
                    } elseif ($access['type'] === 'NE') {
4775
                        array_push($arrTmp, 10);
4776
                    } elseif ($access['type'] === 'NDNE') {
4777
                        array_push($arrTmp, 15);
4778
                    } else {
4779
                        // Ensure to give access Right if allowed folder
4780
                        if (in_array($inputData['id'], $session->get('user-accessible_folders')) === true) {
4781
                            array_push($arrTmp, 30);
4782
                        } else {
4783
                            array_push($arrTmp, 0);
4784
                        }
4785
                    }
4786
                }
4787
            }
4788
            // 3.0.0.0 - changed  MIN to MAX
4789
            $accessLevel = count($arrTmp) > 0 ? max($arrTmp) : $accessLevel;
4790
        } elseif ($folder_is_personal === 1) {
4791
4792
            // Check if personal folder is owned by user
4793
            $folder = DB::queryFirstRow(
4794
                'SELECT id
4795
                FROM ' . prefixTable('nested_tree') . '
4796
                WHERE title = %s',
4797
                $session->get('user-id'),
4798
            );
4799
4800
            if ($folder) {
4801
                // Get all subfolders of user personal folder
4802
                $ids = $tree->getDescendants($folder['id'], true, false, true);
4803
4804
                // This folder is owned by user
4805
                if (in_array($inputData['folderId'], $ids))
4806
                    $accessLevel = 30;
4807
            }
4808
        }
4809
4810
        // Access is not allowed to this folder
4811
        if ($accessLevel === 0) {
4812
            echo (string) prepareExchangedData(
4813
                [
4814
                    'error' => true,
4815
                    'message' => $lang->get('error_not_allowed_to_access_this_folder'),
4816
                ],
4817
                'encode'
4818
            );
4819
            break;
4820
        }
4821
4822
        $returnValues = array(
4823
            'folderId' => (int) $inputData['folderId'],
4824
            'error' => false,
4825
            'val' => $data !== null ? (int) $data['valeur'] : 0,
4826
            'visibility' => $visibilite,
4827
            'complexity' => $complexity,
4828
            'personal' => $folder_is_personal,
4829
            'usersList' => $listOptionsForUsers,
4830
            'rolesList' => $listOptionsForRoles,
4831
            'setting_restricted_to_roles' => isset($SETTINGS['restricted_to_roles']) === true
4832
                && (int) $SETTINGS['restricted_to_roles'] === 1 ? 1 : 0,
4833
            'itemAccessRight' => $accessLevel,
4834
        );
4835
        echo (string) prepareExchangedData(
4836
            $returnValues,
4837
            'encode'
4838
        );
4839
        break;
4840
4841
    case 'handle_item_edition_lock':
4842
        // Check KEY
4843
        if ($inputData['key'] !== $session->get('key')) {
4844
            echo (string) prepareExchangedData(
4845
                array(
4846
                    'error' => true,
4847
                    'message' => $lang->get('key_is_not_correct'),
4848
                ),
4849
                'encode'
4850
            );
4851
            break;
4852
        }
4853
4854
        // decrypt and retreive data in JSON format
4855
        $dataReceived = prepareExchangedData(
4856
            $inputData['data'],
4857
            'decode'
4858
        );
4859
        $itemId = filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
4860
        $action = filter_var($dataReceived['action'], FILTER_SANITIZE_SPECIAL_CHARS);
4861
4862
        if ($action === 'release_lock') {
4863
            DB::delete(
4864
                prefixTable('items_edition'), 
4865
                'item_id = %i AND user_id = %i', 
4866
                $itemId,
4867
                $session->get('user-id')
4868
            );
4869
        }
4870
        
4871
        break;
4872
4873
    /*
4874
     * CASE
4875
     * DELETE attached file from an item
4876
     */
4877
    case 'delete_attached_file':
4878
        // Check KEY
4879
        if ($inputData['key'] !== $session->get('key')) {
4880
            echo (string) prepareExchangedData(
4881
                array(
4882
                    'error' => true,
4883
                    'message' => $lang->get('key_is_not_correct'),
4884
                ),
4885
                'encode'
4886
            );
4887
            break;
4888
        }
4889
4890
        // decrypt and retreive data in JSON format
4891
        $dataReceived = prepareExchangedData(
4892
            $inputData['data'],
4893
            'decode'
4894
        );
4895
        $fileId = filter_var($dataReceived['file_id'], FILTER_SANITIZE_NUMBER_INT);
4896
4897
        // Get some info before deleting
4898
        $data = DB::queryFirstRow(
4899
            'SELECT name, id_item, file
4900
            FROM ' . prefixTable('files') . '
4901
            WHERE id = %i',
4902
            $fileId
4903
        );
4904
4905
        // Load item data
4906
        $data_item = DB::queryFirstRow(
4907
            'SELECT id_tree
4908
            FROM ' . prefixTable('items') . '
4909
            WHERE id = %i',
4910
            $data['id_item']
4911
        );
4912
4913
        // Check that user can access this folder
4914
        if (in_array($data_item['id_tree'], $session->get('user-accessible_folders')) === false) {
4915
            echo (string) prepareExchangedData(
4916
                array('error' => 'ERR_FOLDER_NOT_ALLOWED'),
4917
                'encode'
4918
            );
4919
            break;
4920
        }
4921
4922
        if (empty($data['id_item']) === false) {
4923
            // Delete from FILES table
4924
            DB::delete(
4925
                prefixTable('files'),
4926
                'id = %i',
4927
                $fileId
4928
            );
4929
4930
            // Update the log
4931
            logItems(
4932
                $SETTINGS,
4933
                (int) $data['id_item'],
4934
                $data['name'],
4935
                $session->get('user-id'),
4936
                'at_modification',
4937
                $session->get('user-login'),
4938
                'at_del_file : ' . $data['name']
4939
            );
4940
4941
            // DElete sharekeys
4942
            DB::delete(
4943
                prefixTable('sharekeys_files'),
4944
                'object_id = %i',
4945
                $fileId
4946
            );
4947
4948
            // Delete file from server
4949
            $fileToDelete = $SETTINGS['path_to_upload_folder'] . '/' . TP_FILE_PREFIX . base64_decode($data['file']);
4950
            $fileToDelete = realpath($fileToDelete);
4951
            if ($fileToDelete && strpos($fileToDelete, $SETTINGS['path_to_upload_folder']) === 0) {
4952
                fileDelete($fileToDelete, $SETTINGS);
4953
            }
4954
        }
4955
4956
        echo (string) prepareExchangedData(
4957
            array(
4958
                'error' => false,
4959
                'message' => '',
4960
            ),
4961
            'encode'
4962
        );
4963
        break;
4964
4965
    /*
4966
     * FUNCTION
4967
     * Launch an action when clicking on a quick icon
4968
     * $action = 0 => Make not favorite
4969
     * $action = 1 => Make favorite
4970
     */
4971
    case 'action_on_quick_icon':
4972
        // Check KEY and rights
4973
        if (
4974
            $inputData['key'] !== $session->get('key')
4975
            || $session->get('user-read_only') === 1 || !isset($SETTINGS['pwd_maximum_length'])
4976
        ) {
4977
            // error
4978
            exit;
4979
        }
4980
4981
        // decrypt and retreive data in JSON format
4982
        $dataReceived = prepareExchangedData(
4983
            $inputData['data'],
4984
            'decode'
4985
        );
4986
        $inputData['action'] = (int) filter_var($dataReceived['action'], FILTER_SANITIZE_NUMBER_INT);
4987
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
4988
4989
        if ((int) $inputData['action'] === 0) {
4990
            // Add new favourite
4991
            SessionManager::addRemoveFromSessionArray('user-favorites', [$inputData['itemId']], 'add');
4992
            DB::update(
4993
                prefixTable('users'),
4994
                array(
4995
                    'favourites' => implode(';', $session->get('user-favorites')),
4996
                ),
4997
                'id = %i',
4998
                $session->get('user-id')
4999
            );
5000
            // Update SESSION with this new favourite
5001
            $data = DB::queryFirstRow(
5002
                'SELECT label,id_tree
5003
                FROM ' . prefixTable('items') . '
5004
                WHERE id = %i',
5005
                $inputData['itemId']
5006
            );
5007
            SessionManager::addRemoveFromSessionAssociativeArray(
5008
                'user-favorites_tab',
5009
                [
5010
                    $inputData['itemId'] => [
5011
                        'label' => $data['label'],
5012
                        'url' => 'index.php?page=items&amp;group=' . $data['id_tree'] . '&amp;id=' . $inputData['itemId'],
5013
                    ],
5014
                ],
5015
                'add'
5016
            );
5017
        } elseif ((int) $inputData['action'] === 1) {
5018
            // delete from session
5019
            SessionManager::addRemoveFromSessionArray('user-favorites', [$inputData['itemId']], 'remove');
5020
5021
            // delete from DB
5022
            DB::update(
5023
                prefixTable('users'),
5024
                array(
5025
                    'favourites' => implode(';', $session->get('user-favorites')),
5026
                ),
5027
                'id = %i',
5028
                $session->get('user-id')
5029
            );
5030
            // refresh session fav list
5031
            if ($session->has('user-favorites_tab') && $session->has('user-favorites_tab') && null !== $session->get('user-favorites_tab')) {
5032
                $user_favorites_tab = $session->get('user-favorites_tab');
5033
                foreach ($user_favorites_tab as $key => $value) {
5034
                    if ($key === $inputData['id']) {
5035
                        SessionManager::addRemoveFromSessionAssociativeArray('user-favorites_tab', [$key], 'remove');
5036
                        break;
5037
                    }
5038
                }
5039
            }
5040
        }
5041
        break;
5042
5043
    /*
5044
     * CASE
5045
     * Move an ITEM
5046
     */
5047
    case 'move_item':
5048
        // Check KEY and rights
5049
        if ($inputData['key'] !== $session->get('key')) {
5050
            echo (string) prepareExchangedData(
5051
                array(
5052
                    'error' => true,
5053
                    'message' => $lang->get('key_is_not_correct'),
5054
                ),
5055
                'encode'
5056
            );
5057
            break;
5058
        }
5059
        if ($session->get('user-read_only') === 1 || isset($SETTINGS['pwd_maximum_length']) === false) {
5060
            echo (string) prepareExchangedData(
5061
                array(
5062
                    'error' => true,
5063
                    'message' => $lang->get('error_not_allowed_to'),
5064
                ),
5065
                'encode'
5066
            );
5067
            break;
5068
        }
5069
5070
        // decrypt and retreive data in JSON format
5071
        $dataReceived = prepareExchangedData(
5072
            $inputData['data'],
5073
            'decode'
5074
        );
5075
        $inputData['folderId'] = (int) filter_var($dataReceived['folder_id'], FILTER_SANITIZE_NUMBER_INT);
5076
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
5077
5078
        // get data about item
5079
        $dataSource = DB::queryFirstRow(
5080
            'SELECT i.pw, f.personal_folder,i.id_tree, f.title,i.label
5081
            FROM ' . prefixTable('items') . ' as i
5082
            INNER JOIN ' . prefixTable('nested_tree') . ' as f ON (i.id_tree=f.id)
5083
            WHERE i.id=%i',
5084
            $inputData['itemId']
5085
        );
5086
5087
        // Check that user can delete on old folder
5088
        $checkRights = getCurrentAccessRights(
5089
            $session->get('user-id'),
5090
            $inputData['itemId'],
5091
            (int) $dataSource['id_tree'],
5092
        );
5093
5094
        if ($checkRights['error'] || !$checkRights['delete']) {
5095
            echo (string) prepareExchangedData(
5096
                array(
5097
                    'error' => true,
5098
                    'message' => $lang->get('error_not_allowed_to'),
5099
                ),
5100
                'encode'
5101
            );
5102
            break;
5103
        }
5104
5105
        // Check that user can write on requested folder
5106
        $checkRights = getCurrentAccessRights(
5107
            $session->get('user-id'),
5108
            $inputData['itemId'],
5109
            $inputData['folderId'],
5110
        );
5111
5112
        if ($checkRights['error'] || !$checkRights['edit']) {
5113
            echo (string) prepareExchangedData(
5114
                array(
5115
                    'error' => true,
5116
                    'message' => $lang->get('error_not_allowed_to'),
5117
                ),
5118
                'encode'
5119
            );
5120
            break;
5121
        }
5122
5123
        // get data about new folder
5124
        $dataDestination = DB::queryFirstRow(
5125
            'SELECT personal_folder, title
5126
            FROM ' . prefixTable('nested_tree') . '
5127
            WHERE id = %i',
5128
            $inputData['folderId']
5129
        );
5130
5131
        // Check that user can access this folder
5132
        if (
5133
            in_array($dataSource['id_tree'], $session->get('user-accessible_folders')) === false
5134
            || in_array($inputData['folderId'], $session->get('user-accessible_folders')) === false
5135
            //|| (int) $dataSource['personal_folder'] === (int) $dataDestination['personal_folder']
5136
        ) {
5137
            echo (string) prepareExchangedData(
5138
                array(
5139
                    'error' => true,
5140
                    'message' => $lang->get('error_not_allowed_to'),
5141
                ),
5142
                'encode'
5143
            );
5144
            break;
5145
        }
5146
5147
        // Manage possible cases
5148
        if ((int) $dataSource['personal_folder'] === 0 && (int) $dataDestination['personal_folder'] === 0) {
5149
            // Previous is non personal folder and new too
5150
            // Just update is needed. Item key is the same
5151
            DB::update(
5152
                prefixTable('items'),
5153
                array(
5154
                    'id_tree' => $inputData['folderId'],
5155
                    'updated_at' => time(),
5156
                ),
5157
                'id=%i',
5158
                $inputData['itemId']
5159
            );
5160
            // ---
5161
            // ---
5162
        } elseif ((int) $dataSource['personal_folder'] === 0 && (int) $dataDestination['personal_folder'] === 1) {
5163
            // Source is public and destination is personal
5164
            // Decrypt and remove all sharekeys (items, fields, files)
5165
            // Encrypt only for the user
5166
5167
            // Remove all item sharekeys items
5168
            DB::delete(
5169
                prefixTable('sharekeys_items'),
5170
                'object_id = %i AND user_id != %i',
5171
                $inputData['itemId'],
5172
                $session->get('user-id')
5173
            );
5174
5175
            // Remove all item sharekeys fields
5176
            // Get fields for this Item
5177
            $rows = DB::query(
5178
                'SELECT id
5179
                FROM ' . prefixTable('categories_items') . '
5180
                WHERE item_id = %i',
5181
                $inputData['itemId']
5182
            );
5183
            foreach ($rows as $field) {
5184
                DB::delete(
5185
                    prefixTable('sharekeys_fields'),
5186
                    'object_id = %i AND user_id != %i',
5187
                    $field['id'],
5188
                    $session->get('user-id')
5189
                );
5190
            }
5191
5192
            // Remove all item sharekeys files
5193
            // Get FILES for this Item
5194
            $rows = DB::query(
5195
                'SELECT id
5196
                FROM ' . prefixTable('files') . '
5197
                WHERE id_item = %i',
5198
                $inputData['itemId']
5199
            );
5200
            foreach ($rows as $attachment) {
5201
                DB::delete(
5202
                    prefixTable('sharekeys_files'),
5203
                    'object_id = %i AND user_id != %i',
5204
                    $attachment['id'],
5205
                    $session->get('user-id')
5206
                );
5207
            }
5208
5209
            // update pw
5210
            DB::update(
5211
                prefixTable('items'),
5212
                array(
5213
                    'id_tree' => $inputData['folderId'],
5214
                    'perso' => 1,
5215
                    'updated_at' => time(),
5216
                ),
5217
                'id=%i',
5218
                $inputData['itemId']
5219
            );
5220
            // ---
5221
            // ---
5222
        } elseif ((int) $dataSource['personal_folder'] === 1 && (int) $dataDestination['personal_folder'] === 1) {
5223
            // If previous is personal folder and new is personal folder too => no key exist on item
5224
            // just update is needed. Item key is the same
5225
            DB::update(
5226
                prefixTable('items'),
5227
                array(
5228
                    'id_tree' => $inputData['folderId'],
5229
                    'updated_at' => time(),
5230
                ),
5231
                'id=%i',
5232
                $inputData['itemId']
5233
            );
5234
            // ---
5235
            // ---
5236
        } elseif ((int) $dataSource['personal_folder'] === 1 && (int) $dataDestination['personal_folder'] === 0) {
5237
            // If previous is personal folder and new is not personal folder => no key exist on item => add new
5238
            // Create keys for all users
5239
5240
            // Get the ITEM object key for the user
5241
            $userKey = DB::queryFirstRow(
5242
                'SELECT share_key
5243
                FROM ' . prefixTable('sharekeys_items') . '
5244
                WHERE user_id = %i AND object_id = %i',
5245
                $session->get('user-id'),
5246
                $inputData['itemId']
5247
            );
5248
            if (DB::count() > 0) {
5249
                $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5250
5251
                // This is a public object
5252
                $users = DB::query(
5253
                    'SELECT id, public_key
5254
                    FROM ' . prefixTable('users') . '
5255
                    WHERE id NOT IN %li
5256
                    AND public_key != ""',
5257
                    $tpUsersIDs
5258
                );
5259
5260
                foreach ($users as $user) {
5261
                    // Insert in DB the new object key for this item by user
5262
                    DB::insert(
5263
                        prefixTable('sharekeys_items'),
5264
                        array(
5265
                            'object_id' => $inputData['itemId'],
5266
                            'user_id' => (int) $user['id'],
5267
                            'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5268
                        )
5269
                    );
5270
                }
5271
            }
5272
5273
            // Get the FIELDS object key for the user
5274
            // Get fields for this Item
5275
            $rows = DB::query(
5276
                'SELECT id
5277
                FROM ' . prefixTable('categories_items') . '
5278
                WHERE item_id = %i',
5279
                $inputData['itemId']
5280
            );
5281
            foreach ($rows as $field) {
5282
                $userKey = DB::queryFirstRow(
5283
                    'SELECT share_key
5284
                    FROM ' . prefixTable('sharekeys_fields') . '
5285
                    WHERE user_id = %i AND object_id = %i',
5286
                    $session->get('user-id'),
5287
                    $field['id']
5288
                );
5289
                if (DB::count() > 0) {
5290
                    $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5291
5292
                    // This is a public object
5293
                    $users = DB::query(
5294
                        'SELECT id, public_key
5295
                        FROM ' . prefixTable('users') . '
5296
                        WHERE id NOT IN %li
5297
                        AND public_key != ""',
5298
                        $tpUsersIDs
5299
                    );
5300
                    foreach ($users as $user) {
5301
                        // Insert in DB the new object key for this item by user
5302
                        DB::insert(
5303
                            prefixTable('sharekeys_fields'),
5304
                            array(
5305
                                'object_id' => $field['id'],
5306
                                'user_id' => (int) $user['id'],
5307
                                'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5308
                            )
5309
                        );
5310
                    }
5311
                }
5312
            }
5313
5314
            // Get the FILE object key for the user
5315
            // Get FILES for this Item
5316
            $rows = DB::query(
5317
                'SELECT id
5318
                FROM ' . prefixTable('files') . '
5319
                WHERE id_item = %i',
5320
                $inputData['itemId']
5321
            );
5322
            foreach ($rows as $attachment) {
5323
                $userKey = DB::queryFirstRow(
5324
                    'SELECT share_key
5325
                    FROM ' . prefixTable('sharekeys_files') . '
5326
                    WHERE user_id = %i AND object_id = %i',
5327
                    $session->get('user-id'),
5328
                    $attachment['id']
5329
                );
5330
                if (DB::count() > 0) {
5331
                    $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5332
5333
                    // This is a public object
5334
                    $users = DB::query(
5335
                        'SELECT id, public_key
5336
                        FROM ' . prefixTable('users') . '
5337
                        WHERE id NOT IN %li
5338
                        AND public_key != ""',
5339
                        $tpUsersIDs
5340
                    );
5341
5342
                    foreach ($users as $user) {
5343
                        // Insert in DB the new object key for this item by user
5344
                        DB::insert(
5345
                            prefixTable('sharekeys_files'),
5346
                            array(
5347
                                'object_id' => $attachment['id'],
5348
                                'user_id' => (int) $user['id'],
5349
                                'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5350
                            )
5351
                        );
5352
                    }
5353
                }
5354
            }
5355
5356
            // update item
5357
            DB::update(
5358
                prefixTable('items'),
5359
                array(
5360
                    'id_tree' => $inputData['folderId'],
5361
                    'perso' => 0,
5362
                    'updated_at' => time(),
5363
                ),
5364
                'id=%i',
5365
                $inputData['itemId']
5366
            );
5367
        }
5368
5369
        // Log item moved
5370
        logItems(
5371
            $SETTINGS,
5372
            (int) $inputData['itemId'],
5373
            $dataSource['label'],
5374
            $session->get('user-id'),
5375
            'at_modification',
5376
            $session->get('user-login'),
5377
            'at_moved : ' . $dataSource['title'] . ' -> ' . $dataDestination['title']
5378
        );
5379
5380
        // Update cache table
5381
        updateCacheTable('update_value', (int) $inputData['itemId']);
5382
5383
        $returnValues = array(
5384
            'error' => '',
5385
            'message' => '',
5386
            'from_folder' => $dataSource['id_tree'],
5387
            'to_folder' => $inputData['folderId'],
5388
        );
5389
        echo (string) prepareExchangedData(
5390
            $returnValues,
5391
            'encode'
5392
        );
5393
        break;
5394
5395
    /*
5396
     * CASE
5397
     * MASSIVE Move an ITEM
5398
     */
5399
    case 'mass_move_items':
5400
        // Check KEY and rights
5401
        if ($inputData['key'] !== $session->get('key')) {
5402
            echo (string) prepareExchangedData(
5403
                array(
5404
                    'error' => true,
5405
                    'message' => $lang->get('key_is_not_correct'),
5406
                ),
5407
                'encode'
5408
            );
5409
            break;
5410
        }
5411
        if ($session->get('user-read_only') === 1 || isset($SETTINGS['pwd_maximum_length']) === false) {
5412
            echo (string) prepareExchangedData(
5413
                array(
5414
                    'error' => true,
5415
                    'message' => $lang->get('error_not_allowed_to'),
5416
                ),
5417
                'encode'
5418
            );
5419
            break;
5420
        }
5421
5422
        // decrypt and retreive data in JSON format
5423
        $dataReceived = prepareExchangedData(
5424
            $inputData['data'],
5425
            'decode'
5426
        );
5427
        $inputData['folderId'] = filter_var($dataReceived['folder_id'], FILTER_SANITIZE_NUMBER_INT);
5428
        $post_item_ids = filter_var($dataReceived['item_ids'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5429
5430
        // loop on items to move
5431
        foreach (explode(';', $post_item_ids) as $item_id) {
5432
            if (empty($item_id) === false) {
5433
                // get data about item
5434
                $dataSource = DB::queryFirstRow(
5435
                    'SELECT i.pw, f.personal_folder,i.id_tree, f.title,i.label
5436
                    FROM ' . prefixTable('items') . ' as i
5437
                    INNER JOIN ' . prefixTable('nested_tree') . ' as f ON (i.id_tree=f.id)
5438
                    WHERE i.id=%i',
5439
                    $item_id
5440
                );
5441
5442
                // Check that user can access this folder
5443
                if (
5444
                    in_array($dataSource['id_tree'], $session->get('user-accessible_folders')) === false
5445
                    || in_array($inputData['folderId'], $session->get('user-accessible_folders')) === false
5446
                ) {
5447
                    echo (string) prepareExchangedData(
5448
                        array(
5449
                            'error' => true,
5450
                            'message' => $lang->get('error_not_allowed_to'),
5451
                        ),
5452
                        'encode'
5453
                    );
5454
                    exit;
5455
                }
5456
5457
                // get data about new folder
5458
                $dataDestination = DB::queryFirstRow(
5459
                    'SELECT personal_folder, title FROM ' . prefixTable('nested_tree') . ' WHERE id = %i',
5460
                    $inputData['folderId']
5461
                );
5462
5463
                // previous is non personal folder and new too
5464
                if (
5465
                    (int) $dataSource['personal_folder'] === 0
5466
                    && (int) $dataDestination['personal_folder'] === 0
5467
                ) {
5468
                    // just update is needed. Item key is the same
5469
                    DB::update(
5470
                        prefixTable('items'),
5471
                        array(
5472
                            'id_tree' => $inputData['folderId'],
5473
                            'updated_at' => time(),
5474
                        ),
5475
                        'id = %i',
5476
                        $item_id
5477
                    );
5478
                    // ---
5479
                    // ---
5480
                    // ---
5481
                } elseif (
5482
                    (int) $dataSource['personal_folder'] === 0
5483
                    && (int) $dataDestination['personal_folder'] === 1
5484
                ) {
5485
                    // Source is public and destination is personal
5486
                    // Decrypt and remove all sharekeys (items, fields, files)
5487
                    // Encrypt only for the user
5488
5489
                    // Remove all item sharekeys items
5490
                    DB::delete(
5491
                        prefixTable('sharekeys_items'),
5492
                        'object_id = %i AND user_id != %i',
5493
                        $item_id,
5494
                        $session->get('user-id')
5495
                    );
5496
5497
                    // Remove all item sharekeys fields
5498
                    // Get fields for this Item
5499
                    $rows = DB::query(
5500
                        'SELECT id
5501
                        FROM ' . prefixTable('categories_items') . '
5502
                        WHERE item_id = %i',
5503
                        $item_id
5504
                    );
5505
                    foreach ($rows as $field) {
5506
                        DB::delete(
5507
                            prefixTable('sharekeys_fields'),
5508
                            'object_id = %i AND user_id != %i',
5509
                            $field['id'],
5510
                            $session->get('user-id')
5511
                        );
5512
                    }
5513
5514
                    // Remove all item sharekeys files
5515
                    // Get FILES for this Item
5516
                    $rows = DB::query(
5517
                        'SELECT id
5518
                        FROM ' . prefixTable('files') . '
5519
                        WHERE id_item = %i',
5520
                        $item_id
5521
                    );
5522
                    foreach ($rows as $attachment) {
5523
                        DB::delete(
5524
                            prefixTable('sharekeys_files'),
5525
                            'object_id = %i AND user_id != %i',
5526
                            $attachment['id'],
5527
                            $session->get('user-id')
5528
                        );
5529
                    }
5530
5531
                    // update pw
5532
                    DB::update(
5533
                        prefixTable('items'),
5534
                        array(
5535
                            'id_tree' => $inputData['folderId'],
5536
                            'perso' => 1,
5537
                            'updated_at' => time(),
5538
                        ),
5539
                        'id = %i',
5540
                        $item_id
5541
                    );
5542
                    // ---
5543
                    // ---
5544
                    // ---
5545
                } elseif (
5546
                    (int) $dataSource['personal_folder'] === 1
5547
                    && (int) $dataDestination['personal_folder'] === 1
5548
                ) {
5549
                    // If previous is personal folder and new is personal folder too => no key exist on item
5550
                    // just update is needed. Item key is the same
5551
                    DB::update(
5552
                        prefixTable('items'),
5553
                        array(
5554
                            'id_tree' => $inputData['folderId'],
5555
                            'updated_at' => time(),
5556
                        ),
5557
                        'id = %i',
5558
                        $item_id
5559
                    );
5560
                    // ---
5561
                    // ---
5562
                    // ---
5563
                } elseif (
5564
                    (int) $dataSource['personal_folder'] === 1
5565
                    && (int) $dataDestination['personal_folder'] === 0
5566
                ) {
5567
                    // If previous is personal folder and new is not personal folder => no key exist on item => add new
5568
                    // Create keys for all users
5569
5570
                    // Get the ITEM object key for the user
5571
                    $userKey = DB::queryFirstRow(
5572
                        'SELECT share_key
5573
                        FROM ' . prefixTable('sharekeys_items') . '
5574
                        WHERE user_id = %i AND object_id = %i',
5575
                        $session->get('user-id'),
5576
                        $item_id
5577
                    );
5578
                    if (DB::count() > 0) {
5579
                        $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5580
5581
                        // This is a public object
5582
                        $users = DB::query(
5583
                            'SELECT id, public_key
5584
                            FROM ' . prefixTable('users') . '
5585
                            WHERE id NOT IN %li
5586
                            AND public_key != ""',
5587
                            $tpUsersIDs
5588
                        );
5589
5590
                        foreach ($users as $user) {
5591
                            // Insert in DB the new object key for this item by user
5592
                            DB::insert(
5593
                                prefixTable('sharekeys_items'),
5594
                                array(
5595
                                    'object_id' => $item_id,
5596
                                    'user_id' => (int) $user['id'],
5597
                                    'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5598
                                )
5599
                            );
5600
                        }
5601
                    }
5602
5603
                    // Get the FIELDS object key for the user
5604
                    // Get fields for this Item
5605
                    $rows = DB::query(
5606
                        'SELECT id
5607
                        FROM ' . prefixTable('categories_items') . '
5608
                        WHERE item_id = %i',
5609
                        $item_id
5610
                    );
5611
                    foreach ($rows as $field) {
5612
                        $userKey = DB::queryFirstRow(
5613
                            'SELECT share_key
5614
                            FROM ' . prefixTable('sharekeys_fields') . '
5615
                            WHERE user_id = %i AND object_id = %i',
5616
                            $session->get('user-id'),
5617
                            $field['id']
5618
                        );
5619
                        if (DB::count() > 0) {
5620
                            $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5621
5622
                            // This is a public object
5623
                            $users = DB::query(
5624
                                'SELECT id, public_key
5625
                                FROM ' . prefixTable('users') . '
5626
                                WHERE id NOT IN %li
5627
                                AND public_key != ""',
5628
                                $tpUsersIDs
5629
                            );
5630
5631
                            foreach ($users as $user) {
5632
                                // Insert in DB the new object key for this item by user
5633
                                DB::insert(
5634
                                    prefixTable('sharekeys_fields'),
5635
                                    array(
5636
                                        'object_id' => $field['id'],
5637
                                        'user_id' => (int) $user['id'],
5638
                                        'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5639
                                    )
5640
                                );
5641
                            }
5642
                        }
5643
                    }
5644
5645
                    // Get the FILE object key for the user
5646
                    // Get FILES for this Item
5647
                    $rows = DB::query(
5648
                        'SELECT id
5649
                        FROM ' . prefixTable('files') . '
5650
                        WHERE id_item = %i',
5651
                        $item_id
5652
                    );
5653
                    foreach ($rows as $attachment) {
5654
                        $userKey = DB::queryFirstRow(
5655
                            'SELECT share_key
5656
                            FROM ' . prefixTable('sharekeys_files') . '
5657
                            WHERE user_id = %i AND object_id = %i',
5658
                            $session->get('user-id'),
5659
                            $attachment['id']
5660
                        );
5661
                        if (DB::count() > 0) {
5662
                            $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5663
5664
                            // This is a public object
5665
                            $users = DB::query(
5666
                                'SELECT id, public_key
5667
                                FROM ' . prefixTable('users') . '
5668
                                WHERE id NOT IN %li
5669
                                AND public_key != ""',
5670
                                $tpUsersIDs
5671
                            );
5672
5673
                            foreach ($users as $user) {
5674
                                // Insert in DB the new object key for this item by user
5675
                                DB::insert(
5676
                                    prefixTable('sharekeys_files'),
5677
                                    array(
5678
                                        'object_id' => $attachment['id'],
5679
                                        'user_id' => (int) $user['id'],
5680
                                        'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5681
                                    )
5682
                                );
5683
                            }
5684
                        }
5685
                    }
5686
5687
                    // update item
5688
                    DB::update(
5689
                        prefixTable('items'),
5690
                        array(
5691
                            'id_tree' => $inputData['folderId'],
5692
                            'perso' => 0,
5693
                            'updated_at' => time(),
5694
                        ),
5695
                        'id=%i',
5696
                        $item_id
5697
                    );
5698
                }
5699
                // Log item moved
5700
                logItems(
5701
                    $SETTINGS,
5702
                    (int) $item_id,
5703
                    $dataSource['label'],
5704
                    $session->get('user-id'),
5705
                    'at_modification',
5706
                    $session->get('user-login'),
5707
                    'at_moved : ' . $dataSource['title'] . ' -> ' . $dataDestination['title']
5708
                );
5709
            }
5710
        }
5711
5712
        // reload cache table
5713
        require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
5714
        updateCacheTable('reload', null);
5715
5716
        echo (string) prepareExchangedData(
5717
            array(
5718
                'error' => false,
5719
                'message' => '',
5720
            ),
5721
            'encode'
5722
        );
5723
        break;
5724
5725
    /*
5726
     * CASE
5727
     * MASSIVE Delete an item
5728
     */
5729
    case 'mass_delete_items':
5730
        // Check KEY and rights
5731
        if ($inputData['key'] !== $session->get('key')) {
5732
            echo (string) prepareExchangedData(
5733
                array(
5734
                    'error' => true,
5735
                    'message' => $lang->get('key_is_not_correct'),
5736
                ),
5737
                'encode'
5738
            );
5739
            break;
5740
        }
5741
        if ($session->get('user-read_only') === 1) {
5742
            echo (string) prepareExchangedData(
5743
                array(
5744
                    'error' => true,
5745
                    'message' => $lang->get('error_not_allowed_to'),
5746
                ),
5747
                'encode'
5748
            );
5749
            break;
5750
        }
5751
5752
        // decrypt and retreive data in JSON format
5753
        $dataReceived = prepareExchangedData(
5754
            $inputData['data'],
5755
            'decode'
5756
        );
5757
        $post_item_ids = filter_var($dataReceived['item_ids'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5758
5759
        // perform a check in case of Read-Only user creating an item in his PF
5760
        if ($session->get('user-read_only') === 1) {
5761
            echo (string) prepareExchangedData(
5762
                array(
5763
                    'error' => true,
5764
                    'message' => $lang->get('error_not_allowed_to'),
5765
                ),
5766
                'encode'
5767
            );
5768
            break;
5769
        }
5770
5771
        // loop on items to move
5772
        foreach (explode(';', $post_item_ids) as $item_id) {
5773
            if (empty($item_id) === false) {
5774
                // get info
5775
                $dataSource = DB::queryFirstRow(
5776
                    'SELECT label, id_tree
5777
                    FROM ' . prefixTable('items') . '
5778
                    WHERE id=%i',
5779
                    $item_id
5780
                );
5781
5782
                // Check that user can access this folder
5783
                if (
5784
                    in_array($dataSource['id_tree'], $session->get('user-accessible_folders')) === false
5785
                ) {
5786
                    echo (string) prepareExchangedData(
5787
                        array(
5788
                            'error' => true,
5789
                            'message' => $lang->get('error_not_allowed_to'),
5790
                        ),
5791
                        'encode'
5792
                    );
5793
                    break;
5794
                }
5795
5796
                // delete item consists in disabling it
5797
                DB::update(
5798
                    prefixTable('items'),
5799
                    array(
5800
                        'inactif' => '1',
5801
                        'deleted_at' => time(),
5802
                    ),
5803
                    'id = %i',
5804
                    $item_id
5805
                );
5806
5807
                // log
5808
                logItems(
5809
                    $SETTINGS,
5810
                    (int) $item_id,
5811
                    $dataSource['label'],
5812
                    $session->get('user-id'),
5813
                    'at_delete',
5814
                    $session->get('user-login')
5815
                );
5816
5817
                // Update CACHE table
5818
                updateCacheTable('delete_value', (int) $item_id);
5819
            }
5820
        }
5821
5822
        echo (string) prepareExchangedData(
5823
            array(
5824
                'error' => false,
5825
                'message' => '',
5826
            ),
5827
            'encode'
5828
        );
5829
        break;
5830
5831
        /*
5832
        * CASE
5833
        * Send email
5834
    */
5835
    case 'send_email':
5836
        // Check KEY
5837
        if ($inputData['key'] !== $session->get('key')) {
5838
            echo (string) prepareExchangedData(
5839
                array(
5840
                    'error' => true,
5841
                    'message' => $lang->get('key_is_not_correct'),
5842
                ),
5843
                'encode'
5844
            );
5845
            break;
5846
        }
5847
        if ($session->get('user-read_only') === 1) {
5848
            echo (string) prepareExchangedData(
5849
                array(
5850
                    'error' => true,
5851
                    'message' => $lang->get('error_not_allowed_to'),
5852
                ),
5853
                'encode'
5854
            );
5855
            break;
5856
        }
5857
5858
        // decrypt and retrieve data in JSON format
5859
        $dataReceived = prepareExchangedData(
5860
            $inputData['data'],
5861
            'decode'
5862
        );
5863
5864
        // Prepare variables
5865
        $inputData['id'] = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
5866
        $inputData['receipt'] = filter_var($dataReceived['receipt'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5867
        $inputData['cat'] = filter_var($dataReceived['cat'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5868
        $post_content = $request->request->has('name') ? explode(',', $request->request->filter('content', '', FILTER_SANITIZE_FULL_SPECIAL_CHARS)) : '';
5869
5870
        // get links url
5871
        if (empty($SETTINGS['email_server_url']) === true) {
5872
            $SETTINGS['email_server_url'] = $SETTINGS['cpassman_url'];
5873
        }
5874
        if ($inputData['cat'] === 'request_access_to_author') {
5875
            // Variables
5876
            $dataAuthor = DB::queryFirstRow(
5877
                'SELECT email,login
5878
                FROM ' . prefixTable('users') . '
5879
                WHERE id = %i',
5880
                $post_content[1]
5881
            );
5882
5883
            $dataItem = DB::queryFirstRow(
5884
                'SELECT label, id_tree
5885
                FROM ' . prefixTable('items') . '
5886
                WHERE id = %i',
5887
                $post_content[0]
5888
            );
5889
5890
            // Get path
5891
            $path = geItemReadablePath(
5892
                (int) $dataItem['id_tree'],
5893
                $dataItem['label'],
5894
                $SETTINGS
5895
            );
5896
5897
            // Prepare email
5898
            prepareSendingEmail(
5899
                $lang->get('email_request_access_subject'),
5900
                str_replace(
5901
                    array('#tp_item_author#', '#tp_user#', '#tp_item#'),
5902
                    array(' ' . addslashes($dataAuthor['login']), addslashes($session->get('user-login')), $path),
5903
                    $lang->get('email_request_access_mail')
5904
                ),
5905
                $dataAuthor['email'],
5906
                ""
5907
            );
5908
        } elseif ($inputData['cat'] === 'share_this_item') {
5909
            $dataItem = DB::queryFirstRow(
5910
                'SELECT label,id_tree
5911
                FROM ' . prefixTable('items') . '
5912
                WHERE id= %i',
5913
                $inputData['id']
5914
            );
5915
5916
            // Get path
5917
            $path = geItemReadablePath(
5918
                (int) $dataItem['id_tree'],
5919
                $dataItem['label'],
5920
                $SETTINGS
5921
            );
5922
5923
            // Prepare email
5924
            prepareSendingEmail(
5925
                $lang->get('email_share_item_subject'),
5926
                str_replace(
5927
                    array(
5928
                        '#tp_link#',
5929
                        '#tp_user#',
5930
                        '#tp_item#',
5931
                    ),
5932
                    array(
5933
                        empty($SETTINGS['email_server_url']) === false ?
5934
                            $SETTINGS['email_server_url'] . '/index.php?page=items&group=' . $dataItem['id_tree'] . '&id=' . $inputData['id'] : $SETTINGS['cpassman_url'] . '/index.php?page=items&group=' . $dataItem['id_tree'] . '&id=' . $inputData['id'],
5935
                        addslashes($session->get('user-login')),
5936
                        addslashes($path),
5937
                    ),
5938
                    $lang->get('email_share_item_mail')
5939
                ),
5940
                $inputData['receipt'],
5941
                ""
5942
            );
5943
        }
5944
5945
        echo (string) prepareExchangedData(
5946
            array(
5947
                'error' => false,
5948
                'message' => '',
5949
            ),
5950
            'encode'
5951
        );
5952
        break;
5953
5954
    /*
5955
     * CASE
5956
     * Item History Log - add new entry
5957
     */
5958
    case 'history_entry_add':
5959
        if ($inputData['key'] !== $session->get('key')) {
5960
            $data = array('error' => 'key_is_wrong');
5961
            echo (string) prepareExchangedData(
5962
                $data,
5963
                'encode'
5964
            );
5965
            break;
5966
        }
5967
5968
        // decrypt and retreive data in JSON format
5969
        $dataReceived = prepareExchangedData(
5970
            $inputData['data'],
5971
            'decode'
5972
        );
5973
5974
        $item_id = filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
5975
        $label = filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5976
        $date = filter_var($dataReceived['date'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5977
        $time = filter_var($dataReceived['time'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5978
        $session__user_list_folders_limited = $session->get('user-list_folders_limited');
5979
5980
        // Get all informations for this item
5981
        $dataItem = DB::queryFirstRow(
5982
            'SELECT *
5983
            FROM ' . prefixTable('items') . ' as i
5984
            INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
5985
            WHERE i.id=%i AND l.action = %s',
5986
            $item_id,
5987
            'at_creation'
5988
        );
5989
        // check that actual user can access this item
5990
        $restrictionActive = true;
5991
        $restrictedTo = is_null($dataItem['restricted_to']) === false ? array_filter(explode(';', $dataItem['restricted_to'])) : [];
5992
        if (in_array($session->get('user-id'), $restrictedTo)) {
5993
            $restrictionActive = false;
5994
        }
5995
        if (empty($dataItem['restricted_to'])) {
5996
            $restrictionActive = false;
5997
        }
5998
5999
        if (((in_array($dataItem['id_tree'], $session->get('user-accessible_folders'))) && ((int) $dataItem['perso'] === 0 || ((int) $dataItem['perso'] === 1 && $dataItem['id_user'] === $session->get('user-id'))) && $restrictionActive === false)
6000
            || (isset($SETTINGS['anyone_can_modify']) && (int) $SETTINGS['anyone_can_modify'] === 1 && (int) $dataItem['anyone_can_modify'] === 1 && (in_array($dataItem['id_tree'], $session->get('user-accessible_folders')) || (int) $session->get('user-admin') === 1) && $restrictionActive === false)
6001
            || (is_array($session__user_list_folders_limited[$inputData['folderId']]) === true && in_array($inputData['id'], $session__user_list_folders_limited[$inputData['folderId']]) === true)
6002
        ) {
6003
            // Query
6004
            logItems(
6005
                $SETTINGS,
6006
                (int) $item_id,
6007
                $dataItem['label'],
6008
                $session->get('user-id'),
6009
                'at_manual',
6010
                $session->get('user-login'),
6011
                htmlspecialchars_decode($label, ENT_QUOTES),
6012
                null,
6013
                (string) dateToStamp($date.' '.$time, $SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'])
6014
            );
6015
            // Prepare new line
6016
            $data = DB::queryFirstRow(
6017
                'SELECT * FROM ' . prefixTable('log_items') . ' WHERE id_item = %i ORDER BY date DESC',
6018
                $item_id
6019
            );
6020
            $historic = date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $data['date']) . ' - ' . $session->get('user-login') . ' - ' . $lang->get($data['action']) . ' - ' . $data['raison'];
6021
            // send back
6022
            $data = array(
6023
                'error' => '',
6024
                'new_line' => '<br>' . addslashes($historic),
6025
            );
6026
            echo (string) prepareExchangedData(
6027
                $data,
6028
                'encode'
6029
            );
6030
        } else {
6031
            $data = array('error' => 'something_wrong');
6032
            echo (string) prepareExchangedData(
6033
                $data,
6034
                'encode'
6035
            );
6036
            break;
6037
        }
6038
        break;
6039
6040
    /*
6041
     * CASE
6042
     * Free Item for Edition
6043
     */
6044
    case 'free_item_for_edition':
6045
        // Check KEY
6046
        if ($inputData['key'] !== $session->get('key')) {
6047
            echo '[ { "error" : "key_not_conform" } ]';
6048
            break;
6049
        }
6050
        // Do
6051
        DB::delete(
6052
            prefixTable('items_edition'),
6053
            'item_id = %i',
6054
            $inputData['id']
6055
        );
6056
        break;
6057
6058
    /*
6059
     * CASE
6060
     * Check if Item has been changed since loaded
6061
     */
6062
    case 'generate_OTV_url':
6063
        // Check KEY
6064
        if ($inputData['key'] !== $session->get('key')) {
6065
            echo '[ { "error" : "key_not_conform" } ]';
6066
            break;
6067
        }
6068
6069
        // decrypt and retreive data in JSON format
6070
        $dataReceived = prepareExchangedData(
6071
            $inputData['data'],
6072
            'decode'
6073
        );
6074
6075
        // delete all existing old otv codes
6076
        DB::delete(
6077
            prefixTable('otv'),
6078
            'time_limit < %i',
6079
            time()
6080
        );
6081
6082
        // generate session
6083
        $otv_code = GenerateCryptKey(32, false, true, true, false, true);
6084
        $otv_key = GenerateCryptKey(32, false, true, true, false, true);
6085
6086
        // Generate Defuse key
6087
        $otv_user_code_encrypted = defuse_generate_personal_key($otv_key);
6088
6089
        // check if psk is correct.
6090
        $otv_key_encoded = defuse_validate_personal_key(
6091
            $otv_key,
6092
            $otv_user_code_encrypted
6093
        );
6094
6095
        // Decrypt the pwd
6096
        // Should we log a password change?
6097
        $itemQ = DB::queryFirstRow(
6098
            'SELECT s.share_key, i.pw
6099
            FROM ' . prefixTable('items') . ' AS i
6100
            INNER JOIN ' . prefixTable('sharekeys_items') . ' AS s ON (i.id = s.object_id)
6101
            WHERE s.user_id = %i AND s.object_id = %i',
6102
            $session->get('user-id'),
6103
            $dataReceived['id']
6104
        );
6105
        if (DB::count() === 0 || empty($itemQ['pw']) === true) {
6106
            // No share key found
6107
            $pw = '';
6108
        } else {
6109
            $pw = base64_decode(doDataDecryption(
6110
                $itemQ['pw'],
6111
                decryptUserObjectKey(
6112
                    $itemQ['share_key'],
6113
                    $session->get('user-private_key')
6114
                )
6115
            ));
6116
        }
6117
6118
        // Encrypt it with DEFUSE using the generated code as key
6119
        // This is required as the OTV is used by someone without any Teampass account
6120
        $passwd = cryption(
6121
            $pw,
6122
            $otv_key_encoded,
6123
            'encrypt',
6124
            $SETTINGS
6125
        );
6126
        $timestampReference = time();
6127
6128
        DB::insert(
6129
            prefixTable('otv'),
6130
            array(
6131
                'id' => null,
6132
                'item_id' => $dataReceived['id'],
6133
                'timestamp' => $timestampReference,
6134
                'originator' => intval($session->get('user-id')),
6135
                'code' => $otv_code,
6136
                'encrypted' => $passwd['string'],
6137
                'time_limit' => (int) $dataReceived['days'] * (int) TP_ONE_DAY_SECONDS + time(),
6138
                'max_views' => (int) $dataReceived['views'],
6139
                'shared_globaly' => 0,
6140
            )
6141
        );
6142
        $newID = DB::insertId();
6143
6144
        // Prepare URL content
6145
        $otv_session = array(
6146
            'otv' => true,
6147
            'code' => $otv_code,
6148
            'key' => $otv_key_encoded,
6149
            'stamp' => $timestampReference,
6150
        );
6151
6152
        if (isset($SETTINGS['otv_expiration_period']) === false) {
6153
            $SETTINGS['otv_expiration_period'] = 7;
6154
        }
6155
        $url = $SETTINGS['cpassman_url'] . '/index.php?' . http_build_query($otv_session);
6156
6157
        echo json_encode(
6158
            array(
6159
                'error' => '',
6160
                'url' => $url,
6161
                'otv_id' => $newID,
6162
            )
6163
        );
6164
        break;
6165
6166
    /*
6167
    * CASE
6168
    * Check if Item has been changed since loaded
6169
    */
6170
    case 'update_OTV_url':
6171
        // Check KEY
6172
        if ($inputData['key'] !== $session->get('key')) {
6173
            echo '[ { "error" : "key_not_conform" } ]';
6174
            break;
6175
        }
6176
6177
        // decrypt and retreive data in JSON format
6178
        $dataReceived = prepareExchangedData(
6179
            $inputData['data'],
6180
            'decode'
6181
        );
6182
6183
        // get parameters from original link
6184
        $url = $dataReceived['original_link'];
6185
        $parts = parse_url($url);
6186
        if(isset($parts['query'])){
6187
            parse_str($parts['query'], $orignal_link_parameters);
6188
        } else {
6189
            $orignal_link_parameters = array();
6190
        }
6191
6192
        // update database
6193
        DB::update(
6194
            prefixTable('otv'),
6195
            array(
6196
                'time_limit' => (int) $dataReceived['days'] * (int) TP_ONE_DAY_SECONDS + time(),
6197
                'max_views' => (int) $dataReceived['views'],
6198
                'shared_globaly' => (int) $dataReceived['shared_globaly'] === 1 ? 1 : 0,
6199
            ),
6200
            'id = %i',
6201
            $dataReceived['otv_id']
6202
        );
6203
6204
        // Prepare URL content
6205
        $otv_session = [
6206
            'otv' => true,
6207
            'code' => $orignal_link_parameters['code'],
6208
            'key' => $orignal_link_parameters['key'],
6209
            'stamp' => $orignal_link_parameters['stamp'],
6210
        ];
6211
6212
        if ((int) $dataReceived['shared_globaly'] === 1 && isset($SETTINGS['otv_subdomain']) === true && empty($SETTINGS['otv_subdomain']) === false) {
6213
            // Inject subdomain in URL by convering www. to subdomain.
6214
            $domain_scheme = parse_url($SETTINGS['cpassman_url'], PHP_URL_SCHEME);
6215
            $domain_host = parse_url($SETTINGS['cpassman_url'], PHP_URL_HOST);
6216
            if (str_contains($domain_host, 'www.') === true) {
6217
                $domain_host = (string) $SETTINGS['otv_subdomain'] . '.' . substr($domain_host, 4);
6218
            } else {
6219
                $domain_host = (string) $SETTINGS['otv_subdomain'] . '.' . $domain_host;
6220
            }
6221
            $url = $domain_scheme.'://'.$domain_host . '/index.php?'.http_build_query($otv_session);
6222
        } else {
6223
            $url = $SETTINGS['cpassman_url'] . '/index.php?'.http_build_query($otv_session);
6224
        }
6225
6226
        echo (string) prepareExchangedData(
6227
            array(
6228
                'error' => false,
6229
                'new_url' => $url,
6230
            ),
6231
            'encode'
6232
        );
6233
        break;
6234
6235
6236
        /*
6237
    * CASE
6238
    * Free Item for Edition
6239
    */
6240
    case 'image_preview_preparation':
6241
        // Check KEY
6242
        if ($inputData['key'] !== $session->get('key')) {
6243
            echo (string) prepareExchangedData(
6244
                array(
6245
                    'error' => true,
6246
                    'message' => $lang->get('key_is_not_correct'),
6247
                ),
6248
                'encode'
6249
            );
6250
            break;
6251
        }
6252
6253
        // get file info
6254
        $file_info = DB::queryFirstRow(
6255
            'SELECT f.id AS id, f.file AS file, f.name AS name, f.status AS status,
6256
            f.extension AS extension, f.type AS type,
6257
            s.share_key AS share_key
6258
            FROM ' . prefixTable('files') . ' AS f
6259
            INNER JOIN ' . prefixTable('sharekeys_files') . ' AS s ON (f.id = s.object_id)
6260
            WHERE s.user_id = %i AND s.object_id = %i',
6261
            $session->get('user-id'),
6262
            $inputData['id']
6263
        );
6264
6265
        // Check if user has this sharekey
6266
        if (empty($file_info['share_key']) === true) {
6267
            echo (string) prepareExchangedData(
6268
                array(
6269
                    'error' => true,
6270
                    'message' => $lang->get('no_sharekey_found'),
6271
                ),
6272
                'encode'
6273
            );
6274
            break;
6275
        }
6276
6277
        //$fileName = basename($file_info['name'], '.'.$file_info['extension']);
6278
6279
        // prepare image info
6280
        $post_title = basename($file_info['name'], '.' . $file_info['extension']);
6281
        $post_title = isBase64($post_title) === true ? base64_decode($post_title) : $post_title;
6282
        
6283
        // Get image content
6284
        // deepcode ignore PT: File and path are secured directly inside the function decryptFile()
6285
        $fileContent = decryptFile(
6286
            $file_info['file'],
6287
            $SETTINGS['path_to_upload_folder'],
6288
            decryptUserObjectKey($file_info['share_key'], $session->get('user-private_key'))
6289
        );
6290
6291
        // Check error
6292
        if (isset($fileContent['error']) === true) {
6293
            echo (string) prepareExchangedData(
6294
                array(
6295
                    'error' => true,
6296
                    'message' => $fileContent['message'],
6297
                ),
6298
                'encode'
6299
            );
6300
            break;
6301
        }
6302
6303
        // Encrypt data to return
6304
        echo (string) prepareExchangedData(
6305
            array(
6306
                'error' => false,
6307
                'filename' => $post_title . '.' . $file_info['extension'],
6308
                'file_type' => $file_info['type'],
6309
                'file_content' => $fileContent,
6310
            ),
6311
            'encode'
6312
        );
6313
        break;
6314
6315
    /*
6316
     * CASE
6317
     * Get list of users that have access to the folder
6318
     */
6319
    case 'refresh_visible_folders':
6320
        // Check KEY
6321
        if ($inputData['key'] !== $session->get('key')) {
6322
            echo (string) prepareExchangedData(
6323
                array(
6324
                    'error' => true,
6325
                    'message' => $lang->get('key_is_not_correct'),
6326
                ),
6327
                'encode'
6328
            );
6329
            break;
6330
        }
6331
        $arr_data = [];
6332
        $arrayFolders = [];
6333
6334
        // decrypt and retreive data in JSON format
6335
        $dataReceived = prepareExchangedData(
6336
            $inputData['data'],
6337
            'decode'
6338
        );
6339
6340
        // Will we show the root folder?
6341
        if ($session->has('user-can_create_root_folder') && (int) $session->get('user-can_create_root_folder') && $session->has('user-can_create_root_folder') && (int) $session->get('user-can_create_root_folder') && null !== $session->get('user-can_create_root_folder') && (int) $session->get('user-can_create_root_folder') === 1
6342
        ) {
6343
            $arr_data['can_create_root_folder'] = 1;
6344
        } else {
6345
            $arr_data['can_create_root_folder'] = 0;
6346
        }
6347
6348
        // do we have a cache to be used?
6349
        if (isset($dataReceived['force_refresh_cache']) === true && $dataReceived['force_refresh_cache'] === false) {
6350
            $goCachedFolders = loadFoldersListByCache('visible_folders', 'folders');
6351
            if ($goCachedFolders['state'] === true) {
6352
                $arr_data['folders'] = json_decode($goCachedFolders['data'], true);
6353
                // send data
6354
                echo (string) prepareExchangedData(
6355
                    [
6356
                        'error' => 'false',
6357
                        'html_json' => ($arr_data),
6358
                        'extra' => isset($goCachedFolders['extra']) ? $goCachedFolders['extra'] : '',
6359
                    ],
6360
                    'encode'
6361
                );
6362
                break;
6363
            }
6364
        }
6365
        // Build list of visible folders
6366
        if (
6367
            (int) $session->get('user-admin') === 1
6368
        ) {
6369
            $session->set('user-accessible_folders', $session->get('user-personal_visible_folders'));
6370
        }
6371
6372
        if (null !== $session->get('user-list_folders_limited') && count($session->get('user-list_folders_limited')) > 0) {
6373
            $listFoldersLimitedKeys = array_keys($session->get('user-list_folders_limited'));
6374
        } else {
6375
            $listFoldersLimitedKeys = array();
6376
        }
6377
        // list of items accessible but not in an allowed folder
6378
        if (
6379
            null !== $session->get('system-list_restricted_folders_for_items') &&
6380
            count($session->get('system-list_restricted_folders_for_items')) > 0
6381
        ) {
6382
            $listRestrictedFoldersForItemsKeys = array_keys($session->get('system-list_restricted_folders_for_items'));
6383
        } else {
6384
            $listRestrictedFoldersForItemsKeys = array();
6385
        }
6386
        
6387
        //Build tree
6388
        $tree->rebuild();
6389
        $folders = $tree->getDescendants();
6390
        foreach ($folders as $folder) {
6391
            // Be sure that user can only see folders he/she is allowed to
6392
            if (
6393
                in_array($folder->id, $session->get('user-forbiden_personal_folders')) === false
6394
                || in_array($folder->id, $session->get('user-accessible_folders')) === true
6395
                || in_array($folder->id, $listFoldersLimitedKeys) === true
6396
                || in_array($folder->id, $listRestrictedFoldersForItemsKeys) === true
6397
            ) {
6398
                // Init
6399
                $displayThisNode = false;
6400
6401
                // Check if any allowed folder is part of the descendants of this node
6402
                $nodeDescendants = $tree->getDescendantsFromTreeArray($folders, $folder->id);
6403
                foreach ($nodeDescendants as $node) {
6404
                    // manage tree counters
6405
                    if (
6406
                        in_array($node, array_merge($session->get('user-accessible_folders'), $session->get('system-list_restricted_folders_for_items'))) === true
6407
                        || (is_array($listFoldersLimitedKeys) === true && in_array($node, $listFoldersLimitedKeys) === true)
6408
                        || (is_array($listRestrictedFoldersForItemsKeys) === true && in_array($node, $listRestrictedFoldersForItemsKeys) === true)
6409
                    ) {
6410
                        $displayThisNode = true;
6411
                        break;
6412
                    }
6413
                }
6414
6415
                if ($displayThisNode === true) {
6416
                    // ALL FOLDERS
6417
                    // Build path
6418
                    $arbo = $tree->getPath($folder->id, false);
6419
                    $path = '';
6420
                    foreach ($arbo as $elem) {
6421
                        $path = (empty($path) ? '' : $path . ' / ') . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
6422
                    }
6423
6424
                    // Build array
6425
                    array_push($arrayFolders, [
6426
                        'id' => (int) $folder->id,
6427
                        'level' => (int) $folder->nlevel,
6428
                        'title' => ((int) $folder->title === (int) $session->get('user-id') && (int) $folder->nlevel === 1) ? $session->get('user-login') : $folder->title,
6429
                        'disabled' => (
6430
                            in_array($folder->id, $session->get('user-accessible_folders')) === false
6431
                            || in_array($folder->id, $session->get('user-read_only_folders')) === true
6432
                        ) ? 1 : 0,
6433
                        'parent_id' => (int) $folder->parent_id,
6434
                        'perso' => (int) $folder->personal_folder,
6435
                        'path' => htmlspecialchars($path),
6436
                        'is_visible_active' => (null !== $session->get('user-read_only_folders') && in_array($folder->id, $session->get('user-read_only_folders'))) ? 1 : 0,
6437
                    ]);
6438
                }
6439
            }
6440
        }
6441
        if (empty($arrayFolders) === false) {
6442
            // store array to return
6443
            $arr_data['folders'] = $arrayFolders;
6444
6445
            // update session
6446
            $session->set('user-folders_list', $arr_data['folders']);
6447
            
6448
            // update cache
6449
            cacheTreeUserHandler(
6450
                (int) $session->get('user-id'),
6451
                json_encode($arr_data['folders']),
6452
                $SETTINGS,
6453
                'visible_folders',
6454
            );
6455
        }
6456
6457
        // send data
6458
        echo (string) prepareExchangedData(
6459
            [
6460
                'error' => 'false',
6461
                'html_json' => $arr_data,
6462
            ],
6463
            'encode'
6464
        );
6465
6466
        break;
6467
6468
    /*
6469
     * CASE
6470
     * Get list of users that have access to the folder
6471
     */
6472
    case 'refresh_folders_other_info':
6473
        // Check KEY
6474
        if ($inputData['key'] !== $session->get('key')) {
6475
            echo (string) prepareExchangedData(
6476
                array(
6477
                    'error' => true,
6478
                    'message' => $lang->get('key_is_not_correct'),
6479
                ),
6480
                'encode'
6481
            );
6482
            break;
6483
        }
6484
6485
        $ret = [];
6486
        $foldersArray = json_decode($inputData['data'], true);
6487
        if (is_array($foldersArray) === true && $inputData['data'] !== '[null]') {
6488
            $rows = DB::query(
6489
                'SELECT id, categories
6490
                FROM ' . prefixTable('nested_tree') . '
6491
                WHERE id IN (%l)',
6492
                implode(',', $foldersArray)
6493
            );
6494
            foreach ($rows as $record) {
6495
                if (empty($record['categories']) === false) {
6496
                    array_push(
6497
                        $ret,
6498
                        array($record['id'] => json_decode($record['categories'], true))
6499
                    );
6500
                }
6501
            }
6502
        }
6503
6504
        // send data
6505
        echo (string) prepareExchangedData(
6506
            [
6507
                'error' => '',
6508
                'result' => $ret,
6509
            ],
6510
            'encode'
6511
        );
6512
6513
        break;
6514
6515
        /*
6516
    * CASE
6517
    * Load item history
6518
    */
6519
    case 'load_item_history':
6520
        // Check KEY
6521
        if ($inputData['key'] !== $session->get('key')) {
6522
            echo (string) prepareExchangedData(
6523
                array('error' => 'ERR_KEY_NOT_CORRECT'),
6524
                'encode'
6525
            );
6526
            break;
6527
        }
6528
        
6529
        // get item info
6530
        $dataItem = DB::queryFirstRow(
6531
            'SELECT *
6532
            FROM ' . prefixTable('items') . '
6533
            WHERE id=%i',
6534
            $inputData['itemId']
6535
        );
6536
6537
        // get item history
6538
        $history = [];
6539
        $previous_passwords = [];
6540
        $rows = DB::query(
6541
            'SELECT l.date as date, l.action as action, l.raison as raison,
6542
                u.login as login, u.avatar_thumb as avatar_thumb, u.name as name, u.lastname as lastname,
6543
                l.old_value as old_value
6544
            FROM ' . prefixTable('log_items') . ' as l
6545
            INNER JOIN ' . prefixTable('users') . ' as u ON (l.id_user=u.id)
6546
            WHERE id_item=%i AND l.action NOT IN (%l)
6547
            ORDER BY date DESC',
6548
            $inputData['itemId'],
6549
            '"at_shown","at_password_copied", "at_shown", "at_password_shown", "at_password_shown_edit_form"'
6550
        );
6551
        foreach ($rows as $record) {
6552
            if (empty($record['raison']) === true) {
6553
                $reason[0] = '';
6554
            } else {
6555
                $reason = array_map('trim', explode(':', $record['raison']));
6556
            }
6557
            
6558
            // imported via API
6559
            if (empty($record['login']) === true) {
6560
                $record['login'] = $lang->get('imported_via_api') . ' [' . $record['raison'] . ']';
6561
            }
6562
            
6563
            // Prepare avatar
6564
            if (isset($record['avatar_thumb']) && empty($record['avatar_thumb']) === false) {
6565
                if (file_exists($SETTINGS['cpassman_dir'] . '/includes/avatars/' . $record['avatar_thumb'])) {
6566
                    $avatar = $SETTINGS['cpassman_url'] . '/includes/avatars/' . $record['avatar_thumb'];
6567
                } else {
6568
                    $avatar = $SETTINGS['cpassman_url'] . '/includes/images/photo.jpg';
6569
                }
6570
            } else {
6571
                $avatar = $SETTINGS['cpassman_url'] . '/includes/images/photo.jpg';
6572
            }
6573
6574
            // Prepare action
6575
            $action = '';
6576
            $detail = '';
6577
            if ($reason[0] === 'at_pw') {
6578
                $action = $lang->get($reason[0]);
6579
                
6580
                // get previous password
6581
                if (empty($record['old_value']) === false) {
6582
                    $previous_pwd = cryption(
6583
                        $record['old_value'],
6584
                        '',
6585
                        'decrypt'
6586
                    );
6587
                    array_push(
6588
                        $previous_passwords, 
6589
                        [
6590
                            'password' => htmlentities($previous_pwd['string']),
6591
                            'date' => date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['date']),
6592
                        ]
6593
                    );
6594
                }
6595
            } elseif ($record['action'] === 'at_manual') {
6596
                $detail = $reason[0];
6597
                $action = $lang->get($record['action']);
6598
            } elseif ($reason[0] === 'at_description') {
6599
                $action = $lang->get('description_has_changed');
6600
            } elseif (empty($record['raison']) === false && $reason[0] !== 'at_creation') {
6601
                $action = $lang->get($reason[0]);
6602
                if ($reason[0] === 'at_moved') {
6603
                    $tmp = explode(' -> ', $reason[1]);
6604
                    $detail = $lang->get('from') . ' <span class="font-weight-light">' . $tmp[0] . '</span> ' . $lang->get('to') . ' <span class="font-weight-light">' . $tmp[1] . ' </span>';
6605
                } elseif ($reason[0] === 'at_field') {
6606
                    $tmp = explode(' => ', $reason[1]);
6607
                    if (count($tmp) > 1) {
6608
                        $detail = '<b>' . trim($tmp[0]) . '</b> | ' . $lang->get('previous_value') .
6609
                            ': <span class="font-weight-light">' . trim($tmp[1]) . '</span>';
6610
                    } else {
6611
                        $detail = trim($reason[1]);
6612
                    }
6613
                } elseif (in_array($reason[0], array('at_restriction', 'at_email', 'at_login', 'at_label', 'at_url', 'at_tag')) === true) {
6614
                    $tmp = explode(' => ', $reason[1]);
6615
                    $detail = empty(trim($tmp[0])) === true ?
6616
                        $lang->get('no_previous_value') : $lang->get('previous_value') . ': <span class="font-weight-light">' . $tmp[0] . ' </span>';
6617
                } elseif ($reason[0] === 'at_automatic_del') {
6618
                    $detail = $lang->get($reason[1]);
6619
                } elseif ($reason[0] === 'at_anyoneconmodify' || $reason[0] === 'at_otp_status' || $reason[0] === 'at_otp_secret' || $reason[0] === 'at_phone_number') {
6620
                    $detail = $lang->get($reason[1]);
6621
                } elseif ($reason[0] === 'at_add_file' || $reason[0] === 'at_del_file') {
6622
                    $tmp = explode(':', $reason[1]);
6623
                    $tmp = explode('.', $tmp[0]);
6624
                    $detail = isBase64($tmp[0]) === true ?
6625
                        base64_decode($tmp[0]) . '.' . $tmp[1] : $tmp[0];
6626
                } elseif ($reason[0] === 'at_import') {
6627
                    $detail = '';
6628
                } elseif (in_array($reason[0], array('csv', 'pdf')) === true) {
6629
                    $detail = $reason[0];
6630
                    $action = $lang->get('exported_to_file');
6631
                } else {
6632
                    $detail = $reason[0];
6633
                }
6634
            } else {
6635
                $detail = $lang->get($record['action']);
6636
                $action = '';
6637
            }
6638
6639
            array_push(
6640
                $history,
6641
                array(
6642
                    'avatar' => $avatar,
6643
                    'login' => $record['login'],
6644
                    'name' => $record['name'] . ' ' . $record['lastname'],
6645
                    'date' => date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['date']),
6646
                    'action' => $action,
6647
                    'detail' => $detail,
6648
                )
6649
            );
6650
        }
6651
6652
        // order previous passwords by date
6653
        $key_values = array_column($previous_passwords, 'date'); 
6654
        array_multisort($key_values, /** @scrutinizer ignore-type */SORT_DESC, $previous_passwords);
6655
6656
        // send data
6657
        // deepcode ignore ServerLeak: Data is encrypted before being sent
6658
        echo (string) prepareExchangedData(
6659
            [
6660
                'error' => '',
6661
                'history' => $history,
6662
                'previous_passwords' => $previous_passwords,
6663
            ],
6664
            'encode'
6665
        );
6666
6667
        break;
6668
6669
    case 'suggest_item_change':
6670
        // Check KEY
6671
        if ($inputData['key'] !== $session->get('key')) {
6672
            echo (string) prepareExchangedData(
6673
                array(
6674
                    'error' => 'key_not_conform',
6675
                    'message' => $lang->get('key_is_not_correct'),
6676
                ),
6677
                'encode'
6678
            );
6679
            break;
6680
        }
6681
        // decrypt and retrieve data in JSON format
6682
        $data_received = prepareExchangedData(
6683
            $inputData['data'],
6684
            'decode'
6685
        );
6686
6687
        // prepare variables
6688
        $label = htmlspecialchars_decode($data_received['label'], ENT_QUOTES);
6689
        $pwd = htmlspecialchars_decode($data_received['password']);
6690
        $login = htmlspecialchars_decode($data_received['login'], ENT_QUOTES);
6691
        $email = htmlspecialchars_decode($data_received['email']);
6692
        $url = htmlspecialchars_decode($data_received['url']);
6693
        $folder = htmlspecialchars_decode($data_received['folder_id']);
6694
        $comment = htmlspecialchars_decode($data_received['comment']);
6695
        $item_id = htmlspecialchars_decode($data_received['item_id']);
6696
6697
        if (empty($pwd)) {
6698
            $cryptedStuff['encrypted'] = '';
6699
            $cryptedStuff['objectKey'] = '';
6700
        } else {
6701
            $cryptedStuff = doDataEncryption($pwd);
6702
        }
6703
6704
        // query
6705
        DB::insert(
6706
            prefixTable('items_change'),
6707
            array(
6708
                'item_id' => $item_id,
6709
                'label' => $label,
6710
                'pw' => $cryptedStuff['encrypted'],
6711
                'login' => $login,
6712
                'email' => $email,
6713
                'url' => $url,
6714
                'description' => '',
6715
                'comment' => $comment,
6716
                'folder_id' => $folder,
6717
                'user_id' => (int) $session->get('user-id'),
6718
                'timestamp' => time(),
6719
            )
6720
        );
6721
        $newID = DB::insertId();
6722
6723
        // Create sharekeys for users
6724
        storeUsersShareKey(
6725
            prefixTable('sharekeys_items'),
6726
            0,
6727
            (int) $newID,
6728
            $cryptedStuff['objectKey'],
6729
        );
6730
6731
        // get some info to add to the notification email
6732
        $resp_user = DB::queryFirstRow(
6733
            'SELECT login FROM ' . prefixTable('users') . ' WHERE id = %i',
6734
            $session->get('user-id')
6735
        );
6736
        $resp_folder = DB::queryFirstRow(
6737
            'SELECT title FROM ' . prefixTable('nested_tree') . ' WHERE id = %i',
6738
            $folder
6739
        );
6740
6741
        // notify Managers
6742
        $emailSettings = new EmailSettings($SETTINGS);
6743
        $emailService = new EmailService();
6744
        $rows = DB::query(
6745
            'SELECT email
6746
            FROM ' . prefixTable('users') . '
6747
            WHERE `gestionnaire` = %i AND `email` IS NOT NULL',
6748
            1
6749
        );
6750
        foreach ($rows as $record) {
6751
            $emailService->sendMail(
6752
                $lang->get('suggestion_notify_subject'),
6753
                str_replace(array('#tp_label#', '#tp_user#', '#tp_folder#'), array(addslashes($label), addslashes($resp_user['login']), addslashes($resp_folder['title'])), $lang->get('suggestion_notify_body')),
6754
                $record['email'],
6755
                $emailSettings
6756
            );
6757
        }
6758
6759
        echo (string) prepareExchangedData(
6760
            array(
6761
                'error' => '',
6762
            ),
6763
            'encode'
6764
        );
6765
        break;
6766
6767
    case 'send_request_access':
6768
        // Check KEY
6769
        if ($inputData['key'] !== $session->get('key')) {
6770
            echo (string) prepareExchangedData(
6771
                array(
6772
                    'error' => 'key_not_conform',
6773
                    'message' => $lang->get('key_is_not_correct'),
6774
                ),
6775
                'encode'
6776
            );
6777
            break;
6778
        }
6779
        // decrypt and retrieve data in JSON format
6780
        $dataReceived = prepareExchangedData(
6781
            $inputData['data'],
6782
            'decode'
6783
        );
6784
6785
        // prepare variables
6786
        //$post_email_body = filter_var($dataReceived['email'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
6787
        $inputData['itemId'] = (int) filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
6788
6789
        // Send email
6790
        $dataItem = DB::queryFirstRow(
6791
            'SELECT label, id_tree
6792
            FROM ' . prefixTable('items') . '
6793
            WHERE id = %i',
6794
            $inputData['itemId']
6795
        );
6796
6797
        // Do log
6798
        logItems(
6799
            $SETTINGS,
6800
            (int) $inputData['itemId'],
6801
            $dataItem['label'],
6802
            $session->get('user-id'),
6803
            'at_access',
6804
            $session->get('user-login')
6805
        );
6806
6807
        // Return
6808
        echo (string) prepareExchangedData(
6809
            array(
6810
                'error' => false,
6811
                'message' => '',
6812
            ),
6813
            'encode'
6814
        );
6815
6816
        break;
6817
6818
    /*
6819
     * CASE
6820
     * save_notification_status
6821
     */
6822
    case 'save_notification_status':
6823
        // Check KEY
6824
        if ($inputData['key'] !== $session->get('key')) {
6825
            echo (string) prepareExchangedData(
6826
                array(
6827
                    'error' => 'key_not_conform',
6828
                    'message' => $lang->get('key_is_not_correct'),
6829
                ),
6830
                'encode'
6831
            );
6832
            break;
6833
        }
6834
        // decrypt and retrieve data in JSON format
6835
        $dataReceived = prepareExchangedData(
6836
            $inputData['data'],
6837
            'decode'
6838
        );
6839
6840
        // prepare variables
6841
        $post_notification_status = (int) filter_var($dataReceived['notification_status'], FILTER_SANITIZE_NUMBER_INT);
6842
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
6843
6844
        DB::query(
6845
            'SELECT *
6846
            FROM ' . prefixTable('notification') . '
6847
            WHERE item_id = %i AND user_id = %i',
6848
            $inputData['itemId'],
6849
            $session->get('user-id')
6850
        );
6851
        if (DB::count() > 0) {
6852
            // Notification is set for this user on this item
6853
            if ((int) $post_notification_status === 0) {
6854
                // Remove the notification
6855
                DB::delete(
6856
                    prefixTable('notification'),
6857
                    'item_id = %i AND user_id = %i',
6858
                    $inputData['itemId'],
6859
                    $session->get('user-id')
6860
                );
6861
            }
6862
        } else {
6863
            // Notification is not set on this item
6864
            if ((int) $post_notification_status === 1) {
6865
                // Add the notification
6866
                DB::insert(
6867
                    prefixTable('notification'),
6868
                    array(
6869
                        'item_id' => $inputData['itemId'],
6870
                        'user_id' => (int) $session->get('user-id'),
6871
                    )
6872
                );
6873
            }
6874
        }
6875
6876
        $data = array(
6877
            'error' => false,
6878
            'message' => '',
6879
        );
6880
6881
        // send data
6882
        echo (string) prepareExchangedData(
6883
            $data,
6884
            'encode'
6885
        );
6886
6887
        break;
6888
6889
    /*
6890
     * CASE
6891
     * delete_uploaded_files_but_not_saved
6892
     */
6893
    case 'delete_uploaded_files_but_not_saved':
6894
        // Check KEY
6895
        if ($inputData['key'] !== $session->get('key')) {
6896
            echo (string) prepareExchangedData(
6897
                array(
6898
                    'error' => 'key_not_conform',
6899
                    'message' => $lang->get('key_is_not_correct'),
6900
                ),
6901
                'encode'
6902
            );
6903
            break;
6904
        }
6905
        // decrypt and retrieve data in JSON format
6906
        $dataReceived = prepareExchangedData(
6907
            $inputData['data'],
6908
            'decode'
6909
        );
6910
6911
        // prepare variables
6912
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
6913
6914
        // Delete non confirmed files for this item
6915
        // And related logs
6916
        $rows = DB::query(
6917
            'SELECT id, file AS filename
6918
            FROM ' . prefixTable('files') . '
6919
            WHERE id_item = %i AND confirmed = %i',
6920
            $inputData['itemId'],
6921
            0
6922
        );
6923
        foreach ($rows as $file) {
6924
            // Delete file in DB
6925
            DB::delete(
6926
                prefixTable('files'),
6927
                'id = %i',
6928
                $file['id']
6929
            );
6930
6931
            // Delete file on server
6932
            unlink($SETTINGS['path_to_upload_folder'] . '/' . TP_FILE_PREFIX . base64_decode($file['filename']));
6933
6934
            // Delete related logs
6935
            $logFile = DB::query(
6936
                'SELECT increment_id, raison
6937
                FROM ' . prefixTable('log_items') . '
6938
                WHERE id_item = %i AND id_user = %i AND action = %s AND raison LIKE "at_add_file :%"',
6939
                $inputData['itemId'],
6940
                $session->get('user-id'),
6941
                'at_modification'
6942
            );
6943
            foreach ($logFile as $log) {
6944
                $tmp = explode(':', $log['raison']);
6945
                if (count($tmp) === 3 && (int) $tmp[2] === (int) $file['id']) {
6946
                    DB::delete(
6947
                        prefixTable('log_items'),
6948
                        'increment_id = %i',
6949
                        $log['increment_id']
6950
                    );
6951
                }
6952
            }
6953
        }
6954
6955
        $data = array(
6956
            'error' => false,
6957
            'message' => '',
6958
        );
6959
6960
        // send data
6961
        echo (string) prepareExchangedData(
6962
            $data,
6963
            'encode'
6964
        );
6965
6966
        break;
6967
6968
        /*
6969
    * CASE
6970
    * confirm_attachments
6971
    */
6972
    case 'confirm_attachments':
6973
        // Check KEY
6974
        if ($inputData['key'] !== $session->get('key')) {
6975
            echo (string) prepareExchangedData(
6976
                array(
6977
                    'error' => 'key_not_conform',
6978
                    'message' => $lang->get('key_is_not_correct'),
6979
                ),
6980
                'encode'
6981
            );
6982
            break;
6983
        }
6984
        // decrypt and retrieve data in JSON format
6985
        $dataReceived = prepareExchangedData(
6986
            $inputData['data'],
6987
            'decode'
6988
        );
6989
6990
        // prepare variables
6991
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
6992
6993
        // Confirm attachments
6994
        $rows = DB::query(
6995
            'SELECT id, file AS filename
6996
            FROM ' . prefixTable('files') . '
6997
            WHERE id_item = %i AND confirmed = %i',
6998
            $inputData['itemId'],
6999
            0
7000
        );
7001
        foreach ($rows as $file) {
7002
            DB::update(
7003
                prefixTable('files'),
7004
                array(
7005
                    'confirmed' => 1,
7006
                ),
7007
                'id_item = %i',
7008
                $inputData['itemId']
7009
            );
7010
        }
7011
7012
        $data = array(
7013
            'error' => false,
7014
            'message' => '',
7015
        );
7016
7017
        // send data
7018
        echo (string) prepareExchangedData(
7019
            $data,
7020
            'encode'
7021
        );
7022
7023
        break;
7024
7025
    /*
7026
    * CASE
7027
    * check_current_access_rights
7028
    */
7029
    case 'check_current_access_rights':
7030
        // Check KEY
7031
        if ($inputData['key'] !== $session->get('key')) {
7032
            echo (string) prepareExchangedData(
7033
                array(
7034
                    'error' => 'key_not_conform',
7035
                    'message' => $lang->get('key_is_not_correct'),
7036
                ),
7037
                'encode'
7038
            );
7039
            break;
7040
        }
7041
7042
        // Init
7043
        $editionLock = false;
7044
7045
        // decrypt and retrieve data in JSON format
7046
        $dataReceived = prepareExchangedData(
7047
            $inputData['data'],
7048
            'decode'
7049
        );
7050
7051
        // Check rights
7052
        $data = getCurrentAccessRights(
7053
            (int) filter_var($dataReceived['userId'], FILTER_SANITIZE_NUMBER_INT),
7054
            (int) filter_var($dataReceived['itemId'], FILTER_SANITIZE_NUMBER_INT),
7055
            (int) filter_var($dataReceived['treeId'], FILTER_SANITIZE_NUMBER_INT),
7056
            (string) filter_var($dataReceived['action'], FILTER_SANITIZE_SPECIAL_CHARS),
7057
        );
7058
7059
        // send data
7060
        echo (string) prepareExchangedData(
7061
            $data,
7062
            'encode'
7063
        );
7064
7065
        break;
7066
7067
        /*
7068
        * CASE
7069
        * items_delete
7070
        */
7071
        case 'items_delete':
7072
            // Check KEY
7073
            if ($inputData['key'] !== $session->get('key')) {
7074
                echo (string) prepareExchangedData(
7075
                    array(
7076
                        'error' => 'key_not_conform',
7077
                        'message' => $lang->get('key_is_not_correct'),
7078
                    ),
7079
                    'encode'
7080
                );
7081
                break;
7082
            }
7083
7084
            if ($session->get('user-read_only') === 1) {
7085
                echo (string) prepareExchangedData(
7086
                    array(
7087
                        'error' => true,
7088
                        'message' => $lang->get('error_not_allowed_to'),
7089
                    ),
7090
                    'encode'
7091
                );
7092
                break;
7093
            }
7094
7095
            // decrypt and retrieve data in JSON format
7096
            $dataReceived = prepareExchangedData(
7097
                $inputData['data'],
7098
                'decode'
7099
            );
7100
            
7101
            // Prepare POST variables
7102
            $selectedItemIdsJson = $dataReceived['selectedItemIds'] ?? '[]';
7103
            $selectedItemIds = json_decode($selectedItemIdsJson, true) ?: array();
7104
            $selectedItemIds = array_map(function($id) {
7105
                return filter_var($id, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
7106
            }, $selectedItemIds);
7107
7108
            // Initialiser les variables de gestion d'erreurs
7109
            $successfulDeletions = array();
7110
            $failedDeletions = array();
7111
7112
            foreach( $selectedItemIds as $itemId) {
7113
                // Check that user can access this item
7114
                $granted = accessToItemIsGranted((int) $itemId, $SETTINGS);
7115
                if ($granted !== true) {
7116
                    $failedDeletions[$itemId] = $granted;
7117
                    continue; // Passer à l'item suivant
7118
                }
7119
7120
                // Load item data
7121
                $data = DB::queryFirstRow(
7122
                    'SELECT id_tree, id, label
7123
                    FROM ' . prefixTable('items') . '
7124
                    WHERE id = %i',
7125
                    $itemId
7126
                );
7127
                if ($data === null) {
7128
                    $failedDeletions[$itemId] = $lang->get('error_item_not_found');
7129
                    continue; // Passer à l'item suivant
7130
                }
7131
                $itemLabel = $data['label'];
7132
                $itemTreeId = (int) $data['id_tree'];
7133
7134
                // Check that user can delete on this folder
7135
                $checkRights = getCurrentAccessRights(
7136
                    $session->get('user-id'),
7137
                    (int) $itemId,
7138
                    $itemTreeId,
7139
                );
7140
7141
                if ($checkRights['error'] || !$checkRights['delete']) {
7142
                    echo (string) prepareExchangedData(
7143
                        array(
7144
                            'error' => true,
7145
                            'message' => $lang->get('error_not_allowed_to'),
7146
                        ),
7147
                        'encode'
7148
                    );
7149
                }
7150
7151
                // delete item consists in disabling it
7152
                DB::update(
7153
                    prefixTable('items'),
7154
                    array(
7155
                        'inactif' => '1',
7156
                        'deleted_at' => time(),
7157
                    ),
7158
                    'id = %i',
7159
                    $itemId
7160
                );
7161
7162
                // log
7163
                logItems(
7164
                    $SETTINGS,
7165
                    (int) $itemId,
7166
                    $itemLabel,
7167
                    $session->get('user-id'),
7168
                    'at_delete',
7169
                    $session->get('user-login')
7170
                );
7171
7172
                // Update CACHE table
7173
                updateCacheTable('delete_value', (int) $itemId);
7174
7175
                // Ajouter l'item à la liste des succès
7176
                $successfulDeletions[] = $itemId;
7177
            }
7178
7179
            // Préparer la réponse
7180
            $response = array(
7181
                'successfulDeletions' => $successfulDeletions,
7182
                'failedDeletions' => $failedDeletions,
7183
                'error' => !empty($failedDeletions), // Indiquer s'il y a eu des erreurs
7184
                'message' => !empty($failedDeletions) ? $lang->get('some_items_failed_to_delete') : $lang->get('all_items_deleted_successfully')
7185
            );
7186
7187
            // send data
7188
            echo (string) prepareExchangedData(
7189
                $response,
7190
                'encode'
7191
            );
7192
7193
            break;
7194
}
7195
7196
// Build the QUERY in case of GET
7197
if (isset($inputData['getType'])) {
7198
    switch ($inputData['getType']) {
7199
        /*
7200
        * CASE
7201
        * Autocomplet for TAGS
7202
        */
7203
        case 'autocomplete_tags':
7204
            // Get a list off all existing TAGS
7205
            $listOfTags = '';
7206
            $rows = DB::query('SELECT tag FROM ' . prefixTable('tags') . ' WHERE tag LIKE %ss GROUP BY tag', $inputData['getTerm']);
7207
            foreach ($rows as $record) {
7208
                if (empty($listOfTags)) {
7209
                    $listOfTags = '"' . $record['tag'] . '"';
7210
                } else {
7211
                    $listOfTags .= ', "' . $record['tag'] . '"';
7212
                }
7213
            }
7214
            echo '[' . $listOfTags . ']';
7215
            break;
7216
    }
7217
}
7218
7219
/**
7220
 * Identify if this group authorize creation of item without the complexit level reached
7221
 *
7222
 * @param int $groupe ID for group
7223
 *
7224
 * @return array list of roles
7225
*/
7226
function recupDroitCreationSansComplexite($groupe)
7227
{
7228
    $data = DB::queryFirstRow(
7229
        'SELECT bloquer_creation, bloquer_modification, personal_folder
7230
        FROM ' . prefixTable('nested_tree') . ' WHERE id = %i',
7231
        $groupe
7232
    );
7233
    // Check if it's in a personal folder. If yes, then force complexity overhead.
7234
    if ($data !== null && (int) $data['personal_folder'] === 1) {
7235
        return array(
7236
            'bloquer_modification_complexite' => 1,
7237
            'bloquer_creation_complexite' => 1,
7238
        );
7239
    }
7240
7241
    return array(
7242
        'bloquer_modification_complexite' => $data !== null ? (int) $data['bloquer_modification'] : 0,
7243
        'bloquer_creation_complexite' => $data !== null ? (int) $data['bloquer_creation'] : 0,
7244
    );
7245
}
7246
7247
/**
7248
 * Permits to identify what icon to display depending on file extension.
7249
 *
7250
 * @param string $ext extension
7251
 *
7252
 * @return string
7253
 */
7254
function fileFormatImage($ext)
7255
{
7256
    if (in_array($ext, TP_OFFICE_FILE_EXT)) {
7257
        $image = 'fas fa-file-word';
7258
    } elseif ($ext === 'pdf') {
7259
        $image = 'fas fa-file-pdf';
7260
    } elseif (in_array($ext, TP_IMAGE_FILE_EXT)) {
7261
        $image = 'fas fa-file-image';
7262
    } elseif ($ext === 'txt') {
7263
        $image = 'fas fa-file-alt';
7264
    } else {
7265
        $image = 'fas fa-file';
7266
    }
7267
7268
    return $image;
7269
}
7270
7271
7272
/**
7273
 * Get rights of user on specific folder/item.
7274
 * 
7275
 * @param int $userId ID of user.
7276
 * @param int $itemId ID of item.
7277
 * @param int $treeId ID of folder.
7278
 * @param string $action Type of action (e.g., 'edit', 'delete').
7279
 * 
7280
 * @return array with access rights.
7281
 */
7282
function getCurrentAccessRights(int $userId, int $itemId, int $treeId, string $action = ''): array
7283
{
7284
    $session = SessionManager::getSession();
7285
    
7286
    // Check if the item is locked and whether the current user can edit it
7287
    $editionLock = isItemLocked($itemId, $session, $userId, $action);
7288
7289
    // Check if user is allowed restriction list of users
7290
    // If the item is restricted to specific users, check if the current user is in that list
7291
    if (getItemRestrictedUsersList($itemId, $userId) === false) {
7292
        return getAccessResponse(false, false, false, false);
7293
    }
7294
7295
    // Check if the item is being processed by another user
7296
    if (isProcessOnGoing($itemId)) {
7297
        return getAccessResponse(false, true, false, false);
7298
    }
7299
7300
    // Retrieve user's visible folders from the cache_tree table
7301
    $visibleFolders = getUserVisibleFolders($userId);
7302
7303
    // Check if the folder is in the user's read-only list
7304
    if (in_array($treeId, $session->get('user-read_only_folders'))) {
7305
        return getAccessResponse(false, true, false, false);
7306
    }
7307
    
7308
    // Check if the folder is in the user's allowed folders list defined by admin
7309
    if (in_array($treeId, $session->get('user-allowed_folders_by_definition'))) {
7310
        return getAccessResponse(false, true, true, true);
7311
    }
7312
7313
    // Check if the folder is personal to the user
7314
    foreach ($visibleFolders as $folder) {
7315
        if ($folder['id'] == $treeId && (int) $folder['perso'] === 1) {
7316
            return getAccessResponse(false, true, true, true);
7317
        }
7318
    }
7319
7320
    // Determine the user's access rights based on their roles for this folder
7321
    [$edit, $delete] = getRoleBasedAccess($session, $treeId);
7322
7323
    // Log access rights information if logging is enabled
7324
    if (LOG_TO_SERVER === true) {
7325
        error_log("TEAMPASS - Folder: $treeId - User: $userId - edit: $edit - delete: $delete");
7326
    }
7327
7328
    return getAccessResponse(false, true, $edit, $delete, $editionLock);
7329
}
7330
7331
/**
7332
* Get user access restriction response.
7333
*
7334
* @param int $itemId The ID of the item to check
7335
* @param int $userId The ID of the current user
7336
*
7337
* @return bool Restricted to user response.
7338
*/
7339
function getItemRestrictedUsersList($itemId, $userId)
7340
{
7341
    // Get item date
7342
    $itemRestrictedUsersList = DB::queryFirstRow(
7343
        'SELECT restricted_to
7344
         FROM ' . prefixTable('items') . '
7345
         WHERE id = %i',
7346
        $itemId
7347
    );
7348
    // Check if user is in the list of restriction if the item is restricted
7349
    if (empty($itemRestrictedUsersList['restricted_to']) === false) {
7350
        $restrictedUsers = array_map('intval', explode(';', $itemRestrictedUsersList['restricted_to']));
7351
        if (!in_array($userId, $restrictedUsers, true)) {
7352
            return false;
7353
        }
7354
    }    
7355
7356
    return true;
7357
}
7358
7359
/**
7360
 * Checks if the item is locked by another user or if there is an ongoing encryption process.
7361
 * If the item is locked, the function determines if the lock has expired or not.
7362
 * 
7363
 * @param int $itemId The ID of the item to check
7364
 * @param object $session The current session object
7365
 * @param int $userId The ID of the current user
7366
 * @param string $actionType The type of action being performed (e.g., 'edit', 'delete')
7367
 * 
7368
 * @return array True if the item is locked, false otherwise
7369
 */
7370
function isItemLocked(int $itemId, $session, int $userId, string $actionType = ''): array
7371
{
7372
    global $SETTINGS;
7373
7374
    $now = time();
7375
    $editionLocks = DB::query(
7376
        'SELECT timestamp, user_id, increment_id
7377
         FROM ' . prefixTable('items_edition') . '
7378
         WHERE item_id = %i 
7379
         ORDER BY increment_id DESC',
7380
        $itemId
7381
    );
7382
7383
    // Check if there are any locks for this item
7384
    if (count($editionLocks) === 0 && $actionType === 'edit') {
7385
        // If no locks exist and the action is 'edit', create a new lock
7386
        createEditionLock($itemId, $userId, $now);
7387
        return [
7388
            'status' => false,
7389
        ];
7390
    }
7391
7392
    // Check if the last lock is older than the defined period
7393
    $lastLock = $editionLocks[0];
7394
7395
    // If the lock is for the current user, update the timestamp
7396
    if ((int) $lastLock['user_id'] === $userId) {
7397
        DB::update(
7398
            prefixTable('items_edition'),
7399
            ['timestamp' => $now],
7400
            'increment_id = %i',
7401
            $lastLock['increment_id']
7402
        );
7403
        return [
7404
            'status' => false,
7405
        ];
7406
    }
7407
7408
    // Calculate the delay for the lock
7409
    $delay = isset($SETTINGS['delay_item_edition']) && $SETTINGS['delay_item_edition'] > 0
7410
        ? $SETTINGS['delay_item_edition'] * 60
7411
        : EDITION_LOCK_PERIOD;
7412
7413
    // Calculate the elapsed time since the last lock
7414
    $elapsed = abs($now - (int) $lastLock['timestamp']);
7415
7416
    // Check if the lock has expired
7417
    if ($elapsed > $delay) {
7418
        // Delete all edition locks for this item
7419
        DB::delete(prefixTable('items_edition'), 'item_id = %i', $itemId);
7420
7421
        // Delete related background tasks if any
7422
        $task = DB::queryFirstRow(
7423
            'SELECT increment_id FROM ' . prefixTable('background_tasks') . '
7424
             WHERE item_id = %i AND finished_at = ""',
7425
            $itemId
7426
        );
7427
7428
        // If a task is found, delete its related tasks
7429
        if (!empty($task)) {
7430
            deleteProcessAndRelatedTasks((int) $task['increment_id']);
7431
        }
7432
7433
        // Check if encryption process is still running
7434
        DB::queryFirstRow(
7435
            'SELECT JSON_EXTRACT(arguments, "$.all_users_except_id") AS all_users_except_id
7436
             FROM ' . prefixTable('background_tasks') . '
7437
             WHERE item_id = %i AND finished_at = ""
7438
             ORDER BY increment_id DESC',
7439
            $itemId
7440
        );
7441
7442
        // If encryption process is not running, delete the lock        
7443
        if (DB::count() === 0) {
7444
            DB::update(
7445
                prefixTable('items_edition'),
7446
                ['timestamp' => $now],
7447
                'item_id = %i AND user_id = %i',
7448
                $itemId,
7449
                $userId
7450
            );
7451
            return [
7452
                'status' => false,
7453
            ];
7454
        }
7455
7456
        return [
7457
            'status' => true,   // Encryption in progress
7458
            'delay' => $delay - $elapsed, // Time remaining before the lock expires
7459
        ];
7460
    }
7461
7462
    // Lock still valid and owned by another user
7463
    return [
7464
        'status' => true,
7465
        'delay' => $delay - $elapsed, // Time remaining before the lock expires
7466
    ];
7467
}
7468
7469
/**
7470
 * Creates an edition lock for a given item and user
7471
 * 
7472
 * @param int $itemId The ID of the item to lock
7473
 * @param int $userId The ID of the user who is locking the item
7474
 * @param int $timestamp The timestamp of the lock
7475
 * 
7476
 * @return void
7477
 */
7478
function createEditionLock(int $itemId, int $userId, int $timestamp): void
7479
{
7480
    DB::insert(
7481
        prefixTable('items_edition'),
7482
        [
7483
            'timestamp' => $timestamp,
7484
            'item_id' => $itemId,
7485
            'user_id' => $userId,
7486
        ]
7487
    );
7488
}
7489
7490
7491
/**
7492
 * Checks if there is an ongoing background encryption process for the given item.
7493
 * 
7494
 * @param int $itemId The ID of the item to check
7495
 * 
7496
 * @return bool True if an ongoing process is found, false otherwise
7497
 */
7498
function isProcessOnGoing(int $itemId): bool
7499
{
7500
    // Check if there's an ongoing background encryption process for the item
7501
    $ongoingProcess = DB::queryFirstRow(
7502
        'SELECT 1 FROM ' . prefixTable('background_tasks') . ' WHERE item_id = %i AND finished_at = "" LIMIT 1', 
7503
        $itemId
7504
    );
7505
7506
    // Return true if an ongoing process is found, otherwise false
7507
    return $ongoingProcess ? true : false;
7508
}
7509
7510
/**
7511
 * Retrieves the list of visible folders for a specific user from the cache_tree table.
7512
 * 
7513
 * @param int $userId The ID of the user
7514
 * 
7515
 * @return array An array of visible folders for the user
7516
 */
7517
function getUserVisibleFolders(int $userId): array
7518
{
7519
    // Query to retrieve visible folders for the user
7520
    $data = DB::queryFirstRow('SELECT visible_folders FROM ' . prefixTable('cache_tree') . ' WHERE user_id = %i', $userId);
7521
    
7522
    // Decode JSON data into an array; return an empty array if the data is invalid
7523
    return json_decode($data['visible_folders'], true) ?? [];
7524
}
7525
7526
/**
7527
 * Determines access rights (edit/delete) based on the user's roles for a given folder.
7528
 * It checks the roles_values table to see the permissions defined for each role.
7529
 * 
7530
 * @param object $session The current session object
7531
 * @param int $treeId The ID of the folder to check access rights for
7532
 * 
7533
 * @return array An array containing edit and delete access rights [edit, delete]
7534
 */
7535
function getRoleBasedAccess($session, int $treeId): array
7536
{
7537
    $edit = $delete = false;
7538
    
7539
    // Retrieve all role IDs assigned to the user
7540
    $roles = array_column($session->get('system-array_roles'), 'id');
7541
7542
    // Query the access rights for the given roles and folder
7543
    $accessTypes = DB::queryFirstColumn(
7544
        'SELECT DISTINCT type FROM ' . prefixTable('roles_values') . ' WHERE role_id IN %ls AND folder_id = %i', 
7545
        $roles, 
7546
        $treeId
7547
    );
7548
    // Values ​​to be checked
7549
    $check_value = 'W';
7550
    // Values ​​to be deleted
7551
    $delete_value = 'R';
7552
    
7553
    // Check if $check_value exists
7554
    if (in_array($check_value, $accessTypes)) {
7555
        // Find the index of $delete_value in the array
7556
        $key = array_search($delete_value, $accessTypes);
7557
7558
        // If the value is found, delete
7559
        if ($key !== false) {
7560
            unset($accessTypes[$key]);
7561
        }
7562
    }
7563
    // Determine access rights based on the retrieved types
7564
    foreach ($accessTypes as $access) {
7565
        switch ($access) {
7566
            case 'ND': // No Delete
7567
                $edit = true;
7568
                $delete = false;
7569
                break;
7570
            case 'NE': // No Edit
7571
                $edit = false;
7572
                $delete = true;
7573
                break;
7574
            case 'NDNE':
7575
            case 'R': // Read only
7576
                $edit = $delete = false;
7577
                break;
7578
            case 'W': // Write access
7579
                $edit = $delete = true;
7580
                break;
7581
        }
7582
    }
7583
    return [$edit, $delete];
7584
}
7585
7586
/**
7587
 * Constructs the final access response array with the given parameters.
7588
 * 
7589
 * @param bool $error Indicates if there was an error
7590
 * @param bool $access Indicates if the user has access
7591
 * @param bool $edit Indicates if the user has edit rights
7592
 * @param bool $delete Indicates if the user has delete rights
7593
 * @param bool $editionLocked Indicates if the edition is locked
7594
 * 
7595
 * @return array An array containing the access rights information
7596
 */
7597
function getAccessResponse(bool $error, bool $access, bool $edit, bool $delete, array $editionLocked = []): array
7598
{
7599
    return [
7600
        'error' => $error,
7601
        'access' => $access,
7602
        'edit' => $edit,
7603
        'delete' => $delete,
7604
        'edition_locked' => $editionLocked['status'] ?? false,
7605
        'edition_locked_delay' => $editionLocked['delay'] ?? null,
7606
    ];
7607
}
7608