MDL-31112 repository: Fixed phpdocs on new function
[moodle.git] / repository / upload / lib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * A repository plugin to allow user uploading files
20  *
21  * @since 2.0
22  * @package    repository
23  * @subpackage upload
24  * @copyright  2009 Dongsheng Cai
25  * @author     Dongsheng Cai <dongsheng@moodle.com>
26  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27  */
29 class repository_upload extends repository {
30     private $mimetypes = array();
32     /**
33      * Print a upload form
34      * @return array
35      */
36     public function print_login() {
37         return $this->get_listing();
38     }
40     /**
41      * Process uploaded file
42      * @return array|bool
43      */
44     public function upload($saveas_filename, $maxbytes) {
45         global $USER, $CFG;
47         $types = optional_param_array('accepted_types', '*', PARAM_RAW);
48         if ((is_array($types) and in_array('*', $types)) or $types == '*') {
49             $this->mimetypes = '*';
50         } else {
51             foreach ($types as $type) {
52                 $this->mimetypes[] = mimeinfo('type', $type);
53             }
54         }
56         $record = new stdClass();
57         $record->filearea = 'draft';
58         $record->component = 'user';
59         $record->filepath = optional_param('savepath', '/', PARAM_PATH);
60         $record->itemid   = optional_param('itemid', 0, PARAM_INT);
61         $record->license  = optional_param('license', $CFG->sitedefaultlicense, PARAM_TEXT);
62         $record->author   = optional_param('author', '', PARAM_TEXT);
64         $context = get_context_instance(CONTEXT_USER, $USER->id);
65         $elname = 'repo_upload_file';
67         $fs = get_file_storage();
68         $sm = get_string_manager();
70         if ($record->filepath !== '/') {
71             $record->filepath = file_correct_filepath($record->filepath);
72         }
74         if (!isset($_FILES[$elname])) {
75             throw new moodle_exception('nofile');
76         }
77         if (!empty($_FILES[$elname]['error'])) {
78             switch ($_FILES[$elname]['error']) {
79             case UPLOAD_ERR_INI_SIZE:
80                 throw new moodle_exception('upload_error_ini_size', 'repository_upload');
81                 break;
82             case UPLOAD_ERR_FORM_SIZE:
83                 throw new moodle_exception('upload_error_form_size', 'repository_upload');
84                 break;
85             case UPLOAD_ERR_PARTIAL:
86                 throw new moodle_exception('upload_error_partial', 'repository_upload');
87                 break;
88             case UPLOAD_ERR_NO_FILE:
89                 throw new moodle_exception('upload_error_no_file', 'repository_upload');
90                 break;
91             case UPLOAD_ERR_NO_TMP_DIR:
92                 throw new moodle_exception('upload_error_no_tmp_dir', 'repository_upload');
93                 break;
94             case UPLOAD_ERR_CANT_WRITE:
95                 throw new moodle_exception('upload_error_cant_write', 'repository_upload');
96                 break;
97             case UPLOAD_ERR_EXTENSION:
98                 throw new moodle_exception('upload_error_extension', 'repository_upload');
99                 break;
100             default:
101                 throw new moodle_exception('nofile');
102             }
103         }
105         // scan the files, throws exception and deletes if virus found
106         // this is tricky because clamdscan daemon might not be able to access the files
107         $permissions = fileperms($_FILES[$elname]['tmp_name']);
108         @chmod($_FILES[$elname]['tmp_name'], $CFG->filepermissions);
109         self::antivir_scan_file($_FILES[$elname]['tmp_name'], $_FILES[$elname]['name'], true);
110         @chmod($_FILES[$elname]['tmp_name'], $permissions);
112         if (empty($saveas_filename)) {
113             $record->filename = clean_param($_FILES[$elname]['name'], PARAM_FILE);
114         } else {
115             $ext = '';
116             $match = array();
117             $filename = clean_param($_FILES[$elname]['name'], PARAM_FILE);
118             if (preg_match('/\.([a-z0-9]+)$/i', $filename, $match)) {
119                 if (isset($match[1])) {
120                     $ext = $match[1];
121                 }
122             }
123             $ext = !empty($ext) ? $ext : '';
124             if (preg_match('#\.(' . $ext . ')$#i', $saveas_filename)) {
125                 // saveas filename contains file extension already
126                 $record->filename = $saveas_filename;
127             } else {
128                 $record->filename = $saveas_filename . '.' . $ext;
129             }
130         }
132         // Check the file has some non-null contents - usually an indication that a user has
133         // tried to upload a folder by mistake
134         if (!$this->check_valid_contents($_FILES[$elname]['tmp_name'])) {
135             throw new moodle_exception('upload_error_invalid_file', 'repository_upload', '', $record->filename);
136         }
138         if ($this->mimetypes != '*') {
139             // check filetype
140             $filemimetype = mimeinfo('type', $_FILES[$elname]['name']);
141             if (!in_array($filemimetype, $this->mimetypes)) {
142                 if ($sm->string_exists($filemimetype, 'mimetypes')) {
143                     $filemimetype = get_string($filemimetype, 'mimetypes');
144                 }
145                 throw new moodle_exception('invalidfiletype', 'repository', '', $filemimetype);
146             }
147         }
149         if (empty($record->itemid)) {
150             $record->itemid = 0;
151         }
153         if (($maxbytes!==-1) && (filesize($_FILES[$elname]['tmp_name']) > $maxbytes)) {
154             throw new file_exception('maxbytes');
155         }
156         $record->contextid = $context->id;
157         $record->userid    = $USER->id;
158         $record->source    = '';
160         if (repository::draftfile_exists($record->itemid, $record->filepath, $record->filename)) {
161             $existingfilename = $record->filename;
162             $unused_filename = repository::get_unused_filename($record->itemid, $record->filepath, $record->filename);
163             $record->filename = $unused_filename;
164             $stored_file = $fs->create_file_from_pathname($record, $_FILES[$elname]['tmp_name']);
165             $event = array();
166             $event['event'] = 'fileexists';
167             $event['newfile'] = new stdClass;
168             $event['newfile']->filepath = $record->filepath;
169             $event['newfile']->filename = $unused_filename;
170             $event['newfile']->url = moodle_url::make_draftfile_url($record->itemid, $record->filepath, $unused_filename)->out();
172             $event['existingfile'] = new stdClass;
173             $event['existingfile']->filepath = $record->filepath;
174             $event['existingfile']->filename = $existingfilename;
175             $event['existingfile']->url      = moodle_url::make_draftfile_url($record->itemid, $record->filepath, $existingfilename)->out();;
176             return $event;
177         } else {
178             $stored_file = $fs->create_file_from_pathname($record, $_FILES[$elname]['tmp_name']);
180             return array(
181                 'url'=>moodle_url::make_draftfile_url($record->itemid, $record->filepath, $record->filename)->out(),
182                 'id'=>$record->itemid,
183                 'file'=>$record->filename);
184         }
185     }
187     /**
188      * Checks the contents of the given file is not completely NULL - this can happen if a
189      * user drags & drops a folder onto a filemanager / filepicker element
190      * @param string $filepath full path (including filename) to file to check
191      * @return true if file has at least one non-null byte within it
192      */
193     protected function check_valid_contents($filepath) {
194         $buffersize = 4096;
196         $fp = fopen($filepath, 'r');
197         if (!$fp) {
198             return false; // Cannot read the file - something has gone wrong
199         }
200         while (!feof($fp)) {
201             // Read the file 4k at a time
202             $data = fread($fp, $buffersize);
203             if (preg_match('/[^\0]+/', $data)) {
204                 fclose($fp);
205                 return true; // Return as soon as a non-null byte is found
206             }
207         }
208         // Entire file is NULL
209         fclose($fp);
210         return false;
211     }
213     /**
214      * Return a upload form
215      * @return array
216      */
217     public function get_listing() {
218         global $CFG;
219         $ret = array();
220         $ret['nologin']  = true;
221         $ret['nosearch'] = true;
222         $ret['norefresh'] = true;
223         $ret['list'] = array();
224         $ret['dynload'] = false;
225         $ret['upload'] = array('label'=>get_string('attachment', 'repository'), 'id'=>'repo-form');
226         return $ret;
227     }
229     /**
230      * supported return types
231      * @return int
232      */
233     public function supported_returntypes() {
234         return FILE_INTERNAL;
235     }