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