Subversion Repositories oidplus

Rev

Rev 1086 | Rev 1130 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
635 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1086 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
635 daniel-mar 6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
1050 daniel-mar 20
namespace ViaThinkSoft\OIDplus;
635 daniel-mar 21
 
1086 daniel-mar 22
// phpcs:disable PSR1.Files.SideEffects
23
\defined('INSIDE_OIDPLUS') or die;
24
// phpcs:enable PSR1.Files.SideEffects
25
 
635 daniel-mar 26
class OIDplusPagePublicAttachments extends OIDplusPagePluginPublic {
27
 
28
        const DIR_UNLOCK_FILE = 'oidplus_upload.dir';
29
 
1116 daniel-mar 30
        /**
31
         * @param $dir
32
         * @return void
33
         * @throws OIDplusException
34
         */
635 daniel-mar 35
        private static function checkUploadDir($dir) {
36
                if (!is_dir($dir)) {
37
                        throw new OIDplusException(_L('The attachment directory "%1" is not existing.', $dir));
38
                }
39
 
40
                $realdir = realpath($dir);
41
                if ($realdir === false) {
42
                        throw new OIDplusException(_L('The attachment directory "%1" cannot be resolved (realpath).', $dir));
43
                }
44
 
45
                $unlock_file = $realdir . DIRECTORY_SEPARATOR . self::DIR_UNLOCK_FILE;
46
                if (!file_exists($unlock_file)) {
47
                        throw new OIDplusException(_L('Unlock file "%1" is not existing in attachment directory "%2".', self::DIR_UNLOCK_FILE, $dir));
48
                }
49
 
50
                if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
51
                        // Linux check 1: Check for critical directories
52
                        if (self::isCriticalLinuxDirectory($realdir)) {
53
                                throw new OIDplusException(_L('The attachment directory must not be inside a critical system directory!'));
54
                        }
55
 
56
                        // Linux check 2: Check file owner
57
                        $file_owner_a = fileowner(OIDplus::localpath().'index.php');
58
                        if ($file_owner_a === false) {
59
                                $file_owner_a = -1;
60
                                $file_owner_a_name = '???';
61
                        } else {
720 daniel-mar 62
                                $tmp = function_exists('posix_getpwuid') ? posix_getpwuid($file_owner_a) : false;
635 daniel-mar 63
                                $file_owner_a_name = $tmp !== false ? $tmp['name'] : 'UID '.$file_owner_a;
64
                        }
65
 
66
                        $file_owner_b = fileowner($unlock_file);
67
                        if ($file_owner_b === false) {
68
                                $file_owner_b = -1;
69
                                $file_owner_b_name = '???';
70
                        } else {
720 daniel-mar 71
                                $tmp = function_exists('posix_getpwuid') ? posix_getpwuid($file_owner_b) : false;
635 daniel-mar 72
                                $file_owner_b_name = $tmp !== false ? $tmp['name'] : 'UID '.$file_owner_b;
73
                        }
74
 
75
                        if ($file_owner_a != $file_owner_b) {
76
                                throw new OIDplusException(_L('Owner of unlock file "%1" is wrong. It is "%2", but it should be "%3".', $unlock_file, $file_owner_b_name, $file_owner_a_name));
77
                        }
78
                } else {
79
                        // Windows check 1: Check for critical directories
80
                        if (self::isCriticalWindowsDirectory($realdir)) {
81
                                throw new OIDplusException(_L('The attachment directory must not be inside a critical system directory!'));
82
                        }
83
 
84
                        // Note: We will not query the file owner in Windows systems.
85
                        // It would be possible, however, on Windows systems, the file
86
                        // ownership is rather hidden to the user and the user needs
87
                        // to go into several menus and windows in order to see/change
88
                        // the owner. We don't want to over-complicate it to the Windows admin.
89
                }
90
        }
91
 
1116 daniel-mar 92
        /**
93
         * @param $dir
94
         * @return bool
95
         */
