Completed
Push — master ( cf0d01...a2ef41 )
by Michael
23s
created

image.php ➔ exit404BadReq()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 5
rs 9.4285
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 63 and the first side effect is on line 72.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * XOOPS image access/edit
4
 *
5
 * You may not change or alter any portion of this comment or credits
6
 * of supporting developers from this source code or any supporting source code
7
 * which is considered copyrighted (c) material of the original comment or credit authors.
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
13
 * @license             GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package             core
15
 * @since               2.5.7
16
 * @author              luciorota <[email protected]>, Joe Lencioni <[email protected]>
17
 *
18
 * Enhanced image access/edit
19
 * This enhanced version is very useful in many cases, for example when you need a
20
 * smallest version of an image. This script uses Xoops cache to minimize server load.
21
 *
22
 *
23
 * Parameters need to be passed in through the URL's query string:
24
 * @param int      id             Xoops image id;
25
 * @param string   url            relative to XOOPS_ROOT_PATH, path of local image starting with "/"
26
 *                                 (e.g. /images/toast.jpg);
27
 * @param string   src            relative to XOOPS_ROOT_PATH, path of local image starting with "/"
28
 * @param int      width          (optional) maximum width of final image in pixels (e.g. 700);
29
 * @param int      height         (optional) maximum height of final image in pixels (e.g. 700);
30
 * @param string   color          (optional) background hex color for filling transparent PNGs (e.g. 900 or 16a942);
31
 * @param string   cropratio      (optional) ratio of width to height to crop final image (e.g. 1:1 or 3:2);
32
 * @param boolean  nocache        (optional) don't read image from the cache;
33
 * @param boolean  noservercache  (optional) don't read image from the server cache;
34
 * @param boolean  nobrowsercache (optional) don't read image from the browser cache;
35
 * @param int      quality        (optional, 0-100, default: 90) quality of output image;
36
 * @param mixed    filter         (optional, imagefilter 2nd, 3rd, 4th, 5th arguments, more info on php.net
37
 *                                 manual) a filter or an array of filters;
38
 * @param int      radius         (optional, 1, 2, 3 or 4 integer values, CW) round corner radius
39
 * @param float    angle          (optional), rotation angle)
40
 *
41
 */
42
43
/* @example         image.php
44
 * Resizing a JPEG:
45
 * <img src="/image.php?url=image-name.jpg&width=100&height=100" alt="Don't forget your alt text" />
46
 * Resizing and cropping a JPEG into a square:
47
 * <img src="/image.php?url=image-name.jpg?width=100&height=100&cropratio=1:1" alt="Don't forget your alt text" />
48
 * Matting a PNG with #990000:
49
 * <img src="/image.php?url=image-name.png?color=900&image=/path/to/image.png" alt="Don't forget your alt text" />
50
 * Apply a filter:
51
 * <img src="/image.php?url=/path/to/image.png&filter=IMG_FILTER_COLORIZE,128,60,256" alt="Don't forget the alt text" />
52
 * Apply more filters (array) :
53
 * <img src="/image.php?url=/path/to/image.png&filter[]=IMG_FILTER_GRAYSCALE&filter[]=IMG_FILTER_COLORIZE,128,60,256" />
54
 * Round the image corners:
55
 * All corners with same radius:
56
 * <img src="/image.php?url=/path/to/image.png&radius=20" alt="Don't forget your alt text" />
57
 * Left and right corners with different radius (20 for left corners and 40 for right corners)
58
 * <img src="/image.php?url=/path/to/image.png&radius=20,40" alt="Don't forget your alt text" />
59
 * 4 corners, 4 radius, clockwise order
60
 * <img src="/image.php?url=/path/to/image.png&radius=20,40,0,10" alt="Don't forget your alt text" />
61
 *
62
 */
