Subversion Repositories oidplus

Rev

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