96
        private static function isCriticalWindowsDirectory($dir): bool {
635 daniel-mar 97
                $dir .= '\\';
98
                $windir = isset($_SERVER['SystemRoot']) ? $_SERVER['SystemRoot'].'\\' : 'C:\\Windows\\';
99
                if (stripos($dir,$windir) === 0) return true;
100
                return false;
101
        }
102
 
1116 daniel-mar 103
        /**
104
         * @param $dir
105
         * @return bool
106
         */
107
        private static function isCriticalLinuxDirectory($dir): bool {
635 daniel-mar 108
                if ($dir == '/') return true;
109
                $dir .= '/';
110
                if (strpos($dir,'/bin/') === 0) return true;
111
                if (strpos($dir,'/boot/') === 0) return true;
112
                if (strpos($dir,'/dev/') === 0) return true;
113
                if (strpos($dir,'/etc/') === 0) return true;
114
                if (strpos($dir,'/lib') === 0) return true;
115
                if (strpos($dir,'/opt/') === 0) return true;
116
                if (strpos($dir,'/proc/') === 0) return true;
117
                if (strpos($dir,'/root/') === 0) return true;
118
                if (strpos($dir,'/run/') === 0) return true;
119
                if (strpos($dir,'/sbin/') === 0) return true;
120
                if (strpos($dir,'/sys/') === 0) return true;
121
                if (strpos($dir,'/tmp/') === 0) return true;
122
                if (strpos($dir,'/usr/bin/') === 0) return true;
123
                if (strpos($dir,'/usr/include/') === 0) return true;
124
                if (strpos($dir,'/usr/lib') === 0) return true;
125
                if (strpos($dir,'/usr/sbin/') === 0) return true;
126
                if (strpos($dir,'/usr/src/') === 0) return true;
127
                if (strpos($dir,'/var/cache/') === 0) return true;
128
                if (strpos($dir,'/var/lib') === 0) return true;
129
                if (strpos($dir,'/var/lock/') === 0) return true;
130
                if (strpos($dir,'/var/log/') === 0) return true;
131
                if (strpos($dir,'/var/mail/') === 0) return true;
132
                if (strpos($dir,'/var/opt/') === 0) return true;
133
                if (strpos($dir,'/var/run/') === 0) return true;
134
                if (strpos($dir,'/var/spool/') === 0) return true;
135
                if (strpos($dir,'/var/tmp/') === 0) return true;
136
                return false;
137
        }
138
 
1116 daniel-mar 139
        /**
140
         * @param $id
141
         * @return string
142
         * @throws OIDplusException
143
         */
144
        public static function getUploadDir($id=null): string {
635 daniel-mar 145
                // Get base path
146
                $cfg = OIDplus::config()->getValue('attachment_upload_dir', '');
147
                $cfg = trim($cfg);
148
                if ($cfg === '') {
149
                        $basepath = OIDplus::localpath() . 'userdata' . DIRECTORY_SEPARATOR . 'attachments';
150
                } else {
151
                        $basepath = $cfg;
152
                }
153
 
154
                try {
155
                        self::checkUploadDir($basepath);
1050 daniel-mar 156
                } catch (\Exception $e) {
635 daniel-mar 157
                        $error = _L('This functionality is not available due to a misconfiguration');
158
                        if (OIDplus::authUtils()->isAdminLoggedIn()) {
159
                                $error .= ': '.$e->getMessage();
160
                        } else {
161
                                $error .= '. '._L('Please notify the system administrator. After they log-in, they can see the reason at this place.');
162
                        }
163
                        throw new OIDplusException($error);
164
                }
165
 
166
                // Get object-specific path
167
                if (!is_null($id)) {
168
                        $obj = OIDplusObject::parse($id);
1116 daniel-mar 169
                        if (!$obj) throw new OIDplusException(_L('Invalid object "%1"',$id));
635 daniel-mar 170
 
171
                        $path_v1 = $basepath . DIRECTORY_SEPARATOR . $obj->getLegacyDirectoryName();
172
                        $path_v1_bug = $basepath . $obj->getLegacyDirectoryName();
173
                        $path_v2 = $basepath . DIRECTORY_SEPARATOR . $obj->getDirectoryName();
174
 
175
                        if (is_dir($path_v1)) return $path_v1; // backwards compatibility
176
                        if (is_dir($path_v1_bug)) return $path_v1_bug; // backwards compatibility
177
                        return $path_v2;
178
                } else {
179
                        return $basepath;
180
                }
181
        }