63
define('MEMORY_TO_ALLOCATE', '100M');
64
define('DEFAULT_IMAGE_QUALITY', 90);
65
define('DEFAULT_BACKGROUND_COLOR', '000000');
66
define('ONLY_LOCAL_IMAGES', true);
67
define('ENABLE_IMAGEFILTER', true); // Set to false to avoid excessive server load
68
define('ENABLE_ROUNDCORNER', true); // Set to false to avoid excessive server load
69
define('ENABLE_IMAGEROTATE', true); // Set to false to avoid excessive server load
70
71
if (get_magic_quotes_runtime()) {
72
    set_magic_quotes_runtime(false); // will never get called on PHP 5.4+
0 ignored issues
show
Deprecated Code introduced by
The function set_magic_quotes_runtime() has been deprecated with message: Deprecated as of PHP 5.3.0. Relying on this feature is highly discouraged.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
73
}
74
if (function_exists('mb_http_output')) {
75
    mb_http_output('pass');
76
}
77
78
$xoopsOption['nocommon'] = true;
79
require_once __DIR__ . '/mainfile.php';
80
81
include_once __DIR__ . '/include/defines.php';
82
include_once __DIR__ . '/include/functions.php';
83
include_once __DIR__ . '/include/version.php';
84
include_once __DIR__ . '/kernel/object.php';
85
include_once __DIR__ . '/class/xoopsload.php';
86
include_once __DIR__ . '/class/preload.php';
87
include_once __DIR__ . '/class/module.textsanitizer.php';
88
include_once __DIR__ . '/class/database/databasefactory.php';
89
require_once __DIR__ . '/class/criteria.php';
90
XoopsLoad::load('xoopslogger');
91
$xoopsLogger = XoopsLogger::getInstance();
92
$xoopsLogger->startTime();
93
error_reporting(0);
94
95
/**
96
 * @param $etag
97
 * @param $lastModified
98
 * @return null
99
 */
100
function doConditionalGet($etag, $lastModified)
101
{
102
    header("Last-Modified: $lastModified");
103
    header("ETag: \"{$etag}\"");
104
    $ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : false;
105
    $ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
106
        ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false;
107
    if (!$ifModifiedSince && !$ifNoneMatch) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ifModifiedSince of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $ifNoneMatch of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
108
        return null;
109
    }
110
    if ($ifNoneMatch && $ifNoneMatch != $etag && $ifNoneMatch != '"' . $etag . '"') {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ifNoneMatch of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
111
        return null;
112
    } // etag is there but doesn't match
113
    if ($ifModifiedSince && $ifModifiedSince != $lastModified) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ifModifiedSince of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
114
        return null;
115
    } // if-modified-since is there but doesn't match
116
    // Nothing has changed since their last request - serve a 304 and exit
117
    header('HTTP/1.1 304 Not Modified');
118
    exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The function doConditionalGet() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
119
}
120
121
/**
122
 * ref: http://www.tricksofit.com/2014/08/round-corners-on-image-using-php-and-gd-library
123
 *
124
 * @param resource $sourceImage GD Image resource
125
 * @param int[]    $radii       array(top left, top right, bottom left, bottom right) of pixel radius
126
 *                               for each corner. A 0 disables rounding on a corner.
127
 *
128
 * @return resource
129
 */
