Subs-Graphics.php 33 KB

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