182
 
1116 daniel-mar 183
        /**
184
         * @return mixed|null
185
         * @throws OIDplusException
186
         */
635 daniel-mar 187
        private function raMayDelete() {
188
                return OIDplus::config()->getValue('attachments_allow_ra_delete', 0);
189
        }
190
 
1116 daniel-mar 191
        /**
192
         * @return mixed|null
193
         * @throws OIDplusException
194
         */
635 daniel-mar 195
        private function raMayUpload() {
196
                return OIDplus::config()->getValue('attachments_allow_ra_upload', 0);
197
        }
198
 
1116 daniel-mar 199
        /**
200
         * @param string $actionID
201
         * @param array $params
202
         * @return int[]
203
         * @throws OIDplusException
204
         */
205
        public function action(string $actionID, array $params): array {
635 daniel-mar 206
 
207
                if ($actionID == 'deleteAttachment') {
208
                        _CheckParamExists($params, 'id');
209
                        $id = $params['id'];
210
                        $obj = OIDplusObject::parse($id);
1116 daniel-mar 211
                        if (!$obj) throw new OIDplusException(_L('Invalid object "%1"',$id));
635 daniel-mar 212
                        if (!$obj->userHasWriteRights()) throw new OIDplusException(_L('Authentication error. Please log in as admin, or as the RA of "%1" to upload an attachment.',$id));
213
 
214
                        if (!OIDplus::authUtils()->isAdminLoggedIn() && !$this->raMayDelete()) {
215
                                throw new OIDplusException(_L('The administrator has disabled deleting attachments by RAs.'));
216
                        }
217
 
218
                        _CheckParamExists($params, 'filename');
219
                        $req_filename = $params['filename'];
220
                        if (strpos($req_filename, '/') !== false) throw new OIDplusException(_L('Illegal file name'));
221
                        if (strpos($req_filename, '\\') !== false) throw new OIDplusException(_L('Illegal file name'));
222
                        if (strpos($req_filename, '..') !== false) throw new OIDplusException(_L('Illegal file name'));
223
                        if (strpos($req_filename, chr(0)) !== false) throw new OIDplusException(_L('Illegal file name'));
224
 
225
                        $uploaddir = self::getUploadDir($id);
226
                        $uploadfile = $uploaddir . DIRECTORY_SEPARATOR . basename($req_filename);
227
 
228
                        if (!file_exists($uploadfile)) throw new OIDplusException(_L('File does not exist'));
229
                        @unlink($uploadfile);
230
                        if (file_exists($uploadfile)) {
231
                                OIDplus::logger()->log("[ERR]OID($id)+[ERR]A!", "Attachment file '".basename($uploadfile)."' could not be deleted from object '$id' (problem with permissions?)");
232
                                $msg = _L('Attachment file "%1" could not be deleted from object "%2" (problem with permissions?)',basename($uploadfile),$id);
233
                                if (OIDplus::authUtils()->isAdminLoggedIn()) {
234
                                        throw new OIDplusException($msg);
235
                                } else {
236
                                        throw new OIDplusException($msg.'. '._L('Please contact the system administrator.'));
237
                                }
238
                        } else {
239
                                // If it was the last file, delete the empty directory
719 daniel-mar 240
                                $ary = @glob($uploaddir . DIRECTORY_SEPARATOR . '*');
241
                                if (is_array($ary) && (count($ary) == 0)) @rmdir($uploaddir);
635 daniel-mar 242
                        }
243
 
244
                        OIDplus::logger()->log("[OK]OID($id)+[?INFO/!OK]OIDRA($id)?/[?INFO/!OK]A?", "Deleted attachment '".basename($uploadfile)."' from object '$id'");
245
 
246
                        return array("status" => 0);
247
 
248
                } else if ($actionID == 'uploadAttachment') {
249
                        _CheckParamExists($params, 'id');
250
                        $id = $params['id'];
251
                        $obj = OIDplusObject::parse($id);
1116 daniel-mar 252
                        if (!$obj) throw new OIDplusException(_L('Invalid object "%1"',$id));
635 daniel-mar 253
                        if (!$obj->userHasWriteRights()) throw new OIDplusException(_L('Authentication error. Please log in as admin, or as the RA of "%1" to upload an attachment.',$id));
254
 
255
                        if (!OIDplus::authUtils()->isAdminLoggedIn() && !$this->raMayUpload()) {
256
                                throw new OIDplusException(_L('The administrator has disabled uploading attachments by RAs.'));
257
                        }
258
 
259
                        if (!isset($_FILES['userfile'])) {
260
                                throw new OIDplusException(_L('Please choose a file.'));
261
                        }
262
 
263
                        if (!OIDplus::authUtils()->isAdminLoggedIn()) {
836 daniel-mar 264
                                $fname = basename($_FILES['userfile']['name']);
265
 
834 daniel-mar 266
                                // 1. If something is on the blacklist, we always block it, even if it is on the whitelist, too
635 daniel-mar 267
                                $banned = explode(',', OIDplus::config()->getValue('attachments_block_extensions', ''));
268
                                foreach ($banned as $ext) {
269
                                        $ext = trim($ext);
270
                                        if ($ext == '') continue;
836 daniel-mar 271
                                        if (strtolower(substr($fname, -strlen($ext)-1)) == strtolower('.'.$ext)) {
635 daniel-mar 272
                                                throw new OIDplusException(_L('The file extension "%1" is banned by the administrator (it can be uploaded by the administrator though)',$ext));
273
                                        }
274
                                }
834 daniel-mar 275
 
276
                                // 2. Something on the whitelist is always OK
277
                                $allowed = explode(',', OIDplus::config()->getValue('attachments_allow_extensions', ''));
278
                                $is_whitelisted = false;
279
                                foreach ($allowed as $ext) {
280
                                        $ext = trim($ext);
281
                                        if ($ext == '') continue;
836 daniel-mar 282
                                        if (strtolower(substr($fname, -strlen($ext)-1)) == strtolower('.'.$ext)) {
834 daniel-mar 283
                                                $is_whitelisted = true;
284
                                                break;
285
                                        }
286
                                }
287
 
288
                                // 3. For everything that is neither whitelisted, nor blacklisted, the admin can decide if these grey zone is allowed or blocked
289
                                if (!$is_whitelisted) {
290
                                        if (!OIDplus::config()->getValue('attachments_allow_grey_extensions', '1')) {
836 daniel-mar 291
                                                $tmp = explode('.', $fname);
292
                                                $ext = array_pop($tmp);
834 daniel-mar 293
                                                throw new OIDplusException(_L('The file extension "%1" is not on the whitelist (it can be uploaded by the administrator though)',$ext));
294
                                        }
295
                                }
635 daniel-mar 296
                        }
297
 
298
                        $req_filename = $_FILES['userfile']['name'];
299
                        if (strpos($req_filename, '/') !== false) throw new OIDplusException(_L('Illegal file name'));
300
                        if (strpos($req_filename, '\\') !== false) throw new OIDplusException(_L('Illegal file name'));
301
                        if (strpos($req_filename, '..') !== false) throw new OIDplusException(_L('Illegal file name'));
302
                        if (strpos($req_filename, chr(0)) !== false) throw new OIDplusException(_L('Illegal file name'));
303
 
304
                        $uploaddir = self::getUploadDir($id);
305
                        $uploadfile = $uploaddir . DIRECTORY_SEPARATOR . basename($req_filename);
306
 
307
                        if (!is_dir($uploaddir)) {
308
                                @mkdir($uploaddir, 0777, true);
309
                                if (!is_dir($uploaddir)) {
310
                                        OIDplus::logger()->log("[ERR]OID($id)+[ERR]A!", "Upload attachment '".basename($uploadfile)."' to object '$id' failed: Cannot create directory '".basename($uploaddir)."' (problem with permissions?)");
311
                                        $msg = _L('Upload attachment "%1" to object "%2" failed',basename($uploadfile),$id).': '._L('Cannot create directory "%1" (problem with permissions?)',basename($uploaddir));
312
                                        if (OIDplus::authUtils()->isAdminLoggedIn()) {
313
                                                throw new OIDplusException($msg);
314
                                        } else {
315
                                                throw new OIDplusException($msg.'. '._L('Please contact the system administrator.'));
316
                                        }
317
                                }
318
                        }
319
 
320
                        if (!@move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
321
                                OIDplus::logger()->log("[ERR]OID($id)+[ERR]A!", "Upload attachment '".basename($uploadfile)."' to object '$id' failed: Cannot move uploaded file into directory (problem with permissions?)");
322
                                $msg = _L('Upload attachment "%1" to object "%2" failed',basename($uploadfile),$id).': '._L('Cannot move uploaded file into directory (problem with permissions?)');
323
                                if (OIDplus::authUtils()->isAdminLoggedIn()) {
324
                                        throw new OIDplusException($msg);
325
                                } else {
326
                                        throw new OIDplusException($msg.'. '._L('Please contact the system administrator.'));
327
                                }
328
                        }
329
 
330
                        OIDplus::logger()->log("[OK]OID($id)+[?INFO/!OK]OIDRA($id)?/[?INFO/!OK]A?", "Uploaded attachment '".basename($uploadfile)."' to object '$id'");
331
 
332
                        return array("status" => 0);
333
                } else {
1116 daniel-mar 334
                        return parent::action($actionID, $params);
635 daniel-mar 335
                }
336
        }