130
function imageCreateCorners($sourceImage, $radii)
131
{
132
    $q = 2; // quality - improve alpha blending by using larger (*$q) image size
133
134
    // find a unique color
135
    $tryCounter = 0;
136
    do {
137
        if (++$tryCounter > 255) {
138
            $r = 2;
139
            $g = 254;
140
            $b = 0;
141
            break;
142
        }
143
        $r = rand(0, 255);
144
        $g = rand(0, 255);
145
        $b = rand(0, 255);
146
    } while (imagecolorexact($sourceImage, $r, $g, $b) < 0);
147
148
    $imageWidth = imagesx($sourceImage);
149
    $imageHeight = imagesy($sourceImage);
150
151
    $workingWidth = $imageWidth * $q;
152
    $workingHeight = $imageHeight * $q;
153
154
    $workingImage= imagecreatetruecolor($workingWidth, $workingHeight);
155
    $alphaColor = imagecolorallocatealpha($workingImage, $r, $g, $b, 127);
156
    imagealphablending($workingImage, false);
0 ignored issues
show
Unused Code introduced by
The call to the function imagealphablending() seems unnecessary as the function has no side-effects.
Loading history...
157
    imagesavealpha($workingImage, true);
0 ignored issues
show
Unused Code introduced by
The call to the function imagesavealpha() seems unnecessary as the function has no side-effects.
Loading history...
158
    imagefilledrectangle($workingImage, 0, 0, $workingWidth, $workingHeight, $alphaColor);
159
160
    imagefill($workingImage, 0, 0, $alphaColor);
161
    imagecopyresampled($workingImage, $sourceImage, 0, 0, 0, 0, $workingWidth, $workingHeight, $imageWidth, $imageHeight);
162
    if (0 < ($radius = $radii[0] * $q)) { // left top
163
        imagearc($workingImage, $radius - 1, $radius - 1, $radius * 2, $radius * 2, 180, 270, $alphaColor);
164
        imagefilltoborder($workingImage, 0, 0, $alphaColor, $alphaColor);
165
    }
166 View Code Duplication
    if (0 < ($radius = $radii[1] * $q)) { // right top
167
        imagearc($workingImage, $workingWidth - $radius, $radius - 1, $radius * 2, $radius * 2, 270, 0, $alphaColor);
168
        imagefilltoborder($workingImage, $workingWidth - 1, 0, $alphaColor, $alphaColor);
169
    }
170 View Code Duplication
    if (0 < ($radius = $radii[2] * $q)) { // left bottom
171
        imagearc($workingImage, $radius - 1, $workingHeight - $radius, $radius * 2, $radius * 2, 90, 180, $alphaColor);
172
        imagefilltoborder($workingImage, 0, $workingHeight - 1, $alphaColor, $alphaColor);
173
    }
174 View Code Duplication
    if (0 < ($radius = $radii[3] * $q)) { // right bottom
175
        imagearc($workingImage, $workingWidth - $radius, $workingHeight - $radius, $radius * 2, $radius * 2, 0, 90, $alphaColor);
176
        imagefilltoborder($workingImage, $workingWidth - 1, $workingHeight - 1, $alphaColor, $alphaColor);
177
    }
178
    imagealphablending($workingImage, true);
0 ignored issues
show
Unused Code introduced by
The call to the function imagealphablending() seems unnecessary as the function has no side-effects.
Loading history...
179
    imagecolortransparent($workingImage, $alphaColor);
180
181
    // scale back down to original size
182
    $destinationImage = imagecreatetruecolor($imageWidth, $imageHeight);
183
    imagealphablending($destinationImage, false);
0 ignored issues
show
Unused Code introduced by
The call to the function imagealphablending() seems unnecessary as the function has no side-effects.
Loading history...
184
    imagesavealpha($destinationImage, true);
0 ignored issues
show
Unused Code introduced by
The call to the function imagesavealpha() seems unnecessary as the function has no side-effects.
Loading history...
185
    imagefilledrectangle($destinationImage, 0, 0, $imageWidth, $imageHeight, $alphaColor);
186
    imagecopyresampled($destinationImage, $workingImage, 0, 0, 0, 0, $imageWidth, $imageHeight, $workingWidth, $workingHeight);
187
188
    // imagedestroy($sourceImage);
189
    imagedestroy($workingImage);
190
191
    return $destinationImage;
192
}
193
194
/**
195
 * @param $orig
196
 * @param $final
197
 *
198
 * @return mixed
199
 */
200
function findSharp($orig, $final)
201
{
202
    // Function from Ryan Rud (http://adryrun.com)
203
    $final *= (750.0 / $orig);
204
    $a = 52;
205
    $b = -0.27810650887573124;
206
    $c = .00047337278106508946;
207
    $result = $a + $b * $final + $c * $final * $final;
208
209
    return max(round($result), 0);
210
}
211
212
/**
213
 * issue an error for bad request
214
 *
215
 * Many different issues end up here, so message is generic 404. This keeps us from leaking info by probing
216
 */
217
function exit404BadReq()
218
{
219
    header('HTTP/1.1 404 Not Found');
220
    exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The function exit404BadReq() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
221
}
222
223
/**
224
 * check local image url for possible issues
225
 *
226
 * @param string $imageUrl url to local image starting at site root with a '/'
227
 *
228
 * @return bool true if name is acceptable, exit if not
229
 */
230
function imageFilenameCheck($imageUrl)
231
{
232
    if ($imageUrl[0] !== '/') { // must start with slash
233
        exit404BadReq();
234
    }
235
236
    if ($imageUrl === '/') { // can't be empty
237
        exit404BadReq();
238
    }
239
240
    if (preg_match('/(\.\.|<|>|\:|[[:cntrl:]])/', $imageUrl)) { // no "..", "<", ">", ":" or controls
241
        exit404BadReq();
242
    }
243
244
    $fullPath = XOOPS_ROOT_PATH . $imageUrl;
245
    if (strpos($fullPath, XOOPS_VAR_PATH) === 0) { // no access to data (shouldn't be in root, but...)
246
        exit404BadReq();
247
    }
248
    if (strpos($fullPath, XOOPS_PATH) === 0) { // no access to lib (shouldn't be in root, but...)
249
        exit404BadReq();
250
    }
251
252
    return true;
253
}
254
255
/*
256
 * Get image
257
 */
