This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point'); |
||
3 | /********************************************************************************* |
||
4 | * SugarCRM Community Edition is a customer relationship management program developed by |
||
5 | * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc. |
||
6 | |||
7 | * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd. |
||
8 | * Copyright (C) 2011 - 2014 Salesagility Ltd. |
||
9 | * |
||
10 | * This program is free software; you can redistribute it and/or modify it under |
||
11 | * the terms of the GNU Affero General Public License version 3 as published by the |
||
12 | * Free Software Foundation with the addition of the following permission added |
||
13 | * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK |
||
14 | * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY |
||
15 | * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. |
||
16 | * |
||
17 | * This program is distributed in the hope that it will be useful, but WITHOUT |
||
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
||
19 | * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
||
20 | * details. |
||
21 | * |
||
22 | * You should have received a copy of the GNU Affero General Public License along with |
||
23 | * this program; if not, see http://www.gnu.org/licenses or write to the Free |
||
24 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
||
25 | * 02110-1301 USA. |
||
26 | * |
||
27 | * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road, |
||
28 | * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected]. |
||
29 | * |
||
30 | * The interactive user interfaces in modified source and object code versions |
||
31 | * of this program must display Appropriate Legal Notices, as required under |
||
32 | * Section 5 of the GNU Affero General Public License version 3. |
||
33 | * |
||
34 | * In accordance with Section 7(b) of the GNU Affero General Public License version 3, |
||
35 | * these Appropriate Legal Notices must retain the display of the "Powered by |
||
36 | * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not |
||
37 | * reasonably feasible for technical reasons, the Appropriate Legal Notices must |
||
38 | * display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM". |
||
39 | ********************************************************************************/ |
||
40 | |||
41 | /********************************************************************************* |
||
42 | |||
43 | * Description: |
||
44 | ********************************************************************************/ |
||
45 | require_once('include/externalAPI/ExternalAPIFactory.php'); |
||
46 | |||
47 | /** |
||
48 | * @api |
||
49 | * Manage uploaded files |
||
50 | */ |
||
51 | class UploadFile |
||
52 | { |
||
53 | var $field_name; |
||
54 | var $stored_file_name; |
||
55 | var $uploaded_file_name; |
||
56 | var $original_file_name; |
||
57 | var $temp_file_location; |
||
58 | var $use_soap = false; |
||
59 | var $file; |
||
60 | var $file_ext; |
||
61 | protected static $url = "upload/"; |
||
62 | |||
63 | /** |
||
64 | * Upload errors |
||
65 | * @var array |
||
66 | */ |
||
67 | protected static $filesError = array( |
||
68 | UPLOAD_ERR_OK => 'UPLOAD_ERR_OK - There is no error, the file uploaded with success.', |
||
69 | UPLOAD_ERR_INI_SIZE => 'UPLOAD_ERR_INI_SIZE - The uploaded file exceeds the upload_max_filesize directive in php.ini.', |
||
70 | UPLOAD_ERR_FORM_SIZE => 'UPLOAD_ERR_FORM_SIZE - The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', |
||
71 | UPLOAD_ERR_PARTIAL => 'UPLOAD_ERR_PARTIAL - The uploaded file was only partially uploaded.', |
||
72 | UPLOAD_ERR_NO_FILE => 'UPLOAD_ERR_NO_FILE - No file was uploaded.', |
||
73 | 5 => 'UNKNOWN ERROR', |
||
74 | UPLOAD_ERR_NO_TMP_DIR => 'UPLOAD_ERR_NO_TMP_DIR - Missing a temporary folder.', |
||
75 | UPLOAD_ERR_CANT_WRITE => 'UPLOAD_ERR_CANT_WRITE - Failed to write file to disk.', |
||
76 | UPLOAD_ERR_EXTENSION => 'UPLOAD_ERR_EXTENSION - A PHP extension stopped the file upload.', |
||
77 | ); |
||
78 | |||
79 | /** |
||
80 | * Create upload file handler |
||
81 | * @param string $field_name Form field name |
||
82 | */ |
||
83 | 4 | public function __construct($field_name = '') |
|
84 | { |
||
85 | // $field_name is the name of your passed file selector field in your form |
||
86 | // i.e., for Emails, it is "email_attachmentX" where X is 0-9 |
||
87 | 4 | $this->field_name = $field_name; |
|
88 | 4 | } |
|
89 | |||
90 | /** |
||
91 | * @deprecated deprecated since version 7.6, PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code, use __construct instead |
||
92 | */ |
||
93 | public function UploadFile($field_name = ''){ |
||
94 | $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code'; |
||
95 | if(isset($GLOBALS['log'])) { |
||
96 | $GLOBALS['log']->deprecated($deprecatedMessage); |
||
97 | } |
||
98 | else { |
||
99 | trigger_error($deprecatedMessage, E_USER_DEPRECATED); |
||
100 | } |
||
101 | self::__construct($field_name); |
||
102 | } |
||
103 | |||
104 | |||
105 | /** |
||
106 | * Setup for SOAP upload |
||
107 | * @param string $filename Name for the file |
||
108 | * @param string $file |
||
109 | */ |
||
110 | function set_for_soap($filename, $file) { |
||
111 | $this->stored_file_name = $filename; |
||
112 | $this->use_soap = true; |
||
113 | $this->file = $file; |
||
114 | } |
||
115 | |||
116 | /** |
||
117 | * Get URL for a document |
||
118 | * @deprecated |
||
119 | * @param string stored_file_name File name in filesystem |
||
120 | * @param string bean_id note bean ID |
||
121 | * @return string path with file name |
||
122 | */ |
||
123 | public static function get_url($stored_file_name, $bean_id) |
||
124 | { |
||
125 | if ( empty($bean_id) && empty($stored_file_name) ) { |
||
126 | return self::$url; |
||
127 | } |
||
128 | |||
129 | return self::$url . $bean_id; |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * Get URL of the uploaded file related to the document |
||
134 | * @param SugarBean $document |
||
135 | * @param string $type Type of the document, if different from $document |
||
136 | */ |
||
137 | public static function get_upload_url($document, $type = null) |
||
138 | { |
||
139 | if(empty($type)) { |
||
140 | $type = $document->module_dir; |
||
141 | } |
||
142 | return "index.php?entryPoint=download&type=$type&id={$document->id}"; |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * Try renaming a file to bean_id name |
||
147 | * @param string $filename |
||
148 | * @param string $bean_id |
||
149 | */ |
||
150 | protected static function tryRename($filename, $bean_id) |
||
151 | { |
||
152 | $fullname = "upload://$bean_id.$filename"; |
||
153 | if(file_exists($fullname)) { |
||
154 | if(!rename($fullname, "upload://$bean_id")) { |
||
155 | $GLOBALS['log']->fatal("unable to rename file: $fullname => $bean_id"); |
||
156 | } |
||
157 | return true; |
||
158 | } |
||
159 | return false; |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * builds a URL path for an anchor tag |
||
164 | * @param string stored_file_name File name in filesystem |
||
165 | * @param string bean_id note bean ID |
||
166 | * @return string path with file name |
||
167 | */ |
||
168 | static public function get_file_path($stored_file_name, $bean_id, $skip_rename = false) |
||
0 ignored issues
–
show
Coding Style
introduced
by
![]() |
|||
169 | { |
||
170 | global $locale; |
||
171 | |||
172 | // if the parameters are empty strings, just return back the upload_dir |
||
173 | if ( empty($bean_id) && empty($stored_file_name) ) { |
||
174 | return "upload://"; |
||
175 | } |
||
176 | |||
177 | if(!$skip_rename) { |
||
178 | self::tryRename(rawurlencode($stored_file_name), $bean_id) || |
||
179 | self::tryRename(urlencode($stored_file_name), $bean_id) || |
||
180 | self::tryRename($stored_file_name, $bean_id) || |
||
181 | self::tryRename($locale->translateCharset( $stored_file_name, 'UTF-8', $locale->getExportCharset()), $bean_id); |
||
182 | } |
||
183 | |||
184 | return "upload://$bean_id"; |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * duplicates an already uploaded file in the filesystem. |
||
189 | * @param string old_id ID of original note |
||
190 | * @param string new_id ID of new (copied) note |
||
191 | * @param string filename Filename of file (deprecated) |
||
192 | */ |
||
193 | public static function duplicate_file($old_id, $new_id, $file_name) |
||
194 | { |
||
195 | global $sugar_config; |
||
196 | |||
197 | // current file system (GUID) |
||
198 | $source = "upload://$old_id"; |
||
199 | |||
200 | if(!file_exists($source)) { |
||
201 | // old-style file system (GUID.filename.extension) |
||
202 | $oldStyleSource = $source.$file_name; |
||
203 | if(file_exists($oldStyleSource)) { |
||
204 | // change to new style |
||
205 | if(copy($oldStyleSource, $source)) { |
||
206 | // delete the old |
||
207 | if(!unlink($oldStyleSource)) { |
||
208 | $GLOBALS['log']->error("upload_file could not unlink [ {$oldStyleSource} ]"); |
||
209 | } |
||
210 | } else { |
||
211 | $GLOBALS['log']->error("upload_file could not copy [ {$oldStyleSource} ] to [ {$source} ]"); |
||
212 | } |
||
213 | } |
||
214 | } |
||
215 | |||
216 | $destination = "upload://$new_id"; |
||
217 | if(!copy($source, $destination)) { |
||
218 | $GLOBALS['log']->error("upload_file could not copy [ {$source} ] to [ {$destination} ]"); |
||
219 | } |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * Get upload error from system |
||
224 | * @return string upload error |
||
225 | */ |
||
226 | 1 | public function get_upload_error() |
|
227 | { |
||
228 | 1 | if(isset($this->field_name) && isset($_FILES[$this->field_name]['error'])) { |
|
229 | return $_FILES[$this->field_name]['error']; |
||
230 | } |
||
231 | 1 | return false; |
|
232 | } |
||
233 | |||
234 | /** |
||
235 | * standard PHP file-upload security measures. all variables accessed in a global context |
||
236 | * @return bool True on success |
||
237 | */ |
||
238 | 1 | public function confirm_upload() |
|
239 | { |
||
240 | 1 | global $sugar_config; |
|
241 | |||
242 | 1 | if(empty($this->field_name) || !isset($_FILES[$this->field_name])) { |
|
243 | 1 | return false; |
|
244 | } |
||
245 | |||
246 | //check to see if there are any errors from upload |
||
247 | if($_FILES[$this->field_name]['error'] != UPLOAD_ERR_OK) { |
||
248 | if($_FILES[$this->field_name]['error'] != UPLOAD_ERR_NO_FILE) { |
||
249 | if($_FILES[$this->field_name]['error'] == UPLOAD_ERR_INI_SIZE) { |
||
250 | //log the error, the string produced will read something like: |
||
251 | //ERROR: There was an error during upload. Error code: 1 - UPLOAD_ERR_INI_SIZE - The uploaded file exceeds the upload_max_filesize directive in php.ini. upload_maxsize is 16 |
||
252 | $errMess = string_format($GLOBALS['app_strings']['UPLOAD_ERROR_TEXT_SIZEINFO'],array($_FILES['filename_file']['error'], self::$filesError[$_FILES['filename_file']['error']],$sugar_config['upload_maxsize'])); |
||
253 | $GLOBALS['log']->fatal($errMess); |
||
254 | }else{ |
||
255 | //log the error, the string produced will read something like: |
||
256 | //ERROR: There was an error during upload. Error code: 3 - UPLOAD_ERR_PARTIAL - The uploaded file was only partially uploaded. |
||
257 | $errMess = string_format($GLOBALS['app_strings']['UPLOAD_ERROR_TEXT'],array($_FILES['filename_file']['error'], self::$filesError[$_FILES['filename_file']['error']])); |
||
258 | $GLOBALS['log']->fatal($errMess); |
||
259 | } |
||
260 | } |
||
261 | return false; |
||
262 | } |
||
263 | |||
264 | if(!is_uploaded_file($_FILES[$this->field_name]['tmp_name'])) { |
||
265 | return false; |
||
266 | } elseif($_FILES[$this->field_name]['size'] > $sugar_config['upload_maxsize']) { |
||
267 | $GLOBALS['log']->fatal("ERROR: uploaded file was too big: max filesize: {$sugar_config['upload_maxsize']}"); |
||
268 | return false; |
||
269 | } |
||
270 | |||
271 | if(!UploadStream::writable()) { |
||
272 | $GLOBALS['log']->fatal("ERROR: cannot write to upload directory"); |
||
273 | return false; |
||
274 | } |
||
275 | |||
276 | $this->mime_type = $this->getMime($_FILES[$this->field_name]); |
||
277 | $this->stored_file_name = $this->create_stored_filename(); |
||
278 | $this->temp_file_location = $_FILES[$this->field_name]['tmp_name']; |
||
279 | $this->uploaded_file_name = $_FILES[$this->field_name]['name']; |
||
280 | |||
281 | return true; |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * Guess MIME type for file |
||
286 | * @param string $filename |
||
287 | * @return string MIME type |
||
288 | */ |
||
289 | function getMimeSoap($filename){ |
||
290 | |||
291 | if( function_exists( 'ext2mime' ) ) |
||
292 | { |
||
293 | $mime = ext2mime($filename); |
||
294 | } |
||
295 | else |
||
296 | { |
||
297 | $mime = ' application/octet-stream'; |
||
298 | } |
||
299 | return $mime; |
||
300 | |||
301 | } |
||
302 | |||
303 | /** |
||
304 | * Get MIME type for uploaded file |
||
305 | * @param array $_FILES_element $_FILES element required |
||
306 | * @return string MIME type |
||
307 | */ |
||
308 | function getMime($_FILES_element) |
||
309 | { |
||
310 | $filename = $_FILES_element['name']; |
||
311 | $filetype = isset($_FILES_element['type']) ? $_FILES_element['type'] : null; |
||
312 | $file_ext = pathinfo($filename, PATHINFO_EXTENSION); |
||
313 | |||
314 | $is_image = strpos($filetype, 'image/') === 0; |
||
315 | // if it's an image, or no file extension is available and the mime is octet-stream |
||
316 | // try to determine the mime type |
||
317 | $recheckMime = $is_image || (empty($file_ext) && $filetype == 'application/octet-stream'); |
||
318 | |||
319 | $mime = 'application/octet-stream'; |
||
320 | if ($filetype && !$recheckMime) { |
||
321 | $mime = $filetype; |
||
322 | } elseif( function_exists( 'mime_content_type' ) ) { |
||
323 | $mime = mime_content_type( $_FILES_element['tmp_name'] ); |
||
324 | } elseif ($is_image) { |
||
325 | $info = getimagesize($_FILES_element['tmp_name']); |
||
326 | if ($info) { |
||
0 ignored issues
–
show
The expression
$info of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
327 | $mime = $info['mime']; |
||
328 | } |
||
329 | } elseif (function_exists('ext2mime')) { |
||
330 | $mime = ext2mime($filename); |
||
331 | } |
||
332 | |||
333 | return $mime; |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * gets note's filename |
||
338 | * @return string |
||
339 | */ |
||
340 | function get_stored_file_name() |
||
341 | { |
||
342 | return $this->stored_file_name; |
||
343 | } |
||
344 | |||
345 | function get_temp_file_location() |
||
346 | { |
||
347 | return $this->temp_file_location; |
||
348 | } |
||
349 | |||
350 | function get_uploaded_file_name() |
||
351 | { |
||
352 | return $this->uploaded_file_name; |
||
353 | } |
||
354 | |||
355 | function get_mime_type() |
||
356 | { |
||
357 | return $this->mime_type; |
||
358 | } |
||
359 | |||
360 | /** |
||
361 | * Returns the contents of the uploaded file |
||
362 | */ |
||
363 | public function get_file_contents() { |
||
364 | |||
365 | // Need to call |
||
366 | if ( !isset($this->temp_file_location) ) { |
||
367 | $this->confirm_upload(); |
||
368 | } |
||
369 | |||
370 | if (($data = @file_get_contents($this->temp_file_location)) === false) { |
||
371 | return false; |
||
372 | } |
||
373 | |||
374 | return $data; |
||
375 | } |
||
376 | |||
377 | |||
378 | |||
379 | /** |
||
380 | * creates a file's name for preparation for saving |
||
381 | * @return string |
||
382 | */ |
||
383 | function create_stored_filename() |
||
384 | { |
||
385 | global $sugar_config; |
||
386 | |||
387 | if(!$this->use_soap) { |
||
388 | $stored_file_name = $_FILES[$this->field_name]['name']; |
||
389 | $this->original_file_name = $stored_file_name; |
||
390 | |||
391 | /** |
||
392 | * cn: bug 8056 - windows filesystems and IIS do not like utf8. we are forced to urlencode() to ensure that |
||
393 | * the file is linkable from the browser. this will stay broken until we move to a db-storage system |
||
394 | */ |
||
395 | if(is_windows()) { |
||
396 | // create a non UTF-8 name encoding |
||
397 | // 176 + 36 char guid = windows' maximum filename length |
||
398 | $end = (strlen($stored_file_name) > 176) ? 176 : strlen($stored_file_name); |
||
399 | $stored_file_name = substr($stored_file_name, 0, $end); |
||
400 | $this->original_file_name = $_FILES[$this->field_name]['name']; |
||
401 | } |
||
402 | $stored_file_name = str_replace("\\", "", $stored_file_name); |
||
403 | } else { |
||
404 | $stored_file_name = $this->stored_file_name; |
||
405 | $this->original_file_name = $stored_file_name; |
||
406 | } |
||
407 | |||
408 | $this->file_ext = pathinfo($stored_file_name, PATHINFO_EXTENSION); |
||
409 | // cn: bug 6347 - fix file extension detection |
||
410 | foreach($sugar_config['upload_badext'] as $badExt) { |
||
411 | if(strtolower($this->file_ext) == strtolower($badExt)) { |
||
412 | $stored_file_name .= ".txt"; |
||
413 | $this->file_ext="txt"; |
||
414 | break; // no need to look for more |
||
415 | } |
||
416 | } |
||
417 | return $stored_file_name; |
||
418 | } |
||
419 | |||
420 | /** |
||
421 | * moves uploaded temp file to permanent save location |
||
422 | * @param string bean_id ID of parent bean |
||
423 | * @return bool True on success |
||
424 | */ |
||
425 | function final_move($bean_id) |
||
426 | { |
||
427 | $destination = $bean_id; |
||
428 | if(substr($destination, 0, 9) != "upload://") { |
||
429 | $destination = "upload://$bean_id"; |
||
430 | } |
||
431 | if($this->use_soap) { |
||
432 | if(!file_put_contents($destination, $this->file)){ |
||
433 | $GLOBALS['log']->fatal("ERROR: can't save file to $destination"); |
||
434 | return false; |
||
435 | } |
||
436 | } else { |
||
437 | if(!UploadStream::move_uploaded_file($_FILES[$this->field_name]['tmp_name'], $destination)) { |
||
438 | $GLOBALS['log']->fatal("ERROR: can't move_uploaded_file to $destination. You should try making the directory writable by the webserver"); |
||
439 | return false; |
||
440 | } |
||
441 | } |
||
442 | return true; |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * Upload document to external service |
||
447 | * @param SugarBean $bean Related bean |
||
448 | * @param string $bean_id |
||
449 | * @param string $doc_type |
||
450 | * @param string $file_name |
||
451 | * @param string $mime_type |
||
452 | */ |
||
453 | function upload_doc($bean, $bean_id, $doc_type, $file_name, $mime_type) |
||
454 | { |
||
455 | if(!empty($doc_type)&&$doc_type!='Sugar') { |
||
456 | global $sugar_config; |
||
457 | $destination = $this->get_upload_path($bean_id); |
||
458 | sugar_rename($destination, str_replace($bean_id, $bean_id.'_'.$file_name, $destination)); |
||
459 | $new_destination = $this->get_upload_path($bean_id.'_'.$file_name); |
||
460 | |||
461 | try{ |
||
462 | $this->api = ExternalAPIFactory::loadAPI($doc_type); |
||
463 | |||
464 | if ( isset($this->api) && $this->api !== false ) { |
||
465 | $result = $this->api->uploadDoc( |
||
0 ignored issues
–
show
The method
uploadDoc() does not seem to exist on object<ExternalAPIBase> .
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||
466 | $bean, |
||
467 | $new_destination, |
||
468 | $file_name, |
||
469 | $mime_type |
||
470 | ); |
||
471 | } else { |
||
472 | $result['success'] = FALSE; |
||
473 | // FIXME: Translate |
||
474 | $GLOBALS['log']->error("Could not load the requested API (".$doc_type.")"); |
||
475 | $result['errorMessage'] = 'Could not find a proper API'; |
||
476 | } |
||
477 | }catch(Exception $e){ |
||
478 | $result['success'] = FALSE; |
||
479 | $result['errorMessage'] = $e->getMessage(); |
||
480 | $GLOBALS['log']->error("Caught exception: (".$e->getMessage().") "); |
||
481 | } |
||
482 | if ( !$result['success'] ) { |
||
483 | sugar_rename($new_destination, str_replace($bean_id.'_'.$file_name, $bean_id, $new_destination)); |
||
484 | $bean->doc_type = 'Sugar'; |
||
485 | // FIXME: Translate |
||
486 | if ( ! is_array($_SESSION['user_error_message']) ) |
||
487 | $_SESSION['user_error_message'] = array(); |
||
488 | |||
489 | $error_message = isset($result['errorMessage']) ? $result['errorMessage'] : $GLOBALS['app_strings']['ERR_EXTERNAL_API_SAVE_FAIL']; |
||
490 | $_SESSION['user_error_message'][] = $error_message; |
||
491 | |||
492 | } |
||
493 | else { |
||
494 | unlink($new_destination); |
||
495 | } |
||
496 | } |
||
497 | |||
498 | } |
||
499 | |||
500 | /** |
||
501 | * returns the path with file name to save an uploaded file |
||
502 | * @param string bean_id ID of the parent bean |
||
503 | * @return string |
||
504 | */ |
||
505 | function get_upload_path($bean_id) |
||
506 | { |
||
507 | $file_name = $bean_id; |
||
508 | |||
509 | // cn: bug 8056 - mbcs filename in urlencoding > 212 chars in Windows fails |
||
510 | $end = (strlen($file_name) > 212) ? 212 : strlen($file_name); |
||
511 | $ret_file_name = substr($file_name, 0, $end); |
||
512 | |||
513 | return "upload://$ret_file_name"; |
||
514 | } |
||
515 | |||
516 | /** |
||
517 | * deletes a file |
||
518 | * @param string bean_id ID of the parent bean |
||
519 | * @param string file_name File's name |
||
520 | */ |
||
521 | static public function unlink_file($bean_id,$file_name = '') |
||
0 ignored issues
–
show
|
|||
522 | { |
||
523 | if(file_exists("upload://$bean_id$file_name")) { |
||
524 | return unlink("upload://$bean_id$file_name"); |
||
525 | } |
||
526 | } |
||
527 | |||
528 | /** |
||
529 | * Get upload file location prefix |
||
530 | * @return string prefix |
||
531 | */ |
||
532 | public function get_upload_dir() |
||
533 | { |
||
534 | return "upload://"; |
||
535 | } |
||
536 | |||
537 | /** |
||
538 | * Return real FS path of the file |
||
539 | * @param string $path |
||
540 | */ |
||
541 | 4 | public static function realpath($path) |
|
542 | { |
||
543 | 4 | if(substr($path, 0, 9) == "upload://") { |
|
544 | $path = UploadStream::path($path); |
||
545 | } |
||
546 | 4 | $ret = realpath($path); |
|
547 | 4 | return $ret?$ret:$path; |
|
548 | } |
||
549 | |||
550 | /** |
||
551 | * Return path of uploaded file relative to uploads dir |
||
552 | * @param string $path |
||
553 | */ |
||
554 | public static function relativeName($path) |
||
555 | { |
||
556 | if(substr($path, 0, 9) == "upload://") { |
||
557 | $path = substr($path, 9); |
||
558 | } |
||
559 | return $path; |
||
560 | } |
||
561 | } |
||
562 | |||
563 | /** |
||
564 | * @internal |
||
565 | * Upload file stream handler |
||
566 | */ |
||
567 | class UploadStream |
||
0 ignored issues
–
show
|
|||
568 | { |
||
569 | const STREAM_NAME = "upload"; |
||
570 | protected static $upload_dir; |
||
571 | |||
572 | /** |
||
573 | * Method checks Suhosin restrictions to use streams in php |
||
574 | * |
||
575 | * @static |
||
576 | * @return bool is allowed stream or not |
||
577 | */ |
||
578 | public static function getSuhosinStatus() |
||
579 | { |
||
580 | // looks like suhosin patch doesn't block protocols, only suhosin extension (tested on FreeBSD) |
||
581 | // if suhosin is not installed it is okay for us |
||
582 | if (extension_loaded('suhosin') == false) |
||
0 ignored issues
–
show
|
|||
583 | { |
||
584 | return true; |
||
585 | } |
||
586 | $configuration = ini_get_all('suhosin', false); |
||
587 | |||
588 | // suhosin simulation is okay for us |
||
589 | if ($configuration['suhosin.simulation'] == true) |
||
590 | { |
||
591 | return true; |
||
592 | } |
||
593 | |||
594 | // checking that UploadStream::STREAM_NAME is allowed by white list |
||
595 | $streams = $configuration['suhosin.executor.include.whitelist']; |
||
596 | if ($streams != '') |
||
597 | { |
||
598 | $streams = explode(',', $streams); |
||
599 | foreach($streams as $stream) |
||
600 | { |
||
601 | $stream = explode('://', $stream, 2); |
||
602 | if (count($stream) == 1) |
||
603 | { |
||
604 | if ($stream[0] == UploadStream::STREAM_NAME) |
||
605 | { |
||
606 | return true; |
||
607 | } |
||
608 | } |
||
609 | elseif ($stream[1] == '' && $stream[0] == UploadStream::STREAM_NAME) |
||
610 | { |
||
611 | return true; |
||
612 | } |
||
613 | } |
||
614 | |||
615 | $GLOBALS['log']->fatal('Stream ' . UploadStream::STREAM_NAME . ' is not listed in suhosin.executor.include.whitelist and blocked because of it'); |
||
616 | return false; |
||
617 | } |
||
618 | |||
619 | // checking that UploadStream::STREAM_NAME is not blocked by black list |
||
620 | $streams = $configuration['suhosin.executor.include.blacklist']; |
||
621 | if ($streams != '') |
||
622 | { |
||
623 | $streams = explode(',', $streams); |
||
624 | foreach($streams as $stream) |
||
625 | { |
||
626 | $stream = explode('://', $stream, 2); |
||
627 | if ($stream[0] == UploadStream::STREAM_NAME) |
||
628 | { |
||
629 | $GLOBALS['log']->fatal('Stream ' . UploadStream::STREAM_NAME . 'is listed in suhosin.executor.include.blacklist and blocked because of it'); |
||
630 | return false; |
||
631 | } |
||
632 | } |
||
633 | return true; |
||
634 | } |
||
635 | |||
636 | $GLOBALS['log']->fatal('Suhosin blocks all streams, please define ' . UploadStream::STREAM_NAME . ' stream in suhosin.executor.include.whitelist'); |
||
637 | return false; |
||
638 | } |
||
639 | |||
640 | /** |
||
641 | * Get upload directory |
||
642 | * @return string |
||
643 | */ |
||
644 | 5 | public static function getDir() |
|
645 | { |
||
646 | 5 | if(empty(self::$upload_dir)) { |
|
647 | 1 | self::$upload_dir = rtrim($GLOBALS['sugar_config']['upload_dir'], '/\\'); |
|
648 | 1 | if(empty(self::$upload_dir)) { |
|
649 | self::$upload_dir = "upload"; |
||
650 | } |
||
651 | 1 | if(!file_exists(self::$upload_dir)) { |
|
652 | sugar_mkdir(self::$upload_dir, 0755, true); |
||
653 | } |
||
654 | } |
||
655 | 5 | return self::$upload_dir; |
|
656 | } |
||
657 | |||
658 | /** |
||
659 | * Check if upload dir is writable |
||
660 | * @return bool |
||
661 | */ |
||
662 | public static function writable() |
||
663 | { |
||
664 | return is_writable(self::getDir()); |
||
665 | } |
||
666 | |||
667 | /** |
||
668 | * Register the stream |
||
669 | */ |
||
670 | public static function register() |
||
671 | { |
||
672 | stream_register_wrapper(self::STREAM_NAME, __CLASS__); |
||
673 | } |
||
674 | |||
675 | /** |
||
676 | * Get real FS path of the upload stream file |
||
677 | * @param string $path Upload stream path (with upload://) |
||
678 | * @return string FS path |
||
679 | */ |
||
680 | 5 | public static function path($path) |
|
681 | { |
||
682 | 5 | $path = substr($path, strlen(self::STREAM_NAME)+3); // cut off upload:// |
|
683 | 5 | $path = str_replace("\\", "/", $path); // canonicalize path |
|
684 | 5 | if($path == ".." || substr($path, 0, 3) == "../" || substr($path, -3, 3) == "/.." || strstr($path, "/../")) { |
|
685 | return null; |
||
686 | } |
||
687 | 5 | return self::getDir()."/".$path; |
|
688 | } |
||
689 | |||
690 | /** |
||
691 | * Ensure upload subdir exists |
||
692 | * @param string $path Upload stream path (with upload://) |
||
693 | * @param bool $writable |
||
694 | * @return boolean |
||
695 | */ |
||
696 | public static function ensureDir($path, $writable = true) |
||
697 | { |
||
698 | $path = self::path($path); |
||
699 | if(!is_dir($path)) { |
||
700 | return sugar_mkdir($path, 0755, true); |
||
701 | } |
||
702 | return true; |
||
703 | } |
||
704 | |||
705 | public function dir_closedir() |
||
706 | { |
||
707 | closedir($this->dirp); |
||
708 | } |
||
709 | |||
710 | public function dir_opendir ($path, $options ) |
||
711 | { |
||
712 | $this->dirp = opendir(self::path($path)); |
||
713 | return !empty($this->dirp); |
||
714 | } |
||
715 | |||
716 | public function dir_readdir() |
||
717 | { |
||
718 | return readdir($this->dirp); |
||
719 | } |
||
720 | |||
721 | public function dir_rewinddir() |
||
722 | { |
||
723 | return rewinddir($this->dirp); |
||
724 | } |
||
725 | |||
726 | 2 | public function mkdir($path, $mode, $options) |
|
727 | { |
||
728 | 2 | return mkdir(self::path($path), $mode, ($options&STREAM_MKDIR_RECURSIVE) != 0); |
|
729 | } |
||
730 | |||
731 | public function rename($path_from, $path_to) |
||
732 | { |
||
733 | return rename(self::path($path_from), self::path($path_to)); |
||
734 | } |
||
735 | |||
736 | public function rmdir($path, $options) |
||
737 | { |
||
738 | return rmdir(self::path($path)); |
||
739 | } |
||
740 | |||
741 | public function stream_cast ($cast_as) |
||
742 | { |
||
743 | return $this->fp; |
||
744 | } |
||
745 | |||
746 | public function stream_close () |
||
747 | { |
||
748 | fclose($this->fp); |
||
749 | return true; |
||
750 | } |
||
751 | |||
752 | public function stream_eof () |
||
753 | { |
||
754 | return feof($this->fp); |
||
755 | } |
||
756 | public function stream_flush () |
||
757 | { |
||
758 | return fflush($this->fp); |
||
759 | } |
||
760 | |||
761 | public function stream_lock($operation) |
||
762 | { |
||
763 | return flock($this->fp, $operation); |
||
764 | } |
||
765 | |||
766 | public function stream_open($path, $mode) |
||
767 | { |
||
768 | $fullpath = self::path($path); |
||
769 | if(empty($fullpath)) return false; |
||
770 | if($mode == 'r') { |
||
771 | $this->fp = fopen($fullpath, $mode); |
||
772 | } else { |
||
773 | // if we will be writing, try to transparently create the directory |
||
774 | $this->fp = @fopen($fullpath, $mode); |
||
775 | if(!$this->fp && !file_exists(dirname($fullpath))) { |
||
776 | mkdir(dirname($fullpath), 0755, true); |
||
777 | $this->fp = fopen($fullpath, $mode); |
||
778 | } |
||
779 | } |
||
780 | return !empty($this->fp); |
||
781 | } |
||
782 | |||
783 | public function stream_read($count) |
||
784 | { |
||
785 | return fread($this->fp, $count); |
||
786 | } |
||
787 | |||
788 | public function stream_seek($offset, $whence = SEEK_SET) |
||
789 | { |
||
790 | return fseek($this->fp, $offset, $whence) == 0; |
||
791 | } |
||
792 | |||
793 | public function stream_set_option($option, $arg1, $arg2) |
||
794 | { |
||
795 | return true; |
||
796 | } |
||
797 | |||
798 | public function stream_stat() |
||
799 | { |
||
800 | return fstat($this->fp); |
||
801 | } |
||
802 | |||
803 | public function stream_tell() |
||
804 | { |
||
805 | return ftell($this->fp); |
||
806 | } |
||
807 | public function stream_write($data) |
||
808 | { |
||
809 | return fwrite($this->fp, $data); |
||
810 | } |
||
811 | |||
812 | public function unlink($path) |
||
813 | { |
||
814 | unlink(self::path($path)); |
||
815 | return true; |
||
816 | } |
||
817 | |||
818 | 5 | public function url_stat($path, $flags) |
|
819 | { |
||
820 | 5 | return @stat(self::path($path)); |
|
821 | } |
||
822 | |||
823 | public static function move_uploaded_file($upload, $path) |
||
824 | { |
||
825 | return move_uploaded_file($upload, self::path($path)); |
||
826 | } |
||
827 | } |
||
828 | |||
829 |