337
 
1116 daniel-mar 338
        /**
339
         * @param bool $html
340
         * @return void
341
         * @throws OIDplusException
342
         */
343
        public function init(bool $html=true) {
635 daniel-mar 344
                OIDplus::config()->prepareConfigKey('attachments_block_extensions', 'Block file name extensions being used in file attachments (comma separated)', 'exe,scr,pif,bat,com,vbs,cmd', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
834 daniel-mar 345
                        // TODO: check if a blacklist entry is also on the whitelist (which is not allowed)
635 daniel-mar 346
                });
834 daniel-mar 347
                OIDplus::config()->prepareConfigKey('attachments_allow_extensions', 'Allow (whitelist) file name extensions being used in file attachments (comma separated)', '', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
348
                        // TODO: check if a whitelist entry is also on the blacklist (which is not allowed)
349
                });
350
                OIDplus::config()->prepareConfigKey('attachments_allow_grey_extensions', 'Should file-extensions which are neither be on the whitelist, nor be at the blacklist, be allowed? (1=Yes, 0=No)', '1', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
351
                        if (!is_numeric($value) || ($value < 0) || ($value > 1)) {
352
                                throw new OIDplusException(_L('Please enter a valid value (0=no, 1=yes).'));
353
                        }
354
                });
635 daniel-mar 355
                OIDplus::config()->prepareConfigKey('attachments_allow_ra_delete', 'Allow that RAs delete file attachments? (0=no, 1=yes)', '0', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
356
                        if (!is_numeric($value) || ($value < 0) || ($value > 1)) {
357
                                throw new OIDplusException(_L('Please enter a valid value (0=no, 1=yes).'));
358
                        }
359
                });
