JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
re-licensed under GPLv3
[wfpl.git] / upload.php
1 <?php
2
3 #  Copyright (C) 2007 Jason Woofenden
4 #
5 #  This program 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 #  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
17
18
19 # This file contains functions to accept files being uplodad with the <input
20 # type="file" name="foo"> control.
21 #
22 # ########
23 # # HTML #
24 # ########
25
26 # First, your <form> tag must contain this attribute:
27 # enctype="multipart/form-data"
28
29 # Second, you should indicate to the browser the maximum file size (in bytes)
30 # allowed for uploads with a hidden input field named MAX_FILE_SIZE. You can
31 # use the function upload_max_filesize() to get the maximum allowed size that
32 # PHP will accept.
33
34 # Example:
35
36 # <form action="foo.php" enctype="multipart/form-data" method="post">
37 # <input type="hidden" name="MAX_FILE_SIZE" value="2097152" />
38 # <input type="file" name="photo" />
39 # <input type="submit" name="save" value="Save" />
40 # </form>
41
42 # #######
43 # # PHP #
44 # #######
45 #
46 # In the php code you can use either save_uploaded_file('photo',
47 # 'upload/dir/'); or save_uploaded_image('photo', 'upload/dir/'); The only
48 # difference being that save_uploaded_image() will convert gifs to PNGs.
49
50 # Both functions will generate a reasonable filename based on the filename
51 # passed from the browser (and on the mime-type if there's no extension) unless
52 # you specify a filename. See the comments above the function definitions below
53 # for more details.
54
55 # In a future version of save_uploaded_image(), when you specify a filename, it
56 # will check the image type of the uploaded image, and if it's different than
57 # the type you specified, it will convert the image for you.
58
59
60 $GLOBALS['mime_to_ext'] = array(
61         'text/plain' => 'txt',
62         'text/html'  => 'html',
63         'image/jpeg' => 'jpg',
64         'image/jpe' => 'jpg',
65         'image/jpg'  => 'jpg',
66         'image/gif'  => 'gif',
67         'image/png'  => 'png',
68         'application/pdf' => 'pdf'
69 );
70
71 $GLOBALS['ext_to_ext'] = array(
72         'text' => 'txt',
73         'jpe'  => 'jpg',
74         'jpeg' => 'jpg',
75         'htm'  => 'html'
76 );
77
78 # return the upload_max_filesize in bytes
79 function upload_max_filesize() {
80         $max = ini_get('upload_max_filesize');
81         $postfix = strtolower(substr($max, -1));
82         if($postfix == 'g') {
83                 return substr($max, 0, -1) * 1073741824;
84         } elseif($postfix == 'm') {
85                 return substr($max, 0, -1) * 1048576;
86         } elseif ($postfix == 'k') {
87                 return substr($max, 0, -1) * 1024;
88         } else {
89                 return $max;
90         }
91 }
92
93
94 # pass in the client's path that came from an html <input type="file"/> tag
95 #
96 # mime time used to generate extension ONLY IF it doesn't have one already.
97 function generate_filename($path, $mime = 'text/plain') {
98         # lower case
99         $filename = strtolower($path);
100
101         # remove directories (unix, windows and mac paths)
102         $last = strrpos($filename, '/');
103         if($last === false) {
104                 $last = strrpos($filename, '\\');
105         }
106         if($last === false) {
107                 $last = strrpos($filename, ':');
108         }
109         if($last) {
110                 $filename = substr($filename, $last + 1);
111         }
112
113         # replace symbols with underscores
114         $filename = ereg_replace('[^a-z0-9_.]', '_', $filename);
115
116         # remove dots from the beginning (no invisible files)
117         $filename = ereg_replace('^\.*', '', $filename);
118
119         # fix extension
120         $last_dot = strrpos($filename, '.');
121         if($last_dot === false) {
122                 #no extension
123                 if(isset($GLOBALS['mime_to_ext'][$mime])) {
124                         $filename .= '.' . $GLOBALS['mime_to_ext'][$mime];
125                 }
126         } else {
127                 $basename = substr($filename, 0, $last_dot);
128                 $ext = substr($filename, $last_dot + 1);
129                 if(isset($GLOBALS['ext_to_ext'][$ext])) {
130                         $ext .= $GLOBALS['ext_to_ext'][$ext];
131                 }
132                 $filename = $basename . '.' . $ext;
133         }
134         return $filename;
135 }
136
137
138
139 # Move uploaded file, and return the new filename.
140 #
141 # Pass in the index into the $_FILES array (the name of the html input tag) and
142 # the path to the folder you'd like it saved to. If path ends with a slash this
143 # function will generate a filename based on the client's name, otherwise it'll
144 # name the file that.
145 #
146 # example: save_uploaded_file('pdf', 'uploaded_pdfs/');
147 # example: save_uploaded_file('resume', "/www/example.com/remumes/$user_id.txt");
148 function save_uploaded_file($key, $path) {
149         if(substr($path, -1) == '/') {
150                 $filename = $path . generate_filename($_FILES[$key]['name'], $_FILES[$key]['type']);
151         } else {
152                 $filename = $path;
153         }
154
155         if(!move_uploaded_file($_FILES[$key]['tmp_name'], $filename)) {
156                 return false;
157         }
158
159         return $filename;
160 }
161
162 # this function exists to deal with cases where binaries are installed in very
163 # standard places (like /usr/bin or /usr/local bin) and PHP's PATH environment
164 # variable is not set appropriately.
165 function path_to($prog, $or_die = true) {
166         $prog = ereg_replace('[^a-zA-Z0-9_.-]', '', $prog);
167         $prog = ereg_replace('^[-.]*', '', $prog);
168         if($prog == '') {
169                 die('Invalid argument to path_to()');
170         }
171
172         if(!isset($GLOBALS["path_to_$prog"])) {
173                 $ret = _path_to($prog, $or_die);
174                 if($ret == false) {
175                         return false;
176                 }
177                 $GLOBALS["path_to_$prog"] = $ret;
178         }
179
180         return $GLOBALS["path_to_$prog"];
181 }
182         
183 function _path_to($prog, $or_die) {
184         # relies on PHP's short-circuit mechanism
185         if(file_exists($path = "/usr/local/bin/$prog") ||
186            file_exists($path = "/usr/bin/$prog") ||
187            ($path = `which $prog` != '' && file_exists($path))) {
188                 return $path;
189         } else {
190                 if($or_die) {
191                         die("Failed to locate '$prog' executable.");
192                 }
193                 return false;
194         }
195 }
196
197
198 # returns new filename with .png extension
199 function gif_to_png($filename, $new_filename = 'just change extension') {
200         if($new_filename == 'just change extension') {
201                 $new_filename = $filename;
202                 $last_dot = strrpos($new_filename, '.');
203                 if($last_dot !== false) {
204                         $new_filename = substr($new_filename, 0, $last_dot);
205                 }
206                 $new_filename .= '.png';
207         }
208
209         $convert = path_to('convert');
210
211         $command = "$convert " . escapeshellarg($filename) . ' ' . escapeshellarg($new_filename);
212
213         exec($command, $dummy, $ret);
214         if($ret != 0) {
215                 die("image conversion failed. convert did exit($ret)");
216         }
217         unlink($filename);
218         return $new_filename;
219 }
220
221 # make a thumbnail image.
222 #
223 # Thumbnail will have the same filename, except "_thumb" will be added right
224 # before the dot preceding the extension. so foo.png yields foo_thumb.png
225 #
226 # Thumbnail will retain aspect ratio, and be either $max_width wide or
227 # $max_height tall (or, if the aspect is just right, both)
228 function make_thumbnail($filename, $max_width = '70', $max_height = '70') {
229         $last_dot = strrpos($filename, '.');
230         if($last_dot === false) {
231                 die("couldn't make thumbnail because filename has no extension.");
232         }
233
234         $thumb = substr($filename, 0, $last_dot);
235         $thumb .= '_thumb';
236         $thumb .= substr($filename, $last_dot);
237
238         $convert = path_to('convert');
239
240         # can't be too careful
241         $max_width = ereg_replace('[^0-9]', '', $max_width);
242         if($max_width == '') {
243                 $max_width = '70';
244         }
245         $max_height = ereg_replace('[^0-9]', '', $max_height);
246         if($max_height == '') {
247                 $max_height = '70';
248         }
249         
250         $command = "$convert -geometry ${max_width}x$max_height " . escapeshellarg($filename) . ' ' . escapeshellarg($thumb);
251
252         exec($command, $dummy, $ret);
253         if($ret != 0) {
254                 die("Thumbnail creation failed. Convert called exit($ret)");
255         }
256
257         return $thumb;
258 }
259
260 # Argument: path to image file
261 #
262 # Return: string in the format WIDTHxHEIGHT, or boolean false
263 #
264 # Example: image_dimensions('uploads/foo.png'); ==> "124x58"
265 function image_dimensions($image) {
266         $identify = path_to('identify');
267         $command = "$identify -format '%wx%h' " . escapeshellarg($image);
268         $dimensions = rtrim(`$command`);
269         if($dimensions == '') {
270                 return false;
271         } else {
272                 return $dimensions;
273         }
274 }
275
276 # like save_uploaded_file() (above) except it converts gifs to pngs.
277 #
278 # FIXME: if a filename is passed in the end of path, we should check if the file type matches, and if not run convert.
279 function save_uploaded_image($key, $path) {
280         if(substr($path, -1) == '/') {
281                 $filename = save_uploaded_file($key, $path);
282                 if(substr($filename, -4) == '.gif') {
283                         $filename = gif_to_png($filename);
284                 }
285                 return $filename;
286         } else {
287                 return save_uploaded_file($key, $path);
288         }
289 }
290
291 ?>