Passed
Pull Request — master (#4682)
by Nils
06:11
created

isProcessOnGoing()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 10
rs 10
c 0
b 0
f 0
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) {
0 ignored issues
show
Bug introduced by
$dataReceived of type string is incompatible with the type Countable|array expected by parameter $value of count(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

877
        if (is_array($dataReceived) === false || count(/** @scrutinizer ignore-type */ $dataReceived) === 0) {
Loading history...
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
        // If source and destination folder are different -> move item
1056
        if ((int) $dataItem['id_tree'] !== $inputData['folderId']) {
1057
            // Check that user can delete on old folder
1058
            $checkRights = getCurrentAccessRights(
1059
                $session->get('user-id'),
1060
                $inputData['itemId'],
1061
                (int) $dataItem['id_tree'],
1062
            );
1063
1064
            if ($checkRights['error'] || !$checkRights['delete']) {
1065
                echo (string) prepareExchangedData(
1066
                    array(
1067
                        'error' => true,
1068
                        'message' => $lang->get('error_not_allowed_to'),
1069
                    ),
1070
                    'encode'
1071
                );
1072
                break;
1073
            }
1074
        }
1075
1076
        // Always check that user can write on requested folder
1077
        $checkRights = getCurrentAccessRights(
1078
            $session->get('user-id'),
1079
            $inputData['itemId'],
1080
            $inputData['folderId'],
1081
        );
1082
1083
        if ($checkRights['error'] || !$checkRights['edit']) {
1084
            echo (string) prepareExchangedData(
1085
                array(
1086
                    'error' => true,
1087
                    'message' => $lang->get('error_not_allowed_to'),
1088
                ),
1089
                'encode'
1090
            );
1091
            break;
1092
        }
1093
1094
        // Does the user has the sharekey
1095
        //db::debugmode(true);
1096
        DB::query(
1097
            'SELECT *
1098
            FROM ' . prefixTable('sharekeys_items') . '
1099
            WHERE object_id = %i AND user_id = %s',
1100
            $inputData['itemId'],
1101
            $session->get('user-id')
1102
        );
1103
        if (DB::count() === 0) {
1104
            if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) {
1105
                error_log('TEAMPASS | user '.$session->get('user-id').' has no sharekey for item '.$inputData['itemId']);
1106
            }
1107
            echo (string) prepareExchangedData(
1108
                array(
1109
                    'error' => true,
1110
                    'message' => $lang->get('error_not_allowed_to'),
1111
                ),
1112
                'encode'
1113
            );
1114
            break;
1115
        }
1116
1117
        // check that actual user can access this item
1118
        $restrictionActive = true;
1119
        $restrictedTo = is_null($dataItem['restricted_to']) === false ? array_filter(explode(';', $dataItem['restricted_to'])) : [];
1120
        if (in_array($session->get('user-id'), $restrictedTo) === true) {
1121
            $restrictionActive = false;
1122
        }
1123
        if (empty($dataItem['restricted_to']) === true) {
1124
            $restrictionActive = false;
1125
        }
1126
1127
        // DO init        
1128
        $listOfRestricted = $oldRestrictionList = '';
1129
        $arrayOfUsersRestriction = [];
1130
        $arrayOfUsersIdRestriction = [];
1131
        $diffUsersRestiction = [];
1132
        $diffRolesRestiction = [];
1133
        $arrayOfRestrictionRoles = [];
1134
1135
        $session__list_restricted_folders_for_items = $session->get('system-list_restricted_folders_for_items') ?? [];
1136
        if ((in_array($dataItem['id_tree'], $session->get('user-accessible_folders')) === true
1137
                && ((int) $dataItem['perso'] === 0
1138
                    || ((int) $dataItem['perso'] === 1
1139
                        //&& (int) $session->get('user-id') === (int) $dataItem['id_user']))
1140
                    ))
1141
                && $restrictionActive === false)
1142
            || (isset($SETTINGS['anyone_can_modify']) === true
1143
                && (int) $SETTINGS['anyone_can_modify'] === 1
1144
                && (int) $dataItem['anyone_can_modify'] === 1
1145
                && (in_array($dataItem['id_tree'], $session->get('user-accessible_folders')) === true
1146
                    || (int) $session->get('user-admin') === 1)
1147
                && $restrictionActive === false)
1148
            || (null !== $inputData['folderId']
1149
                && count($session__list_restricted_folders_for_items) > 0
1150
                && in_array($inputData['id'], $session__list_restricted_folders_for_items[$inputData['folderId']]) === true
1151
                && $restrictionActive === false)
1152
        ) {
1153
            // Get existing values
1154
            $data = DB::queryFirstRow(
1155
                '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, 
1156
                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,
1157
                u.login as user_login, u.email as user_email
1158
                FROM ' . prefixTable('items') . ' as i
1159
                INNER JOIN ' . prefixTable('log_items') . ' as l ON (i.id=l.id_item)
1160
                INNER JOIN ' . prefixTable('users') . ' as u ON (u.id=l.id_user)
1161
                WHERE i.id=%i',
1162
                $inputData['itemId']
1163
            );
1164
1165
            // Should we log a password change?
1166
            $userKey = DB::queryFirstRow(
1167
                'SELECT share_key
1168
                FROM ' . prefixTable('sharekeys_items') . '
1169
                WHERE user_id = %i AND object_id = %i',
1170
                $session->get('user-id'),
1171
                $inputData['itemId']
1172
            );
1173
            if (DB::count() === 0 || empty($data['pw']) === true) {
1174
                // No share key found
1175
                $pw = '';
1176
            } else {
1177
                $pw = base64_decode(doDataDecryption(
1178
                    $data['pw'],
1179
                    decryptUserObjectKey(
1180
                        $userKey['share_key'],
1181
                        $session->get('user-private_key')
1182
                    )
1183
                ));
1184
            }
1185
1186
            if ($post_password !== $pw) {
1187
                // Encrypt previous pw
1188
                $previousValue = cryption(
1189
                    $pw,
1190
                    '',
1191
                    'encrypt'
1192
                );
1193
1194
                // log the change of PW
1195
                logItems(
1196
                    $SETTINGS,
1197
                    (int) $inputData['itemId'],
1198
                    $inputData['label'],
1199
                    $session->get('user-id'),
1200
                    'at_modification',
1201
                    $session->get('user-login'),
1202
                    'at_pw',
1203
                    TP_ENCRYPTION_NAME,
1204
                    NULL,
1205
                    isset($previousValue['string']) === true ? $previousValue['string'] : '',
1206
                );
1207
            }
1208
            
1209
            // encrypt PW on if it has changed, or if it is empty
1210
            if (
1211
                (
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($session->has('user-cre... $post_password !== $pw, Probably Intended Meaning: $session->has('user-crea...$post_password !== $pw)
Loading history...
1212
                    (
1213
                        $session->has('user-create_item_without_password')
1214
                        && (int) $session->get('user-create_item_without_password') !== 1
1215
                    )
1216
                    || !empty($post_password)
1217
                )
1218
                && $post_password !== $pw
1219
            ) {
1220
                //-----
1221
                // NEW ENCRYPTION
1222
                $cryptedStuff = doDataEncryption($post_password);
1223
                $encrypted_password = $cryptedStuff['encrypted'];
1224
                $encrypted_password_key = $cryptedStuff['objectKey'];
1225
1226
                // Create sharekeys for users
1227
                storeUsersShareKey(
1228
                    prefixTable('sharekeys_items'),
1229
                    (int) $post_folder_is_personal,
1230
                    (int) $inputData['itemId'],
1231
                    $encrypted_password_key,
1232
                    true,   // only for the item creator
1233
                    true,   // delete all
1234
                );
1235
1236
                // Create a task to create sharekeys for users
1237
                if (WIP=== true) error_log('createTaskForItem - new password for this item - '.$post_password ." -- ". $pw);
1238
                $tasksToBePerformed = ['item_password'];
1239
                $encryptionTaskIsRequested = true;
1240
            } else {
1241
                $encrypted_password = $data['pw'];
1242
            }
1243
1244
            // ---Manage tags
1245
            // Get list of tags
1246
            $itemTags = DB::queryFirstColumn(
1247
                'SELECT tag
1248
                FROM ' . prefixTable('tags') . '
1249
                WHERE item_id = %i',
1250
                $inputData['itemId']
1251
            );
1252
1253
            // deleting existing tags for this item
1254
            DB::delete(
1255
                prefixTable('tags'),
1256
                'item_id = %i',
1257
                $inputData['itemId']
1258
            );
1259
1260
            // Add new tags
1261
            $postArrayTags = [];
1262
            if (empty($post_tags) === false) {
1263
                $postArrayTags = explode(' ', $post_tags);
1264
                foreach ($postArrayTags as $tag) {
1265
                    if (empty($tag) === false) {
1266
                    // save in DB
1267
                        DB::insert(
1268
                            prefixTable('tags'),
1269
                            array(
1270
                                'item_id' => $inputData['itemId'],
1271
                                'tag' => strtolower($tag),
1272
                            )
1273
                        );
1274
                    }
1275
                }
1276
            }
1277
1278
            // Store LOG
1279
            if (count(array_diff($postArrayTags, $itemTags)) > 0) {
1280
                // Store updates performed
1281
                array_push(
1282
                    $arrayOfChanges,
1283
                    'tags'
1284
                );
1285
1286
                // update LOG
1287
                logItems(
1288
                    $SETTINGS,
1289
                    (int) $inputData['itemId'],
1290
                    $inputData['label'],
1291
                    $session->get('user-id'),
1292
                    'at_modification',
1293
                    $session->get('user-login'),
1294
                    'at_tag : ' . implode(' ', $itemTags) . ' => ' . $post_tags
1295
                );
1296
            }
1297
1298
            // update item
1299
            DB::update(
1300
                prefixTable('items'),
1301
                array(
1302
                    'label' => $inputData['label'],
1303
                    'description' => $post_description,
1304
                    'pw' => $encrypted_password,
1305
                    'pw_len' => $strlen_post_password,
1306
                    'email' => $post_email,
1307
                    'login' => $post_login,
1308
                    'url' => $post_url,
1309
                    'id_tree' => $inputData['folderId'],
1310
                    'restricted_to' => empty($post_restricted_to) === true || count($post_restricted_to) === 0 ? '' : implode(';', $post_restricted_to),
1311
                    'anyone_can_modify' => (int) $post_anyone_can_modify,
1312
                    'complexity_level' => (int) $post_complexity_level,
1313
                    'encryption_type' => TP_ENCRYPTION_NAME,
1314
                    'perso' => in_array($inputData['folderId'], $session->get('user-personal_folders')) === true ? 1 : 0,
1315
                    'fa_icon' => $post_fa_icon,
1316
                    'updated_at' => time(),
1317
                ),
1318
                'id=%i',
1319
                $inputData['itemId']
1320
            );
1321
1322
            // update fields
1323
            if (
1324
                isset($SETTINGS['item_extra_fields']) === true
1325
                && (int) $SETTINGS['item_extra_fields'] === 1
1326
                && empty($post_fields) === false
1327
            ) {                
1328
                foreach ($post_fields as $field) {
1329
                    if (empty($field['value']) === false) {
1330
                        $dataTmpCat = DB::queryFirstRow(
1331
                            'SELECT c.id AS id, c.title AS title, i.data AS data, i.data_iv AS data_iv,
1332
                            i.encryption_type AS encryption_type, c.encrypted_data AS encrypted_data,
1333
                            c.masked AS masked, i.id AS field_item_id
1334
                            FROM ' . prefixTable('categories_items') . ' AS i
1335
                            INNER JOIN ' . prefixTable('categories') . ' AS c ON (i.field_id=c.id)
1336
                            WHERE i.field_id = %i AND i.item_id = %i',
1337
                            $field['id'],
1338
                            $inputData['itemId']
1339
                        );
1340
                        $cryptedStuff = [];
1341
                        $encryptedFieldIsChanged = false;
1342
1343
                        // store Field text in DB
1344
                        if (DB::count() === 0) {
1345
                            // The data for this field doesn't exist
1346
                            // It has to be added
1347
1348
                            // Perform new query
1349
                            $dataTmpCat = DB::queryFirstRow(
1350
                                'SELECT id, title, encrypted_data, masked
1351
                                FROM ' . prefixTable('categories') . '
1352
                                WHERE id = %i',
1353
                                $field['id']
1354
                            );
1355
1356
                            // store field text
1357
                            DB::insert(
1358
                                prefixTable('categories_items'),
1359
                                array(
1360
                                    'item_id' => $inputData['itemId'],
1361
                                    'field_id' => $field['id'],
1362
                                    'data' => $field['value'],
1363
                                    'data_iv' => '',
1364
                                    'encryption_type' => 'not_set',
1365
                                )
1366
                            );
1367
1368
                            $newId = DB::insertId();
1369
                            $dataTmpCat['field_item_id'] = $newId;
1370
1371
                            // Should we encrypt the data
1372
                            if ((int) $dataTmpCat['encrypted_data'] === 1) {
1373
                                $cryptedStuff = doDataEncryption($field['value']);
1374
1375
                                // Create sharekeys for users
1376
                                storeUsersShareKey(
1377
                                    prefixTable('sharekeys_fields'),
1378
                                    (int) $post_folder_is_personal,
1379
                                    (int) $newId,
1380
                                    $cryptedStuff['objectKey'],
1381
                                    true,   // only for the item creator
1382
                                    true,   // delete all
1383
                                );
1384
1385
                                // update value
1386
                                DB::update(
1387
                                    prefixTable('categories_items'),
1388
                                    array(
1389
                                        'data' => $cryptedStuff['encrypted'],
1390
                                        'data_iv' => '',
1391
                                        'encryption_type' => TP_ENCRYPTION_NAME,
1392
                                    ),
1393
                                    'id = %i',
1394
                                    $newId
1395
                                );
1396
1397
                                array_push(
1398
                                    $tasksToBePerformed,
1399
                                    'item_field'
1400
                                );
1401
                                $encryptedFieldIsChanged = true;
1402
                            } else {
1403
                                // update value
1404
                                DB::update(
1405
                                    prefixTable('categories_items'),
1406
                                    array(
1407
                                        'data' => $field['value'],
1408
                                        'data_iv' => '',
1409
                                        'encryption_type' => 'not_set',
1410
                                    ),
1411
                                    'id = %i',
1412
                                    $newId
1413
                                );
1414
                            }
1415
1416
                            // Store updates performed
1417
                            array_push(
1418
                                $arrayOfChanges,
1419
                                $dataTmpCat['title']
1420
                            );
1421
1422
                            // update LOG
1423
                            logItems(
1424
                                $SETTINGS,
1425
                                (int) $inputData['itemId'],
1426
                                $inputData['label'],
1427
                                $session->get('user-id'),
1428
                                'at_modification',
1429
                                $session->get('user-login'),
1430
                                'at_field : ' . $dataTmpCat['title'] . ' : ' . $field['value']
1431
                            );
1432
                        } else {
1433
                            // Case where the field already exists
1434
                            // compare the old and new value
1435
                            if ($dataTmpCat['encryption_type'] !== 'not_set') {
1436
                                // Get user sharekey for this field
1437
                                $userKey = DB::queryFirstRow(
1438
                                    'SELECT share_key
1439
                                    FROM ' . prefixTable('sharekeys_fields') . '
1440
                                    WHERE user_id = %i AND object_id = %i',
1441
                                    $session->get('user-id'),
1442
                                    $dataTmpCat['field_item_id']
1443
                                );
1444
1445
                                // Decrypt the current value
1446
                                if (DB::count() > 0) {
1447
                                    $oldVal = base64_decode(doDataDecryption(
1448
                                        $dataTmpCat['data'],
1449
                                        decryptUserObjectKey(
1450
                                            $userKey['share_key'],
1451
                                            $session->get('user-private_key')
1452
                                        )
1453
                                    ));
1454
                                } else {
1455
                                    $oldVal = '';
1456
                                }
1457
                            } else {
1458
                                $oldVal = $dataTmpCat['data'];
1459
                            }
1460
1461
                            // Compare both values to see if any change was done
1462
                            if ($field['value'] !== $oldVal) {
1463
                                // The strings are different
1464
                                $encrypt = [];
1465
                                
1466
                                // Should we encrypt the data
1467
                                if ((int) $dataTmpCat['encrypted_data'] === 1) {
1468
                                    $cryptedStuff = doDataEncryption($field['value']);
1469
                                    $encrypt['string'] = $cryptedStuff['encrypted'];
1470
                                    $encrypt['type'] = TP_ENCRYPTION_NAME;
1471
1472
                                    // Create sharekeys for users
1473
                                    storeUsersShareKey(
1474
                                        prefixTable('sharekeys_fields'),
1475
                                        (int) $post_folder_is_personal,
1476
                                        (int) $dataTmpCat['field_item_id'],
1477
                                        $cryptedStuff['objectKey'],
1478
                                        true,   // only for the item creator
1479
                                        true,   // delete all
1480
                                    );
1481
1482
                                    array_push(
1483
                                        $tasksToBePerformed,
1484
                                        'item_field'
1485
                                    );
1486
                                    $encryptedFieldIsChanged = true;
1487
                                } else {
1488
                                    $encrypt['string'] = $field['value'];
1489
                                    $encrypt['type'] = 'not_set';
1490
                                }
1491
1492
                                // update value
1493
                                DB::update(
1494
                                    prefixTable('categories_items'),
1495
                                    array(
1496
                                        'data' => $encrypt['string'],
1497
                                        'data_iv' => '',
1498
                                        'encryption_type' => $encrypt['type'],
1499
                                    ),
1500
                                    'item_id = %i AND field_id = %i',
1501
                                    $inputData['itemId'],
1502
                                    $field['id']
1503
                                );
1504
1505
                                // Store updates performed
1506
                                array_push(
1507
                                    $arrayOfChanges,
1508
                                    $dataTmpCat['title']
1509
                                );
1510
1511
                                // update LOG
1512
                                logItems(
1513
                                    $SETTINGS,
1514
                                    (int) $inputData['itemId'],
1515
                                    $inputData['label'],
1516
                                    $session->get('user-id'),
1517
                                    'at_modification',
1518
                                    $session->get('user-login'),
1519
                                    'at_field : ' . $dataTmpCat['title'] . ' => ' . $oldVal
1520
                                );
1521
                            }
1522
                        }
1523
1524
                        // Create a task to create sharekeys for this field for users
1525
                        // If this field is encrypted
1526
                        if ((int) $dataTmpCat['encrypted_data'] === 1 && $encryptedFieldIsChanged === true) {
1527
                            array_push(
1528
                                $itemFieldsForTasks,
1529
                                [
1530
                                    'object_id' => $dataTmpCat['field_item_id'],
1531
                                    'object_key' => $cryptedStuff['objectKey'],
1532
                                ]
1533
                            );
1534
                            $encryptionTaskIsRequested = true;
1535
                        }
1536
                    } else {
1537
                        // Case where field new value is empty
1538
                        // then delete field
1539
                        if (empty($field_data[1]) === true) {
1540
                            DB::delete(
1541
                                prefixTable('categories_items'),
1542
                                'item_id = %i AND field_id = %s',
1543
                                $inputData['itemId'],
1544
                                $field['id']
1545
                            );
1546
                        }
1547
                    }
1548
                }
1549
            }
1550
1551
            // create a task for all fields updated
1552
            if ($encryptionTaskIsRequested === true) {
1553
                if (WIP === true) error_log('createTaskForItem - '.print_r($tasksToBePerformed, true));
1554
                createTaskForItem(
1555
                    'item_update_create_keys',
1556
                    $tasksToBePerformed,
1557
                    (int) $inputData['itemId'],
1558
                    (int) $session->get('user-id'),
1559
                    $encrypted_password_key,
1560
                    (int) $inputData['itemId'],
1561
                    $itemFieldsForTasks,
1562
                    []
1563
                );
1564
            }
1565
1566
            // If template enable, is there a main one selected?
1567
            if (
1568
                isset($SETTINGS['item_creation_templates']) === true
1569
                && (int) $SETTINGS['item_creation_templates'] === 1
1570
            ) {
1571
                DB::queryFirstRow(
1572
                    'SELECT *
1573
                    FROM ' . prefixTable('templates') . '
1574
                    WHERE item_id = %i',
1575
                    $inputData['itemId']
1576
                );
1577
                if (DB::count() === 0 && empty($post_template_id) === false) {
1578
                    // store field text
1579
                    DB::insert(
1580
                        prefixTable('templates'),
1581
                        array(
1582
                            'item_id' => $inputData['itemId'],
1583
                            'category_id' => $post_template_id,
1584
                        )
1585
                    );
1586
                } else {
1587
                    // Delete if empty
1588
                    if (empty($post_template_id) === true) {
1589
                        DB::delete(
1590
                            prefixTable('templates'),
1591
                            'item_id = %i',
1592
                            $inputData['itemId']
1593
                        );
1594
                    } else {
1595
                        // Update value
1596
                        DB::update(
1597
                            prefixTable('templates'),
1598
                            array(
1599
                                'category_id' => $post_template_id,
1600
                            ),
1601
                            'item_id = %i',
1602
                            $inputData['itemId']
1603
                        );
1604
                    }
1605
                }
1606
            }
1607
1608
            // Update automatic deletion - Only by the creator of the Item
1609
            if (
1610
                isset($SETTINGS['enable_delete_after_consultation']) === true
1611
                && (int) $SETTINGS['enable_delete_after_consultation'] === 1
1612
            ) {
1613
                // check if elem exists in Table. If not add it or update it.
1614
                DB::query(
1615
                    'SELECT *
1616
                    FROM ' . prefixTable('automatic_del') . '
1617
                    WHERE item_id = %i',
1618
                    $inputData['itemId']
1619
                );
1620
1621
                if (DB::count() === 0) {
1622
                    // No automatic deletion for this item
1623
                    if (
1624
                        empty($post_to_be_deleted_after_date) === false
1625
                        || (int) $post_to_be_deleted_after_x_views > 0
1626
                    ) {
1627
                        // Automatic deletion to be added
1628
                        DB::insert(
1629
                            prefixTable('automatic_del'),
1630
                            array(
1631
                                'item_id' => $inputData['itemId'],
1632
                                'del_enabled' => 1,
1633
                                'del_type' => empty($post_to_be_deleted_after_x_views) === false ?
1634
                                    1 : 2, //1 = numeric : 2 = date
1635
                                'del_value' => empty($post_to_be_deleted_after_x_views) === false ?
1636
                                    (int) $post_to_be_deleted_after_x_views : dateToStamp($post_to_be_deleted_after_date, $SETTINGS['date_format']),
1637
                            )
1638
                        );
1639
1640
                        // Store updates performed
1641
                        array_push(
1642
                            $arrayOfChanges,
1643
                            $lang->get('automatic_deletion_engaged') . ': ' . $lang->get('enabled')
1644
                        );
1645
1646
                        // update LOG
1647
                        logItems(
1648
                            $SETTINGS,
1649
                            (int) $inputData['itemId'],
1650
                            $inputData['label'],
1651
                            $session->get('user-id'),
1652
                            'at_modification',
1653
                            $session->get('user-login'),
1654
                            'at_automatic_del : enabled'
1655
                        );
1656
                    }
1657
                } else {
1658
                    // Automatic deletion exists for this item
1659
                    if (
1660
                        empty($post_to_be_deleted_after_date) === false
1661
                        || (int) $post_to_be_deleted_after_x_views > 0
1662
                    ) {
1663
                        // Update automatic deletion
1664
                        DB::update(
1665
                            prefixTable('automatic_del'),
1666
                            array(
1667
                                'del_type' => empty($post_to_be_deleted_after_x_views) === false ?
1668
                                    1 : 2, //1 = numeric : 2 = date
1669
                                'del_value' => empty($post_to_be_deleted_after_x_views) === false ?
1670
                                    $post_to_be_deleted_after_x_views : dateToStamp($post_to_be_deleted_after_date, $SETTINGS['date_format']),
1671
                            ),
1672
                            'item_id = %i',
1673
                            $inputData['itemId']
1674
                        );
1675
                    } else {
1676
                        // delete automatic deleteion for this item
1677
                        DB::delete(
1678
                            prefixTable('automatic_del'),
1679
                            'item_id = %i',
1680
                            $inputData['itemId']
1681
                        );
1682
1683
                        // Store updates performed
1684
                        array_push(
1685
                            $arrayOfChanges,
1686
                            $lang->get('automatic_deletion_engaged') . ': ' . $lang->get('disabled')
1687
                        );
1688
1689
                        // update LOG
1690
                        logItems(
1691
                            $SETTINGS,
1692
                            (int) $inputData['itemId'],
1693
                            $inputData['label'],
1694
                            $session->get('user-id'),
1695
                            'at_modification',
1696
                            $session->get('user-login'),
1697
                            'at_automatic_del : disabled'
1698
                        );
1699
                    }
1700
                }
1701
            }
1702
1703
            // get readable list of restriction
1704
            if (
1705
                is_array($post_restricted_to) === true
1706
                && count($post_restricted_to) > 0
1707
                && isset($SETTINGS['restricted_to']) === true
1708
                && (int) $SETTINGS['restricted_to'] === 1
1709
            ) {
1710
                foreach ($post_restricted_to as $userId) {
1711
                    if (empty($userId) === false) {
1712
                        $dataTmp = DB::queryFirstRow(
1713
                            'SELECT id, name, lastname
1714
                            FROM ' . prefixTable('users') . '
1715
                            WHERE id= %i',
1716
                            $userId
1717
                        );
1718
1719
                        // Add to array
1720
                        array_push(
1721
                            $arrayOfUsersRestriction,
1722
                            $dataTmp['name'] . ' ' . $dataTmp['lastname']
1723
                        );
1724
                        array_push(
1725
                            $arrayOfUsersIdRestriction,
1726
                            $dataTmp['id']
1727
                        );
1728
                    }
1729
                }
1730
            }
1731
            if ((int) $SETTINGS['restricted_to'] === 1) {
1732
                $diffUsersRestiction = array_diff(
1733
                    empty($data['restricted_to']) === false ?
1734
                        explode(';', $data['restricted_to']) : array(),
1735
                    $arrayOfUsersIdRestriction
1736
                );
1737
            }
1738
1739
            // Manage retriction_to_roles
1740
            if (
1741
                is_array($post_restricted_to_roles) === true
1742
                && count($post_restricted_to_roles) > 0
1743
                && isset($SETTINGS['restricted_to_roles']) === true
1744
                && (int) $SETTINGS['restricted_to_roles'] === 1
1745
            ) {
1746
                // Init
1747
                $arrayOfRestrictionRolesOld = array();
1748
                $arrayOfRestrictionRoles = array();
1749
1750
                // get values before deleting them
1751
                $rows = DB::query(
1752
                    'SELECT t.title, t.id AS id
1753
                    FROM ' . prefixTable('roles_title') . ' as t
1754
                    INNER JOIN ' . prefixTable('restriction_to_roles') . ' as r ON (t.id=r.role_id)
1755
                    WHERE r.item_id = %i
1756
                    ORDER BY t.title ASC',
1757
                    $inputData['itemId']
1758
                );
1759
                foreach ($rows as $record) {
1760
                    // Add to array
1761
                    array_push(
1762
                        $arrayOfRestrictionRolesOld,
1763
                        $record['title']
1764
                    );
1765
                }
1766
                // delete previous values
1767
                DB::delete(
1768
                    prefixTable('restriction_to_roles'),
1769
                    'item_id = %i',
1770
                    $inputData['itemId']
1771
                );
1772
1773
                // add roles for item
1774
                if (
1775
                    is_array($post_restricted_to_roles) === true
1776
                    && count($post_restricted_to_roles) > 0
1777
                ) {
1778
                    foreach ($post_restricted_to_roles as $role) {
1779
                        DB::insert(
1780
                            prefixTable('restriction_to_roles'),
1781
                            array(
1782
                                'role_id' => $role,
1783
                                'item_id' => $inputData['itemId'],
1784
                            )
1785
                        );
1786
                        $dataTmp = DB::queryFirstRow(
1787
                            'SELECT title
1788
                            FROM ' . prefixTable('roles_title') . '
1789
                            WHERE id = %i',
1790
                            $role
1791
                        );
1792
1793
                        // Add to array
1794
                        array_push(
1795
                            $arrayOfRestrictionRoles,
1796
                            $dataTmp['title']
1797
                        );
1798
                    }
1799
1800
                    if ((int) $SETTINGS['restricted_to'] === 1) {
1801
                        $diffRolesRestiction = array_diff(
1802
                            $arrayOfRestrictionRoles,
1803
                            $arrayOfRestrictionRolesOld
1804
                        );
1805
                    }
1806
                }
1807
            }
1808
            // Update CACHE table
1809
            updateCacheTable('update_value', (int) $inputData['itemId']);
1810
1811
1812
            // Manage OTP status
1813
            // Get current status
1814
            $otpStatus = DB::queryFirstRow(
1815
                'SELECT enabled as otp_is_enabled
1816
                FROM ' . prefixTable('items_otp') . '
1817
                WHERE item_id = %i',
1818
                $inputData['itemId']
1819
            );
1820
1821
            // Check if status has changed
1822
            if (DB::count() > 0 && (int) $otpStatus['otp_is_enabled'] !== (int) $post_otp_is_enabled) {
1823
                // Update status
1824
                DB::update(
1825
                    prefixTable('items_otp'),
1826
                    array(
1827
                        'enabled' => (int) $post_otp_is_enabled,
1828
                    ),
1829
                    'item_id = %i',
1830
                    $inputData['itemId']
1831
                );
1832
1833
                // Store updates performed
1834
                array_push(
1835
                    $arrayOfChanges,
1836
                    $lang->get('otp_status')
1837
                );
1838
1839
                // update LOG
1840
                logItems(
1841
                    $SETTINGS,
1842
                    (int) $inputData['itemId'],
1843
                    $inputData['label'],
1844
                    $session->get('user-id'),
1845
                    'at_modification',
1846
                    $session->get('user-login'),
1847
                    'at_otp_status:' . ((int) $post_otp_is_enabled === 0 ? 'disabled' : 'enabled')
1848
                );
1849
            } elseif (DB::count() === 0 && empty($post_otp_secret) === false) {
1850
                // Create the entry in items_otp table
1851
                // OTP doesn't exist then create it
1852
1853
                // Encrypt secret
1854
                $encryptedSecret = cryption(
1855
                    $post_otp_secret,
1856
                    '',
1857
                    'encrypt'
1858
                );
1859
                
1860
                // insert in table
1861
                DB::insert(
1862
                    prefixTable('items_otp'),
1863
                    array(
1864
                        'item_id' => $inputData['itemId'],
1865
                        'secret' => $encryptedSecret['string'],
1866
                        'phone_number' => $post_otp_phone_number,
1867
                        'timestamp' => time(),
1868
                        'enabled' => 1,
1869
                    )
1870
                );
1871
            }
1872
1873
            //---- Log all modifications done ----
1874
1875
            // RESTRICTIONS
1876
            if (count($diffRolesRestiction) > 0 || count($diffUsersRestiction) > 0) {
1877
                // Store updates performed
1878
                array_push(
1879
                    $arrayOfChanges,
1880
                    $lang->get('at_restriction')
1881
                );
1882
1883
                // Log
1884
                logItems(
1885
                    $SETTINGS,
1886
                    (int) $inputData['itemId'],
1887
                    $inputData['label'],
1888
                    $session->get('user-id'),
1889
                    'at_modification',
1890
                    $session->get('user-login'),
1891
                    'at_restriction : ' . (count($diffUsersRestiction) > 0 ?
1892
                        implode(', ', $arrayOfUsersRestriction) . (count($diffRolesRestiction) > 0 ? ', ' : '') : '') . (count($diffRolesRestiction) > 0 ? implode(', ', $arrayOfRestrictionRoles) : '')
1893
                );
1894
            }
1895
1896
            // LABEL
1897
            if ($data['label'] !== $inputData['label']) {
1898
                // Store updates performed
1899
                array_push(
1900
                    $arrayOfChanges,
1901
                    $lang->get('at_label')
1902
                );
1903
1904
                // Log
1905
                logItems(
1906
                    $SETTINGS,
1907
                    (int) $inputData['itemId'],
1908
                    $inputData['label'],
1909
                    $session->get('user-id'),
1910
                    'at_modification',
1911
                    $session->get('user-login'),
1912
                    'at_label : ' . $data['label'] . ' => ' . $inputData['label']
1913
                );
1914
            }
1915
            // LOGIN
1916
            if ($data['login'] !== $post_login) {
1917
                // Store updates performed
1918
                array_push(
1919
                    $arrayOfChanges,
1920
                    $lang->get('at_login')
1921
                );
1922
1923
                // Log
1924
                logItems(
1925
                    $SETTINGS,
1926
                    (int) $inputData['itemId'],
1927
                    $inputData['label'],
1928
                    $session->get('user-id'),
1929
                    'at_modification',
1930
                    $session->get('user-login'),
1931
                    'at_login : ' . $data['login'] . ' => ' . $post_login
1932
                );
1933
            }
1934
            // EMAIL
1935
            if ($post_email !== null && $data['email'] !== null && strcmp($data['email'], $post_email) !== 0) {
1936
                // Store updates performed
1937
                array_push(
1938
                    $arrayOfChanges,
1939
                    $lang->get('at_email')
1940
                );
1941
1942
                // Log
1943
                logItems(
1944
                    $SETTINGS,
1945
                    (int) $inputData['itemId'],
1946
                    $inputData['label'],
1947
                    $session->get('user-id'),
1948
                    'at_modification',
1949
                    $session->get('user-login'),
1950
                    'at_email : ' . $data['email'] . ' => ' . $post_email
1951
                );
1952
            }
1953
            // URL
1954
            if ($data['url'] !== $post_url && $post_url !== 'http://') {
1955
                // Store updates performed
1956
                array_push(
1957
                    $arrayOfChanges,
1958
                    $lang->get('at_url')
1959
                );
1960
1961
                // Log
1962
                logItems(
1963
                    $SETTINGS,
1964
                    (int) $inputData['itemId'],
1965
                    $inputData['label'],
1966
                    $session->get('user-id'),
1967
                    'at_modification',
1968
                    $session->get('user-login'),
1969
                    'at_url : ' . $data['url'] . ' => ' . $post_url
1970
                );
1971
            }
1972
            // DESCRIPTION
1973
            // deepcode ignore InsecureHash: md5 is used just to perform a string encrypted comparison
1974
            if (strcmp(md5(strip_tags($data['description'])), md5(strip_tags($post_description))) !== 0) {
1975
                // Store updates performed
1976
                array_push(
1977
                    $arrayOfChanges,
1978
                    $lang->get('at_description')
1979
                );
1980
1981
                // Log
1982
                logItems(
1983
                    $SETTINGS,
1984
                    (int) $inputData['itemId'],
1985
                    $inputData['label'],
1986
                    $session->get('user-id'),
1987
                    'at_modification',
1988
                    $session->get('user-login'),
1989
                    'at_description'
1990
                );
1991
            }
1992
            // FOLDER
1993
            if ((int) $data['id_tree'] !== (int) $inputData['folderId']) {
1994
                // Get name of folders
1995
                $dataTmp = DB::query('SELECT title FROM ' . prefixTable('nested_tree') . ' WHERE id IN %li', array($data['id_tree'], $inputData['folderId']));
1996
1997
                // Store updates performed
1998
                array_push(
1999
                    $arrayOfChanges,
2000
                    $lang->get('at_category')
2001
                );
2002
2003
                // Log
2004
                logItems(
2005
                    $SETTINGS,
2006
                    (int) $inputData['itemId'],
2007
                    $inputData['label'],
2008
                    $session->get('user-id'),
2009
                    'at_modification',
2010
                    $session->get('user-login'),
2011
                    'at_category : ' . $dataTmp[0]['title'] . ' => ' . $dataTmp[1]['title']
2012
                );
2013
            }
2014
            // ANYONE_CAN_MODIFY
2015
            if ((int) $post_anyone_can_modify !== (int) $data['anyone_can_modify']) {
2016
                // Store updates performed
2017
                array_push(
2018
                    $arrayOfChanges,
2019
                    $lang->get('at_anyoneconmodify') . ': ' . ((int) $post_anyone_can_modify === 0 ? $lang->get('disabled') : $lang->get('enabled'))
2020
                );
2021
2022
                // Log
2023
                logItems(
2024
                    $SETTINGS,
2025
                    (int) $inputData['itemId'],
2026
                    $inputData['label'],
2027
                    $session->get('user-id'),
2028
                    'at_modification',
2029
                    $session->get('user-login'),
2030
                    'at_anyoneconmodify : ' . ((int) $post_anyone_can_modify === 0 ? 'disabled' : 'enabled')
2031
                );
2032
            }
2033
2034
            // Reload new values
2035
            $dataItem = DB::queryFirstRow(
2036
                'SELECT *
2037
                FROM ' . prefixTable('items') . ' as i
2038
                INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
2039
                WHERE i.id = %i AND l.action = %s',
2040
                $inputData['itemId'],
2041
                'at_creation'
2042
            );
2043
            // Reload History
2044
            $history = '';
2045
            $rows = DB::query(
2046
                'SELECT l.date as date, l.action as action, l.raison as raison, u.login as login
2047
                FROM ' . prefixTable('log_items') . ' as l
2048
                LEFT JOIN ' . prefixTable('users') . ' as u ON (l.id_user=u.id)
2049
                WHERE l.action <> %s AND id_item=%s',
2050
                'at_shown',
2051
                $inputData['itemId']
2052
            );
2053
            foreach ($rows as $record) {
2054
                if ($record['raison'] === NULL) continue;
2055
                $reason = explode(':', $record['raison']);
2056
                if (count($reason) > 0) {
2057
                    $sentence = date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['date']) . ' - '
2058
                        . $record['login'] . ' - ' . $lang->get($record['action']) . ' - '
2059
                        . (empty($record['raison']) === false ? (count($reason) > 1 ? $lang->get(trim($reason[0])) . ' : ' . $reason[1]
2060
                            : $lang->get(trim($reason[0]))) : '');
2061
                    if (empty($history)) {
2062
                        $history = $sentence;
2063
                    } else {
2064
                        $history .= '<br />' . $sentence;
2065
                    }
2066
                }
2067
            }
2068
2069
            // generate 2d key
2070
            $session->set('user-key_tmp', bin2hex(GenerateCryptKey(16, false, true, true, false, true)));
2071
2072
            // Send email
2073
            if (is_array($post_diffusion_list) === true && count($post_diffusion_list) > 0) {
2074
                $cpt = 0;
2075
                foreach ($post_diffusion_list as $emailAddress) {
2076
                    if (empty($emailAddress) === false) {
2077
                        prepareSendingEmail(
2078
                            $lang->get('email_subject_item_updated'),
2079
                            str_replace(
2080
                                array('#item_label#', '#item_category#', '#item_id#', '#url#', '#name#', '#lastname#', '#folder_name#'),
2081
                                array($inputData['label'], (string) $inputData['folderId'], (string) $inputData['itemId'], $SETTINGS['cpassman_url'], $session->get('user-name'), $session->get('user-lastname'), $dataFolderSettings['title']),
2082
                                $lang->get('email_body_item_updated')
2083
                            ),
2084
                            $emailAddress,
2085
                            $post_diffusion_list_names[$cpt]
2086
                        );
2087
                        $cpt++;
2088
                    }
2089
                }
2090
            }
2091
2092
            // Remove the edition lock if no  encryption steps are needed
2093
            if ($encryptionTaskIsRequested === false) {
2094
                if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) {
2095
                    error_log('Remove the edition lock if no  encryption steps are needed');
2096
                }
2097
                DB::delete(
2098
                    prefixTable('items_edition'), 
2099
                    'item_id = %i AND user_id = %i', 
2100
                    $inputData['itemId'],
2101
                    $session->get('user-id')
2102
                );
2103
            }
2104
2105
            // Notifiy changes to the users
2106
            notifyChangesToSubscribers($inputData['itemId'], $inputData['label'], $arrayOfChanges, $SETTINGS);
2107
2108
            // Prepare some stuff to return
2109
            $arrData = array(
2110
                'error' => false,
2111
                'message' => '',
2112
            );
2113
        } else {
2114
            echo (string) prepareExchangedData(
2115
                array(
2116
                    'error' => true,
2117
                    'message' => $lang->get('error_not_allowed_to_edit_item'),
2118
                ),
2119
                'encode'
2120
            );
2121
            break;
2122
        }
2123
        
2124
        // return data
2125
        echo (string) prepareExchangedData(
2126
            $arrData,
2127
            'encode'
2128
        );
2129
        break;
2130
2131
    /*
2132
     * CASE
2133
     * Copy an Item
2134
     */
2135
    case 'copy_item':
2136
        // Check KEY and rights
2137
        if ($inputData['key'] !== $session->get('key')) {
2138
            echo (string) prepareExchangedData(
2139
                array(
2140
                    'error' => true,
2141
                    'message' => $lang->get('key_is_not_correct'),
2142
                ),
2143
                'encode'
2144
            );
2145
            break;
2146
        }
2147
        if ($session->get('user-read_only') === 1) {
2148
            echo (string) prepareExchangedData(
2149
                array(
2150
                    'error' => true,
2151
                    'message' => $lang->get('error_not_allowed_to'),
2152
                ),
2153
                'encode'
2154
            );
2155
            break;
2156
        }
2157
2158
        // decrypt and retreive data in JSON format
2159
        $dataReceived = prepareExchangedData(
2160
            $inputData['data'],
2161
            'decode'
2162
        );
2163
2164
        // Prepare POST variables
2165
        $post_new_label = (string) filter_var($dataReceived['new_label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2166
        $post_source_id = (int) filter_var($dataReceived['source_id'], FILTER_SANITIZE_NUMBER_INT);
2167
        $post_dest_id = (int) filter_var($dataReceived['dest_id'], FILTER_SANITIZE_NUMBER_INT);
2168
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
2169
2170
        // perform a check in case of Read-Only user creating an item in his PF
2171
        if (
2172
            (int) $session->get('user-read_only') === 1
2173
            && (in_array($post_source_id, $session->get('user-personal_folders')) === false
2174
                || in_array($post_dest_id, $session->get('user-personal_folders')) === false)
2175
        ) {
2176
            echo (string) prepareExchangedData(
2177
                array(
2178
                    'error' => true,
2179
                    'message' => $lang->get('error_not_allowed_to'),
2180
                ),
2181
                'encode'
2182
            );
2183
            break;
2184
        }
2185
2186
        // Init
2187
        $returnValues = '';
2188
        $pw = '';
2189
        $is_perso = 0;
2190
        $itemDataArray = array(
2191
            'pwd' => '',
2192
            'fields' => [],
2193
            'files' => [],
2194
        );
2195
2196
        if (
2197
            empty($inputData['itemId']) === false
2198
            && empty($post_dest_id) === false
2199
        ) {
2200
            // load the original record into an array
2201
            $originalRecord = DB::queryFirstRow(
2202
                'SELECT * FROM ' . prefixTable('items') . '
2203
                WHERE id = %i',
2204
                $inputData['itemId']
2205
            );
2206
2207
            // Check if the folder where this item is accessible to the user
2208
            if (in_array($originalRecord['id_tree'], $session->get('user-accessible_folders')) === false) {
2209
                echo (string) prepareExchangedData(
2210
                    array(
2211
                        'error' => true,
2212
                        'message' => $lang->get('error_not_allowed_to'),
2213
                    ),
2214
                    'encode'
2215
                );
2216
                break;
2217
            }
2218
2219
            // Load the destination folder record into an array
2220
            $dataDestination = DB::queryFirstRow(
2221
                'SELECT personal_folder FROM ' . prefixTable('nested_tree') . '
2222
                WHERE id = %i',
2223
                $post_dest_id
2224
            );
2225
2226
            // Get the ITEM object key for the user
2227
            $userKey = DB::queryFirstRow(
2228
                'SELECT share_key
2229
                FROM ' . prefixTable('sharekeys_items') . '
2230
                WHERE user_id = %i AND object_id = %i',
2231
                $session->get('user-id'),
2232
                $inputData['itemId']
2233
            );
2234
            if (DB::count() === 0) {
2235
                // ERROR - No sharekey found for this item and user
2236
                echo (string) prepareExchangedData(
2237
                    array(
2238
                        'error' => true,
2239
                        'message' => $lang->get('error_not_allowed_to'),
2240
                    ),
2241
                    'encode'
2242
                );
2243
                break;
2244
            }
2245
2246
            // Decrypt / Encrypt the password
2247
            $cryptedStuff = doDataEncryption(
2248
                base64_decode(
2249
                    doDataDecryption(
2250
                        $originalRecord['pw'],
2251
                        decryptUserObjectKey(
2252
                            $userKey['share_key'],
2253
                            $session->get('user-private_key')
2254
                        )
2255
                    )
2256
                )
2257
            );
2258
            // reaffect pw
2259
            $originalRecord['pw'] = $cryptedStuff['encrypted'];
2260
2261
            // store pwd object key
2262
            $itemDataArray['pwd'] = $cryptedStuff['objectKey'];
2263
2264
            // generate the query to update the new record with the previous values
2265
            $aSet = array();
2266
            foreach ($originalRecord as $key => $value) {
2267
                $aSet['item_key'] = uniqidReal(50);
2268
                $aSet['created_at'] = time();
2269
                if ($key === 'id_tree') {
2270
                    $aSet['id_tree'] = $post_dest_id;
2271
                } elseif ($key === 'label') {
2272
                    $aSet[$key] = $post_new_label;
2273
                } elseif ($key === 'viewed_no') {
2274
                    $aSet['viewed_no'] = '0';
2275
                } elseif ($key === 'pw') {
2276
                    $aSet['pw'] = $originalRecord['pw'];
2277
                    $aSet['pw_iv'] = '';
2278
                } elseif ($key === 'perso') {
2279
                    $aSet['perso'] = $is_perso;
2280
                } elseif ($key !== 'id' && $key !== 'key') {
2281
                    $aSet[$key] = $value;
2282
                }
2283
            }
2284
2285
            // insert the new record and get the new auto_increment id
2286
            DB::insert(
2287
                prefixTable('items'),
2288
                $aSet
2289
            );
2290
            $newItemId = DB::insertId();
2291
2292
            // Create sharekeys for users of this new ITEM
2293
            storeUsersShareKey(
2294
                prefixTable('sharekeys_items'),
2295
                (int) $dataDestination['personal_folder'],
2296
                (int) $newItemId,
2297
                $itemDataArray['pwd'],
2298
                true,
2299
                false,
2300
            );
2301
2302
            // --------------------
2303
            // Manage Custom Fields
2304
            $rows = DB::query(
2305
                'SELECT ci.id AS id, ci.data AS data, ci.field_id AS field_id, c.encrypted_data AS encrypted_data
2306
                FROM ' . prefixTable('categories_items') . ' AS ci
2307
                INNER JOIN ' . prefixTable('categories') . ' AS c ON (c.id = ci.field_id)
2308
                WHERE ci.item_id = %i',
2309
                $inputData['itemId']
2310
            );
2311
            foreach ($rows as $field) {
2312
                // Create the entry for the new item
2313
2314
                // Is the data encrypted
2315
                if ((int) $field['encrypted_data'] === 1) {
2316
                    // Get user key
2317
                    $userKey = DB::queryFirstRow(
2318
                        'SELECT share_key
2319
                        FROM ' . prefixTable('sharekeys_fields') . '
2320
                        WHERE user_id = %i AND object_id = %i',
2321
                        $session->get('user-id'),
2322
                        $field['id']
2323
                    );
2324
                    // Then decrypt original field value and encrypt with new key
2325
                    $cryptedStuff = doDataEncryption(
2326
                        base64_decode(
2327
                            doDataDecryption(
2328
                                $field['data'],
2329
                                decryptUserObjectKey(
2330
                                    $userKey['share_key'],
2331
                                    $session->get('user-private_key')
2332
                                )
2333
                            )
2334
                        )
2335
                    );
2336
                    // reaffect pw
2337
                    $field['data'] = $cryptedStuff['encrypted'];
2338
                }
2339
2340
                // store field text
2341
                DB::insert(
2342
                    prefixTable('categories_items'),
2343
                    array(
2344
                        'item_id' => $newItemId,
2345
                        'field_id' => $field['field_id'],
2346
                        'data' => (int) $field['encrypted_data'] === 1 ?
2347
                            $cryptedStuff['encrypted'] : $field['data'],
2348
                        'data_iv' => '',
2349
                        'encryption_type' => (int) $field['encrypted_data'] === 1 ?
2350
                            TP_ENCRYPTION_NAME : 'not_set',
2351
                    )
2352
                );
2353
                $newFieldId = DB::insertId();
2354
2355
                // Create sharekeys for current user
2356
                if ((int) $field['encrypted_data'] === 1) {
2357
                    // Create sharekeys for user
2358
                    storeUsersShareKey(
2359
                        prefixTable('sharekeys_fields'),
2360
                        (int) $dataDestination['personal_folder'],
2361
                        (int) $newFieldId,
2362
                        $cryptedStuff['objectKey'],
2363
                        true,
2364
                        false,
2365
                    );
2366
2367
                    // Build list of fields
2368
                    array_push(
2369
                        $itemDataArray['fields'],
2370
                        array(
2371
                            'object_id' => $newFieldId,
2372
                            'object_key' => $cryptedStuff['objectKey'],
2373
                        )
2374
                    );
2375
                }
2376
            }
2377
            // <---
2378
2379
            // ------------------
2380
            // Manage attachments
2381
2382
            // get file key
2383
            $rows = DB::query(
2384
                'SELECT f.id AS id, f.file AS file, f.name AS name, f.status AS status, f.extension AS extension,
2385
                f.size AS size, f.type AS type, s.share_key AS share_key
2386
                FROM ' . prefixTable('files') . ' AS f
2387
                INNER JOIN ' . prefixTable('sharekeys_files') . ' AS s ON (f.id = s.object_id)
2388
                WHERE s.user_id = %i AND f.id_item = %i',
2389
                $session->get('user-id'),
2390
                $inputData['itemId']
2391
            );
2392
            foreach ($rows as $record) {
2393
                // Check if file still exists
2394
                if (file_exists($SETTINGS['path_to_upload_folder'] . DIRECTORY_SEPARATOR . TP_FILE_PREFIX . base64_decode($record['file'])) === true) {
2395
                    // Step1 - decrypt the file
2396
                    // deepcode ignore PT: path is sanitized inside decryptFile()
2397
                    $fileContent = decryptFile(
2398
                        $record['file'],
2399
                        $SETTINGS['path_to_upload_folder'],
2400
                        decryptUserObjectKey($record['share_key'], $session->get('user-private_key'))
2401
                    );
2402
2403
                    // Step2 - create file
2404
                    // deepcode ignore InsecureHash: md5 is used jonly for file name in order to get a hashed value in database
2405
                    $newFileName = md5(time() . '_' . $record['id']) . '.' . $record['extension'];
2406
                    $outstream = fopen($SETTINGS['path_to_upload_folder'] . DIRECTORY_SEPARATOR . $newFileName, 'ab');
2407
                    if ($outstream === false) {
2408
                        echo prepareExchangedData(
2409
                            array(
2410
                                'error' => true,
2411
                                'message' => $lang->get('error_cannot_open_file'),
2412
                            ),
2413
                            'encode'
2414
                        );
2415
                        break;
2416
                    }
2417
                    fwrite(
2418
                        $outstream,
2419
                        base64_decode($fileContent)
0 ignored issues
show
Bug introduced by
It seems like $fileContent can also be of type array<string,string|true>; however, parameter $string of base64_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2419
                        base64_decode(/** @scrutinizer ignore-type */ $fileContent)
Loading history...
2420
                    );
2421
2422
                    // Step3 - encrypt the file
2423
                    $newFile = encryptFile($newFileName, $SETTINGS['path_to_upload_folder']);
2424
2425
                    // Step4 - store in database
2426
                    DB::insert(
2427
                        prefixTable('files'),
2428
                        array(
2429
                            'id_item' => $newItemId,
2430
                            'name' => $record['name'],
2431
                            'size' => $record['size'],
2432
                            'extension' => $record['extension'],
2433
                            'type' => $record['type'],
2434
                            'file' => $newFile['fileHash'],
2435
                            'status' => TP_ENCRYPTION_NAME,
2436
                            'confirmed' => 1,
2437
                        )
2438
                    );
2439
                    $newFileId = DB::insertId();
2440
2441
                    // Step5 - create sharekeys
2442
                    // Build list of fields
2443
                    array_push(
2444
                        $itemDataArray['files'],
2445
                        array(
2446
                            'object_id' => $newFileId,
2447
                            'object_key' => $newFile['objectKey'],
2448
                        )
2449
                    );
2450
2451
                    storeUsersShareKey(
2452
                        prefixTable('sharekeys_files'),
2453
                        (int) $dataDestination['personal_folder'],
2454
                        (int) $newFileId,
2455
                        $newFile['objectKey'],
2456
                        true
2457
                    );
2458
                }
2459
            }
2460
            // <---
2461
2462
            // Create new task for the new item
2463
            // If it is not a personnal one
2464
            if ((int) $dataDestination['personal_folder'] !== 1) {
2465
                storeTask(
2466
                    'item_copy',
2467
                    $session->get('user-id'),
2468
                    0,
2469
                    (int) $post_dest_id,
2470
                    (int) $newItemId,
2471
                    $itemDataArray['pwd'],
2472
                    $itemDataArray['fields'],
2473
                    $itemDataArray['files'],
2474
                );
2475
            }
2476
2477
            // -------------------------
2478
            // Add specific restrictions
2479
            $rows = DB::query('SELECT * FROM ' . prefixTable('restriction_to_roles') . ' WHERE item_id = %i', $inputData['itemId']);
2480
            foreach ($rows as $record) {
2481
                DB::insert(
2482
                    prefixTable('restriction_to_roles'),
2483
                    array(
2484
                        'item_id' => $newItemId,
2485
                        'role_id' => $record['role_id'],
2486
                    )
2487
                );
2488
            }
2489
2490
            // Add Tags
2491
            $rows = DB::query('SELECT * FROM ' . prefixTable('tags') . ' WHERE item_id = %i', $inputData['itemId']);
2492
            foreach ($rows as $record) {
2493
                DB::insert(
2494
                    prefixTable('tags'),
2495
                    array(
2496
                        'item_id' => $newItemId,
2497
                        'tag' => $record['tag'],
2498
                    )
2499
                );
2500
            }
2501
2502
            // Add this duplicate in logs
2503
            logItems(
2504
                $SETTINGS,
2505
                (int) $newItemId,
2506
                $originalRecord['label'],
2507
                $session->get('user-id'),
2508
                'at_creation',
2509
                $session->get('user-login')
2510
            );
2511
            // Add the fact that item has been copied in logs
2512
            logItems(
2513
                $SETTINGS,
2514
                (int) $newItemId,
2515
                $originalRecord['label'],
2516
                $session->get('user-id'),
2517
                'at_copy',
2518
                $session->get('user-login')
2519
            );
2520
2521
            echo (string) prepareExchangedData(
2522
                array(
2523
                    'error' => false,
2524
                    'message' => '',
2525
                    'new_id' => $newItemId
2526
                ),
2527
                'encode'
2528
            );
2529
2530
            // Add new item to cache table.
2531
            updateCacheTable('add_value', (int) $newItemId);
2532
        } else {
2533
            // no item
2534
            echo (string) prepareExchangedData(
2535
                array(
2536
                    'error' => true,
2537
                    'message' => $lang->get('error_missing_id'),
2538
                ),
2539
                'encode'
2540
            );
2541
        }
2542
        break;
2543
2544
    /*
2545
     * CASE
2546
     * Display informations of selected item
2547
     */
2548
    case 'show_details_item':
2549
        // Check KEY and rights
2550
        if ($inputData['key'] !== $session->get('key')) {
2551
            echo (string) prepareExchangedData(
2552
                array(
2553
                    'error' => true,
2554
                    'message' => $lang->get('key_is_not_correct'),
2555
                ),
2556
                'encode'
2557
            );
2558
            break;
2559
        }
2560
2561
        // Step #1
2562
        $session->set('system-show_step2', false);
2563
2564
        // Decrypt and retreive data in JSON format
2565
        $dataReceived = prepareExchangedData(
2566
            $inputData['data'],
2567
            'decode'
2568
        );
2569
2570
        // Init post variables
2571
        $inputData['id'] = filter_var(($dataReceived['id']), FILTER_SANITIZE_NUMBER_INT);
2572
        $inputData['folderId'] = filter_var(($dataReceived['folder_id']), FILTER_SANITIZE_NUMBER_INT);
2573
        $post_expired_item = filter_var(($dataReceived['expired_item']), FILTER_SANITIZE_NUMBER_INT);
2574
        $post_restricted = filter_var(($dataReceived['restricted']), FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2575
        $post_folder_access_level = isset($dataReceived['folder_access_level']) === true ?
2576
            filter_var(($dataReceived['folder_access_level']), FILTER_SANITIZE_FULL_SPECIAL_CHARS)
2577
            : '';
2578
        $post_item_rights = filter_var($dataReceived['rights'], FILTER_SANITIZE_NUMBER_INT);
2579
2580
        $pwIsEmptyNormal = false;
2581
        $arrData = array();
2582
        // return ID
2583
        $arrData['id'] = (int) $inputData['id'];
2584
        $arrData['id_user'] = API_USER_ID;
2585
        $arrData['author'] = 'API';
2586
2587
        // Check if item is deleted
2588
        // taking into account that item can be restored.
2589
        // so if restoration timestamp is higher than the deletion one
2590
        // then we can show it
2591
        $item_deleted = DB::queryFirstRow(
2592
            'SELECT *
2593
            FROM ' . prefixTable('log_items') . '
2594
            WHERE id_item = %i AND action = %s
2595
            ORDER BY date DESC
2596
            LIMIT 0, 1',
2597
            $inputData['id'],
2598
            'at_delete'
2599
        );
2600
        $dataDeleted = DB::count();
2601
2602
        $item_restored = DB::queryFirstRow(
2603
            'SELECT *
2604
            FROM ' . prefixTable('log_items') . '
2605
            WHERE id_item = %i AND action = %s
2606
            ORDER BY date DESC
2607
            LIMIT 0, 1',
2608
            $inputData['id'],
2609
            'at_restored'
2610
        );
2611
2612
        if ($dataDeleted !== 0 && intval($item_deleted['date']) > intval($item_restored['date'])) {
2613
            // This item is deleted => exit
2614
            echo (string) prepareExchangedData(
2615
                array(
2616
                    'error' => true,
2617
                    'message' => $lang->get('not_allowed_to_see_pw'),
2618
                    'show_detail_option' => 2,
2619
                ),
2620
                'encode'
2621
            );
2622
            break;
2623
        }
2624
2625
        // Get all informations for this item
2626
        $dataItem = DB::queryFirstRow(
2627
            'SELECT *
2628
            FROM ' . prefixTable('items') . ' as i
2629
            INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
2630
            WHERE i.id = %i AND l.action = %s',
2631
            $inputData['id'],
2632
            'at_creation'
2633
        );
2634
2635
        // Notification
2636
        DB::queryFirstRow(
2637
            'SELECT *
2638
            FROM ' . prefixTable('notification') . '
2639
            WHERE item_id = %i AND user_id = %i',
2640
            $inputData['id'],
2641
            $session->get('user-id')
2642
        );
2643
        if (DB::count() > 0) {
2644
            $arrData['notification_status'] = true;
2645
        } else {
2646
            $arrData['notification_status'] = false;
2647
        }
2648
2649
        // Get all USERS infos
2650
        $listeRestriction = is_null($dataItem['restricted_to']) === false ? array_filter(explode(';', $dataItem['restricted_to'])) : [];
2651
        $session->set('system-emails_list_for_notif', '');
2652
2653
        $user_in_restricted_list_of_item = in_array($session->get('user-id'), $listeRestriction) === true ? true : false;
2654
2655
        // manage case of API user
2656
        if ($dataItem['id_user'] === API_USER_ID) {
2657
            $arrData['author'] = 'API [' . $dataItem['description'] . ']';
2658
            $arrData['id_user'] = API_USER_ID;
2659
            $arrData['author_email'] = '';
2660
            $arrData['notification_status'] = false;
2661
        }
2662
2663
        // Get all tags for this item
2664
        $tags = array();
2665
        $rows = DB::query(
2666
            'SELECT tag
2667
            FROM ' . prefixTable('tags') . '
2668
            WHERE item_id = %i',
2669
            $inputData['id']
2670
        );
2671
        foreach ($rows as $record) {
2672
            array_push($tags, $record['tag']);
2673
        }
2674
2675
        // TODO -> improve this check
2676
        // check that actual user can access this item
2677
        $restrictionActive = true;
2678
        $restrictedTo = is_null($dataItem['restricted_to']) === false ? array_filter(explode(';', $dataItem['restricted_to'])) : [];
2679
        if (
2680
            in_array($session->get('user-id'), $restrictedTo) === true
2681
            || ((int) $session->get('user-manager') === 1 && (int) $SETTINGS['manager_edit'] === 1)
2682
        ) {
2683
            $restrictionActive = false;
2684
        }
2685
        if (empty($dataItem['restricted_to']) === true) {
2686
            $restrictionActive = false;
2687
        }
2688
2689
        // Check if user has a role that is accepted
2690
        $rows_tmp = DB::query(
2691
            'SELECT role_id
2692
            FROM ' . prefixTable('restriction_to_roles') . '
2693
            WHERE item_id=%i',
2694
            $inputData['id']
2695
        );
2696
        foreach ($rows_tmp as $rec_tmp) {
2697
            if (in_array($rec_tmp['role_id'], explode(';', $session->get('user-roles')))) {
2698
                $restrictionActive = false;
2699
            }
2700
        }
2701
2702
        // Uncrypt PW
2703
        // Get the object key for the user
2704
        $userKey = DB::queryFirstRow(
2705
            'SELECT share_key
2706
            FROM ' . prefixTable('sharekeys_items') . '
2707
            WHERE user_id = %i AND object_id = %i',
2708
            $session->get('user-id'),
2709
            $inputData['id']
2710
        );
2711
        if (DB::count() === 0 || empty($dataItem['pw']) === true) {
2712
            // No share key found
2713
            $pwIsEmptyNormally = false;
2714
            // Is this a personal and defuse password?
2715
            if ((int) $dataItem['perso'] === 1 && substr($dataItem['pw'], 0, 3) === 'def') {
2716
                // Yes, then ask for decryption with old personal salt key
2717
                echo (string) prepareExchangedData(
2718
                    array(
2719
                        'error' => true,
2720
                        'message' => $lang->get('error'),
2721
                        'show_detail_option' => 2,
2722
                        'error_type' => 'private_items_to_encrypt',
2723
                    ),
2724
                    'encode'
2725
                );
2726
                break;
2727
            } else {
2728
                $pw = '';
2729
            }
2730
        } else {
2731
            $pwIsEmptyNormal = true;
2732
            $decryptedObject = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
2733
            // if null then we have an error.
2734
            // suspecting bad password
2735
            if (empty($decryptedObject) === false) {
2736
                $pw = doDataDecryption(
2737
                    $dataItem['pw'],
2738
                    $decryptedObject
2739
                );
2740
                $arrData['pwd_encryption_error'] = false;
2741
                $arrData['pwd_encryption_error_message'] = '';
2742
            } else {
2743
                $pw = '';
2744
                $arrData['pwd_encryption_error'] = 'inconsistent_password';
2745
                $arrData['pwd_encryption_error_message'] = $lang->get('error_new_ldap_password_detected');
2746
            }
2747
        }
2748
2749
        // check user is admin
2750
        $session__list_restricted_folders_for_items = $session->get('system-list_restricted_folders_for_items') ?? [];
2751
        if (
2752
            (int) $session->get('user-admin') === 1
2753
            && (int) $dataItem['perso'] !== 1
2754
        ) {
2755
            $arrData['show_details'] = 0;
2756
            // ---
2757
            // ---
2758
        } elseif ((
2759
                (in_array($dataItem['id_tree'], $session->get('user-accessible_folders')) === true || (int) $session->get('user-admin') === 1)
2760
                && ((int) $dataItem['perso'] === 0 || ((int) $dataItem['perso'] === 1 && in_array($dataItem['id_tree'], $session->get('user-personal_folders')) === true))
2761
                && $restrictionActive === false)
2762
            || (isset($SETTINGS['anyone_can_modify']) && (int) $SETTINGS['anyone_can_modify'] === 1
2763
                && (int) $dataItem['anyone_can_modify'] === 1
2764
                && (in_array($dataItem['id_tree'], $session->get('user-accessible_folders')) || (int) $session->get('user-admin') === 1)
2765
                && $restrictionActive === false)
2766
            || (null !== $inputData['folderId']
2767
                && isset($session__list_restricted_folders_for_items[$inputData['folderId']])
2768
                && in_array($inputData['id'], $session__list_restricted_folders_for_items[$inputData['folderId']])
2769
                && (int) $post_restricted === 1
2770
                && $user_in_restricted_list_of_item === true)
2771
            || (isset($SETTINGS['restricted_to_roles']) && (int) $SETTINGS['restricted_to_roles'] === 1
2772
                && $restrictionActive === false)
2773
        ) {
2774
            // Check if actual USER can see this ITEM
2775
            // Allow show details
2776
            $arrData['show_details'] = 1;
2777
2778
            // Display menu icon for deleting if user is allowed
2779
            if (
2780
                (int) $dataItem['id_user'] === (int) $session->get('user-id')
2781
                || (int) $session->get('user-admin') === 1
2782
                || ((int) $session->get('user-manager') === 1 && (int) $SETTINGS['manager_edit'] === 1)
2783
                || (int) $dataItem['anyone_can_modify'] === 1
2784
                || in_array($dataItem['id_tree'], $session->get('system-list_folders_editable_by_role')) === true
2785
                || in_array($session->get('user-id'), $restrictedTo) === true
2786
                //|| count($restrictedTo) === 0
2787
                || (int) $post_folder_access_level === 30
2788
                || (int) $post_item_rights >= 40
2789
            ) {
2790
                $arrData['user_can_modify'] = 1;
2791
                $user_is_allowed_to_modify = true;
2792
            } else {
2793
                $arrData['user_can_modify'] = 0;
2794
                $user_is_allowed_to_modify = false;
2795
            }
2796
2797
            // Get restriction list for roles
2798
            $listRestrictionRoles = array();
2799
            if (isset($SETTINGS['restricted_to_roles']) && (int) $SETTINGS['restricted_to_roles'] === 1) {
2800
                // Add restriction if item is restricted to roles
2801
                $rows = DB::query(
2802
                    'SELECT t.title, t.id
2803
                    FROM ' . prefixTable('roles_title') . ' AS t
2804
                    INNER JOIN ' . prefixTable('restriction_to_roles') . ' AS r ON (t.id=r.role_id)
2805
                    WHERE r.item_id = %i
2806
                    ORDER BY t.title ASC',
2807
                    $inputData['id']
2808
                );
2809
                foreach ($rows as $record) {
2810
                    if (!in_array($record['title'], $listRestrictionRoles)) {
2811
                        array_push($listRestrictionRoles, $record['id']);
2812
                    }
2813
                }
2814
            }
2815
            // Check if any KB is linked to this item
2816
            if (isset($SETTINGS['enable_kb']) && (int) $SETTINGS['enable_kb'] === 1) {
2817
                $tmp = array();
2818
                $rows = DB::query(
2819
                    'SELECT k.label, k.id
2820
                    FROM ' . prefixTable('kb_items') . ' as i
2821
                    INNER JOIN ' . prefixTable('kb') . ' as k ON (i.kb_id=k.id)
2822
                    WHERE i.item_id = %i
2823
                    ORDER BY k.label ASC',
2824
                    $inputData['id']
2825
                );
2826
                foreach ($rows as $record) {
2827
                    array_push(
2828
                        $tmp,
2829
                        array(
2830
                            'id' => $record['id'],
2831
                            'label' => $record['label'],
2832
                        )
2833
                    );
2834
                }
2835
                $arrData['links_to_kbs'] = $tmp;
2836
            }
2837
            // Prepare DIalogBox data
2838
            if ((int) $post_expired_item === 0) {
2839
                $arrData['show_detail_option'] = 0;
2840
            } elseif ($user_is_allowed_to_modify === true && (int) $post_expired_item === 1) {
2841
                $arrData['show_detail_option'] = 1;
2842
            } else {
2843
                $arrData['show_detail_option'] = 2;
2844
            }
2845
2846
            $arrData['label'] = $dataItem['label'] === '' ? '' : $dataItem['label'];
2847
            $arrData['pw_length'] = strlen($pw);
2848
            $arrData['pw_decrypt_info'] = empty($pw) === true && $pwIsEmptyNormal === false ? 'error_no_sharekey_yet' : '';
2849
            $arrData['email'] = empty($dataItem['email']) === true || $dataItem['email'] === null ? '' : $dataItem['email'];
2850
            $arrData['url'] = empty($dataItem['url']) === true ? '' : $dataItem['url'];
2851
            $arrData['folder'] = $dataItem['id_tree'];
2852
            $arrData['description'] = $dataItem['description'];
2853
            $arrData['login'] = $dataItem['login'];
2854
            $arrData['id_restricted_to'] = $listeRestriction;
2855
            $arrData['id_restricted_to_roles'] = $listRestrictionRoles;
2856
            $arrData['tags'] = $tags;
2857
            $arrData['folder'] = (int) $dataItem['id_tree'];
2858
            $arrData['fa_icon'] = $dataItem['fa_icon'];
2859
            $arrData['item_key'] = $dataItem['item_key'];
2860
2861
            if (
2862
                isset($SETTINGS['enable_server_password_change'])
2863
                && (int) $SETTINGS['enable_server_password_change'] === 1
2864
            ) {
2865
                $arrData['auto_update_pwd_frequency'] = $dataItem['auto_update_pwd_frequency'];
2866
            } else {
2867
                $arrData['auto_update_pwd_frequency'] = '0';
2868
            }
2869
2870
            $arrData['anyone_can_modify'] = (int) $dataItem['anyone_can_modify'];
2871
2872
            // Add the fact that item has been viewed in logs
2873
            if (isset($SETTINGS['log_accessed']) && (int) $SETTINGS['log_accessed'] === 1) {
2874
                logItems(
2875
                    $SETTINGS,
2876
                    (int) $inputData['id'],
2877
                    $dataItem['label'],
2878
                    (int) $session->get('user-id'),
2879
                    'at_shown',
2880
                    $session->get('user-login')
2881
                );
2882
            }
2883
2884
            // statistics
2885
            DB::update(
2886
                prefixTable('items'),
2887
                array(
2888
                    'viewed_no' => $dataItem['viewed_no'] + 1,
2889
                    'updated_at' => time(),
2890
                ),
2891
                'id = %i',
2892
                $inputData['id']
2893
            );
2894
            $arrData['viewed_no'] = $dataItem['viewed_no'] + 1;
2895
2896
            // get fields
2897
            $fieldsTmp = array();
2898
            $arrCatList = $template_id = '';
2899
            if (isset($SETTINGS['item_extra_fields']) && (int) $SETTINGS['item_extra_fields'] === 1) {
2900
                // get list of associated Categories
2901
                $arrCatList = array();
2902
                $rows_tmp = DB::query(
2903
                    'SELECT id_category
2904
                    FROM ' . prefixTable('categories_folders') . '
2905
                    WHERE id_folder=%i',
2906
                    $inputData['folderId']
2907
                );
2908
                
2909
                if (DB::count() > 0) {
2910
                    foreach ($rows_tmp as $row) {
2911
                        array_push($arrCatList, (int) $row['id_category']);
2912
                    }
2913
2914
                    // get fields for this Item
2915
                    $rows_tmp = DB::query(
2916
                        'SELECT i.id AS id, i.field_id AS field_id, i.data AS data, i.item_id AS item_id,
2917
                        i.encryption_type AS encryption_type, c.encrypted_data AS encrypted_data, c.parent_id AS parent_id,
2918
                        c.type as field_type, c.masked AS field_masked, c.role_visibility AS role_visibility
2919
                        FROM ' . prefixTable('categories_items') . ' AS i
2920
                        INNER JOIN ' . prefixTable('categories') . ' AS c ON (i.field_id=c.id)
2921
                        WHERE i.item_id=%i AND c.parent_id IN %ls',
2922
                        $inputData['id'],
2923
                        $arrCatList
2924
                    );
2925
                    foreach ($rows_tmp as $row) {
2926
                        // Uncrypt data
2927
                        // Get the object key for the user
2928
                        //db::debugmode(true);
2929
                        $userKey = DB::queryFirstRow(
2930
                            'SELECT share_key
2931
                            FROM ' . prefixTable('sharekeys_fields') . '
2932
                            WHERE user_id = %i AND object_id = %i',
2933
                            $session->get('user-id'),
2934
                            $row['id']
2935
                        );
2936
                        //db::debugmode(false);
2937
                        $fieldText = [];
2938
                        if (DB::count() === 0 && (int) $row['encrypted_data'] === 1) {
2939
                            // Data should be encrypted but no key yet
2940
                            // System is currently creating the keys
2941
                            $fieldText = [
2942
                                'string' => '',
2943
                                'encrypted' => false,
2944
                                'error' => 'error_no_sharekey_yet',
2945
                            ];
2946
                        } else if (DB::count() === 0 && (int) $row['encrypted_data'] === 0) {
2947
                            // Data is not encrypted in DB
2948
                            $fieldText = [
2949
                                'string' => $row['data'],//#3945 - isBase64($row['data']) === true ? base64_decode($row['data']) : 
2950
                                'encrypted' => false,
2951
                                'error' => false,
2952
                            ];
2953
                        } else {
2954
                            // Data is encrypted in DB and we have a key
2955
                            $fieldText = [
2956
                                'string' => doDataDecryption(
2957
                                    $row['data'],
2958
                                    decryptUserObjectKey(
2959
                                        $userKey['share_key'],
2960
                                        $session->get('user-private_key')
2961
                                    )
2962
                                ),
2963
                                'encrypted' => true,
2964
                                'error' => '',
2965
                            ];
2966
                        }
2967
2968
                        // Manage textarea string
2969
                        /*if ($row['field_type'] === 'textarea') {
2970
                            $fieldText = $fieldText;
2971
                        }*/
2972
2973
                        // build returned list of Fields text
2974
                        array_push(
2975
                            $fieldsTmp,
2976
                            array(
2977
                                'id' => (int) $row['field_id'],
2978
                                'value' => $fieldText['string'],
2979
                                'encrypted' => (int) $fieldText['encrypted'],
2980
                                'parent_id' => (int) $row['parent_id'],
2981
                                'type' => $row['field_type'],
2982
                                'masked' => (int) $row['field_masked'],
2983
                                'error' => (string) $fieldText['error'],
2984
                            )
2985
                        );
2986
                    }
2987
                }
2988
            }
2989
2990
            // Now get the selected template (if exists)
2991
            if (isset($SETTINGS['item_creation_templates']) && (int) $SETTINGS['item_creation_templates'] === 1) {
2992
                $rows_tmp = DB::queryFirstRow(
2993
                    'SELECT category_id
2994
                    FROM ' . prefixTable('templates') . '
2995
                    WHERE item_id = %i',
2996
                    $inputData['id']
2997
                );
2998
                if (DB::count() > 0) {
2999
                    $template_id = $rows_tmp['category_id'];
3000
                }
3001
            }
3002
            //}
3003
            $arrData['fields'] = $fieldsTmp;
3004
            $arrData['categories'] = $arrCatList;
3005
            $arrData['template_id'] = (int) $template_id;
3006
            $arrData['to_be_deleted'] = '';
3007
3008
            // Evaluate if item is ready for all users
3009
            $rows_tmp = DB::queryFirstRow(
3010
                'SELECT finished_at
3011
                FROM ' . prefixTable('background_tasks') . '
3012
                WHERE item_id = %i',
3013
                $inputData['id']
3014
            );
3015
            $arrData['item_ready'] = DB::count() === 0 ? true : (DB::count() > 0 && empty($rows_tmp['finished_at']) === true ? false : true);
3016
3017
            // Manage user restriction
3018
            if (null !== $post_restricted) {
3019
                $arrData['restricted'] = $post_restricted;
3020
            } else {
3021
                $arrData['restricted'] = '';
3022
            }
3023
            // Decrement the number before being deleted
3024
            if (isset($SETTINGS['enable_delete_after_consultation']) && (int) $SETTINGS['enable_delete_after_consultation'] === 1) {
3025
                // Is the Item to be deleted?
3026
                $dataDelete = DB::queryFirstRow(
3027
                    'SELECT * 
3028
                    FROM ' . prefixTable('automatic_del') . '
3029
                    WHERE item_id = %i',
3030
                    $inputData['id']
3031
                );
3032
                if (DB::count() > 0) {
3033
                    $arrData['to_be_deleted'] = $dataDelete['del_value'];
3034
                    $arrData['to_be_deleted_type'] = (int) $dataDelete['del_type'];
3035
                }
3036
3037
                // Now delete if required
3038
                if ($dataDelete !== null && ((int) $dataDelete['del_enabled'] === 1
3039
                    || intval($arrData['id_user']) !== intval($session->get('user-id'))))
3040
                {
3041
                    if ((int) $dataDelete['del_type'] === 1 && $dataDelete['del_value'] >= 1) {
3042
                        // decrease counter
3043
                        DB::update(
3044
                            prefixTable('automatic_del'),
3045
                            array(
3046
                                'del_value' => $dataDelete['del_value'] - 1,
3047
                            ),
3048
                            'item_id = %i',
3049
                            $inputData['id']
3050
                        );
3051
                        // store value
3052
                        $arrData['to_be_deleted'] = $dataDelete['del_value'] - 1;
3053
                    } elseif (
3054
                        (int) $dataDelete['del_type'] === 1
3055
                        && $dataDelete['del_value'] <= 1
3056
                        || (int) $dataDelete['del_type'] === 2
3057
                        && $dataDelete['del_value'] < time()
3058
                    ) {
3059
                        $arrData['show_details'] = 0;
3060
                        // delete item
3061
                        DB::delete(prefixTable('automatic_del'), 'item_id = %i', $inputData['id']);
3062
                        // make inactive object
3063
                        DB::update(
3064
                            prefixTable('items'),
3065
                            array(
3066
                                'inactif' => 1,
3067
                                'deleted_at' => time(),
3068
                            ),
3069
                            'id = %i',
3070
                            $inputData['id']
3071
                        );
3072
3073
                        // log
3074
                        logItems(
3075
                            $SETTINGS,
3076
                            (int) $inputData['id'],
3077
                            $dataItem['label'],
3078
                            (int) $session->get('user-id'),
3079
                            'at_delete',
3080
                            $session->get('user-login'),
3081
                            'at_automatically_deleted'
3082
                        );
3083
3084
                        // Update cache table
3085
                        updateCacheTable('delete_value', (int) $inputData['id']);
3086
3087
                        $arrData['show_detail_option'] = 1;
3088
                        $arrData['to_be_deleted'] = 0;
3089
                    } elseif ($dataDelete['del_type'] === '2') {
3090
                        $arrData['to_be_deleted'] = date($SETTINGS['date_format'], (int) $dataDelete['del_value']);
3091
                    }
3092
                } else {
3093
                    $arrData['to_be_deleted'] = '';
3094
                }
3095
            } else {
3096
                $arrData['to_be_deleted'] = $lang->get('no');
3097
            }
3098
            // ---
3099
            // ---
3100
        } else {
3101
            $arrData['show_details'] = 0;
3102
            // get readable list of restriction
3103
            $listOfRestricted = '';
3104
            if (empty($dataItem['restricted_to']) === false) {
3105
                foreach (explode(';', $dataItem['restricted_to']) as $userRest) {
3106
                    if (empty($userRest) === false) {
3107
                        $dataTmp = DB::queryFirstRow(
3108
                            'SELECT login
3109
                            FROM ' . prefixTable('users') . '
3110
                            WHERE id= %i',
3111
                            $userRest
3112
                        );
3113
                        if (empty($listOfRestricted)) {
3114
                            $listOfRestricted = $dataTmp['login'];
3115
                        } else {
3116
                            $listOfRestricted .= ';' . $dataTmp['login'];
3117
                        }
3118
                    }
3119
                }
3120
            }
3121
            $arrData['restricted_to'] = $listOfRestricted;
3122
            $arrData['notification_list'] = '';
3123
            $arrData['notification_status'] = '';
3124
        }
3125
3126
        // Set a timestamp
3127
        $arrData['timestamp'] = time();
3128
3129
        // Set temporary session variable to allow step2
3130
        $session->set('system-show_step2', true);
3131
3132
        // Error
3133
        $arrData['error'] = '';
3134
3135
        // Encrypt data to return
3136
        echo (string) prepareExchangedData(
3137
            $arrData, 
3138
            'encode'
3139
        );
3140
        break;
3141
3142
    /*
3143
     * CASE
3144
     * Display History of the selected Item
3145
     */
3146
    case 'showDetailsStep2':
3147
        // Is this query expected (must be run after a step1 and not standalone)
3148
        if ($session->get('system-show_step2') !== true) {
3149
            // Check KEY and rights
3150
            if ($inputData['key'] !== $session->get('key')) {
3151
                echo (string) prepareExchangedData(
3152
                    array(
3153
                        'error' => true,
3154
                        'message' => $lang->get('key_is_not_correct'),
3155
                    ),
3156
                    'encode'
3157
                );
3158
                break;
3159
            }
3160
            if ($session->get('user-read_only') === 1) {
3161
                echo (string) prepareExchangedData(
3162
                    array(
3163
                        'error' => true,
3164
                        'message' => $lang->get('error_not_allowed_to'),
3165
                    ),
3166
                    'encode'
3167
                );
3168
                break;
3169
            }
3170
        }
3171
3172
        // prepare return array
3173
        $returnArray = [
3174
            'show_details' => 0,
3175
            'attachments' => [],
3176
            'favourite' => 0,
3177
            'otp_for_item_enabled' => 0,
3178
            'otp_phone_number' => '',
3179
            'otp_secret' => '',
3180
            'users_list' => [],
3181
            'roles_list' => [],
3182
            'has_change_proposal' => 0,
3183
            'setting_restricted_to_roles' => 0,
3184
            'otv_links' => 0,
3185
        ];
3186
3187
        // Load item data
3188
        $dataItem = DB::queryFirstRow(
3189
            '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
3190
            FROM ' . prefixTable('items') . ' AS i
3191
            INNER JOIN ' . prefixTable('nested_tree') . ' AS n ON (i.id_tree = n.id)
3192
            LEFT JOIN ' . prefixTable('items_otp') . ' AS o ON (o.item_id = i.id)
3193
            WHERE i.id = %i',
3194
            $inputData['id']
3195
        );
3196
3197
        // check that actual user can access this item
3198
        $restrictionActive = true;
3199
        $restrictedTo = is_null($dataItem['restricted_to']) === false ? array_filter(explode(';', $dataItem['restricted_to'])) : [];
3200
        if (
3201
            in_array($session->get('user-id'), $restrictedTo)
3202
            || (((int) $session->get('user-manager') === 1 || (int) $session->get('user-can_manage_all_users') === 1)
3203
                && (int) $SETTINGS['manager_edit'] === 1)
3204
        ) {
3205
            $restrictionActive = false;
3206
        }
3207
        if (empty($dataItem['restricted_to'])) {
3208
            $restrictionActive = false;
3209
        }
3210
3211
        // Check if user has a role that is accepted
3212
        $rows_tmp = DB::query(
3213
            'SELECT role_id
3214
            FROM ' . prefixTable('restriction_to_roles') . '
3215
            WHERE item_id=%i',
3216
            $inputData['id']
3217
        );
3218
        foreach ($rows_tmp as $rec_tmp) {
3219
            if (in_array($rec_tmp['role_id'], explode(';', $session->get('user-roles')))) {
3220
                $restrictionActive = false;
3221
            }
3222
        }
3223
3224
        // check user is admin
3225
        $session__list_restricted_folders_for_items = $session->get('system-list_restricted_folders_for_items') ?? [];
3226
        if (
3227
            (int) $session->get('user-admin') === 1
3228
            && (int) $dataItem['perso'] === 0
3229
        ) {
3230
            $returnArray['show_details'] = 0;
3231
            echo (string) prepareExchangedData(
3232
                $returnArray,
3233
                'encode'
3234
            );
3235
        // Get all expected data about this ITEM
3236
        } else {
3237
            // generate 2d key
3238
            $session->set('user-key_tmp', bin2hex(GenerateCryptKey(16, false, true, true, false, true)));
3239
3240
            // Prepare files listing
3241
            $attachments = [];
3242
            
3243
            // launch query
3244
            $rows = DB::query(
3245
                'SELECT id, name, file, extension, size
3246
                FROM ' . prefixTable('files') . '
3247
                WHERE id_item = %i AND confirmed = 1',
3248
                $inputData['id']
3249
            );
3250
            foreach ($rows as $record) {
3251
                $filename = basename($record['name'], '.' . $record['extension']);
3252
                $filename = isBase64($filename) === true ? base64_decode($filename) : $filename;
3253
3254
                array_push(
3255
                    $attachments,
3256
                    array(
3257
                        'icon' => fileFormatImage(strtolower($record['extension'])),
3258
                        'filename' => $filename,
3259
                        'extension' => $record['extension'],
3260
                        'size' => formatSizeUnits((int) $record['size']),
3261
                        'is_image' => in_array(strtolower($record['extension']), TP_IMAGE_FILE_EXT) === true ? 1 : 0,
3262
                        'id' => $record['id'],
3263
                        'key' => $session->get('user-key_tmp'),
3264
                        'internalFilename' => basename($record['name'], '.' . $record['extension']),
3265
                    )
3266
                );
3267
            }
3268
            $returnArray['attachments'] = $attachments;
3269
3270
            // disable add bookmark if alread bookmarked
3271
            $returnArray['favourite'] = in_array($inputData['id'], $session->get('user-favorites')) === true ? 1 : 0;
3272
            
3273
            // get OTP enabled for item
3274
            $returnArray['otp_for_item_enabled'] = (int) $dataItem['otp_for_item_enabled'];
3275
            $returnArray['otp_phone_number'] = (string) $dataItem['otp_phone_number'];
3276
            if (empty($dataItem['otp_secret']) === false) {
3277
                $secret = cryption(
3278
                    $dataItem['otp_secret'],
3279
                    '',
3280
                    'decrypt'
3281
                )['string'];
3282
            } else {
3283
                $secret = '';
3284
            }
3285
            $returnArray['otp_secret'] = (string) $secret;
3286
3287
            // Add this item to the latests list
3288
            if ($session->has('user-latest_items') && $session->has('user-latest_items') && null !== $session->get('user-latest_items') && isset($SETTINGS['max_latest_items']) && 
3289
                in_array($dataItem['id'], $session->get('user-latest_items')) === false
3290
            ) {
3291
                if (count($session->get('user-latest_items')) >= $SETTINGS['max_latest_items']) {
3292
                    // delete last items
3293
                    SessionManager::specificOpsOnSessionArray('user-latest_items', 'pop');
3294
                }
3295
                SessionManager::specificOpsOnSessionArray('user-latest_items', 'unshift', $dataItem['id']);
3296
                // update DB
3297
                DB::update(
3298
                    prefixTable('users'),
3299
                    array(
3300
                        'latest_items' => implode(';', $session->get('user-latest_items')),
3301
                    ),
3302
                    'id=' . $session->get('user-id')
3303
                );
3304
            }
3305
3306
            // get list of roles
3307
            $listOptionsForUsers = array();
3308
            $listOptionsForRoles = array();
3309
            $rows = DB::query(
3310
                'SELECT r.role_id AS role_id, t.title AS title
3311
                FROM ' . prefixTable('roles_values') . ' AS r
3312
                INNER JOIN ' . prefixTable('roles_title') . ' AS t ON (r.role_id = t.id)
3313
                WHERE r.folder_id = %i',
3314
                $dataItem['id_tree']
3315
            );
3316
            foreach ($rows as $record) {
3317
                array_push(
3318
                    $listOptionsForRoles,
3319
                    array(
3320
                        'id' => (int) $record['role_id'],
3321
                        'title' => $record['title'],
3322
                    )
3323
                );
3324
                $rows2 = DB::query(
3325
                    'SELECT id, login, fonction_id, email, name, lastname
3326
                    FROM ' . prefixTable('users') . '
3327
                    WHERE fonction_id LIKE %s',
3328
                    '%' . $record['role_id'] . '%'
3329
                );
3330
                foreach ($rows2 as $record2) {
3331
                    foreach (explode(';', $record2['fonction_id']) as $role) {
3332
                        if (
3333
                            array_search($record2['id'], array_column($listOptionsForUsers, 'id')) === false
3334
                            && $role === $record['role_id']
3335
                        ) {
3336
                            array_push(
3337
                                $listOptionsForUsers,
3338
                                array(
3339
                                    'id' => (int) $record2['id'],
3340
                                    'login' => $record2['login'],
3341
                                    'name' => $record2['name'] . ' ' . $record2['lastname'],
3342
                                    'email' => $record2['email'],
3343
                                )
3344
                            );
3345
                        }
3346
                    }
3347
                }
3348
            }
3349
3350
            $returnArray['users_list'] = $listOptionsForUsers;
3351
            $returnArray['roles_list'] = $listOptionsForRoles;
3352
3353
            // send notification if enabled
3354
            if (isset($SETTINGS['enable_email_notification_on_item_shown']) === true && (int) $SETTINGS['enable_email_notification_on_item_shown'] === 1) {
3355
                // Get path
3356
                $arbo = $tree->getPath($dataItem['id_tree'], true);
3357
                $path = '';
3358
                foreach ($arbo as $elem) {
3359
                    if (empty($path) === true) {
3360
                        $path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' ';
3361
                    } else {
3362
                        $path .= '&#8594; ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
3363
                    }
3364
                }
3365
                // Build text to show user
3366
                if (empty($path) === true) {
3367
                    $path = addslashes($dataItem['label']);
3368
                } else {
3369
                    $path = addslashes($dataItem['label']) . ' (' . $path . ')';
3370
                }
3371
3372
                // Add Admins to notification list if expected
3373
                $reveivers = [];
3374
                $rows = DB::query(
3375
                    'SELECT email
3376
                    FROM ' . prefixTable('users').'
3377
                    WHERE admin = %i',
3378
                    1
3379
                );
3380
                foreach ($rows as $user) {
3381
                    array_push($reveivers, $user['email']);
3382
                }
3383
3384
                // prepare sending email
3385
                prepareSendingEmail(
3386
                    $lang->get('email_on_open_notification_subject'),
3387
                    str_replace(
3388
                        array('#tp_user#', '#tp_item#', '#tp_link#'),
3389
                        array(
3390
                            addslashes($session->get('user-login')),
3391
                            $path,
3392
                            $SETTINGS['cpassman_url'] . '/index.php?page=items&group=' . $dataItem['id_tree'] . '&id=' . $dataItem['id'],
3393
                        ),
3394
                        $lang->get('email_on_open_notification_mail')
3395
                    ),
3396
                    implode(",", $reveivers),
3397
                    ""
3398
                );
3399
            }
3400
3401
            // has this item a change proposal
3402
            DB::query('SELECT * FROM ' . prefixTable('items_change') . ' WHERE item_id = %i', $inputData['id']);
3403
            $returnArray['has_change_proposal'] = DB::count();
3404
3405
            // Setting
3406
            $returnArray['setting_restricted_to_roles'] = isset($SETTINGS['restricted_to_roles']) === true
3407
                && (int) $SETTINGS['restricted_to_roles'] === 1 ? 1 : 0;
3408
3409
            // get OTV links
3410
            if (isset($SETTINGS['otv_is_enabled']) === true && (int) $SETTINGS['otv_is_enabled'] === 1) {
3411
                DB::query(
3412
                    'SELECT *
3413
                    FROM ' . prefixTable('otv') . '
3414
                    WHERE item_id = %i
3415
                    AND time_limit > %i',
3416
                    $inputData['id'],
3417
                    time()
3418
                );
3419
                $returnArray['otv_links'] = (int) DB::count();
3420
            }
3421
3422
            $session->set('system-show_step2', false);
3423
            
3424
            // deepcode ignore ServerLeak: Data is encrypted before being sent
3425
            echo (string) prepareExchangedData(
3426
                $returnArray,
3427
                'encode'
3428
            );
3429
        }
3430
        break;
3431
3432
    /*
3433
     * CASE
3434
     * Delete an item
3435
     */
3436
    case 'delete_item':
3437
        // Check KEY and rights
3438
        if ($inputData['key'] !== $session->get('key')) {
3439
            echo (string) prepareExchangedData(
3440
                array(
3441
                    'error' => true,
3442
                    'message' => $lang->get('key_is_not_correct'),
3443
                ),
3444
                'encode'
3445
            );
3446
            break;
3447
        }
3448
        if ($session->get('user-read_only') === 1) {
3449
            echo (string) prepareExchangedData(
3450
                array(
3451
                    'error' => true,
3452
                    'message' => $lang->get('error_not_allowed_to'),
3453
                ),
3454
                'encode'
3455
            );
3456
            break;
3457
        }
3458
3459
        // decrypt and retreive data in JSON format
3460
        $dataReceived = prepareExchangedData(
3461
            $inputData['data'],
3462
            'decode'
3463
        );
3464
        
3465
        // Prepare POST variables
3466
        $data = [
3467
            'itemId' => isset($dataReceived['item_id']) === true ? $dataReceived['item_id'] : '',
3468
            'folderId' => isset($dataReceived['folder_id']) === true ? $dataReceived['folder_id'] : '',
3469
            'accessLevel' => isset($dataReceived['access_level']) === true ? $dataReceived['access_level'] : '',
3470
            'itemKey' => isset($dataReceived['item_key']) === true ? $dataReceived['item_key'] : '',
3471
        ];
3472
        
3473
        $filters = [
3474
            'itemId' => 'cast:integer',
3475
            'folderId' => 'cast:integer',
3476
            'accessLevel' => 'cast:integer',
3477
            'itemKey' => 'trim|escape',
3478
        ];
3479
        
3480
        $inputData = dataSanitizer(
3481
            $data,
3482
            $filters
3483
        );
3484
        
3485
        if (empty($inputData['itemId']) === true && (empty($inputData['itemKey']) === true || is_null($inputData['itemKey']) === true)) {
3486
            echo (string) prepareExchangedData(
3487
                array(
3488
                    'error' => true,
3489
                    'message' => $lang->get('nothing_to_do'),
3490
                ),
3491
                'encode'
3492
            );
3493
            break;
3494
        }
3495
3496
        // Check that user can access this item
3497
        $granted = accessToItemIsGranted($inputData['itemId'], $SETTINGS);
3498
        if ($granted !== true) {
3499
            echo (string) prepareExchangedData(
3500
                array(
3501
                    'error' => true,
3502
                    'message' => $granted,
3503
                ),
3504
                'encode'
3505
            );
3506
            break;
3507
        }
3508
3509
        // Load item data
3510
        $data = DB::queryFirstRow(
3511
            'SELECT id_tree, id, label
3512
            FROM ' . prefixTable('items') . '
3513
            WHERE id = %i OR item_key = %s',
3514
            $inputData['itemId'],
3515
            $inputData['itemKey']
3516
        );
3517
        if (empty($inputData['itemId']) === true) {
3518
            $inputData['itemId'] = $data['id'];
3519
        }
3520
        $inputData['label'] = $data['label'];
3521
3522
        // Check that user can delete on this folder
3523
        $checkRights = getCurrentAccessRights(
3524
            $session->get('user-id'),
3525
            $inputData['itemId'],
3526
            (int) $data['id_tree'],
3527
        );
3528
3529
        if ($checkRights['error'] || !$checkRights['delete']) {
3530
            echo (string) prepareExchangedData(
3531
                array(
3532
                    'error' => true,
3533
                    'message' => $lang->get('error_not_allowed_to'),
3534
                ),
3535
                'encode'
3536
            );
3537
        }
3538
3539
        // delete item consists in disabling it
3540
        DB::update(
3541
            prefixTable('items'),
3542
            array(
3543
                'inactif' => '1',
3544
                'deleted_at' => time(),
3545
            ),
3546
            'id = %i OR item_key = %s',
3547
            $inputData['itemId'],
3548
            $inputData['itemKey']
3549
        );
3550
3551
        // log
3552
        logItems(
3553
            $SETTINGS,
3554
            (int) $inputData['itemId'],
3555
            $inputData['label'],
3556
            $session->get('user-id'),
3557
            'at_delete',
3558
            $session->get('user-login')
3559
        );
3560
        // Update CACHE table
3561
        updateCacheTable('delete_value', (int) $inputData['itemId']);
3562
3563
        echo (string) prepareExchangedData(
3564
            array(
3565
                'error' => false,
3566
                'message' => '',
3567
            ),
3568
            'encode'
3569
        );
3570
        break;
3571
3572
        
3573
    /*
3574
     * CASE
3575
     * Display OTP of the selected Item
3576
     */
3577
    case 'show_opt_code':
3578
        // Check KEY and rights
3579
        if ($inputData['key'] !== $session->get('key')) {
3580
            echo (string) prepareExchangedData(
3581
                array(
3582
                    'error' => true,
3583
                    'message' => $lang->get('key_is_not_correct'),
3584
                ),
3585
                'encode'
3586
            );
3587
            break;
3588
        }
3589
        if ($session->get('user-read_only') === 1) {
3590
            echo (string) prepareExchangedData(
3591
                array(
3592
                    'error' => true,
3593
                    'message' => $lang->get('error_not_allowed_to'),
3594
                ),
3595
                'encode'
3596
            );
3597
            break;
3598
        }
3599
3600
        // Load item data
3601
        $dataItem = DB::queryFirstRow(
3602
            'SELECT secret, enabled
3603
            FROM ' . prefixTable('items_otp') . '
3604
            WHERE item_id = %i',
3605
            $inputData['id']
3606
        );
3607
3608
        if (DB::count() > 0) {
3609
            // OTP exists then display it
3610
            $secret = cryption(
3611
                $dataItem['secret'],
3612
                '',
3613
                'decrypt'
3614
            )['string'];
3615
        }
3616
        
3617
        // Generate OTP code
3618
        if (empty($secret) === false) {
3619
            try {
3620
                $otp = TOTP::createFromSecret($secret);
3621
                $otpCode = $otp->now();
3622
                $otpExpiresIn = $otp->expiresIn();
3623
            } catch (RuntimeException $e) {
3624
                $error = true;
3625
                $otpCode = '';
3626
                $otpExpiresIn = '';
3627
                $message = $e->getMessage();
3628
            }
3629
        } else {
3630
            $otpCode = '';
3631
            $otpExpiresIn = '';
3632
        }
3633
        
3634
        // deepcode ignore ServerLeak: Data is encrypted before being sent
3635
        echo (string) prepareExchangedData(
3636
            array(
3637
                'error' => isset($error) === true ? $error : false,
3638
                'message' => isset($message) === true ? $message : '',
3639
                'otp_code' => $otpCode,
3640
                'otp_expires_in' => $otpExpiresIn,
3641
                'otp_enabled' => $dataItem['enabled'],
3642
            ),
3643
            'encode'
3644
        );
3645
        break;
3646
3647
    /*
3648
     * CASE
3649
     * Update a Group
3650
     */
3651
    case 'update_folder':
3652
        // Check KEY and rights
3653
        if ($inputData['key'] !== $session->get('key')) {
3654
            echo (string) prepareExchangedData(
3655
                array(
3656
                    'error' => true,
3657
                    'message' => $lang->get('key_is_not_correct'),
3658
                ),
3659
                'encode'
3660
            );
3661
            break;
3662
        }
3663
        if ($session->get('user-read_only') === 1) {
3664
            echo (string) prepareExchangedData(
3665
                array(
3666
                    'error' => true,
3667
                    'message' => $lang->get('error_not_allowed_to'),
3668
                ),
3669
                'encode'
3670
            );
3671
            break;
3672
        }
3673
        // decrypt and retreive data in JSON format
3674
        $dataReceived = prepareExchangedData(
3675
            $inputData['data'],
3676
            'decode'
3677
        );
3678
3679
        // Prepare variables
3680
        $title = filter_var(htmlspecialchars_decode($dataReceived['title'], ENT_QUOTES), FILTER_SANITIZE_FULL_SPECIAL_CHARS);
3681
        $inputData['folderId'] = filter_var(htmlspecialchars_decode($dataReceived['folder']), FILTER_SANITIZE_NUMBER_INT);
3682
3683
        // Check if user is allowed to access this folder
3684
        if (!in_array($inputData['folderId'], $session->get('user-accessible_folders'))) {
3685
            echo '[{"error" : "' . $lang->get('error_not_allowed_to') . '"}]';
3686
            break;
3687
        }
3688
3689
        // Check if title doesn't contains html codes
3690
        if (preg_match_all('|<[^>]+>(.*)</[^>]+>|U', $title, $out)) {
3691
            echo '[ { "error" : "' . $lang->get('error_html_codes') . '" } ]';
3692
            break;
3693
        }
3694
        // check that title is not numeric
3695
        if (is_numeric($title) === true) {
3696
            echo '[{"error" : "ERR_TITLE_ONLY_WITH_NUMBERS"}]';
3697
            break;
3698
        }
3699
3700
        // Check if duplicate folders name are allowed
3701
        if (isset($SETTINGS['duplicate_folder']) && $SETTINGS['duplicate_folder'] === '0') {
3702
            $data = DB::queryFirstRow('SELECT id, title FROM ' . prefixTable('nested_tree') . ' WHERE title = %s', $title);
3703
            if (empty($data['id']) === false && $dataReceived['folder'] !== $data['id']) {
3704
                echo '[ { "error" : "' . $lang->get('error_group_exist') . '" } ]';
3705
                break;
3706
            }
3707
        }
3708
3709
        // query on folder
3710
        $data = DB::queryFirstRow(
3711
            'SELECT parent_id, personal_folder
3712
            FROM ' . prefixTable('nested_tree') . '
3713
            WHERE id = %i',
3714
            $inputData['folderId']
3715
        );
3716
3717
        // check if complexity level is good
3718
        // if manager or admin don't care
3719
        if ($session->get('user-admin') !== 1 && $session->get('user-manager') !== 1 && $data['personal_folder'] === '0') {
3720
            $data = DB::queryFirstRow(
3721
                'SELECT valeur
3722
                FROM ' . prefixTable('misc') . '
3723
                WHERE intitule = %i AND type = %s',
3724
                $data['parent_id'],
3725
                'complex'
3726
            );
3727
            if (intval($dataReceived['complexity']) < intval($data['valeur'])) {
3728
                echo '[ { "error" : "' . $lang->get('error_folder_complexity_lower_than_top_folder') . ' [<b>' . TP_PW_COMPLEXITY[$data['valeur']][1] . '</b>]"} ]';
3729
                break;
3730
            }
3731
        }
3732
3733
        // update Folders table
3734
        $tmp = DB::queryFirstRow(
3735
            'SELECT title, parent_id, personal_folder FROM ' . prefixTable('nested_tree') . ' WHERE id = %i',
3736
            $dataReceived['folder']
3737
        );
3738
        if ($tmp['parent_id'] !== 0 || $tmp['title'] !== $session->get('user-id') || $tmp['personal_folder'] !== 1) {
3739
            DB::update(
3740
                prefixTable('nested_tree'),
3741
                array(
3742
                    'title' => $title,
3743
                ),
3744
                'id=%s',
3745
                $inputData['folderId']
3746
            );
3747
            // update complixity value
3748
            DB::update(
3749
                prefixTable('misc'),
3750
                array(
3751
                    'valeur' => $dataReceived['complexity'],
3752
                    'updated_at' => time(),
3753
                ),
3754
                'intitule = %s AND type = %s',
3755
                $inputData['folderId'],
3756
                'complex'
3757
            );
3758
            // rebuild fuild tree folder
3759
            $tree->rebuild();
3760
        }
3761
        // send data
3762
        echo '[{"error" : ""}]';
3763
        break;
3764
3765
    /*
3766
     * CASE
3767
     * List items of a group
3768
     */
3769
    case 'do_items_list_in_folder':
3770
        // Check KEY and rights
3771
        if ($inputData['key'] !== $session->get('key')) {
3772
            echo (string) prepareExchangedData(
3773
                array(
3774
                    'error' => true,
3775
                    'message' => $lang->get('error_not_allowed_to')." BOOH 1",
3776
                ),
3777
                'encode'
3778
            );
3779
            break;
3780
        }
3781
3782
        if (count($session->get('user-roles_array')) === 0) {
3783
            echo (string) prepareExchangedData(
3784
                array(
3785
                    'error' => true,
3786
                    'message' => $lang->get('error_not_allowed_to'),
3787
                ),
3788
                'encode'
3789
            );
3790
            break;
3791
        }
3792
3793
        // decrypt and retreive data in JSON format
3794
        $dataReceived = prepareExchangedData(
3795
            $inputData['data'],
3796
            'decode'
3797
        );
3798
3799
        if (is_array($dataReceived) === true && array_key_exists('id', $dataReceived) === false) {
3800
            echo (string) prepareExchangedData(
3801
                array(
3802
                    'error' => true,
3803
                    'message' => $lang->get('error_unknown'),
3804
                ),
3805
                'encode'
3806
            );
3807
            break;
3808
        }
3809
3810
        // Prepare POST variables
3811
        $inputData['id'] = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
3812
        $post_restricted = filter_var($dataReceived['restricted'], FILTER_SANITIZE_NUMBER_INT);
3813
        $post_start = filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT);
3814
        $post_nb_items_to_display_once = filter_var($dataReceived['nb_items_to_display_once'], FILTER_SANITIZE_NUMBER_INT);
3815
3816
        $arr_arbo = [];
3817
        $folderIsPf = in_array($inputData['id'], $session->get('user-personal_folders')) === true ? true : false;
3818
        $showError = 0;
3819
        $itemsIDList = $rights = $returnedData = $uniqueLoadData = $html_json = array();
3820
        // Build query limits
3821
        if (empty($post_start) === true) {
3822
            $start = 0;
3823
        } else {
3824
            $start = $post_start;
3825
        }
3826
3827
        // to do only on 1st iteration
3828
        if ((int) $start === 0) {
3829
            // Prepare tree
3830
            $arbo = $tree->getPath($inputData['id'], true);
3831
            foreach ($arbo as $elem) {
3832
                // Personnal folder
3833
                if ((int) $elem->title === (int) $session->get('user-id') && (int) $elem->nlevel === 1) {
3834
                    $elem->title = $session->get('user-login');
3835
                }
3836
                // Store path elements
3837
                array_push(
3838
                    $arr_arbo,
3839
                    array(
3840
                        'id' => $elem->id,
3841
                        'title' => htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES),
3842
                        'visible' => in_array($elem->id, $session->get('user-accessible_folders')) ? 1 : 0,
3843
                    )
3844
                );
3845
            }
3846
            $uniqueLoadData['path'] = $arr_arbo;
3847
3848
            // store last folder accessed in cookie
3849
            $arr_cookie_options = array (
3850
                'expires' => time() + TP_ONE_DAY_SECONDS * 5,
3851
                'path' => '/', 
3852
                'secure' => true,
3853
                'httponly' => true,
3854
                'samesite' => 'Lax' // None || Lax  || Strict
3855
            );
3856
            // deepcode ignore WebCookieSecureDisabledByDefault: defined in $arr_cookie_options, deepcode ignore WebCookieHttpOnlyDisabledByDefault: defined in $arr_cookie_options
3857
            setcookie('jstree_select', $inputData['id'], $arr_cookie_options);
3858
3859
            // CHeck if roles have 'allow_pw_change' set to true
3860
            $forceItemEditPrivilege = false;
3861
            foreach ($session->get('user-roles_array') as $role) {
3862
                $roleQ = DB::queryFirstRow(
3863
                    'SELECT allow_pw_change
3864
                    FROM ' . prefixTable('roles_title') . '
3865
                    WHERE id = %i',
3866
                    $role
3867
                );
3868
                if ((int) $roleQ['allow_pw_change'] === 1) {
3869
                    $forceItemEditPrivilege = true;
3870
                    break;
3871
                }
3872
            }
3873
3874
            // is this folder a personal one
3875
            $folder_is_personal = in_array($inputData['id'], $session->get('user-personal_folders'));
3876
            $uniqueLoadData['folder_is_personal'] = $folder_is_personal;
3877
3878
            $folder_is_in_personal = in_array(
3879
                $inputData['id'],
3880
                array_merge(
3881
                    $session->get('user-personal_visible_folders'),
3882
                    $session->get('user-personal_folders')
3883
                )
3884
            );
3885
            $uniqueLoadData['folder_is_in_personal'] = $folder_is_in_personal;
3886
3887
3888
            // check role access on this folder (get the most restrictive) (2.1.23)
3889
            if ((int) $folder_is_personal === 0) {
3890
                $accessLevel = 20;
3891
                $arrTmp = [];
3892
                
3893
                foreach ($session->get('user-roles_array') as $role) {
3894
                    $access = DB::queryFirstRow(
3895
                        'SELECT type FROM ' . prefixTable('roles_values') . ' WHERE role_id = %i AND folder_id = %i',
3896
                        $role,
3897
                        $inputData['id']
3898
                    );
3899
                    if (DB::count()>0) {
3900
                        if ($access['type'] === 'R') {
3901
                            array_push($arrTmp, 10);
3902
                        } elseif ($access['type'] === 'W') {
3903
                            array_push($arrTmp, 30);
3904
                        } elseif (
3905
                            $access['type'] === 'ND'
3906
                            || ($forceItemEditPrivilege === true && $access['type'] === 'NDNE')
3907
                        ) {
3908
                            array_push($arrTmp, 20);
3909
                        } elseif ($access['type'] === 'NE') {
3910
                            array_push($arrTmp, 10);
3911
                        } elseif ($access['type'] === 'NDNE') {
3912
                            array_push($arrTmp, 15);
3913
                        } else {
3914
                            // Ensure to give access Right if allowed folder
3915
                            if (in_array($inputData['id'], $session->get('user-accessible_folders')) === true) {
3916
                                array_push($arrTmp, 30);
3917
                            } else {
3918
                                array_push($arrTmp, 0);
3919
                            }
3920
                        }
3921
                    } else {
3922
                        // Ensure to give access Right if allowed folder
3923
                        if (in_array($inputData['id'], $session->get('user-accessible_folders')) === true) {
3924
                            array_push($arrTmp, 50);
3925
                        } else {
3926
                            array_push($arrTmp, 0);
3927
                        }
3928
                    }
3929
                }
3930
                // 3.0.0.0 - changed  MIN to MAX
3931
                $accessLevel = count($arrTmp) > 0 ? max($arrTmp) : $accessLevel;
3932
            } else {
3933
                $accessLevel = 30;
3934
            }
3935
            $uniqueLoadData['accessLevel'] = $accessLevel;
3936
            $uniqueLoadData['showError'] = $showError;
3937
3938
            // check if items exist
3939
            $where = new WhereClause('and');
3940
            $session__user_list_folders_limited = $session->get('user-list_folders_limited');
3941
            if (null !== $post_restricted && (int) $post_restricted === 1 && empty($session__user_list_folders_limited[$inputData['id']]) === false) {
3942
                $counter = count($session__user_list_folders_limited[$inputData['id']]);
3943
                $uniqueLoadData['counter'] = $counter;
3944
                // check if this folder is visible
3945
            } elseif (!in_array(
3946
                $inputData['id'],
3947
                array_merge(
3948
                    $session->get('user-accessible_folders'),
3949
                    array_keys($session->get('system-list_restricted_folders_for_items')),
3950
                    array_keys($session->get('user-list_folders_limited'))
3951
                )
3952
            )) {
3953
                echo (string) prepareExchangedData(
3954
                    array(
3955
                        'error' => 'not_authorized',
3956
                        'arborescence' => $arr_arbo,
3957
                    ),
3958
                    'encode'
3959
                );
3960
                break;
3961
            } else {
3962
                DB::query(
3963
                    'SELECT *
3964
                    FROM ' . prefixTable('items') . '
3965
                    WHERE inactif = %i',
3966
                    0
3967
                );
3968
                $counter = DB::count();
3969
                $uniqueLoadData['counter'] = $counter;
3970
            }
3971
3972
            // Get folder complexity
3973
            $folderComplexity = DB::queryFirstRow(
3974
                'SELECT valeur FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %i',
3975
                'complex',
3976
                $inputData['id']
3977
            );
3978
            $folderComplexity = $folderComplexity !== null ? (int) $folderComplexity['valeur'] : 0;
3979
            $uniqueLoadData['folderComplexity'] = $folderComplexity;
3980
3981
            // Has this folder some categories to be displayed?
3982
            $categoriesStructure = array();
3983
            if (isset($SETTINGS['item_extra_fields']) && (int) $SETTINGS['item_extra_fields'] === 1) {
3984
                $folderRow = DB::query(
3985
                    'SELECT id_category
3986
                    FROM ' . prefixTable('categories_folders') . '
3987
                    WHERE id_folder = %i',
3988
                    $inputData['id']
3989
                );
3990
                foreach ($folderRow as $category) {
3991
                    array_push(
3992
                        $categoriesStructure,
3993
                        $category['id_category']
3994
                    );
3995
                }
3996
            }
3997
            $uniqueLoadData['categoriesStructure'] = $categoriesStructure;
3998
3999
            /*$categoriesStructure = array();
4000
            if (isset($SETTINGS['item_extra_fields']) && (int) $SETTINGS['item_extra_fields'] === 1) {
4001
                $folderRow = DB::query(
4002
                    'SELECT f.id_category, c.title AS title
4003
                    FROM '.prefixTable('categories_folders').' AS f
4004
                    INNER JOIN '.prefixTable('categories').' AS c ON (c.id = f.id_category)
4005
                    WHERE f.id_folder = %i',
4006
                    $inputData['id']
4007
                );
4008
                foreach ($folderRow as $category) {
4009
                    $arrFields = array();
4010
                    // Get each category definition with fields
4011
                    $categoryRow = DB::query(
4012
                        "SELECT *
4013
                        FROM ".prefixTable("categories")."
4014
                        WHERE parent_id=%i
4015
                        ORDER BY `order` ASC",
4016
                        $category['id_category']
4017
                    );
4018
4019
                    if (DB::count() > 0) {
4020
                        foreach ($categoryRow as $field) {
4021
                            // Is this Field visibile by user?
4022
                            if ($field['role_visibility'] === 'all'
4023
                                || count(
4024
                                    array_intersect(
4025
                                        explode(';', $session->get('user-roles')),
4026
                                        explode(',', $field['role_visibility'])
4027
                                    )
4028
                                ) > 0
4029
                            ) {
4030
                                array_push(
4031
                                    $arrFields,
4032
                                    array(
4033
                                        $field['id'],
4034
                                        $field['title'],
4035
                                        $field['encrypted_data'],
4036
                                        $field['type'],
4037
                                        $field['masked'],
4038
                                        $field['is_mandatory']
4039
                                    )
4040
                                );
4041
                            }
4042
                        }
4043
                    }
4044
4045
                    // store the categories
4046
                    array_push(
4047
                        $categoriesStructure,
4048
                        array(
4049
                            $category['id_category'],
4050
                            $category['title'],
4051
                            $arrFields
4052
                        )
4053
                    );
4054
                }
4055
            }
4056
            $uniqueLoadData['categoriesStructure'] = $categoriesStructure;
4057
            */
4058
4059
            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')) {
4060
                $list_folders_editable_by_role = in_array($inputData['id'], $session->get('system-list_folders_editable_by_role'));
4061
            } else {
4062
                $list_folders_editable_by_role = '';
4063
            }
4064
            $uniqueLoadData['list_folders_editable_by_role'] = $list_folders_editable_by_role;
4065
        } else {
4066
            $uniqueLoadData = json_decode(
4067
                filter_var($dataReceived['uniqueLoadData'], FILTER_UNSAFE_RAW),
4068
                true
4069
            );
4070
4071
            // initialize main variables
4072
            $showError = $uniqueLoadData['showError'];
4073
            $accessLevel = $uniqueLoadData['accessLevel'];
4074
            $counter = $uniqueLoadData['counter'];
4075
            $counter_full = $uniqueLoadData['counter_full'];
4076
            $categoriesStructure = $uniqueLoadData['categoriesStructure'];
4077
            $folderComplexity = $uniqueLoadData['folderComplexity'];
4078
            $folder_is_personal = $uniqueLoadData['folder_is_personal'];
4079
            $folder_is_in_personal = $uniqueLoadData['folder_is_in_personal'];
4080
            //$list_folders_editable_by_role = $uniqueLoadData['list_folders_editable_by_role'];
4081
        }
4082
        
4083
        // prepare query WHere conditions
4084
        $where = new WhereClause('and');
4085
        $session__user_list_folders_limited = $session->get('user-list_folders_limited');
4086
        if (null !== $post_restricted && (int) $post_restricted === 1 && empty($session__user_list_folders_limited[$inputData['id']]) === false) {
4087
            $where->add('i.id IN %ls', $session__user_list_folders_limited[$inputData['id']]);
4088
        } else {
4089
            $where->add('i.id_tree=%i', $inputData['id']);
4090
        }
4091
4092
        // build the HTML for this set of Items
4093
        if ($counter > 0 && empty($showError)) {
4094
            // init variables
4095
            $expired_item = false;
4096
            $limited_to_items = '';
4097
4098
            // List all ITEMS
4099
            if ($folderIsPf === false) {
4100
                $where->add('i.inactif=%i', 0);
4101
                $sql_e='(SELECT date FROM ' . prefixTable('log_items') 
4102
                    . " WHERE action = 'at_creation' AND id_item=i.id " 
4103
                    . 'union all SELECT date FROM '. prefixTable('log_items') 
4104
                    . " WHERE action = 'at_modification' AND raison = 'at_pw'
4105
                    AND id_item=i.id ORDER BY date DESC LIMIT 1)";
4106
                $where->add('l.date=%l', $sql_e);
4107
4108
                $query_limit = ' LIMIT ' .
4109
                    $start . ',' .
4110
                    $post_nb_items_to_display_once;
4111
                //db::debugmode(true);
4112
                $rows = DB::query(
4113
                    'SELECT i.id AS id, i.item_key AS item_key, MIN(i.restricted_to) AS restricted_to, MIN(i.perso) AS perso,
4114
                    MIN(i.label) AS label, MIN(i.description) AS description, MIN(i.pw) AS pw, MIN(i.login) AS login,
4115
                    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,
4116
                    MIN(n.renewal_period) AS renewal_period,
4117
                    MIN(l.action) AS log_action,
4118
                    l.id_user AS log_user,
4119
                    i.url AS link,
4120
                    i.email AS email
4121
                    FROM ' . prefixTable('items') . ' AS i
4122
                    INNER JOIN ' . prefixTable('nested_tree') . ' AS n ON (i.id_tree = n.id)
4123
                    INNER JOIN ' . prefixTable('log_items') . ' AS l ON (i.id = l.id_item)
4124
                    WHERE %l
4125
                    GROUP BY i.id, l.date, l.id_user, l.action
4126
                    ORDER BY i.label ASC, l.date DESC' . $query_limit,
4127
                    $where
4128
                );
4129
                //db::debugmode(false);
4130
            } else {
4131
                $post_nb_items_to_display_once = 'max';
4132
                $where->add('i.inactif=%i', 0);
4133
4134
                $rows = DB::query(
4135
                    'SELECT i.id AS id, i.item_key AS item_key, MIN(i.restricted_to) AS restricted_to, MIN(i.perso) AS perso,
4136
                    MIN(i.label) AS label, MIN(i.description) AS description, MIN(i.pw) AS pw, MIN(i.login) AS login,
4137
                    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,
4138
                    MIN(n.renewal_period) AS renewal_period,
4139
                    MIN(l.action) AS log_action,
4140
                    l.id_user AS log_user,
4141
                    i.url AS link,
4142
                    i.email AS email
4143
                    FROM ' . prefixTable('items') . ' AS i
4144
                    INNER JOIN ' . prefixTable('nested_tree') . ' AS n ON (i.id_tree = n.id)
4145
                    INNER JOIN ' . prefixTable('log_items') . ' AS l ON (i.id = l.id_item)
4146
                    WHERE %l
4147
                    GROUP BY i.id, l.date, l.id_user, l.action
4148
                    ORDER BY i.label ASC, l.date DESC',
4149
                    $where
4150
                );
4151
            }
4152
4153
            $idManaged = '';
4154
4155
            foreach ($rows as $record) {
4156
                // exclude all results except the first one returned by query
4157
                if (empty($idManaged) === true || $idManaged !== $record['id']) {
4158
                    // Fix a bug on Personal Item creation - field `perso` must be set to `1`
4159
                    if ((int) $record['perso'] !== 1 && (int) $folder_is_personal === 1) {
4160
                        DB::update(
4161
                            prefixTable('items'),
4162
                            array(
4163
                                'perso' => 1,
4164
                                'updated_at' => time(),
4165
                            ),
4166
                            'id=%i',
4167
                            $record['id']
4168
                        );
4169
                        $record['perso'] = 1;
4170
                    }
4171
4172
                    // Does this item has restriction to groups of users?
4173
                    $item_is_restricted_to_role = false;
4174
                    DB::queryFirstRow(
4175
                        'SELECT role_id
4176
                        FROM ' . prefixTable('restriction_to_roles') . '
4177
                        WHERE item_id = %i',
4178
                        $record['id']
4179
                    );
4180
                    if (DB::count() > 0) {
4181
                        $item_is_restricted_to_role = true;
4182
                    }
4183
4184
                    // Has this item a restriction to Groups of Users
4185
                    $user_is_included_in_role = false;
4186
                    DB::query(
4187
                        'SELECT role_id
4188
                        FROM ' . prefixTable('restriction_to_roles') . '
4189
                        WHERE item_id = %i AND role_id IN %ls',
4190
                        $record['id'],
4191
                        $session->get('user-roles_array')
4192
                    );
4193
                    if (DB::count() > 0) {
4194
                        $user_is_included_in_role = true;
4195
                    }
4196
4197
                    // Is user in restricted list of users
4198
                    if (empty($record['restricted_to']) === false) {
4199
                        if (
4200
                            in_array($session->get('user-id'), explode(';', $record['restricted_to'])) === true
4201
                            || (((int) $session->get('user-manager') === 1 || (int) $session->get('user-can_manage_all_users') === 1)
4202
                                && (int) $SETTINGS['manager_edit'] === 1)
4203
                        ) {
4204
                            $user_is_in_restricted_list = true;
4205
                        } else {
4206
                            $user_is_in_restricted_list = false;
4207
                        }
4208
                    } else {
4209
                        $user_is_in_restricted_list = false;
4210
                    }
4211
4212
                    // Get Expiration date
4213
                    $expired_item = 0;
4214
                    if (
4215
                        (int) $SETTINGS['activate_expiration'] === 1
4216
                        && $record['renewal_period'] > 0
4217
                        && ($record['date'] + ($record['renewal_period'] * TP_ONE_MONTH_SECONDS)) < time()
4218
                    ) {
4219
                        $expired_item = 1;
4220
                    }
4221
                    // Init
4222
                    $html_json[$record['id']]['expired'] = (int) $expired_item;
4223
                    $html_json[$record['id']]['item_id'] = (int) $record['id'];
4224
                    $html_json[$record['id']]['item_key'] = (string) $record['item_key'];
4225
                    $html_json[$record['id']]['tree_id'] = (int) $record['tree_id'];
4226
                    $html_json[$record['id']]['label'] = strip_tags($record['label']);
4227
                    if (isset($SETTINGS['show_description']) === true && (int) $SETTINGS['show_description'] === 1 && is_null($record['description']) === false && empty($record['description']) === false) {
4228
                        $html_json[$record['id']]['desc'] = mb_substr(preg_replace('#<[^>]+>#', ' ', $record['description']), 0, 200);
4229
                    } else {
4230
                        $html_json[$record['id']]['desc'] = '';
4231
                    }
4232
                    $html_json[$record['id']]['login'] = $record['login'];
4233
                    $html_json[$record['id']]['anyone_can_modify'] = (int) $record['anyone_can_modify'];
4234
                    $html_json[$record['id']]['is_result_of_search'] = 0;
4235
                    $html_json[$record['id']]['is_favourited'] = in_array($record['id'], $session->get('user-favorites')) === true ? 1 : 0;
4236
                    $html_json[$record['id']]['link'] = $record['link'];
4237
                    $html_json[$record['id']]['email'] = $record['email'] ?? '';
4238
                    $html_json[$record['id']]['fa_icon'] = $record['fa_icon'];
4239
4240
                    // Possible values:
4241
                    // 0 -> no access to item
4242
                    // 10 -> appears in list but no view
4243
                    // 20 -> can view without edit (no copy) or move
4244
                    // 30 -> can view without edit (no copy) but can move
4245
                    // 40 -> can edit but not move
4246
                    // 50 -> can edit and move
4247
                    $itemIsPersonal = false;
4248
4249
                    // Let's identify the rights belonging to this ITEM
4250
                    if (
4251
                        (int) $record['perso'] === 1
4252
                        && $record['log_action'] === 'at_creation'
4253
                        && $record['log_user'] === $session->get('user-id')
4254
                        && (int) $folder_is_in_personal === 1
4255
                        && (int) $folder_is_personal === 1
4256
                    ) {
4257
                        // Case 1 - Is this item personal and user its owner?
4258
                        // If yes then allow
4259
                        // If no then continue
4260
                        $itemIsPersonal = true;
4261
                        $right = 70;
4262
                        // ---
4263
                        // ----- END CASE 1 -----
4264
                    } 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)
4265
                            || ($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))
4266
                        && (isset($SETTINGS['manager_edit']) === true && (int) $SETTINGS['manager_edit'] === 1)
4267
                        && (int) $record['perso'] !== 1
4268
                        && $user_is_in_restricted_list === true
4269
                    ) {
4270
                        // Case 2 - Is user manager and option "manager_edit" set to true?
4271
                        // Allow all rights
4272
                        $right = 70;
4273
                        // ---
4274
                        // ----- END CASE 2 -----
4275
                    } elseif (
4276
                        (int) $record['anyone_can_modify'] === 1
4277
                        && (int) $record['perso'] !== 1
4278
                        && (int) $session->get('user-read_only') === 0
4279
                    ) {
4280
                        // Case 3 - Has this item the setting "anyone can modify" set to true?
4281
                        // Allow all rights
4282
                        $right = 70;
4283
                        // ---
4284
                        // ----- END CASE 3 -----
4285
                    } elseif (
4286
                        $user_is_in_restricted_list === true
4287
                        && (int) $record['perso'] !== 1
4288
                        && (int) $session->get('user-read_only') === 0
4289
                    ) {
4290
                        // Case 4 - Is this item limited to Users? Is current user in this list?
4291
                        // Allow all rights
4292
                        $right = 70;
4293
                        // ---
4294
                        // ----- END CASE 4 -----
4295
                    } elseif (
4296
                        $user_is_included_in_role === true
4297
                        && (int) $record['perso'] !== 1
4298
                        && (int) $session->get('user-read_only') === 0
4299
                    ) {
4300
                        // Case 5 - Is this item limited to group of users? Is current user in one of those groups?
4301
                        // Allow all rights
4302
                        $right = 60;
4303
                        // ---
4304
                        // ----- END CASE 5 -----
4305
                    } elseif (
4306
                        (int) $record['perso'] !== 1
4307
                        && (int) $session->get('user-read_only') === 1
4308
                    ) {
4309
                        // Case 6 - Is user readonly?
4310
                        // Allow limited rights
4311
                        $right = 10;
4312
                        // ---
4313
                        // ----- END CASE 6 -----
4314
                    } elseif (
4315
                        (int) $record['perso'] !== 1
4316
                        && in_array($record['tree_id'], $session->get('user-allowed_folders_by_definition'))
4317
                    ) {
4318
                        // Case 7 - Is folder allowed by definition for this user?
4319
                        // Allow limited rights
4320
                        $right = 70;
4321
                        // ---
4322
                        // ----- END CASE 7 -----
4323
                    } elseif (
4324
                        (int) $record['perso'] !== 1
4325
                        && (int) $session->get('user-read_only') === 1
4326
                    ) {
4327
                        // Case 8 - Is user allowed to access?
4328
                        // Allow rights
4329
                        $right = 10;
4330
                        // ---
4331
                        // ----- END CASE 8 -----
4332
                    } elseif (($user_is_included_in_role === false && $item_is_restricted_to_role === true)
4333
                        && (int) $record['perso'] !== 1
4334
                        && (int) $session->get('user-read_only') === 0
4335
                    ) {
4336
                        // Case 9 - Is this item limited to Users or Groups? Is current user in this list?
4337
                        // If no then Allow none
4338
                        $right = 10;
4339
                        // ---
4340
                        // ----- END CASE 9 -----
4341
                    } else {
4342
                        // Define the access based upon setting on folder
4343
                        // 0 -> no access to item
4344
                        // 10 -> appears in list but no view
4345
                        // 20 -> can view without edit (no copy) or move or delete
4346
                        // 30 -> can view without edit (no copy) or delete but can move
4347
                        // 40 -> can edit but not move and not delete
4348
                        // 50 -> can edit and delete but not move
4349
                        // 60 -> can edit and move but not delete
4350
                        // 70 -> can edit and move
4351
                        if ((int) $accessLevel === 0) {
4352
                            $right = 0;
4353
                        } elseif ((10 <= (int) $accessLevel) && ((int) $accessLevel < 20)) {
4354
                            $right = 20;
4355
                        } elseif ((20 <= (int) $accessLevel) && ((int) $accessLevel < 30)) {
4356
                            $right = 60;
4357
                        } elseif ((int) $accessLevel >= 30) {
4358
                            $right = 70;
4359
                        } else {
4360
                            $right = 10;
4361
                        }
4362
                    }