360
                OIDplus::config()->prepareConfigKey('attachments_allow_ra_upload', 'Allow that RAs upload file attachments? (0=no, 1=yes)', '0', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
361
                        if (!is_numeric($value) || ($value < 0) || ($value > 1)) {
362
                                throw new OIDplusException(_L('Please enter a valid value (0=no, 1=yes).'));
363
                        }
364
                });
365
 
366
                $info_txt  = 'Alternative directory for attachments. It must contain a file named "';
367
                $info_txt .= self::DIR_UNLOCK_FILE;
368
                $info_txt .= '"';
369
                if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
370
                        $info_txt .= ' with the same owner as index.php';
371
                }
372
                $info_txt .= '. If this setting is empty, then the userdata directory is used.';
373
                OIDplus::config()->prepareConfigKey('attachment_upload_dir', $info_txt, '', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
374
                        if (trim($value) !== '') {
375
                                self::checkUploadDir($value);
376
                        }
377
                });
378
        }
379
 
1116 daniel-mar 380
        /**
381
         * @param string $id
382
         * @param array $out
383
         * @param bool $handled
384
         * @return void
385
         */
386
        public function gui(string $id, array &$out, bool &$handled) {
635 daniel-mar 387
                // Nothing
388
        }
389
 
1116 daniel-mar 390
        /**
391
         * @param array $out
392
         * @return void
393
         */