258
// Get id (Xoops image) or url or src (standard image)
259
$imageId = isset($_GET['id']) ? (int)$_GET['id'] : false;
260
$imageUrl = isset($_GET['url']) ? (string)$_GET['url'] : (isset($_GET['src']) ? (string)$_GET['src'] : false);
261
if (!empty($imageId)) {
262
    // If image is a Xoops image
263
    $imageHandler = xoops_getHandler('image');
264
    $criteria = new CriteriaCompo(new Criteria('i.image_display', true));
265
    $criteria->add(new Criteria('i.image_id', $imageId));
266
    $images = $imageHandler->getObjects($criteria, false, true);
267
    if (count($images) != 1) {
268
        // No Xoops images or to many Xoops images
269
        header('Content-type: image/gif');
270
        readfile(XOOPS_UPLOAD_PATH . '/blank.gif');
271
        exit();
272
    }
273
    $image = $images[0];
274
    // Get image category
275
    $imgcatId = $image->getVar('imgcat_id');
276
    $imgcatHandler = xoops_getHandler('imagecategory');
277
    if (!$imgcat = $imgcatHandler->get($imgcatId)) {
278
        // No Image category
279
        header('Content-type: image/gif');
280
        readfile(XOOPS_UPLOAD_PATH . '/blank.gif');
281
        exit();
282
    }
283
    // Get image data
284
    $imageFilename = $image->getVar('image_name'); // image filename
285
    $imageMimetype = $image->getVar('image_mimetype');
286
    $imageCreatedTime = $image->getVar('image_created'); // image creation date
287
    if ($imgcat->getVar('imgcat_storetype') === 'db') {
288
        $imagePath = null;
289
        $imageData = $image->getVar('image_body');
290
    } else {
291
        $imagePath = XOOPS_UPLOAD_PATH . '/' . $image->getVar('image_name');
292
        $imageData = file_get_contents($imagePath);
293
    }
294
    $sourceImage = imagecreatefromstring($imageData);
295
    $imageWidth = imagesx($sourceImage);
296
    $imageHeight = imagesy($sourceImage);
297
} elseif (!empty($imageUrl)) {
298
    // If image is a standard image
299
    if (ONLY_LOCAL_IMAGES) {
300
        // Images must be local files, so for convenience we strip the domain if it's there
301
        $imageUrl = str_replace(XOOPS_URL, '', $imageUrl);
302
303
        // will exit on any unacceptable urls
304
        imageFilenameCheck($imageUrl);
305
306
        $imagePath = XOOPS_ROOT_PATH . $imageUrl;
307
        if (!file_exists($imagePath)) {
308
            exit404BadReq();
309
        }
310
    } else {
311
        if ($imageUrl{0} === '/') {
312
            $imageUrl = substr($imageUrl, 0, 1);
313
        }
314
        $imagePath = $imageUrl;
315
    }
316
    // Get the size and MIME type of the requested image
317
    $imageFilename = basename($imagePath);  // image filename
318
    $imagesize = getimagesize($imagePath);
319
    $imageWidth = $imagesize[0];
320
    $imageHeight = $imagesize[1];
321
    $imageMimetype = $imagesize['mime'];
322
    $imageCreatedTime = filemtime($imagePath); // image creation date
323
    $imageData = file_get_contents($imagePath);
324
    switch ($imageMimetype) {
325
        case 'image/gif':
326
            $sourceImage = imagecreatefromgif($imagePath);
327
            break;
328
        case 'image/png':
329
            $sourceImage = imagecreatefrompng($imagePath);
330
            break;
331
        case 'image/jpeg':
332
            $sourceImage = imagecreatefromjpeg($imagePath);
333
            break;
334
        default:
335
            exit404BadReq();
336
            break;
337
    }
338
} else {
339
    // No id, no url, no src parameters
340
    header('Content-type: image/gif');
341
    readfile(XOOPS_ROOT_PATH . '/uploads/blank.gif');
342
    exit();
343
}
344
345
/*
346
 * Use Xoops cache
347
 */