4363
4364
                    // Is drag and drop enabled?
4365
                    $dragDrop = (int) $SETTINGS['disable_drag_drop'] !== 1;
4366
4367
                    // Now finalize the data to send back
4368
                    $html_json[$record['id']]['rights'] = $right;
4369
                    $html_json[$record['id']]['perso'] = 'fa-tag mi-red';
4370
                    $html_json[$record['id']]['sk'] = $itemIsPersonal === true ? 1 : 0;
4371
                    $html_json[$record['id']]['display'] = $right > 0 ? 1 : 0;
4372
                    $html_json[$record['id']]['open_edit'] = in_array($right, array(40, 50, 60, 70)) === true ? 1 : 0;
4373
                    $html_json[$record['id']]['canMove'] = in_array($right, array(30, 60, 70)) === true ? (int) $dragDrop : 0;
4374
4375
                    //*************** */
4376
4377
                    // Build array with items
4378
                    array_push(
4379
                        $itemsIDList,
4380
                        array(
4381
                            'id' => (int) $record['id'],
4382
                            //'display' => $displayItem,
4383
                            'edit' => $html_json[$record['id']]['open_edit'],
4384
                        )
4385
                    );
4386
                }
4387
                $idManaged = $record['id'];
4388
            }
4389
4390
            $rights = recupDroitCreationSansComplexite((int) $inputData['id']);