394
        public function publicSitemap(array &$out) {
635 daniel-mar 395
                // Nothing
396
        }
397
 
1116 daniel-mar 398
        /**
399
         * @param array $json
400
         * @param string|null $ra_email
401
         * @param bool $nonjs
402
         * @param string $req_goto
403
         * @return bool
404
         */
405
        public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool {
635 daniel-mar 406
                return false;
407
        }
408
 
1116 daniel-mar 409
        /**
410
         * Convert amount of bytes to human-friendly name
411
         * @param int $bytes
412
         * @param int $decimals
413
         * @return string
414
         */
415
        private static function convert_filesize(int $bytes, int $decimals = 2): string {
635 daniel-mar 416
                $size = array(_L('Bytes'),_L('KiB'),_L('MiB'),_L('GiB'),_L('TiB'),_L('PiB'),_L('EiB'),_L('ZiB'),_L('YiB'));
1116 daniel-mar 417
                $factor = floor((strlen("$bytes") - 1) / 3);
635 daniel-mar 418
                return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor];
419
        }
420
 
1116 daniel-mar 421
        /**
422
         * @param string $id
423
         * @return bool
424
         */
425
        public function implementsFeature(string $id): bool {
635 daniel-mar 426
                if (strtolower($id) == '1.3.6.1.4.1.37476.2.5.2.3.2') return true; // modifyContent
427
                if (strtolower($id) == '1.3.6.1.4.1.37476.2.5.2.3.3') return true; // beforeObject*, afterObject*
428
                if (strtolower($id) == '1.3.6.1.4.1.37476.2.5.2.3.4') return true; // whois*Attributes
429
                return false;
430
        }
431
 
1116 daniel-mar 432
        /**
433
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.2
434
         * @param string $id
435
         * @param string $title
436
         * @param string $icon
437
         * @param string $text
438
         * @return void
439
         */
