Subversion Repositories oidplus

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
310 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
5
 * Copyright 2019 Daniel Marschall, ViaThinkSoft
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
 
20
class OIDplusPagePublicAttachments extends OIDplusPagePluginPublic {
21
 
22
        public static function getUploadDir($id) {
23
                $path = realpath(__DIR__.'/../../../').'/userdata/attachments/';
24
 
25
                $obj = OIDplusObject::parse($id);
26
                if ($obj === null) throw new OIDplusException("Invalid object '$id'");
27
                if ($obj::ns() == 'oid') {
28
                        $oid = $obj->nodeId(false);
29
                } else {
30
                        $oid = null;
31
                        $alt_ids = $obj->getAltIds();
32
                        foreach ($alt_ids as $alt_id) {
33
                                if ($alt_id->getNamespace() == 'oid') {
34
                                        $oid = $alt_id->getId();
35
                                        break; // we prefer the first OID (for GUIDs, the first OID is the OIDplus-OID, and the second OID is the UUID OID)
36
                                }
37
                        }
38
                }
39
 
40
                if (!is_null($oid)) {
41
                        // For OIDs, it is the OID, for other identifiers
42
                        // it it the OID alt ID (generated using the SystemID)
43
                        $path .= str_replace('.', '_', $oid);
44
 
45
                } else {
46
                        // Can happen if you don't have a system ID (due to missing OpenSSL plugin)
47
                        $path .= md5($obj->nodeId(true)); // we don't use $id, because $obj->nodeId(true) is possibly more canonical than $id
48
                }
49
 
50
                return $path;
51
        }
52
 
53
        private function raMayDelete() {
54
                return OIDplus::config()->getValue('attachments_allow_ra_delete', 0);
55
        }
56
 
57
        private function raMayUpload() {
58
                return OIDplus::config()->getValue('attachments_allow_ra_upload', 0);
59
        }
60
 
61
        public function action(&$handled) {
62
 
63
                if ($_REQUEST['action'] == 'deleteAttachment') {
64
                        $handled = true;
65
 
66
                        $id = $_POST['id'];
67
                        $obj = OIDplusObject::parse($id);
68
                        if ($obj === null) throw new OIDplusException("Invalid object '$id'");
69
                        if (!$obj->userHasWriteRights()) throw new OIDplusException("Authentication error. Please log in as the RA of '$id' to upload an attachment.");
70
 
71
                        if (!OIDplus::authUtils()::isAdminLoggedIn() && !$this->raMayDelete()) {
72
                                throw new OIDplusException("The administrator has disabled deleting attachments by RAs.");
73
                        }
74
 
75
                        $req_filename = $_REQUEST['filename'];
76
                        if (strpos($req_filename, '/') !== false) throw new OIDplusException("Illegal file name");
77
                        if (strpos($req_filename, '\\') !== false) throw new OIDplusException("Illegal file name");
78
                        if (strpos($req_filename, '..') !== false) throw new OIDplusException("Illegal file name");
79
                        if (strpos($req_filename, chr(0)) !== false) throw new OIDplusException("Illegal file name");
80
 
81
                        $uploaddir = self::getUploadDir($id);
82
                        $uploadfile = $uploaddir . '/' . basename($req_filename);
83
 
84
                        if (!file_exists($uploadfile)) throw new OIDplusException("File does not exist");
85
                        @unlink($uploadfile);
86
                        if (file_exists($uploadfile)) {
87
                                $msg = "Attachment file '".basename($uploadfile)."' could not be deleted from object '$oid' (problem with permissions?)";
88
                                OIDplus::logger()->log("[ERR]OID($id)+[ERR]A!", $msg);
89
                                if (OIDplus::authUtils()::isAdminLoggedIn()) {
90
                                        throw new OIDplusException($msg);
91
                                } else {
92
                                        throw new OIDplusException("$msg. Please contact the system administrator.");
93
                                }
94
                        }
95
 
96
                        OIDplus::logger()->log("[OK]OID($id)+[?INFO/!OK]OIDRA($id)?/[?INFO/!OK]A?", "Deleted attachment '".basename($uploadfile)."' from object '$id'");
97
 
98
                        echo json_encode(array("status" => 0));
99
                }
100
 
101
                if ($_REQUEST['action'] == 'uploadAttachment') {
102
                        $handled = true;
103
 
104
                        $id = $_POST['id'];
105
                        $obj = OIDplusObject::parse($id);
106
                        if ($obj === null) throw new OIDplusException("Invalid object '$id'");
107
                        if (!$obj->userHasWriteRights()) throw new OIDplusException("Authentication error. Please log in as the RA of '$id' to upload an attachment.");
108
 
109
                        if (!OIDplus::authUtils()::isAdminLoggedIn() && !$this->raMayUpload()) {
110
                                throw new OIDplusException("The administrator has disabled uploading attachments by RAs.");
111
                        }
112
 
113
                        if (!OIDplus::authUtils()::isAdminLoggedIn()) {
114
                                $banned = explode(',', OIDplus::config()->getValue('attachments_block_extensions', ''));
115
                                foreach ($banned as $ext) {
116
                                        $ext = trim($ext);
117
                                        if ($ext == '') continue;
118
                                        if (strtolower(substr(basename($_FILES['userfile']['name']), -strlen($ext)-1)) == strtolower('.'.$ext)) {
119
                                                throw new OIDplusException("The file extension '.$ext' is banned by the administrator (it can be uploaded by the administrator though)");
120
                                        }
121
                                }
122
                        }
123
 
124
                        $req_filename = $_FILES['userfile']['name'];
125
                        if (strpos($req_filename, '/') !== false) throw new OIDplusException("Illegal file name");
126
                        if (strpos($req_filename, '\\') !== false) throw new OIDplusException("Illegal file name");
127
                        if (strpos($req_filename, '..') !== false) throw new OIDplusException("Illegal file name");
128
                        if (strpos($req_filename, chr(0)) !== false) throw new OIDplusException("Illegal file name");
129
 
130
                        $uploaddir = self::getUploadDir($id);
131
                        $uploadfile = $uploaddir . '/' . basename($req_filename);
132
 
133
                        if (!is_dir($uploaddir)) {
134
                                @mkdir($uploaddir, 0777, true);
135
                                if (!is_dir($uploaddir)) {
136
                                        $msg = "Upload attachment '".basename($uploadfile)."' to object '$id' failed: Cannot create directory '".basename($uploaddir)."' (problem with permissions?)";
137
                                        OIDplus::logger()->log("[ERR]OID($id)+[ERR]A!", $msg);
138
                                        if (OIDplus::authUtils()::isAdminLoggedIn()) {
139
                                                throw new OIDplusException($msg);
140
                                        } else {
141
                                                throw new OIDplusException("$msg. Please contact the system administrator.");
142
                                        }
143
                                }
144
                        }
145
 
146
                        if (!@move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
147
                                $msg = "Upload attachment '".basename($uploadfile)."' to object '$id' failed: Cannot move uploaded file into directory (problem with permissions?)";
148
                                OIDplus::logger()->log("[ERR]OID($id)+[ERR]A!", $msg);
149
                                if (OIDplus::authUtils()::isAdminLoggedIn()) {
150
                                        throw new OIDplusException($msg);
151
                                } else {
152
                                        throw new OIDplusException("$msg. Please contact the system administrator.");
153
                                }
154
                        }
155
 
156
                        OIDplus::logger()->log("[OK]OID($id)+[?INFO/!OK]OIDRA($id)?/[?INFO/!OK]A?", "Uploaded attachment '".basename($uploadfile)."' to object '$id'");
157
 
158
                        echo json_encode(array("status" => 0));
159
                }
160
        }
161
 
162
        public function init($html=true) {
163
                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) {
164
                });