4391
        }
4392
4393
        // DELETE - 2.1.19 - AND (l.action = 'at_creation' OR (l.action = 'at_modification' AND l.raison LIKE 'at_pw :%'))
4394
        // count
4395
        if ((int) $start === 0) {
4396
            DB::query(
4397
                'SELECT i.id
4398
                FROM ' . prefixTable('items') . ' as i
4399
                INNER JOIN ' . prefixTable('nested_tree') . ' as n ON (i.id_tree = n.id)
4400
                INNER JOIN ' . prefixTable('log_items') . ' as l ON (i.id = l.id_item)
4401
                WHERE %l
4402
                ORDER BY i.label ASC, l.date DESC',
4403
                $where
4404
            );
4405
            $counter_full = DB::count();
4406
            $uniqueLoadData['counter_full'] = $counter_full;
4407
        }
4408
4409
        // Check list to be continued status
4410
        if ($post_nb_items_to_display_once !== 'max' && ($post_nb_items_to_display_once + $start) < $counter_full) {
4411
            $listToBeContinued = 'yes';
4412
        } else {
4413
            $listToBeContinued = 'end';
4414
        }
4415
4416
        // Prepare returned values
4417
        $returnValues = array(
4418
            'html_json' => $html_json,
4419
            //'folder_requests_psk' => $findPfGroup,
4420
            'arborescence' => $arr_arbo,
4421
            'array_items' => $itemsIDList,
4422
            'error' => $showError,
4423
            //'saltkey_is_required' => $folderIsPf === true ? 1 : 0,
4424
            'show_clipboard_small_icons' => isset($SETTINGS['copy_to_clipboard_small_icons']) && (int) $SETTINGS['copy_to_clipboard_small_icons'] === 1 ? 1 : 0,
4425
            'next_start' => intval($post_nb_items_to_display_once) + intval($start),
4426
            'list_to_be_continued' => $listToBeContinued,
4427
            'items_count' => $counter,
4428
            'counter_full' => $counter_full,
4429
            'folder_complexity' => (int) $folderComplexity,
4430
            'categoriesStructure' => $categoriesStructure,
4431
            'access_level' => $accessLevel,
4432
            'IsPersonalFolder' => $folderIsPf === true ? 1 : 0,
4433
            'uniqueLoadData' => json_encode($uniqueLoadData),
4434
        );