440
        public function modifyContent(string $id, string &$title, string &$icon, string &$text) {
635 daniel-mar 441
                $output = '';
442
                $doshow = false;
443
 
444
                try {
445
                        $upload_dir = self::getUploadDir($id);
719 daniel-mar 446
                        $files = @glob($upload_dir . DIRECTORY_SEPARATOR . '*');
635 daniel-mar 447
                        $found_files = false;
448
 
449
                        $obj = OIDplusObject::parse($id);
1116 daniel-mar 450
                        if (!$obj) throw new OIDplusException(_L('Invalid object "%1"',$id));
635 daniel-mar 451
                        $can_upload = OIDplus::authUtils()->isAdminLoggedIn() || ($this->raMayUpload() && $obj->userHasWriteRights());
452
                        $can_delete = OIDplus::authUtils()->isAdminLoggedIn() || ($this->raMayDelete() && $obj->userHasWriteRights());
453
 
454
                        if (OIDplus::authUtils()->isAdminLoggedIn()) {
455
                                $output .= '<p>'._L('Admin info: The directory is %1','<b>'.htmlentities($upload_dir).'</b>').'</p>';
456
                                $doshow = true;
457
                        }
458
 
459
                        $output .= '<div id="fileattachments_table" class="table-responsive">';
460
                        $output .= '<table class="table table-bordered table-striped">';
461
                        $output .= '<tr>';
462
                        $output .= '<th>'._L('Filename').'</th>';
463
                        $output .= '<th>'._L('Size').'</th>';
464
                        $output .= '<th>'._L('File type').'</th>';
465
                        $output .= '<th>'._L('Download').'</th>';
466
                        if ($can_delete) $output .= '<th>'._L('Delete').'</th>';
467
                        $output .= '</tr>';
719 daniel-mar 468
                        if ($files) foreach ($files as $file) {
635 daniel-mar 469
                                if (is_dir($file)) continue;
470
 
471
                                $output .= '<tr>';
472
                                $output .= '<td>'.htmlentities(basename($file)).'</td>';
473
                                $output .= '<td>'.htmlentities(self::convert_filesize(filesize($file), 0)).'</td>';
474
                                $lookup_files = array(
475
                                        OIDplus::localpath().'userdata/attachments/filetypes$'.OIDplus::getCurrentLang().'.conf',
476
                                        OIDplus::localpath().'userdata/attachments/filetypes.conf',
477
                                        OIDplus::localpath().'vendor/danielmarschall/fileformats/filetypes$'.OIDplus::getCurrentLang().'.local', // not recommended
478
                                        OIDplus::localpath().'vendor/danielmarschall/fileformats/filetypes.local', // not recommended
479
                                        OIDplus::localpath().'vendor/danielmarschall/fileformats/filetypes$'.OIDplus::getCurrentLang().'.conf',
480
                                        OIDplus::localpath().'vendor/danielmarschall/fileformats/filetypes.conf'
481
                                );
1050 daniel-mar 482
                                $output .= '<td>'.htmlentities(\VtsFileTypeDetect::getDescription($file, $lookup_files)).'</td>';
635 daniel-mar 483
 
801 daniel-mar 484
                                $output .= '     <td><button type="button" name="download_'.md5($file).'" id="download_'.md5($file).'" class="btn btn-success btn-xs download" onclick="OIDplusPagePublicAttachments.downloadAttachment('.js_escape(OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE)).', current_node,'.js_escape(basename($file)).')">'._L('Download').'</button></td>';
635 daniel-mar 485
                                if ($can_delete) {
486
                                        $output .= '     <td><button type="button" name="delete_'.md5($file).'" id="delete_'.md5($file).'" class="btn btn-danger btn-xs delete" onclick="OIDplusPagePublicAttachments.deleteAttachment(current_node,'.js_escape(basename($file)).')">'._L('Delete').'</button></td>';
487
                                }
488
 
489
                                $output .= '</tr>';
490
                                $doshow = true;
491
                                $found_files = true;
492
                        }
493
 
494
                        if (!$found_files) $output .= '<tr><td colspan="'.($can_delete ? 5 : 4).'"><i>'._L('No attachments').'</i></td></tr>';
495
 
496
                        $output .= '</table></div>';
497
 
498
                        if ($can_upload) {
499
                                $output .= '<form action="javascript:void(0);" onsubmit="return OIDplusPagePublicAttachments.uploadAttachmentOnSubmit(this);" enctype="multipart/form-data" id="uploadAttachmentForm">';
500
                                $output .= '<input type="hidden" name="id" value="'.htmlentities($id).'">';
501
                                $output .= '<div>'._L('Add a file attachment').':<input type="file" name="userfile" value="" id="fileAttachment">';
502
                                $output .= '<br><input type="submit" value="'._L('Upload').'"></div>';
503
                                $output .= '</form>';
504
                                $doshow = true;
505
                        }
1050 daniel-mar 506
                } catch (\Exception $e) {
635 daniel-mar 507
                        $doshow = true;
508
                        $output = '<p>'.$e->getMessage().'</p>';
509
                }
510
 
511
                $output = '<h2>'._L('File attachments').'</h2>' .
512
                          '<div class="container box">' .
513
                          $output .
514
                          '</div>';
515
                if ($doshow) $text .= $output;
516
        }
517
 
1116 daniel-mar 518
        /**
519
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.3
520
         * @param $id
521
         * @return void
522
         */
523
        public function beforeObjectDelete($id) {}
524
 
