source: trunk/lib/Upload.inc.php @ 415

Last change on this file since 415 was 415, checked in by anonymous, 11 years ago

Disabled MX record check for email validation. Updated PEdit to work better with AcceptPathInfo? enabled.

File size: 30.9 KB
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2012 Strangecode, LLC
6 *
7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
24 * Upload.inc.php
25 *
26 * The Upload class provides an interface to deal with http uploaded files.
27 *
28 * @author  Quinn Comendant <quinn@strangecode.com>
29 * @requires App.inc.php
30 * @version 1.4
31 */
32
33// Upload error types.
34define('UPLOAD_USER_ERR_EMPTY_FILE', 100);
35define('UPLOAD_USER_ERR_NOT_UPLOADED_FILE', 101);
36define('UPLOAD_USER_ERR_INVALID_EXTENSION', 102);
37define('UPLOAD_USER_ERR_NOT_UNIQUE', 103);
38define('UPLOAD_USER_ERR_MOVE_FAILED', 104);
39
40class Upload {
41
42    // General object parameters.
43    var $_params = array(
44
45        // Which messages do we pass to raiseMsg?
46        'display_messages' => MSG_ALL,
47
48        // Existing files will be overwritten when there is a name conflict?
49        'allow_overwriting' => false,
50
51        // The filesystem path to the final upload directory.
52        'upload_path' => null,
53
54        // The file permissions of the uploaded files. Remember, files will be owned by the web server user.
55        'dest_file_perms' => 0600,
56
57        // The file permissions of the uploaded files. Remember, files will be owned by the web server user.
58        'dest_dir_perms' => 0700,
59
60        // Require file to have one of the following file name extensions.
61        'valid_file_extensions' => array('jpg', 'jpeg', 'gif', 'png', 'pdf', 'txt', 'text', 'html', 'htm'),
62    );
63
64    // Array of files with errors.
65    var $errors = array();
66
67    // Array of file name extensions and corresponding mime-types.
68    var $mime_extension_map = array( 'ez' => 'application/andrew-inset', 'hqx' => 'application/mac-binhex40', 'cpt' => 'application/mac-compactpro', 'doc' => 'application/msword', 'bin' => 'application/octet-stream', 'class' => 'application/octet-stream', 'dll' => 'application/octet-stream', 'dms' => 'application/octet-stream', 'exe' => 'application/octet-stream', 'lha' => 'application/octet-stream', 'lzh' => 'application/octet-stream', 'so' => 'application/octet-stream', 'oda' => 'application/oda', 'pdf' => 'application/pdf', 'ai' => 'application/postscript', 'eps' => 'application/postscript', 'ps' => 'application/postscript', 'smi' => 'application/smil', 'smil' => 'application/smil', 'mif' => 'application/vnd.mif', 'xls' => 'application/vnd.ms-excel', 'ppt' => 'application/vnd.ms-powerpoint', 'sxc' => 'application/vnd.sun.xml.calc', 'stc' => 'application/vnd.sun.xml.calc.template', 'sxd' => 'application/vnd.sun.xml.draw', 'std' => 'application/vnd.sun.xml.draw.template', 'sxi' => 'application/vnd.sun.xml.impress', 'sti' => 'application/vnd.sun.xml.impress.template', 'sxm' => 'application/vnd.sun.xml.math', 'sxw' => 'application/vnd.sun.xml.writer', 'sxg' => 'application/vnd.sun.xml.writer.global', 'stw' => 'application/vnd.sun.xml.writer.template', 'vsd' => 'application/vnd.visio', 'wbxml' => 'application/vnd.wap.wbxml', 'wmlc' => 'application/vnd.wap.wmlc', 'wmlsc' => 'application/vnd.wap.wmlscriptc', 'bcpio' => 'application/x-bcpio', 'vcd' => 'application/x-cdlink', 'pgn' => 'application/x-chess-pgn', 'Z' => 'application/x-compress', 'cpio' => 'application/x-cpio', 'csh' => 'application/x-csh', 'dcr' => 'application/x-director', 'dir' => 'application/x-director', 'dxr' => 'application/x-director', 'dvi' => 'application/x-dvi', 'spl' => 'application/x-futuresplash', 'gtar' => 'application/x-gtar', 'tgz' => 'application/x-gtar', 'gz' => 'application/x-gzip', 'hdf' => 'application/x-hdf', 'php' => 'application/x-httpd-php', 'php3' => 'application/x-httpd-php3', 'js' => 'application/x-javascript', 'skd' => 'application/x-koan', 'skm' => 'application/x-koan', 'skp' => 'application/x-koan', 'skt' => 'application/x-koan', 'latex' => 'application/x-latex', 'wmd' => 'application/x-ms-wmd', 'wmz' => 'application/x-ms-wmz', 'cdf' => 'application/x-netcdf', 'nc' => 'application/x-netcdf', 'pl' => 'application/x-perl', 'pm' => 'application/x-perl', 'psd' => 'application/x-photoshop', 'sh' => 'application/x-sh', 'shar' => 'application/x-shar', 'swf' => 'application/x-shockwave-flash', 'sit' => 'application/x-stuffit', 'sv4cpio' => 'application/x-sv4cpio', 'sv4crc' => 'application/x-sv4crc', 'tar' => 'application/x-tar', 'tcl' => 'application/x-tcl', 'tex' => 'application/x-tex', 'texi' => 'application/x-texinfo', 'texinfo' => 'application/x-texinfo', 'roff' => 'application/x-troff', 't' => 'application/x-troff', 'tr' => 'application/x-troff', 'man' => 'application/x-troff-man', 'me' => 'application/x-troff-me', 'ms' => 'application/x-troff-ms', 'ustar' => 'application/x-ustar', 'src' => 'application/x-wais-source', 'xht' => 'application/xhtml+xml', 'xhtml' => 'application/xhtml+xml', 'xml' => 'application/xml', 'zip' => 'application/zip', 'au' => 'audio/basic', 'snd' => 'audio/basic', 'kar' => 'audio/midi', 'mid' => 'audio/midi', 'midi' => 'audio/midi', 'mp2' => 'audio/mpeg', 'mp3' => 'audio/mpeg', 'mpga' => 'audio/mpeg', 'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 'aiff' => 'audio/x-aiff', 'm3u' => 'audio/x-mpegurl', 'wax' => 'audio/x-ms-wax', 'wma' => 'audio/x-ms-wma', 'ram' => 'audio/x-pn-realaudio', 'rm' => 'audio/x-pn-realaudio', 'rpm' => 'audio/x-pn-realaudio-plugin', 'ra' => 'audio/x-realaudio', 'wav' => 'audio/x-wav', 'pdb' => 'chemical/x-pdb', 'xyz' => 'chemical/x-xyz', 'bmp' => 'image/bmp', 'gif' => 'image/gif', 'ief' => 'image/ief', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', 'png' => 'image/png', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wbmp' => 'image/vnd.wap.wbmp', 'ras' => 'image/x-cmu-raster', 'pnm' => 'image/x-portable-anymap', 'pbm' => 'image/x-portable-bitmap', 'pgm' => 'image/x-portable-graymap', 'ppm' => 'image/x-portable-pixmap', 'rgb' => 'image/x-rgb', 'xbm' => 'image/x-xbitmap', 'xpm' => 'image/x-xpixmap', 'xwd' => 'image/x-xwindowdump', 'iges' => 'model/iges', 'igs' => 'model/iges', 'mesh' => 'model/mesh', 'msh' => 'model/mesh', 'silo' => 'model/mesh', 'vrml' => 'model/vrml', 'wrl' => 'model/vrml', 'ics' => 'text/calendar', 'ifb' => 'text/calendar', 'vcs' => 'text/calendar', 'vfb' => 'text/calendar', 'css' => 'text/css', 'csv' => 'text/csv', 'diff' => 'text/diff', 'patch' => 'text/diff', 'htm' => 'text/html', 'html' => 'text/html', 'shtml' => 'text/html', 'asc' => 'text/plain', 'log' => 'text/plain', 'po' => 'text/plain', 'txt' => 'text/plain', 'rtx' => 'text/richtext', 'rtf' => 'text/rtf', 'sgm' => 'text/sgml', 'sgml' => 'text/sgml', 'tsv' => 'text/tab-separated-values', 'wml' => 'text/vnd.wap.wml', 'wmls' => 'text/vnd.wap.wmlscript', 'etx' => 'text/x-setext', 'vcf' => 'text/x-vcard', 'xsl' => 'text/xml', 'mp4' => 'video/mp4', 'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mpg' => 'video/mpeg', 'mov' => 'video/quicktime', 'qt' => 'video/quicktime', 'mxu' => 'video/vnd.mpegurl', 'asf' => 'video/x-ms-asf', 'asx' => 'video/x-ms-asf', 'wm' => 'video/x-ms-wm', 'wmv' => 'video/x-ms-wmv', 'wmx' => 'video/x-ms-wmx', 'wvx' => 'video/x-ms-wvx', 'avi' => 'video/x-msvideo', 'movie' => 'video/x-sgi-movie', 'ice' => 'x-conference/x-cooltalk', );
69
70    /**
71     * Set (or overwrite existing) parameters by passing an array of new parameters.
72     *
73     * @access public
74     * @param  array    $params     Array of parameters (key => val pairs).
75     */
76    function setParam($params)
77    {
78        $app =& App::getInstance();
79
80        if (isset($params) && is_array($params)) {
81
82            // Enforce valid upload_path parameter.
83            if (isset($params['upload_path'])) {
84                $params['upload_path'] = realpath($params['upload_path']);
85                // Source must be directory.
86                if (!is_dir($params['upload_path'])) {
87                    $app->logMsg(sprintf('Attempting to auto-create upload directory: %s', $params['upload_path']), LOG_NOTICE, __FILE__, __LINE__);
88                    if (phpversion() > '5') {
89                        // Recursive.
90                        mkdir($params['upload_path'], isset($params['dest_dir_perms']) ? $params['dest_dir_perms'] : $this->getParam('dest_dir_perms'), true);
91                    } else {
92                        mkdir($params['upload_path'], isset($params['dest_dir_perms']) ? $params['dest_dir_perms'] : $this->getParam('dest_dir_perms'));
93                    }
94                    if (!is_dir($params['upload_path'])) {
95                        $app->logMsg(sprintf('Upload directory invalid: %s', $params['upload_path']), LOG_ERR, __FILE__, __LINE__);
96                        trigger_error(sprintf('Upload directory invalid: %s', $params['upload_path']), E_USER_ERROR);
97                    }
98                }
99                // Source must be writable.
100                if (!is_writable($params['upload_path'])) {
101                    $app->logMsg(sprintf('Upload directory not writable: %s', $params['upload_path']), LOG_ERR, __FILE__, __LINE__);
102                    trigger_error(sprintf('Upload directory not writable: %s', $params['upload_path']), E_USER_ERROR);
103                }
104            }
105
106            // Merge new parameters with old overriding only those passed.
107            $this->_params = array_merge($this->_params, $params);
108        } else {
109            $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
110        }
111    }
112
113    /**
114     * Return the value of a parameter, if it exists.
115     *
116     * @access public
117     * @param string $param        Which parameter to return.
118     * @return mixed               Configured parameter value.
119     */
120    function getParam($param)
121    {
122        $app =& App::getInstance();
123   
124        if (isset($this->_params[$param])) {
125            return $this->_params[$param];
126        } else {
127            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
128            return null;
129        }
130    }
131
132    /**
133     * Process uploaded files. Processes files existing within the specified $_FILES['form_name'] array.
134     * It tests for errors, cleans the filename, optionally sets custom file names. It will process
135     * multiple files automatically if the file form element is an array (<input type="file" name="myfiles[]" />).
136     *
137     * @access  public
138     * @param   string  $form_name          The name of the form to process.
139     * @param   string  $custom_file_name   The new name of the file. An array of filenames in the case of multiple files.
140     * @return  mixed   Returns FALSE if a major error occurred preventing any file uploads.
141     *                  Returns an empty array if any minor errors occurred or no files were found.
142     *                  Returns a multidimensional array of filenames, sizes and extensions, if one-or-more files succeeded uploading.
143     *                  Note: this last option presents a problem in the case of when some files uploaded successfully, and some failed.
144     *                        In this case it is necessary to check the Upload::anyErrors method to discover if any did fail.
145     */
146    function process($form_name, $custom_file_name=null)
147    {
148        $app =& App::getInstance();
149
150        // Ensure we have a upload directory.
151        if (!$this->getParam('upload_path')) {
152            $app->logMsg(sprintf('Upload directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
153            $this->_raiseMsg(_("There was a problem with the file upload. Please try again later."), MSG_ERR, __FILE__, __LINE__);
154            return false;
155        }
156
157        // Ensure the file form element specified actually exists.
158        if (!isset($_FILES[$form_name])) {
159            $app->logMsg(sprintf('Form element %s does not exist.', $form_name), LOG_ERR, __FILE__, __LINE__);
160            $this->_raiseMsg(_("There was a problem with the file upload. Please try again later."), MSG_ERR, __FILE__, __LINE__);
161            return false;
162        }
163
164        if (is_array($_FILES[$form_name]['name'])) {
165            $files = $_FILES[$form_name];
166        } else {
167            // Convert variables to single-cell array so it will loop.
168            $files = array(
169                'name'      => array($_FILES[$form_name]['name']),
170                'type'      => array($_FILES[$form_name]['type']),
171                'tmp_name'  => array($_FILES[$form_name]['tmp_name']),
172                'error'     => array($_FILES[$form_name]['error']),
173                'size'      => array($_FILES[$form_name]['size']),
174            );
175        }
176
177        // To keep this script running even if user tries to stop browser.
178        ignore_user_abort(true);
179        ini_set('max_execution_time', 300);
180        ini_set('max_input_time', 300);
181
182        $new_file_names = array();
183
184        $num = sizeof($files['name']);
185        for ($i=0; $i<$num; $i++) {
186            $file_path_name = '';
187
188            if ('' == trim($files['name'][$i])) {
189                // User may not have attached a file.
190                continue;
191            }
192
193            // Determine final file name.
194            if ($num == 1) {
195                // Single upload.
196                if (isset($custom_file_name)) {
197                    if (is_array($custom_file_name) && sizeof($custom_file_name) == 1) {
198                        // Is an array, but just one value. Pull it out.
199                        $custom_file_name = current($custom_file_name);
200                        $this->_raiseMsg(sprintf(_("The file %s has been renamed to %s."), $files['name'][$i], $file_name), MSG_NOTICE, __FILE__, __LINE__);
201                        $app->logMsg(sprintf('Using custom file name: %s', $file_name), LOG_DEBUG, __FILE__, __LINE__);
202                    } else if (!is_array($custom_file_name) && '' != $custom_file_name) {
203                        // Valid custom file name.
204                        $file_name = $custom_file_name;
205                        $this->_raiseMsg(sprintf(_("The file %s has been renamed to %s."), $files['name'][$i], $file_name), MSG_NOTICE, __FILE__, __LINE__);
206                        $app->logMsg(sprintf('Using custom file name: %s', $file_name), LOG_DEBUG, __FILE__, __LINE__);
207                    } else {
208                        // Invalid custom file name provided. Use uploaded file name.
209                        $file_name = $files['name'][$i];
210                        $app->logMsg(sprintf('Custom filename invalid! Using uploaded file name: %s', $file_name), LOG_WARNING, __FILE__, __LINE__);
211                    }
212                } else {
213                    // Normal case. Use uploaded file name.
214                    $file_name = $files['name'][$i];
215                    $app->logMsg(sprintf('Using uploaded file name: %s', $file_name), LOG_DEBUG, __FILE__, __LINE__);
216                }
217            } else {
218                // Multiple upload. Final file names must be array.
219                if (isset($custom_file_name) && is_array($custom_file_name) && '' != $custom_file_name[$i]) {
220                    // Valid custom file name.
221                    $file_name = $custom_file_name[$i];
222                    $this->_raiseMsg(sprintf(_("The file %s has been renamed to %s."), $files['name'][$i], $file_name), MSG_NOTICE, __FILE__, __LINE__);
223                    $app->logMsg(sprintf('Using custom file name: %s', $file_name), LOG_DEBUG, __FILE__, __LINE__);
224                } else {
225                    // Invalid custom file name provided. Use uploaded file name.
226                    $file_name = $files['name'][$i];
227                    $app->logMsg(sprintf('Using uploaded file name: %s', $file_name), LOG_DEBUG, __FILE__, __LINE__);
228                }
229            }
230
231            // Check The php upload error messages.
232            if (UPLOAD_ERR_INI_SIZE === $files['error'][$i]) { // Error code 1
233                $this->_raiseMsg(sprintf(_("The file %s failed uploading: it exceeds the maximum allowed upload file size of %s."), $file_name, ini_get('upload_max_filesize')), MSG_ERR, __FILE__, __LINE__);
234                $app->logMsg(sprintf('The file %s failed uploading with PHP error UPLOAD_ERR_INI_SIZE (currently %s).', $file_name, ini_get('upload_max_filesize')), LOG_ERR, __FILE__, __LINE__);
235                $this->errors[] = array('filename' => $file_name, 'errortype' => UPLOAD_ERR_INI_SIZE);
236                continue;
237            }
238            if (UPLOAD_ERR_FORM_SIZE === $files['error'][$i]) { // Error code 2
239                $this->_raiseMsg(sprintf(_("The file %s failed uploading: it exceeds the maximum allowed upload file size of %s."), $file_name, $_POST['MAX_FILE_SIZE']), MSG_ERR, __FILE__, __LINE__);
240                $app->logMsg(sprintf('The file %s failed uploading with PHP error UPLOAD_ERR_FORM_SIZE (currently %s).', $file_name, $_POST['MAX_FILE_SIZE']), LOG_ERR, __FILE__, __LINE__);
241                $this->errors[] = array('filename' => $file_name, 'errortype' => UPLOAD_ERR_FORM_SIZE);
242                continue;
243            }
244            if (UPLOAD_ERR_PARTIAL === $files['error'][$i]) { // Error code 3
245                $this->_raiseMsg(sprintf(_("The file %s failed uploading: it was only partially uploaded."), $file_name), MSG_ERR, __FILE__, __LINE__);
246                $app->logMsg(sprintf('The file %s failed uploading with PHP error UPLOAD_ERR_PARTIAL.', $file_name), LOG_ERR, __FILE__, __LINE__);
247                $this->errors[] = array('filename' => $file_name, 'errortype' => UPLOAD_ERR_PARTIAL);
248                continue;
249            }
250            if (UPLOAD_ERR_NO_FILE === $files['error'][$i]) { // Error code 4
251                $this->_raiseMsg(sprintf(_("The file %s failed uploading: no file was uploaded."), $file_name), MSG_ERR, __FILE__, __LINE__);
252                $app->logMsg(sprintf('The file %s failed uploading with PHP error UPLOAD_ERR_NO_FILE.', $file_name), LOG_ERR, __FILE__, __LINE__);
253                $this->errors[] = array('filename' => $file_name, 'errortype' => UPLOAD_ERR_NO_FILE);
254                continue;
255            }
256            if (UPLOAD_ERR_NO_TMP_DIR === $files['error'][$i]) { // Error code 6
257                $this->_raiseMsg(sprintf(_("The file %s failed uploading: temporary upload directory missing."), $file_name), MSG_ERR, __FILE__, __LINE__);
258                $app->logMsg(sprintf('The file %s failed uploading with PHP error UPLOAD_ERR_NO_TMP_DIR.', $file_name), LOG_ERR, __FILE__, __LINE__);
259                $this->errors[] = array('filename' => $file_name, 'errortype' => UPLOAD_ERR_NO_TMP_DIR);
260                continue;
261            }
262
263            // Check to be sure it's an uploaded file.
264            if (!is_uploaded_file($files['tmp_name'][$i])) {
265                $this->_raiseMsg(sprintf(_("The file %s failed uploading."), $file_name), MSG_ERR, __FILE__, __LINE__);
266                $app->logMsg(sprintf('The file %s failed is_uploaded_file.', $file_name), LOG_ERR, __FILE__, __LINE__);
267                $this->errors[] = array('filename' => $file_name, 'errortype' => UPLOAD_USER_ERR_NOT_UPLOADED_FILE);
268                continue;
269            }
270
271            // Check to be sure the file is not empty.
272            if ($files['size'][$i] <= 0) {
273                $this->_raiseMsg(sprintf(_("The file %s failed uploading: it contains zero bytes."), $file_name), MSG_ERR, __FILE__, __LINE__);
274                $app->logMsg(sprintf('The uploaded file %s contains zero bytes.', $file_name), LOG_ERR, __FILE__, __LINE__);
275                $this->errors[] = array('filename' => $file_name, 'errortype' => UPLOAD_USER_ERR_EMPTY_FILE);
276                continue;
277            }
278
279            // Check to be sure the file has a valid file name extension.
280            if (!in_array(mb_strtolower($this->getFilenameExtension($file_name)), $this->getParam('valid_file_extensions'))) {
281                /// TODO: Add option to allow any extention to be uploaded.
282                $this->_raiseMsg(sprintf(_("The file %s failed uploading: it is an unrecognized type. Files must have one of the following file name extensions: %s."), $file_name, join(', ', $this->getParam('valid_file_extensions'))), MSG_ERR, __FILE__, __LINE__);
283                $app->logMsg(sprintf('The uploaded file %s has an unrecognized file name extension.', $file_name), LOG_WARNING, __FILE__, __LINE__);
284                $this->errors[] = array('filename' => $file_name, 'errortype' => UPLOAD_USER_ERR_INVALID_EXTENSION);
285                continue;
286            }
287
288            // Check to be sure the file has a unique file name.
289            if (!$this->getParam('allow_overwriting') && $this->exists($file_name)) {
290                $this->_raiseMsg(sprintf(_("The file %s failed uploading: a file with that name already exists."), $file_name), MSG_ERR, __FILE__, __LINE__);
291                $app->logMsg(sprintf('The uploaded file %s does not have a unique filename.', $file_name), LOG_WARNING, __FILE__, __LINE__);
292                $this->errors[] = array('filename' => $file_name, 'errortype' => UPLOAD_USER_ERR_NOT_UNIQUE);
293                continue;
294            }
295
296            // If the file name has no extension, use the mime-type to choose one.
297            if (!preg_match('/\.[^.]{1,5}$/', $file_name) && function_exists('mime_content_type')) {
298                // TODO: will this run if an extention is filtered by 'valid_file_extensions'?
299                if ($ext = array_search(mime_content_type($files['tmp_name'][$i]), $this->mime_extension_map)) {
300                    $file_name .= ".$ext";
301                }
302            }
303
304            // Clean the file name of bad characters.
305            $file_name = $this->cleanFileName($file_name);
306
307            // FINAL path and file name, lowercase extension.
308            $file_extension = mb_strtolower($this->getFilenameExtension($file_name));
309            $file_name = sprintf('%s.%s', mb_substr($file_name, 0, mb_strrpos($file_name, '.')), $file_extension);
310            $file_path_name = sprintf('%s/%s', $this->getParam('upload_path'), $file_name);
311
312            // Move the file to the final place.
313            if (move_uploaded_file($files['tmp_name'][$i], $file_path_name)) {
314                chmod($file_path_name, $this->getParam('dest_file_perms'));
315                $app->logMsg(sprintf('File uploaded: %s', $file_path_name), LOG_INFO, __FILE__, __LINE__);
316                $this->_raiseMsg(sprintf(_("The file %s uploaded successfully."), $file_name), MSG_SUCCESS, __FILE__, __LINE__);
317                if (!isset($custom_file_name) && $files['name'][$i] != $file_name) {
318                    // Notify user if uploaded file name was modified (unless a custom file name will be used anyways).
319                    $this->_raiseMsg(sprintf(_("The file %s was renamed to %s."), $files['name'][$i], $file_name), MSG_NOTICE, __FILE__, __LINE__);
320                }
321                $new_file_names[] = array(
322                    'name' => $file_name,
323                    'mime' => $files['type'][$i],
324                    'size' => filesize($file_path_name),
325                    'extension' => $file_extension,
326                    'original_index' => $i,
327                    'original_name' => $files['name'][$i],
328                );
329            } else {
330                $this->_raiseMsg(sprintf(_("The file %s failed uploading."), $file_name), MSG_ERR, __FILE__, __LINE__);
331                $app->logMsg(sprintf('Moving file failed: %s -> %s', $files['tmp_name'][$i], $file_path_name), LOG_ALERT, __FILE__, __LINE__);
332                $this->errors[] = array('filename' => $file_name, 'errortype' => UPLOAD_USER_ERR_MOVE_FAILED);
333            }
334
335            // Check file extension with browsers interpretation of file type.
336            if (isset($this->mime_extension_map[$file_extension]) && $files['type'][$i] != $this->mime_extension_map[$file_extension]) {
337                $app->logMsg(sprintf('File extension (%s) does not match mime type (%s).', $file_extension, $files['type'][$i]), LOG_NOTICE, __FILE__, __LINE__);
338            }
339        }
340
341        // Return names of files uploaded (or empty array when none processed).
342        return $new_file_names;
343    }
344
345    /**
346     * Remove file within upload path.
347     *
348     * @access  public
349     * @param   string  $file_name  A name of a file.
350     * @param   bool    $use_glob   Set true to use glob to find the filename (using $file_name as a pattern)
351     * @return  bool                Success of operation.
352     */
353    function deleteFile($file_name, $use_glob=false)
354    {
355        $app =& App::getInstance();
356
357        // Ensure we have a upload directory.
358        if (!$this->getParam('upload_path')) {
359            $app->logMsg(sprintf('Upload directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
360            return false;
361        }
362
363        $file_path_name = $this->getParam('upload_path') . '/' . ($use_glob ? $this->getFilenameGlob($file_name) : $file_name);
364
365        if (!is_file($file_path_name)) {
366            $app->logMsg(sprintf('Error deleting nonexistent file: %s', $file_path_name), LOG_ERR, __FILE__, __LINE__);
367            return false;
368        } else if (unlink($file_path_name)) {
369            $app->logMsg(sprintf('Deleted file: %s', $file_path_name), LOG_INFO, __FILE__, __LINE__);
370            return true;
371        } else {
372            $this->_raiseMsg(sprintf(_("The file %s could not be deleted."), $file_name), MSG_ERR, __FILE__, __LINE__);
373            $app->logMsg(sprintf('Failed deleting file: %s', $file_path_name), LOG_ERR, __FILE__, __LINE__);
374            return false;
375        }
376    }
377
378    /**
379     * Renames a file within the upload path.
380     *
381     * @access  public
382     * @param   string  $old_name   The currently existing file name.
383     * @param   string  $new_name   The new name for this file.
384     * @return  bool                Success of operation.
385     */
386    function moveFile($old_name, $new_name)
387    {
388        $app =& App::getInstance();
389
390        // Ensure we have an upload directory.
391        if (!$this->getParam('upload_path')) {
392            $app->logMsg(sprintf('Upload directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
393            return false;
394        }
395
396        $old_file_path_name = $this->getParam('upload_path') . '/' . $old_name;
397        $new_file_path_name = $this->getParam('upload_path') . '/' . $new_name;
398        if (file_exists($old_file_path_name)) {
399            if (rename($old_file_path_name, $new_file_path_name)) {
400                $this->_raiseMsg(sprintf(_("The file %s has been renamed to %s."), basename($old_file_path_name), basename($new_file_path_name)), MSG_NOTICE, __FILE__, __LINE__);
401                $app->logMsg(sprintf('File renamed from %s to %s', $old_file_path_name, $new_file_path_name), LOG_DEBUG, __FILE__, __LINE__);
402            } else {
403                $this->_raiseMsg(sprintf(_("Error renaming file to %s"), $new_file_path_name), MSG_WARNING, __FILE__, __LINE__);
404                $app->logMsg(sprintf('Error renaming file to %s', $new_file_path_name), LOG_WARNING, __FILE__, __LINE__);
405                return false;
406            }
407        } else {
408            $this->_raiseMsg(sprintf(_("Couldn't rename nonexistent file %s."), $old_name), MSG_WARNING, __FILE__, __LINE__);
409            $app->logMsg(sprintf('Error renaming nonexistent file: %s', $old_file_path_name), LOG_WARNING, __FILE__, __LINE__);
410            return false;
411        }
412    }
413
414    /**
415     * Tests if a file exists within the current upload_path.
416     *
417     * @access  public
418     * @param   string  $file_name  A name of a file.
419     * @return  bool                Existence of file.
420     */
421    function exists($file_name)
422    {
423        $app =& App::getInstance();
424
425        // Ensure we have a upload directory.
426        if (!$this->getParam('upload_path')) {
427            $app->logMsg(sprintf('Upload directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
428            return false;
429        }
430
431        return file_exists($this->getParam('upload_path') . '/' . $file_name);
432    }
433
434    /**
435     * Get filename by glob pattern. Searches a directory for an image that matches the
436     * specified glob pattern and returns the filename of the first file found.
437     *
438     * @access  public
439     * @param   string  $pattern   Pattern to match filename.
440     * @return  string filename on success, empty string on failure.
441     * @author  Quinn Comendant <quinn@strangecode.com>
442     * @since   15 Nov 2005 20:55:22
443     */
444    function getFilenameGlob($pattern)
445    {
446        $file_list = glob(sprintf('%s/%s', $this->getParam('upload_path'), $pattern));
447        if (isset($file_list[0])) {
448            return basename($file_list[0]);
449        } else {
450            return '';
451        }
452    }
453
454    /**
455     * Returns an array of file names that failed uploading.
456     *
457     * @access  public
458     * @return  array   List of file names.
459     */
460    function getErrors()
461    {
462        return $this->errors;
463    }
464
465    /**
466     * Determines if any errors occurred while calling the Upload::process method.
467     *
468     * @access  public
469     */
470    function anyErrors()
471    {
472        return sizeof($this->errors) > 0;
473    }
474
475    /**
476     * Removes unsafe characters from file name.
477     *
478     * @access  public
479     * @param   string  $file_name  A name of a file.
480     * @return  string              The same name, but cleaned.
481     */
482    function cleanFileName($file_name)
483    {
484        $bad  = 'áéíóúàÚìòùÀëïöÌÁÉÍÓÚÀÈÌÒÙÄËÏÖÜâêîÎûÂÊÎÔÛñçÇ@';
485        $good = 'aeiouaeiouaeiouAEIOUAEIOUAEIOUaeiouAEIOUncCa';
486        $file_name = trim($file_name);
487        $file_name = strtr($file_name, $bad, $good);
488        $file_name = preg_replace('/[^-\w.,~_=+()]+/i', '_', $file_name);
489        $file_name = mb_substr($file_name, 0, 250);
490        return $file_name;
491    }
492
493
494    /**
495     * Returns the extension of a file name, or an empty string if non exists.
496     *
497     * @access  public
498     * @param   string  $file_name  A name of a file, with extension after a dot.
499     * @return  string              The value found after the dot
500     */
501    function getFilenameExtension($file_name)
502    {
503        preg_match('/.*?\.(\w+)$/i', trim($file_name), $ext);
504        return isset($ext[1]) ? $ext[1] : '';
505    }
506
507    /**
508     * An alias for $app->raiseMsg that only sends messages configured by display_messages.
509     *
510     * @access public
511     *
512     * @param string $message The text description of the message.
513     * @param int    $type    The type of message: MSG_NOTICE,
514     *                        MSG_SUCCESS, MSG_WARNING, or MSG_ERR.
515     * @param string $file    __FILE__.
516     * @param string $line    __LINE__.
517     */
518    function _raiseMsg($message, $type, $file, $line)
519    {
520        $app =& App::getInstance();
521
522        if ($this->getParam('display_messages') === true || (is_int($this->getParam('display_messages')) && $this->getParam('display_messages') & $type > 0)) {
523            $app->raiseMsg($message, $type, $file, $line);
524        }
525    }
526}
527
528?>
Note: See TracBrowser for help on using the repository browser.