4435
        // Check if $rights is not null
4436
        if (count($rights) > 0) {
4437
            $returnValues = array_merge($returnValues, $rights);
4438
        }
4439
4440
        // Encrypt data to return
4441
        echo (string) prepareExchangedData(
4442
            $returnValues,
4443
            'encode'
4444
        );
4445
4446
        break;
4447
4448
    case 'get_item_password':
4449
        // Check KEY
4450
        if ($inputData['key'] !== $session->get('key')) {
4451
            echo (string) prepareExchangedData(
4452
                array(
4453
                    'error' => true,
4454
                    'message' => $lang->get('key_is_not_correct'),
4455
                ),
4456
                'encode'
4457
            );
4458
            break;
4459
        }
4460
4461
        // Get item details and its sharekey
4462
        $dataItem = DB::queryFirstRow(
4463
            'SELECT i.pw AS pw, s.share_key AS share_key, i.id AS id,
4464
                    i.label AS label, i.id_tree AS id_tree
4465
            FROM ' . prefixTable('items') . ' AS i
4466
            INNER JOIN ' . prefixTable('sharekeys_items') . ' AS s ON (s.object_id = i.id)
4467
            WHERE user_id = %i AND (i.item_key = %s OR i.id = %i)',
4468
            $session->get('user-id'),
4469
            $inputData['itemKey'] ?? '',
4470
            $inputData['itemId'] ?? 0
4471
        );
