Subversion Repositories oidplus

Rev

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