165
                OIDplus::config()->prepareConfigKey('attachments_allow_ra_delete', 'Allow that RAs delete file attachments? (0=no, 1=yes)', '0', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
166
                        if (!is_numeric($value) || ($value < 0) || ($value > 1)) {
167
                                throw new OIDplusException("Please enter a valid value (0 or 1).");
168
                        }
169
                });
170
                OIDplus::config()->prepareConfigKey('attachments_allow_ra_upload', 'Allow that RAs upload file attachments? (0=no, 1=yes)', '0', OIDplusConfig::PROTECTION_EDITABLE, function($value) {
171
                        if (!is_numeric($value) || ($value < 0) || ($value > 1)) {
172
                                throw new OIDplusException("Please enter a valid value (0 or 1).");
173
                        }
174
                });
175
        }
176
 
177
        public function gui($id, &$out, &$handled) {
178
                // Nothing
179
        }
180
 
181
        public function publicSitemap(&$out) {
182
                // Nothing
183
        }
184
 
185
        public function tree(&$json, $ra_email=null, $nonjs=false, $req_goto='') {
186
                return false;
187
        }
188
 
189
        private static function convert_filesize($bytes, $decimals = 2){
190
                $size = array('Bytes','KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB');
191
                $factor = floor((strlen($bytes) - 1) / 3);
192
                return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor];