4472
4473
        // Check if password item exists
4474
        if (DB::count() === 0) {
4475
            echo (string) prepareExchangedData(
4476
                [
4477
                    'error' => true,
4478
                    'password' => '',
4479
                    'password_error' => $lang->get('password_is_empty'),
4480
                ],
4481
                'encode'
4482
            );
4483
            break;
4484
        }
4485
4486
        // Get user access rights
4487
        $userAccess = getCurrentAccessRights(
4488
            (int) $session->get('user-id'),
4489
            (int) $dataItem['id'],
4490
            (int) $dataItem['id_tree']
4491
        )['access'];
4492
4493
        // List of allowed actions
4494
        $allowedActions = [
4495
            'at_password_copied',
4496
            'at_password_shown',
4497
            'at_password_shown_edit_form',
4498
        ];
4499
4500
        // User not allowed to see this password or invalid action provided
4501
        if ($userAccess !== true
4502
            || empty($inputData['action'])
4503
            || !in_array($inputData['action'], $allowedActions, true)) {
4504
4505
            echo (string) prepareExchangedData(
4506
                [
4507
                    'error' => true,
4508
                    'password' => '',
4509
                    'password_error' => $lang->get('not_allowed_to_see_pw'),
4510
                ],
4511
                'encode'
4512
            );
4513
            break;
4514
        }
4515
4516
        // Log the action on password
4517
        logItems(
4518
            $SETTINGS,
4519
            (int) $dataItem['id'],
4520
            $dataItem['label'],
4521
            (int) $session->get('user-id'),
4522
            $inputData['action'], // Filtered by array of allowed values
4523
            $session->get('user-login')
4524
        );
4525
4526
        // Uncrypt PW if sharekey is available (empty password otherwise)
4527
        $pw = '';
4528
        if (!empty($dataItem['share_key'])) {
4529
            $pw = doDataDecryption(
4530
                $dataItem['pw'],
4531
                decryptUserObjectKey(
4532
                    $dataItem['share_key'],
4533
                    $session->get('user-private_key')
4534
                )
4535
            );
4536
        }
4537
4538
        $returnValues = array(
4539
            'error' => false,
4540
            'password' => $pw,
4541
            'password_error' => '',
4542
        );
4543
4544
        // Encrypt data to return
4545
        echo (string) prepareExchangedData(
4546
            $returnValues,
4547
            'encode'
4548
        );
4549
        break;
4550
4551
    /*
4552
     * CASE
4553
     * Get complexity level of a group
4554
     */
4555
    case 'get_complixity_level':
4556
        // get some info about ITEM
4557
        if (null !== $inputData['itemId'] && empty($inputData['itemId']) === false) {
4558
            // Is this item locked?
4559
            if (isItemLocked((int) $inputData['itemId'], $session, (int) $session->get('user-id'))) {
4560
                $returnValues = array(
4561
                    'error' => true,
4562
                    'message' => $lang->get('error_no_edition_possible_locked'),
4563
                );
4564
                echo (string) prepareExchangedData(
4565
                    $returnValues,
4566
                    'encode'
4567
                );
4568
                break;
4569
            }
4570
        }
4571
4572
        // do query on this folder
4573
        $data_this_folder = DB::queryFirstRow(
4574
            'SELECT id, personal_folder, title
4575
            FROM ' . prefixTable('nested_tree') . '
4576
            WHERE id = %s',
4577
            $inputData['folderId']
4578
        );
4579
4580
        // check if user can perform this action
4581
        if (
4582
            null !== $inputData['context']
4583
            && empty($inputData['context']) === false
4584
        ) {
4585
            if (
4586
                $inputData['context'] === 'create_folder'
4587
                || $inputData['context'] === 'edit_folder'
4588
                || $inputData['context'] === 'delete_folder'
4589
                || $inputData['context'] === 'copy_folder'
4590
            ) {
4591
                if (
4592
                    (int) $session->get('user-admin') !== 1
4593
                    && ((int) $session->get('user-manager') !== 1)
4594
                    && (isset($SETTINGS['enable_user_can_create_folders'])
4595
                        && (int) $SETTINGS['enable_user_can_create_folders'] !== 1)
4596
                    && ((int) $data_this_folder['personal_folder'] !== 1 && $data_this_folder['title'] !== $session->get('user-id'))   // take into consideration if this is a personal folder
4597
                ) {
4598
                    $returnValues = array(
4599
                        'error' => true,
4600
                        'message' => $lang->get('error_not_allowed_to'),
4601
                    );
4602
                    echo (string) prepareExchangedData(
4603
                        $returnValues,
4604
                        'encode'
4605
                    );
4606
                    break;
4607
                }
4608
            }
4609
        }
4610
4611
        // Get required Complexity for this Folder
4612
        $visibilite = '';
4613
        $data = DB::queryFirstRow(
4614
            'SELECT m.valeur, n.personal_folder
4615
            FROM ' . prefixTable('misc') . ' AS m
4616
            INNER JOIN ' . prefixTable('nested_tree') . ' AS n ON (m.intitule = n.id)
4617
            WHERE type=%s AND intitule = %s',
4618
            'complex',
4619
            $inputData['folderId']
4620
        );
4621
4622
        if (isset($data['valeur']) === true && (empty($data['valeur']) === false || $data['valeur'] === '0')) {
4623
            $complexity = TP_PW_COMPLEXITY[$data['valeur']][1];
4624
            $folder_is_personal = (int) $data['personal_folder'];
4625
4626
            // Prepare Item actual visibility (what Users/Roles can see it)
4627
            $rows = DB::query(
4628
                'SELECT t.title
4629
                FROM ' . prefixTable('roles_values') . ' as v
4630
                INNER JOIN ' . prefixTable('roles_title') . ' as t ON (v.role_id = t.id)
4631
                WHERE v.folder_id = %i
4632
                GROUP BY title',
4633
                $inputData['folderId']
4634
            );
4635
            foreach ($rows as $record) {
4636
                if (empty($visibilite)) {
4637
                    $visibilite = $record['title'];
4638
                } else {
4639
                    $visibilite .= ' - ' . $record['title'];
4640
                }
4641
            }
4642
        } else {
4643
            $complexity = $lang->get('not_defined');
4644
4645
            // if not defined, then previous query failed and personal_folder is null
4646
            // do new query to know if current folder is pf
4647
            $data_pf = DB::queryFirstRow(
4648
                'SELECT personal_folder
4649
                FROM ' . prefixTable('nested_tree') . '
4650
                WHERE id = %s',
4651
                $inputData['folderId']
4652
            );
4653
            
4654
            $folder_is_personal = $data_pf !== null ? (int) $data_pf['personal_folder'] : 0;
4655
            
4656
            $visibilite = $session->get('user-name') . ' ' . $session->get('user-lastname') . ' (' . $session->get('user-login') . ')';
4657
        }
4658
4659
        recupDroitCreationSansComplexite($inputData['folderId']);
4660
4661
        // get list of roles
4662
        $listOptionsForUsers = array();
4663
        $listOptionsForRoles = array();
4664
        $rows = DB::query(
4665
            'SELECT r.role_id AS role_id, t.title AS title
4666
            FROM ' . prefixTable('roles_values') . ' AS r
4667
            INNER JOIN ' . prefixTable('roles_title') . ' AS t ON (r.role_id = t.id)
4668
            WHERE r.folder_id = %i',
4669
            $inputData['folderId']
4670
        );
4671
        foreach ($rows as $record) {
4672
            array_push(
4673
                $listOptionsForRoles,
4674
                array(
4675
                    'id' => $record['role_id'],
4676
                    'title' => $record['title'],
4677
                )
4678
            );
4679
            $rows2 = DB::query(
4680
                'SELECT id, login, fonction_id, email, name, lastname
4681
                FROM ' . prefixTable('users') . '
4682
                WHERE admin = 0 AND fonction_id is not null'
4683
            );
4684
            foreach ($rows2 as $record2) {
4685
                foreach (explode(';', $record2['fonction_id']) as $role) {
4686
                    if (
4687
                        array_search($record2['id'], array_column($listOptionsForUsers, 'id')) === false
4688
                        && $role === $record['role_id']
4689
                    ) {
4690
                        array_push(
4691
                            $listOptionsForUsers,
4692
                            array(
4693
                                'id' => $record2['id'],
4694
                                'login' => $record2['login'],
4695
                                'name' => $record2['name'] . ' ' . $record2['lastname'],
4696
                                'email' => $record2['email'],
4697
                            )
4698
                        );
4699
                    }
4700
                }
4701
            }
4702
        }
4703
        
4704
        // Get access level for this folder
4705
        $accessLevel = 0;
4706
        if ($folder_is_personal === 0) {
4707
            $arrTmp = [];
4708
            foreach ($session->get('user-roles_array') as $role) {
4709
                //db::debugmode(true);
4710
                $access = DB::queryFirstRow(
4711
                    'SELECT type
4712
                    FROM ' . prefixTable('roles_values') . '
4713
                    WHERE role_id = %i AND folder_id = %i',
4714
                    $role,
4715
                    $inputData['folderId']
4716
                );
4717
                //db::debugmode(false);
4718
                if (DB::count()>0) {
4719
                    if ($access['type'] === 'R') {
4720
                        array_push($arrTmp, 10);
4721
                    } elseif ($access['type'] === 'W') {
4722
                        array_push($arrTmp, 30);
4723
                    } elseif ($access['type'] === 'ND') {
4724
                        array_push($arrTmp, 20);
4725
                    } elseif ($access['type'] === 'NE') {
4726
                        array_push($arrTmp, 10);
4727
                    } elseif ($access['type'] === 'NDNE') {
4728
                        array_push($arrTmp, 15);
4729
                    } else {
4730
                        // Ensure to give access Right if allowed folder
4731
                        if (in_array($inputData['id'], $session->get('user-accessible_folders')) === true) {
4732
                            array_push($arrTmp, 30);
4733
                        } else {
4734
                            array_push($arrTmp, 0);
4735
                        }
4736
                    }
4737
                }
4738
            }
4739
            // 3.0.0.0 - changed  MIN to MAX
4740
            $accessLevel = count($arrTmp) > 0 ? max($arrTmp) : $accessLevel;
4741
        } elseif ($folder_is_personal === 1) {
4742
4743
            // Check if personal folder is owned by user
4744
            $folder = DB::queryFirstRow(
4745
                'SELECT id
4746
                FROM ' . prefixTable('nested_tree') . '
4747
                WHERE title = %s',
4748
                $session->get('user-id'),
4749
            );
4750
4751
            if ($folder) {
4752
                // Get all subfolders of user personal folder
4753
                $ids = $tree->getDescendants($folder['id'], true, false, true);
4754
4755
                // This folder is owned by user
4756
                if (in_array($inputData['folderId'], $ids))
4757
                    $accessLevel = 30;
4758
            }
4759
        }
4760
4761
        // Access is not allowed to this folder
4762
        if ($accessLevel === 0) {
4763
            echo (string) prepareExchangedData(
4764
                [
4765
                    'error' => true,
4766
                    'message' => $lang->get('error_not_allowed_to_access_this_folder'),
4767
                ],
4768
                'encode'
4769
            );
4770
            break;
4771
        }
4772
4773
        $returnValues = array(
4774
            'folderId' => (int) $inputData['folderId'],
4775
            'error' => false,
4776
            'val' => $data !== null ? (int) $data['valeur'] : 0,
4777
            'visibility' => $visibilite,
4778
            'complexity' => $complexity,
4779
            'personal' => $folder_is_personal,
4780
            'usersList' => $listOptionsForUsers,
4781
            'rolesList' => $listOptionsForRoles,
4782
            'setting_restricted_to_roles' => isset($SETTINGS['restricted_to_roles']) === true
4783
                && (int) $SETTINGS['restricted_to_roles'] === 1 ? 1 : 0,
4784
            'itemAccessRight' => $accessLevel,
4785
        );
4786
        echo (string) prepareExchangedData(
4787
            $returnValues,
4788
            'encode'
4789
        );
4790
        break;
4791
4792
    case 'handle_item_edition_lock':
4793
        // Check KEY
4794
        if ($inputData['key'] !== $session->get('key')) {
4795
            echo (string) prepareExchangedData(
4796
                array(
4797
                    'error' => true,
4798
                    'message' => $lang->get('key_is_not_correct'),
4799
                ),
4800
                'encode'
4801
            );
4802
            break;
4803
        }
4804
4805
        // decrypt and retreive data in JSON format
4806
        $dataReceived = prepareExchangedData(
4807
            $inputData['data'],
4808
            'decode'
4809
        );
4810
        $itemId = filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
4811
        $action = filter_var($dataReceived['action'], FILTER_SANITIZE_SPECIAL_CHARS);
4812
4813
        if ($action === 'release_lock') {
4814
            DB::delete(
4815
                prefixTable('items_edition'), 
4816
                'item_id = %i AND user_id = %i', 
4817
                $itemId,
4818
                $session->get('user-id')
4819
            );
4820
        }
4821
        
4822
        break;
4823
4824
    /*
4825
     * CASE
4826
     * DELETE attached file from an item
4827
     */
4828
    case 'delete_attached_file':
4829
        // Check KEY
4830
        if ($inputData['key'] !== $session->get('key')) {
4831
            echo (string) prepareExchangedData(
4832
                array(
4833
                    'error' => true,
4834
                    'message' => $lang->get('key_is_not_correct'),
4835
                ),
4836
                'encode'
4837
            );
4838
            break;
4839
        }
