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 | } |