PageRenderTime 58ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://bitbucket.org/crypticrod/sr_wp_code
PHP | 1442 lines | 641 code | 199 blank | 602 comment | 177 complexity | a2c26bf8c00b531f1d1f660c3d09f3bb MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, LGPL-2.1, GPL-3.0, LGPL-2.0, AGPL-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() . '/ahg-bold.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() . '/ahg-bold.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. //mchallis modified so that a 4 letter swear word could never appear
  798. // $code = '';
  799. // for($i = 1, $cslen = strlen($this->charset); $i <= $len; ++$i) {
  800. // $code .= $this->charset{rand(0, $cslen - 1)};
  801. // }
  802. $chars_num = '234578'; // do not change this or the code will break!!
  803. // one random position always has to be a number so that a 4 letter swear word could never appear
  804. $rand_pos = mt_rand( 0, $len - 1 );
  805. $code = '';
  806. for($i = 0; $i < $len; $i++ ) {
  807. // this rand character position is a number only so that a 4 letter swear word could never appear
  808. if($i == $rand_pos) {
  809. $pos = mt_rand( 0, strlen( $chars_num ) - 1 );
  810. $char = $chars_num[$pos];
  811. } else {
  812. $pos = mt_rand( 0, strlen( $this->charset ) - 1 );
  813. $char = $this->charset[$pos];
  814. }
  815. $code .= $char;
  816. }
  817. if ( $this->nosession == true )
  818. return $this->captcha_word;
  819. if ( $this->nosession == false )
  820. return $code;
  821. }
  822. /**
  823. * Reads a word list file to get a code
  824. *
  825. * @access private
  826. * @since 1.0.2
  827. * @return mixed false on failure, a word on success
  828. */
  829. function readCodeFromFile()
  830. {
  831. $fp = @fopen($this->wordlist_file, 'rb');
  832. if (!$fp) return false;
  833. $fsize = filesize($this->wordlist_file);
  834. if ($fsize < 32) return false; // too small of a list to be effective
  835. if ($fsize < 128) {
  836. $max = $fsize; // still pretty small but changes the range of seeking
  837. } else {
  838. $max = 128;
  839. }
  840. fseek($fp, rand(0, $fsize - $max), SEEK_SET);
  841. $data = fread($fp, 128); // read a random 128 bytes from file
  842. fclose($fp);
  843. $data = preg_replace("/\r?\n/", "\n", $data);
  844. $start = strpos($data, "\n", rand(0, 100)) + 1; // random start position
  845. $end = strpos($data, "\n", $start); // find end of word
  846. return strtolower(substr($data, $start, $end - $start)); // return substring in 128 bytes
  847. }
  848. /**
  849. * Output image to the browser
  850. *
  851. * @access private
  852. *
  853. */
  854. function output()
  855. {
  856. header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
  857. header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
  858. header("Cache-Control: no-store, no-cache, must-revalidate");
  859. header("Cache-Control: post-check=0, pre-check=0", false);
  860. header("Pragma: no-cache");
  861. if ( strtolower($this->image_type) == 'gif' ) $this->image_type = '1';
  862. if ( strtolower($this->image_type) == 'jpg' ) $this->image_type = '2';
  863. switch($this->image_type)
  864. {
  865. case '1':
  866. header("Content-Type: image/gif");
  867. imagegif($this->im);
  868. break;
  869. case '2':
  870. header("Content-Type: image/jpeg");
  871. imagejpeg($this->im, null, 90);
  872. break;
  873. default:
  874. header("Content-Type: image/png");
  875. imagepng($this->im);
  876. break;
  877. }
  878. imagedestroy($this->im);
  879. //exit;
  880. }
  881. /**
  882. * Get WAV or MP3 file data of the spoken code.<br />
  883. * This is appropriate for output to the browser as audio/x-wav or audio/mpeg
  884. *
  885. * @since 1.0.1
  886. * @return string WAV or MP3 data
  887. *
  888. */
  889. function getAudibleCode($format = 'wav')
  890. {
  891. $letters = array();
  892. if ( $this->nosession == false ) {
  893. $code = $this->getCode();
  894. if ($code == '') {
  895. $this->createCode();
  896. $code = $this->getCode();
  897. }
  898. }
  899. if ( $this->nosession == true ) {
  900. if ( is_readable( $this->captcha_path . $this->prefix . '.php' ) ) {
  901. include( $this->captcha_path . $this->prefix . '.php' );
  902. $code = $captcha_word;
  903. } else {
  904. $code = 'nono';
  905. }
  906. }
  907. for($i = 0; $i < strlen($code); ++$i) {
  908. $letters[] = $code{$i};
  909. }
  910. if ($format == 'mp3') {
  911. return $this->generateMP3($letters);
  912. } else {
  913. return $this->generateWAV($letters);
  914. }
  915. }
  916. /**
  917. * Set the path to the audio directory.<br />
  918. *
  919. * @since 1.0.4
  920. * @return bool true if the directory exists and is readble, false if not
  921. */
  922. function setAudioPath($audio_directory)
  923. {
  924. if (is_dir($audio_directory) && is_readable($audio_directory)) {
  925. $this->audio_path = $audio_directory;
  926. return true;
  927. } else {
  928. return false;
  929. }
  930. }
  931. /**
  932. * Save the code in the session
  933. *
  934. * @access private
  935. *
  936. */
  937. function saveData()
  938. {
  939. $_SESSION['securimage_code_si_'.$this->form_id] = strtolower($this->code);
  940. }
  941. /**
  942. * Validate the code to the user code
  943. *
  944. * @access private
  945. *
  946. */
  947. function validate()
  948. {
  949. if ( isset($_SESSION['securimage_code_si_'.$this->form_id]) && !empty($_SESSION['securimage_code_si_'.$this->form_id]) ) {
  950. if ( strtolower($_SESSION['securimage_code_si_'.$this->form_id]) == strtolower(trim($this->code_entered)) ) {
  951. $this->correct_code = true;
  952. $_SESSION['securimage_code_si_'.$this->form_id] = ''; // clear code to prevent session re-use
  953. } else {
  954. $this->correct_code = false;
  955. }
  956. } else {
  957. $this->correct_code = false; // value was never set or is blank
  958. }
  959. }
  960. /**
  961. * Get the captcha code
  962. *
  963. * @since 1.0.1
  964. * @return string
  965. */
  966. function getCode()
  967. {
  968. if (isset($_SESSION['securimage_code_si_'.$this->form_id]) && !empty($_SESSION['securimage_code_si_'.$this->form_id])) {
  969. return strtolower($_SESSION['securimage_code_si_'.$this->form_id]);
  970. } else {
  971. return '';
  972. }
  973. }
  974. /**
  975. * Check if the user entered code was correct
  976. *
  977. * @access private
  978. * @return boolean
  979. */
  980. function checkCode()
  981. {
  982. return $this->correct_code;
  983. }
  984. /**
  985. * Generate a wav file by concatenating individual files
  986. *
  987. * @since 1.0.1
  988. * @access private
  989. * @param array $letters Array of letters to build a file from
  990. * @return string WAV file data
  991. */
  992. function generateWAV($letters)
  993. {
  994. $data_len = 0;
  995. $files = array();
  996. $out_data = '';
  997. foreach ($letters as $letter) {
  998. $filename = $this->audio_path . strtoupper($letter) . '.wav';
  999. $fp = fopen($filename, 'rb');
  1000. $file = array();
  1001. $data = fread($fp, filesize($filename)); // read file in
  1002. $header = substr($data, 0, 36);
  1003. $body = substr($data, 44);
  1004. $data = unpack('NChunkID/VChunkSize/NFormat/NSubChunk1ID/VSubChunk1Size/vAudioFormat/vNumChannels/VSampleRate/VByteRate/vBlockAlign/vBitsPerSample', $header);
  1005. $file['sub_chunk1_id'] = $data['SubChunk1ID'];
  1006. $file['bits_per_sample'] = $data['BitsPerSample'];
  1007. $file['channels'] = $data['NumChannels'];
  1008. $file['format'] = $data['AudioFormat'];
  1009. $file['sample_rate'] = $data['SampleRate'];
  1010. $file['size'] = $data['ChunkSize'] + 8;
  1011. $file['data'] = $body;
  1012. if ( ($p = strpos($file['data'], 'LIST')) !== false) {
  1013. // If the LIST data is not at the end of the file, this will probably break your sound file
  1014. $info = substr($file['data'], $p + 4, 8);
  1015. $data = unpack('Vlength/Vjunk', $info);
  1016. $file['data'] = substr($file['data'], 0, $p);
  1017. $file['size'] = $file['size'] - (strlen($file['data']) - $p);
  1018. }
  1019. $files[] = $file;
  1020. $data = null;
  1021. $header = null;
  1022. $body = null;
  1023. $data_len += strlen($file['data']);
  1024. fclose($fp);
  1025. }
  1026. $out_data = '';
  1027. for($i = 0; $i < sizeof($files); ++$i) {
  1028. if ($i == 0) { // output header
  1029. $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(' '));
  1030. $out_data .= pack('VvvVVvv',
  1031. 16,
  1032. $files[$i]['format'],
  1033. $files[$i]['channels'],
  1034. $files[$i]['sample_rate'],
  1035. $files[$i]['sample_rate'] * (($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8),
  1036. ($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8,
  1037. $files[$i]['bits_per_sample'] );
  1038. $out_data .= pack('C4', ord('d'), ord('a'), ord('t'), ord('a'));
  1039. $out_data .= pack('V', $data_len);
  1040. }
  1041. $out_data .= $files[$i]['data'];
  1042. }
  1043. $this->scrambleAudioData($out_data, 'wav');
  1044. return $out_data;
  1045. }
  1046. /**
  1047. * Randomly modify the audio data to scramble sound and prevent binary recognition.<br />
  1048. * Take care not to "break" the audio file by leaving the header data intact.
  1049. *
  1050. * @since 2.0
  1051. * @param $data Sound data in mp3 of wav format
  1052. */
  1053. function scrambleAudioData(&$data, $format)
  1054. {
  1055. if ($format == 'wav') {
  1056. $start = strpos($data, 'data') + 4; // look for "data" indicator
  1057. if ($start === false) $start = 44; // if not found assume 44 byte header
  1058. } else { // mp3
  1059. $start = 4; // 4 byte (32 bit) frame header
  1060. }
  1061. $start += rand(1, 64); // randomize starting offset
  1062. $datalen = strlen($data) - $start - 256; // leave last 256 bytes unchanged
  1063. for ($i = $start; $i < $datalen; $i += 64) {
  1064. $ch = ord($data{$i});
  1065. if ($ch < 9 || $ch > 119) continue;
  1066. $data{$i} = chr($ch + rand(-8, 8));
  1067. }
  1068. }
  1069. /**
  1070. * Generate an mp3 file by concatenating individual files
  1071. * @since 1.0.4
  1072. * @access private
  1073. * @param array $letters Array of letters to build a file from
  1074. * @return string MP3 file data
  1075. */
  1076. function generateMP3($letters)
  1077. {
  1078. $data_len = 0;
  1079. $files = array();
  1080. $out_data = '';
  1081. foreach ($letters as $letter) {
  1082. $filename = $this->audio_path . strtoupper($letter) . '.mp3';
  1083. $fp = fopen($filename, 'rb');
  1084. $data = fread($fp, filesize($filename)); // read file in
  1085. $this->scrambleAudioData($data, 'mp3');
  1086. $out_data .= $data;
  1087. fclose($fp);
  1088. }
  1089. return $out_data;
  1090. }
  1091. /**
  1092. * Generate random number less than 1
  1093. * @since 2.0
  1094. * @access private
  1095. * @return float
  1096. */
  1097. function frand()
  1098. {
  1099. return 0.0001*rand(0,9999);
  1100. }
  1101. /**
  1102. * Print signature text on image
  1103. *
  1104. * @since 2.0
  1105. * @access private
  1106. *
  1107. */
  1108. function addSignature()
  1109. {
  1110. $sig_color = $this->getColorArray($this->signature_color, '#3d3d3d');
  1111. $cmtcol = imagecolorallocate($this->im, $sig_color[0], $sig_color[1], $sig_color[2]);
  1112. if ($this->use_gd_font) {
  1113. imagestring($this->im, 5, $this->image_width - (strlen($this->image_signature) * 10), $this->image_height - 20, $this->image_signature, $cmtcol);
  1114. } else {
  1115. $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
  1116. // repeat this line to fix random missing text on some Debian servers
  1117. $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
  1118. $textlen = $bbox[2] - $bbox[0];
  1119. $x = $this->image_width - $textlen - 5;
  1120. $y = $this->image_height - 3;
  1121. imagettftext($this->im, 10, 0, $x, $y, $cmtcol, $this->signature_font, $this->image_signature);
  1122. }
  1123. }
  1124. /**
  1125. *
  1126. * Create a color array based on user setting.
  1127. * @since 2.0
  1128. * contributed by Mike Challis
  1129. * Specify the red, green, and blue components using their HTML hex code.<br />
  1130. * i.e. #4A203C
  1131. *
  1132. * @param $color color the user has set
  1133. * @param $default default color to use if color the user set does not validate
  1134. */
  1135. function getColorArray($color, $default)
  1136. {
  1137. if ( is_object($color) ) {
  1138. // This method: new Securimage_Color(0xea, 0xea, 0xea);
  1139. return array('0' => $color->r, '1' => $color->g, '2' => $color->b);
  1140. }
  1141. // This method: $this->text_color = '#3d3d3d';
  1142. if ( !$this->validateHexColor($color) ) $color = $default; // color was not valid, use default
  1143. $color = str_replace('#','',$color);
  1144. $color_int = hexdec("#$color");
  1145. $color_arr = array('0' => 0xFF & ($color_int >> 0x10),'1' => 0xFF & ($color_int >> 0x8),'2' => 0xFF & $color_int);
  1146. //$color_arr[0] = red, $color_arr[1] = green, $color_arr[2] = blue
  1147. return $color_arr;
  1148. }
  1149. /**
  1150. *
  1151. * Validate a CSS HEX color, i.e. #4A203C
  1152. * @since 2.0
  1153. * contributed by Mike Challis
  1154. * only allow simple 6 char hex codes with or without # like this 336699 or #336699
  1155. *
  1156. * @param $color color the user has set
  1157. */
  1158. function validateHexColor($color)
  1159. {
  1160. if ( preg_match("/^#[a-f0-9]{6}$/i", $color) ) {
  1161. return true;
  1162. }
  1163. if ( preg_match("/^[a-f0-9]{6}$/i", $color) ) {
  1164. return true;
  1165. }
  1166. return false;
  1167. }
  1168. /**
  1169. * Process the multi_text_color array
  1170. *
  1171. * @since 2.0
  1172. * contributed by Mike Challis
  1173. *
  1174. *
  1175. * @param $color_arr array of HEX colors the user has set
  1176. */
  1177. function convertMultiTextColor($color_arr)
  1178. {
  1179. $colors = array();
  1180. foreach($color_arr as $color){
  1181. $colors[]= $this->getColorArray($color, '#0020CC');
  1182. }
  1183. return $colors;
  1184. }
  1185. } /* class Securimage */
  1186. /**
  1187. * Color object for Securimage CAPTCHA
  1188. *
  1189. * @since 2.0
  1190. * @package Securimage
  1191. * @subpackage classes
  1192. *
  1193. */
  1194. class Securimage_Color {
  1195. /**
  1196. * Red component: 0-255
  1197. *
  1198. * @var int
  1199. */
  1200. var $r;
  1201. /**
  1202. * Green component: 0-255
  1203. *
  1204. * @var int
  1205. */
  1206. var $g;
  1207. /**
  1208. * Blue component: 0-255
  1209. *
  1210. * @var int
  1211. */
  1212. var $b;
  1213. /**
  1214. * Create a new Securimage_Color object.<br />
  1215. * Specify the red, green, and blue components using their HTML hex code equivalent.<br />
  1216. * i.e. #4A203C is declared as new Securimage_Color(0x4A, 0x20, 0x3C)
  1217. *
  1218. * @param $red Red component 0-255
  1219. * @param $green Green component 0-255
  1220. * @param $blue Blue component 0-255
  1221. */
  1222. function Securimage_Color($red, $green, $blue)
  1223. {
  1224. if ($red < 0) $red = 0;
  1225. if ($red > 255) $red = 255;
  1226. if ($green < 0) $green = 0;
  1227. if ($green > 255) $green = 255;
  1228. if ($blue < 0) $blue = 0;
  1229. if ($blue > 255) $blue = 255;
  1230. $this->r = $red;
  1231. $this->g = $green;
  1232. $this->b = $blue;
  1233. }
  1234. }
  1235. ?>