qa-app-upload.php 6.83 KB
Newer Older
Gideon Greenspan committed
1 2 3 4 5 6 7
<?php

/*
	Question2Answer (c) Gideon Greenspan

	http://www.question2answer.org/

Scott Vivian committed
8

Gideon Greenspan committed
9 10 11 12 13 14 15 16 17
	File: qa-include/qa-app-upload.php
	Version: See define()s at top of qa-include/qa-base.php
	Description: Application-level file upload functionality


	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.
Scott Vivian committed
18

Gideon Greenspan committed
19 20 21 22 23 24 25 26 27 28 29 30 31
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/

	if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
		header('Location: ../');
		exit;
	}

Scott Vivian committed
32

Gideon Greenspan committed
33 34 35 36 37 38
	function qa_get_max_upload_size()
/*
	Return the maximum size of file that can be uploaded, based on database and PHP limits
*/
	{
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
39

Gideon Greenspan committed
40
		$mindb=16777215; // from MEDIUMBLOB column type
Scott Vivian committed
41

Gideon Greenspan committed
42
		$minphp=trim(ini_get('upload_max_filesize'));
Scott Vivian committed
43

Gideon Greenspan committed
44 45 46 47 48 49 50 51
		switch (strtolower(substr($minphp, -1))) {
			case 'g':
				$minphp*=1024;
			case 'm':
				$minphp*=1024;
			case 'k':
				$minphp*=1024;
		}
Scott Vivian committed
52

Gideon Greenspan committed
53 54
		return min($mindb, $minphp);
	}
Scott Vivian committed
55 56


Gideon Greenspan committed
57
	function qa_upload_file($localfilename, $sourcefilename, $maxfilesize=null, $onlyimage=false, $imagemaxwidth=null, $imagemaxheight=null)
