PageRenderTime 67ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-content/plugins/si-captcha-for-wordpress/captcha-secureimage/securimage.php

https://bitbucket.org/openfarmtech/weblog-content
PHP | 1427 lines | 632 code | 200 blank | 595 comment | 174 complexity | 6c60b2dd55136f77268c05b2c89ee0a8 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, LGPL-2.0, LGPL-3.0, BSD-3-Clause, GPL-3.0, LGPL-2.1, AGPL-3.0, CC-BY-SA-3.0
  1. <?php
  2. /**
  3. * Project: Securimage: A PHP class for creating and managing form CAPTCHA images<br />
  4. * File: securimage.php<br />
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or any later version.<br /><br />
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.<br /><br />
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br /><br />
  19. *
  20. * Any modifications to the library should be indicated clearly in the source code
  21. * to inform users that the changes are not a part of the original software.<br /><br />
  22. *
  23. * If you found this script useful, please take a quick moment to rate it.<br />
  24. * http://www.hotscripts.com/rate/49400.html Thanks.
  25. *
  26. * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA
  27. * @link http://www.phpcaptcha.org/latest.zip Download Latest Version
  28. * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
  29. * @copyright 2009 Drew Phillips
  30. * @author Drew Phillips <drew@drew-phillips.com>
  31. * @version 2.0 BETA (November 15, 2009)
  32. * @package Securimage
  33. *
  34. */
  35. /**
  36. ChangeLog
  37. Fixes contributed by Mike Challis: http://www.642weather.com/weather/scripts.php
  38. - Remove image type constants (I ran into an odd installation that errored because they cannot be redeclared)
  39. - Easier color settings (now uses hex codes like #336699)
  40. - Fix for sound files on Safari (safari was trying to download securimage.wav.php instead of securimage.wav)
  41. - Improvement for PHP installation is configured without "--with-ttf".
  42. It will automatically fail over to GD Fonts when TTF Fonts are not enabled in PHP.
  43. Some users were reporting there was no error and the captcha was not working.
  44. 2.0.0
  45. - Add mathematical distortion to characters (using code from HKCaptcha)
  46. - Improved session support
  47. - Added Securimage_Color class for easier color definitions
  48. - Add distortion to audio output to prevent binary comparison attack (proposed by Sven "SavageTiger" Hagemann [insecurity.nl])
  49. - Flash button to stream mp3 audio (Douglas Walsh www.douglaswalsh.net)
  50. - Audio output is mp3 format by default
  51. - Change font to AlteHaasGrotesk by yann le coroller
  52. - Some code cleanup
  53. 1.0.4 (unreleased)
  54. - Ability to output audible codes in mp3 format to stream from flash
  55. 1.0.3.1
  56. - Error reading from wordlist in some cases caused words to be cut off 1 letter short
  57. 1.0.3
  58. - Removed shadow_text from code which could cause an undefined property error due to removal from previous version
  59. 1.0.2
  60. - Audible CAPTCHA Code wav files
  61. - Create codes from a word list instead of random strings
  62. 1.0
  63. - Added the ability to use a selected character set, rather than a-z0-9 only.
  64. - Added the multi-color text option to use different colors for each letter.
  65. - Switched to automatic session handling instead of using files for code storage
  66. - Added GD Font support if ttf support is not available. Can use internal GD fonts or load new ones.
  67. - Added the ability to set line thickness
  68. - Added option for drawing arced lines over letters
  69. - Added ability to choose image type for output
  70. */
  71. /**
  72. * Securimage CAPTCHA Class.
  73. *
  74. * @package Securimage
  75. * @subpackage classes
  76. *
  77. */
  78. class Securimage {
  79. /**
  80. * The desired width of the CAPTCHA image.
  81. *
  82. * @var int
  83. */
  84. var $image_width;
  85. /**
  86. * The desired width of the CAPTCHA image.
  87. *
  88. * @var int
  89. */
  90. var $image_height;
  91. /**
  92. * The image format for output.<br />
  93. * Valid options: png, jpg, gif
  94. *
  95. * @var string
  96. */
  97. var $image_type;
  98. /**
  99. * The length of the code to generate.
  100. *
  101. * @var int
  102. */
  103. var $code_length;
  104. /**
  105. * Form id (for multi-captchas on same page).
  106. *
  107. * @var string
  108. */
  109. var $form_id;
  110. /**
  111. * The character set for individual characters in the image.<br />
  112. * Letters are converted to uppercase.<br />
  113. * The font must support the letters or there may be problematic substitutions.
  114. *
  115. * @var string
  116. */
  117. var $charset;
  118. /**
  119. * Create codes using this word list
  120. *
  121. * @var string The path to the word list to use for creating CAPTCHA codes
  122. */
  123. var $wordlist_file;
  124. /**
  125. * Use wordlist of not
  126. *
  127. * @var bool true to use wordlist file, false to use random code
  128. */
  129. var $use_wordlist = false;
  130. /**
  131. * Note: Use of GD fonts is not recommended as many distortion features are not available<br />
  132. * The GD font to use.<br />
  133. * Internal gd fonts can be loaded by their number.<br />
  134. * Alternatively, a file path can be given and the font will be loaded from file.
  135. *
  136. * @var mixed
  137. */
  138. var $gd_font_file;
  139. /**
  140. * The approximate size of the font in pixels.<br />
  141. * This does not control the size of the font because that is determined by the GD font itself.<br />
  142. * This is used to aid the calculations of positioning used by this class.<br />
  143. *
  144. * @var int
  145. */
  146. var $gd_font_size;
  147. /**
  148. * Use a gd font instead of TTF
  149. *
  150. * @var bool true for gd font, false for TTF
  151. */
  152. var $use_gd_font;
  153. // Note: These font options below do not apply if you set $use_gd_font to true with the exception of $text_color
  154. /**
  155. * The path to the TTF font file to load.
  156. *
  157. * @var string
  158. */
  159. var $ttf_file;
  160. /**
  161. * How much to distort image, higher = more distortion.<br />
  162. * Distortion is only available when using TTF fonts.<br />
  163. *
  164. * @var float
  165. */
  166. var $perturbation;
  167. /**
  168. * The minimum angle in degrees, with 0 degrees being left-to-right reading text.<br />
  169. * Higher values represent a counter-clockwise rotation.<br />
  170. * For example, a value of 90 would result in bottom-to-top reading text.<br />
  171. * This value along with maximum angle distance do not need to be very high with perturbation
  172. *
  173. * @var int
  174. */
  175. var $text_angle_minimum;
  176. /**
  177. * The minimum angle in degrees, with 0 degrees being left-to-right reading text.<br />
  178. * Higher values represent a counter-clockwise rotation.<br />
  179. * For example, a value of 90 would result in bottom-to-top reading text.
  180. *
  181. * @var int
  182. */
  183. var $text_angle_maximum;
  184. /**
  185. * The X-Position on the image where letter drawing will begin.<br />
  186. * This value is in pixels from the left side of the image.
  187. *
  188. * @var int
  189. * @deprecated 2.0
  190. */
  191. var $text_x_start;
  192. /**
  193. * The background color for the image as a Securimage_Color.<br />
  194. *
  195. * @var Securimage_Color
  196. */
  197. var $image_bg_color;
  198. /**
  199. * Scan this directory for gif, jpg, and png files to use as background images.<br />
  200. * A random image file will be picked each time.<br />
  201. * Change from null to the full path to your directory.<br />
  202. * i.e. var $background_directory = $_SERVER['DOCUMENT_ROOT'] . '/securimage/backgrounds';
  203. * Make sure not to pass a background image to the show function, otherwise this directive is ignored.
  204. *
  205. * @var string
  206. */
  207. var $background_directory = null; //'./backgrounds';
  208. var $ttf_font_directory = null; //'./ttffonts';
  209. /**
  210. * The text color to use for drawing characters as a Securimage_Color.<br />
  211. * This value is ignored if $use_multi_text is set to true.<br />
  212. * Make sure this contrasts well with the background color or image.<br />
  213. *
  214. * @see Securimage::$use_multi_text
  215. * @var Securimage_Color
  216. */
  217. var $text_color;
  218. /**
  219. * Set to true to use multiple colors for each character.
  220. *
  221. * @see Securimage::$multi_text_color
  222. * @var boolean
  223. */
  224. var $use_multi_text;
  225. /**
  226. * Array of Securimage_Colors which will be randomly selected for each letter.<br />
  227. *
  228. * @var array
  229. */
  230. var $multi_text_color;
  231. /**
  232. * Set to true to make the characters appear transparent.
  233. *
  234. * @see Securimage::$text_transparency_percentage
  235. * @var boolean
  236. */
  237. var $use_transparent_text;
  238. /**
  239. * The percentage of transparency, 0 to 100.<br />
  240. * A value of 0 is completely opaque, 100 is completely transparent (invisble)
  241. *
  242. * @see Securimage::$use_transparent_text
  243. * @var int
  244. */
  245. var $text_transparency_percentage;
  246. // Line options
  247. /**
  248. * Draw vertical and horizontal lines on the image.
  249. *
  250. * @see Securimage::$line_color
  251. * @see Securimage::$draw_lines_over_text
  252. * @var boolean
  253. */
  254. var $num_lines;
  255. /**
  256. * Color of lines drawn over text
  257. *
  258. * @var string
  259. */
  260. var $line_color;
  261. /**
  262. * Draw the lines over the text.<br />
  263. * If fales lines will be drawn before putting the text on the image.
  264. *
  265. * @var boolean
  266. */
  267. var $draw_lines_over_text;
  268. /**
  269. * Text to write at the bottom corner of captcha image
  270. *
  271. * @since 2.0
  272. * @var string Signature text
  273. */
  274. var $image_signature;
  275. /**
  276. * Color to use for writing signature text
  277. *
  278. * @since 2.0
  279. * @var Securimage_Color
  280. */
  281. var $signature_color;
  282. /**
  283. * Full path to the WAV files to use to make the audio files, include trailing /.<br />
  284. * Name Files [A-Z0-9].wav
  285. *
  286. * @since 1.0.1
  287. * @var string
  288. */
  289. var $audio_path;
  290. /**
  291. * Type of audio file to generate (mp3 or wav)
  292. *
  293. * @var string
  294. */
  295. var $audio_format;
  296. /**
  297. * The session name to use if not the default. Blank for none
  298. *
  299. * @see http://php.net/session_name
  300. * @since 2.0
  301. * @var string
  302. */
  303. var $session_name = '';
  304. //END USER CONFIGURATION
  305. //There should be no need to edit below unless you really know what you are doing.
  306. /**
  307. * The gd image resource.
  308. *
  309. * @access private
  310. * @var resource
  311. */
  312. var $im;
  313. /**
  314. * Temporary image for rendering
  315. *
  316. * @access private
  317. * @var resource
  318. */
  319. var $tmpimg;
  320. /**
  321. * Internal scale factor for anti-alias @hkcaptcha
  322. *
  323. * @access private
  324. * @since 2.0
  325. * @var int
  326. */
  327. var $iscale; // internal scale factor for anti-alias @hkcaptcha
  328. /**
  329. * The background image resource
  330. *
  331. * @access private
  332. * @var resource
  333. */
  334. var $bgimg;
  335. /**
  336. * The code generated by the script
  337. *
  338. * @access private
  339. * @var string
  340. */
  341. var $code;
  342. /**
  343. * The code that was entered by the user
  344. *
  345. * @access private
  346. * @var string
  347. */
  348. var $code_entered;
  349. /**
  350. * Whether or not the correct code was entered
  351. *
  352. * @access private
  353. * @var boolean
  354. */
  355. var $correct_code;
  356. var $captcha_word;
  357. var $captcha_path;
  358. var $ctf_sm_captcha;
  359. var $prefix;
  360. var $nosession;
  361. /**
  362. * Class constructor.<br />
  363. * Because the class uses sessions, this will attempt to start a session if there is no previous one.<br />
  364. * If you do not start a session before calling the class, the constructor must be called before any
  365. * output is sent to the browser.
  366. *
  367. * <code>
  368. * $securimage = new Securimage();
  369. * </code>
  370. *
  371. */
  372. function Securimage()
  373. {
  374. // Set Default Values
  375. $this->form_id = 'com';
  376. $this->nosession = false;
  377. $this->prefix = '000000';
  378. $this->image_width = 175;
  379. $this->image_height = 60;
  380. $this->image_type = 'png'; // png, jpg or gif
  381. $this->code_length = 6;
  382. $this->charset = 'ABCDEFGHKLMNPRSTUVWYZabcdefghklmnprstuvwyz23456789';
  383. $this->wordlist_file = getcwd() . '/words/words.txt';
  384. $this->use_wordlist = false;
  385. $this->gd_font_file = getcwd() . '/gdfonts/bubblebath.gdf';
  386. $this->use_gd_font = false;
  387. $this->gd_font_size = 24;
  388. $this->text_x_start = 15;
  389. $this->ttf_file = getcwd() . '/AHGBold.ttf';
  390. $this->perturbation = 0.75;
  391. $this->iscale = 5;
  392. $this->text_angle_minimum = 0;
  393. $this->text_angle_maximum = 0;
  394. $this->image_bg_color = '#ffffff';
  395. $this->text_color = '#ff0000';
  396. $this->multi_text_color = array('#0020CC','#0030EE','#0040CC','#0050EE','#0060CC');
  397. $this->use_multi_text = false;
  398. $this->use_transparent_text = false;
  399. $this->text_transparency_percentage = 30;
  400. $this->num_lines = 10;
  401. $this->line_color = '#3d3d3d';
  402. $this->draw_lines_over_text = true;
  403. $this->image_signature = '';
  404. $this->signature_color = '#2050CC';
  405. $this->signature_font = getcwd() . '/AHGBold.ttf';
  406. $this->audio_path = getcwd() . '/audio/';
  407. $this->audio_format = 'mp3';
  408. $this->session_name = '';
  409. // Initialize session or attach to existing
  410. if ( $this->nosession == false && session_id() == '' ) { // no session has been started yet, which is needed for validation
  411. if (trim($this->session_name) != '') {
  412. session_name($this->session_name);
  413. }
  414. session_start();
  415. }
  416. }
  417. /**
  418. * Generate a code and output the image to the browser.
  419. *
  420. * <code>
  421. * <?php
  422. * include 'securimage.php';
  423. * $securimage = new Securimage();
  424. * $securimage->show('bg.jpg');
  425. * ?>
  426. * </code>
  427. *
  428. * @param string $background_image The path to an image to use as the background for the CAPTCHA
  429. */
  430. function show($background_image = "")
  431. {
  432. if($background_image != "" && is_readable($background_image)) {
  433. $this->bgimg = $background_image;
  434. }
  435. $this->doImage();
  436. }
  437. /**
  438. * Validate the code entered by the user.
  439. *
  440. * <code>
  441. * $code = $_POST['code'];
  442. * if ($securimage->check($code) == false) {
  443. * die("Sorry, the code entered did not match.");
  444. * } else {
  445. * $valid = true;
  446. * }
  447. * </code>
  448. * @param string $code The code the user entered
  449. * @return boolean true if the code was correct, false if not
  450. */
  451. function check($code)
  452. {
  453. $this->code_entered = $code;
  454. $this->validate();
  455. return $this->correct_code;
  456. }
  457. /**
  458. * Output audio file with HTTP headers to browser
  459. *
  460. * <code>
  461. * $sound = new Securimage();
  462. * $sound->audio_format = 'mp3';
  463. * $sound->outputAudioFile();
  464. * </code>
  465. *
  466. * @since 2.0
  467. */
  468. function outputAudioFile()
  469. {
  470. if (strtolower($this->audio_format) == 'wav') {
  471. header('Content-type: audio/x-wav');
  472. $ext = 'wav';
  473. } else {
  474. header('Content-type: audio/mpeg'); // default to mp3
  475. $ext = 'mp3';
  476. }
  477. header("Content-Disposition: attachment; filename=\"securimage_audio.{$ext}\"");
  478. header('Cache-Control: no-store, no-cache, must-revalidate');
  479. header('Expires: Sun, 1 Jan 2000 12:00:00 GMT');
  480. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
  481. $audio = $this->getAudibleCode($ext);
  482. header('Content-Length: ' . strlen($audio));
  483. echo $audio;
  484. exit;
  485. }
  486. /**
  487. * Generate and output the image
  488. *
  489. * @access private
  490. *
  491. */
  492. function doImage()
  493. {
  494. $bg_color = $this->getColorArray($this->image_bg_color, '#ffffff');
  495. if ($this->use_gd_font == true) {
  496. $this->iscale = 1;
  497. }
  498. if($this->use_transparent_text == true || $this->bgimg != "") {
  499. $this->im = imagecreatetruecolor($this->image_width, $this->image_height);
  500. $bgcolor = imagecolorallocate($this->im, $bg_color[0], $bg_color[1], $bg_color[2]);
  501. imagefilledrectangle($this->im, 0, 0, $this->image_width * $this->iscale, $this->image_height * $this->iscale, $bgcolor);
  502. $this->tmpimg = imagecreatetruecolor($this->image_width * $this->iscale, $this->image_height * $this->iscale);
  503. imagepalettecopy($this->tmpimg, $this->im);
  504. imagefilledrectangle($this->tmpimg, 0, 0, $this->image_width * $this->iscale, $this->image_height * $this->iscale, $bgcolor);
  505. } else { //no transparency
  506. $this->im = imagecreate($this->image_width, $this->image_height);
  507. $bgcolor = imagecolorallocate($this->im, $bg_color[0], $bg_color[1], $bg_color[2]);
  508. $this->tmpimg = imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale);
  509. imagepalettecopy($this->tmpimg, $this->im);
  510. }
  511. $this->setBackground();
  512. $this->createCode();
  513. if (!$this->draw_lines_over_text && $this->num_lines > 0) $this->drawLines();
  514. $this->drawWord();
  515. if ($this->use_gd_font == false) $this->distortedCopy();
  516. if ($this->draw_lines_over_text && $this->num_lines > 0) $this->drawLines();
  517. if (trim($this->image_signature) != '') $this->addSignature();
  518. $this->output();
  519. }
  520. /**
  521. * Set the background of the CAPTCHA image
  522. *
  523. * @access private
  524. *
  525. */
  526. function setBackground()
  527. {
  528. if ($this->bgimg == '') {
  529. if ($this->background_directory != null && is_dir($this->background_directory) && is_readable($this->background_directory)) {
  530. $img = $this->getBackgroundFromDirectory();
  531. if ($img != false) {
  532. $this->bgimg = $img;
  533. }
  534. }
  535. }
  536. $dat = @getimagesize($this->bgimg);
  537. if($dat == false) { return; }
  538. switch($dat[2]) {
  539. case 1: $newim = @imagecreatefromgif($this->bgimg); break;
  540. case 2: $newim = @imagecreatefromjpeg($this->bgimg); break;
  541. case 3: $newim = @imagecreatefrompng($this->bgimg); break;
  542. case 15: $newim = @imagecreatefromwbmp($this->bgimg); break;
  543. case 16: $newim = @imagecreatefromxbm($this->bgimg); break;
  544. default: return;
  545. }
  546. if(!$newim) return;
  547. imagecopyresized($this->im, $newim, 0, 0, 0, 0, $this->image_width, $this->image_height, imagesx($newim), imagesy($newim));
  548. }
  549. /**
  550. * Return the full path to a random gif, jpg, or png from the background directory.
  551. *
  552. * @see Securimage::$background_directory
  553. * @return mixed false if none found, string $path if found
  554. */
  555. function getBackgroundFromDirectory()
  556. {
  557. $images = array();
  558. if ($dh = opendir($this->background_directory)) {
  559. while (($file = readdir($dh)) !== false) {
  560. $supported_formats = array();
  561. $gd_support = extension_loaded('gd');
  562. if ($gd_support) $gd_info = gd_info(); else $gd_info = array();
  563. if ($gd_support && ( (isset($gd_info['JPG Support']) && $gd_info['JPG Support'] === true) || isset($gd_info['JPEG Support']) && $gd_info['JPEG Support'] === true ) ) $supported_formats[] = 'jpg';
  564. if ($gd_support && $gd_info['PNG Support']) $supported_formats[] = 'png';
  565. if ($gd_support && $gd_info['GIF Create Support']) $supported_formats[] = 'gif';
  566. if (preg_match('/('.implode('|', $supported_formats).')$/i', $file)) $images[] = $file;
  567. }
  568. closedir($dh);
  569. if (sizeof($images) > 0) {
  570. return rtrim($this->background_directory, '/') . '/' . $images[rand(0, sizeof($images)-1)];
  571. }
  572. }
  573. return false;
  574. }
  575. /**
  576. * Return the full path to a random font from the font directory.
  577. *
  578. *
  579. * @return mixed false if none found, string $path if found
  580. */
  581. function getFontFromDirectory()
  582. {
  583. $fonts = array();
  584. if ($dh = opendir($this->ttf_font_directory)) {
  585. while (($file = readdir($dh)) !== false) {
  586. if (preg_match('/(ttf)$/i', $file)) $fonts[] = $file;
  587. }
  588. closedir($dh);
  589. if (sizeof($fonts) > 0) {
  590. return rtrim($this->ttf_font_directory, '/') . '/' . $fonts[rand(0, sizeof($fonts)-1)];
  591. }
  592. }
  593. return false;
  594. }
  595. /**
  596. * Draw random curvy lines over the image<br />
  597. * Modified code from HKCaptcha
  598. *
  599. * @since 2.0
  600. * @access private
  601. *
  602. */
  603. function drawLines()
  604. {
  605. $line_color = $this->getColorArray($this->line_color, '#3d3d3d');
  606. $linecolor = imagecolorallocate($this->im, $line_color[0], $line_color[1], $line_color[2]);
  607. for ($line = 0; $line < $this->num_lines; ++$line) {
  608. $x = $this->image_width * (1 + $line) / ($this->num_lines + 1);
  609. $x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines;
  610. $y = rand($this->image_height * 0.1, $this->image_height * 0.9);
  611. $theta = ($this->frand()-0.5) * M_PI * 0.7;
  612. $w = $this->image_width;
  613. $len = rand($w * 0.4, $w * 0.7);
  614. $lwid = rand(0, 2);
  615. $k = $this->frand() * 0.6 + 0.2;
  616. $k = $k * $k * 0.5;
  617. $phi = $this->frand() * 6.28;
  618. $step = 0.5;
  619. $dx = $step * cos($theta);
  620. $dy = $step * sin($theta);
  621. $n = $len / $step;
  622. $amp = 1.5 * $this->frand() / ($k + 5.0 / $len);
  623. $x0 = $x - 0.5 * $len * cos($theta);
  624. $y0 = $y - 0.5 * $len * sin($theta);
  625. $ldx = round(-$dy * $lwid);
  626. $ldy = round($dx * $lwid);
  627. for ($i = 0; $i < $n; ++$i) {
  628. $x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi);
  629. $y = $y0 + $i * $dy - $amp * $dx * sin($k * $i * $step + $phi);
  630. imagefilledrectangle($this->im, $x, $y, $x + $lwid, $y + $lwid, $linecolor);
  631. }
  632. }
  633. }
  634. /**
  635. * Draw the CAPTCHA code over the image
  636. *
  637. * @access private
  638. *
  639. */
  640. function drawWord()
  641. {
  642. $width2 = $this->image_width * $this->iscale;
  643. $height2 = $this->image_height * $this->iscale;
  644. $text_color = $this->text_color;
  645. $gd_info = gd_info();
  646. if ($this->use_gd_font == true || !function_exists('imagettftext') || $gd_info['FreeType Support'] == false ) {
  647. if (!is_int($this->gd_font_file)) { //is a file name
  648. $font = @imageloadfont($this->gd_font_file);
  649. if ($font == false) {
  650. trigger_error("Failed to load GD Font file {$this->gd_font_file} ", E_USER_WARNING);
  651. return;
  652. }
  653. } else { //gd font identifier
  654. $font = $this->gd_font_file;
  655. }
  656. $color = imagecolorallocate($this->im, hexdec(substr($text_color, 1, 2)), hexdec(substr($text_color, 3, 2)), hexdec(substr($text_color, 5, 2)));
  657. imagestring($this->im, $font, $this->text_x_start, ($this->image_height / 2) - ($this->gd_font_size / 2), $this->code, $color);
  658. } else { //ttf font
  659. $text_color = $this->getColorArray($this->text_color, '#3d3d3d');
  660. $font_size = $height2 * .35;
  661. $bb = imagettfbbox($font_size, 0, $this->ttf_file, $this->code);
  662. // repeat this line to fix random missing text on some Debian servers
  663. $bb = imagettfbbox($font_size, 0, $this->ttf_file, $this->code);
  664. $tx = $bb[4] - $bb[0];
  665. $ty = $bb[5] - $bb[1];
  666. $x = floor($width2 / 2 - $tx / 2 - $bb[0]);
  667. $y = round($height2 / 2 - $ty / 2 - $bb[1]);
  668. if($this->use_transparent_text == true) {
  669. $alpha = intval($this->text_transparency_percentage / 100 * 127);
  670. $font_color = imagecolorallocatealpha($this->tmpimg, $text_color[0], $text_color[1], $text_color[2], $alpha);
  671. } else { //no transparency
  672. $font_color = imagecolorallocate($this->tmpimg, $text_color[0], $text_color[1], $text_color[2]);
  673. }
  674. $strlen = strlen($this->code);
  675. if (!is_array($this->multi_text_color)) $this->use_multi_text = false;
  676. if ($this->use_multi_text == false && $this->text_angle_minimum == 0 && $this->text_angle_maximum == 0) { // no angled or multi-color characters
  677. imagettftext($this->tmpimg, $font_size, 0, $x, $y, $font_color, $this->ttf_file, $this->code);
  678. } else {
  679. $this->multi_text_color = $this->convertMultiTextColor($this->multi_text_color);
  680. for($i = 0; $i < $strlen; ++$i) {
  681. $angle = rand($this->text_angle_minimum, $this->text_angle_maximum);
  682. $y = rand($y - 5, $y + 5);
  683. if ($this->use_multi_text == true) {
  684. $idx = rand(0, sizeof($this->multi_text_color) - 1);
  685. if($this->use_transparent_text == true) {
  686. $font_color = imagecolorallocatealpha($this->tmpimg, $this->multi_text_color[$idx][0], $this->multi_text_color[$idx][1], $this->multi_text_color[$idx][2], $alpha);
  687. } else {
  688. $font_color = imagecolorallocate($this->tmpimg, $this->multi_text_color[$idx][0], $this->multi_text_color[$idx][1], $this->multi_text_color[$idx][2]);
  689. }
  690. }
  691. $ch = $this->code{$i};
  692. imagettftext($this->tmpimg, $font_size, $angle, $x, $y, $font_color, $this->ttf_file, $ch);
  693. // estimate character widths to increment $x without creating spaces that are too large or too small
  694. // these are best estimates to align text but may vary between fonts
  695. // for optimal character widths, do not use multiple text colors or character angles and the complete string will be written by imagettftext
  696. if (strpos('abcdeghknopqsuvxyz', $ch) !== false) {
  697. $min_x = $font_size - ($this->iscale * 6);
  698. $max_x = $font_size - ($this->iscale * 6);
  699. } else if (strpos('ilI1', $ch) !== false) {
  700. $min_x = $font_size / 5;
  701. $max_x = $font_size / 3;
  702. } else if (strpos('fjrt', $ch) !== false) {
  703. $min_x = $font_size - ($this->iscale * 12);
  704. $max_x = $font_size - ($this->iscale * 12);
  705. } else if ($ch == 'wm') {
  706. $min_x = $font_size;
  707. $max_x = $font_size + ($this->iscale * 3);
  708. } else { // numbers, capitals or unicode
  709. $min_x = $font_size + ($this->iscale * 2);
  710. $max_x = $font_size + ($this->iscale * 5);
  711. }
  712. $x += rand($min_x, $max_x);
  713. } //for loop
  714. } // angled or multi-color
  715. } //else ttf font
  716. //$this->im = $this->tmpimg;
  717. //$this->output();
  718. } //function
  719. /**
  720. * Warp text from temporary image onto final image.<br />
  721. * Modified for securimage
  722. *
  723. * @access private
  724. * @since 2.0
  725. * @author Han-Kwang Nienhuys modified
  726. * @copyright Han-Kwang Neinhuys
  727. *
  728. */
  729. function distortedCopy()
  730. {
  731. $numpoles = 3; // distortion factor
  732. // make array of poles AKA attractor points
  733. for ($i = 0; $i < $numpoles; ++$i) {
  734. $px[$i] = rand($this->image_width * 0.3, $this->image_width * 0.7);
  735. $py[$i] = rand($this->image_height * 0.3, $this->image_height * 0.7);
  736. $rad[$i] = rand($this->image_width * 0.4, $this->image_width * 0.7);
  737. $tmp = -$this->frand() * 0.15 - 0.15;
  738. $amp[$i] = $this->perturbation * $tmp;
  739. }
  740. $bgCol = imagecolorat($this->tmpimg, 0, 0);
  741. $width2 = $this->iscale * $this->image_width;
  742. $height2 = $this->iscale * $this->image_height;
  743. imagepalettecopy($this->im, $this->tmpimg); // copy palette to final image so text colors come across
  744. // loop over $img pixels, take pixels from $tmpimg with distortion field
  745. for ($ix = 0; $ix < $this->image_width; ++$ix) {
  746. for ($iy = 0; $iy < $this->image_height; ++$iy) {
  747. $x = $ix;
  748. $y = $iy;
  749. for ($i = 0; $i < $numpoles; ++$i) {
  750. $dx = $ix - $px[$i];
  751. $dy = $iy - $py[$i];
  752. if ($dx == 0 && $dy == 0) continue;
  753. $r = sqrt($dx * $dx + $dy * $dy);
  754. if ($r > $rad[$i]) continue;
  755. $rscale = $amp[$i] * sin(3.14 * $r / $rad[$i]);
  756. $x += $dx * $rscale;
  757. $y += $dy * $rscale;
  758. }
  759. $c = $bgCol;
  760. $x *= $this->iscale;
  761. $y *= $this->iscale;
  762. if ($x >= 0 && $x < $width2 && $y >= 0 && $y < $height2) {
  763. $c = imagecolorat($this->tmpimg, $x, $y);
  764. }
  765. if ($c != $bgCol) { // only copy pixels of letters to preserve any background image
  766. imagesetpixel($this->im, $ix, $iy, $c);
  767. }
  768. }
  769. }
  770. }
  771. /**
  772. * Create a code and save to the session
  773. *
  774. * @since 1.0.1
  775. *
  776. */
  777. function createCode()
  778. {
  779. $this->code = false;
  780. if ($this->use_wordlist && is_readable($this->wordlist_file)) {
  781. $this->code = $this->readCodeFromFile();
  782. }
  783. if ($this->code == false) {
  784. $this->code = $this->generateCode($this->code_length);
  785. }
  786. $this->saveData();
  787. }
  788. /**
  789. * Generate a code
  790. *
  791. * @access private
  792. * @param int $len The code length
  793. * @return string
  794. */
  795. function generateCode($len)
  796. {
  797. $code = '';
  798. for($i = 1, $cslen = strlen($this->charset); $i <= $len; ++$i) {
  799. $code .= $this->charset{rand(0, $cslen - 1)};
  800. }
  801. if ( $this->nosession == true )
  802. return $this->captcha_word;
  803. if ( $this->nosession == false )
  804. return $code;
  805. }
  806. /**
  807. * Reads a word list file to get a code
  808. *
  809. * @access private
  810. * @since 1.0.2
  811. * @return mixed false on failure, a word on success
  812. */
  813. function readCodeFromFile()
  814. {
  815. $fp = @fopen($this->wordlist_file, 'rb');
  816. if (!$fp) return false;
  817. $fsize = filesize($this->wordlist_file);
  818. if ($fsize < 32) return false; // too small of a list to be effective
  819. if ($fsize < 128) {
  820. $max = $fsize; // still pretty small but changes the range of seeking
  821. } else {
  822. $max = 128;
  823. }
  824. fseek($fp, rand(0, $fsize - $max), SEEK_SET);
  825. $data = fread($fp, 128); // read a random 128 bytes from file
  826. fclose($fp);
  827. $data = preg_replace("/\r?\n/", "\n", $data);
  828. $start = strpos($data, "\n", rand(0, 100)) + 1; // random start position
  829. $end = strpos($data, "\n", $start); // find end of word
  830. return strtolower(substr($data, $start, $end - $start)); // return substring in 128 bytes
  831. }
  832. /**
  833. * Output image to the browser
  834. *
  835. * @access private
  836. *
  837. */
  838. function output()
  839. {
  840. header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
  841. header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
  842. header("Cache-Control: no-store, no-cache, must-revalidate");
  843. header("Cache-Control: post-check=0, pre-check=0", false);
  844. header("Pragma: no-cache");
  845. if ( strtolower($this->image_type) == 'gif' ) $this->image_type = '1';
  846. if ( strtolower($this->image_type) == 'jpg' ) $this->image_type = '2';
  847. switch($this->image_type)
  848. {
  849. case '1':
  850. header("Content-Type: image/gif");
  851. imagegif($this->im);
  852. break;
  853. case '2':
  854. header("Content-Type: image/jpeg");
  855. imagejpeg($this->im, null, 90);
  856. break;
  857. default:
  858. header("Content-Type: image/png");
  859. imagepng($this->im);
  860. break;
  861. }
  862. imagedestroy($this->im);
  863. //exit;
  864. }
  865. /**
  866. * Get WAV or MP3 file data of the spoken code.<br />
  867. * This is appropriate for output to the browser as audio/x-wav or audio/mpeg
  868. *
  869. * @since 1.0.1
  870. * @return string WAV or MP3 data
  871. *
  872. */
  873. function getAudibleCode($format = 'wav')
  874. {
  875. $letters = array();
  876. if ( $this->nosession == false ) {
  877. $code = $this->getCode();
  878. if ($code == '') {
  879. $this->createCode();
  880. $code = $this->getCode();
  881. }
  882. }
  883. if ( $this->nosession == true ) {
  884. if ( is_readable( $this->captcha_path . $this->prefix . '.php' ) ) {
  885. include( $this->captcha_path . $this->prefix . '.php' );
  886. $code = $captcha_word;
  887. } else {
  888. $code = 'nono';
  889. }
  890. }
  891. for($i = 0; $i < strlen($code); ++$i) {
  892. $letters[] = $code{$i};
  893. }
  894. if ($format == 'mp3') {
  895. return $this->generateMP3($letters);
  896. } else {
  897. return $this->generateWAV($letters);
  898. }
  899. }
  900. /**
  901. * Set the path to the audio directory.<br />
  902. *
  903. * @since 1.0.4
  904. * @return bool true if the directory exists and is readble, false if not
  905. */
  906. function setAudioPath($audio_directory)
  907. {
  908. if (is_dir($audio_directory) && is_readable($audio_directory)) {
  909. $this->audio_path = $audio_directory;
  910. return true;
  911. } else {
  912. return false;
  913. }
  914. }
  915. /**
  916. * Save the code in the session
  917. *
  918. * @access private
  919. *
  920. */
  921. function saveData()
  922. {
  923. $_SESSION['securimage_code_si_'.$this->form_id] = strtolower($this->code);
  924. }
  925. /**
  926. * Validate the code to the user code
  927. *
  928. * @access private
  929. *
  930. */
  931. function validate()
  932. {
  933. if ( isset($_SESSION['securimage_code_si_'.$this->form_id]) && !empty($_SESSION['securimage_code_si_'.$this->form_id]) ) {
  934. if ( strtolower($_SESSION['securimage_code_si_'.$this->form_id]) == strtolower(trim($this->code_entered)) ) {
  935. $this->correct_code = true;
  936. $_SESSION['securimage_code_si_'.$this->form_id] = ''; // clear code to prevent session re-use
  937. } else {
  938. $this->correct_code = false;
  939. }
  940. } else {
  941. $this->correct_code = false; // value was never set or is blank
  942. }
  943. }
  944. /**
  945. * Get the captcha code
  946. *
  947. * @since 1.0.1
  948. * @return string
  949. */
  950. function getCode()
  951. {
  952. if (isset($_SESSION['securimage_code_si_'.$this->form_id]) && !empty($_SESSION['securimage_code_si_'.$this->form_id])) {
  953. return strtolower($_SESSION['securimage_code_si_'.$this->form_id]);
  954. } else {
  955. return '';
  956. }
  957. }
  958. /**
  959. * Check if the user entered code was correct
  960. *
  961. * @access private
  962. * @return boolean
  963. */
  964. function checkCode()
  965. {
  966. return $this->correct_code;
  967. }
  968. /**
  969. * Generate a wav file by concatenating individual files
  970. *
  971. * @since 1.0.1
  972. * @access private
  973. * @param array $letters Array of letters to build a file from
  974. * @return string WAV file data
  975. */
  976. function generateWAV($letters)
  977. {
  978. $data_len = 0;
  979. $files = array();
  980. $out_data = '';
  981. foreach ($letters as $letter) {
  982. $filename = $this->audio_path . strtoupper($letter) . '.wav';
  983. $fp = fopen($filename, 'rb');
  984. $file = array();
  985. $data = fread($fp, filesize($filename)); // read file in
  986. $header = substr($data, 0, 36);
  987. $body = substr($data, 44);
  988. $data = unpack('NChunkID/VChunkSize/NFormat/NSubChunk1ID/VSubChunk1Size/vAudioFormat/vNumChannels/VSampleRate/VByteRate/vBlockAlign/vBitsPerSample', $header);
  989. $file['sub_chunk1_id'] = $data['SubChunk1ID'];
  990. $file['bits_per_sample'] = $data['BitsPerSample'];
  991. $file['channels'] = $data['NumChannels'];
  992. $file['format'] = $data['AudioFormat'];
  993. $file['sample_rate'] = $data['SampleRate'];
  994. $file['size'] = $data['ChunkSize'] + 8;
  995. $file['data'] = $body;
  996. if ( ($p = strpos($file['data'], 'LIST')) !== false) {
  997. // If the LIST data is not at the end of the file, this will probably break your sound file
  998. $info = substr($file['data'], $p + 4, 8);
  999. $data = unpack('Vlength/Vjunk', $info);
  1000. $file['data'] = substr($file['data'], 0, $p);
  1001. $file['size'] = $file['size'] - (strlen($file['data']) - $p);
  1002. }
  1003. $files[] = $file;
  1004. $data = null;
  1005. $header = null;
  1006. $body = null;
  1007. $data_len += strlen($file['data']);
  1008. fclose($fp);
  1009. }
  1010. $out_data = '';
  1011. for($i = 0; $i < sizeof($files); ++$i) {
  1012. if ($i == 0) { // output header
  1013. $out_data .= pack('C4VC8', ord('R'), ord('I'), ord('F'), ord('F'), $data_len + 36, ord('W'), ord('A'), ord('V'), ord('E'), ord('f'), ord('m'), ord('t'), ord(' '));
  1014. $out_data .= pack('VvvVVvv',
  1015. 16,
  1016. $files[$i]['format'],
  1017. $files[$i]['channels'],
  1018. $files[$i]['sample_rate'],
  1019. $files[$i]['sample_rate'] * (($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8),
  1020. ($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8,
  1021. $files[$i]['bits_per_sample'] );
  1022. $out_data .= pack('C4', ord('d'), ord('a'), ord('t'), ord('a'));
  1023. $out_data .= pack('V', $data_len);
  1024. }
  1025. $out_data .= $files[$i]['data'];
  1026. }
  1027. $this->scrambleAudioData($out_data, 'wav');
  1028. return $out_data;
  1029. }
  1030. /**
  1031. * Randomly modify the audio data to scramble sound and prevent binary recognition.<br />
  1032. * Take care not to "break" the audio file by leaving the header data intact.
  1033. *
  1034. * @since 2.0
  1035. * @param $data Sound data in mp3 of wav format
  1036. */
  1037. function scrambleAudioData(&$data, $format)
  1038. {
  1039. if ($format == 'wav') {
  1040. $start = strpos($data, 'data') + 4; // look for "data" indicator
  1041. if ($start === false) $start = 44; // if not found assume 44 byte header
  1042. } else { // mp3
  1043. $start = 4; // 4 byte (32 bit) frame header
  1044. }
  1045. $start += rand(1, 64); // randomize starting offset
  1046. $datalen = strlen($data) - $start - 256; // leave last 256 bytes unchanged
  1047. for ($i = $start; $i < $datalen; $i += 64) {
  1048. $ch = ord($data{$i});
  1049. if ($ch < 9 || $ch > 119) continue;
  1050. $data{$i} = chr($ch + rand(-8, 8));
  1051. }
  1052. }
  1053. /**
  1054. * Generate an mp3 file by concatenating individual files
  1055. * @since 1.0.4
  1056. * @access private
  1057. * @param array $letters Array of letters to build a file from
  1058. * @return string MP3 file data
  1059. */
  1060. function generateMP3($letters)
  1061. {
  1062. $data_len = 0;
  1063. $files = array();
  1064. $out_data = '';
  1065. foreach ($letters as $letter) {
  1066. $filename = $this->audio_path . strtoupper($letter) . '.mp3';
  1067. $fp = fopen($filename, 'rb');
  1068. $data = fread($fp, filesize($filename)); // read file in
  1069. $this->scrambleAudioData($data, 'mp3');
  1070. $out_data .= $data;
  1071. fclose($fp);
  1072. }
  1073. return $out_data;
  1074. }
  1075. /**
  1076. * Generate random number less than 1
  1077. * @since 2.0
  1078. * @access private
  1079. * @return float
  1080. */
  1081. function frand()
  1082. {
  1083. return 0.0001*rand(0,9999);
  1084. }
  1085. /**
  1086. * Print signature text on image
  1087. *
  1088. * @since 2.0
  1089. * @access private
  1090. *
  1091. */
  1092. function addSignature()
  1093. {
  1094. $sig_color = $this->getColorArray($this->signature_color, '#3d3d3d');
  1095. $cmtcol = imagecolorallocate($this->im, $sig_color[0], $sig_color[1], $sig_color[2]);
  1096. if ($this->use_gd_font) {
  1097. imagestring($this->im, 5, $this->image_width - (strlen($this->image_signature) * 10), $this->image_height - 20, $this->image_signature, $cmtcol);
  1098. } else {
  1099. $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
  1100. // repeat this line to fix random missing text on some Debian servers
  1101. $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
  1102. $textlen = $bbox[2] - $bbox[0];
  1103. $x = $this->image_width - $textlen - 5;
  1104. $y = $this->image_height - 3;
  1105. imagettftext($this->im, 10, 0, $x, $y, $cmtcol, $this->signature_font, $this->image_signature);
  1106. }
  1107. }
  1108. /**
  1109. *
  1110. * Create a color array based on user setting.
  1111. * @since 2.0
  1112. * contributed by Mike Challis
  1113. * Specify the red, green, and blue components using their HTML hex code.<br />
  1114. * i.e. #4A203C
  1115. *
  1116. * @param $color color the user has set
  1117. * @param $default default color to use if color the user set does not validate
  1118. */
  1119. function getColorArray($color, $default)
  1120. {
  1121. if ( is_object($color) ) {
  1122. // This method: new Securimage_Color(0xea, 0xea, 0xea);
  1123. return array('0' => $color->r, '1' => $color->g, '2' => $color->b);
  1124. }
  1125. // This method: $this->text_color = '#3d3d3d';
  1126. if ( !$this->validateHexColor($color) ) $color = $default; // color was not valid, use default
  1127. $color = str_replace('#','',$color);
  1128. $color_int = hexdec("#$color");
  1129. $color_arr = array('0' => 0xFF & ($color_int >> 0x10),'1' => 0xFF & ($color_int >> 0x8),'2' => 0xFF & $color_int);
  1130. //$color_arr[0] = red, $color_arr[1] = green, $color_arr[2] = blue
  1131. return $color_arr;
  1132. }
  1133. /**
  1134. *
  1135. * Validate a CSS HEX color, i.e. #4A203C
  1136. * @since 2.0
  1137. * contributed by Mike Challis
  1138. * only allow simple 6 char hex codes with or without # like this 336699 or #336699
  1139. *
  1140. * @param $color color the user has set
  1141. */
  1142. function validateHexColor($color)
  1143. {
  1144. if ( preg_match("/^#[a-f0-9]{6}$/i", $color) ) {
  1145. return true;
  1146. }
  1147. if ( preg_match("/^[a-f0-9]{6}$/i", $color) ) {
  1148. return true;
  1149. }
  1150. return false;
  1151. }
  1152. /**
  1153. * Process the multi_text_color array
  1154. *
  1155. * @since 2.0
  1156. * contributed by Mike Challis
  1157. *
  1158. *
  1159. * @param $color_arr array of HEX colors the user has set
  1160. */
  1161. function convertMultiTextColor($color_arr)
  1162. {
  1163. $colors = array();
  1164. foreach($color_arr as $color){
  1165. $colors[]= $this->getColorArray($color, '#0020CC');
  1166. }
  1167. return $colors;
  1168. }
  1169. } /* class Securimage */
  1170. /**
  1171. * Color object for Securimage CAPTCHA
  1172. *
  1173. * @since 2.0
  1174. * @package Securimage
  1175. * @subpackage classes
  1176. *
  1177. */
  1178. class Securimage_Color {
  1179. /**
  1180. * Red component: 0-255
  1181. *
  1182. * @var int
  1183. */
  1184. var $r;
  1185. /**
  1186. * Green component: 0-255
  1187. *
  1188. * @var int
  1189. */
  1190. var $g;
  1191. /**
  1192. * Blue component: 0-255
  1193. *
  1194. * @var int
  1195. */
  1196. var $b;
  1197. /**
  1198. * Create a new Securimage_Color object.<br />
  1199. * Specify the red, green, and blue components using their HTML hex code equivalent.<br />
  1200. * i.e. #4A203C is declared as new Securimage_Color(0x4A, 0x20, 0x3C)
  1201. *
  1202. * @param $red Red component 0-255
  1203. * @param $green Green component 0-255
  1204. * @param $blue Blue component 0-255
  1205. */
  1206. function Securimage_Color($red, $green, $blue)
  1207. {
  1208. if ($red < 0) $red = 0;
  1209. if ($red > 255) $red = 255;
  1210. if ($green < 0) $green = 0;
  1211. if ($green > 255) $green = 255;
  1212. if ($blue < 0) $blue = 0;
  1213. if ($blue > 255) $blue = 255;
  1214. $this->r = $red;
  1215. $this->g = $green;
  1216. $this->b = $blue;
  1217. }
  1218. }
  1219. ?>