4840
4841
        // decrypt and retreive data in JSON format
4842
        $dataReceived = prepareExchangedData(
4843
            $inputData['data'],
4844
            'decode'
4845
        );
4846
        $fileId = filter_var($dataReceived['file_id'], FILTER_SANITIZE_NUMBER_INT);
4847
4848
        // Get some info before deleting
4849
        $data = DB::queryFirstRow(
4850
            'SELECT name, id_item, file
4851
            FROM ' . prefixTable('files') . '
4852
            WHERE id = %i',
4853
            $fileId
4854
        );
4855
4856
        // Load item data
4857
        $data_item = DB::queryFirstRow(
4858
            'SELECT id_tree
4859
            FROM ' . prefixTable('items') . '
4860
            WHERE id = %i',
4861
            $data['id_item']
4862
        );
4863
4864
        // Check that user can access this folder
4865
        if (in_array($data_item['id_tree'], $session->get('user-accessible_folders')) === false) {
4866
            echo (string) prepareExchangedData(
4867
                array('error' => 'ERR_FOLDER_NOT_ALLOWED'),
4868
                'encode'
4869
            );
4870
            break;
4871
        }
4872
4873
        if (empty($data['id_item']) === false) {
4874
            // Delete from FILES table
4875
            DB::delete(
4876
                prefixTable('files'),
4877
                'id = %i',
4878
                $fileId
4879
            );
4880
4881
            // Update the log
4882
            logItems(
4883
                $SETTINGS,
4884
                (int) $data['id_item'],
4885
                $data['name'],
4886
                $session->get('user-id'),
4887
                'at_modification',
4888
                $session->get('user-login'),
4889
                'at_del_file : ' . $data['name']
4890
            );
4891
4892
            // DElete sharekeys
4893
            DB::delete(
4894
                prefixTable('sharekeys_files'),
4895
                'object_id = %i',
4896
                $fileId
4897
            );
4898
4899
            // Delete file from server
4900
            $fileToDelete = $SETTINGS['path_to_upload_folder'] . '/' . TP_FILE_PREFIX . base64_decode($data['file']);
4901
            $fileToDelete = realpath($fileToDelete);
4902
            if ($fileToDelete && strpos($fileToDelete, $SETTINGS['path_to_upload_folder']) === 0) {
4903
                fileDelete($fileToDelete, $SETTINGS);
4904
            }
4905
        }
4906
4907
        echo (string) prepareExchangedData(
4908
            array(
4909
                'error' => false,
4910
                'message' => '',
4911
            ),
4912
            'encode'
4913
        );
4914
        break;
4915
4916
    /*
4917
     * FUNCTION
4918
     * Launch an action when clicking on a quick icon
4919
     * $action = 0 => Make not favorite
4920
     * $action = 1 => Make favorite
4921
     */
4922
    case 'action_on_quick_icon':
4923
        // Check KEY and rights
4924
        if (
4925
            $inputData['key'] !== $session->get('key')
4926
            || $session->get('user-read_only') === 1 || !isset($SETTINGS['pwd_maximum_length'])
4927
        ) {
4928
            // error
4929
            exit;
4930
        }
4931
4932
        // decrypt and retreive data in JSON format
4933
        $dataReceived = prepareExchangedData(
4934
            $inputData['data'],
4935
            'decode'
4936
        );
4937
        $inputData['action'] = (int) filter_var($dataReceived['action'], FILTER_SANITIZE_NUMBER_INT);
4938
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
4939
4940
        if ((int) $inputData['action'] === 0) {
4941
            // Add new favourite
4942
            SessionManager::addRemoveFromSessionArray('user-favorites', [$inputData['itemId']], 'add');
4943
            DB::update(
4944
                prefixTable('users'),
4945
                array(
4946
                    'favourites' => implode(';', $session->get('user-favorites')),
4947
                ),
4948
                'id = %i',
4949
                $session->get('user-id')
4950
            );
4951
            // Update SESSION with this new favourite
4952
            $data = DB::queryFirstRow(
4953
                'SELECT label,id_tree
4954
                FROM ' . prefixTable('items') . '
4955
                WHERE id = %i',
4956
                $inputData['itemId']
4957
            );
4958
            SessionManager::addRemoveFromSessionAssociativeArray(
4959
                'user-favorites_tab',
4960
                [
4961
                    $inputData['itemId'] => [
4962
                        'label' => $data['label'],
4963
                        'url' => 'index.php?page=items&amp;group=' . $data['id_tree'] . '&amp;id=' . $inputData['itemId'],
4964
                    ],
4965
                ],
4966
                'add'
4967
            );
4968
        } elseif ((int) $inputData['action'] === 1) {
4969
            // delete from session
4970
            SessionManager::addRemoveFromSessionArray('user-favorites', [$inputData['itemId']], 'remove');
4971
4972
            // delete from DB
4973
            DB::update(
4974
                prefixTable('users'),
4975
                array(
4976
                    'favourites' => implode(';', $session->get('user-favorites')),
4977
                ),
4978
                'id = %i',
4979
                $session->get('user-id')
4980
            );
4981
            // refresh session fav list
4982
            if ($session->has('user-favorites_tab') && $session->has('user-favorites_tab') && null !== $session->get('user-favorites_tab')) {
4983
                $user_favorites_tab = $session->get('user-favorites_tab');
4984
                foreach ($user_favorites_tab as $key => $value) {
4985
                    if ($key === $inputData['id']) {
4986
                        SessionManager::addRemoveFromSessionAssociativeArray('user-favorites_tab', [$key], 'remove');
4987
                        break;
4988
                    }
4989
                }
4990
            }
4991
        }
4992
        break;
4993
4994
    /*
4995
     * CASE
4996
     * Move an ITEM
4997
     */
4998
    case 'move_item':
4999
        // Check KEY and rights
5000
        if ($inputData['key'] !== $session->get('key')) {
5001
            echo (string) prepareExchangedData(
5002
                array(
5003
                    'error' => true,
5004
                    'message' => $lang->get('key_is_not_correct'),
5005
                ),
5006
                'encode'
5007
            );
5008
            break;
5009
        }
5010
        if ($session->get('user-read_only') === 1 || isset($SETTINGS['pwd_maximum_length']) === false) {
5011
            echo (string) prepareExchangedData(
5012
                array(
5013
                    'error' => true,
5014
                    'message' => $lang->get('error_not_allowed_to'),
5015
                ),
5016
                'encode'
5017
            );
5018
            break;
5019
        }
5020
5021
        // decrypt and retreive data in JSON format
5022
        $dataReceived = prepareExchangedData(
5023
            $inputData['data'],
5024
            'decode'
5025
        );
5026
        $inputData['folderId'] = (int) filter_var($dataReceived['folder_id'], FILTER_SANITIZE_NUMBER_INT);
5027
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
5028
5029
        // get data about item
5030
        $dataSource = DB::queryFirstRow(
5031
            'SELECT i.pw, f.personal_folder,i.id_tree, f.title,i.label
5032
            FROM ' . prefixTable('items') . ' as i
5033
            INNER JOIN ' . prefixTable('nested_tree') . ' as f ON (i.id_tree=f.id)
5034
            WHERE i.id=%i',
5035
            $inputData['itemId']
5036
        );
5037
5038
        // Check that user can delete on old folder
5039
        $checkRights = getCurrentAccessRights(
5040
            $session->get('user-id'),
5041
            $inputData['itemId'],
5042
            (int) $dataSource['id_tree'],
5043
        );
5044
5045
        if ($checkRights['error'] || !$checkRights['delete']) {
5046
            echo (string) prepareExchangedData(
5047
                array(
5048
                    'error' => true,
5049
                    'message' => $lang->get('error_not_allowed_to'),
5050
                ),
5051
                'encode'
5052
            );
5053
            break;
5054
        }
5055
5056
        // Check that user can write on requested folder
5057
        $checkRights = getCurrentAccessRights(
5058
            $session->get('user-id'),
5059
            $inputData['itemId'],
5060
            $inputData['folderId'],
5061
        );
5062
5063
        if ($checkRights['error'] || !$checkRights['edit']) {
5064
            echo (string) prepareExchangedData(
5065
                array(
5066
                    'error' => true,
5067
                    'message' => $lang->get('error_not_allowed_to'),
5068
                ),
5069
                'encode'
5070
            );
5071
            break;
5072
        }
5073
5074
        // get data about new folder
5075
        $dataDestination = DB::queryFirstRow(
5076
            'SELECT personal_folder, title
5077
            FROM ' . prefixTable('nested_tree') . '
5078
            WHERE id = %i',
5079
            $inputData['folderId']
5080
        );
5081
5082
        // Check that user can access this folder
5083
        if (
5084
            in_array($dataSource['id_tree'], $session->get('user-accessible_folders')) === false
5085
            || in_array($inputData['folderId'], $session->get('user-accessible_folders')) === false
5086
            //|| (int) $dataSource['personal_folder'] === (int) $dataDestination['personal_folder']
5087
        ) {
5088
            echo (string) prepareExchangedData(
5089
                array(
5090
                    'error' => true,
5091
                    'message' => $lang->get('error_not_allowed_to'),
5092
                ),
5093
                'encode'
5094
            );
5095
            break;
5096
        }
5097
5098
        // Manage possible cases
5099
        if ((int) $dataSource['personal_folder'] === 0 && (int) $dataDestination['personal_folder'] === 0) {
5100
            // Previous is non personal folder and new too
5101
            // Just update is needed. Item key is the same
5102
            DB::update(
5103
                prefixTable('items'),
5104
                array(
5105
                    'id_tree' => $inputData['folderId'],
5106
                    'updated_at' => time(),
5107
                ),
5108
                'id=%i',
5109
                $inputData['itemId']
5110
            );
5111
            // ---
5112
            // ---
5113
        } elseif ((int) $dataSource['personal_folder'] === 0 && (int) $dataDestination['personal_folder'] === 1) {
5114
            // Source is public and destination is personal
5115
            // Decrypt and remove all sharekeys (items, fields, files)
5116
            // Encrypt only for the user
5117
5118
            // Remove all item sharekeys items
5119
            DB::delete(
5120
                prefixTable('sharekeys_items'),
5121
                'object_id = %i AND user_id != %i',
5122
                $inputData['itemId'],
5123
                $session->get('user-id')
5124
            );
5125
5126
            // Remove all item sharekeys fields
5127
            // Get fields for this Item
5128
            $rows = DB::query(
5129
                'SELECT id
5130
                FROM ' . prefixTable('categories_items') . '
5131
                WHERE item_id = %i',
5132
                $inputData['itemId']
5133
            );
5134
            foreach ($rows as $field) {
5135
                DB::delete(
5136
                    prefixTable('sharekeys_fields'),
5137
                    'object_id = %i AND user_id != %i',
5138
                    $field['id'],
5139
                    $session->get('user-id')
5140
                );
5141
            }
5142
5143
            // Remove all item sharekeys files
5144
            // Get FILES for this Item
5145
            $rows = DB::query(
5146
                'SELECT id
5147
                FROM ' . prefixTable('files') . '
5148
                WHERE id_item = %i',
5149
                $inputData['itemId']
5150
            );
5151
            foreach ($rows as $attachment) {
5152
                DB::delete(
5153
                    prefixTable('sharekeys_files'),
5154
                    'object_id = %i AND user_id != %i',
5155
                    $attachment['id'],
5156
                    $session->get('user-id')
5157
                );
5158
            }
5159
5160
            // update pw
5161
            DB::update(
5162
                prefixTable('items'),
5163
                array(
5164
                    'id_tree' => $inputData['folderId'],
5165
                    'perso' => 1,
5166
                    'updated_at' => time(),
5167
                ),
5168
                'id=%i',
5169
                $inputData['itemId']
5170
            );
5171
            // ---
5172
            // ---
5173
        } elseif ((int) $dataSource['personal_folder'] === 1 && (int) $dataDestination['personal_folder'] === 1) {
5174
            // If previous is personal folder and new is personal folder too => no key exist on item
5175
            // just update is needed. Item key is the same
5176
            DB::update(
5177
                prefixTable('items'),
5178
                array(
5179
                    'id_tree' => $inputData['folderId'],
5180
                    'updated_at' => time(),
5181
                ),
5182
                'id=%i',
5183
                $inputData['itemId']
5184
            );
5185
            // ---
5186
            // ---
5187
        } elseif ((int) $dataSource['personal_folder'] === 1 && (int) $dataDestination['personal_folder'] === 0) {
5188
            // If previous is personal folder and new is not personal folder => no key exist on item => add new
5189
            // Create keys for all users
5190
5191
            // Get the ITEM object key for the user
5192
            $userKey = DB::queryFirstRow(
5193
                'SELECT share_key
5194
                FROM ' . prefixTable('sharekeys_items') . '
5195
                WHERE user_id = %i AND object_id = %i',
5196
                $session->get('user-id'),
5197
                $inputData['itemId']
5198
            );
5199
            if (DB::count() > 0) {
5200
                $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5201
5202
                // This is a public object
5203
                $users = DB::query(
5204
                    'SELECT id, public_key
5205
                    FROM ' . prefixTable('users') . '
5206
                    WHERE id NOT IN %li
5207
                    AND public_key != ""',
5208
                    $tpUsersIDs
5209
                );
5210
5211
                foreach ($users as $user) {
5212
                    // Insert in DB the new object key for this item by user
5213
                    DB::insert(
5214
                        prefixTable('sharekeys_items'),
5215
                        array(
5216
                            'object_id' => $inputData['itemId'],
5217
                            'user_id' => (int) $user['id'],
5218
                            'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5219
                        )
5220
                    );
5221
                }
5222
            }
5223
5224
            // Get the FIELDS object key for the user
5225
            // Get fields for this Item
5226
            $rows = DB::query(
5227
                'SELECT id
5228
                FROM ' . prefixTable('categories_items') . '
5229
                WHERE item_id = %i',
5230
                $inputData['itemId']
5231
            );
5232
            foreach ($rows as $field) {
5233
                $userKey = DB::queryFirstRow(
5234
                    'SELECT share_key
5235
                    FROM ' . prefixTable('sharekeys_fields') . '
5236
                    WHERE user_id = %i AND object_id = %i',
5237
                    $session->get('user-id'),
5238
                    $field['id']
5239
                );
5240
                if (DB::count() > 0) {
5241
                    $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5242
5243
                    // This is a public object
5244
                    $users = DB::query(
5245
                        'SELECT id, public_key
5246
                        FROM ' . prefixTable('users') . '
5247
                        WHERE id NOT IN %li
5248
                        AND public_key != ""',
5249
                        $tpUsersIDs
5250
                    );
5251
                    foreach ($users as $user) {
5252
                        // Insert in DB the new object key for this item by user
5253
                        DB::insert(
5254
                            prefixTable('sharekeys_fields'),
5255
                            array(
5256
                                'object_id' => $field['id'],
5257
                                'user_id' => (int) $user['id'],
5258
                                'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5259
                            )
5260
                        );
5261
                    }
5262
                }
5263
            }
5264
5265
            // Get the FILE object key for the user
5266
            // Get FILES for this Item
5267
            $rows = DB::query(
5268
                'SELECT id
5269
                FROM ' . prefixTable('files') . '
5270
                WHERE id_item = %i',
5271
                $inputData['itemId']
5272
            );
5273
            foreach ($rows as $attachment) {
5274
                $userKey = DB::queryFirstRow(
5275
                    'SELECT share_key
5276
                    FROM ' . prefixTable('sharekeys_files') . '
5277
                    WHERE user_id = %i AND object_id = %i',
5278
                    $session->get('user-id'),
5279
                    $attachment['id']
5280
                );
5281
                if (DB::count() > 0) {
5282
                    $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5283
5284
                    // This is a public object
5285
                    $users = DB::query(
5286
                        'SELECT id, public_key
5287
                        FROM ' . prefixTable('users') . '
5288
                        WHERE id NOT IN %li
5289
                        AND public_key != ""',
5290
                        $tpUsersIDs
5291
                    );
5292
5293
                    foreach ($users as $user) {
5294
                        // Insert in DB the new object key for this item by user
5295
                        DB::insert(
5296
                            prefixTable('sharekeys_files'),
5297
                            array(
5298
                                'object_id' => $attachment['id'],
5299
                                'user_id' => (int) $user['id'],
5300
                                'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5301
                            )
5302
                        );
5303
                    }
5304
                }
5305
            }
5306
5307
            // update item
5308
            DB::update(
5309
                prefixTable('items'),
5310
                array(
5311
                    'id_tree' => $inputData['folderId'],
5312
                    'perso' => 0,
5313
                    'updated_at' => time(),
5314
                ),
5315
                'id=%i',
5316
                $inputData['itemId']
5317
            );
5318
        }
5319
5320
        // Log item moved
5321
        logItems(
5322
            $SETTINGS,
5323
            (int) $inputData['itemId'],
5324
            $dataSource['label'],
5325
            $session->get('user-id'),
5326
            'at_modification',
5327
            $session->get('user-login'),
5328
            'at_moved : ' . $dataSource['title'] . ' -> ' . $dataDestination['title']
5329
        );
5330
5331
        // Update cache table
5332
        updateCacheTable('update_value', (int) $inputData['itemId']);
5333
5334
        $returnValues = array(
5335
            'error' => '',
5336
            'message' => '',
5337
            'from_folder' => $dataSource['id_tree'],
5338
            'to_folder' => $inputData['folderId'],
5339
        );
5340
        echo (string) prepareExchangedData(
5341
            $returnValues,
5342
            'encode'
5343
        );
5344
        break;
5345
5346
    /*
5347
     * CASE
5348
     * MASSIVE Move an ITEM
5349
     */
5350
    case 'mass_move_items':
5351
        // Check KEY and rights
5352
        if ($inputData['key'] !== $session->get('key')) {
5353
            echo (string) prepareExchangedData(
5354
                array(
5355
                    'error' => true,
5356
                    'message' => $lang->get('key_is_not_correct'),
5357
                ),
5358
                'encode'
5359
            );
5360
            break;
5361
        }
5362
        if ($session->get('user-read_only') === 1 || isset($SETTINGS['pwd_maximum_length']) === false) {
5363
            echo (string) prepareExchangedData(
5364
                array(
5365
                    'error' => true,
5366
                    'message' => $lang->get('error_not_allowed_to'),
5367
                ),
5368
                'encode'
5369
            );
5370
            break;
5371
        }
5372
5373
        // decrypt and retreive data in JSON format
5374
        $dataReceived = prepareExchangedData(
5375
            $inputData['data'],
5376
            'decode'
5377
        );
5378
        $inputData['folderId'] = filter_var($dataReceived['folder_id'], FILTER_SANITIZE_NUMBER_INT);