348
// Get image_data from the Xoops cache only if the edited image has been cached after the latest modification
349
// of the original image
350
xoops_load('XoopsCache');
351
$edited_image_filename = 'editedimage_' . md5($_SERVER['REQUEST_URI']) . '_' . $imageFilename;
352
$cached_image = XoopsCache::read($edited_image_filename);
353
if (!isset($_GET['nocache']) && !isset($_GET['noservercache']) && !empty($cached_image)
354
    && ($cached_image['cached_time'] >= $imageCreatedTime)) {
355
    header("Content-type: {$imageMimetype}");
356
    header('Content-Length: ' . strlen($cached_image['image_data']));
357
    echo $cached_image['image_data'];
358
    exit();
359
}
360
361
/*
362
 * Get/check editing parameters
363
 */
364
// width, height
365
$max_width = isset($_GET['width']) ? (int)$_GET['width'] : false;
366
$max_height = isset($_GET['height']) ? (int)$_GET['height'] : false;
367
// If either a max width or max height are not specified, we default to something large so the unspecified
368
// dimension isn't a constraint on our resized image.
369
// If neither are specified but the color is, we aren't going to be resizing at all, just coloring.
370
if (!$max_width && $max_height) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $max_width of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $max_height of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
371
    $max_width = PHP_INT_MAX;
372
} elseif ($max_width && !$max_height) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $max_width of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $max_height of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
373
    $max_height = PHP_INT_MAX;
374
} elseif (!$max_width && !$max_height) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $max_width of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $max_height of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
375
    $max_width = $imageWidth;
376
    $max_height = $imageHeight;
377
}
378
379
// color
380
$color = isset($_GET['color']) ? preg_replace('/[^0-9a-fA-F]/', '', (string)$_GET['color']) : false;
381
382
// filter, radius, angle
383
$filter = isset($_GET['filter']) ? $_GET['filter'] : false;
384
$radius = isset($_GET['radius']) ? (string)$_GET['radius'] : false;
385
$angle = isset($_GET['angle']) ? (float)$_GET['angle'] : false;
386
387
// If we don't have a width or height or color or filter or radius or rotate we simply output the original
388
// image and exit
389
if (empty($_GET['width']) && empty($_GET['height']) && empty($_GET['color']) && empty($_GET['filter'])
390
    && empty($_GET['radius']) && empty($_GET['angle'])) {
391
    $last_modified_string = gmdate('D, d M Y H:i:s', $imageCreatedTime) . ' GMT';
392
    $etag = md5($imageData);
393
    doConditionalGet($etag, $last_modified_string);
394
    header("Content-type: {$imageMimetype}");
395
    header('Content-Length: ' . strlen($imageData));
396
    echo $imageData;
397
    exit();
398
}
399
400
// cropratio
401
$offset_x = 0;
402
$offset_y = 0;
403
if (isset($_GET['cropratio'])) {
404
    $crop_ratio = explode(':', (string)$_GET['cropratio']);
405
    if (count($crop_ratio) == 2) {
406
        $ratio_computed = $imageWidth / $imageHeight;
407
        $crop_radio_computed = (float)$crop_ratio[0] / (float)$crop_ratio[1];
408
        if ($ratio_computed < $crop_radio_computed) {
409
            // Image is too tall so we will crop the top and bottom
410
            $orig_height = $imageHeight;
411
            $imageHeight = $imageWidth / $crop_radio_computed;
412
            $offset_y = ($orig_height - $imageHeight) / 2;
413
        } elseif ($ratio_computed > $crop_radio_computed) {
414
            // Image is too wide so we will crop off the left and right sides
415
            $orig_width = $imageWidth;
416
            $imageWidth = $imageHeight * $crop_radio_computed;
417
            $offset_x = ($orig_width - $imageWidth) / 2;
418
        }
419
    }
420
}
421
// Setting up the ratios needed for resizing. We will compare these below to determine how to resize the image
422
// (based on height or based on width)
423
$xRatio = $max_width / $imageWidth;
424
$yRatio = $max_height / $imageHeight;
425
if ($xRatio * $imageHeight < $max_height) {
426
    // Resize the image based on width
427
    $tn_height = ceil($xRatio * $imageHeight);
428
    $tn_width = $max_width;
429
} else {
430
    // Resize the image based on height
431
    $tn_width = ceil($yRatio * $imageWidth);
432
    $tn_height = $max_height;
433
}
434
435
// quality
436
$quality = isset($_GET['quality']) ? (int)$_GET['quality'] : DEFAULT_IMAGE_QUALITY;
437
438
/*
439
 * Start image editing
440
 */
