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-2011 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
38 /*********************************************************************************
41 ********************************************************************************/
42 require_once('include/externalAPI/ExternalAPIFactory.php');
47 var $stored_file_name;
48 var $original_file_name;
49 var $temp_file_location;
50 var $use_soap = false;
54 function UploadFile ($field_name) {
55 // $field_name is the name of your passed file selector field in your form
56 // i.e., for Emails, it is "email_attachmentX" where X is 0-9
57 $this->field_name = $field_name;
58 // Bug 28408 - Add automatic creation of upload cache directory if it doesn't exist
59 if ( !is_dir($GLOBALS['sugar_config']['upload_dir']) )
60 create_cache_directory(str_replace($GLOBALS['sugar_config']['cache_dir'],'',$GLOBALS['sugar_config']['upload_dir']));
63 function set_for_soap($filename, $file) {
64 $this->stored_file_name = $filename;
65 $this->use_soap = true;
70 * wrapper for this::get_file_path()
71 * @param string stored_file_name File name in filesystem
72 * @param string bean_id note bean ID
73 * @return string path with file name
75 function get_url($stored_file_name,$bean_id) {
77 return UploadFile::get_file_path($stored_file_name,$bean_id);
81 * builds a URL path for an anchor tag
82 * @param string stored_file_name File name in filesystem
83 * @param string bean_id note bean ID
84 * @return string path with file name
86 function get_file_path($stored_file_name,$bean_id) {
90 // if the parameters are empty strings, just return back the upload_dir
91 if ( empty($bean_id) && empty($stored_file_name) )
92 return $sugar_config['upload_dir'];
94 if (file_exists($sugar_config['upload_dir'] . $bean_id . rawurlencode($stored_file_name))){
95 if (!rename($sugar_config['upload_dir'] . $bean_id . rawurlencode($stored_file_name),
96 $sugar_config['upload_dir'] . $bean_id)){
97 $GLOBALS['log']->fatal("unable to rename file in {$sugar_config['upload_dir']}");
100 else if (file_exists($sugar_config['upload_dir'] . $bean_id . urlencode($stored_file_name))){
101 if (!rename($sugar_config['upload_dir'] . $bean_id . urlencode($stored_file_name),
102 $sugar_config['upload_dir'] . $bean_id)){
103 $GLOBALS['log']->fatal("unable to rename file in {$sugar_config['upload_dir']}");
106 else if (file_exists($sugar_config['upload_dir'] . $bean_id . $stored_file_name)){
107 if (!rename($sugar_config['upload_dir'] . $bean_id . $stored_file_name,
108 $sugar_config['upload_dir'] . $bean_id)){
109 $GLOBALS['log']->fatal("unable to rename file in {$sugar_config['upload_dir']}");
112 else if (file_exists($sugar_config['upload_dir'] . $bean_id . $locale->translateCharset( $stored_file_name, 'UTF-8', $locale->getExportCharset() ))){
113 if (!rename($sugar_config['upload_dir'] . $bean_id . $locale->translateCharset( $stored_file_name, 'UTF-8', $locale->getExportCharset() ),
114 $sugar_config['upload_dir'] . $bean_id)){
115 $GLOBALS['log']->fatal("unable to rename file in {$sugar_config['upload_dir']}");
119 return $sugar_config['upload_dir'] . $bean_id;
123 * duplicates an already uploaded file in the filesystem.
124 * @param string old_id ID of original note
125 * @param string new_id ID of new (copied) note
126 * @param string filename Filename of file (deprecated)
128 function duplicate_file($old_id, $new_id, $file_name) {
129 global $sugar_config;
131 // current file system (GUID)
132 $source = $sugar_config['upload_dir'] . $old_id;
134 if(!file_exists($source)) {
135 // old-style file system (GUID.filename.extension)
136 $oldStyleSource = $source.$file_name;
137 if(file_exists($oldStyleSource)) {
138 // change to new style
139 if(copy($oldStyleSource, $source)) {
141 if(!unlink($oldStyleSource)) {
142 $GLOBALS['log']->warn("upload_file could not unlink [ {$oldStyleSource} ]");
145 $GLOBALS['log']->warn("upload_file could not copy [ {$oldStyleSource} ] to [ {$source} ]");
150 $destination = $sugar_config['upload_dir'] . $new_id;
151 if(!copy($source, $destination)) {
152 $GLOBALS['log']->warn("upload_file could not copy [ {$source} ] to [ {$destination} ]");
157 * standard PHP file-upload security measures. all variables accessed in a global context
158 * @return bool True on success
160 function confirm_upload() {
161 global $sugar_config;
163 if(!is_uploaded_file($_FILES[$this->field_name]['tmp_name'])) {
165 } elseif($_FILES[$this->field_name]['size'] > $sugar_config['upload_maxsize']) {
166 die("ERROR: uploaded file was too big: max filesize: {$sugar_config['upload_maxsize']}");
169 if(!is_writable($sugar_config['upload_dir'])) {
170 die("ERROR: cannot write to directory: {$sugar_config['upload_dir']} for uploads");
173 $this->mime_type =$this->getMime($_FILES[$this->field_name]);
174 $this->stored_file_name = $this->create_stored_filename();
175 $this->temp_file_location = $_FILES[$this->field_name]['tmp_name'];
180 function getMimeSoap($filename){
182 if( function_exists( 'ext2mime' ) )
184 $mime = ext2mime($filename);
188 $mime = ' application/octet-stream';
193 function getMime(&$_FILES_element)
196 $filename = $_FILES_element['name'];
197 $file_ext = pathinfo($filename, PATHINFO_EXTENSION);
199 //If no file extension is available and the mime is octet-stream try to determine the mime type.
200 $recheckMime = empty($file_ext) && ($_FILES_element['type'] == 'application/octet-stream');
202 if( $_FILES_element['type'] && !$recheckMime)
204 $mime = $_FILES_element['type'];
206 elseif( function_exists( 'mime_content_type' ) )
208 $mime = mime_content_type( $_FILES_element['tmp_name'] );
210 elseif( function_exists( 'ext2mime' ) )
212 $mime = ext2mime( $_FILES_element['name'] );
216 $mime = ' application/octet-stream';
222 * gets note's filename
225 function get_stored_file_name() {
226 return $this->stored_file_name;
230 * creates a file's name for preparation for saving
233 function create_stored_filename() {
234 global $sugar_config;
236 if(!$this->use_soap) {
237 $stored_file_name = $_FILES[$this->field_name]['name'];
238 $this->original_file_name = $stored_file_name;
241 * cn: bug 8056 - windows filesystems and IIS do not like utf8. we are forced to urlencode() to ensure that
242 * the file is linkable from the browser. this will stay broken until we move to a db-storage system
245 // create a non UTF-8 name encoding
246 // 176 + 36 char guid = windows' maximum filename length
247 $end = (strlen($stored_file_name) > 176) ? 176 : strlen($stored_file_name);
248 $stored_file_name = substr($stored_file_name, 0, $end);
249 $this->original_file_name = $_FILES[$this->field_name]['name'];
252 $stored_file_name = $this->stored_file_name;
253 $this->original_file_name = $stored_file_name;
256 $ext_pos = strrpos($stored_file_name, ".");
257 if($ext_pos !== false)
258 $this->file_ext = substr($stored_file_name, $ext_pos + 1);
259 // cn: bug 6347 - fix file extension detection
260 foreach($sugar_config['upload_badext'] as $badExt) {
261 if(strtolower($this->file_ext) == strtolower($badExt)) {
262 $stored_file_name .= ".txt";
263 $this->file_ext="txt";
264 break; // no need to look for more
267 return $stored_file_name;
271 * moves uploaded temp file to permanent save location
272 * @param string bean_id ID of parent bean
273 * @return bool True on success
275 function final_move($bean_id) {
276 global $sugar_config;
278 $destination = clean_path($this->get_upload_path($bean_id));
279 if($this->use_soap) {
280 $fp = sugar_fopen($destination, 'wb');
281 if(!fwrite($fp, $this->file)){
282 die("ERROR: can't save file to $destination");
286 if(!move_uploaded_file($_FILES[$this->field_name]['tmp_name'], $destination)) {
287 die("ERROR: can't move_uploaded_file to $destination. You should try making the directory writable by the webserver");
293 function upload_doc(&$bean, $bean_id, $doc_type, $file_name, $mime_type){
295 if(!empty($doc_type)&&$doc_type!='Sugar') {
296 global $sugar_config;
297 $destination = clean_path($this->get_upload_path($bean_id));
298 sugar_rename($destination, str_replace($bean_id, $bean_id.'_'.$file_name, $destination));
299 $new_destination = clean_path($this->get_upload_path($bean_id.'_'.$file_name));
302 $this->api = ExternalAPIFactory::loadAPI($doc_type);
304 if ( isset($this->api) && $this->api !== false ) {
305 $result = $this->api->uploadDoc(
312 $result['success'] = FALSE;
314 $GLOBALS['log']->error("Could not load the requested API (".$doc_type.")");
315 $result['errorMessage'] = 'Could not find a proper API';
317 }catch(Exception $e){
318 $result['success'] = FALSE;
319 $result['errorMessage'] = $e->getMessage();
320 $GLOBALS['log']->error("Caught exception: (".$e->getMessage().") ");
322 if ( !$result['success'] ) {
323 sugar_rename($new_destination, str_replace($bean_id.'_'.$file_name, $bean_id, $new_destination));
324 $bean->doc_type = 'Sugar';
326 if ( ! is_array($_SESSION['user_error_message']) )
327 $_SESSION['user_error_message'] = array();
329 $error_message = isset($result['errorMessage']) ? $result['errorMessage'] : $GLOBALS['app_strings']['ERR_EXTERNAL_API_SAVE_FAIL'];
330 $_SESSION['user_error_message'][] = $error_message;
334 unlink($new_destination);
340 * returns the path with file name to save an uploaded file
341 * @param string bean_id ID of the parent bean
344 function get_upload_path($bean_id) {
345 global $sugar_config;
346 $file_name = $bean_id;
348 // cn: bug 8056 - mbcs filename in urlencoding > 212 chars in Windows fails
349 $end = (strlen($file_name) > 212) ? 212 : strlen($file_name);
350 $ret_file_name = substr($file_name, 0, $end);
352 return $sugar_config['upload_dir'].$ret_file_name;
357 * @param string bean_id ID of the parent bean
358 * @param string file_name File's name
360 function unlink_file($bean_id,$file_name) {
361 global $sugar_config;
362 return unlink($sugar_config['upload_dir'].$bean_id.$file_name);