5379
        $post_item_ids = filter_var($dataReceived['item_ids'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5380
5381
        // loop on items to move
5382
        foreach (explode(';', $post_item_ids) as $item_id) {
5383
            if (empty($item_id) === false) {
5384
                // get data about item
5385
                $dataSource = DB::queryFirstRow(
5386
                    'SELECT i.pw, f.personal_folder,i.id_tree, f.title,i.label
5387
                    FROM ' . prefixTable('items') . ' as i
5388
                    INNER JOIN ' . prefixTable('nested_tree') . ' as f ON (i.id_tree=f.id)
5389
                    WHERE i.id=%i',
5390
                    $item_id
5391
                );
5392
5393
                // Check that user can access this folder
5394
                if (
5395
                    in_array($dataSource['id_tree'], $session->get('user-accessible_folders')) === false
5396
                    || in_array($inputData['folderId'], $session->get('user-accessible_folders')) === false
5397
                ) {
5398
                    echo (string) prepareExchangedData(
5399
                        array(
5400
                            'error' => true,
5401
                            'message' => $lang->get('error_not_allowed_to'),
5402
                        ),
5403
                        'encode'
5404
                    );
5405
                    exit;
5406
                }
5407
5408
                // get data about new folder
5409
                $dataDestination = DB::queryFirstRow(
5410
                    'SELECT personal_folder, title FROM ' . prefixTable('nested_tree') . ' WHERE id = %i',
5411
                    $inputData['folderId']
5412
                );
5413
5414
                // previous is non personal folder and new too
5415
                if (
5416
                    (int) $dataSource['personal_folder'] === 0
5417
                    && (int) $dataDestination['personal_folder'] === 0
5418
                ) {
5419
                    // just update is needed. Item key is the same
5420
                    DB::update(
5421
                        prefixTable('items'),
5422
                        array(
5423
                            'id_tree' => $inputData['folderId'],
5424
                            'updated_at' => time(),
5425
                        ),
5426
                        'id = %i',
5427
                        $item_id
5428
                    );
5429
                    // ---
5430
                    // ---
5431
                    // ---
5432
                } elseif (
5433
                    (int) $dataSource['personal_folder'] === 0
5434
                    && (int) $dataDestination['personal_folder'] === 1
5435
                ) {
5436
                    // Source is public and destination is personal
5437
                    // Decrypt and remove all sharekeys (items, fields, files)
5438
                    // Encrypt only for the user
5439
5440
                    // Remove all item sharekeys items
5441
                    DB::delete(
5442
                        prefixTable('sharekeys_items'),
5443
                        'object_id = %i AND user_id != %i',
5444
                        $item_id,
5445
                        $session->get('user-id')
5446
                    );
5447
5448
                    // Remove all item sharekeys fields
5449
                    // Get fields for this Item
5450
                    $rows = DB::query(
5451
                        'SELECT id
5452
                        FROM ' . prefixTable('categories_items') . '
5453
                        WHERE item_id = %i',
5454
                        $item_id
5455
                    );
5456
                    foreach ($rows as $field) {
5457
                        DB::delete(
5458
                            prefixTable('sharekeys_fields'),
5459
                            'object_id = %i AND user_id != %i',
5460
                            $field['id'],
5461
                            $session->get('user-id')
5462
                        );
5463
                    }
5464
5465
                    // Remove all item sharekeys files
5466
                    // Get FILES for this Item
5467
                    $rows = DB::query(
5468
                        'SELECT id
5469
                        FROM ' . prefixTable('files') . '
5470
                        WHERE id_item = %i',
5471
                        $item_id
5472
                    );
5473
                    foreach ($rows as $attachment) {
5474
                        DB::delete(
5475
                            prefixTable('sharekeys_files'),
5476
                            'object_id = %i AND user_id != %i',
5477
                            $attachment['id'],
5478
                            $session->get('user-id')
5479
                        );
5480
                    }
5481
5482
                    // update pw
5483
                    DB::update(
5484
                        prefixTable('items'),
5485
                        array(
5486
                            'id_tree' => $inputData['folderId'],
5487
                            'perso' => 1,
5488
                            'updated_at' => time(),
5489
                        ),
5490
                        'id = %i',
5491
                        $item_id
5492
                    );
5493
                    // ---
5494
                    // ---
5495
                    // ---
5496
                } elseif (
5497
                    (int) $dataSource['personal_folder'] === 1
5498
                    && (int) $dataDestination['personal_folder'] === 1
5499
                ) {
5500
                    // If previous is personal folder and new is personal folder too => no key exist on item
5501
                    // just update is needed. Item key is the same
5502
                    DB::update(
5503
                        prefixTable('items'),
5504
                        array(
5505
                            'id_tree' => $inputData['folderId'],
5506
                            'updated_at' => time(),
5507
                        ),
5508
                        'id = %i',
5509
                        $item_id
5510
                    );
5511
                    // ---
5512
                    // ---
5513
                    // ---
5514
                } elseif (
5515
                    (int) $dataSource['personal_folder'] === 1
5516
                    && (int) $dataDestination['personal_folder'] === 0
5517
                ) {
5518
                    // If previous is personal folder and new is not personal folder => no key exist on item => add new
5519
                    // Create keys for all users
5520
5521
                    // Get the ITEM object key for the user
5522
                    $userKey = DB::queryFirstRow(
5523
                        'SELECT share_key
5524
                        FROM ' . prefixTable('sharekeys_items') . '
5525
                        WHERE user_id = %i AND object_id = %i',
5526
                        $session->get('user-id'),
5527
                        $item_id
5528
                    );
5529
                    if (DB::count() > 0) {
5530
                        $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5531
5532
                        // This is a public object
5533
                        $users = DB::query(
5534
                            'SELECT id, public_key
5535
                            FROM ' . prefixTable('users') . '
5536
                            WHERE id NOT IN %li
5537
                            AND public_key != ""',
5538
                            $tpUsersIDs
5539
                        );
5540
5541
                        foreach ($users as $user) {
5542
                            // Insert in DB the new object key for this item by user
5543
                            DB::insert(
5544
                                prefixTable('sharekeys_items'),
5545
                                array(
5546
                                    'object_id' => $item_id,
5547
                                    'user_id' => (int) $user['id'],
5548
                                    'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5549
                                )
5550
                            );
5551
                        }
5552
                    }
5553
5554
                    // Get the FIELDS object key for the user
5555
                    // Get fields for this Item
5556
                    $rows = DB::query(
5557
                        'SELECT id
5558
                        FROM ' . prefixTable('categories_items') . '
5559
                        WHERE item_id = %i',
5560
                        $item_id
5561
                    );
5562
                    foreach ($rows as $field) {
5563
                        $userKey = DB::queryFirstRow(
5564
                            'SELECT share_key
5565
                            FROM ' . prefixTable('sharekeys_fields') . '
5566
                            WHERE user_id = %i AND object_id = %i',
5567
                            $session->get('user-id'),
5568
                            $field['id']
5569
                        );
5570
                        if (DB::count() > 0) {
5571
                            $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5572
5573
                            // This is a public object
5574
                            $users = DB::query(
5575
                                'SELECT id, public_key
5576
                                FROM ' . prefixTable('users') . '
5577
                                WHERE id NOT IN %li
5578
                                AND public_key != ""',
5579
                                $tpUsersIDs
5580
                            );
5581
5582
                            foreach ($users as $user) {
5583
                                // Insert in DB the new object key for this item by user
5584
                                DB::insert(
5585
                                    prefixTable('sharekeys_fields'),
5586
                                    array(
5587
                                        'object_id' => $field['id'],
5588
                                        'user_id' => (int) $user['id'],
5589
                                        'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5590
                                    )
5591
                                );
5592
                            }
5593
                        }
5594
                    }
5595
5596
                    // Get the FILE object key for the user
5597
                    // Get FILES for this Item
5598
                    $rows = DB::query(
5599
                        'SELECT id
5600
                        FROM ' . prefixTable('files') . '
5601
                        WHERE id_item = %i',
5602
                        $item_id
5603
                    );
5604
                    foreach ($rows as $attachment) {
5605
                        $userKey = DB::queryFirstRow(
5606
                            'SELECT share_key
5607
                            FROM ' . prefixTable('sharekeys_files') . '
5608
                            WHERE user_id = %i AND object_id = %i',
5609
                            $session->get('user-id'),
5610
                            $attachment['id']
5611
                        );
5612
                        if (DB::count() > 0) {
5613
                            $objectKey = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key'));
5614
5615
                            // This is a public object
5616
                            $users = DB::query(
5617
                                'SELECT id, public_key
5618
                                FROM ' . prefixTable('users') . '
5619
                                WHERE id NOT IN %li
5620
                                AND public_key != ""',
5621
                                $tpUsersIDs
5622
                            );
5623
5624
                            foreach ($users as $user) {
5625
                                // Insert in DB the new object key for this item by user
5626
                                DB::insert(
5627
                                    prefixTable('sharekeys_files'),
5628
                                    array(
5629
                                        'object_id' => $attachment['id'],
5630
                                        'user_id' => (int) $user['id'],
5631
                                        'share_key' => encryptUserObjectKey($objectKey, $user['public_key']),
5632
                                    )
5633
                                );
5634
                            }
5635
                        }
5636
                    }
5637
5638
                    // update item
5639
                    DB::update(
5640
                        prefixTable('items'),
5641
                        array(
5642
                            'id_tree' => $inputData['folderId'],
5643
                            'perso' => 0,
5644
                            'updated_at' => time(),
5645
                        ),
5646
                        'id=%i',
5647
                        $item_id
5648
                    );
5649
                }
5650
                // Log item moved
5651
                logItems(
5652
                    $SETTINGS,
5653
                    (int) $item_id,
5654
                    $dataSource['label'],
5655
                    $session->get('user-id'),
5656
                    'at_modification',
5657
                    $session->get('user-login'),
5658
                    'at_moved : ' . $dataSource['title'] . ' -> ' . $dataDestination['title']
5659
                );
5660
            }
5661
        }
5662
5663
        // reload cache table
5664
        require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
5665
        updateCacheTable('reload', null);
5666
5667
        echo (string) prepareExchangedData(
5668
            array(
5669
                'error' => false,
5670
                'message' => '',
5671
            ),
5672
            'encode'
5673
        );
5674
        break;
5675
5676
    /*
5677
     * CASE
5678
     * MASSIVE Delete an item
5679
     */
5680
    case 'mass_delete_items':
5681
        // Check KEY and rights
5682
        if ($inputData['key'] !== $session->get('key')) {
5683
            echo (string) prepareExchangedData(
5684
                array(
5685
                    'error' => true,
5686
                    'message' => $lang->get('key_is_not_correct'),
5687
                ),
5688
                'encode'
5689
            );
5690
            break;
5691
        }
5692
        if ($session->get('user-read_only') === 1) {
5693
            echo (string) prepareExchangedData(
5694
                array(
5695
                    'error' => true,
5696
                    'message' => $lang->get('error_not_allowed_to'),
5697
                ),
5698
                'encode'
5699
            );
5700
            break;
5701
        }
5702
5703
        // decrypt and retreive data in JSON format
5704
        $dataReceived = prepareExchangedData(
5705
            $inputData['data'],
5706
            'decode'
5707
        );
5708
        $post_item_ids = filter_var($dataReceived['item_ids'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5709
5710
        // perform a check in case of Read-Only user creating an item in his PF
5711
        if ($session->get('user-read_only') === 1) {
5712
            echo (string) prepareExchangedData(
5713
                array(
5714
                    'error' => true,
5715
                    'message' => $lang->get('error_not_allowed_to'),
5716
                ),
5717
                'encode'
5718
            );
5719
            break;
5720
        }
5721
5722
        // loop on items to move
5723
        foreach (explode(';', $post_item_ids) as $item_id) {
5724
            if (empty($item_id) === false) {
5725
                // get info
5726
                $dataSource = DB::queryFirstRow(
5727
                    'SELECT label, id_tree
5728
                    FROM ' . prefixTable('items') . '
5729
                    WHERE id=%i',
5730
                    $item_id
5731
                );
5732
5733
                // Check that user can access this folder
5734
                if (
5735
                    in_array($dataSource['id_tree'], $session->get('user-accessible_folders')) === false
5736
                ) {
5737
                    echo (string) prepareExchangedData(
5738
                        array(
5739
                            'error' => true,
5740
                            'message' => $lang->get('error_not_allowed_to'),
5741
                        ),
5742
                        'encode'
5743
                    );
5744
                    break;
5745
                }
5746
5747
                // delete item consists in disabling it
5748
                DB::update(
5749
                    prefixTable('items'),
5750
                    array(
5751
                        'inactif' => '1',
5752
                        'deleted_at' => time(),
5753
                    ),
5754
                    'id = %i',
5755
                    $item_id
5756
                );
5757
5758
                // log
5759
                logItems(
5760
                    $SETTINGS,
5761
                    (int) $item_id,
5762
                    $dataSource['label'],
5763
                    $session->get('user-id'),
5764
                    'at_delete',
5765
                    $session->get('user-login')
5766
                );
5767
5768
                // Update CACHE table
5769
                updateCacheTable('delete_value', (int) $item_id);
5770
            }
5771
        }
5772
5773
        echo (string) prepareExchangedData(
5774
            array(
5775
                'error' => false,
5776
                'message' => '',
5777
            ),
5778
            'encode'
5779
        );
5780
        break;
5781
5782
        /*
5783
        * CASE
5784
        * Send email
5785
    */
5786
    case 'send_email':
5787
        // Check KEY
5788
        if ($inputData['key'] !== $session->get('key')) {
5789
            echo (string) prepareExchangedData(
5790
                array(
5791
                    'error' => true,
5792
                    'message' => $lang->get('key_is_not_correct'),
5793
                ),
5794
                'encode'
5795
            );
5796
            break;
5797
        }
5798
        if ($session->get('user-read_only') === 1) {
5799
            echo (string) prepareExchangedData(
5800
                array(
5801
                    'error' => true,
5802
                    'message' => $lang->get('error_not_allowed_to'),
5803
                ),
5804
                'encode'
5805
            );
5806
            break;
5807
        }
5808
5809
        // decrypt and retrieve data in JSON format
5810
        $dataReceived = prepareExchangedData(
5811
            $inputData['data'],
5812
            'decode'
5813
        );
5814
5815
        // Prepare variables
5816
        $inputData['id'] = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
5817
        $inputData['receipt'] = filter_var($dataReceived['receipt'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5818
        $inputData['cat'] = filter_var($dataReceived['cat'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5819
        $post_content = $request->request->has('name') ? explode(',', $request->request->filter('content', '', FILTER_SANITIZE_FULL_SPECIAL_CHARS)) : '';
5820
5821
        // get links url
5822
        if (empty($SETTINGS['email_server_url']) === true) {
5823
            $SETTINGS['email_server_url'] = $SETTINGS['cpassman_url'];
5824
        }
5825
        if ($inputData['cat'] === 'request_access_to_author') {
5826
            // Variables
5827
            $dataAuthor = DB::queryFirstRow(
5828
                'SELECT email,login
5829
                FROM ' . prefixTable('users') . '
5830
                WHERE id = %i',
5831
                $post_content[1]
5832
            );
5833
5834
            $dataItem = DB::queryFirstRow(
5835
                'SELECT label, id_tree
5836
                FROM ' . prefixTable('items') . '
5837
                WHERE id = %i',
5838
                $post_content[0]
5839
            );
5840
5841
            // Get path
5842
            $path = geItemReadablePath(
5843
                (int) $dataItem['id_tree'],
5844
                $dataItem['label'],
5845
                $SETTINGS
5846
            );
5847
5848
            // Prepare email
5849
            prepareSendingEmail(
5850
                $lang->get('email_request_access_subject'),
5851
                str_replace(
5852
                    array('#tp_item_author#', '#tp_user#', '#tp_item#'),
5853
                    array(' ' . addslashes($dataAuthor['login']), addslashes($session->get('user-login')), $path),
5854
                    $lang->get('email_request_access_mail')
5855
                ),
5856
                $dataAuthor['email'],
5857
                ""
5858
            );
5859
        } elseif ($inputData['cat'] === 'share_this_item') {
5860
            $dataItem = DB::queryFirstRow(
5861
                'SELECT label,id_tree
5862
                FROM ' . prefixTable('items') . '
5863
                WHERE id= %i',
5864
                $inputData['id']
5865
            );
5866
5867
            // Get path
5868
            $path = geItemReadablePath(
5869
                (int) $dataItem['id_tree'],
5870
                $dataItem['label'],
5871
                $SETTINGS
5872
            );
5873
5874
            // Prepare email
5875
            prepareSendingEmail(
5876
                $lang->get('email_share_item_subject'),
5877
                str_replace(
5878
                    array(
5879
                        '#tp_link#',
5880
                        '#tp_user#',
5881
                        '#tp_item#',
5882
                    ),
5883
                    array(
5884
                        empty($SETTINGS['email_server_url']) === false ?
5885
                            $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'],
5886
                        addslashes($session->get('user-login')),
5887
                        addslashes($path),
5888
                    ),
5889
                    $lang->get('email_share_item_mail')
5890
                ),
5891
                $inputData['receipt'],
5892
                ""
5893
            );
5894
        }
5895
5896
        echo (string) prepareExchangedData(
5897
            array(
5898
                'error' => false,
5899
                'message' => '',
5900
            ),
5901
            'encode'
5902
        );
5903
        break;
5904
5905
    /*
5906
     * CASE
5907
     * Item History Log - add new entry
5908
     */
5909
    case 'history_entry_add':
5910
        if ($inputData['key'] !== $session->get('key')) {
5911
            $data = array('error' => 'key_is_wrong');
5912
            echo (string) prepareExchangedData(
5913
                $data,
5914
                'encode'
5915
            );
5916
            break;
5917
        }
5918
5919
        // decrypt and retreive data in JSON format
5920
        $dataReceived = prepareExchangedData(
5921
            $inputData['data'],
5922
            'decode'
5923
        );
5924
5925
        $item_id = filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
5926
        $label = filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5927
        $date = filter_var($dataReceived['date'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5928
        $time = filter_var($dataReceived['time'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
5929
        $session__user_list_folders_limited = $session->get('user-list_folders_limited');
5930
5931
        // Get all informations for this item
5932
        $dataItem = DB::queryFirstRow(
5933
            'SELECT *
5934
            FROM ' . prefixTable('items') . ' as i
5935
            INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
5936
            WHERE i.id=%i AND l.action = %s',
5937
            $item_id,
5938
            'at_creation'
5939
        );
5940
        // check that actual user can access this item
5941
        $restrictionActive = true;
5942
        $restrictedTo = is_null($dataItem['restricted_to']) === false ? array_filter(explode(';', $dataItem['restricted_to'])) : [];
5943
        if (in_array($session->get('user-id'), $restrictedTo)) {
5944
            $restrictionActive = false;
5945
        }
5946
        if (empty($dataItem['restricted_to'])) {
5947
            $restrictionActive = false;
5948
        }
5949
5950
        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)
5951
            || (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)
5952
            || (is_array($session__user_list_folders_limited[$inputData['folderId']]) === true && in_array($inputData['id'], $session__user_list_folders_limited[$inputData['folderId']]) === true)
5953
        ) {
5954
            // Query
5955
            logItems(
5956
                $SETTINGS,
5957
                (int) $item_id,
5958
                $dataItem['label'],
5959
                $session->get('user-id'),
5960
                'at_manual',
5961
                $session->get('user-login'),
5962
                htmlspecialchars_decode($label, ENT_QUOTES),
5963
                null,
5964
                (string) dateToStamp($date.' '.$time, $SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'])
5965
            );
5966
            // Prepare new line
5967
            $data = DB::queryFirstRow(
5968
                'SELECT * FROM ' . prefixTable('log_items') . ' WHERE id_item = %i ORDER BY date DESC',
5969
                $item_id
5970
            );
5971
            $historic = date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $data['date']) . ' - ' . $session->get('user-login') . ' - ' . $lang->get($data['action']) . ' - ' . $data['raison'];
5972
            // send back
5973
            $data = array(
5974
                'error' => '',
5975
                'new_line' => '<br>' . addslashes($historic),
5976
            );
5977
            echo (string) prepareExchangedData(
5978
                $data,
5979
                'encode'
5980
            );
5981
        } else {
5982
            $data = array('error' => 'something_wrong');
5983
            echo (string) prepareExchangedData(
5984
                $data,
5985
                'encode'
5986
            );
5987
            break;
5988
        }
5989
        break;
5990
5991
    /*
5992
     * CASE
5993
     * Free Item for Edition
5994
     */
5995
    case 'free_item_for_edition':
5996
        // Check KEY
5997
        if ($inputData['key'] !== $session->get('key')) {
5998
            echo '[ { "error" : "key_not_conform" } ]';
5999
            break;
6000
        }
6001
        // Do
6002
        DB::delete(
6003
            prefixTable('items_edition'),
6004
            'item_id = %i',
6005
            $inputData['id']
6006
        );
6007
        break;
6008
6009
    /*
6010
     * CASE
6011
     * Check if Item has been changed since loaded
6012
     */
6013
    case 'generate_OTV_url':
6014
        // Check KEY
6015
        if ($inputData['key'] !== $session->get('key')) {
6016
            echo '[ { "error" : "key_not_conform" } ]';
6017
            break;
6018
        }
6019
6020
        // decrypt and retreive data in JSON format
6021
        $dataReceived = prepareExchangedData(
6022
            $inputData['data'],
6023
            'decode'
6024
        );
6025
6026
        // delete all existing old otv codes
6027
        DB::delete(
6028
            prefixTable('otv'),
6029
            'time_limit < %i',
6030
            time()
6031
        );
6032
6033
        // generate session
6034
        $otv_code = GenerateCryptKey(32, false, true, true, false, true);
6035
        $otv_key = GenerateCryptKey(32, false, true, true, false, true);
6036
6037
        // Generate Defuse key
6038
        $otv_user_code_encrypted = defuse_generate_personal_key($otv_key);
6039
6040
        // check if psk is correct.
6041
        $otv_key_encoded = defuse_validate_personal_key(
6042
            $otv_key,
6043
            $otv_user_code_encrypted
6044
        );
6045
6046
        // Decrypt the pwd
6047
        // Should we log a password change?
6048
        $itemQ = DB::queryFirstRow(
6049
            'SELECT s.share_key, i.pw
6050
            FROM ' . prefixTable('items') . ' AS i
6051
            INNER JOIN ' . prefixTable('sharekeys_items') . ' AS s ON (i.id = s.object_id)
6052
            WHERE s.user_id = %i AND s.object_id = %i',
6053
            $session->get('user-id'),
6054
            $dataReceived['id']
6055
        );
6056
        if (DB::count() === 0 || empty($itemQ['pw']) === true) {
6057
            // No share key found
6058
            $pw = '';
6059
        } else {
6060
            $pw = base64_decode(doDataDecryption(
6061
                $itemQ['pw'],
6062
                decryptUserObjectKey(
6063
                    $itemQ['share_key'],
6064
                    $session->get('user-private_key')
6065
                )
6066
            ));
6067
        }
6068
6069
        // Encrypt it with DEFUSE using the generated code as key
6070
        // This is required as the OTV is used by someone without any Teampass account
6071
        $passwd = cryption(
6072
            $pw,
6073
            $otv_key_encoded,
6074
            'encrypt',
6075
            $SETTINGS
6076
        );
6077
        $timestampReference = time();
6078
6079
        DB::insert(
6080
            prefixTable('otv'),
6081
            array(
6082
                'id' => null,
6083
                'item_id' => $dataReceived['id'],
6084
                'timestamp' => $timestampReference,
6085
                'originator' => intval($session->get('user-id')),
6086
                'code' => $otv_code,
6087
                'encrypted' => $passwd['string'],
6088
                'time_limit' => (int) $dataReceived['days'] * (int) TP_ONE_DAY_SECONDS + time(),
6089
                'max_views' => (int) $dataReceived['views'],
6090
                'shared_globaly' => 0,
6091
            )
6092
        );
6093
        $newID = DB::insertId();
6094
6095
        // Prepare URL content
6096
        $otv_session = array(
6097
            'otv' => true,
6098
            'code' => $otv_code,
6099
            'key' => $otv_key_encoded,
6100
            'stamp' => $timestampReference,
6101
        );
6102
6103
        if (isset($SETTINGS['otv_expiration_period']) === false) {
6104
            $SETTINGS['otv_expiration_period'] = 7;
6105
        }
6106
        $url = $SETTINGS['cpassman_url'] . '/index.php?' . http_build_query($otv_session);
6107
6108
        echo json_encode(
6109
            array(
6110
                'error' => '',
6111
                'url' => $url,
6112
                'otv_id' => $newID,
6113
            )
6114
        );
6115
        break;
6116
6117
    /*
6118
    * CASE
6119
    * Check if Item has been changed since loaded
6120
    */
6121
    case 'update_OTV_url':
6122
        // Check KEY
6123
        if ($inputData['key'] !== $session->get('key')) {
6124
            echo '[ { "error" : "key_not_conform" } ]';
6125
            break;
6126
        }
6127
6128
        // decrypt and retreive data in JSON format
6129
        $dataReceived = prepareExchangedData(
6130
            $inputData['data'],
6131
            'decode'
6132
        );
6133
6134
        // get parameters from original link
6135
        $url = $dataReceived['original_link'];
6136
        $parts = parse_url($url);
6137
        if(isset($parts['query'])){
6138
            parse_str($parts['query'], $orignal_link_parameters);
6139
        } else {
6140
            $orignal_link_parameters = array();
6141
        }
6142
6143
        // update database
6144
        DB::update(
6145
            prefixTable('otv'),
6146
            array(
6147
                'time_limit' => (int) $dataReceived['days'] * (int) TP_ONE_DAY_SECONDS + time(),
6148
                'max_views' => (int) $dataReceived['views'],
6149
                'shared_globaly' => (int) $dataReceived['shared_globaly'] === 1 ? 1 : 0,
6150
            ),
6151
            'id = %i',
6152
            $dataReceived['otv_id']
6153
        );
6154
6155
        // Prepare URL content
6156
        $otv_session = [
6157
            'otv' => true,
6158
            'code' => $orignal_link_parameters['code'],
6159
            'key' => $orignal_link_parameters['key'],
6160
            'stamp' => $orignal_link_parameters['stamp'],
6161
        ];
6162
6163
        if ((int) $dataReceived['shared_globaly'] === 1 && isset($SETTINGS['otv_subdomain']) === true && empty($SETTINGS['otv_subdomain']) === false) {
6164
            // Inject subdomain in URL by convering www. to subdomain.
6165
            $domain_scheme = parse_url($SETTINGS['cpassman_url'], PHP_URL_SCHEME);
6166
            $domain_host = parse_url($SETTINGS['cpassman_url'], PHP_URL_HOST);
6167
            if (str_contains($domain_host, 'www.') === true) {
6168
                $domain_host = (string) $SETTINGS['otv_subdomain'] . '.' . substr($domain_host, 4);
6169
            } else {
6170
                $domain_host = (string) $SETTINGS['otv_subdomain'] . '.' . $domain_host;
6171
            }
6172
            $url = $domain_scheme.'://'.$domain_host . '/index.php?'.http_build_query($otv_session);
6173
        } else {
6174
            $url = $SETTINGS['cpassman_url'] . '/index.php?'.http_build_query($otv_session);
6175
        }
6176
6177
        echo (string) prepareExchangedData(
6178
            array(
6179
                'error' => false,
6180
                'new_url' => $url,
6181
            ),
6182
            'encode'
6183
        );
6184
        break;
6185
6186
6187
        /*
6188
    * CASE
6189
    * Free Item for Edition
6190
    */
6191
    case 'image_preview_preparation':
6192
        // Check KEY
6193
        if ($inputData['key'] !== $session->get('key')) {
6194
            echo (string) prepareExchangedData(
6195
                array(
6196
                    'error' => true,
6197
                    'message' => $lang->get('key_is_not_correct'),
6198
                ),
6199
                'encode'
6200
            );
6201
            break;
6202
        }
6203
6204
        // get file info
6205
        $file_info = DB::queryFirstRow(
6206
            'SELECT f.id AS id, f.file AS file, f.name AS name, f.status AS status,
6207
            f.extension AS extension, f.type AS type,
6208
            s.share_key AS share_key
6209
            FROM ' . prefixTable('files') . ' AS f
6210
            INNER JOIN ' . prefixTable('sharekeys_files') . ' AS s ON (f.id = s.object_id)
6211
            WHERE s.user_id = %i AND s.object_id = %i',
6212
            $session->get('user-id'),
6213
            $inputData['id']
6214
        );
6215
6216
        // Check if user has this sharekey
6217
        if (empty($file_info['share_key']) === true) {
6218
            echo (string) prepareExchangedData(
6219
                array(
6220
                    'error' => true,
6221
                    'message' => $lang->get('no_sharekey_found'),
6222
                ),
6223
                'encode'
6224
            );
6225
            break;
6226
        }
6227
6228
        //$fileName = basename($file_info['name'], '.'.$file_info['extension']);
6229
6230
        // prepare image info
6231
        $post_title = basename($file_info['name'], '.' . $file_info['extension']);
6232
        $post_title = isBase64($post_title) === true ? base64_decode($post_title) : $post_title;
6233
        
6234
        // Get image content
6235
        // deepcode ignore PT: File and path are secured directly inside the function decryptFile()
6236
        $fileContent = decryptFile(
6237
            $file_info['file'],
6238
            $SETTINGS['path_to_upload_folder'],
6239
            decryptUserObjectKey($file_info['share_key'], $session->get('user-private_key'))
6240
        );
6241
6242
        // Check error
6243
        if (isset($fileContent['error']) === true) {
6244
            echo (string) prepareExchangedData(
6245
                array(
6246
                    'error' => true,
6247
                    'message' => $fileContent['message'],
6248
                ),
6249
                'encode'
6250
            );
6251
            break;
6252
        }
6253
6254
        // Encrypt data to return
6255
        echo (string) prepareExchangedData(
6256
            array(
6257
                'error' => false,
6258
                'filename' => $post_title . '.' . $file_info['extension'],
6259
                'file_type' => $file_info['type'],
6260
                'file_content' => $fileContent,
6261
            ),
6262
            'encode'
6263
        );
6264
        break;
6265
6266
    /*
6267
     * CASE
6268
     * Get list of users that have access to the folder
6269
     */
6270
    case 'refresh_visible_folders':
6271
        // Check KEY
6272
        if ($inputData['key'] !== $session->get('key')) {
6273
            echo (string) prepareExchangedData(
6274
                array(
6275
                    'error' => true,
6276
                    'message' => $lang->get('key_is_not_correct'),
6277
                ),
6278
                'encode'
6279
            );
6280
            break;
6281
        }
6282
        $arr_data = [];
6283
        $arrayFolders = [];
6284
6285
        // decrypt and retreive data in JSON format
6286
        $dataReceived = prepareExchangedData(
6287
            $inputData['data'],
6288
            'decode'
6289
        );
6290
6291
        // Will we show the root folder?
6292
        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
6293
        ) {
6294
            $arr_data['can_create_root_folder'] = 1;
6295
        } else {
6296
            $arr_data['can_create_root_folder'] = 0;
6297
        }
6298
6299
        // do we have a cache to be used?
6300
        if (isset($dataReceived['force_refresh_cache']) === true && $dataReceived['force_refresh_cache'] === false) {
6301
            $goCachedFolders = loadFoldersListByCache('visible_folders', 'folders');
6302
            if ($goCachedFolders['state'] === true) {
6303
                $arr_data['folders'] = json_decode($goCachedFolders['data'], true);
6304
                // send data
6305
                echo (string) prepareExchangedData(
6306
                    [
6307
                        'error' => 'false',
6308
                        'html_json' => ($arr_data),
6309
                        'extra' => isset($goCachedFolders['extra']) ? $goCachedFolders['extra'] : '',
6310
                    ],
6311
                    'encode'
6312
                );
6313
                break;
6314
            }
6315
        }
6316
        // Build list of visible folders
6317
        if (
6318
            (int) $session->get('user-admin') === 1
6319
        ) {
6320
            $session->set('user-accessible_folders', $session->get('user-personal_visible_folders'));
6321
        }
6322
6323
        if (null !== $session->get('user-list_folders_limited') && count($session->get('user-list_folders_limited')) > 0) {
6324
            $listFoldersLimitedKeys = array_keys($session->get('user-list_folders_limited'));
6325
        } else {
6326
            $listFoldersLimitedKeys = array();
6327
        }
6328
        // list of items accessible but not in an allowed folder
6329
        if (
6330
            null !== $session->get('system-list_restricted_folders_for_items') &&
6331
            count($session->get('system-list_restricted_folders_for_items')) > 0
6332
        ) {
6333
            $listRestrictedFoldersForItemsKeys = array_keys($session->get('system-list_restricted_folders_for_items'));
6334
        } else {
6335
            $listRestrictedFoldersForItemsKeys = array();
6336
        }
6337
        
6338
        //Build tree
6339
        $tree->rebuild();
6340
        $folders = $tree->getDescendants();
6341
        foreach ($folders as $folder) {
6342
            // Be sure that user can only see folders he/she is allowed to
6343
            if (
6344
                in_array($folder->id, $session->get('user-forbiden_personal_folders')) === false
6345
                || in_array($folder->id, $session->get('user-accessible_folders')) === true
6346
                || in_array($folder->id, $listFoldersLimitedKeys) === true
6347
                || in_array($folder->id, $listRestrictedFoldersForItemsKeys) === true
6348
            ) {
6349
                // Init
6350
                $displayThisNode = false;
6351
6352
                // Check if any allowed folder is part of the descendants of this node
6353
                $nodeDescendants = $tree->getDescendantsFromTreeArray($folders, $folder->id);
0 ignored issues
show
Bug introduced by
$folders of type array is incompatible with the type integer expected by parameter $treeAray of TeampassClasses\NestedTr...cendantsFromTreeArray(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

6353
                $nodeDescendants = $tree->getDescendantsFromTreeArray(/** @scrutinizer ignore-type */ $folders, $folder->id);
Loading history...
6354
                foreach ($nodeDescendants as $node) {
6355
                    // manage tree counters
6356
                    if (
6357
                        in_array($node, array_merge($session->get('user-accessible_folders'), $session->get('system-list_restricted_folders_for_items'))) === true
6358
                        || (is_array($listFoldersLimitedKeys) === true && in_array($node, $listFoldersLimitedKeys) === true)
6359
                        || (is_array($listRestrictedFoldersForItemsKeys) === true && in_array($node, $listRestrictedFoldersForItemsKeys) === true)
6360
                    ) {
6361
                        $displayThisNode = true;
6362
                        //break;
6363
                    }
6364
                }
6365
6366
                if ($displayThisNode === true) {
6367
                    // ALL FOLDERS
6368
                    // Build path
6369
                    $arbo = $tree->getPath($folder->id, false);
6370
                    $path = '';
6371
                    foreach ($arbo as $elem) {
6372
                        $path = (empty($path) ? '' : $path . ' / ') . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
6373
                    }
6374
6375
                    // Build array
6376
                    array_push($arrayFolders, [
6377
                        'id' => (int) $folder->id,
6378
                        'level' => (int) $folder->nlevel,
6379
                        'title' => ((int) $folder->title === (int) $session->get('user-id') && (int) $folder->nlevel === 1) ? $session->get('user-login') : $folder->title,
6380
                        'disabled' => (
6381
                            in_array($folder->id, $session->get('user-accessible_folders')) === false
6382
                            || in_array($folder->id, $session->get('user-read_only_folders')) === true
6383
                        ) ? 1 : 0,
6384
                        'parent_id' => (int) $folder->parent_id,
6385
                        'perso' => (int) $folder->personal_folder,
6386
                        'path' => htmlspecialchars($path),
6387
                        'is_visible_active' => (null !== $session->get('user-read_only_folders') && in_array($folder->id, $session->get('user-read_only_folders'))) ? 1 : 0,
6388
                    ]);
6389
                }
6390
            }
6391
        }
6392
        if (empty($arrayFolders) === false) {
6393
            // store array to return
6394
            $arr_data['folders'] = $arrayFolders;
6395
6396
            // update session
6397
            $session->set('user-folders_list', $arr_data['folders']);
6398
6399
            // update cache
6400
            cacheTreeUserHandler(
6401
                (int) $session->get('user-id'),
6402
                json_encode($arr_data['folders']),
6403
                $SETTINGS,
6404
                'visible_folders',
6405
            );
6406
        }
6407
6408
        // send data
6409
        echo (string) prepareExchangedData(
6410
            [
6411
                'error' => 'false',
6412
                'html_json' => $arr_data,
6413
            ],
6414
            'encode'
6415
        );
6416
6417
        break;
6418
6419
    /*
6420
     * CASE
6421
     * Get list of users that have access to the folder
6422
     */
6423
    case 'refresh_folders_other_info':
6424
        // Check KEY
6425
        if ($inputData['key'] !== $session->get('key')) {
6426
            echo (string) prepareExchangedData(
6427
                array(
6428
                    'error' => true,
6429
                    'message' => $lang->get('key_is_not_correct'),
6430
                ),
6431
                'encode'
6432
            );
6433
            break;
6434
        }
6435
6436
        $ret = [];
6437
        $foldersArray = json_decode($inputData['data'], true);
6438
        if (is_array($foldersArray) === true && $inputData['data'] !== '[null]') {
6439
            $rows = DB::query(
6440
                'SELECT id, categories
6441
                FROM ' . prefixTable('nested_tree') . '
6442
                WHERE id IN (%l)',
6443
                implode(',', $foldersArray)
6444
            );
6445
            foreach ($rows as $record) {
6446
                if (empty($record['categories']) === false) {
6447
                    array_push(
6448
                        $ret,
6449
                        array($record['id'] => json_decode($record['categories'], true))
6450
                    );
6451
                }
6452
            }
6453
        }
6454
6455
        // send data
6456
        echo (string) prepareExchangedData(
6457
            [
6458
                'error' => '',
6459
                'result' => $ret,
6460
            ],
6461
            'encode'
6462
        );
6463
6464
        break;
6465
6466
        /*
6467
    * CASE
6468
    * Load item history
6469
    */
6470
    case 'load_item_history':
6471
        // Check KEY
6472
        if ($inputData['key'] !== $session->get('key')) {
6473
            echo (string) prepareExchangedData(
6474
                array('error' => 'ERR_KEY_NOT_CORRECT'),
6475
                'encode'
6476
            );
6477
            break;
6478
        }
6479
        
6480
        // get item info
6481
        $dataItem = DB::queryFirstRow(
6482
            'SELECT *
6483
            FROM ' . prefixTable('items') . '
6484
            WHERE id=%i',
6485
            $inputData['itemId']
6486
        );
6487
6488
        // get item history
6489
        $history = [];
6490
        $previous_passwords = [];
6491
        $rows = DB::query(
6492
            'SELECT l.date as date, l.action as action, l.raison as raison,
6493
                u.login as login, u.avatar_thumb as avatar_thumb, u.name as name, u.lastname as lastname,
6494
                l.old_value as old_value
6495
            FROM ' . prefixTable('log_items') . ' as l
6496
            INNER JOIN ' . prefixTable('users') . ' as u ON (l.id_user=u.id)
6497
            WHERE id_item=%i AND l.action NOT IN (%l)
6498
            ORDER BY date DESC',
6499
            $inputData['itemId'],
6500
            '"at_shown","at_password_copied", "at_shown", "at_password_shown", "at_password_shown_edit_form"'
6501
        );
6502
        foreach ($rows as $record) {
6503
            if (empty($record['raison']) === true) {
6504
                $reason[0] = '';
6505
            } else {
6506
                $reason = array_map('trim', explode(':', $record['raison']));
6507
            }
6508
            
6509
            // imported via API
6510
            if (empty($record['login']) === true) {
6511
                $record['login'] = $lang->get('imported_via_api') . ' [' . $record['raison'] . ']';
6512
            }
6513
            
6514
            // Prepare avatar
6515
            if (isset($record['avatar_thumb']) && empty($record['avatar_thumb']) === false) {
6516
                if (file_exists($SETTINGS['cpassman_dir'] . '/includes/avatars/' . $record['avatar_thumb'])) {
6517
                    $avatar = $SETTINGS['cpassman_url'] . '/includes/avatars/' . $record['avatar_thumb'];
6518
                } else {
6519
                    $avatar = $SETTINGS['cpassman_url'] . '/includes/images/photo.jpg';
6520
                }
6521
            } else {
6522
                $avatar = $SETTINGS['cpassman_url'] . '/includes/images/photo.jpg';
6523
            }
6524
6525
            // Prepare action
6526
            $action = '';
6527
            $detail = '';
6528
            if ($reason[0] === 'at_pw') {
6529
                $action = $lang->get($reason[0]);
6530
                
6531
                // get previous password
6532
                if (empty($record['old_value']) === false) {
6533
                    $previous_pwd = cryption(
6534
                        $record['old_value'],
6535
                        '',
6536
                        'decrypt'
6537
                    );
6538
                    array_push(
6539
                        $previous_passwords, 
6540
                        [
6541
                            'password' => htmlentities($previous_pwd['string']),
6542
                            'date' => date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['date']),
6543
                        ]
6544
                    );
6545
                }
6546
            } elseif ($record['action'] === 'at_manual') {
6547
                $detail = $reason[0];
6548
                $action = $lang->get($record['action']);
6549
            } elseif ($reason[0] === 'at_description') {
6550
                $action = $lang->get('description_has_changed');
6551
            } elseif (empty($record['raison']) === false && $reason[0] !== 'at_creation') {
6552
                $action = $lang->get($reason[0]);
6553
                if ($reason[0] === 'at_moved') {
6554
                    $tmp = explode(' -> ', $reason[1]);
6555
                    $detail = $lang->get('from') . ' <span class="font-weight-light">' . $tmp[0] . '</span> ' . $lang->get('to') . ' <span class="font-weight-light">' . $tmp[1] . ' </span>';
6556
                } elseif ($reason[0] === 'at_field') {
6557
                    $tmp = explode(' => ', $reason[1]);
6558
                    if (count($tmp) > 1) {
6559
                        $detail = '<b>' . trim($tmp[0]) . '</b> | ' . $lang->get('previous_value') .
6560
                            ': <span class="font-weight-light">' . trim($tmp[1]) . '</span>';
6561
                    } else {
6562
                        $detail = trim($reason[1]);
6563
                    }
6564
                } elseif (in_array($reason[0], array('at_restriction', 'at_email', 'at_login', 'at_label', 'at_url', 'at_tag')) === true) {
6565
                    $tmp = explode(' => ', $reason[1]);
6566
                    $detail = empty(trim($tmp[0])) === true ?
6567
                        $lang->get('no_previous_value') : $lang->get('previous_value') . ': <span class="font-weight-light">' . $tmp[0] . ' </span>';
6568
                } elseif ($reason[0] === 'at_automatic_del') {
6569
                    $detail = $lang->get($reason[1]);
6570
                } elseif ($reason[0] === 'at_anyoneconmodify' || $reason[0] === 'at_otp_status') {
6571
                    $detail = $lang->get($reason[1]);
6572
                } elseif ($reason[0] === 'at_add_file' || $reason[0] === 'at_del_file') {
6573
                    $tmp = explode(':', $reason[1]);
6574
                    $tmp = explode('.', $tmp[0]);
6575
                    $detail = isBase64($tmp[0]) === true ?
6576
                        base64_decode($tmp[0]) . '.' . $tmp[1] : $tmp[0];
6577
                } elseif ($reason[0] === 'at_import') {
6578
                    $detail = '';
6579
                } elseif (in_array($reason[0], array('csv', 'pdf')) === true) {
6580
                    $detail = $reason[0];
6581
                    $action = $lang->get('exported_to_file');
6582
                } else {
6583
                    $detail = $reason[0];
6584
                }
6585
            } else {
6586
                $detail = $lang->get($record['action']);
6587
                $action = '';
6588
            }
6589
6590
            array_push(
6591
                $history,
6592
                array(
6593
                    'avatar' => $avatar,
6594
                    'login' => $record['login'],
6595
                    'name' => $record['name'] . ' ' . $record['lastname'],
6596
                    'date' => date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['date']),
6597
                    'action' => $action,
6598
                    'detail' => $detail,
6599
                )
6600
            );
6601
        }
6602
6603
        // order previous passwords by date
6604
        $key_values = array_column($previous_passwords, 'date'); 
6605
        array_multisort($key_values, /** @scrutinizer ignore-type */SORT_DESC, $previous_passwords);
0 ignored issues
show
Bug introduced by
SORT_DESC cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

6605
        array_multisort($key_values, /** @scrutinizer ignore-type *//** @scrutinizer ignore-type */ SORT_DESC, $previous_passwords);
Loading history...
6606
6607
        // send data
6608
        // deepcode ignore ServerLeak: Data is encrypted before being sent
6609
        echo (string) prepareExchangedData(
6610
            [
6611
                'error' => '',
6612
                'history' => $history,
6613
                'previous_passwords' => $previous_passwords,
6614
            ],
6615
            'encode'
6616
        );
6617
6618
        break;
6619
6620
    case 'suggest_item_change':
6621
        // Check KEY
6622
        if ($inputData['key'] !== $session->get('key')) {
6623
            echo (string) prepareExchangedData(
6624
                array(
6625
                    'error' => 'key_not_conform',
6626
                    'message' => $lang->get('key_is_not_correct'),
6627
                ),
6628
                'encode'
6629
            );
6630
            break;
6631
        }
6632
        // decrypt and retrieve data in JSON format
6633
        $data_received = prepareExchangedData(
6634
            $inputData['data'],
6635
            'decode'
6636
        );
6637
6638
        // prepare variables
6639
        $label = htmlspecialchars_decode($data_received['label'], ENT_QUOTES);
6640
        $pwd = htmlspecialchars_decode($data_received['password']);
6641
        $login = htmlspecialchars_decode($data_received['login'], ENT_QUOTES);
6642
        $email = htmlspecialchars_decode($data_received['email']);
6643
        $url = htmlspecialchars_decode($data_received['url']);
6644
        $folder = htmlspecialchars_decode($data_received['folder_id']);
6645
        $comment = htmlspecialchars_decode($data_received['comment']);
6646
        $item_id = htmlspecialchars_decode($data_received['item_id']);
6647
6648
        if (empty($pwd)) {
6649
            $cryptedStuff['encrypted'] = '';
6650
            $cryptedStuff['objectKey'] = '';
6651
        } else {
6652
            $cryptedStuff = doDataEncryption($pwd);
6653
        }
6654
6655
        // query
6656
        DB::insert(
6657
            prefixTable('items_change'),
6658
            array(
6659
                'item_id' => $item_id,
6660
                'label' => $label,
6661
                'pw' => $cryptedStuff['encrypted'],
6662
                'login' => $login,
6663
                'email' => $email,
6664
                'url' => $url,
6665
                'description' => '',
6666
                'comment' => $comment,
6667
                'folder_id' => $folder,
6668
                'user_id' => (int) $session->get('user-id'),
6669
                'timestamp' => time(),
6670
            )
6671
        );
6672
        $newID = DB::insertId();
6673
6674
        // Create sharekeys for users
6675
        storeUsersShareKey(
6676
            prefixTable('sharekeys_items'),
6677
            0,
6678
            (int) $newID,
6679
            $cryptedStuff['objectKey'],
6680
        );
6681
6682
        // get some info to add to the notification email
6683
        $resp_user = DB::queryFirstRow(
6684
            'SELECT login FROM ' . prefixTable('users') . ' WHERE id = %i',
6685
            $session->get('user-id')
6686
        );
6687
        $resp_folder = DB::queryFirstRow(
6688
            'SELECT title FROM ' . prefixTable('nested_tree') . ' WHERE id = %i',
6689
            $folder
6690
        );
6691
6692
        // notify Managers
6693
        $emailSettings = new EmailSettings($SETTINGS);
6694
        $emailService = new EmailService();
6695
        $rows = DB::query(
6696
            'SELECT email
6697
            FROM ' . prefixTable('users') . '
6698
            WHERE `gestionnaire` = %i AND `email` IS NOT NULL',
6699
            1
6700
        );
6701
        foreach ($rows as $record) {
6702
            $emailService->sendMail(
6703
                $lang->get('suggestion_notify_subject'),
6704
                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')),
6705
                $record['email'],
6706
                $emailSettings
6707
            );
6708
        }
6709
6710
        echo (string) prepareExchangedData(
6711
            array(
6712
                'error' => '',
6713
            ),
6714
            'encode'
6715
        );
6716
        break;
6717
6718
    case 'send_request_access':
6719
        // Check KEY
6720
        if ($inputData['key'] !== $session->get('key')) {
6721
            echo (string) prepareExchangedData(
6722
                array(
6723
                    'error' => 'key_not_conform',
6724
                    'message' => $lang->get('key_is_not_correct'),
6725
                ),
6726
                'encode'
6727
            );
6728
            break;
6729
        }
6730
        // decrypt and retrieve data in JSON format
6731
        $dataReceived = prepareExchangedData(
6732
            $inputData['data'],
6733
            'decode'
6734
        );
6735
6736
        // prepare variables
6737
        //$post_email_body = filter_var($dataReceived['email'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
6738
        $inputData['itemId'] = (int) filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
6739
6740
        // Send email
6741
        $dataItem = DB::queryFirstRow(
6742
            'SELECT label, id_tree
6743
            FROM ' . prefixTable('items') . '
6744
            WHERE id = %i',
6745
            $inputData['itemId']
6746
        );
6747
6748
        // Do log
6749
        logItems(
6750
            $SETTINGS,
6751
            (int) $inputData['itemId'],
6752
            $dataItem['label'],
6753
            $session->get('user-id'),
6754
            'at_access',
6755
            $session->get('user-login')
6756
        );
6757
6758
        // Return
6759
        echo (string) prepareExchangedData(
6760
            array(
6761
                'error' => false,
6762
                'message' => '',
6763
            ),
6764
            'encode'
6765
        );
6766
6767
        break;
6768
6769
    /*
6770
     * CASE
6771
     * save_notification_status
6772
     */
6773
    case 'save_notification_status':
6774
        // Check KEY
6775
        if ($inputData['key'] !== $session->get('key')) {
6776
            echo (string) prepareExchangedData(
6777
                array(
6778
                    'error' => 'key_not_conform',
6779
                    'message' => $lang->get('key_is_not_correct'),
6780
                ),
6781
                'encode'
6782
            );
6783
            break;
6784
        }
6785
        // decrypt and retrieve data in JSON format
6786
        $dataReceived = prepareExchangedData(
6787
            $inputData['data'],
6788
            'decode'
6789
        );
6790
6791
        // prepare variables
6792
        $post_notification_status = (int) filter_var($dataReceived['notification_status'], FILTER_SANITIZE_NUMBER_INT);
6793
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
6794
6795
        DB::query(
6796
            'SELECT *
6797
            FROM ' . prefixTable('notification') . '
6798
            WHERE item_id = %i AND user_id = %i',
6799
            $inputData['itemId'],
6800
            $session->get('user-id')
6801
        );
6802
        if (DB::count() > 0) {
6803
            // Notification is set for this user on this item
6804
            if ((int) $post_notification_status === 0) {
6805
                // Remove the notification
6806
                DB::delete(
6807
                    prefixTable('notification'),
6808
                    'item_id = %i AND user_id = %i',
6809
                    $inputData['itemId'],
6810
                    $session->get('user-id')
6811
                );
6812
            }
6813
        } else {
6814
            // Notification is not set on this item
6815
            if ((int) $post_notification_status === 1) {
6816
                // Add the notification
6817
                DB::insert(
6818
                    prefixTable('notification'),
6819
                    array(
6820
                        'item_id' => $inputData['itemId'],
6821
                        'user_id' => (int) $session->get('user-id'),
6822
                    )
6823
                );
6824
            }
6825
        }
6826
6827
        $data = array(
6828
            'error' => false,
6829
            'message' => '',
6830
        );
6831
6832
        // send data
6833
        echo (string) prepareExchangedData(
6834
            $data,
6835
            'encode'
6836
        );
6837
6838
        break;
6839
6840
    /*
6841
     * CASE
6842
     * delete_uploaded_files_but_not_saved
6843
     */
6844
    case 'delete_uploaded_files_but_not_saved':
6845
        // Check KEY
6846
        if ($inputData['key'] !== $session->get('key')) {
6847
            echo (string) prepareExchangedData(
6848
                array(
6849
                    'error' => 'key_not_conform',
6850
                    'message' => $lang->get('key_is_not_correct'),
6851
                ),
6852
                'encode'
6853
            );
6854
            break;
6855
        }
6856
        // decrypt and retrieve data in JSON format
6857
        $dataReceived = prepareExchangedData(
6858
            $inputData['data'],
6859
            'decode'
6860
        );
6861
6862
        // prepare variables
6863
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
6864
6865
        // Delete non confirmed files for this item
6866
        // And related logs
6867
        $rows = DB::query(
6868
            'SELECT id, file AS filename
6869
            FROM ' . prefixTable('files') . '
6870
            WHERE id_item = %i AND confirmed = %i',
6871
            $inputData['itemId'],
6872
            0
6873
        );
6874
        foreach ($rows as $file) {
6875
            // Delete file in DB
6876
            DB::delete(
6877
                prefixTable('files'),
6878
                'id = %i',
6879
                $file['id']
6880
            );
6881
6882
            // Delete file on server
6883
            unlink($SETTINGS['path_to_upload_folder'] . '/' . TP_FILE_PREFIX . base64_decode($file['filename']));
6884
6885
            // Delete related logs
6886
            $logFile = DB::query(
6887
                'SELECT increment_id, raison
6888
                FROM ' . prefixTable('log_items') . '
6889
                WHERE id_item = %i AND id_user = %i AND action = %s AND raison LIKE "at_add_file :%"',
6890
                $inputData['itemId'],
6891
                $session->get('user-id'),
6892
                'at_modification'
6893
            );
6894
            foreach ($logFile as $log) {
6895
                $tmp = explode(':', $log['raison']);
6896
                if (count($tmp) === 3 && (int) $tmp[2] === (int) $file['id']) {
6897
                    DB::delete(
6898
                        prefixTable('log_items'),
6899
                        'increment_id = %i',
6900
                        $log['increment_id']
6901
                    );
6902
                }
6903
            }
6904
        }
6905
6906
        $data = array(
6907
            'error' => false,
6908
            'message' => '',
6909
        );
6910
6911
        // send data
6912
        echo (string) prepareExchangedData(
6913
            $data,
6914
            'encode'
6915
        );
6916
6917
        break;
6918
6919
        /*
6920
    * CASE
6921
    * confirm_attachments
6922
    */
6923
    case 'confirm_attachments':
6924
        // Check KEY
6925
        if ($inputData['key'] !== $session->get('key')) {
6926
            echo (string) prepareExchangedData(
6927
                array(
6928
                    'error' => 'key_not_conform',
6929
                    'message' => $lang->get('key_is_not_correct'),
6930
                ),
6931
                'encode'
6932
            );
6933
            break;
6934
        }
6935
        // decrypt and retrieve data in JSON format
6936
        $dataReceived = prepareExchangedData(
6937
            $inputData['data'],
6938
            'decode'
6939
        );
6940
6941
        // prepare variables
6942
        $inputData['itemId'] = (int) filter_var($dataReceived['item_id'], FILTER_SANITIZE_NUMBER_INT);
6943
6944
        // Confirm attachments
6945
        $rows = DB::query(
6946
            'SELECT id, file AS filename
6947
            FROM ' . prefixTable('files') . '
6948
            WHERE id_item = %i AND confirmed = %i',
6949
            $inputData['itemId'],
6950
            0
6951
        );
6952
        foreach ($rows as $file) {
6953
            DB::update(
6954
                prefixTable('files'),
6955
                array(
6956
                    'confirmed' => 1,
6957
                ),
6958
                'id_item = %i',
6959
                $inputData['itemId']
6960
            );
6961
        }
6962
6963
        $data = array(
6964
            'error' => false,
6965
            'message' => '',
6966
        );
6967
6968
        // send data
6969
        echo (string) prepareExchangedData(
6970
            $data,
6971
            'encode'
6972
        );
6973
6974
        break;
6975
6976
    /*
6977
    * CASE
6978
    * check_current_access_rights
6979
    */
6980
    case 'check_current_access_rights':
6981
        // Check KEY
6982
        if ($inputData['key'] !== $session->get('key')) {
6983
            echo (string) prepareExchangedData(
6984
                array(
6985
                    'error' => 'key_not_conform',
6986
                    'message' => $lang->get('key_is_not_correct'),
6987
                ),
6988
                'encode'
6989
            );
6990
            break;
6991
        }
6992
6993
        // Init
6994
        $editionLock = false;
6995
6996
        // decrypt and retrieve data in JSON format
6997
        $dataReceived = prepareExchangedData(
6998
            $inputData['data'],
6999
            'decode'
7000
        );
7001
        
7002
        // prepare variables
7003
        $inputData['userId'] = (int) filter_var($dataReceived['userId'], FILTER_SANITIZE_NUMBER_INT);
7004
        $inputData['itemId'] = (int) filter_var($dataReceived['itemId'], FILTER_SANITIZE_NUMBER_INT);
7005
        $inputData['treeId'] = (int) filter_var($dataReceived['treeId'], FILTER_SANITIZE_NUMBER_INT);
7006
7007
        // Check rights
7008
        $data = getCurrentAccessRights(
7009
            $inputData['userId'],
7010
            $inputData['itemId'],
7011
            $inputData['treeId'],
7012
        );
7013
7014
        // send data
7015
        echo (string) prepareExchangedData(
7016
            $data,
7017
            'encode'
7018
        );
7019
7020
        break;
7021
}
7022
7023
// Build the QUERY in case of GET
7024
if (isset($inputData['getType'])) {
7025
    switch ($inputData['getType']) {
7026
        /*
7027
        * CASE
7028
        * Autocomplet for TAGS
7029
        */
7030
        case 'autocomplete_tags':
7031
            // Get a list off all existing TAGS
7032
            $listOfTags = '';
7033
            $rows = DB::query('SELECT tag FROM ' . prefixTable('tags') . ' WHERE tag LIKE %ss GROUP BY tag', $inputData['getTerm']);
7034
            foreach ($rows as $record) {
7035
                if (empty($listOfTags)) {
7036
                    $listOfTags = '"' . $record['tag'] . '"';
7037
                } else {
7038
                    $listOfTags .= ', "' . $record['tag'] . '"';
7039
                }
7040
            }
7041
            echo '[' . $listOfTags . ']';
7042
            break;
7043
    }
7044
}
7045
7046
/**
7047
 * Identify if this group authorize creation of item without the complexit level reached
7048
 *
7049
 * @param int $groupe ID for group
7050
 *
7051
 * @return array list of roles
7052
*/
7053
function recupDroitCreationSansComplexite($groupe)
7054
{
7055
    $data = DB::queryFirstRow(
7056
        'SELECT bloquer_creation, bloquer_modification, personal_folder
7057
        FROM ' . prefixTable('nested_tree') . ' WHERE id = %i',
7058
        $groupe
7059
    );
7060
    // Check if it's in a personal folder. If yes, then force complexity overhead.
7061
    if ($data !== null && (int) $data['personal_folder'] === 1) {
7062
        return array(
7063
            'bloquer_modification_complexite' => 1,
7064
            'bloquer_creation_complexite' => 1,
7065
        );
7066
    }
7067
7068
    return array(
7069
        'bloquer_modification_complexite' => $data !== null ? (int) $data['bloquer_modification'] : 0,
7070
        'bloquer_creation_complexite' => $data !== null ? (int) $data['bloquer_creation'] : 0,
7071
    );
7072
}
7073
7074
/**
7075
 * Permits to identify what icon to display depending on file extension.
7076
 *
7077
 * @param string $ext extension
7078
 *
7079
 * @return string
7080
 */
7081
function fileFormatImage($ext)
7082
{
7083
    if (in_array($ext, TP_OFFICE_FILE_EXT)) {
7084
        $image = 'fas fa-file-word';
7085
    } elseif ($ext === 'pdf') {
7086
        $image = 'fas fa-file-pdf';
7087
    } elseif (in_array($ext, TP_IMAGE_FILE_EXT)) {
7088
        $image = 'fas fa-file-image';
7089
    } elseif ($ext === 'txt') {
7090
        $image = 'fas fa-file-alt';
7091
    } else {
7092
        $image = 'fas fa-file';
7093
    }
7094
7095
    return $image;
7096
}
7097
7098
7099
/**
7100
 * Get rights of user on specific folder/item.
7101
 * 
7102
 * @param int $userId ID of user.
7103
 * @param int $itemId ID of item.
7104
 * @param int $treeId ID of folder.
7105
 * 
7106
 * @return array with access rights.
7107
 */
7108
function getCurrentAccessRights(int $userId, int $itemId, int $treeId): array
7109
{
7110
    $session = SessionManager::getSession();
7111
    
7112
    // Check if the item is locked and whether the current user can edit it
7113
    $editionLock = isItemLocked($itemId, $session, $userId);
7114
7115
    // Check if the item is being processed by another user
7116
    if (isProcessOnGoing($itemId)) {
7117
        return getAccessResponse(false, true, false, false);
7118
    }
7119
7120
    // Retrieve user's visible folders from the cache_tree table
7121
    $visibleFolders = getUserVisibleFolders($userId);
7122
7123
    // Check if the folder is in the user's read-only list
7124
    if (in_array($treeId, $session->get('user-read_only_folders'))) {
7125
        return getAccessResponse(false, true, false, false);
7126
    }
7127
    
7128
    // Check if the folder is in the user's allowed folders list defined by admin
7129
    if (in_array($treeId, $session->get('user-allowed_folders_by_definition'))) {
7130
        return getAccessResponse(false, true, true, true);
7131
    }
7132
7133
    // Check if the folder is personal to the user
7134
    foreach ($visibleFolders as $folder) {
7135
        if ($folder['id'] == $treeId && (int) $folder['perso'] === 1) {
7136
            return getAccessResponse(false, true, true, true);
7137
        }
7138
    }
7139
7140
    // Determine the user's access rights based on their roles for this folder
7141
    [$edit, $delete] = getRoleBasedAccess($session, $treeId);
7142
7143
    // Log access rights information if logging is enabled
7144
    if (LOG_TO_SERVER === true) {
7145
        error_log("TEAMPASS - Folder: $treeId - User: $userId - edit: $edit - delete: $delete");
7146
    }
7147
7148
    return getAccessResponse(false, true, $edit, $delete, $editionLock);
7149
}
7150
7151
/**
7152
 * Checks if the item is locked by another user or if there is an ongoing encryption process.
7153
 * If the item is locked, the function determines if the lock has expired or not.
7154
 * 
7155
 * @param int $itemId The ID of the item to check
7156
 * @param object $session The current session object
7157
 * @param int $userId The ID of the current user
7158
 * 
7159
 * @return bool True if the item is locked, false otherwise
7160
 */
7161
function isItemLocked(int $itemId, $session, int $userId): bool
7162
{
7163
    global $SETTINGS;
7164
7165
    $now = time();
7166
    $editionLocks = DB::query(
7167
        'SELECT timestamp, user_id, increment_id
7168
         FROM ' . prefixTable('items_edition') . '
7169
         WHERE item_id = %i 
7170
         ORDER BY increment_id DESC',
7171
        $itemId
7172
    );
7173
7174
    // Check if there are any locks for this item
7175
    if (count($editionLocks) === 0) {
7176
        // No locks found, create a new lock
7177
        createEditionLock($itemId, $userId, $now);
7178
        return false;
7179
    }
7180
7181
    // Check if the last lock is older than the defined period
7182
    $lastLock = $editionLocks[0];
7183
7184
    // If the lock is for the current user, update the timestamp
7185
    if ((int) $lastLock['user_id'] === $userId) {
7186
        DB::update(
7187
            prefixTable('items_edition'),
7188
            ['timestamp' => $now],
7189
            'increment_id = %i',
7190
            $lastLock['increment_id']
7191
        );
7192
        return false;
7193
    }
7194
7195
    // Calculate the delay for the lock
7196
    $delay = isset($SETTINGS['delay_item_edition']) && $SETTINGS['delay_item_edition'] > 0
7197
        ? $SETTINGS['delay_item_edition'] * 60
7198
        : EDITION_LOCK_PERIOD;
7199
7200
    // Calculate the elapsed time since the last lock
7201
    $elapsed = abs($now - (int) $lastLock['timestamp']);
7202
7203
    // Check if the lock has expired
7204
    if ($elapsed > $delay) {
7205
        // Delete all edition locks for this item
7206
        DB::delete(prefixTable('items_edition'), 'item_id = %i', $itemId);
7207
7208
        // Delete related background tasks if any
7209
        $task = DB::queryFirstRow(
7210
            'SELECT increment_id FROM ' . prefixTable('background_tasks') . '
7211
             WHERE item_id = %i AND finished_at = ""',
7212
            $itemId
7213
        );
7214
7215
        // If a task is found, delete its related tasks
7216
        if (!empty($task)) {
7217
            deleteProcessAndRelatedTasks((int) $task['increment_id']);
7218
        }
7219
7220
        // Check if encryption process is still running
7221
        DB::queryFirstRow(
7222
            'SELECT JSON_EXTRACT(arguments, "$.all_users_except_id") AS all_users_except_id
7223
             FROM ' . prefixTable('background_tasks') . '
7224
             WHERE item_id = %i AND finished_at = ""
7225
             ORDER BY increment_id DESC',
7226
            $itemId
7227
        );
7228
7229
        // If encryption process is not running, delete the lock        
7230
        if (DB::count() === 0) {
7231
            DB::update(
7232
                prefixTable('items_edition'),
7233
                ['timestamp' => $now],
7234
                'item_id = %i AND user_id = %i',
7235
                $itemId,
7236
                $userId
7237
            );
7238
            return false;
7239
        }
7240
7241
        return true; // Encryption in progress
7242
    }
7243
7244
    // Lock still valid and owned by another user
7245
    return true;
7246
}
7247
7248
/**
7249
 * Creates an edition lock for a given item and user
7250
 * 
7251
 * @param int $itemId The ID of the item to lock
7252
 * @param int $userId The ID of the user who is locking the item
7253
 * @param int $timestamp The timestamp of the lock
7254
 * 
7255
 * @return void
7256
 */
7257
function createEditionLock(int $itemId, int $userId, int $timestamp): void
7258
{
7259
    DB::insert(
7260
        prefixTable('items_edition'),
7261
        [
7262
            'timestamp' => $timestamp,
7263
            'item_id' => $itemId,
7264
            'user_id' => $userId,
7265
        ]
7266
    );
7267
}
7268
7269
7270
/**
7271
 * Checks if there is an ongoing background encryption process for the given item.
7272
 * 
7273
 * @param int $itemId The ID of the item to check
7274
 * 
7275
 * @return bool True if an ongoing process is found, false otherwise
7276
 */
7277
function isProcessOnGoing(int $itemId): bool
7278
{
7279
    // Check if there's an ongoing background encryption process for the item
7280
    $ongoingProcess = DB::queryFirstRow(
7281
        'SELECT 1 FROM ' . prefixTable('background_tasks') . ' WHERE item_id = %i AND finished_at = "" LIMIT 1', 
7282
        $itemId
7283
    );
7284
7285
    // Return true if an ongoing process is found, otherwise false
7286
    return $ongoingProcess ? true : false;
7287
}
7288
7289
/**
7290
 * Retrieves the list of visible folders for a specific user from the cache_tree table.
7291
 * 
7292
 * @param int $userId The ID of the user
7293
 * 
7294
 * @return array An array of visible folders for the user
7295
 */
7296
function getUserVisibleFolders(int $userId): array
7297
{
7298
    // Query to retrieve visible folders for the user
7299
    $data = DB::queryFirstRow('SELECT visible_folders FROM ' . prefixTable('cache_tree') . ' WHERE user_id = %i', $userId);
7300
    
7301
    // Decode JSON data into an array; return an empty array if the data is invalid
7302
    return json_decode($data['visible_folders'], true) ?? [];
7303
}
7304
7305
/**
7306
 * Determines access rights (edit/delete) based on the user's roles for a given folder.
7307
 * It checks the roles_values table to see the permissions defined for each role.
7308
 * 
7309
 * @param object $session The current session object
7310
 * @param int $treeId The ID of the folder to check access rights for
7311
 * 
7312
 * @return array An array containing edit and delete access rights [edit, delete]
7313
 */
7314
function getRoleBasedAccess($session, int $treeId): array
7315
{
7316
    $edit = $delete = false;
7317
    
7318
    // Retrieve all role IDs assigned to the user
7319
    $roles = array_column($session->get('system-array_roles'), 'id');
7320
7321
    // Query the access rights for the given roles and folder
7322
    $accessTypes = DB::queryFirstColumn(
7323
        'SELECT type FROM ' . prefixTable('roles_values') . ' WHERE role_id IN %ls AND folder_id = %i', 
7324
        $roles, 
7325
        $treeId
7326
    );
7327
7328
    // Determine access rights based on the retrieved types
7329
    foreach ($accessTypes as $access) {
7330
        switch ($access) {
7331
            case 'ND': // No Delete
7332
                $edit = true;
7333
                $delete = false;
7334
                break;
7335
            case 'NE': // No Edit
7336
                $edit = false;
7337
                $delete = true;
7338
                break;
7339
            case 'NDNE':
7340
            case 'R': // Read only
7341
                $edit = $delete = false;
7342
                break;
7343
            case 'W': // Write access
7344
                $edit = $delete = true;
7345
                break;
7346
        }
7347
    }
7348
    return [$edit, $delete];
7349
}
7350
7351
/**
7352
 * Constructs the final access response array with the given parameters.
7353
 * 
7354
 * @param bool $error Indicates if there was an error
7355
 * @param bool $access Indicates if the user has access
7356
 * @param bool $edit Indicates if the user has edit rights
7357
 * @param bool $delete Indicates if the user has delete rights
7358
 * @param bool $editionLocked Indicates if the edition is locked
7359
 * 
7360
 * @return array An array containing the access rights information
7361
 */
7362
function getAccessResponse(bool $error, bool $access, bool $edit, bool $delete, bool $editionLocked = false): array
7363
{
7364
    return [
7365
        'error' => $error,
7366
        'access' => $access,
7367
        'edit' => $edit,
7368
        'delete' => $delete,
7369
        'edition_locked' => $editionLocked,
7370
    ];
7371
}
7372