441
// We don't want to run out of memory
442
ini_set('memory_limit', MEMORY_TO_ALLOCATE);
443
444
// Set up a blank canvas for our resized image (destination)
445
$destination_image = imagecreatetruecolor($tn_width, $tn_height);
446
447
// Set up the appropriate image handling functions based on the original image's mime type
448
switch ($imageMimetype) {
449 View Code Duplication
    case 'image/gif':
450
        // We will be converting GIFs to PNGs to avoid transparency issues when resizing GIFs
451
        // This is maybe not the ideal solution, but IE6 can suck it
452
        $output_function = 'imagepng';
453
        $imageMimetype = 'image/png'; // We need to convert GIFs to PNGs
454
        $do_sharpen = false;
455
        $quality = round(10 - ($quality / 10)); // We are converting the GIF to a PNG and PNG needs a compression
456
                                                // level of 0 (no compression) through 9 (max)
457
        break;
458 View Code Duplication
    case 'image/png':
459
        $output_function = 'imagepng';
460
        $do_sharpen = false;
461
        $quality = round(10 - ($quality / 10)); // PNG needs a compression level of 0 (no compression) through 9
462
        break;
463
    case 'image/jpeg':
464
        $output_function = 'imagejpeg';
465
        $do_sharpen = true;
466
        break;
467
    default:
468
        exit400BadReq();
469
        break;
470
}
471
472
// Resample the original image into the resized canvas we set up earlier
473
imagecopyresampled($destination_image, $sourceImage, 0, 0, $offset_x, $offset_y, $tn_width, $tn_height, $imageWidth, $imageHeight);
474
475
// Set background color
476
if (in_array($imageMimetype, array('image/gif', 'image/png'))) {
477
    if (!$color) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $color of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
478
        // If this is a GIF or a PNG, we need to set up transparency
479
        imagealphablending($destination_image, false);
0 ignored issues
show
Unused Code introduced by
The call to the function imagealphablending() seems unnecessary as the function has no side-effects.
Loading history...
480
        imagesavealpha($destination_image, true);
0 ignored issues
show
Unused Code introduced by
The call to the function imagesavealpha() seems unnecessary as the function has no side-effects.
Loading history...
481
        $png_transparency = imagecolorallocatealpha($destination_image, 0, 0, 0, 127);
482
        imagefill($destination_image, 0, 0, $png_transparency);
483 View Code Duplication
    } else {
484
        // Fill the background with the specified color for matting purposes
485
        if ($color[0] === '#') {
486
            $color = substr($color, 1);
487
        }
488
        $background = false;
489
        if (strlen($color) == 6) {
490
            $background = imagecolorallocate(
491
                $destination_image,
492
                intval($color[0] . $color[1], 16),
493
                intval($color[2] . $color[3], 16),
494
                intval($color[4] . $color[5], 16)
495
            );
496
        } elseif (strlen($color) == 3) {
497
            $background = imagecolorallocate(
498
                $destination_image,
499
                intval($color[0] . $color[0], 16),
500
                intval($color[1] . $color[1], 16),
501
                intval($color[2] . $color[2], 16)
502
            );
503
        }
504
        if ($background) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $background of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
505
            imagefill($destination_image, 0, 0, $background);
506
        }
507
    }
508 View Code Duplication
} else {
509
    if (!$color) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $color of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
510
        $color = DEFAULT_BACKGROUND_COLOR;
511
    }
512
    // Fill the background with the specified color for matting purposes
513
    if ($color[0] === '#') {
514
        $color = substr($color, 1);
515
    }
516
    $background = false;
517
    if (strlen($color) == 6) {
518
        $background = imagecolorallocate(
519
            $destination_image,
520
            intval($color[0] . $color[1], 16),
521
            intval($color[2] . $color[3], 16),
522
            intval($color[4] . $color[5], 16)
523
        );
524
    } elseif (strlen($color) == 3) {
525
        $background = imagecolorallocate(
526
            $destination_image,
527
            intval($color[0] . $color[0], 16),
528
            intval($color[1] . $color[1], 16),
529
            intval($color[2] . $color[2], 16)
530
        );
531
    }
532
    if ($background) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $background of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
