comparison forum/Sources/Subs-Graphics.php @ 76:e3e11437ecea website

Add forum code
author Chris Cannam
date Sun, 07 Jul 2013 11:25:48 +0200
parents
children
comparison
equal deleted inserted replaced
75:72f59aa7e503 76:e3e11437ecea
1 <?php
2
3 /**
4 * Simple Machines Forum (SMF)
5 *
6 * @package SMF
7 * @author Simple Machines http://www.simplemachines.org
8 * @copyright 2011 Simple Machines
9 * @license http://www.simplemachines.org/about/smf/license.php BSD
10 *
11 * @version 2.0
12 */
13
14 // TrueType fonts supplied by www.LarabieFonts.com
15
16 if (!defined('SMF'))
17 die('Hacking attempt...');
18
19 /* This whole file deals almost exclusively with handling avatars,
20 specifically uploaded ones. It uses, for gifs at least, Gif Util... for
21 more information on that, please see its website, shown above. The other
22 functions are as follows:
23
24 bool downloadAvatar(string url, int id_member, int max_width,
25 int max_height)
26 - downloads file from url and stores it locally for avatar use
27 by id_member.
28 - supports GIF, JPG, PNG, BMP and WBMP formats.
29 - detects if GD2 is available.
30 - if GIF support isn't present in GD, handles GIFs with gif_loadFile()
31 and gif_outputAsPng().
32 - uses resizeImageFile() to resize to max_width by max_height,
33 and saves the result to a file.
34 - updates the database info for the member's avatar.
35 - returns whether the download and resize was successful.
36
37 bool createThumbnail(string source, int max_width, int max_height)
38 - create a thumbnail of the given source.
39 - uses the resizeImageFile function to achieve the resize.
40 - returns whether the thumbnail creation was successful.
41
42 bool reencodeImage(string fileName, int preferred_format = 0)
43 - creates a copy of the file at the same location as fileName.
44 - the file would have the format preferred_format if possible,
45 otherwise the default format is jpeg.
46 - makes sure that all non-essential image contents are disposed.
47 - returns true on success, false on failure.
48
49 bool checkImageContents(string fileName, bool extensiveCheck = false)
50 - searches through the file to see if there's non-binary content.
51 - if extensiveCheck is true, searches for asp/php short tags as well.
52 - returns true on success, false on failure.
53
54 bool checkGD()
55 - sets a global $gd2 variable needed by some functions to determine
56 whetehr the GD2 library is present.
57 - returns whether or not GD1 is available.
58
59 void resizeImageFile(string source, string destination,
60 int max_width, int max_height, int preferred_format = 0)
61 - resizes an image from a remote location or a local file.
62 - puts the resized image at the destination location.
63 - the file would have the format preferred_format if possible,
64 otherwise the default format is jpeg.
65 - returns whether it succeeded.
66
67 void resizeImage(resource src_img, string destination_filename,
68 int src_width, int src_height, int max_width, int max_height,
69 int preferred_format)
70 - resizes src_img proportionally to fit within max_width and
71 max_height limits if it is too large.
72 - if GD2 is present, it'll use it to achieve better quality.
73 - saves the new image to destination_filename.
74 - saves as preferred_format if possible, default is jpeg.
75
76 void imagecopyresamplebicubic(resource dest_img, resource src_img,
77 int dest_x, int dest_y, int src_x, int src_y, int dest_w,
78 int dest_h, int src_w, int src_h)
79 - used when imagecopyresample() is not available.
80
81 resource gif_loadFile(string filename, int animation_index)
82 - loads a gif file with the Yamasoft GIF utility class.
83 - returns a new GD image.
84
85 bool gif_outputAsPng(resource gif, string destination_filename,
86 int bgColor = -1)
87 - writes a gif file to disk as a png file.
88 - returns whether it was successful or not.
89
90 bool imagecreatefrombmp(string filename)
91 - is set only if it doesn't already exist (for forwards compatiblity.)
92 - only supports uncompressed bitmaps.
93 - returns an image identifier representing the bitmap image obtained
94 from the given filename.
95
96 bool showCodeImage(string code)
97 - show an image containing the visual verification code for registration.
98 - requires the GD extension.
99 - uses a random font for each letter from default_theme_dir/fonts.
100 - outputs a gif or a png (depending on whether gif ix supported).
101 - returns false if something goes wrong.
102
103 bool showLetterImage(string letter)
104 - show a letter for the visual verification code.
105 - alternative function for showCodeImage() in case GD is missing.
106 - includes an image from a random sub directory of
107 default_theme_dir/fonts.
108 */
109
110 function downloadAvatar($url, $memID, $max_width, $max_height)
111 {
112 global $modSettings, $sourcedir, $smcFunc;
113
114 $ext = !empty($modSettings['avatar_download_png']) ? 'png' : 'jpeg';
115 $destName = 'avatar_' . $memID . '_' . time() . '.' . $ext;
116
117 // Just making sure there is a non-zero member.
118 if (empty($memID))
119 return false;
120
121 require_once($sourcedir . '/ManageAttachments.php');
122 removeAttachments(array('id_member' => $memID));
123
124 $id_folder = !empty($modSettings['currentAttachmentUploadDir']) ? $modSettings['currentAttachmentUploadDir'] : 1;
125 $avatar_hash = empty($modSettings['custom_avatar_enabled']) ? getAttachmentFilename($destName, false, null, true) : '';
126 $smcFunc['db_insert']('',
127 '{db_prefix}attachments',
128 array(
129 'id_member' => 'int', 'attachment_type' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-255', 'fileext' => 'string-8', 'size' => 'int',
130 'id_folder' => 'int',
131 ),
132 array(
133 $memID, empty($modSettings['custom_avatar_enabled']) ? 0 : 1, $destName, $avatar_hash, $ext, 1,
134 $id_folder,
135 ),
136 array('id_attach')
137 );
138 $attachID = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach');
139 // Retain this globally in case the script wants it.
140 $modSettings['new_avatar_data'] = array(
141 'id' => $attachID,
142 'filename' => $destName,
143 'type' => empty($modSettings['custom_avatar_enabled']) ? 0 : 1,
144 );
145
146 $destName = (empty($modSettings['custom_avatar_enabled']) ? (is_array($modSettings['attachmentUploadDir']) ? $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']] : $modSettings['attachmentUploadDir']) : $modSettings['custom_avatar_dir']) . '/' . $destName . '.tmp';
147
148 // Resize it.
149 if (!empty($modSettings['avatar_download_png']))
150 $success = resizeImageFile($url, $destName, $max_width, $max_height, 3);
151 else
152 $success = resizeImageFile($url, $destName, $max_width, $max_height);
153
154 // Remove the .tmp extension.
155 $destName = substr($destName, 0, -4);
156
157 if ($success)
158 {
159 // Walk the right path.
160 if (!empty($modSettings['currentAttachmentUploadDir']))
161 {
162 if (!is_array($modSettings['attachmentUploadDir']))
163 $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
164 $path = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
165 }
166 else
167 $path = $modSettings['attachmentUploadDir'];
168
169 // Remove the .tmp extension from the attachment.
170 if (rename($destName . '.tmp', empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash))
171 {
172 $destName = empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash;
173 list ($width, $height) = getimagesize($destName);
174 $mime_type = 'image/' . $ext;
175
176 // Write filesize in the database.
177 $smcFunc['db_query']('', '
178 UPDATE {db_prefix}attachments
179 SET size = {int:filesize}, width = {int:width}, height = {int:height},
180 mime_type = {string:mime_type}
181 WHERE id_attach = {int:current_attachment}',
182 array(
183 'filesize' => filesize($destName),
184 'width' => (int) $width,
185 'height' => (int) $height,
186 'current_attachment' => $attachID,
187 'mime_type' => $mime_type,
188 )
189 );
190 return true;
191 }
192 else
193 return false;
194 }
195 else
196 {
197 $smcFunc['db_query']('', '
198 DELETE FROM {db_prefix}attachments
199 WHERE id_attach = {int:current_attachment}',
200 array(
201 'current_attachment' => $attachID,
202 )
203 );
204
205 @unlink($destName . '.tmp');
206 return false;
207 }
208 }
209
210 function createThumbnail($source, $max_width, $max_height)
211 {
212 global $modSettings;
213
214 $destName = $source . '_thumb.tmp';
215
216 // Do the actual resize.
217 if (!empty($modSettings['attachment_thumb_png']))
218 $success = resizeImageFile($source, $destName, $max_width, $max_height, 3);
219 else
220 $success = resizeImageFile($source, $destName, $max_width, $max_height);
221
222 // Okay, we're done with the temporary stuff.
223 $destName = substr($destName, 0, -4);
224
225 if ($success && @rename($destName . '.tmp', $destName))
226 return true;
227 else
228 {
229 @unlink($destName . '.tmp');
230 @touch($destName);
231 return false;
232 }
233 }
234
235 function reencodeImage($fileName, $preferred_format = 0)
236 {
237 // There is nothing we can do without GD, sorry!
238 if (!checkGD())
239 return false;
240
241 if (!resizeImageFile($fileName, $fileName . '.tmp', null, null, $preferred_format))
242 {
243 if (file_exists($fileName . '.tmp'))
244 unlink($fileName . '.tmp');
245
246 return false;
247 }
248
249 if (!unlink($fileName))
250 return false;
251
252 if (!rename($fileName . '.tmp', $fileName))
253 return false;
254
255 return true;
256 }
257
258 function checkImageContents($fileName, $extensiveCheck = false)
259 {
260 $fp = fopen($fileName, 'rb');
261 if (!$fp)
262 fatal_lang_error('attach_timeout');
263
264 $prev_chunk = '';
265 while (!feof($fp))
266 {
267 $cur_chunk = fread($fp, 8192);
268
269 // Though not exhaustive lists, better safe than sorry.
270 if (!empty($extensiveCheck))
271 {
272 // Paranoid check. Some like it that way.
273 if (preg_match('~(iframe|\\<\\?|\\<%|html|eval|body|script\W|[CF]WS[\x01-\x0C])~i', $prev_chunk . $cur_chunk) === 1)
274 {
275 fclose($fp);
276 return false;
277 }
278 }
279 else
280 {
281 // Check for potential infection
282 if (preg_match('~(iframe|html|eval|body|script\W|[CF]WS[\x01-\x0C])~i', $prev_chunk . $cur_chunk) === 1)
283 {
284 fclose($fp);
285 return false;
286 }
287 }
288 $prev_chunk = $cur_chunk;
289 }
290 fclose($fp);
291
292 return true;
293 }
294
295 function checkGD()
296 {
297 global $gd2;
298
299 // Check to see if GD is installed and what version.
300 if (($extensionFunctions = get_extension_funcs('gd')) === false)
301 return false;
302
303 // Also determine if GD2 is installed and store it in a global.
304 $gd2 = in_array('imagecreatetruecolor', $extensionFunctions) && function_exists('imagecreatetruecolor');
305
306 return true;
307 }
308
309 function resizeImageFile($source, $destination, $max_width, $max_height, $preferred_format = 0)
310 {
311 global $sourcedir;
312
313 // Nothing to do without GD
314 if (!checkGD())
315 return false;
316
317 static $default_formats = array(
318 '1' => 'gif',
319 '2' => 'jpeg',
320 '3' => 'png',
321 '6' => 'bmp',
322 '15' => 'wbmp'
323 );
324
325 require_once($sourcedir . '/Subs-Package.php');
326 @ini_set('memory_limit', '90M');
327
328 $success = false;
329
330 // Get the image file, we have to work with something after all
331 $fp_destination = fopen($destination, 'wb');
332 if ($fp_destination && substr($source, 0, 7) == 'http://')
333 {
334 $fileContents = fetch_web_data($source);
335
336 fwrite($fp_destination, $fileContents);
337 fclose($fp_destination);
338
339 $sizes = @getimagesize($destination);
340 }
341 elseif ($fp_destination)
342 {
343 $sizes = @getimagesize($source);
344
345 $fp_source = fopen($source, 'rb');
346 if ($fp_source !== false)
347 {
348 while (!feof($fp_source))
349 fwrite($fp_destination, fread($fp_source, 8192));
350 fclose($fp_source);
351 }
352 else
353 $sizes = array(-1, -1, -1);
354 fclose($fp_destination);
355 }
356 // We can't get to the file.
357 else
358 $sizes = array(-1, -1, -1);
359
360 // Gif? That might mean trouble if gif support is not available.
361 if ($sizes[2] == 1 && !function_exists('imagecreatefromgif') && function_exists('imagecreatefrompng'))
362 {
363 // Download it to the temporary file... use the special gif library... and save as png.
364 if ($img = @gif_loadFile($destination) && gif_outputAsPng($img, $destination))
365 $sizes[2] = 3;
366 }
367
368 // A known and supported format?
369 if (isset($default_formats[$sizes[2]]) && function_exists('imagecreatefrom' . $default_formats[$sizes[2]]))
370 {
371 $imagecreatefrom = 'imagecreatefrom' . $default_formats[$sizes[2]];
372 if ($src_img = @$imagecreatefrom($destination))
373 {
374 resizeImage($src_img, $destination, imagesx($src_img), imagesy($src_img), $max_width === null ? imagesx($src_img) : $max_width, $max_height === null ? imagesy($src_img) : $max_height, true, $preferred_format);
375 $success = true;
376 }
377 }
378
379 return $success;
380 }
381
382 function resizeImage($src_img, $destName, $src_width, $src_height, $max_width, $max_height, $force_resize = false, $preferred_format = 0)
383 {
384 global $gd2, $modSettings;
385
386 // Without GD, no image resizing at all.
387 if (!checkGD())
388 return false;
389
390 $success = false;
391
392 // Determine whether to resize to max width or to max height (depending on the limits.)
393 if (!empty($max_width) || !empty($max_height))
394 {
395 if (!empty($max_width) && (empty($max_height) || $src_height * $max_width / $src_width <= $max_height))
396 {
397 $dst_width = $max_width;
398 $dst_height = floor($src_height * $max_width / $src_width);
399 }
400 elseif (!empty($max_height))
401 {
402 $dst_width = floor($src_width * $max_height / $src_height);
403 $dst_height = $max_height;
404 }
405
406 // Don't bother resizing if it's already smaller...
407 if (!empty($dst_width) && !empty($dst_height) && ($dst_width < $src_width || $dst_height < $src_height || $force_resize))
408 {
409 // (make a true color image, because it just looks better for resizing.)
410 if ($gd2)
411 {
412 $dst_img = imagecreatetruecolor($dst_width, $dst_height);
413
414 // Deal nicely with a PNG - because we can.
415 if ((!empty($preferred_format)) && ($preferred_format == 3))
416 {
417 imagealphablending($dst_img, false);
418 if (function_exists('imagesavealpha'))
419 imagesavealpha($dst_img, true);
420 }
421 }
422 else
423 $dst_img = imagecreate($dst_width, $dst_height);
424
425 // Resize it!
426 if ($gd2)
427 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
428 else
429 imagecopyresamplebicubic($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
430 }
431 else
432 $dst_img = $src_img;
433 }
434 else
435 $dst_img = $src_img;
436
437 // Save the image as ...
438 if (!empty($preferred_format) && ($preferred_format == 3) && function_exists('imagepng'))
439 $success = imagepng($dst_img, $destName);
440 elseif (!empty($preferred_format) && ($preferred_format == 1) && function_exists('imagegif'))
441 $success = imagegif($dst_img, $destName);
442 elseif (function_exists('imagejpeg'))
443 $success = imagejpeg($dst_img, $destName);
444
445 // Free the memory.
446 imagedestroy($src_img);
447 if ($dst_img != $src_img)
448 imagedestroy($dst_img);
449
450 return $success;
451 }
452
453 function imagecopyresamplebicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
454 {
455 $palsize = imagecolorstotal($src_img);
456 for ($i = 0; $i < $palsize; $i++)
457 {
458 $colors = imagecolorsforindex($src_img, $i);
459 imagecolorallocate($dst_img, $colors['red'], $colors['green'], $colors['blue']);
460 }
461
462 $scaleX = ($src_w - 1) / $dst_w;
463 $scaleY = ($src_h - 1) / $dst_h;
464
465 $scaleX2 = (int) $scaleX / 2;
466 $scaleY2 = (int) $scaleY / 2;
467
468 for ($j = $src_y; $j < $dst_h; $j++)
469 {
470 $sY = (int) $j * $scaleY;
471 $y13 = $sY + $scaleY2;
472
473 for ($i = $src_x; $i < $dst_w; $i++)
474 {
475 $sX = (int) $i * $scaleX;
476 $x34 = $sX + $scaleX2;
477
478 $color1 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $y13));
479 $color2 = imagecolorsforindex($src_img, imagecolorat($src_img, $sX, $sY));
480 $color3 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $y13));
481 $color4 = imagecolorsforindex($src_img, imagecolorat($src_img, $x34, $sY));
482
483 $red = ($color1['red'] + $color2['red'] + $color3['red'] + $color4['red']) / 4;
484 $green = ($color1['green'] + $color2['green'] + $color3['green'] + $color4['green']) / 4;
485 $blue = ($color1['blue'] + $color2['blue'] + $color3['blue'] + $color4['blue']) / 4;
486
487 $color = imagecolorresolve($dst_img, $red, $green, $blue);
488 if ($color == -1)
489 {
490 if ($palsize++ < 256)
491 imagecolorallocate($dst_img, $red, $green, $blue);
492 $color = imagecolorclosest($dst_img, $red, $green, $blue);
493 }
494
495 imagesetpixel($dst_img, $i + $dst_x - $src_x, $j + $dst_y - $src_y, $color);
496 }
497 }
498 }
499
500 if (!function_exists('imagecreatefrombmp'))
501 {
502 function imagecreatefrombmp($filename)
503 {
504 global $gd2;
505
506 $fp = fopen($filename, 'rb');
507
508 $errors = error_reporting(0);
509
510 $header = unpack('vtype/Vsize/Vreserved/Voffset', fread($fp, 14));
511 $info = unpack('Vsize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vncolor/Vcolorimportant', fread($fp, 40));
512
513 if ($header['type'] != 0x4D42)
514 false;
515
516 if ($gd2)
517 $dst_img = imagecreatetruecolor($info['width'], $info['height']);
518 else
519 $dst_img = imagecreate($info['width'], $info['height']);
520
521 $palette_size = $header['offset'] - 54;
522 $info['ncolor'] = $palette_size / 4;
523
524 $palette = array();
525
526 $palettedata = fread($fp, $palette_size);
527 $n = 0;
528 for ($j = 0; $j < $palette_size; $j++)
529 {
530 $b = ord($palettedata{$j++});
531 $g = ord($palettedata{$j++});
532 $r = ord($palettedata{$j++});
533
534 $palette[$n++] = imagecolorallocate($dst_img, $r, $g, $b);
535 }
536
537 $scan_line_size = ($info['bits'] * $info['width'] + 7) >> 3;
538 $scan_line_align = $scan_line_size & 3 ? 4 - ($scan_line_size & 3) : 0;
539
540 for ($y = 0, $l = $info['height'] - 1; $y < $info['height']; $y++, $l--)
541 {
542 fseek($fp, $header['offset'] + ($scan_line_size + $scan_line_align) * $l);
543 $scan_line = fread($fp, $scan_line_size);
544
545 if (strlen($scan_line) < $scan_line_size)
546 continue;
547
548 if ($info['bits'] == 32)
549 {
550 $x = 0;
551 for ($j = 0; $j < $scan_line_size; $x++)
552 {
553 $b = ord($scan_line{$j++});
554 $g = ord($scan_line{$j++});
555 $r = ord($scan_line{$j++});
556 $j++;
557
558 $color = imagecolorexact($dst_img, $r, $g, $b);
559 if ($color == -1)
560 {
561 $color = imagecolorallocate($dst_img, $r, $g, $b);
562
563 // Gah! Out of colors? Stupid GD 1... try anyhow.
564 if ($color == -1)
565 $color = imagecolorclosest($dst_img, $r, $g, $b);
566 }
567
568 imagesetpixel($dst_img, $x, $y, $color);
569 }
570 }
571 elseif ($info['bits'] == 24)
572 {
573 $x = 0;
574 for ($j = 0; $j < $scan_line_size; $x++)
575 {
576 $b = ord($scan_line{$j++});
577 $g = ord($scan_line{$j++});
578 $r = ord($scan_line{$j++});
579
580 $color = imagecolorexact($dst_img, $r, $g, $b);
581 if ($color == -1)
582 {
583 $color = imagecolorallocate($dst_img, $r, $g, $b);
584
585 // Gah! Out of colors? Stupid GD 1... try anyhow.
586 if ($color == -1)
587 $color = imagecolorclosest($dst_img, $r, $g, $b);
588 }
589
590 imagesetpixel($dst_img, $x, $y, $color);
591 }
592 }
593 elseif ($info['bits'] == 16)
594 {
595 $x = 0;
596 for ($j = 0; $j < $scan_line_size; $x++)
597 {
598 $b1 = ord($scan_line{$j++});
599 $b2 = ord($scan_line{$j++});
600
601 $word = $b2 * 256 + $b1;
602
603 $b = (($word & 31) * 255) / 31;
604 $g = ((($word >> 5) & 31) * 255) / 31;
605 $r = ((($word >> 10) & 31) * 255) / 31;
606
607 // Scale the image colors up properly.
608 $color = imagecolorexact($dst_img, $r, $g, $b);
609 if ($color == -1)
610 {
611 $color = imagecolorallocate($dst_img, $r, $g, $b);
612
613 // Gah! Out of colors? Stupid GD 1... try anyhow.
614 if ($color == -1)
615 $color = imagecolorclosest($dst_img, $r, $g, $b);
616 }
617
618 imagesetpixel($dst_img, $x, $y, $color);
619 }
620 }
621 elseif ($info['bits'] == 8)
622 {
623 $x = 0;
624 for ($j = 0; $j < $scan_line_size; $x++)
625 imagesetpixel($dst_img, $x, $y, $palette[ord($scan_line{$j++})]);
626 }
627 elseif ($info['bits'] == 4)
628 {
629 $x = 0;
630 for ($j = 0; $j < $scan_line_size; $x++)
631 {
632 $byte = ord($scan_line{$j++});
633
634 imagesetpixel($dst_img, $x, $y, $palette[(int) ($byte / 16)]);
635 if (++$x < $info['width'])
636 imagesetpixel($dst_img, $x, $y, $palette[$byte & 15]);
637 }
638 }
639 else
640 {
641 // Sorry, I'm just not going to do monochrome :P.
642 }
643 }
644
645 fclose($fp);
646
647 error_reporting($errors);
648
649 return $dst_img;
650 }
651 }
652
653 function gif_loadFile($lpszFileName, $iIndex = 0)
654 {
655 // The classes needed are in this file.
656 loadClassFile('Class-Graphics.php');
657 $gif = new gif_file();
658
659 if (!$gif->loadFile($lpszFileName, $iIndex))
660 return false;
661
662 return $gif;
663 }
664
665 function gif_outputAsPng($gif, $lpszFileName, $background_color = -1)
666 {
667 if (!isset($gif) || @get_class($gif) != 'cgif' || !$gif->loaded || $lpszFileName == '')
668 return false;
669
670 $fd = $gif->get_png_data($background_color);
671 if (strlen($fd) <= 0)
672 return false;
673
674 if (!($fh = @fopen($lpszFileName, 'wb')))
675 return false;
676
677 @fwrite($fh, $fd, strlen($fd));
678 @fflush($fh);
679 @fclose($fh);
680
681 return true;
682 }
683
684 // Create the image for the visual verification code.
685 function showCodeImage($code)
686 {
687 global $settings, $user_info, $modSettings;
688
689 /*
690 Note: The higher the value of visual_verification_type the harder the verification is - from 0 as disabled through to 4 as "Very hard".
691 */
692
693 // What type are we going to be doing?
694 $imageType = $modSettings['visual_verification_type'];
695 // Special case to allow the admin center to show samples.
696 if ($user_info['is_admin'] && isset($_GET['type']))
697 $imageType = (int) $_GET['type'];
698
699 // Some quick references for what we do.
700 // Do we show no, low or high noise?
701 $noiseType = $imageType == 3 ? 'low' : ($imageType == 4 ? 'high' : ($imageType == 5 ? 'extreme' : 'none'));
702 // Can we have more than one font in use?
703 $varyFonts = $imageType > 3 ? true : false;
704 // Just a plain white background?
705 $simpleBGColor = $imageType < 3 ? true : false;
706 // Plain black foreground?
707 $simpleFGColor = $imageType == 0 ? true : false;
708 // High much to rotate each character.
709 $rotationType = $imageType == 1 ? 'none' : ($imageType > 3 ? 'low' : 'high');
710 // Do we show some characters inversed?
711 $showReverseChars = $imageType > 3 ? true : false;
712 // Special case for not showing any characters.
713 $disableChars = $imageType == 0 ? true : false;
714 // What do we do with the font colors. Are they one color, close to one color or random?
715 $fontColorType = $imageType == 1 ? 'plain' : ($imageType > 3 ? 'random' : 'cyclic');
716 // Are the fonts random sizes?
717 $fontSizeRandom = $imageType > 3 ? true : false;
718 // How much space between characters?
719 $fontHorSpace = $imageType > 3 ? 'high' : ($imageType == 1 ? 'medium' : 'minus');
720 // Where do characters sit on the image? (Fixed position or random/very random)
721 $fontVerPos = $imageType == 1 ? 'fixed' : ($imageType > 3 ? 'vrandom' : 'random');
722 // Make font semi-transparent?
723 $fontTrans = $imageType == 2 || $imageType == 3 ? true : false;
724 // Give the image a border?
725 $hasBorder = $simpleBGColor;
726
727 // Is this GD2? Needed for pixel size.
728 $testGD = get_extension_funcs('gd');
729 $gd2 = in_array('imagecreatetruecolor', $testGD) && function_exists('imagecreatetruecolor');
730 unset($testGD);
731
732 // The amount of pixels inbetween characters.
733 $character_spacing = 1;
734
735 // What color is the background - generally white unless we're on "hard".
736 if ($simpleBGColor)
737 $background_color = array(255, 255, 255);
738 else
739 $background_color = isset($settings['verification_background']) ? $settings['verification_background'] : array(236, 237, 243);
740
741 // The color of the characters shown (red, green, blue).
742 if ($simpleFGColor)
743 $foreground_color = array(0, 0, 0);
744 else
745 {
746 $foreground_color = array(64, 101, 136);
747
748 // Has the theme author requested a custom color?
749 if (isset($settings['verification_foreground']))
750 $foreground_color = $settings['verification_foreground'];
751 }
752
753 if (!is_dir($settings['default_theme_dir'] . '/fonts'))
754 return false;
755
756 // Get a list of the available fonts.
757 $font_dir = dir($settings['default_theme_dir'] . '/fonts');
758 $font_list = array();
759 $ttfont_list = array();
760 while ($entry = $font_dir->read())
761 {
762 if (preg_match('~^(.+)\.gdf$~', $entry, $matches) === 1)
763 $font_list[] = $entry;
764 elseif (preg_match('~^(.+)\.ttf$~', $entry, $matches) === 1)
765 $ttfont_list[] = $entry;
766 }
767
768 if (empty($font_list))
769 return false;
770
771 // For non-hard things don't even change fonts.
772 if (!$varyFonts)
773 {
774 $font_list = array($font_list[0]);
775 // Try use Screenge if we can - it looks good!
776 if (in_array('Screenge.ttf', $ttfont_list))
777 $ttfont_list = array('Screenge.ttf');
778 else
779 $ttfont_list = empty($ttfont_list) ? array() : array($ttfont_list[0]);
780
781 }
782
783 // Create a list of characters to be shown.
784 $characters = array();
785 $loaded_fonts = array();
786 for ($i = 0; $i < strlen($code); $i++)
787 {
788 $characters[$i] = array(
789 'id' => $code{$i},
790 'font' => array_rand($font_list),
791 );
792
793 $loaded_fonts[$characters[$i]['font']] = null;
794 }
795
796 // Load all fonts and determine the maximum font height.
797 foreach ($loaded_fonts as $font_index => $dummy)
798 $loaded_fonts[$font_index] = imageloadfont($settings['default_theme_dir'] . '/fonts/' . $font_list[$font_index]);
799
800 // Determine the dimensions of each character.
801 $total_width = $character_spacing * strlen($code) + 20;
802 $max_height = 0;
803 foreach ($characters as $char_index => $character)
804 {
805 $characters[$char_index]['width'] = imagefontwidth($loaded_fonts[$character['font']]);
806 $characters[$char_index]['height'] = imagefontheight($loaded_fonts[$character['font']]);
807
808 $max_height = max($characters[$char_index]['height'] + 5, $max_height);
809 $total_width += $characters[$char_index]['width'];
810 }
811
812 // Create an image.
813 $code_image = $gd2 ? imagecreatetruecolor($total_width, $max_height) : imagecreate($total_width, $max_height);
814
815 // Draw the background.
816 $bg_color = imagecolorallocate($code_image, $background_color[0], $background_color[1], $background_color[2]);
817 imagefilledrectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $bg_color);
818
819 // Randomize the foreground color a little.
820 for ($i = 0; $i < 3; $i++)
821 $foreground_color[$i] = mt_rand(max($foreground_color[$i] - 3, 0), min($foreground_color[$i] + 3, 255));
822 $fg_color = imagecolorallocate($code_image, $foreground_color[0], $foreground_color[1], $foreground_color[2]);
823
824 // Color for the dots.
825 for ($i = 0; $i < 3; $i++)
826 $dotbgcolor[$i] = $background_color[$i] < $foreground_color[$i] ? mt_rand(0, max($foreground_color[$i] - 20, 0)) : mt_rand(min($foreground_color[$i] + 20, 255), 255);
827 $randomness_color = imagecolorallocate($code_image, $dotbgcolor[0], $dotbgcolor[1], $dotbgcolor[2]);
828
829 // Some squares/rectanges for new extreme level
830 if ($noiseType == 'extreme')
831 {
832 for ($i = 0; $i < rand(1, 5); $i++)
833 {
834 $x1 = rand(0, $total_width / 4);
835 $x2 = $x1 + round(rand($total_width / 4, $total_width));
836 $y1 = rand(0, $max_height);
837 $y2 = $y1 + round(rand(0, $max_height / 3));
838 imagefilledrectangle($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
839 }
840 }
841
842 // Fill in the characters.
843 if (!$disableChars)
844 {
845 $cur_x = 0;
846 foreach ($characters as $char_index => $character)
847 {
848 // Can we use true type fonts?
849 $can_do_ttf = function_exists('imagettftext');
850
851 // How much rotation will we give?
852 if ($rotationType == 'none')
853 $angle = 0;
854 else
855 $angle = mt_rand(-100, 100) / ($rotationType == 'high' ? 6 : 10);
856
857 // What color shall we do it?
858 if ($fontColorType == 'cyclic')
859 {
860 // Here we'll pick from a set of acceptance types.
861 $colors = array(
862 array(10, 120, 95),
863 array(46, 81, 29),
864 array(4, 22, 154),
865 array(131, 9, 130),
866 array(0, 0, 0),
867 array(143, 39, 31),
868 );
869 if (!isset($last_index))
870 $last_index = -1;
871 $new_index = $last_index;
872 while ($last_index == $new_index)
873 $new_index = mt_rand(0, count($colors) - 1);
874 $char_fg_color = $colors[$new_index];
875 $last_index = $new_index;
876 }
877 elseif ($fontColorType == 'random')
878 $char_fg_color = array(mt_rand(max($foreground_color[0] - 2, 0), $foreground_color[0]), mt_rand(max($foreground_color[1] - 2, 0), $foreground_color[1]), mt_rand(max($foreground_color[2] - 2, 0), $foreground_color[2]));
879 else
880 $char_fg_color = array($foreground_color[0], $foreground_color[1], $foreground_color[2]);
881
882 if (!empty($can_do_ttf))
883 {
884 // GD2 handles font size differently.
885 if ($fontSizeRandom)
886 $font_size = $gd2 ? mt_rand(17, 19) : mt_rand(18, 25);
887 else
888 $font_size = $gd2 ? 18 : 24;
889
890 // Work out the sizes - also fix the character width cause TTF not quite so wide!
891 $font_x = $fontHorSpace == 'minus' && $cur_x > 0 ? $cur_x - 3 : $cur_x + 5;
892 $font_y = $max_height - ($fontVerPos == 'vrandom' ? mt_rand(2, 8) : ($fontVerPos == 'random' ? mt_rand(3, 5) : 5));
893
894 // What font face?
895 if (!empty($ttfont_list))
896 $fontface = $settings['default_theme_dir'] . '/fonts/' . $ttfont_list[mt_rand(0, count($ttfont_list) - 1)];
897
898 // What color are we to do it in?
899 $is_reverse = $showReverseChars ? mt_rand(0, 1) : false;
900 $char_color = function_exists('imagecolorallocatealpha') && $fontTrans ? imagecolorallocatealpha($code_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2], 50) : imagecolorallocate($code_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2]);
901
902 $fontcord = @imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $char_color, $fontface, $character['id']);
903 if (empty($fontcord))
904 $can_do_ttf = false;
905 elseif ($is_reverse)
906 {
907 imagefilledpolygon($code_image, $fontcord, 4, $fg_color);
908 // Put the character back!
909 imagettftext($code_image, $font_size, $angle, $font_x, $font_y, $randomness_color, $fontface, $character['id']);
910 }
911
912 if ($can_do_ttf)
913 $cur_x = max($fontcord[2], $fontcord[4]) + ($angle == 0 ? 0 : 3);
914 }
915
916 if (!$can_do_ttf)
917 {
918 // Rotating the characters a little...
919 if (function_exists('imagerotate'))
920 {
921 $char_image = $gd2 ? imagecreatetruecolor($character['width'], $character['height']) : imagecreate($character['width'], $character['height']);
922 $char_bgcolor = imagecolorallocate($char_image, $background_color[0], $background_color[1], $background_color[2]);
923 imagefilledrectangle($char_image, 0, 0, $character['width'] - 1, $character['height'] - 1, $char_bgcolor);
924 imagechar($char_image, $loaded_fonts[$character['font']], 0, 0, $character['id'], imagecolorallocate($char_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2]));
925 $rotated_char = imagerotate($char_image, mt_rand(-100, 100) / 10, $char_bgcolor);
926 imagecopy($code_image, $rotated_char, $cur_x, 0, 0, 0, $character['width'], $character['height']);
927 imagedestroy($rotated_char);
928 imagedestroy($char_image);
929 }
930
931 // Sorry, no rotation available.
932 else
933 imagechar($code_image, $loaded_fonts[$character['font']], $cur_x, floor(($max_height - $character['height']) / 2), $character['id'], imagecolorallocate($code_image, $char_fg_color[0], $char_fg_color[1], $char_fg_color[2]));
934 $cur_x += $character['width'] + $character_spacing;
935 }
936 }
937 }
938 // If disabled just show a cross.
939 else
940 {
941 imageline($code_image, 0, 0, $total_width, $max_height, $fg_color);
942 imageline($code_image, 0, $max_height, $total_width, 0, $fg_color);
943 }
944
945 // Make the background color transparent on the hard image.
946 if (!$simpleBGColor)
947 imagecolortransparent($code_image, $bg_color);
948 if ($hasBorder)
949 imagerectangle($code_image, 0, 0, $total_width - 1, $max_height - 1, $fg_color);
950
951 // Add some noise to the background?
952 if ($noiseType != 'none')
953 {
954 for ($i = mt_rand(0, 2); $i < $max_height; $i += mt_rand(1, 2))
955 for ($j = mt_rand(0, 10); $j < $total_width; $j += mt_rand(1, 10))
956 imagesetpixel($code_image, $j, $i, mt_rand(0, 1) ? $fg_color : $randomness_color);
957
958 // Put in some lines too?
959 if ($noiseType != 'extreme')
960 {
961 $num_lines = $noiseType == 'high' ? mt_rand(3, 7) : mt_rand(2, 5);
962 for ($i = 0; $i < $num_lines; $i++)
963 {
964 if (mt_rand(0, 1))
965 {
966 $x1 = mt_rand(0, $total_width);
967 $x2 = mt_rand(0, $total_width);
968 $y1 = 0; $y2 = $max_height;
969 }
970 else
971 {
972 $y1 = mt_rand(0, $max_height);
973 $y2 = mt_rand(0, $max_height);
974 $x1 = 0; $x2 = $total_width;
975 }
976 imagesetthickness($code_image, mt_rand(1, 2));
977 imageline($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
978 }
979 }
980 else
981 {
982 // Put in some ellipse
983 $num_ellipse = $noiseType == 'extreme' ? mt_rand(6, 12) : mt_rand(2, 6);
984 for ($i = 0; $i < $num_ellipse; $i++)
985 {
986 $x1 = round(rand(($total_width / 4) * -1, $total_width + ($total_width / 4)));
987 $x2 = round(rand($total_width / 2, 2 * $total_width));
988 $y1 = round(rand(($max_height / 4) * -1, $max_height + ($max_height / 4)));
989 $y2 = round(rand($max_height / 2, 2 * $max_height));
990 imageellipse($code_image, $x1, $y1, $x2, $y2, mt_rand(0, 1) ? $fg_color : $randomness_color);
991 }
992 }
993 }
994
995 // Show the image.
996 if (function_exists('imagegif'))
997 {
998 header('Content-type: image/gif');
999 imagegif($code_image);
1000 }
1001 else
1002 {
1003 header('Content-type: image/png');
1004 imagepng($code_image);
1005 }
1006
1007 // Bail out.
1008 imagedestroy($code_image);
1009 die();
1010 }
1011
1012 // Create a letter for the visual verification code.
1013 function showLetterImage($letter)
1014 {
1015 global $settings;
1016
1017 if (!is_dir($settings['default_theme_dir'] . '/fonts'))
1018 return false;
1019
1020 // Get a list of the available font directories.
1021 $font_dir = dir($settings['default_theme_dir'] . '/fonts');
1022 $font_list = array();
1023 while ($entry = $font_dir->read())
1024 if ($entry[0] !== '.' && is_dir($settings['default_theme_dir'] . '/fonts/' . $entry) && file_exists($settings['default_theme_dir'] . '/fonts/' . $entry . '.gdf'))
1025 $font_list[] = $entry;
1026
1027 if (empty($font_list))
1028 return false;
1029
1030 // Pick a random font.
1031 $random_font = $font_list[array_rand($font_list)];
1032
1033 // Check if the given letter exists.
1034 if (!file_exists($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.gif'))
1035 return false;
1036
1037 // Include it!
1038 header('Content-type: image/gif');
1039 include($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.gif');
1040
1041 // Nothing more to come.
1042 die();
1043 }
1044
1045 ?>