525
        /**
526
         * Delete the attachment folder including all files in it (note: Subfolders are not possible)
527
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.3
528
         * @param $id
529
         * @return void
530
         * @throws OIDplusException
531
         */
635 daniel-mar 532
        public function afterObjectDelete($id) {
533
                $uploaddir = self::getUploadDir($id);
534
                if ($uploaddir != '') {
719 daniel-mar 535
                        $ary = @glob($uploaddir . DIRECTORY_SEPARATOR . '*');
536
                        if ($ary) foreach ($ary as $a) @unlink($a);
635 daniel-mar 537
                        @rmdir($uploaddir);
538
                        if (is_dir($uploaddir)) {
539
                                OIDplus::logger()->log("[WARN]OID($id)+[WARN]A!", "Attachment directory '$uploaddir' could not be deleted during the deletion of the OID");
540
                        }
541
                }
542
        }
543
 
1116 daniel-mar 544
        /**
545
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.3
546
         * @param $id
547
         * @param $params
548
         * @return void
549
         */
550
        public function beforeObjectUpdateSuperior($id, &$params) {}
551
 
552
        /**
553
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.3
554
         * @param $id
555
         * @param $params
556
         * @return void
557
         */
558
        public function afterObjectUpdateSuperior($id, &$params) {}
559
 
560
        /**
561
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.3
562
         * @param $id
563
         * @param $params
564
         * @return void
565
         */
566
        public function beforeObjectUpdateSelf($id, &$params) {}
567
 
568
        /**
569
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.3
570
         * @param $id
571
         * @param $params
572
         * @return void
573
         */
574
        public function afterObjectUpdateSelf($id, &$params) {}
575
 
576
        /**
577
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.3
578
         * @param $id
579
         * @param $params
580
         * @return void
581
         */
582
        public function beforeObjectInsert($id, &$params) {}
583
 
584
        /**
585
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.3
586
         * @param $id
587
         * @param $params
588
         * @return void
589
         */
590
        public function afterObjectInsert($id, &$params) {}
591
 
592
        /**
593
         * @param string $request
594
         * @return array|false
595
         */
596
        public function tree_search(string $request) {
635 daniel-mar 597
                return false;
598
        }
599
 
1116 daniel-mar 600
        /**
601
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.4
602
         * @param $id
603
         * @param $out
604
         * @return void
605
         * @throws OIDplusException
606
         */
635 daniel-mar 607
        public function whoisObjectAttributes($id, &$out) {
899 daniel-mar 608
                $xmlns = 'oidplus-attachment-plugin';
609
                $xmlschema = 'urn:oid:1.3.6.1.4.1.37476.2.5.2.4.1.95.1';
610
                $xmlschemauri = OIDplus::webpath(__DIR__.'/attachments.xsd',OIDplus::PATH_ABSOLUTE);
611
 
719 daniel-mar 612
                $files = @glob(self::getUploadDir($id) . DIRECTORY_SEPARATOR . '*');
613
                if ($files) foreach ($files as $file) {
899 daniel-mar 614
                        $url = OIDplus::webpath(__DIR__,OIDplus::PATH_ABSOLUTE).'download.php?id='.urlencode($id).'&filename='.urlencode(basename($file));
615
 
616
                        $out[] = array(
617
                                'xmlns' => $xmlns,
618
                                'xmlschema' => $xmlschema,
619
                                'xmlschemauri' => $xmlschemauri,
620
                                'name' => 'attachment-name',
621
                                'value' => basename($file)
622
                        );
623
 
624
                        $out[] = array(
625
                                'xmlns' => $xmlns,
626
                                'xmlschema' => $xmlschema,
627
                                'xmlschemauri' => $xmlschemauri,
628
                                'name' => 'attachment-url',
629
                                'value' => $url
630
                        );
635 daniel-mar 631
                }
632
 
633
        }
1116 daniel-mar 634
 
635
        /**
636
         * Implements interface 1.3.6.1.4.1.37476.2.5.2.3.4
637
         * @param $email
638
         * @param $out
639
         * @return void
640
         */
641
        public function whoisRaAttributes($email, &$out) {}
635 daniel-mar 642
}