Passed
Branch remove-tp-config (101a80)
by Nils
11:52
created

getRoleBasedAccess()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 35
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

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