193
        }
194
 
195
        public function implementsFeature($id) {
196
                if (strtolower($id) == '1.3.6.1.4.1.37476.2.5.2.3.2') return true; // modifyContent
197
                return false;
198
        }
199
 
200
        public function modifyContent($id, &$title, &$icon, &$text) {
201
                // Interface 1.3.6.1.4.1.37476.2.5.2.3.2
202
 
203
                $files = glob(self::getUploadDir($id).'/'.'*');
204
                $doshow = false;
205
                $output = '';
206
 
207
                $obj = OIDplusObject::parse($id);
208
                if ($obj === null) throw new OIDplusException("Invalid object '$id'");
209
                $can_upload = OIDplus::authUtils()::isAdminLoggedIn() || ($this->raMayUpload() && $obj->userHasWriteRights());
210
                $can_delete = OIDplus::authUtils()::isAdminLoggedIn() || ($this->raMayDelete() && $obj->userHasWriteRights());
211
 
212
                $output .= '<h2>File attachments</h2>';
213
                $output .= '<div class="container box">';
214
 
215
                if (OIDplus::authUtils()::isAdminLoggedIn()) {
216
                        $output .= '<p>Admin info: The directory is <b>'.htmlentities(self::getUploadDir($id)).'</b></p>';
217
                        $doshow = true;
218
                }
219
 
220
                $output .= '<div id="fileattachments_table" class="table-responsive">';
221
                $output .= '<table class="table table-bordered table-striped">';
222
                $output .= '<tr>';
223
                $output .= '<th>Filename</th>';
224
                $output .= '<th>Size</th>';
225
                $output .= '<th>File type</th>';
226
                $output .= '<th>Download</th>';
227
                if ($can_delete) $output .= '<th>Delete</th>';
228
                $output .= '</tr>';
229
                foreach ($files as $file) {
230
                        $output .= '<tr>';
231
                        $output .= '<td>'.htmlentities(basename($file)).'</td>';
232
                        $output .= '<td>'.htmlentities(self::convert_filesize(filesize($file), 0)).'</td>';
233
                        $lookup_files = array(__DIR__.'/../../../userdata/attachments/filetypes.conf', __DIR__.'/../../../3p/vts_fileformats/filetypes.local', __DIR__.'/../../../3p/vts_fileformats/filetypes.conf');
234
                        $output .= '<td>'.htmlentities(VtsFileTypeDetect::getDescription($file, $lookup_files)).'</td>';
235
 
236
                        $output .= '     <td><button type="button" name="download_'.md5($file).'" id="download_'.md5($file).'" class="btn btn-success btn-xs download" onclick="downloadAttachment('.js_escape(OIDplus::webpath(__DIR__)).', current_node,'.js_escape(basename($file)).')">Download</button></td>';
237
                        if ($can_delete) {
238
                                $output .= '     <td><button type="button" name="delete_'.md5($file).'" id="delete_'.md5($file).'" class="btn btn-danger btn-xs delete" onclick="deleteAttachment(current_node,'.js_escape(basename($file)).')">Delete</button></td>';
239
                        }
240
 
241
                        $output .= '</tr>';
242
                        $doshow = true;
243
                }
244
 
245
                if (count($files) === 0) $output .= '<tr><td colspan="'.($can_delete ? 5 : 4).'"><i>No attachments</i></td></tr>';
246
 
247
                $output .= '</table></div>';
248
 
249
                if ($can_upload) {
250
                        $output .= '<form onsubmit="return uploadAttachmentOnSubmit(this);" enctype="multipart/form-data" id="uploadAttachmentForm">';
251
                        $output .= '<input type="hidden" name="id" value="'.htmlentities($id).'">';
252
                        $output .= '<input type="hidden" name="action" value="uploadAttachment">';
253
                        $output .= '<div>Add a file attachment:<input type="file" name="userfile" value="" id="fileAttachment">';
254
                        $output .= '<br><input type="submit" value="Upload"></div>';
255
                        $output .= '</form>';
256
                        $doshow = true;
257
                }
258
 
259
                $output .= '</div>';
260
 
261
                if ($doshow) $text .= $output;
262
        }
263
 
264
        public function tree_search($request) {
265
                return false;
266
        }
267
}