JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
no dashes in filenames, improved image uploading, make_thumbnail()
[wfpl.git] / upload.php
1 <?php
2
3 #  Copyright (C) 2007 Jason Woofenden
4 #
5 #  This file is part of wfpl.
6 #
7 #  wfpl is free software; you can redistribute it and/or modify it under the
8 #  terms of the GNU Lesser General Public License as published by the Free
9 #  Software Foundation; either version 2.1 of the License, or (at your option)
10 #  any later version.
11 #
12 #  wfpl is distributed in the hope that it will be useful, but WITHOUT ANY
13 #  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 #  FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
15 #  more details.
16 #
17 #  You should have received a copy of the GNU Lesser General Public License
18 #  along with wfpl; if not, write to the Free Software Foundation, Inc., 51
19 #  Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20
21
22 # This file contains functions to accept files being uplodad with the <input
23 # type="file" name="foo"> control.
24 #
25 # ########
26 # # HTML #
27 # ########
28
29 # First, your <form> tag must contain this attribute:
30 # enctype="multipart/form-data"
31
32 # Second, you should indicate to the browser the maximum file size (in bytes)
33 # allowed for uploads with a hidden input field named MAX_FILE_SIZE. You can
34 # use the function upload_max_filesize() to get the maximum allowed size that
35 # PHP will accept.
36
37 # Example:
38
39 # <form action="foo.php" enctype="multipart/form-data" method="post">
40 # <input type="hidden" name="MAX_FILE_SIZE" value="2097152" />
41 # <input type="file" name="photo" />
42 # <input type="submit" name="save" value="Save" />
43 # </form>
44
45 # #######
46 # # PHP #
47 # #######
48 #
49 # In the php code you can use either save_uploaded_file('photo',
50 # 'upload/dir/'); or save_uploaded_image('photo', 'upload/dir/'); The only
51 # difference being that save_uploaded_image() will convert gifs to PNGs.
52
53 # Both functions will generate a reasonable filename based on the filename
54 # passed from the browser (and on the mime-type if there's no extension) unless
55 # you specify a filename. See the comments above the function definitions below
56 # for more details.
57
58 # In a future version of save_uploaded_image(), when you specify a filename, it
59 # will check the image type of the uploaded image, and if it's different than
60 # the type you specified, it will convert the image for you.
61
62
63 $GLOBALS['mime_to_ext'] = array(
64         'text/plain' => 'txt',
65         'text/html'  => 'html',
66         'image/jpeg' => 'jpg',
67         'image/jpe' => 'jpg',
68         'image/jpg'  => 'jpg',
69         'image/gif'  => 'gif',
70         'image/png'  => 'png',
71         'application/pdf' => 'pdf'
72 );
73
74 $GLOBALS['ext_to_ext'] = array(
75         'text' => 'txt',
76         'jpe'  => 'jpg',
77         'jpeg' => 'jpg',
78         'htm'  => 'html'
79 );
80
81 # return the upload_max_filesize in bytes
82 function upload_max_filesize() {
83         $max = ini_get('upload_max_filesize');
84         $postfix = strtolower(substr($max, -1));
85         if($postfix == 'g') {
86                 return substr($max, 0, -1) * 1073741824;
87         } elseif($postfix == 'm') {
88                 return substr($max, 0, -1) * 1048576;
89         } elseif ($postfix == 'k') {
90                 return substr($max, 0, -1) * 1024;
91         } else {
92                 return $max;
93         }
94 }
95
96
97 # pass in the client's path that came from an html <input type="file"/> tag
98 #
99 # mime time used to generate extension ONLY IF it doesn't have one already.
100 function generate_filename($path, $mime = 'text/plain') {
101         # lower case
102         $filename = strtolower($path);
103
104         # remove directories (unix, windows and mac paths)
105         $last = strrpos($filename, '/');
106         if($last === false) {
107                 $last = strrpos($filename, '\\');
108         }
109         if($last === false) {
110                 $last = strrpos($filename, ':');
111         }
112         if($last) {
113                 $filename = substr($filename, $last + 1);
114         }
115
116         # replace symbols with underscores
117         $filename = ereg_replace('[^a-z0-9_.]', '_', $filename);
118
119         # remove dots from the beginning (no invisible files)
120         $filename = ereg_replace('^\.*', '', $filename);
121
122         # fix extension
123         $last_dot = strrpos($filename, '.');
124         if($last_dot === false) {
125                 #no extension
126                 if(isset($GLOBALS['mime_to_ext'][$mime])) {
127                         $filename .= '.' . $GLOBALS['mime_to_ext'][$mime];
128                 }
129         } else {
130                 $basename = substr($filename, 0, $last_dot);
131                 $ext = substr($filename, $last_dot + 1);
132                 if(isset($GLOBALS['ext_to_ext'][$ext])) {
133                         $ext .= $GLOBALS['ext_to_ext'][$ext];
134                 }
135                 $filename = $basename . '.' . $ext;
136         }
137         return $filename;
138 }
139
140
141
142 # Move uploaded file, and return the new filename.
143 #
144 # Pass in the index into the $_FILES array (the name of the html input tag) and
145 # the path to the folder you'd like it saved to. If path ends with a slash this
146 # function will generate a filename based on the client's name, otherwise it'll
147 # name the file that.
148 #
149 # example: save_uploaded_file('pdf', 'uploaded_pdfs/');
150 # example: save_uploaded_file('resume', "/www/example.com/remumes/$user_id.txt");
151 function save_uploaded_file($key, $path) {
152         if(substr($path, -1) == '/') {
153                 $filename = $path . generate_filename($_FILES[$key]['name'], $_FILES[$key]['type']);
154         } else {
155                 $filename = $path;
156         }
157
158         if(!move_uploaded_file($_FILES[$key]['tmp_name'], $filename)) {
159                 return false;
160         }
161
162         return $filename;
163 }
164
165 function path_to_convert() {
166         if(!isset($GLOBALS['path_to_convert'])) {
167                 $convert = '/usr/local/bin/convert';
168                 if(!file_exists($convert)) {
169                         $convert = '/usr/bin/convert';
170                 }
171                 if(!file_exists($convert)) {
172                         $convert = `which convert`;
173                 }
174                 if($convert == '' || !file_exists($convert)) {
175                         die("can't find imagemagick's 'convert' program");
176                 }
177
178                 $GLOBALS['path_to_convert'] = $convert;
179         }
180
181         return $GLOBALS['path_to_convert'];
182 }
183                 
184
185 # returns new filename with .png extension
186 function gif_to_png($filename, $new_filename = 'just change extension') {
187         if($new_filename == 'just change extension') {
188                 $new_filename = $filename;
189                 $last_dot = strrpos($new_filename, '.');
190                 if($last_dot !== false) {
191                         $new_filename = substr($new_filename, 0, $last_dot);
192                 }
193                 $new_filename .= '.png';
194         }
195
196         $convert = path_to_convert();
197
198         $command = "$convert " . escapeshellarg($filename) . ' ' . escapeshellarg($new_filename);
199
200         exec($command, $dummy, $ret);
201         if($ret != 0) {
202                 die("image conversion failed. convert did exit($ret)");
203         }
204         unlink($filename);
205         return $new_filename;
206 }
207
208 # make a thumbnail image.
209 #
210 # Thumbnail will have the same filename, except "_thumb" will be added right
211 # before the dot preceding the extension. so foo.png yields foo_thumb.png
212 #
213 # Thumbnail will retain aspect ratio, and be either $max_width wide or
214 # $max_height tall (or, if the aspect is just right, both)
215 function make_thumbnail($filename, $max_width = '70', $max_height = '70') {
216         $thumb = ereg_replace('[.]([a-z]+)$', "_thumb.\\1", $filename);
217         if($thumb == $filename) {
218                 die("couldn't make thumbnail because filename has no extension.");
219         }
220
221         $convert = path_to_convert();
222
223         # can't be too careful
224         $max_width = ereg_replace('[^0-9]', '', $max_width);
225         if($max_width == '') {
226                 $max_width = '70';
227         }
228         $max_height = ereg_replace('[^0-9]', '', $max_height);
229         if($max_height == '') {
230                 $max_height = '70';
231         }
232         
233         $command = "$convert -geometry ${max_width}x$max_height " . escapeshellarg($filename) . ' ' . escapeshellarg($thumb);
234
235         exec($command, $dummy, $ret);
236         if($ret != 0) {
237                 die("Thumbnail creatin failed. convert did exit($ret)");
238         }
239
240         return $thumb;
241 }
242
243 # like save_uploaded_file() (above) except it converts gifs to pngs.
244 #
245 # FIXME: if a filename is passed in the end of path, we should check if the file type matches, and if not run convert.
246 function save_uploaded_image($key, $path) {
247         if(substr($path, -1) == '/') {
248                 $filename = save_uploaded_file($key, $path);
249                 if(substr($filename, -4) == '.gif') {
250                         $filename = gif_to_png($filename);
251                 }
252                 return $filename;
253         } else {
254                 return save_uploaded_file($key, $path);
255         }
256 }
257
258 ?>