533
        imagefill($destination_image, 0, 0, $background);
534
    }
535
}
536
537
// Imagefilter
538
if (ENABLE_IMAGEFILTER && !empty($filter)) {
539
    $filterSet = (array) $filter;
540
    foreach ($filterSet as $currentFilter) {
541
        $rawFilterArgs = explode(',', $currentFilter);
542
        $filterConst = constant(array_shift($rawFilterArgs));
543
        if (null !== $filterConst) { // skip if unknown constant
544
            $filterArgs = array();
545
            $filterArgs[] = $destination_image;
546
            $filterArgs[] = $filterConst;
547
            foreach ($rawFilterArgs as $tempValue) {
548
                $filterArgs[] = trim($tempValue);
549
            }
550
            call_user_func_array('imagefilter', $filterArgs);
551
        }
552
    }
553
}
554
555
// Round corners
556
if (ENABLE_ROUNDCORNER && !empty($radius)) {
557
    $radii = explode(',', $radius);
558
    switch (count($radii)) {
559 View Code Duplication
        case 1:
560
            $radii[3] = $radii[2] = $radii[1] = $radii[0];
561
            break;
562 View Code Duplication
        case 2:
563
            $radii[3] = $radii[0];
564
            $radii[2] = $radii[1];
565
            break;
566
        case 3:
567
            $radii[3] = $radii[0];
568
            break;
569
        case 4:
570
            // NOP
571
            break;
572
    }
573
574
    $destination_image = imageCreateCorners($destination_image, $radii);
575
    // we need png to support the alpha corners correctly
576
    if ($imageMimetype === 'image/jpeg') {
577
        $output_function = 'imagepng';
578
        $imageMimetype = 'image/png';
579
        $do_sharpen = false;
580
        $quality = round(10 - ($quality / 10));
581
    }
582
}
583
584
// Imagerotate
585
if (ENABLE_IMAGEROTATE && !empty($angle)) {
586
    $destination_image = imagerotate($destination_image, $angle, $background, 0);
587
}
588
589
if ($do_sharpen) {
590
    // Sharpen the image based on two things:
591
    // (1) the difference between the original size and the final size
592
    // (2) the final size
593
    $sharpness = findSharp($imageWidth, $tn_width);
594
    $sharpen_matrix = array(
595
        array(-1, -2, -1),
596
        array(-2, $sharpness + 12, -2),
597
        array(-1, -2, -1));
598
    $divisor = $sharpness;
599
    $offset = 0;
600
    imageconvolution($destination_image, $sharpen_matrix, $divisor, $offset);
601
}
602
603
// Put the data of the resized image into a variable
604
ob_start();
605
$output_function($destination_image, null, $quality);
606
$imageData = ob_get_contents();
607
ob_end_clean();
608
// Update $image_created_time
609
$imageCreatedTime = time();
610
611
// Clean up the memory
612
imagedestroy($sourceImage);
613
imagedestroy($destination_image);
614
615
/*
616
 * Write the just edited image into the Xoops cache
617
 */
618
$cached_image['edited_image_filename'] = $edited_image_filename;
619
$cached_image['image_data'] = $imageData;
620
$cached_image['cached_time'] = $imageCreatedTime;
621
XoopsCache::write($edited_image_filename, $cached_image);
622
623
/*
624
 * Send the edited image to the browser
625
 */
626
// See if the browser already has the image
627
$last_modified_string = gmdate('D, d M Y H:i:s', $imageCreatedTime) . ' GMT';
628
$etag = md5($imageData);
629
doConditionalGet($etag, $last_modified_string);
630
631
header('HTTP/1.1 200 OK');
632
// if image is cacheable
633
if (!isset($_GET['nocache']) && !isset($_GET['nobrowsercache'])) {
634
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $imageCreatedTime) . 'GMT');
635
    header('Cache-control: max-age=31536000');
636
    header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . 'GMT');
637
} else {
638
    // "Kill" the browser cache
639
    header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // past date
640
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
641
    header('Cache-Control: no-store, no-cache, must-revalidate'); // HTTP/1.1
642
    header('Cache-Control: post-check=0, pre-check=0', false);
643
    header('Pragma: no-cache'); // HTTP/1.0
644
}
645
header("Content-type: {$imageMimetype}");
646
header("Content-disposition: filename={$imageFilename}");
647
header('Content-Length: ' . strlen($imageData));
648
echo $imageData;
649