PageRenderTime 72ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 1ms

/concrete/libraries/3rdparty/securimage/securimage.php

https://bitbucket.org/selfeky/xclusivescardwebsite
PHP | 1597 lines | 686 code | 225 blank | 686 comment | 185 complexity | 6498863b17ba6bd73014675b18615825 MD5 | raw file
  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.1 BETA (December 6th, 2009)
  32. * @package Securimage
  33. *
  34. */
  35. /**
  36. ChangeLog
  37. 2.0.2
  38. - Fix pathing to make integration into libraries easier (Nathan Phillip Brink ohnobinki@ohnopublishing.net)
  39. 2.0.1
  40. - Add support for browsers with cookies disabled (requires php5, sqlite) maps users to md5 hashed ip addresses and md5 hashed codes for security
  41. - Add fallback to gd fonts if ttf support is not enabled or font file not found (Mike Challis http://www.642weather.com/weather/scripts.php)
  42. - Check for previous definition of image type constants (Mike Challis)
  43. - Fix mime type settings for audio output
  44. - Fixed color allocation issues with multiple colors and background images, consolidate allocation to one function
  45. - Ability to let codes expire after a given length of time
  46. - Allow HTML color codes to be passed to Securimage_Color (suggested by Mike Challis)
  47. 2.0.0
  48. - Add mathematical distortion to characters (using code from HKCaptcha)
  49. - Improved session support
  50. - Added Securimage_Color class for easier color definitions
  51. - Add distortion to audio output to prevent binary comparison attack (proposed by Sven "SavageTiger" Hagemann [insecurity.nl])
  52. - Flash button to stream mp3 audio (Douglas Walsh www.douglaswalsh.net)
  53. - Audio output is mp3 format by default
  54. - Change font to AlteHaasGrotesk by yann le coroller
  55. - Some code cleanup
  56. 1.0.4 (unreleased)
  57. - Ability to output audible codes in mp3 format to stream from flash
  58. 1.0.3.1
  59. - Error reading from wordlist in some cases caused words to be cut off 1 letter short
  60. 1.0.3
  61. - Removed shadow_text from code which could cause an undefined property error due to removal from previous version
  62. 1.0.2
  63. - Audible CAPTCHA Code wav files
  64. - Create codes from a word list instead of random strings
  65. 1.0
  66. - Added the ability to use a selected character set, rather than a-z0-9 only.
  67. - Added the multi-color text option to use different colors for each letter.
  68. - Switched to automatic session handling instead of using files for code storage
  69. - Added GD Font support if ttf support is not available. Can use internal GD fonts or load new ones.
  70. - Added the ability to set line thickness
  71. - Added option for drawing arced lines over letters
  72. - Added ability to choose image type for output
  73. */
  74. /**
  75. * Output images in JPEG format
  76. */
  77. if (!defined('SI_IMAGE_JPEG'))
  78. define('SI_IMAGE_JPEG', 1);
  79. /**
  80. * Output images in PNG format
  81. */
  82. if (!defined('SI_IMAGE_PNG'))
  83. define('SI_IMAGE_PNG', 2);
  84. /**
  85. * Output images in GIF format (not recommended)
  86. * Must have GD >= 2.0.28!
  87. */
  88. if (!defined('SI_IMAGE_GIF'))
  89. define('SI_IMAGE_GIF', 3);
  90. /**
  91. * Securimage CAPTCHA Class.
  92. *
  93. * @package Securimage
  94. * @subpackage classes
  95. *
  96. */
  97. class Securimage {
  98. /**
  99. * The path which contains securimage.php.
  100. *
  101. * @var string The path to the securimage installation.
  102. */
  103. var $basepath;
  104. /**
  105. * The desired width of the CAPTCHA image.
  106. *
  107. * @var int
  108. */
  109. var $image_width;
  110. /**
  111. * The desired width of the CAPTCHA image.
  112. *
  113. * @var int
  114. */
  115. var $image_height;
  116. /**
  117. * The image format for output.<br />
  118. * Valid options: SI_IMAGE_PNG, SI_IMAGE_JPG, SI_IMAGE_GIF
  119. *
  120. * @var int
  121. */
  122. var $image_type;
  123. /**
  124. * The length of the code to generate.
  125. *
  126. * @var int
  127. */
  128. var $code_length;
  129. /**
  130. * The character set for individual characters in the image.<br />
  131. * Letters are converted to uppercase.<br />
  132. * The font must support the letters or there may be problematic substitutions.
  133. *
  134. * @var string
  135. */
  136. var $charset;
  137. /**
  138. * Create codes using this word list
  139. *
  140. * @var string The path to the word list to use for creating CAPTCHA codes
  141. */
  142. var $wordlist_file;
  143. /**
  144. * Use wordlist of not
  145. *
  146. * @var bool true to use wordlist file, false to use random code
  147. */
  148. var $use_wordlist = false;
  149. /**
  150. * Note: Use of GD fonts is not recommended as many distortion features are not available<br />
  151. * The GD font to use.<br />
  152. * Internal gd fonts can be loaded by their number.<br />
  153. * Alternatively, a file path can be given and the font will be loaded from file.
  154. *
  155. * @var mixed
  156. */
  157. var $gd_font_file;
  158. /**
  159. * The approximate size of the font in pixels.<br />
  160. * This does not control the size of the font because that is determined by the GD font itself.<br />
  161. * This is used to aid the calculations of positioning used by this class.<br />
  162. *
  163. * @var int
  164. */
  165. var $gd_font_size;
  166. /**
  167. * Use a gd font instead of TTF
  168. *
  169. * @var bool true for gd font, false for TTF
  170. */
  171. var $use_gd_font;
  172. // Note: These font options below do not apply if you set $use_gd_font to true with the exception of $text_color
  173. /**
  174. * The path to the TTF font file to load.
  175. *
  176. * @var string
  177. */
  178. var $ttf_file;
  179. /**
  180. * How much to distort image, higher = more distortion.<br />
  181. * Distortion is only available when using TTF fonts.<br />
  182. *
  183. * @var float
  184. */
  185. var $perturbation;
  186. /**
  187. * The minimum angle in degrees, with 0 degrees being left-to-right reading text.<br />
  188. * Higher values represent a counter-clockwise rotation.<br />
  189. * For example, a value of 90 would result in bottom-to-top reading text.<br />
  190. * This value along with maximum angle distance do not need to be very high with perturbation
  191. *
  192. * @var int
  193. */
  194. var $text_angle_minimum;
  195. /**
  196. * The minimum angle in degrees, with 0 degrees being left-to-right reading text.<br />
  197. * Higher values represent a counter-clockwise rotation.<br />
  198. * For example, a value of 90 would result in bottom-to-top reading text.
  199. *
  200. * @var int
  201. */
  202. var $text_angle_maximum;
  203. /**
  204. * The X-Position on the image where letter drawing will begin.<br />
  205. * This value is in pixels from the left side of the image.
  206. *
  207. * @var int
  208. * @deprecated 2.0
  209. */
  210. var $text_x_start;
  211. /**
  212. * The background color for the image as a Securimage_Color.<br />
  213. *
  214. * @var Securimage_Color
  215. */
  216. var $image_bg_color;
  217. /**
  218. * Scan this directory for gif, jpg, and png files to use as background images.<br />
  219. * A random image file will be picked each time.<br />
  220. * Change from null to the full path to your directory.<br />
  221. * i.e. var $background_directory = $_SERVER['DOCUMENT_ROOT'] . '/securimage/backgrounds';
  222. * Make sure not to pass a background image to the show function, otherwise this directive is ignored.
  223. *
  224. * @var string
  225. */
  226. var $background_directory = null; //'./backgrounds';
  227. /**
  228. * The text color to use for drawing characters as a Securimage_Color.<br />
  229. * This value is ignored if $use_multi_text is set to true.<br />
  230. * Make sure this contrasts well with the background color or image.<br />
  231. *
  232. * @see Securimage::$use_multi_text
  233. * @var Securimage_Color
  234. */
  235. var $text_color;
  236. /**
  237. * Set to true to use multiple colors for each character.
  238. *
  239. * @see Securimage::$multi_text_color
  240. * @var boolean
  241. */
  242. var $use_multi_text;
  243. /**
  244. * Array of Securimage_Colors which will be randomly selected for each letter.<br />
  245. *
  246. * @var array
  247. */
  248. var $multi_text_color;
  249. /**
  250. * Set to true to make the characters appear transparent.
  251. *
  252. * @see Securimage::$text_transparency_percentage
  253. * @var boolean
  254. */
  255. var $use_transparent_text;
  256. /**
  257. * The percentage of transparency, 0 to 100.<br />
  258. * A value of 0 is completely opaque, 100 is completely transparent (invisble)
  259. *
  260. * @see Securimage::$use_transparent_text
  261. * @var int
  262. */
  263. var $text_transparency_percentage;
  264. // Line options
  265. /**
  266. * Draw vertical and horizontal lines on the image.
  267. *
  268. * @see Securimage::$line_color
  269. * @see Securimage::$draw_lines_over_text
  270. * @var boolean
  271. */
  272. var $num_lines;
  273. /**
  274. * Color of lines drawn over text
  275. *
  276. * @var string
  277. */
  278. var $line_color;
  279. /**
  280. * Draw the lines over the text.<br />
  281. * If fales lines will be drawn before putting the text on the image.
  282. *
  283. * @var boolean
  284. */
  285. var $draw_lines_over_text;
  286. /**
  287. * Text to write at the bottom corner of captcha image
  288. *
  289. * @since 2.0
  290. * @var string Signature text
  291. */
  292. var $image_signature;
  293. /**
  294. * Color to use for writing signature text
  295. *
  296. * @since 2.0
  297. * @var Securimage_Color
  298. */
  299. var $signature_color;
  300. /**
  301. * Full path to the WAV files to use to make the audio files, include trailing /.<br />
  302. * Name Files [A-Z0-9].wav
  303. *
  304. * @since 1.0.1
  305. * @var string
  306. */
  307. var $audio_path;
  308. /**
  309. * Type of audio file to generate (mp3 or wav)
  310. *
  311. * @var string
  312. */
  313. var $audio_format;
  314. /**
  315. * The session name to use if not the default. Blank for none
  316. *
  317. * @see http://php.net/session_name
  318. * @since 2.0
  319. * @var string
  320. */
  321. var $session_name = '';
  322. /**
  323. * The amount of time in seconds that a code remains valid.<br />
  324. * Any code older than this number will be considered invalid even if entered correctly.<br />
  325. * Any non-numeric or value less than 1 disables this functionality.
  326. *
  327. * @var int
  328. */
  329. var $expiry_time;
  330. /**
  331. * Path to the file to use for storing codes for users.<br />
  332. * THIS FILE MUST ABSOLUTELY NOT BE ACCESSIBLE FROM A WEB BROWSER!!<br />
  333. * Put this file in a directory below the web root or one that is restricted (i.e. an apache .htaccess file with deny from all)<br />
  334. * If you cannot meet those requirements your forms may not be completely protected.<br />
  335. * You could obscure the database file name but this is also not recommended.
  336. *
  337. * @var string
  338. */
  339. var $sqlite_database;
  340. /**
  341. * Use an SQLite database for storing codes as a backup to sessions.<br />
  342. * Note: Sessions will still be used
  343. */
  344. var $use_sqlite_db;
  345. //END USER CONFIGURATION
  346. //There should be no need to edit below unless you really know what you are doing.
  347. /**
  348. * The gd image resource.
  349. *
  350. * @access private
  351. * @var resource
  352. */
  353. var $im;
  354. /**
  355. * Temporary image for rendering
  356. *
  357. * @access private
  358. * @var resource
  359. */
  360. var $tmpimg;
  361. /**
  362. * Internal scale factor for anti-alias @hkcaptcha
  363. *
  364. * @access private
  365. * @since 2.0
  366. * @var int
  367. */
  368. var $iscale; // internal scale factor for anti-alias @hkcaptcha
  369. /**
  370. * The background image resource
  371. *
  372. * @access private
  373. * @var resource
  374. */
  375. var $bgimg;
  376. /**
  377. * The code generated by the script
  378. *
  379. * @access private
  380. * @var string
  381. */
  382. var $code;
  383. /**
  384. * The code that was entered by the user
  385. *
  386. * @access private
  387. * @var string
  388. */
  389. var $code_entered;
  390. /**
  391. * Whether or not the correct code was entered
  392. *
  393. * @access private
  394. * @var boolean
  395. */
  396. var $correct_code;
  397. /**
  398. * Handle to SQLite database
  399. *
  400. * @access private
  401. * @var resource
  402. */
  403. var $sqlite_handle;
  404. /**
  405. * Color resource for image line color
  406. *
  407. * @access private
  408. * @var int
  409. */
  410. var $gdlinecolor;
  411. /**
  412. * Array of colors for multi colored codes
  413. *
  414. * @access private
  415. * @var array
  416. */
  417. var $gdmulticolor;
  418. /**
  419. * Color resource for image font color
  420. *
  421. * @access private
  422. * @var int
  423. */
  424. var $gdtextcolor;
  425. /**
  426. * Color resource for image signature color
  427. *
  428. * @access private
  429. * @var int
  430. */
  431. var $gdsignaturecolor;
  432. /**
  433. * Color resource for image background color
  434. *
  435. * @access private
  436. * @var int
  437. */
  438. var $gdbgcolor;
  439. /**
  440. * Class constructor.<br />
  441. * Because the class uses sessions, this will attempt to start a session if there is no previous one.<br />
  442. * If you do not start a session before calling the class, the constructor must be called before any
  443. * output is sent to the browser.
  444. *
  445. * <code>
  446. * $securimage = new Securimage();
  447. * </code>
  448. *
  449. */
  450. function Securimage()
  451. {
  452. // Initialize session or attach to existing
  453. if ( session_id() == '' ) { // no session has been started yet, which is needed for validation
  454. if (trim($this->session_name) != '') {
  455. session_name($this->session_name); // set session name if provided
  456. }
  457. session_start();
  458. }
  459. // Calculated value
  460. $this->basepath = dirname(__FILE__);
  461. // Set Default Values
  462. $this->image_width = 230;
  463. $this->image_height = 80;
  464. $this->image_type = SI_IMAGE_PNG;
  465. $this->code_length = 6;
  466. $this->charset = 'ABCDEFGHKLMNPRSTUVWYZabcdefghklmnprstuvwyz23456789';
  467. $this->wordlist_file = $this->basepath . '/words/words.txt';
  468. $this->use_wordlist = false;
  469. $this->gd_font_file = 'gdfonts/automatic.gdf';
  470. $this->use_gd_font = false;
  471. $this->gd_font_size = 24;
  472. $this->text_x_start = 15;
  473. $this->ttf_file = $this->basepath . '/AHGBold.ttf';
  474. $this->perturbation = 0.75;
  475. $this->iscale = 5;
  476. $this->text_angle_minimum = 0;
  477. $this->text_angle_maximum = 0;
  478. $this->image_bg_color = new Securimage_Color(0xff, 0xff, 0xff);
  479. $this->text_color = new Securimage_Color(0x3d, 0x3d, 0x3d);
  480. $this->multi_text_color = array(new Securimage_Color(0x0, 0x20, 0xCC),
  481. new Securimage_Color(0x0, 0x30, 0xEE),
  482. new Securimage_color(0x0, 0x40, 0xCC),
  483. new Securimage_Color(0x0, 0x50, 0xEE),
  484. new Securimage_Color(0x0, 0x60, 0xCC));
  485. $this->use_multi_text = false;
  486. $this->use_transparent_text = false;
  487. $this->text_transparency_percentage = 30;
  488. $this->num_lines = 10;
  489. $this->line_color = new Securimage_Color(0x3d, 0x3d, 0x3d);
  490. $this->draw_lines_over_text = true;
  491. $this->image_signature = '';
  492. $this->signature_color = new Securimage_Color(0x20, 0x50, 0xCC);
  493. $this->signature_font = $this->basepath . '/AHGBold.ttf';
  494. $this->audio_path = $this->basepath . '/audio/';
  495. $this->audio_format = 'mp3';
  496. $this->session_name = '';
  497. $this->expiry_time = 900;
  498. $this->sqlite_database = 'database/securimage.sqlite';
  499. $this->use_sqlite_db = false;
  500. $this->sqlite_handle = false;
  501. }
  502. /**
  503. * Generate a code and output the image to the browser.
  504. *
  505. * <code>
  506. * <?php
  507. * include 'securimage.php';
  508. * $securimage = new Securimage();
  509. * $securimage->show('bg.jpg');
  510. * ?>
  511. * </code>
  512. *
  513. * @param string $background_image The path to an image to use as the background for the CAPTCHA
  514. */
  515. function show($background_image = "")
  516. {
  517. if($background_image != "" && is_readable($background_image)) {
  518. $this->bgimg = $background_image;
  519. }
  520. $this->doImage();
  521. }
  522. /**
  523. * Validate the code entered by the user.
  524. *
  525. * <code>
  526. * $code = $_POST['code'];
  527. * if ($securimage->check($code) == false) {
  528. * die("Sorry, the code entered did not match.");
  529. * } else {
  530. * $valid = true;
  531. * }
  532. * </code>
  533. * @param string $code The code the user entered
  534. * @return boolean true if the code was correct, false if not
  535. */
  536. function check($code)
  537. {
  538. $this->code_entered = $code;
  539. $this->validate();
  540. return $this->correct_code;
  541. }
  542. /**
  543. * Output audio file with HTTP headers to browser
  544. *
  545. * <code>
  546. * $sound = new Securimage();
  547. * $sound->audio_format = 'mp3';
  548. * $sound->outputAudioFile();
  549. * </code>
  550. *
  551. * @since 2.0
  552. */
  553. function outputAudioFile()
  554. {
  555. if (strtolower($this->audio_format) == 'wav') {
  556. header('Content-type: audio/x-wav');
  557. $ext = 'wav';
  558. } else {
  559. header('Content-type: audio/mpeg'); // default to mp3
  560. $ext = 'mp3';
  561. }
  562. header("Content-Disposition: attachment; filename=\"securimage_audio.{$ext}\"");
  563. header('Cache-Control: no-store, no-cache, must-revalidate');
  564. header('Expires: Sun, 1 Jan 2000 12:00:00 GMT');
  565. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
  566. $audio = $this->getAudibleCode($ext);
  567. header('Content-Length: ' . strlen($audio));
  568. echo $audio;
  569. exit;
  570. }
  571. /**
  572. * Generate and output the image
  573. *
  574. * @access private
  575. *
  576. */
  577. function doImage()
  578. {
  579. if ($this->use_gd_font == true) {
  580. $this->iscale = 1;
  581. }
  582. if($this->use_transparent_text == true || $this->bgimg != "") {
  583. $this->im = imagecreatetruecolor($this->image_width, $this->image_height);
  584. $this->tmpimg = imagecreatetruecolor($this->image_width * $this->iscale, $this->image_height * $this->iscale);
  585. } else { //no transparency
  586. $this->im = imagecreate($this->image_width, $this->image_height);
  587. $this->tmpimg = imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale);
  588. }
  589. $this->allocateColors();
  590. imagepalettecopy($this->tmpimg, $this->im);
  591. $this->setBackground();
  592. $this->createCode();
  593. if (!$this->draw_lines_over_text && $this->num_lines > 0) $this->drawLines();
  594. $this->drawWord();
  595. if ($this->use_gd_font == false && is_readable($this->ttf_file)) $this->distortedCopy();
  596. if ($this->draw_lines_over_text && $this->num_lines > 0) $this->drawLines();
  597. if (trim($this->image_signature) != '') $this->addSignature();
  598. $this->output();
  599. }
  600. /**
  601. * Allocate all colors that will be used in the CAPTCHA image
  602. *
  603. * @since 2.0.1
  604. * @access private
  605. */
  606. function allocateColors()
  607. {
  608. // allocate bg color first for imagecreate
  609. $this->gdbgcolor = imagecolorallocate($this->im, $this->image_bg_color->r, $this->image_bg_color->g, $this->image_bg_color->b);
  610. $alpha = intval($this->text_transparency_percentage / 100 * 127);
  611. if ($this->use_transparent_text == true) {
  612. $this->gdtextcolor = imagecolorallocatealpha($this->im, $this->text_color->r, $this->text_color->g, $this->text_color->b, $alpha);
  613. $this->gdlinecolor = imagecolorallocatealpha($this->im, $this->line_color->r, $this->line_color->g, $this->line_color->b, $alpha);
  614. } else {
  615. $this->gdtextcolor = imagecolorallocate($this->im, $this->text_color->r, $this->text_color->g, $this->text_color->b);
  616. $this->gdlinecolor = imagecolorallocate($this->im, $this->line_color->r, $this->line_color->g, $this->line_color->b);
  617. }
  618. $this->gdsignaturecolor = imagecolorallocate($this->im, $this->signature_color->r, $this->signature_color->g, $this->signature_color->b);
  619. if ($this->use_multi_text == true) {
  620. $this->gdmulticolor = array();
  621. foreach($this->multi_text_color as $color) {
  622. if ($this->use_transparent_text == true) {
  623. $this->gdmulticolor[] = imagecolorallocatealpha($this->im, $color->r, $color->g, $color->b, $alpha);
  624. } else {
  625. $this->gdmulticolor[] = imagecolorallocate($this->im, $color->r, $color->g, $color->b);
  626. }
  627. }
  628. }
  629. }
  630. /**
  631. * Set the background of the CAPTCHA image
  632. *
  633. * @access private
  634. *
  635. */
  636. function setBackground()
  637. {
  638. imagefilledrectangle($this->im, 0, 0, $this->image_width * $this->iscale, $this->image_height * $this->iscale, $this->gdbgcolor);
  639. imagefilledrectangle($this->tmpimg, 0, 0, $this->image_width * $this->iscale, $this->image_height * $this->iscale, $this->gdbgcolor);
  640. if ($this->bgimg == '') {
  641. if ($this->background_directory != null && is_dir($this->background_directory) && is_readable($this->background_directory)) {
  642. $img = $this->getBackgroundFromDirectory();
  643. if ($img != false) {
  644. $this->bgimg = $img;
  645. }
  646. }
  647. }
  648. $dat = @getimagesize($this->bgimg);
  649. if($dat == false) {
  650. return;
  651. }
  652. switch($dat[2]) {
  653. case 1: $newim = @imagecreatefromgif($this->bgimg); break;
  654. case 2: $newim = @imagecreatefromjpeg($this->bgimg); break;
  655. case 3: $newim = @imagecreatefrompng($this->bgimg); break;
  656. case 15: $newim = @imagecreatefromwbmp($this->bgimg); break;
  657. case 16: $newim = @imagecreatefromxbm($this->bgimg); break;
  658. default: return;
  659. }
  660. if(!$newim) return;
  661. imagecopyresized($this->im, $newim, 0, 0, 0, 0, $this->image_width, $this->image_height, imagesx($newim), imagesy($newim));
  662. }
  663. /**
  664. * Return the full path to a random gif, jpg, or png from the background directory.
  665. *
  666. * @access private
  667. * @see Securimage::$background_directory
  668. * @return mixed false if none found, string $path if found
  669. */
  670. function getBackgroundFromDirectory()
  671. {
  672. $images = array();
  673. if ($dh = opendir($this->background_directory)) {
  674. while (($file = readdir($dh)) !== false) {
  675. if (preg_match('/(jpg|gif|png)$/i', $file)) $images[] = $file;
  676. }
  677. closedir($dh);
  678. if (sizeof($images) > 0) {
  679. return rtrim($this->background_directory, '/') . '/' . $images[rand(0, sizeof($images)-1)];
  680. }
  681. }
  682. return false;
  683. }
  684. /**
  685. * Draw random curvy lines over the image<br />
  686. * Modified code from HKCaptcha
  687. *
  688. * @since 2.0
  689. * @access private
  690. *
  691. */
  692. function drawLines()
  693. {
  694. for ($line = 0; $line < $this->num_lines; ++$line) {
  695. $x = $this->image_width * (1 + $line) / ($this->num_lines + 1);
  696. $x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines;
  697. $y = rand($this->image_height * 0.1, $this->image_height * 0.9);
  698. $theta = ($this->frand()-0.5) * M_PI * 0.7;
  699. $w = $this->image_width;
  700. $len = rand($w * 0.4, $w * 0.7);
  701. $lwid = rand(0, 2);
  702. $k = $this->frand() * 0.6 + 0.2;
  703. $k = $k * $k * 0.5;
  704. $phi = $this->frand() * 6.28;
  705. $step = 0.5;
  706. $dx = $step * cos($theta);
  707. $dy = $step * sin($theta);
  708. $n = $len / $step;
  709. $amp = 1.5 * $this->frand() / ($k + 5.0 / $len);
  710. $x0 = $x - 0.5 * $len * cos($theta);
  711. $y0 = $y - 0.5 * $len * sin($theta);
  712. $ldx = round(-$dy * $lwid);
  713. $ldy = round($dx * $lwid);
  714. for ($i = 0; $i < $n; ++$i) {
  715. $x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi);
  716. $y = $y0 + $i * $dy - $amp * $dx * sin($k * $i * $step + $phi);
  717. imagefilledrectangle($this->im, $x, $y, $x + $lwid, $y + $lwid, $this->gdlinecolor);
  718. }
  719. }
  720. }
  721. /**
  722. * Draw the CAPTCHA code over the image
  723. *
  724. * @access private
  725. *
  726. */
  727. function drawWord()
  728. {
  729. $width2 = $this->image_width * $this->iscale;
  730. $height2 = $this->image_height * $this->iscale;
  731. if ($this->use_gd_font == true || !is_readable($this->ttf_file)) {
  732. if (!is_int($this->gd_font_file)) { //is a file name
  733. $font = @imageloadfont($this->gd_font_file);
  734. if ($font == false) {
  735. trigger_error("Failed to load GD Font file {$this->gd_font_file} ", E_USER_WARNING);
  736. return;
  737. }
  738. } else { //gd font identifier
  739. $font = $this->gd_font_file;
  740. }
  741. imagestring($this->im, $font, $this->text_x_start, ($this->image_height / 2) - ($this->gd_font_size / 2), $this->code, $this->gdtextcolor);
  742. } else { //ttf font
  743. $font_size = $height2 * .35;
  744. $bb = imagettfbbox($font_size, 0, $this->ttf_file, $this->code);
  745. $tx = $bb[4] - $bb[0];
  746. $ty = $bb[5] - $bb[1];
  747. $x = floor($width2 / 2 - $tx / 2 - $bb[0]);
  748. $y = round($height2 / 2 - $ty / 2 - $bb[1]);
  749. $strlen = strlen($this->code);
  750. if (!is_array($this->multi_text_color)) $this->use_multi_text = false;
  751. if ($this->use_multi_text == false && $this->text_angle_minimum == 0 && $this->text_angle_maximum == 0) { // no angled or multi-color characters
  752. imagettftext($this->tmpimg, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code);
  753. } else {
  754. for($i = 0; $i < $strlen; ++$i) {
  755. $angle = rand($this->text_angle_minimum, $this->text_angle_maximum);
  756. $y = rand($y - 5, $y + 5);
  757. if ($this->use_multi_text == true) {
  758. $font_color = $this->gdmulticolor[rand(0, sizeof($this->gdmulticolor) - 1)];
  759. } else {
  760. $font_color = $this->gdtextcolor;
  761. }
  762. $ch = $this->code{$i};
  763. imagettftext($this->tmpimg, $font_size, $angle, $x, $y, $font_color, $this->ttf_file, $ch);
  764. // estimate character widths to increment $x without creating spaces that are too large or too small
  765. // these are best estimates to align text but may vary between fonts
  766. // for optimal character widths, do not use multiple text colors or character angles and the complete string will be written by imagettftext
  767. if (strpos('abcdeghknopqsuvxyz', $ch) !== false) {
  768. $min_x = $font_size - ($this->iscale * 6);
  769. $max_x = $font_size - ($this->iscale * 6);
  770. } else if (strpos('ilI1', $ch) !== false) {
  771. $min_x = $font_size / 5;
  772. $max_x = $font_size / 3;
  773. } else if (strpos('fjrt', $ch) !== false) {
  774. $min_x = $font_size - ($this->iscale * 12);
  775. $max_x = $font_size - ($this->iscale * 12);
  776. } else if ($ch == 'wm') {
  777. $min_x = $font_size;
  778. $max_x = $font_size + ($this->iscale * 3);
  779. } else { // numbers, capitals or unicode
  780. $min_x = $font_size + ($this->iscale * 2);
  781. $max_x = $font_size + ($this->iscale * 5);
  782. }
  783. $x += rand($min_x, $max_x);
  784. } //for loop
  785. } // angled or multi-color
  786. } //else ttf font
  787. //$this->im = $this->tmpimg;
  788. //$this->output();
  789. } //function
  790. /**
  791. * Warp text from temporary image onto final image.<br />
  792. * Modified for securimage
  793. *
  794. * @access private
  795. * @since 2.0
  796. * @author Han-Kwang Nienhuys modified
  797. * @copyright Han-Kwang Neinhuys
  798. *
  799. */
  800. function distortedCopy()
  801. {
  802. $numpoles = 3; // distortion factor
  803. // make array of poles AKA attractor points
  804. for ($i = 0; $i < $numpoles; ++$i) {
  805. $px[$i] = rand($this->image_width * 0.3, $this->image_width * 0.7);
  806. $py[$i] = rand($this->image_height * 0.3, $this->image_height * 0.7);
  807. $rad[$i] = rand($this->image_width * 0.4, $this->image_width * 0.7);
  808. $tmp = -$this->frand() * 0.15 - 0.15;
  809. $amp[$i] = $this->perturbation * $tmp;
  810. }
  811. $bgCol = imagecolorat($this->tmpimg, 0, 0);
  812. $width2 = $this->iscale * $this->image_width;
  813. $height2 = $this->iscale * $this->image_height;
  814. imagepalettecopy($this->im, $this->tmpimg); // copy palette to final image so text colors come across
  815. // loop over $img pixels, take pixels from $tmpimg with distortion field
  816. for ($ix = 0; $ix < $this->image_width; ++$ix) {
  817. for ($iy = 0; $iy < $this->image_height; ++$iy) {
  818. $x = $ix;
  819. $y = $iy;
  820. for ($i = 0; $i < $numpoles; ++$i) {
  821. $dx = $ix - $px[$i];
  822. $dy = $iy - $py[$i];
  823. if ($dx == 0 && $dy == 0) continue;
  824. $r = sqrt($dx * $dx + $dy * $dy);
  825. if ($r > $rad[$i]) continue;
  826. $rscale = $amp[$i] * sin(3.14 * $r / $rad[$i]);
  827. $x += $dx * $rscale;
  828. $y += $dy * $rscale;
  829. }
  830. $c = $bgCol;
  831. $x *= $this->iscale;
  832. $y *= $this->iscale;
  833. if ($x >= 0 && $x < $width2 && $y >= 0 && $y < $height2) {
  834. $c = imagecolorat($this->tmpimg, $x, $y);
  835. }
  836. if ($c != $bgCol) { // only copy pixels of letters to preserve any background image
  837. imagesetpixel($this->im, $ix, $iy, $c);
  838. }
  839. }
  840. }
  841. }
  842. /**
  843. * Create a code and save to the session
  844. *
  845. * @access private
  846. * @since 1.0.1
  847. *
  848. */
  849. function createCode()
  850. {
  851. $this->code = false;
  852. if ($this->use_wordlist && is_readable($this->wordlist_file)) {
  853. $this->code = $this->readCodeFromFile();
  854. }
  855. if ($this->code == false) {
  856. $this->code = $this->generateCode($this->code_length);
  857. }
  858. $this->saveData();
  859. }
  860. /**
  861. * Generate a code
  862. *
  863. * @access private
  864. * @param int $len The code length
  865. * @return string
  866. */
  867. function generateCode($len)
  868. {
  869. $code = '';
  870. for($i = 1, $cslen = strlen($this->charset); $i <= $len; ++$i) {
  871. $code .= $this->charset{rand(0, $cslen - 1)};
  872. }
  873. return $code;
  874. }
  875. /**
  876. * Reads a word list file to get a code
  877. *
  878. * @access private
  879. * @since 1.0.2
  880. * @return mixed false on failure, a word on success
  881. */
  882. function readCodeFromFile()
  883. {
  884. $fp = @fopen($this->wordlist_file, 'rb');
  885. if (!$fp) return false;
  886. $fsize = filesize($this->wordlist_file);
  887. if ($fsize < 32) return false; // too small of a list to be effective
  888. if ($fsize < 128) {
  889. $max = $fsize; // still pretty small but changes the range of seeking
  890. } else {
  891. $max = 128;
  892. }
  893. fseek($fp, rand(0, $fsize - $max), SEEK_SET);
  894. $data = fread($fp, 128); // read a random 128 bytes from file
  895. fclose($fp);
  896. $data = preg_replace("/\r?\n/", "\n", $data);
  897. $start = strpos($data, "\n", rand(0, 100)) + 1; // random start position
  898. $end = strpos($data, "\n", $start); // find end of word
  899. return strtolower(substr($data, $start, $end - $start)); // return substring in 128 bytes
  900. }
  901. /**
  902. * Output image to the browser
  903. *
  904. * @access private
  905. *
  906. */
  907. function output()
  908. {
  909. header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
  910. header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
  911. header("Cache-Control: no-store, no-cache, must-revalidate");
  912. header("Cache-Control: post-check=0, pre-check=0", false);
  913. header("Pragma: no-cache");
  914. switch($this->image_type)
  915. {
  916. case SI_IMAGE_JPEG:
  917. header("Content-Type: image/jpeg");
  918. imagejpeg($this->im, null, 90);
  919. break;
  920. case SI_IMAGE_GIF:
  921. header("Content-Type: image/gif");
  922. imagegif($this->im);
  923. break;
  924. default:
  925. header("Content-Type: image/png");
  926. imagepng($this->im);
  927. break;
  928. }
  929. imagedestroy($this->im);
  930. exit;
  931. }
  932. /**
  933. * Get WAV or MP3 file data of the spoken code.<br />
  934. * This is appropriate for output to the browser as audio/x-wav or audio/mpeg
  935. *
  936. * @since 1.0.1
  937. * @return string WAV or MP3 data
  938. *
  939. */
  940. function getAudibleCode($format = 'wav')
  941. {
  942. $letters = array();
  943. $code = $this->getCode();
  944. if ($code == '') {
  945. $this->createCode();
  946. $code = $this->getCode();
  947. }
  948. for($i = 0; $i < strlen($code); ++$i) {
  949. $letters[] = $code{$i};
  950. }
  951. if ($format == 'mp3') {
  952. return $this->generateMP3($letters);
  953. } else {
  954. return $this->generateWAV($letters);
  955. }
  956. }
  957. /**
  958. * Set the path to the audio directory.<br />
  959. *
  960. * @since 1.0.4
  961. * @return bool true if the directory exists and is readble, false if not
  962. */
  963. function setAudioPath($audio_directory)
  964. {
  965. if (is_dir($audio_directory) && is_readable($audio_directory)) {
  966. $this->audio_path = $audio_directory;
  967. return true;
  968. } else {
  969. return false;
  970. }
  971. }
  972. /**
  973. * Save the code in the session
  974. *
  975. * @access private
  976. *
  977. */
  978. function saveData()
  979. {
  980. $_SESSION['securimage_code_value'] = strtolower($this->code);
  981. $_SESSION['securimage_code_ctime'] = time();
  982. $this->saveCodeToDatabase();
  983. }
  984. /**
  985. * Validate the code to the user code
  986. *
  987. * @access private
  988. *
  989. */
  990. function validate()
  991. {
  992. // retrieve code from session, if no code exists check sqlite database if supported.
  993. if (isset($_SESSION['securimage_code_value']) && trim($_SESSION['securimage_code_value']) != '') {
  994. if ($this->isCodeExpired($_SESSION['securimage_code_ctime']) == false) {
  995. $code = $_SESSION['securimage_code_value'];
  996. }
  997. } else if ($this->use_sqlite_db == true && function_exists('sqlite_open')) { // no code in session - may mean user has cookies turned off
  998. $this->openDatabase();
  999. $code = $this->getCodeFromDatabase();
  1000. } else {
  1001. // session code invalid or non-existant and code not found in sqlite db or sqlite is not available
  1002. $code = '';
  1003. }
  1004. $code = trim(strtolower($code));
  1005. $code_entered = trim(strtolower($this->code_entered));
  1006. $this->correct_code = false;
  1007. if ($code != '') {
  1008. if ($code == $code_entered) {
  1009. $this->correct_code = true;
  1010. $_SESSION['securimage_code_value'] = '';
  1011. $_SESSION['securimage_code_ctime'] = '';
  1012. $this->clearCodeFromDatabase();
  1013. }
  1014. }
  1015. }
  1016. /**
  1017. * Get the captcha code
  1018. *
  1019. * @since 1.0.1
  1020. * @return string
  1021. */
  1022. function getCode()
  1023. {
  1024. if (isset($_SESSION['securimage_code_value']) && !empty($_SESSION['securimage_code_value'])) {
  1025. return strtolower($_SESSION['securimage_code_value']);
  1026. } else {
  1027. if ($this->sqlite_handle == false) $this->openDatabase();
  1028. return $this->getCodeFromDatabase(); // attempt to get from database, returns empty string if sqlite is not available or disabled
  1029. }
  1030. }
  1031. /**
  1032. * Check if the user entered code was correct
  1033. *
  1034. * @access private
  1035. * @return boolean
  1036. */
  1037. function checkCode()
  1038. {
  1039. return $this->correct_code;
  1040. }
  1041. /**
  1042. * Generate a wav file by concatenating individual files
  1043. *
  1044. * @since 1.0.1
  1045. * @access private
  1046. * @param array $letters Array of letters to build a file from
  1047. * @return string WAV file data
  1048. */
  1049. function generateWAV($letters)
  1050. {
  1051. $data_len = 0;
  1052. $files = array();
  1053. $out_data = '';
  1054. foreach ($letters as $letter) {
  1055. $filename = $this->audio_path . strtoupper($letter) . '.wav';
  1056. $fp = fopen($filename, 'rb');
  1057. $file = array();
  1058. $data = fread($fp, filesize($filename)); // read file in
  1059. $header = substr($data, 0, 36);
  1060. $body = substr($data, 44);
  1061. $data = unpack('NChunkID/VChunkSize/NFormat/NSubChunk1ID/VSubChunk1Size/vAudioFormat/vNumChannels/VSampleRate/VByteRate/vBlockAlign/vBitsPerSample', $header);
  1062. $file['sub_chunk1_id'] = $data['SubChunk1ID'];
  1063. $file['bits_per_sample'] = $data['BitsPerSample'];
  1064. $file['channels'] = $data['NumChannels'];
  1065. $file['format'] = $data['AudioFormat'];
  1066. $file['sample_rate'] = $data['SampleRate'];
  1067. $file['size'] = $data['ChunkSize'] + 8;
  1068. $file['data'] = $body;
  1069. if ( ($p = strpos($file['data'], 'LIST')) !== false) {
  1070. // If the LIST data is not at the end of the file, this will probably break your sound file
  1071. $info = substr($file['data'], $p + 4, 8);
  1072. $data = unpack('Vlength/Vjunk', $info);
  1073. $file['data'] = substr($file['data'], 0, $p);
  1074. $file['size'] = $file['size'] - (strlen($file['data']) - $p);
  1075. }
  1076. $files[] = $file;
  1077. $data = null;
  1078. $header = null;
  1079. $body = null;
  1080. $data_len += strlen($file['data']);
  1081. fclose($fp);
  1082. }
  1083. $out_data = '';
  1084. for($i = 0; $i < sizeof($files); ++$i) {
  1085. if ($i == 0) { // output header
  1086. $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(' '));
  1087. $out_data .= pack('VvvVVvv',
  1088. 16,
  1089. $files[$i]['format'],
  1090. $files[$i]['channels'],
  1091. $files[$i]['sample_rate'],
  1092. $files[$i]['sample_rate'] * (($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8),
  1093. ($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8,
  1094. $files[$i]['bits_per_sample'] );
  1095. $out_data .= pack('C4', ord('d'), ord('a'), ord('t'), ord('a'));
  1096. $out_data .= pack('V', $data_len);
  1097. }
  1098. $out_data .= $files[$i]['data'];
  1099. }
  1100. $this->scrambleAudioData($out_data, 'wav');
  1101. return $out_data;
  1102. }
  1103. /**
  1104. * Randomly modify the audio data to scramble sound and prevent binary recognition.<br />
  1105. * Take care not to "break" the audio file by leaving the header data intact.
  1106. *
  1107. * @since 2.0
  1108. * @access private
  1109. * @param $data Sound data in mp3 of wav format
  1110. */
  1111. function scrambleAudioData(&$data, $format)
  1112. {
  1113. if ($format == 'wav') {
  1114. $start = strpos($data, 'data') + 4; // look for "data" indicator
  1115. if ($start === false) $start = 44; // if not found assume 44 byte header
  1116. } else { // mp3
  1117. $start = 4; // 4 byte (32 bit) frame header
  1118. }
  1119. $start += rand(1, 64); // randomize starting offset
  1120. $datalen = strlen($data) - $start - 256; // leave last 256 bytes unchanged
  1121. for ($i = $start; $i < $datalen; $i += 64) {
  1122. $ch = ord($data{$i});
  1123. if ($ch < 9 || $ch > 119) continue;
  1124. $data{$i} = chr($ch + rand(-8, 8));
  1125. }
  1126. }
  1127. /**
  1128. * Generate an mp3 file by concatenating individual files
  1129. * @since 1.0.4
  1130. * @access private
  1131. * @param array $letters Array of letters to build a file from
  1132. * @return string MP3 file data
  1133. */
  1134. function generateMP3($letters)
  1135. {
  1136. $data_len = 0;
  1137. $files = array();
  1138. $out_data = '';
  1139. foreach ($letters as $letter) {
  1140. $filename = $this->audio_path . strtoupper($letter) . '.mp3';
  1141. $fp = fopen($filename, 'rb');
  1142. $data = fread($fp, filesize($filename)); // read file in
  1143. $this->scrambleAudioData($data, 'mp3');
  1144. $out_data .= $data;
  1145. fclose($fp);
  1146. }
  1147. return $out_data;
  1148. }
  1149. /**
  1150. * Generate random number less than 1
  1151. * @since 2.0
  1152. * @access private
  1153. * @return float
  1154. */
  1155. function frand()
  1156. {
  1157. return 0.0001*rand(0,9999);
  1158. }
  1159. /**
  1160. * Print signature text on image
  1161. *
  1162. * @since 2.0
  1163. * @access private
  1164. *
  1165. */
  1166. function addSignature()
  1167. {
  1168. if ($this->use_gd_font) {
  1169. imagestring($this->im, 5, $this->image_width - (strlen($this->image_signature) * 10), $this->image_height - 20, $this->image_signature, $this->gdsignaturecolor);
  1170. } else {
  1171. $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
  1172. $textlen = $bbox[2] - $bbox[0];
  1173. $x = $this->image_width - $textlen - 5;
  1174. $y = $this->image_height - 3;
  1175. imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature);
  1176. }
  1177. }
  1178. /**
  1179. * Get hashed IP address of remote user
  1180. *
  1181. * @access private
  1182. * @since 2.0.1
  1183. * @return string
  1184. */
  1185. function getIPHash()
  1186. {
  1187. return strtolower(md5($_SERVER['REMOTE_ADDR']));
  1188. }
  1189. /**
  1190. * Open SQLite database
  1191. *
  1192. * @access private
  1193. * @since 2.0.1
  1194. * @return bool true if database was opened successfully
  1195. */
  1196. function openDatabase()
  1197. {
  1198. $this->sqlite_handle = false;
  1199. if ($this->use_sqlite_db && function_exists('sqlite_open')) {
  1200. $this->sqlite_handle = sqlite_open($this->sqlite_database, 0666, $error);
  1201. if ($this->sqlite_handle !== false) {
  1202. $res = sqlite_query($this->sqlite_handle, "PRAGMA table_info(codes)");
  1203. if (sqlite_num_rows($res) == 0) {
  1204. sqlite_query($this->sqlite_handle, "CREATE TABLE codes (iphash VARCHAR(32) PRIMARY KEY, code VARCHAR(32) NOT NULL, created INTEGER)");
  1205. }
  1206. }
  1207. return $this->sqlite_handle != false;
  1208. }
  1209. return $this->sqlite_handle;
  1210. }
  1211. /**
  1212. * Save captcha code to sqlite database
  1213. *
  1214. * @access private
  1215. * @since 2.0.1
  1216. * @return bool true if code was saved, false if not
  1217. */
  1218. function saveCodeToDatabase()
  1219. {
  1220. $success = false;
  1221. $this->openDatabase();
  1222. if ($this->use_sqlite_db && $this->sqlite_handle !== false) {
  1223. $ip = $this->getIPHash();
  1224. $time = time();
  1225. $code = $_SESSION['securimage_code_value']; // hash code for security - if cookies are disabled the session still exists at this point
  1226. $success = sqlite_query($this->sqlite_handle, "INSERT OR REPLACE INTO codes(iphash, code, created) VALUES('$ip', '$code', $time)");
  1227. }
  1228. return $success !== false;
  1229. }
  1230. /**
  1231. * Get stored captcha code from sqlite database based on ip address hash
  1232. *
  1233. * @access private
  1234. * @since 2.0.1
  1235. * @return string captcha code
  1236. */
  1237. function getCodeFromDatabase()
  1238. {
  1239. $code = '';
  1240. if ($this->use_sqlite_db && $this->sqlite_handle !== false) {
  1241. $ip = $this->getIPHash();
  1242. $res = sqlite_query($this->sqlite_handle, "SELECT * FROM codes WHERE iphash = '$ip'");
  1243. if ($res && sqlite_num_rows($res) > 0) {
  1244. $res = sqlite_fetch_array($res);
  1245. if ($this->isCodeExpired($res['created']) == false) {
  1246. $code = $res['code'];
  1247. }
  1248. }
  1249. }
  1250. return $code;
  1251. }
  1252. /**
  1253. * Delete a code from the database by ip address hash
  1254. *
  1255. * @access private
  1256. * @since 2.0.1
  1257. */
  1258. function clearCodeFromDatabase()
  1259. {
  1260. if ($this->sqlite_handle !== false) {
  1261. $ip = $this->getIPHash();
  1262. sqlite_query($this->sqlite_handle, "DELETE FROM codes WHERE iphash = '$ip'");
  1263. }
  1264. }
  1265. /**
  1266. * Purge codes over a day old from database
  1267. *
  1268. * @access private
  1269. * @since 2.0.1
  1270. */
  1271. function purgeOldCodesFromDatabase()
  1272. {
  1273. if ($this->use_sqlite_db && $this->sqlite_handle !== false) {
  1274. $now = time();
  1275. $limit = (!is_numeric($this->expiry_time) || $this->expiry_time < 1) ? 86400 : $this->expiry_time;
  1276. sqlite_query($this->sqlite_handle, "DELETE FROM codes WHERE $now - created > $limit");
  1277. }
  1278. }
  1279. /**
  1280. * Check a code to see if it is expired based on creation time
  1281. *
  1282. * @access private
  1283. * @since 2.0.1
  1284. * @param $creation_time unix timestamp of code creation time
  1285. * @return bool true if code has expired, false if not
  1286. */
  1287. function isCodeExpired($creation_time)
  1288. {
  1289. $expired = true;
  1290. if (!is_numeric($this->expiry_time) || $this->expiry_time < 1) {
  1291. $expired = false;
  1292. } else if (time() - $creation_time < $this->expiry_time) {
  1293. $expired = false;
  1294. }
  1295. return $expired;
  1296. }
  1297. } /* class Securimage */
  1298. /**
  1299. * Color object for Securimage CAPTCHA
  1300. *
  1301. * @since 2.0
  1302. * @package Securimage
  1303. * @subpackage classes
  1304. *
  1305. */
  1306. class Securimage_Color {
  1307. /**
  1308. * Red component: 0-255
  1309. *
  1310. * @var int
  1311. */
  1312. var $r;
  1313. /**
  1314. * Green component: 0-255
  1315. *
  1316. * @var int
  1317. */
  1318. var $g;
  1319. /**
  1320. * Blue component: 0-255
  1321. *
  1322. * @var int
  1323. */
  1324. var $b;
  1325. /**
  1326. * Create a new Securimage_Color object.<br />
  1327. * Specify the red, green, and blue components using their HTML hex code equivalent.<br />
  1328. * Example: The code for the HTML color #4A203C is:<br />
  1329. * $color = new Securimage_Color(0x4A, 0x20, 0x3C);
  1330. *
  1331. * @param $red Red component 0-255
  1332. * @param $green Green component 0-255
  1333. * @param $blue Blue component 0-255
  1334. */
  1335. function Securimage_Color($red, $green = null, $blue = null)
  1336. {
  1337. if ($green == null && $blue == null && preg_match('/^#[a-f0-9]{3,6}$/i', $red)) {
  1338. $col = substr($red, 1);
  1339. if (strlen($col) == 3) {
  1340. $red = str_repeat(substr($col, 0, 1), 2);
  1341. $green = str_repeat(substr($col, 1, 1), 2);
  1342. $blue = str_repeat(substr($col, 2, 1), 2);
  1343. } else {
  1344. $red = substr($col, 0, 2);
  1345. $green = substr($col, 2, 2);
  1346. $blue = substr($col, 4, 2);
  1347. }
  1348. $red = hexdec($red);
  1349. $green = hexdec($green);
  1350. $blue = hexdec($blue);
  1351. } else {
  1352. if ($red < 0) $red = 0;
  1353. if ($red > 255) $red = 255;
  1354. if ($green < 0) $green = 0;
  1355. if ($green > 255) $green = 255;
  1356. if ($blue < 0) $blue = 0;
  1357. if ($blue > 255) $blue = 255;
  1358. }
  1359. $this->r = $red;
  1360. $this->g = $green;
  1361. $this->b = $blue;
  1362. }
  1363. }