]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/upload_file.php
Release 6.2.0
[Github/sugarcrm.git] / include / upload_file.php
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-2011 SugarCRM Inc.
6  * 
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.
13  * 
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
17  * details.
18  * 
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
22  * 02110-1301 USA.
23  * 
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.
26  * 
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.
30  * 
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  ********************************************************************************/
37
38 /*********************************************************************************
39
40  * Description:
41  ********************************************************************************/
42 require_once('include/externalAPI/ExternalAPIFactory.php');
43
44 class UploadFile 
45 {
46         var $field_name;
47         var $stored_file_name;
48         var $original_file_name;
49         var $temp_file_location;
50         var $use_soap = false;
51         var $file;
52         var $file_ext;
53         
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']));
61         }
62
63         function set_for_soap($filename, $file) {
64                 $this->stored_file_name = $filename;
65                 $this->use_soap = true;
66                 $this->file = $file;
67         }
68
69         /**
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
74          */
75         function get_url($stored_file_name,$bean_id) {
76                 global $sugar_config;
77                 return UploadFile::get_file_path($stored_file_name,$bean_id);
78         }
79         
80         /**
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
85          */
86         function get_file_path($stored_file_name,$bean_id) {
87                 global $sugar_config;
88                 global $locale;
89         
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'];
93             
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']}");
98                         }
99                 }
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']}");
104                         }
105                 } 
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']}");
110                         }
111                 }
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']}");
116                         }
117                 }               
118                                 
119                 return $sugar_config['upload_dir'] . $bean_id;
120         }
121
122         /**
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)
127          */
128         function duplicate_file($old_id, $new_id, $file_name) {
129                 global $sugar_config;
130
131                 // current file system (GUID)
132                 $source = $sugar_config['upload_dir'] . $old_id;
133                 
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)) {
140                                         // delete the old
141                                         if(!unlink($oldStyleSource)) {
142                                                 $GLOBALS['log']->warn("upload_file could not unlink [ {$oldStyleSource} ]");
143                                         }
144                                 } else {
145                                         $GLOBALS['log']->warn("upload_file could not copy [ {$oldStyleSource} ] to [ {$source} ]");
146                                 }
147                         }
148                 }
149                 
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} ]");
153                 }
154         }
155
156         /**
157          * standard PHP file-upload security measures. all variables accessed in a global context
158          * @return bool True on success
159          */
160         function confirm_upload() {
161                 global $sugar_config;
162
163                 if(!is_uploaded_file($_FILES[$this->field_name]['tmp_name'])) {
164                         return false;
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']}");
167                 }
168
169                 if(!is_writable($sugar_config['upload_dir'])) {
170                         die("ERROR: cannot write to directory: {$sugar_config['upload_dir']} for uploads");
171                 }
172
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'];
176
177                 return true;
178         }
179
180         function getMimeSoap($filename){
181
182                 if( function_exists( 'ext2mime' ) )
183                 {
184                         $mime = ext2mime($filename);
185                 }
186                 else
187                 {
188                         $mime = ' application/octet-stream';
189                 }
190                 return $mime;
191
192         }
193         function getMime(&$_FILES_element)
194         {
195
196                 $filename = $_FILES_element['name'];
197         $file_ext = pathinfo($filename, PATHINFO_EXTENSION);
198
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');
201
202                 if( $_FILES_element['type'] && !$recheckMime)
203                 {
204                         $mime = $_FILES_element['type'];
205                 }
206                 elseif( function_exists( 'mime_content_type' ) )
207                 {
208                         $mime = mime_content_type( $_FILES_element['tmp_name'] );
209                 }
210                 elseif( function_exists( 'ext2mime' ) )
211                 {
212                         $mime = ext2mime( $_FILES_element['name'] );
213                 }
214                 else
215                 {
216                         $mime = ' application/octet-stream';
217                 }
218                 return $mime;
219         }
220
221         /**
222          * gets note's filename
223          * @return string
224          */
225         function get_stored_file_name() {
226                 return $this->stored_file_name;
227         }
228
229         /**
230          * creates a file's name for preparation for saving
231          * @return string
232          */
233         function create_stored_filename() {
234                 global $sugar_config;
235                 
236                 if(!$this->use_soap) {
237                         $stored_file_name = $_FILES[$this->field_name]['name'];
238                         $this->original_file_name = $stored_file_name;
239                         
240                         /**
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
243                          */
244                         if(is_windows()) {
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'];
250                         }
251                 } else {
252                         $stored_file_name = $this->stored_file_name;
253                         $this->original_file_name = $stored_file_name;
254                 }
255                 
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
265             }
266         }
267                 return $stored_file_name;
268         }
269
270         /**
271          * moves uploaded temp file to permanent save location
272          * @param string bean_id ID of parent bean
273          * @return bool True on success
274          */
275         function final_move($bean_id) {
276                 global $sugar_config;
277
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");
283                 }
284                 fclose($fp);
285                 } else {
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");
288                         }
289                 }
290                 return true;
291         }
292         
293         function upload_doc(&$bean, $bean_id, $doc_type, $file_name, $mime_type){
294         
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));
300                             
301                     try{
302                 $this->api = ExternalAPIFactory::loadAPI($doc_type);
303
304                 if ( isset($this->api) && $this->api !== false ) {
305                     $result = $this->api->uploadDoc(
306                         $bean,
307                         $new_destination,
308                         $file_name,
309                         $mime_type
310                         );
311                 } else {
312                     $result['success'] = FALSE;
313                     // FIXME: Translate
314                     $GLOBALS['log']->error("Could not load the requested API (".$doc_type.")");
315                     $result['errorMessage'] = 'Could not find a proper API';
316                 }
317             }catch(Exception $e){
318                 $result['success'] = FALSE;
319                 $result['errorMessage'] = $e->getMessage();
320                 $GLOBALS['log']->error("Caught exception: (".$e->getMessage().") ");
321             }
322             if ( !$result['success'] ) {
323                 sugar_rename($new_destination, str_replace($bean_id.'_'.$file_name, $bean_id, $new_destination));
324                 $bean->doc_type = 'Sugar';
325                 // FIXME: Translate
326                 if ( ! is_array($_SESSION['user_error_message']) ) 
327                     $_SESSION['user_error_message'] = array(); 
328
329                 $error_message = isset($result['errorMessage']) ? $result['errorMessage'] : $GLOBALS['app_strings']['ERR_EXTERNAL_API_SAVE_FAIL'];
330                 $_SESSION['user_error_message'][] = $error_message;
331
332             }
333             else {
334                 unlink($new_destination);
335             }
336         }
337         }
338
339         /**
340          * returns the path with file name to save an uploaded file
341          * @param string bean_id ID of the parent bean
342          * @return string
343          */
344         function get_upload_path($bean_id) {
345                 global $sugar_config;
346                 $file_name = $bean_id;
347                 
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);
351                 
352                 return $sugar_config['upload_dir'].$ret_file_name;
353         }
354
355         /**
356          * deletes a file
357          * @param string bean_id ID of the parent bean
358          * @param string file_name File's name
359          */
360         function unlink_file($bean_id,$file_name) {
361                 global $sugar_config;
362         return unlink($sugar_config['upload_dir'].$bean_id.$file_name);
363     }
364 }
365 ?>