source: sourcecode/system/libraries/Upload.php @ 1

Last change on this file since 1 was 1, checked in by dungnv, 11 years ago
File size: 26.9 KB
Line 
1<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.1.6 or newer
6 *
7 * @package             CodeIgniter
8 * @author              ExpressionEngine Dev Team
9 * @copyright   Copyright (c) 2008 - 2011, EllisLab, Inc.
10 * @license             http://codeigniter.com/user_guide/license.html
11 * @link                http://codeigniter.com
12 * @since               Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * File Uploading Class
20 *
21 * @package             CodeIgniter
22 * @subpackage  Libraries
23 * @category    Uploads
24 * @author              ExpressionEngine Dev Team
25 * @link                http://codeigniter.com/user_guide/libraries/file_uploading.html
26 */
27class CI_Upload {
28
29        public $max_size                                = 0;
30        public $max_width                               = 0;
31        public $max_height                              = 0;
32        public $max_filename                    = 0;
33        public $allowed_types                   = "";
34        public $file_temp                               = "";
35        public $file_name                               = "";
36        public $orig_name                               = "";
37        public $file_type                               = "";
38        public $file_size                               = "";
39        public $file_ext                                = "";
40        public $upload_path                             = "";
41        public $overwrite                               = FALSE;
42        public $encrypt_name                    = FALSE;
43        public $is_image                                = FALSE;
44        public $image_width                             = '';
45        public $image_height                    = '';
46        public $image_type                              = '';
47        public $image_size_str                  = '';
48        public $error_msg                               = array();
49        public $mimes                                   = array();
50        public $remove_spaces                   = TRUE;
51        public $xss_clean                               = FALSE;
52        public $temp_prefix                             = "temp_file_";
53        public $client_name                             = '';
54
55        protected $_file_name_override  = '';
56
57        /**
58         * Constructor
59         *
60         * @access      public
61         */
62        public function __construct($props = array())
63        {
64                if (count($props) > 0)
65                {
66                        $this->initialize($props);
67                }
68
69                log_message('debug', "Upload Class Initialized");
70        }
71
72        // --------------------------------------------------------------------
73
74        /**
75         * Initialize preferences
76         *
77         * @param       array
78         * @return      void
79         */
80        public function initialize($config = array())
81        {
82                $defaults = array(
83                                                        'max_size'                      => 0,
84                                                        'max_width'                     => 0,
85                                                        'max_height'            => 0,
86                                                        'max_filename'          => 0,
87                                                        'allowed_types'         => "",
88                                                        'file_temp'                     => "",
89                                                        'file_name'                     => "",
90                                                        'orig_name'                     => "",
91                                                        'file_type'                     => "",
92                                                        'file_size'                     => "",
93                                                        'file_ext'                      => "",
94                                                        'upload_path'           => "",
95                                                        'overwrite'                     => FALSE,
96                                                        'encrypt_name'          => FALSE,
97                                                        'is_image'                      => FALSE,
98                                                        'image_width'           => '',
99                                                        'image_height'          => '',
100                                                        'image_type'            => '',
101                                                        'image_size_str'        => '',
102                                                        'error_msg'                     => array(),
103                                                        'mimes'                         => array(),
104                                                        'remove_spaces'         => TRUE,
105                                                        'xss_clean'                     => FALSE,
106                                                        'temp_prefix'           => "temp_file_",
107                                                        'client_name'           => ''
108                                                );
109
110
111                foreach ($defaults as $key => $val)
112                {
113                        if (isset($config[$key]))
114                        {
115                                $method = 'set_'.$key;
116                                if (method_exists($this, $method))
117                                {
118                                        $this->$method($config[$key]);
119                                }
120                                else
121                                {
122                                        $this->$key = $config[$key];
123                                }
124                        }
125                        else
126                        {
127                                $this->$key = $val;
128                        }
129                }
130
131                // if a file_name was provided in the config, use it instead of the user input
132                // supplied file name for all uploads until initialized again
133                $this->_file_name_override = $this->file_name;
134        }
135
136        // --------------------------------------------------------------------
137
138        /**
139         * Perform the file upload
140         *
141         * @return      bool
142         */
143        public function do_upload($field = 'userfile')
144        {
145
146        // Is $_FILES[$field] set? If not, no reason to continue.
147                if ( ! isset($_FILES[$field]))
148                {
149                        $this->set_error('upload_no_file_selected');
150                        return FALSE;
151                }
152
153                // Is the upload path valid?
154                if ( ! $this->validate_upload_path())
155                {
156                        // errors will already be set by validate_upload_path() so just return FALSE
157                        return FALSE;
158                }
159
160                // Was the file able to be uploaded? If not, determine the reason why.
161                if ( ! is_uploaded_file($_FILES[$field]['tmp_name']))
162                {
163                        $error = ( ! isset($_FILES[$field]['error'])) ? 4 : $_FILES[$field]['error'];
164
165                        switch($error)
166                        {
167                                case 1: // UPLOAD_ERR_INI_SIZE
168                                        $this->set_error('upload_file_exceeds_limit');
169                                        break;
170                                case 2: // UPLOAD_ERR_FORM_SIZE
171                                        $this->set_error('upload_file_exceeds_form_limit');
172                                        break;
173                                case 3: // UPLOAD_ERR_PARTIAL
174                                        $this->set_error('upload_file_partial');
175                                        break;
176                                case 4: // UPLOAD_ERR_NO_FILE
177                                        $this->set_error('upload_no_file_selected');
178                                        break;
179                                case 6: // UPLOAD_ERR_NO_TMP_DIR
180                                        $this->set_error('upload_no_temp_directory');
181                                        break;
182                                case 7: // UPLOAD_ERR_CANT_WRITE
183                                        $this->set_error('upload_unable_to_write_file');
184                                        break;
185                                case 8: // UPLOAD_ERR_EXTENSION
186                                        $this->set_error('upload_stopped_by_extension');
187                                        break;
188                                default :   $this->set_error('upload_no_file_selected');
189                                        break;
190                        }
191
192                        return FALSE;
193                }
194
195
196                // Set the uploaded data as class variables
197                $this->file_temp = $_FILES[$field]['tmp_name'];
198                $this->file_size = $_FILES[$field]['size'];
199                $this->_file_mime_type($_FILES[$field]);
200                $this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $this->file_type);
201                $this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
202                $this->file_name = $this->_prep_filename($_FILES[$field]['name']);
203                $this->file_ext  = $this->get_extension($this->file_name);
204                $this->client_name = $this->file_name;
205
206                // Is the file type allowed to be uploaded?
207                if ( ! $this->is_allowed_filetype())
208                {
209                        $this->set_error('upload_invalid_filetype');
210                        return FALSE;
211                }
212
213                // if we're overriding, let's now make sure the new name and type is allowed
214                if ($this->_file_name_override != '')
215                {
216                        $this->file_name = $this->_prep_filename($this->_file_name_override);
217
218                        // If no extension was provided in the file_name config item, use the uploaded one
219                        if (strpos($this->_file_name_override, '.') === FALSE)
220                        {
221                                $this->file_name .= $this->file_ext;
222                        }
223
224                        // An extension was provided, lets have it!
225                        else
226                        {
227                                $this->file_ext  = $this->get_extension($this->_file_name_override);
228                        }
229
230                        if ( ! $this->is_allowed_filetype(TRUE))
231                        {
232                                $this->set_error('upload_invalid_filetype');
233                                return FALSE;
234                        }
235                }
236
237                // Convert the file size to kilobytes
238                if ($this->file_size > 0)
239                {
240                        $this->file_size = round($this->file_size/1024, 2);
241                }
242
243                // Is the file size within the allowed maximum?
244                if ( ! $this->is_allowed_filesize())
245                {
246                        $this->set_error('upload_invalid_filesize');
247                        return FALSE;
248                }
249
250                // Are the image dimensions within the allowed size?
251                // Note: This can fail if the server has an open_basdir restriction.
252                if ( ! $this->is_allowed_dimensions())
253                {
254                        $this->set_error('upload_invalid_dimensions');
255                        return FALSE;
256                }
257
258                // Sanitize the file name for security
259                $this->file_name = $this->clean_file_name($this->file_name);
260
261                // Truncate the file name if it's too long
262                if ($this->max_filename > 0)
263                {
264                        $this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
265                }
266
267                // Remove white spaces in the name
268                if ($this->remove_spaces == TRUE)
269                {
270                        $this->file_name = preg_replace("/\s+/", "_", $this->file_name);
271                }
272
273                /*
274                 * Validate the file name
275                 * This function appends an number onto the end of
276                 * the file if one with the same name already exists.
277                 * If it returns false there was a problem.
278                 */
279                $this->orig_name = $this->file_name;
280
281                if ($this->overwrite == FALSE)
282                {
283                        $this->file_name = $this->set_filename($this->upload_path, $this->file_name);
284
285                        if ($this->file_name === FALSE)
286                        {
287                                return FALSE;
288                        }
289                }
290
291                /*
292                 * Run the file through the XSS hacking filter
293                 * This helps prevent malicious code from being
294                 * embedded within a file.  Scripts can easily
295                 * be disguised as images or other file types.
296                 */
297                if ($this->xss_clean)
298                {
299                        if ($this->do_xss_clean() === FALSE)
300                        {
301                                $this->set_error('upload_unable_to_write_file');
302                                return FALSE;
303                        }
304                }
305
306                /*
307                 * Move the file to the final destination
308                 * To deal with different server configurations
309                 * we'll attempt to use copy() first.  If that fails
310                 * we'll use move_uploaded_file().  One of the two should
311                 * reliably work in most environments
312                 */
313                if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name))
314                {
315                        if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
316                        {
317                                $this->set_error('upload_destination_error');
318                                return FALSE;
319                        }
320                }
321
322                /*
323                 * Set the finalized image dimensions
324                 * This sets the image width/height (assuming the
325                 * file was an image).  We use this information
326                 * in the "data" function.
327                 */
328                $this->set_image_properties($this->upload_path.$this->file_name);
329
330                return TRUE;
331        }
332
333        // --------------------------------------------------------------------
334
335        /**
336         * Finalized Data Array
337         *
338         * Returns an associative array containing all of the information
339         * related to the upload, allowing the developer easy access in one array.
340         *
341         * @return      array
342         */
343        public function data()
344        {
345                return array (
346                                                'file_name'                     => $this->file_name,
347                                                'file_type'                     => $this->file_type,
348                                                'file_path'                     => $this->upload_path,
349                                                'full_path'                     => $this->upload_path.$this->file_name,
350                                                'raw_name'                      => str_replace($this->file_ext, '', $this->file_name),
351                                                'orig_name'                     => $this->orig_name,
352                                                'client_name'           => $this->client_name,
353                                                'file_ext'                      => $this->file_ext,
354                                                'file_size'                     => $this->file_size,
355                                                'is_image'                      => $this->is_image(),
356                                                'image_width'           => $this->image_width,
357                                                'image_height'          => $this->image_height,
358                                                'image_type'            => $this->image_type,
359                                                'image_size_str'        => $this->image_size_str,
360                                        );
361        }
362
363        // --------------------------------------------------------------------
364
365        /**
366         * Set Upload Path
367         *
368         * @param       string
369         * @return      void
370         */
371        public function set_upload_path($path)
372        {
373                // Make sure it has a trailing slash
374                $this->upload_path = rtrim($path, '/').'/';
375        }
376
377        // --------------------------------------------------------------------
378
379        /**
380         * Set the file name
381         *
382         * This function takes a filename/path as input and looks for the
383         * existence of a file with the same name. If found, it will append a
384         * number to the end of the filename to avoid overwriting a pre-existing file.
385         *
386         * @param       string
387         * @param       string
388         * @return      string
389         */
390        public function set_filename($path, $filename)
391        {
392                if ($this->encrypt_name == TRUE)
393                {
394                        mt_srand();
395                        $filename = md5(uniqid(mt_rand())).$this->file_ext;
396                }
397
398                if ( ! file_exists($path.$filename))
399                {
400                        return $filename;
401                }
402
403                $filename = str_replace($this->file_ext, '', $filename);
404
405                $new_filename = '';
406                for ($i = 1; $i < 100; $i++)
407                {
408                        if ( ! file_exists($path.$filename.$i.$this->file_ext))
409                        {
410                                $new_filename = $filename.$i.$this->file_ext;
411                                break;
412                        }
413                }
414
415                if ($new_filename == '')
416                {
417                        $this->set_error('upload_bad_filename');
418                        return FALSE;
419                }
420                else
421                {
422                        return $new_filename;
423                }
424        }
425
426        // --------------------------------------------------------------------
427
428        /**
429         * Set Maximum File Size
430         *
431         * @param       integer
432         * @return      void
433         */
434        public function set_max_filesize($n)
435        {
436                $this->max_size = ((int) $n < 0) ? 0: (int) $n;
437        }
438
439        // --------------------------------------------------------------------
440
441        /**
442         * Set Maximum File Name Length
443         *
444         * @param       integer
445         * @return      void
446         */
447        public function set_max_filename($n)
448        {
449                $this->max_filename = ((int) $n < 0) ? 0: (int) $n;
450        }
451
452        // --------------------------------------------------------------------
453
454        /**
455         * Set Maximum Image Width
456         *
457         * @param       integer
458         * @return      void
459         */
460        public function set_max_width($n)
461        {
462                $this->max_width = ((int) $n < 0) ? 0: (int) $n;
463        }
464
465        // --------------------------------------------------------------------
466
467        /**
468         * Set Maximum Image Height
469         *
470         * @param       integer
471         * @return      void
472         */
473        public function set_max_height($n)
474        {
475                $this->max_height = ((int) $n < 0) ? 0: (int) $n;
476        }
477
478        // --------------------------------------------------------------------
479
480        /**
481         * Set Allowed File Types
482         *
483         * @param       string
484         * @return      void
485         */
486        public function set_allowed_types($types)
487        {
488                if ( ! is_array($types) && $types == '*')
489                {
490                        $this->allowed_types = '*';
491                        return;
492                }
493                $this->allowed_types = explode('|', $types);
494        }
495
496        // --------------------------------------------------------------------
497
498        /**
499         * Set Image Properties
500         *
501         * Uses GD to determine the width/height/type of image
502         *
503         * @param       string
504         * @return      void
505         */
506        public function set_image_properties($path = '')
507        {
508                if ( ! $this->is_image())
509                {
510                        return;
511                }
512
513                if (function_exists('getimagesize'))
514                {
515                        if (FALSE !== ($D = @getimagesize($path)))
516                        {
517                                $types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
518
519                                $this->image_width              = $D['0'];
520                                $this->image_height             = $D['1'];
521                                $this->image_type               = ( ! isset($types[$D['2']])) ? 'unknown' : $types[$D['2']];
522                                $this->image_size_str   = $D['3'];  // string containing height and width
523                        }
524                }
525        }
526
527        // --------------------------------------------------------------------
528
529        /**
530         * Set XSS Clean
531         *
532         * Enables the XSS flag so that the file that was uploaded
533         * will be run through the XSS filter.
534         *
535         * @param       bool
536         * @return      void
537         */
538        public function set_xss_clean($flag = FALSE)
539        {
540                $this->xss_clean = ($flag == TRUE) ? TRUE : FALSE;
541        }
542
543        // --------------------------------------------------------------------
544
545        /**
546         * Validate the image
547         *
548         * @return      bool
549         */
550        public function is_image()
551        {
552                // IE will sometimes return odd mime-types during upload, so here we just standardize all
553                // jpegs or pngs to the same file type.
554
555                $png_mimes  = array('image/x-png');
556                $jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg');
557
558                if (in_array($this->file_type, $png_mimes))
559                {
560                        $this->file_type = 'image/png';
561                }
562
563                if (in_array($this->file_type, $jpeg_mimes))
564                {
565                        $this->file_type = 'image/jpeg';
566                }
567
568                $img_mimes = array(
569                                                        'image/gif',
570                                                        'image/jpeg',
571                                                        'image/png',
572                                                );
573
574                return (in_array($this->file_type, $img_mimes, TRUE)) ? TRUE : FALSE;
575        }
576
577        // --------------------------------------------------------------------
578
579        /**
580         * Verify that the filetype is allowed
581         *
582         * @return      bool
583         */
584        public function is_allowed_filetype($ignore_mime = FALSE)
585        {
586                if ($this->allowed_types == '*')
587                {
588                        return TRUE;
589                }
590
591                if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types))
592                {
593                        $this->set_error('upload_no_file_types');
594                        return FALSE;
595                }
596
597                $ext = strtolower(ltrim($this->file_ext, '.'));
598
599                if ( ! in_array($ext, $this->allowed_types))
600                {
601                        return FALSE;
602                }
603
604                // Images get some additional checks
605                $image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');
606
607                if (in_array($ext, $image_types))
608                {
609                        if (getimagesize($this->file_temp) === FALSE)
610                        {
611                                return FALSE;
612                        }
613                }
614
615                if ($ignore_mime === TRUE)
616                {
617                        return TRUE;
618                }
619
620                $mime = $this->mimes_types($ext);
621
622                if (is_array($mime))
623                {
624                        if (in_array($this->file_type, $mime, TRUE))
625                        {
626                                return TRUE;
627                        }
628                }
629                elseif ($mime == $this->file_type)
630                {
631                                return TRUE;
632                }
633
634                return FALSE;
635        }
636
637        // --------------------------------------------------------------------
638
639        /**
640         * Verify that the file is within the allowed size
641         *
642         * @return      bool
643         */
644        public function is_allowed_filesize()
645        {
646                if ($this->max_size != 0  AND  $this->file_size > $this->max_size)
647                {
648                        return FALSE;
649                }
650                else
651                {
652                        return TRUE;
653                }
654        }
655
656        // --------------------------------------------------------------------
657
658        /**
659         * Verify that the image is within the allowed width/height
660         *
661         * @return      bool
662         */
663        public function is_allowed_dimensions()
664        {
665                if ( ! $this->is_image())
666                {
667                        return TRUE;
668                }
669
670                if (function_exists('getimagesize'))
671                {
672                        $D = @getimagesize($this->file_temp);
673
674                        if ($this->max_width > 0 AND $D['0'] > $this->max_width)
675                        {
676                                return FALSE;
677                        }
678
679                        if ($this->max_height > 0 AND $D['1'] > $this->max_height)
680                        {
681                                return FALSE;
682                        }
683
684                        return TRUE;
685                }
686
687                return TRUE;
688        }
689
690        // --------------------------------------------------------------------
691
692        /**
693         * Validate Upload Path
694         *
695         * Verifies that it is a valid upload path with proper permissions.
696         *
697         *
698         * @return      bool
699         */
700        public function validate_upload_path()
701        {
702                if ($this->upload_path == '')
703                {
704                        $this->set_error('upload_no_filepath');
705                        return FALSE;
706                }
707
708                if (function_exists('realpath') AND @realpath($this->upload_path) !== FALSE)
709                {
710                        $this->upload_path = str_replace("\\", "/", realpath($this->upload_path));
711                }
712
713                if ( ! @is_dir($this->upload_path))
714                {
715                        $this->set_error('upload_no_filepath');
716                        return FALSE;
717                }
718
719                if ( ! is_really_writable($this->upload_path))
720                {
721                        $this->set_error('upload_not_writable');
722                        return FALSE;
723                }
724
725                $this->upload_path = preg_replace("/(.+?)\/*$/", "\\1/",  $this->upload_path);
726                return TRUE;
727        }
728
729        // --------------------------------------------------------------------
730
731        /**
732         * Extract the file extension
733         *
734         * @param       string
735         * @return      string
736         */
737        public function get_extension($filename)
738        {
739                $x = explode('.', $filename);
740                return '.'.end($x);
741        }
742
743        // --------------------------------------------------------------------
744
745        /**
746         * Clean the file name for security
747         *
748         * @param       string
749         * @return      string
750         */
751        public function clean_file_name($filename)
752        {
753                $bad = array(
754                                                "<!--",
755                                                "-->",
756                                                "'",
757                                                "<",
758                                                ">",
759                                                '"',
760                                                '&',
761                                                '$',
762                                                '=',
763                                                ';',
764                                                '?',
765                                                '/',
766                                                "%20",
767                                                "%22",
768                                                "%3c",          // <
769                                                "%253c",        // <
770                                                "%3e",          // >
771                                                "%0e",          // >
772                                                "%28",          // (
773                                                "%29",          // )
774                                                "%2528",        // (
775                                                "%26",          // &
776                                                "%24",          // $
777                                                "%3f",          // ?
778                                                "%3b",          // ;
779                                                "%3d"           // =
780                                        );
781
782                $filename = str_replace($bad, '', $filename);
783
784                return stripslashes($filename);
785        }
786
787        // --------------------------------------------------------------------
788
789        /**
790         * Limit the File Name Length
791         *
792         * @param       string
793         * @return      string
794         */
795        public function limit_filename_length($filename, $length)
796        {
797                if (strlen($filename) < $length)
798                {
799                        return $filename;
800                }
801
802                $ext = '';
803                if (strpos($filename, '.') !== FALSE)
804                {
805                        $parts          = explode('.', $filename);
806                        $ext            = '.'.array_pop($parts);
807                        $filename       = implode('.', $parts);
808                }
809
810                return substr($filename, 0, ($length - strlen($ext))).$ext;
811        }
812
813        // --------------------------------------------------------------------
814
815        /**
816         * Runs the file through the XSS clean function
817         *
818         * This prevents people from embedding malicious code in their files.
819         * I'm not sure that it won't negatively affect certain files in unexpected ways,
820         * but so far I haven't found that it causes trouble.
821         *
822         * @return      void
823         */
824        public function do_xss_clean()
825        {
826                $file = $this->file_temp;
827
828                if (filesize($file) == 0)
829                {
830                        return FALSE;
831                }
832
833                if (function_exists('memory_get_usage') && memory_get_usage() && ini_get('memory_limit') != '')
834                {
835                        $current = ini_get('memory_limit') * 1024 * 1024;
836
837                        // There was a bug/behavioural change in PHP 5.2, where numbers over one million get output
838                        // into scientific notation.  number_format() ensures this number is an integer
839                        // http://bugs.php.net/bug.php?id=43053
840
841                        $new_memory = number_format(ceil(filesize($file) + $current), 0, '.', '');
842
843                        ini_set('memory_limit', $new_memory); // When an integer is used, the value is measured in bytes. - PHP.net
844                }
845
846                // If the file being uploaded is an image, then we should have no problem with XSS attacks (in theory), but
847                // IE can be fooled into mime-type detecting a malformed image as an html file, thus executing an XSS attack on anyone
848                // using IE who looks at the image.  It does this by inspecting the first 255 bytes of an image.  To get around this
849                // CI will itself look at the first 255 bytes of an image to determine its relative safety.  This can save a lot of
850                // processor power and time if it is actually a clean image, as it will be in nearly all instances _except_ an
851                // attempted XSS attack.
852
853                if (function_exists('getimagesize') && @getimagesize($file) !== FALSE)
854                {
855                        if (($file = @fopen($file, 'rb')) === FALSE) // "b" to force binary
856                        {
857                                return FALSE; // Couldn't open the file, return FALSE
858                        }
859
860                        $opening_bytes = fread($file, 256);
861                        fclose($file);
862
863                        // These are known to throw IE into mime-type detection chaos
864                        // <a, <body, <head, <html, <img, <plaintext, <pre, <script, <table, <title
865                        // title is basically just in SVG, but we filter it anyhow
866
867                        if ( ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes))
868                        {
869                                return TRUE; // its an image, no "triggers" detected in the first 256 bytes, we're good
870                        }
871                        else
872                        {
873                                return FALSE;
874                        }
875                }
876
877                if (($data = @file_get_contents($file)) === FALSE)
878                {
879                        return FALSE;
880                }
881
882                $CI =& get_instance();
883                return $CI->security->xss_clean($data, TRUE);
884        }
885
886        // --------------------------------------------------------------------
887
888        /**
889         * Set an error message
890         *
891         * @param       string
892         * @return      void
893         */
894        public function set_error($msg)
895        {
896                $CI =& get_instance();
897                $CI->lang->load('upload');
898
899                if (is_array($msg))
900                {
901                        foreach ($msg as $val)
902                        {
903                                $msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);
904                                $this->error_msg[] = $msg;
905                                log_message('error', $msg);
906                        }
907                }
908                else
909                {
910                        $msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
911                        $this->error_msg[] = $msg;
912                        log_message('error', $msg);
913                }
914        }
915
916        // --------------------------------------------------------------------
917
918        /**
919         * Display the error message
920         *
921         * @param       string
922         * @param       string
923         * @return      string
924         */
925        public function display_errors($open = '<p>', $close = '</p>')
926        {
927                $str = '';
928                foreach ($this->error_msg as $val)
929                {
930                        $str .= $open.$val.$close;
931                }
932
933                return $str;
934        }
935
936        // --------------------------------------------------------------------
937
938        /**
939         * List of Mime Types
940         *
941         * This is a list of mime types.  We use it to validate
942         * the "allowed types" set by the developer
943         *
944         * @param       string
945         * @return      string
946         */
947        public function mimes_types($mime)
948        {
949                global $mimes;
950
951                if (count($this->mimes) == 0)
952                {
953                        if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
954                        {
955                                include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
956                        }
957                        elseif (is_file(APPPATH.'config/mimes.php'))
958                        {
959                                include(APPPATH.'config//mimes.php');
960                        }
961                        else
962                        {
963                                return FALSE;
964                        }
965
966                        $this->mimes = $mimes;
967                        unset($mimes);
968                }
969
970                return ( ! isset($this->mimes[$mime])) ? FALSE : $this->mimes[$mime];
971        }
972
973        // --------------------------------------------------------------------
974
975        /**
976         * Prep Filename
977         *
978         * Prevents possible script execution from Apache's handling of files multiple extensions
979         * http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext
980         *
981         * @param       string
982         * @return      string
983         */
984        protected function _prep_filename($filename)
985        {
986                if (strpos($filename, '.') === FALSE OR $this->allowed_types == '*')
987                {
988                        return $filename;
989                }
990
991                $parts          = explode('.', $filename);
992                $ext            = array_pop($parts);
993                $filename       = array_shift($parts);
994
995                foreach ($parts as $part)
996                {
997                        if ( ! in_array(strtolower($part), $this->allowed_types) OR $this->mimes_types(strtolower($part)) === FALSE)
998                        {
999                                $filename .= '.'.$part.'_';
1000                        }
1001                        else
1002                        {
1003                                $filename .= '.'.$part;
1004                        }
1005                }
1006
1007                $filename .= '.'.$ext;
1008
1009                return $filename;
1010        }
1011
1012        // --------------------------------------------------------------------
1013
1014        /**
1015         * File MIME type
1016         *
1017         * Detects the (actual) MIME type of the uploaded file, if possible.
1018         * The input array is expected to be $_FILES[$field]
1019         *
1020         * @param       array
1021         * @return      void
1022         */
1023        protected function _file_mime_type($file)
1024        {
1025                // We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii)
1026                $regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
1027
1028                /* Fileinfo extension - most reliable method
1029                 *
1030                 * Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the
1031                 * more convenient FILEINFO_MIME_TYPE flag doesn't exist.
1032                 */
1033                if (function_exists('finfo_file'))
1034                {
1035                        $finfo = finfo_open(FILEINFO_MIME);
1036                        if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
1037                        {
1038                                $mime = @finfo_file($finfo, $file['tmp_name']);
1039                                finfo_close($finfo);
1040
1041                                /* According to the comments section of the PHP manual page,
1042                                 * it is possible that this function returns an empty string
1043                                 * for some files (e.g. if they don't exist in the magic MIME database)
1044                                 */
1045                                if (is_string($mime) && preg_match($regexp, $mime, $matches))
1046                                {
1047                                        $this->file_type = $matches[1];
1048                                        return;
1049                                }
1050                        }
1051                }
1052
1053                /* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type,
1054                 * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it
1055                 * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better
1056                 * than mime_content_type() as well, hence the attempts to try calling the command line with
1057                 * three different functions.
1058                 *
1059                 * Notes:
1060                 *      - the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
1061                 *      - many system admins would disable the exec(), shell_exec(), popen() and similar functions
1062                 *        due to security concerns, hence the function_exists() checks
1063                 */
1064                if (DIRECTORY_SEPARATOR !== '\\')
1065                {
1066                        $cmd = 'file --brief --mime ' . escapeshellarg($file['tmp_name']) . ' 2>&1';
1067
1068                        if (function_exists('exec'))
1069                        {
1070                                /* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
1071                                 * However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites
1072                                 * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
1073                                 * value, which is only put to allow us to get the return status code.
1074                                 */
1075                                $mime = @exec($cmd, $mime, $return_status);
1076                                if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches))
1077                                {
1078                                        $this->file_type = $matches[1];
1079                                        return;
1080                                }
1081                        }
1082
1083                        if ( (bool) @ini_get('safe_mode') === FALSE && function_exists('shell_exec'))
1084                        {
1085                                $mime = @shell_exec($cmd);
1086                                if (strlen($mime) > 0)
1087                                {
1088                                        $mime = explode("\n", trim($mime));
1089                                        if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
1090                                        {
1091                                                $this->file_type = $matches[1];
1092                                                return;
1093                                        }
1094                                }
1095                        }
1096
1097                        if (function_exists('popen'))
1098                        {
1099                                $proc = @popen($cmd, 'r');
1100                                if (is_resource($proc))
1101                                {
1102                                        $mime = @fread($proc, 512);
1103                                        @pclose($proc);
1104                                        if ($mime !== FALSE)
1105                                        {
1106                                                $mime = explode("\n", trim($mime));
1107                                                if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
1108                                                {
1109                                                        $this->file_type = $matches[1];
1110                                                        return;
1111                                                }
1112                                        }
1113                                }
1114                        }
1115                }
1116
1117                // Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type'])
1118                if (function_exists('mime_content_type'))
1119                {
1120                        $this->file_type = @mime_content_type($file['tmp_name']);
1121                        if (strlen($this->file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string
1122                        {
1123                                return;
1124                        }
1125                }
1126
1127                $this->file_type = $file['type'];
1128        }
1129
1130        // --------------------------------------------------------------------
1131
1132}
1133// END Upload Class
1134
1135/* End of file Upload.php */
1136/* Location: ./system/libraries/Upload.php */
Note: See TracBrowser for help on using the repository browser.