Gideon Greenspan committed
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
/*
	Move an uploaded image or other file into blob storage. Pass the $localfilename where the file is currently stored
	(temporarily) and the $sourcefilename of the file on the user's computer (if using PHP's usual file upload
	mechanism, these are obtained from $_FILES[..]['tmp_name'] and $_FILES[..]['name'] fields respectively). To apply a
	maximum file size (in bytes) beyond the general one, use $maxfilesize, otherwise set it to null. Set $onlyimage to
	true if only image uploads (PNG, GIF, JPEG) are allowed. To apply a maximum width or height (in pixels) to uploaded
	images, set $imagemaxwidth and $imagemaxheight. The function returns an array which may contain the following elements:

	'error' => a string containing an error, if one occurred
	'format' => the format (file extension) of the blob created (all scaled images end up as 'jpeg')
	'width' => if an image, the width in pixels of the blob created (after possible scaling)
	'height' => if an image, the height in pixels of the blob created (after possible scaling)
	'blobid' => the blobid that was created (if there was no error)
	'bloburl' => the url that can be used to view/download the created blob (if there was no error)
*/
Gideon Greenspan committed
73 74
	{
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
75

Gideon Greenspan committed
76
		$result=array();
Scott Vivian committed
77

Gideon Greenspan committed
78
	//	Check per-user upload limits
Scott Vivian committed
79

Gideon Greenspan committed
80 81
		require_once QA_INCLUDE_DIR.'qa-app-users.php';
		require_once QA_INCLUDE_DIR.'qa-app-limits.php';
Scott Vivian committed
82

Gideon Greenspan committed
83 84 85 86 87
		switch (qa_user_permit_error(null, QA_LIMIT_UPLOADS))
		{
			case 'limit':
				$result['error']=qa_lang('main/upload_limit');
				return $result;
Scott Vivian committed
88

Gideon Greenspan committed
89 90 91 92 93 94 95 96
			case false:
				qa_limits_increment(qa_get_logged_in_userid(), QA_LIMIT_UPLOADS);
				break;

			default:
				$result['error']=qa_lang('users/no_permission');
				return $result;
		}
Scott Vivian committed
97

Gideon Greenspan committed
98
	//	Check the uploaded file is not too large
Scott Vivian committed
99

Gideon Greenspan committed
100 101 102 103 104
		$filesize=filesize($localfilename);
		if (isset($maxfilesize))
			$maxfilesize=min($maxfilesize, qa_get_max_upload_size());
		else
			$maxfilesize=qa_get_max_upload_size();
Scott Vivian committed
105

Gideon Greenspan committed
106 107 108 109
		if ( ($filesize<=0) || ($filesize>$maxfilesize) ) { // if file was too big for PHP, $filesize will be zero
			$result['error']=qa_lang_sub('main/max_upload_size_x', number_format($maxfilesize/1048576, 1).'MB');
			return $result;
		}
Scott Vivian committed
110

Gideon Greenspan committed
111 112 113 114 115
	//	Find out what type of source file was uploaded and if appropriate, check it's an image and get preliminary size measure

		$pathinfo=pathinfo($sourcefilename);
		$format=strtolower(@$pathinfo['extension']);
		$isimage=($format=='png') || ($format=='gif') || ($format=='jpeg') || ($format=='jpg'); // allowed image extensions
Scott Vivian committed
116

Gideon Greenspan committed
117 118 119 120 121 122
		if ($isimage) {
			$imagesize=@getimagesize($localfilename);

			if (is_array($imagesize)) {
				$result['width']=$imagesize[0];
				$result['height']=$imagesize[1];
Scott Vivian committed
123

Gideon Greenspan committed
124 125 126 127
				switch ($imagesize['2']) { // reassign format based on actual content, if we can
					case IMAGETYPE_GIF:
						$format='gif';
						break;
Scott Vivian committed
128

Gideon Greenspan committed
129 130 131
					case IMAGETYPE_JPEG:
						$format='jpeg';
						break;
Scott Vivian committed
132

Gideon Greenspan committed
133 134 135 136 137 138
					case IMAGETYPE_PNG:
						$format='png';
						break;
				}
			}
		}
Scott Vivian committed
139

Gideon Greenspan committed
140
		$result['format']=$format;
Scott Vivian committed
141

Gideon Greenspan committed
142 143 144 145 146
		if ($onlyimage)
			if ( (!$isimage) || !is_array($imagesize) ) {
				$result['error']=qa_lang_sub('main/image_not_read', 'GIF, JPG, PNG');
				return $result;
			}
Scott Vivian committed
147

Gideon Greenspan committed
148
	//	Read in the raw file contents
Scott Vivian committed
149

Gideon Greenspan committed
150
		$content=file_get_contents($localfilename);
Scott Vivian committed
151

Gideon Greenspan committed
152
	//	If appropriate, get more accurate image size and apply constraints to it
Scott Vivian committed
153

Gideon Greenspan committed
154 155 156 157
		require_once QA_INCLUDE_DIR.'qa-util-image.php';

		if ($isimage && qa_has_gd_image()) {
			$image=@imagecreatefromstring($content);
Scott Vivian committed
158

Gideon Greenspan committed
159 160 161
			if (is_resource($image)) {
				$result['width']=$width=imagesx($image);
				$result['height']=$height=imagesy($image);
Scott Vivian committed
162

Gideon Greenspan committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
				if (isset($imagemaxwidth) || isset($imagemaxheight))
					if (qa_image_constrain(
						$width, $height,
						isset($imagemaxwidth) ? $imagemaxwidth : $width,
						isset($imagemaxheight) ? $imagemaxheight : $height
					)) {
						qa_gd_image_resize($image, $width, $height);

						if (is_resource($image)) {
							$content=qa_gd_image_jpeg($image);
							$result['format']=$format='jpeg';
							$result['width']=$width;
							$result['height']=$height;
						}
					}
Scott Vivian committed
178

Gideon Greenspan committed
179 180 181 182
				if (is_resource($image)) // might have been lost
					imagedestroy($image);
			}
		}
Scott Vivian committed
183

Gideon Greenspan committed
184
	//	Create the blob and return
Scott Vivian committed
185

Gideon Greenspan committed
186 187 188 189 190
		require_once QA_INCLUDE_DIR.'qa-app-blobs.php';

		$userid=qa_get_logged_in_userid();
		$cookieid=isset($userid) ? qa_cookie_get() : qa_cookie_get_create();
		$result['blobid']=qa_create_blob($content, $format, $sourcefilename, $userid, $cookieid, qa_remote_ip_address());
Scott Vivian committed
191

Gideon Greenspan committed
192 193 194 195
		if (!isset($result['blobid'])) {
			$result['error']=qa_lang('main/general_error');
			return $result;
		}
Scott Vivian committed
196

Gideon Greenspan committed
197
		$result['bloburl']=qa_get_blob_url($result['blobid'], true);
Scott Vivian committed
198 199

		return $result;
Gideon Greenspan committed
200
	}
Scott Vivian committed
201

Gideon Greenspan committed
202

Gideon Greenspan committed
203 204 205 206 207 208
	function qa_upload_file_one($maxfilesize=null, $onlyimage=false, $imagemaxwidth=null, $imagemaxheight=null)
/*
	In response to a file upload, move the first uploaded file into blob storage. Other parameters are as for qa_upload_file(...)
*/
	{
		$file=reset($_FILES);
Scott Vivian committed
209

Gideon Greenspan committed
210 211
		return qa_upload_file($file['tmp_name'], $file['name'], $maxfilesize, $onlyimage, $imagemaxwidth, $imagemaxheight);
	}
Scott Vivian committed
212 213


Gideon Greenspan committed
214 215 216
/*
	Omit PHP closing tag to help avoid accidental output
*/