ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
securimage.php
Go to the documentation of this file.
1<?php
2
3// error_reporting(E_ALL); ini_set('display_errors', 1); // uncomment this line for debugging
4
166{
167 // All of the public variables below are securimage options
168 // They can be passed as an array to the Securimage constructor, set below,
169 // or set from securimage_show.php and securimage_play.php
170
175 const SI_IMAGE_JPEG = 1;
180 const SI_IMAGE_PNG = 2;
185 const SI_IMAGE_GIF = 3;
186
202
208 const SI_DRIVER_MYSQL = 'mysql';
209
215 const SI_DRIVER_PGSQL = 'pgsql';
216
222 const SI_DRIVER_SQLITE3 = 'sqlite';
223
224 /*%*********************************************************************%*/
225 // Properties
226
231 public $image_width = 215;
236 public $image_height = 80;
242
247 public $image_bg_color = '#ffffff';
252 public $text_color = '#707070';
257 public $line_color = '#707070';
262 public $noise_color = '#707070';
263
274
279 public $code_length = 6;
284 public $case_sensitive = false;
289 public $charset = 'ABCDEFGHKLMNPRSTUVWYZabcdefghklmnprstuvwyz23456789';
294 public $expiry_time = 900;
295
301 public $session_name = null;
302
307 public $use_wordlist = false;
308
313 public $perturbation = 0.85;
318 public $num_lines = 5;
323 public $noise_level = 2;
324
329 public $image_signature = '';
334 public $signature_color = '#707070';
340
348 public $use_sqlite_db = false;
349
358 public $use_database = false;
359
368
376 public $database_host = 'localhost';
377
384 public $database_user = '';
385
392 public $database_pass = '';
393
400 public $database_name = '';
401
409 public $database_table = 'captcha_codes';
410
419
425 public $captcha_type = self::SI_CAPTCHA_STRING; // or self::SI_CAPTCHA_MATHEMATIC;
426
443
448 public $ttf_file;
526 public $audio_gap_min = 0;
533 public $audio_gap_max = 600;
534
539 protected static $_captchaId = null;
540
541 protected $im;
542 protected $tmpimg;
543 protected $bgimg;
544 protected $iscale = 5;
545
546 public $securimage_path = null;
547
553 protected $code;
554
560 protected $code_display;
561
571
577 protected $captcha_code;
578
584 protected $no_exit;
585
591 protected $no_session;
592
598 protected $send_headers;
599
605 protected $pdo_conn;
606
607 // gd color resources that are allocated for drawing the image
608 protected $gdbgcolor;
609 protected $gdtextcolor;
610 protected $gdlinecolor;
612
629 public function __construct($options = array())
630 {
631 $this->securimage_path = dirname(__FILE__);
632
633 if (is_array($options) && sizeof($options) > 0) {
634 foreach ($options as $prop => $val) {
635 if ($prop == 'captchaId') {
637 $this->use_database = true;
638 } elseif ($prop == 'use_sqlite_db') {
639 trigger_error("The use_sqlite_db option is deprecated, use 'use_database' instead", E_USER_NOTICE);
640 } else {
641 $this->$prop = $val;
642 }
643 }
644 }
645
646 $this->image_bg_color = $this->initColor($this->image_bg_color, '#ffffff');
647 $this->text_color = $this->initColor($this->text_color, '#616161');
648 $this->line_color = $this->initColor($this->line_color, '#616161');
649 $this->noise_color = $this->initColor($this->noise_color, '#616161');
650 $this->signature_color = $this->initColor($this->signature_color, '#616161');
651
652 if (is_null($this->ttf_file)) {
653 $this->ttf_file = $this->securimage_path . '/AHGBold.ttf';
654 }
655
656 $this->signature_font = $this->ttf_file;
657
658 if (is_null($this->wordlist_file)) {
659 $this->wordlist_file = $this->securimage_path . '/words/words.txt';
660 }
661
662 if (is_null($this->database_file)) {
663 $this->database_file = $this->securimage_path . '/database/securimage.sq3';
664 }
665
666 if (is_null($this->audio_path)) {
667 $this->audio_path = $this->securimage_path . '/audio/en/';
668 }
669
670 if (is_null($this->audio_noise_path)) {
671 $this->audio_noise_path = $this->securimage_path . '/audio/noise/';
672 }
673
674 if (is_null($this->audio_use_noise)) {
675 $this->audio_use_noise = true;
676 }
677
678 if (is_null($this->degrade_audio)) {
679 $this->degrade_audio = true;
680 }
681
682 if (is_null($this->code_length) || (int) $this->code_length < 1) {
683 $this->code_length = 6;
684 }
685
686 if (is_null($this->perturbation) || !is_numeric($this->perturbation)) {
687 $this->perturbation = 0.75;
688 }
689
690 if (is_null($this->namespace) || !is_string($this->namespace)) {
691 $this->namespace = 'default';
692 }
693
694 if (is_null($this->no_exit)) {
695 $this->no_exit = false;
696 }
697
698 if (is_null($this->no_session)) {
699 $this->no_session = false;
700 }
701
702 if (is_null($this->send_headers)) {
703 $this->send_headers = true;
704 }
705
706 if ($this->no_session != true) {
707 // Initialize session or attach to existing
708 if (session_id() == '') { // no session has been started yet, which is needed for validation
709 if (!is_null($this->session_name) && trim($this->session_name) != '') {
710 session_name(trim($this->session_name)); // set session name if provided
711 }
712 session_start();
713 }
714 }
715 }
716
721 public static function getPath()
722 {
723 return dirname(__FILE__);
724 }
725
735 public static function getCaptchaId($new = true, array $options = array())
736 {
737 if (is_null($new) || (bool) $new == true) {
738 $id = sha1(uniqid($_SERVER['REMOTE_ADDR'], true));
739 $opts = array('no_session' => true,
740 'use_database' => true);
741 if (sizeof($options) > 0) {
742 $opts = array_merge($options, $opts);
743 }
744 $si = new self($opts);
746 $si->createCode();
747
748 return $id;
749 } else {
751 }
752 }
753
765 public static function checkByCaptchaId($id, $value, array $options = array())
766 {
767 $opts = array('captchaId' => $id,
768 'no_session' => true,
769 'use_database' => true);
770
771 if (sizeof($options) > 0) {
772 $opts = array_merge($options, $opts);
773 }
774
775 $si = new self($opts);
776
777 if ($si->openDatabase()) {
778 $code = $si->getCodeFromDatabase();
779
780 if (is_array($code)) {
781 $si->code = $code['code'];
782 $si->code_display = $code['code_disp'];
783 }
784
785 if ($si->check($value)) {
786 $si->clearCodeFromDatabase();
787
788 return true;
789 } else {
790 return false;
791 }
792 } else {
793 return false;
794 }
795 }
796
797
811 public function show($background_image = '')
812 {
813 set_error_handler(array(&$this, 'errorHandler'));
814
815 if ($background_image != '' && is_readable($background_image)) {
816 $this->bgimg = $background_image;
817 }
818
819 $this->doImage();
820 }
821
835 public function check($code)
836 {
837 $this->code_entered = $code;
838 $this->validate();
839 return $this->correct_code;
840 }
841
851 public function outputAudioFile()
852 {
853 set_error_handler(array(&$this, 'errorHandler'));
854
855 require_once dirname(__FILE__) . '/WavFile.php';
856
857 try {
858 $audio = $this->getAudibleCode();
859 } catch (Exception $ex) {
860 if (($fp = @fopen(dirname(__FILE__) . '/si.error_log', 'a+')) !== false) {
861 fwrite($fp, date('Y-m-d H:i:s') . ': Securimage audio error "' . $ex->getMessage() . '"' . "\n");
862 fclose($fp);
863 }
864
865 $audio = $this->audioError();
866 }
867
868 if ($this->canSendHeaders() || $this->send_headers == false) {
869 if ($this->send_headers) {
870 $uniq = md5(uniqid(microtime()));
871 header("Content-Disposition: attachment; filename=\"securimage_audio-{$uniq}.wav\"");
872 header('Cache-Control: no-store, no-cache, must-revalidate');
873 header('Expires: Sun, 1 Jan 2000 12:00:00 GMT');
874 header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
875 header('Content-type: audio/x-wav');
876
877 if (extension_loaded('zlib')) {
878 ini_set('zlib.output_compression', true); // compress output if supported by browser
879 } else {
880 header('Content-Length: ' . strlen($audio));
881 }
882 }
883
884 echo $audio;
885 } else {
886 echo '<hr /><strong>'
887 . 'Failed to generate audio file, content has already been '
888 . 'output.<br />This is most likely due to misconfiguration or '
889 . 'a PHP error was sent to the browser.</strong>';
890 }
891
892 restore_error_handler();
893
894 if (!$this->no_exit) {
895 exit;
896 }
897 }
898
905 public function getCode($array = false, $returnExisting = false)
906 {
907 $code = '';
908 $time = 0;
909 $disp = 'error';
910
911 if ($returnExisting && strlen($this->code) > 0) {
912 if ($array) {
913 return array('code' => $this->code,
914 'display' => $this->code_display,
915 'code_display' => $this->code_display,
916 'time' => 0);
917 } else {
918 return $this->code;
919 }
920 }
921
922 if ($this->no_session != true) {
923 if (isset($_SESSION['securimage_code_value'][$this->namespace]) &&
924 trim($_SESSION['securimage_code_value'][$this->namespace]) != '') {
925 if ($this->isCodeExpired(
926 $_SESSION['securimage_code_ctime'][$this->namespace]
927 ) == false) {
928 $code = $_SESSION['securimage_code_value'][$this->namespace];
929 $time = $_SESSION['securimage_code_ctime'][$this->namespace];
930 $disp = $_SESSION['securimage_code_disp'] [$this->namespace];
931 }
932 }
933 }
934
935 if (empty($code) && $this->use_database) {
936 // no code in session - may mean user has cookies turned off
937 $this->openDatabase();
938 $code = $this->getCodeFromDatabase();
939 } else { /* no code stored in session or sqlite database, validation will fail */
940 }
941
942 if ($array == true) {
943 return array('code' => $code, 'ctime' => $time, 'display' => $disp);
944 } else {
945 return $code;
946 }
947 }
948
952 protected function doImage()
953 {
954 if (($this->use_transparent_text == true || $this->bgimg != '') && function_exists('imagecreatetruecolor')) {
955 $imagecreate = 'imagecreatetruecolor';
956 } else {
957 $imagecreate = 'imagecreate';
958 }
959
960 $this->im = $imagecreate($this->image_width, $this->image_height);
961 $this->tmpimg = $imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale);
962
963 $this->allocateColors();
964 imagepalettecopy($this->tmpimg, $this->im);
965
966 $this->setBackground();
967
968 $code = '';
969
970 if ($this->getCaptchaId(false) !== null) {
971 // a captcha Id was supplied
972
973 // check to see if a display_value for the captcha image was set
974 if (is_string($this->display_value) && strlen($this->display_value) > 0) {
975 $this->code_display = $this->display_value;
976 $this->code = ($this->case_sensitive) ?
977 $this->display_value :
978 strtolower($this->display_value);
980 } elseif ($this->openDatabase()) {
981 // no display_value, check the database for existing captchaId
982 $code = $this->getCodeFromDatabase();
983
984 // got back a result from the database with a valid code for captchaId
985 if (is_array($code)) {
986 $this->code = $code['code'];
987 $this->code_display = $code['code_disp'];
988 $code = $code['code'];
989 }
990 }
991 }
992
993 if ($code == '') {
994 // if the code was not set using display_value or was not found in
995 // the database, create a new code
996 $this->createCode();
997 }
998
999 if ($this->noise_level > 0) {
1000 $this->drawNoise();
1001 }
1002
1003 $this->drawWord();
1004
1005 if ($this->perturbation > 0 && is_readable($this->ttf_file)) {
1006 $this->distortedCopy();
1007 }
1008
1009 if ($this->num_lines > 0) {
1010 $this->drawLines();
1011 }
1012
1013 if (trim($this->image_signature) != '') {
1014 $this->addSignature();
1015 }
1016
1017 $this->output();
1018 }
1019
1023 protected function allocateColors()
1024 {
1025 // allocate bg color first for imagecreate
1026 $this->gdbgcolor = imagecolorallocate(
1027 $this->im,
1028 $this->image_bg_color->r,
1029 $this->image_bg_color->g,
1030 $this->image_bg_color->b
1031 );
1032
1033 $alpha = intval($this->text_transparency_percentage / 100 * 127);
1034
1035 if ($this->use_transparent_text == true) {
1036 $this->gdtextcolor = imagecolorallocatealpha(
1037 $this->im,
1038 $this->text_color->r,
1039 $this->text_color->g,
1040 $this->text_color->b,
1041 $alpha
1042 );
1043 $this->gdlinecolor = imagecolorallocatealpha(
1044 $this->im,
1045 $this->line_color->r,
1046 $this->line_color->g,
1047 $this->line_color->b,
1048 $alpha
1049 );
1050 $this->gdnoisecolor = imagecolorallocatealpha(
1051 $this->im,
1052 $this->noise_color->r,
1053 $this->noise_color->g,
1054 $this->noise_color->b,
1055 $alpha
1056 );
1057 } else {
1058 $this->gdtextcolor = imagecolorallocate(
1059 $this->im,
1060 $this->text_color->r,
1061 $this->text_color->g,
1062 $this->text_color->b
1063 );
1064 $this->gdlinecolor = imagecolorallocate(
1065 $this->im,
1066 $this->line_color->r,
1067 $this->line_color->g,
1068 $this->line_color->b
1069 );
1070 $this->gdnoisecolor = imagecolorallocate(
1071 $this->im,
1072 $this->noise_color->r,
1073 $this->noise_color->g,
1074 $this->noise_color->b
1075 );
1076 }
1077
1078 $this->gdsignaturecolor = imagecolorallocate(
1079 $this->im,
1080 $this->signature_color->r,
1081 $this->signature_color->g,
1082 $this->signature_color->b
1083 );
1084 }
1085
1089 protected function setBackground()
1090 {
1091 // set background color of image by drawing a rectangle since imagecreatetruecolor doesn't set a bg color
1092 imagefilledrectangle(
1093 $this->im,
1094 0,
1095 0,
1096 $this->image_width,
1097 $this->image_height,
1098 $this->gdbgcolor
1099 );
1100 imagefilledrectangle(
1101 $this->tmpimg,
1102 0,
1103 0,
1104 $this->image_width * $this->iscale,
1105 $this->image_height * $this->iscale,
1106 $this->gdbgcolor
1107 );
1108
1109 if ($this->bgimg == '') {
1110 if ($this->background_directory != null &&
1111 is_dir($this->background_directory) &&
1112 is_readable($this->background_directory)) {
1114 if ($img != false) {
1115 $this->bgimg = $img;
1116 }
1117 }
1118 }
1119
1120 if ($this->bgimg == '') {
1121 return;
1122 }
1123
1124 $dat = @getimagesize($this->bgimg);
1125 if ($dat == false) {
1126 return;
1127 }
1128
1129 switch ($dat[2]) {
1130 case 1: $newim = @imagecreatefromgif($this->bgimg); break;
1131 case 2: $newim = @imagecreatefromjpeg($this->bgimg); break;
1132 case 3: $newim = @imagecreatefrompng($this->bgimg); break;
1133 default: return;
1134 }
1135
1136 if (!$newim) {
1137 return;
1138 }
1139
1140 imagecopyresized(
1141 $this->im,
1142 $newim,
1143 0,
1144 0,
1145 0,
1146 0,
1147 $this->image_width,
1148 $this->image_height,
1149 imagesx($newim),
1150 imagesy($newim)
1151 );
1152 }
1153
1157 protected function getBackgroundFromDirectory()
1158 {
1159 $images = array();
1160
1161 if (($dh = opendir($this->background_directory)) !== false) {
1162 while (($file = readdir($dh)) !== false) {
1163 if (preg_match('/(jpg|gif|png)$/i', $file)) {
1164 $images[] = $file;
1165 }
1166 }
1167
1168 closedir($dh);
1169
1170 if (sizeof($images) > 0) {
1171 return rtrim($this->background_directory, '/') . '/' . $images[mt_rand(0, sizeof($images) - 1)];
1172 }
1173 }
1174
1175 return false;
1176 }
1177
1181 public function createCode()
1182 {
1183 $this->code = false;
1184
1185 switch ($this->captcha_type) {
1187 {
1188 do {
1189 $signs = array('+', '-', 'x');
1190 $left = mt_rand(1, 10);
1191 $right = mt_rand(1, 5);
1192 $sign = $signs[mt_rand(0, 2)];
1193
1194 switch ($sign) {
1195 case 'x': $c = $left * $right; break;
1196 case '-': $c = $left - $right; break;
1197 default: $c = $left + $right; break;
1198 }
1199 } while ($c <= 0); // no negative #'s or 0
1200
1201 $this->code = $c;
1202 $this->code_display = "$left $sign $right";
1203 break;
1204 }
1205
1207 $words = $this->readCodeFromFile(2);
1208 $this->code = implode(' ', $words);
1209 $this->code_display = $this->code;
1210 break;
1211
1212 default:
1213 {
1214 if ($this->use_wordlist && is_readable($this->wordlist_file)) {
1215 $this->code = $this->readCodeFromFile();
1216 }
1217
1218 if ($this->code == false) {
1219 $this->code = $this->generateCode($this->code_length);
1220 }
1221
1222 $this->code_display = $this->code;
1223 $this->code = ($this->case_sensitive) ? $this->code : strtolower($this->code);
1224 } // default
1225 }
1226
1227 $this->saveData();
1228 }
1229
1233 protected function drawWord()
1234 {
1235 $width2 = $this->image_width * $this->iscale;
1236 $height2 = $this->image_height * $this->iscale;
1237
1238 if (!is_readable($this->ttf_file)) {
1239 imagestring($this->im, 4, 10, ($this->image_height / 2) - 5, 'Failed to load TTF font file!', $this->gdtextcolor);
1240 } else {
1241 if ($this->perturbation > 0) {
1242 $font_size = $height2 * .4;
1243 $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
1244 $tx = $bb[4] - $bb[0];
1245 $ty = $bb[5] - $bb[1];
1246 $x = floor($width2 / 2 - $tx / 2 - $bb[0]);
1247 $y = round($height2 / 2 - $ty / 2 - $bb[1]);
1248
1249 imagettftext($this->tmpimg, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
1250 } else {
1251 $font_size = $this->image_height * .4;
1252 $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
1253 $tx = $bb[4] - $bb[0];
1254 $ty = $bb[5] - $bb[1];
1255 $x = floor($this->image_width / 2 - $tx / 2 - $bb[0]);
1256 $y = round($this->image_height / 2 - $ty / 2 - $bb[1]);
1257
1258 imagettftext($this->im, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
1259 }
1260 }
1261
1262 // DEBUG
1263 //$this->im = $this->tmpimg;
1264 //$this->output();
1265 }
1266
1270 protected function distortedCopy()
1271 {
1272 $numpoles = 3; // distortion factor
1273 // make array of poles AKA attractor points
1274 for ($i = 0; $i < $numpoles; ++$i) {
1275 $px[$i] = mt_rand($this->image_width * 0.2, $this->image_width * 0.8);
1276 $py[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8);
1277 $rad[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8);
1278 $tmp = ((-$this->frand()) * 0.15) - .15;
1279 $amp[$i] = $this->perturbation * $tmp;
1280 }
1281
1282 $bgCol = imagecolorat($this->tmpimg, 0, 0);
1283 $width2 = $this->iscale * $this->image_width;
1284 $height2 = $this->iscale * $this->image_height;
1285 imagepalettecopy($this->im, $this->tmpimg); // copy palette to final image so text colors come across
1286 // loop over $img pixels, take pixels from $tmpimg with distortion field
1287 for ($ix = 0; $ix < $this->image_width; ++$ix) {
1288 for ($iy = 0; $iy < $this->image_height; ++$iy) {
1289 $x = $ix;
1290 $y = $iy;
1291 for ($i = 0; $i < $numpoles; ++$i) {
1292 $dx = $ix - $px[$i];
1293 $dy = $iy - $py[$i];
1294 if ($dx == 0 && $dy == 0) {
1295 continue;
1296 }
1297 $r = sqrt($dx * $dx + $dy * $dy);
1298 if ($r > $rad[$i]) {
1299 continue;
1300 }
1301 $rscale = $amp[$i] * sin(3.14 * $r / $rad[$i]);
1302 $x += $dx * $rscale;
1303 $y += $dy * $rscale;
1304 }
1305 $c = $bgCol;
1306 $x *= $this->iscale;
1307 $y *= $this->iscale;
1308 if ($x >= 0 && $x < $width2 && $y >= 0 && $y < $height2) {
1309 $c = imagecolorat($this->tmpimg, $x, $y);
1310 }
1311 if ($c != $bgCol) { // only copy pixels of letters to preserve any background image
1312 imagesetpixel($this->im, $ix, $iy, $c);
1313 }
1314 }
1315 }
1316 }
1317
1321 protected function drawLines()
1322 {
1323 for ($line = 0; $line < $this->num_lines; ++$line) {
1324 $x = $this->image_width * (1 + $line) / ($this->num_lines + 1);
1325 $x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines;
1326 $y = mt_rand($this->image_height * 0.1, $this->image_height * 0.9);
1327
1328 $theta = ($this->frand() - 0.5) * M_PI * 0.7;
1330 $len = mt_rand($w * 0.4, $w * 0.7);
1331 $lwid = mt_rand(0, 2);
1332
1333 $k = $this->frand() * 0.6 + 0.2;
1334 $k = $k * $k * 0.5;
1335 $phi = $this->frand() * 6.28;
1336 $step = 0.5;
1337 $dx = $step * cos($theta);
1338 $dy = $step * sin($theta);
1339 $n = $len / $step;
1340 $amp = 1.5 * $this->frand() / ($k + 5.0 / $len);
1341 $x0 = $x - 0.5 * $len * cos($theta);
1342 $y0 = $y - 0.5 * $len * sin($theta);
1343
1344 $ldx = round(-$dy * $lwid);
1345 $ldy = round($dx * $lwid);
1346
1347 for ($i = 0; $i < $n; ++$i) {
1348 $x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi);
1349 $y = $y0 + $i * $dy - $amp * $dx * sin($k * $i * $step + $phi);
1350 imagefilledrectangle($this->im, $x, $y, $x + $lwid, $y + $lwid, $this->gdlinecolor);
1351 }
1352 }
1353 }
1354
1358 protected function drawNoise()
1359 {
1360 if ($this->noise_level > 10) {
1361 $noise_level = 10;
1362 } else {
1364 }
1365
1366 $t0 = microtime(true);
1367
1368 $noise_level *= 125; // an arbitrary number that works well on a 1-10 scale
1369
1370 $points = $this->image_width * $this->image_height * $this->iscale;
1371 $height = $this->image_height * $this->iscale;
1372 $width = $this->image_width * $this->iscale;
1373 for ($i = 0; $i < $noise_level; ++$i) {
1374 $x = mt_rand(10, $width);
1375 $y = mt_rand(10, $height);
1376 $size = mt_rand(7, 10);
1377 if ($x - $size <= 0 && $y - $size <= 0) {
1378 continue;
1379 } // dont cover 0,0 since it is used by imagedistortedcopy
1380 imagefilledarc($this->tmpimg, $x, $y, $size, $size, 0, 360, $this->gdnoisecolor, IMG_ARC_PIE);
1381 }
1382
1383 $t1 = microtime(true);
1384
1385 $t = $t1 - $t0;
1386
1387 /*
1388 // DEBUG
1389 imagestring($this->tmpimg, 5, 25, 30, "$t", $this->gdnoisecolor);
1390 header('content-type: image/png');
1391 imagepng($this->tmpimg);
1392 exit;
1393 */
1394 }
1395
1399 protected function addSignature()
1400 {
1401 $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
1402 $textlen = $bbox[2] - $bbox[0];
1403 $x = $this->image_width - $textlen - 5;
1404 $y = $this->image_height - 3;
1405
1406 imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature);
1407 }
1408
1412 protected function output()
1413 {
1414 if ($this->canSendHeaders() || $this->send_headers == false) {
1415 if ($this->send_headers) {
1416 // only send the content-type headers if no headers have been output
1417 // this will ease debugging on misconfigured servers where warnings
1418 // may have been output which break the image and prevent easily viewing
1419 // source to see the error.
1420 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
1421 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
1422 header("Cache-Control: no-store, no-cache, must-revalidate");
1423 header("Cache-Control: post-check=0, pre-check=0", false);
1424 header("Pragma: no-cache");
1425 }
1426
1427 switch ($this->image_type) {
1429 if ($this->send_headers) {
1430 header("Content-Type: image/jpeg");
1431 }
1432 imagejpeg($this->im, null, 90);
1433 break;
1434 case self::SI_IMAGE_GIF:
1435 if ($this->send_headers) {
1436 header("Content-Type: image/gif");
1437 }
1438 imagegif($this->im);
1439 break;
1440 default:
1441 if ($this->send_headers) {
1442 header("Content-Type: image/png");
1443 }
1444 imagepng($this->im);
1445 break;
1446 }
1447 } else {
1448 echo '<hr /><strong>'
1449 . 'Failed to generate captcha image, content has already been '
1450 . 'output.<br />This is most likely due to misconfiguration or '
1451 . 'a PHP error was sent to the browser.</strong>';
1452 }
1453
1454 imagedestroy($this->im);
1455 restore_error_handler();
1456
1457 if (!$this->no_exit) {
1458 exit;
1459 }
1460 }
1461
1467 protected function getAudibleCode()
1468 {
1469 $letters = array();
1470 $code = $this->getCode(true, true);
1471
1472 if ($code['code'] == '') {
1473 if (strlen($this->display_value) > 0) {
1474 $code = array('code' => $this->display_value, 'display' => $this->display_value);
1475 } else {
1476 $this->createCode();
1477 $code = $this->getCode(true);
1478 }
1479 }
1480
1481 if (preg_match('/(\d+) (\+|-|x) (\d+)/i', $code['display'], $eq)) {
1482 $math = true;
1483
1484 $left = $eq[1];
1485 $sign = str_replace(array('+', '-', 'x'), array('plus', 'minus', 'times'), $eq[2]);
1486 $right = $eq[3];
1487
1488 $letters = array($left, $sign, $right);
1489 } else {
1490 $math = false;
1491
1492 $length = strlen($code['display']);
1493
1494 for ($i = 0; $i < $length; ++$i) {
1495 $letter = $code['display']{$i};
1496 $letters[] = $letter;
1497 }
1498 }
1499
1500 try {
1501 return $this->generateWAV($letters);
1502 } catch (Exception $ex) {
1503 throw $ex;
1504 }
1505 }
1506
1510 protected function readCodeFromFile($numWords = 1)
1511 {
1512 $fp = fopen($this->wordlist_file, 'rb');
1513 if (!$fp) {
1514 return false;
1515 }
1516
1517 $fsize = filesize($this->wordlist_file);
1518 if ($fsize < 128) {
1519 return false;
1520 } // too small of a list to be effective
1521
1522 if ((int) $numWords < 1 || (int) $numWords > 5) {
1523 $numWords = 1;
1524 }
1525
1526 $words = array();
1527 $i = 0;
1528 do {
1529 fseek($fp, mt_rand(0, $fsize - 64), SEEK_SET); // seek to a random position of file from 0 to filesize-64
1530 $data = fread($fp, 64); // read a chunk from our random position
1531 $data = preg_replace("/\r?\n/", "\n", $data);
1532
1533 $start = @strpos($data, "\n", mt_rand(0, 56)) + 1; // random start position
1534 $end = @strpos($data, "\n", $start); // find end of word
1535
1536 if ($start === false) {
1537 // picked start position at end of file
1538 continue;
1539 } elseif ($end === false) {
1540 $end = strlen($data);
1541 }
1542
1543 $word = strtolower(substr($data, $start, $end - $start)); // return a line of the file
1544 $words[] = $word;
1545 } while (++$i < $numWords);
1546
1547 fclose($fp);
1548
1549 if ($numWords < 2) {
1550 return $words[0];
1551 } else {
1552 return $words;
1553 }
1554 }
1555
1559 protected function generateCode()
1560 {
1561 $code = '';
1562
1563 if (function_exists('mb_strlen')) {
1564 for ($i = 1, $cslen = mb_strlen($this->charset); $i <= $this->code_length; ++$i) {
1565 $code .= mb_substr($this->charset, mt_rand(0, $cslen - 1), 1, 'UTF-8');
1566 }
1567 } else {
1568 for ($i = 1, $cslen = strlen($this->charset); $i <= $this->code_length; ++$i) {
1569 $code .= substr($this->charset, mt_rand(0, $cslen - 1), 1);
1570 }
1571 }
1572
1573 return $code;
1574 }
1575
1580 protected function validate()
1581 {
1582 if (!is_string($this->code) || strlen($this->code) == 0) {
1583 $code = $this->getCode();
1584 // returns stored code, or an empty string if no stored code was found
1585 // checks the session and database if enabled
1586 } else {
1588 }
1589
1590 if ($this->case_sensitive == false && preg_match('/[A-Z]/', $code)) {
1591 // case sensitive was set from securimage_show.php but not in class
1592 // the code saved in the session has capitals so set case sensitive to true
1593 $this->case_sensitive = true;
1594 }
1595
1596 $code_entered = trim(
1597 (($this->case_sensitive) ? $this->code_entered
1598 : strtolower($this->code_entered))
1599 );
1600 $this->correct_code = false;
1601
1602 if ($code != '') {
1603 if (strpos($code, ' ') !== false) {
1604 // for multi word captchas, remove more than once space from input
1605 $code_entered = preg_replace('/\s+/', ' ', $code_entered);
1606 $code_entered = strtolower($code_entered);
1607 }
1608
1609 if ($code == $code_entered) {
1610 $this->correct_code = true;
1611 if ($this->no_session != true) {
1612 $_SESSION['securimage_code_value'][$this->namespace] = '';
1613 $_SESSION['securimage_code_ctime'][$this->namespace] = '';
1614 }
1615 $this->clearCodeFromDatabase();
1616 }
1617 }
1618 }
1619
1623 protected function saveData()
1624 {
1625 if ($this->no_session != true) {
1626 if (isset($_SESSION['securimage_code_value']) && is_scalar($_SESSION['securimage_code_value'])) {
1627 // fix for migration from v2 - v3
1628 unset($_SESSION['securimage_code_value']);
1629 unset($_SESSION['securimage_code_ctime']);
1630 }
1631
1632 $_SESSION['securimage_code_disp'] [$this->namespace] = $this->code_display;
1633 $_SESSION['securimage_code_value'][$this->namespace] = $this->code;
1634 $_SESSION['securimage_code_ctime'][$this->namespace] = time();
1635 }
1636
1637 if ($this->use_database) {
1638 $this->saveCodeToDatabase();
1639 }
1640 }
1641
1645 protected function saveCodeToDatabase()
1646 {
1647 $success = false;
1648 $this->openDatabase();
1649
1650 if ($this->use_database && $this->pdo_conn) {
1651 $id = $this->getCaptchaId(false);
1652 $ip = $_SERVER['REMOTE_ADDR'];
1653
1654 if (empty($id)) {
1655 $id = $ip;
1656 }
1657
1658 $time = time();
1660 $code_disp = $this->code_display;
1661
1662 // This is somewhat expensive in PDO Sqlite3 (when there is something to delete)
1663 $this->clearCodeFromDatabase();
1664
1665 $query = "INSERT INTO {$this->database_table} ("
1666 . "id, code, code_display, namespace, created) "
1667 . "VALUES(?, ?, ?, ?, ?)";
1668
1669 $stmt = $this->pdo_conn->prepare($query);
1670 $success = $stmt->execute(array($id, $code, $code_disp, $this->namespace, $time));
1671
1672 if (!$success) {
1673 $err = $stmt->errorInfo();
1674 trigger_error("Failed to insert code into database. {$err[1]}: {$err[2]}", E_USER_WARNING);
1675 }
1676 }
1677
1678 return $success !== false;
1679 }
1680
1684 protected function openDatabase()
1685 {
1686 $this->pdo_conn = false;
1687
1688 if ($this->use_database) {
1689 $pdo_extension = 'PDO_' . strtoupper($this->database_driver);
1690
1691 if (!extension_loaded($pdo_extension)) {
1692 trigger_error("Database support is turned on in Securimage, but the chosen extension $pdo_extension is not loaded in PHP.", E_USER_WARNING);
1693 return false;
1694 }
1695 }
1696
1697 if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
1698 if (!file_exists($this->database_file)) {
1699 $fp = fopen($this->database_file, 'w+');
1700 if (!$fp) {
1701 $err = error_get_last();
1702 trigger_error("Securimage failed to create SQLite3 database file '{$this->database_file}'. Reason: {$err['message']}", E_USER_WARNING);
1703 return false;
1704 }
1705 fclose($fp);
1706 chmod($this->database_file, 0666);
1707 } elseif (!is_writeable($this->database_file)) {
1708 trigger_error("Securimage does not have read/write access to database file '{$this->database_file}. Make sure permissions are 0666 and writeable by user '" . get_current_user() . "'", E_USER_WARNING);
1709 return false;
1710 }
1711 }
1712
1713 $dsn = $this->getDsn();
1714
1715 try {
1716 $options = array();
1717 $this->pdo_conn = new PDO($dsn, $this->database_user, $this->database_pass, $options);
1718 } catch (PDOException $pdoex) {
1719 trigger_error("Database connection failed: " . $pdoex->getMessage(), E_USER_WARNING);
1720 return false;
1721 }
1722
1723 try {
1724 if (!$this->checkTablesExist()) {
1725 // create tables...
1726 $this->createDatabaseTables();
1727 }
1728 } catch (Exception $ex) {
1729 trigger_error($ex->getMessage(), E_USER_WARNING);
1730 $this->pdo_conn = null;
1731 return false;
1732 }
1733
1734 if (mt_rand(0, 100) / 100.0 == 1.0) {
1736 }
1737
1738 return $this->pdo_conn;
1739 }
1740
1741 protected function getDsn()
1742 {
1743 $dsn = sprintf('%s:', $this->database_driver);
1744
1745 switch ($this->database_driver) {
1748 break;
1749
1752 $dsn .= sprintf(
1753 'host=%s;dbname=%s',
1754 $this->database_host,
1755 $this->database_name
1756 );
1757 break;
1758
1759 }
1760
1761 return $dsn;
1762 }
1763
1764 protected function checkTablesExist()
1765 {
1766 $table = $this->pdo_conn->quote($this->database_table);
1767
1768 switch ($this->database_driver) {
1770 // query row count for sqlite, PRAGMA queries seem to return no
1771 // rowCount using PDO even if there are rows returned
1772 $query = "SELECT COUNT(id) FROM $table";
1773 break;
1774
1776 $query = "SHOW TABLES LIKE $table";
1777 break;
1778
1780 $query = "SELECT * FROM information_schema.columns WHERE table_name = $table;";
1781 break;
1782 }
1783
1784 $result = $this->pdo_conn->query($query);
1785
1786 if (!$result) {
1787 $err = $this->pdo_conn->errorInfo();
1788
1789 if ($this->database_driver == self::SI_DRIVER_SQLITE3 &&
1790 $err[1] === 1 && strpos($err[2], 'no such table') !== false) {
1791 return false;
1792 }
1793
1794 throw new Exception("Failed to check tables: {$err[0]} - {$err[1]}: {$err[2]}");
1795 } elseif ($this->database_driver == self::SI_DRIVER_SQLITE3) {
1796 // successful here regardless of row count for sqlite
1797 return true;
1798 } elseif ($result->rowCount() == 0) {
1799 return false;
1800 } else {
1801 return true;
1802 }
1803 }
1804
1805 protected function createDatabaseTables()
1806 {
1807 $queries = array();
1808
1809 switch ($this->database_driver) {
1811 $queries[] = "CREATE TABLE \"{$this->database_table}\" (
1812 id VARCHAR(40),
1813 namespace VARCHAR(32) NOT NULL,
1814 code VARCHAR(32) NOT NULL,
1815 code_display VARCHAR(32) NOT NULL,
1816 created INTEGER NOT NULL,
1817 PRIMARY KEY(id, namespace)
1818 )";
1819
1820 $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created)";
1821 break;
1822
1824 $queries[] = "CREATE TABLE `{$this->database_table}` (
1825 `id` VARCHAR(40) NOT NULL,
1826 `namespace` VARCHAR(32) NOT NULL,
1827 `code` VARCHAR(32) NOT NULL,
1828 `code_display` VARCHAR(32) NOT NULL,
1829 `created` INT NOT NULL,
1830 PRIMARY KEY(id, namespace),
1831 INDEX(created)
1832 )";
1833 break;
1834
1836 $queries[] = "CREATE TABLE {$this->database_table} (
1837 id character varying(40) NOT NULL,
1838 namespace character varying(32) NOT NULL,
1839 code character varying(32) NOT NULL,
1840 code_display character varying(32) NOT NULL,
1841 created integer NOT NULL,
1842 CONSTRAINT pkey_id_namespace PRIMARY KEY (id, namespace)
1843 )";
1844
1845 $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created);";
1846 break;
1847 }
1848
1849 $this->pdo_conn->beginTransaction();
1850
1851 foreach ($queries as $query) {
1852 $result = $this->pdo_conn->query($query);
1853
1854 if (!$result) {
1855 $err = $this->pdo_conn->errorInfo();
1856 trigger_error("Failed to create table. {$err[1]}: {$err[2]}", E_USER_WARNING);
1857 $this->pdo_conn->rollBack();
1858 $this->pdo_conn = false;
1859 return false;
1860 }
1861 }
1862
1863 $this->pdo_conn->commit();
1864
1865 return true;
1866 }
1867
1875 protected function getCodeFromDatabase()
1876 {
1877 $code = '';
1878
1879 if ($this->use_database == true && $this->pdo_conn) {
1880 if (Securimage::$_captchaId !== null) {
1881 $query = "SELECT * FROM {$this->database_table} WHERE id = ?";
1882 $stmt = $this->pdo_conn->prepare($query);
1883 $result = $stmt->execute(array(Securimage::$_captchaId));
1884 } else {
1885 $ip = $_SERVER['REMOTE_ADDR'];
1886 $ns = $this->namespace;
1887
1888 // ip is stored in id column when no captchaId
1889 $query = "SELECT * FROM {$this->database_table} WHERE id = ? AND namespace = ?";
1890 $stmt = $this->pdo_conn->prepare($query);
1891 $result = $stmt->execute(array($ip, $ns));
1892 }
1893
1894 if (!$result) {
1895 $err = $this->pdo_conn->errorInfo();
1896 trigger_error("Failed to select code from database. {$err[0]}: {$err[1]}", E_USER_WARNING);
1897 } else {
1898 if (($row = $stmt->fetch()) !== false) {
1899 if (false == $this->isCodeExpired($row['created'])) {
1900 if (Securimage::$_captchaId !== null) {
1901 // return an array when using captchaId
1902 $code = array('code' => $row['code'],
1903 'code_disp' => $row['code_display']);
1904 } else {
1905 $code = $row['code'];
1906 }
1907 }
1908 }
1909 }
1910 }
1911
1912 return $code;
1913 }
1914
1918 protected function clearCodeFromDatabase()
1919 {
1920 if ($this->pdo_conn) {
1921 $ip = $_SERVER['REMOTE_ADDR'];
1922 $ns = $this->pdo_conn->quote($this->namespace);
1924
1925 if (empty($id)) {
1926 $id = $ip; // if no captchaId set, IP address is captchaId.
1927 }
1928
1929 $id = $this->pdo_conn->quote($id);
1930
1931 $query = sprintf(
1932 "DELETE FROM %s WHERE id = %s AND namespace = %s",
1933 $this->database_table,
1934 $id,
1935 $ns
1936 );
1937
1938 $result = $this->pdo_conn->query($query);
1939 if (!$result) {
1940 trigger_error("Failed to delete code from database.", E_USER_WARNING);
1941 }
1942 }
1943 }
1944
1948 protected function purgeOldCodesFromDatabase()
1949 {
1950 if ($this->use_database && $this->pdo_conn) {
1951 $now = time();
1952 $limit = (!is_numeric($this->expiry_time) || $this->expiry_time < 1) ? 86400 : $this->expiry_time;
1953
1954 $query = sprintf(
1955 "DELETE FROM %s WHERE %s - created > %s",
1956 $this->database_table,
1957 $this->pdo_conn->quote($now, PDO::PARAM_INT),
1958 $this->pdo_conn->quote($limit, PDO::PARAM_INT)
1959 );
1960
1961 $result = $this->pdo_conn->query($query);
1962 }
1963 }
1964
1969 protected function isCodeExpired($creation_time)
1970 {
1971 $expired = true;
1972
1973 if (!is_numeric($this->expiry_time) || $this->expiry_time < 1) {
1974 $expired = false;
1975 } elseif (time() - $creation_time < $this->expiry_time) {
1976 $expired = false;
1977 }
1978
1979 return $expired;
1980 }
1981
1988 protected function generateWAV($letters)
1989 {
1990 $wavCaptcha = new WavFile();
1991 $first = true; // reading first wav file
1992
1993 foreach ($letters as $letter) {
1994 $letter = strtoupper($letter);
1995
1996 try {
1997 $l = new WavFile($this->audio_path . '/' . $letter . '.wav');
1998
1999 if ($first) {
2000 // set sample rate, bits/sample, and # of channels for file based on first letter
2001 $wavCaptcha->setSampleRate($l->getSampleRate())
2002 ->setBitsPerSample($l->getBitsPerSample())
2003 ->setNumChannels($l->getNumChannels());
2004 $first = false;
2005 }
2006
2007 // append letter to the captcha audio
2008 $wavCaptcha->appendWav($l);
2009
2010 // random length of silence between $audio_gap_min and $audio_gap_max
2011 if ($this->audio_gap_max > 0 && $this->audio_gap_max > $this->audio_gap_min) {
2012 $wavCaptcha->insertSilence(mt_rand($this->audio_gap_min, $this->audio_gap_max) / 1000.0);
2013 }
2014 } catch (Exception $ex) {
2015 // failed to open file, or the wav file is broken or not supported
2016 // 2 wav files were not compatible, different # channels, bits/sample, or sample rate
2017 throw $ex;
2018 }
2019 }
2020
2021 /********* Set up audio filters *****************************/
2022 $filters = array();
2023
2024 if ($this->audio_use_noise == true) {
2025 // use background audio - find random file
2026 $noiseFile = $this->getRandomNoiseFile();
2027
2028 if ($noiseFile !== false && is_readable($noiseFile)) {
2029 try {
2030 $wavNoise = new WavFile($noiseFile, false);
2031 } catch (Exception $ex) {
2032 throw $ex;
2033 }
2034
2035 // start at a random offset from the beginning of the wavfile
2036 // in order to add more randomness
2037 $randOffset = 0;
2038 if ($wavNoise->getNumBlocks() > 2 * $wavCaptcha->getNumBlocks()) {
2039 $randBlock = mt_rand(0, $wavNoise->getNumBlocks() - $wavCaptcha->getNumBlocks());
2040 $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $wavCaptcha->getNumBlocks() * $wavNoise->getBlockAlign());
2041 } else {
2042 $wavNoise->readWavData();
2043 $randOffset = mt_rand(0, $wavNoise->getNumBlocks() - 1);
2044 }
2045
2046
2047 $mixOpts = array('wav' => $wavNoise,
2048 'loop' => true,
2049 'blockOffset' => $randOffset);
2050
2051 $filters[WavFile::FILTER_MIX] = $mixOpts;
2053 }
2054 }
2055
2056 if ($this->degrade_audio == true) {
2057 // add random noise.
2058 // any noise level below 95% is intensely distorted and not pleasant to the ear
2059 $filters[WavFile::FILTER_DEGRADE] = mt_rand(95, 98) / 100.0;
2060 }
2061
2062 if (!empty($filters)) {
2063 $wavCaptcha->filter($filters); // apply filters to captcha audio
2064 }
2065
2066 return $wavCaptcha->__toString();
2067 }
2068
2069 public function getRandomNoiseFile()
2070 {
2071 $return = false;
2072
2073 if (($dh = opendir($this->audio_noise_path)) !== false) {
2074 $list = array();
2075
2076 while (($file = readdir($dh)) !== false) {
2077 if ($file == '.' || $file == '..') {
2078 continue;
2079 }
2080 if (strtolower(substr($file, -4)) != '.wav') {
2081 continue;
2082 }
2083
2084 $list[] = $file;
2085 }
2086
2087 closedir($dh);
2088
2089 if (sizeof($list) > 0) {
2090 $file = $list[array_rand($list, 1)];
2091 $return = $this->audio_noise_path . DIRECTORY_SEPARATOR . $file;
2092 }
2093 }
2094
2095 return $return;
2096 }
2097
2103 protected function audioError()
2104 {
2105 return @file_get_contents(dirname(__FILE__) . '/audio/en/error.wav');
2106 }
2107
2113 protected function canSendHeaders()
2114 {
2115 if (headers_sent()) {
2116 // output has been flushed and headers have already been sent
2117 return false;
2118 } elseif (strlen((string) ob_get_contents()) > 0) {
2119 // headers haven't been sent, but there is data in the buffer that will break image and audio data
2120 return false;
2121 }
2122
2123 return true;
2124 }
2125
2131 public function frand()
2132 {
2133 return 0.0001 * mt_rand(0, 9999);
2134 }
2135
2141 protected function initColor($color, $default)
2142 {
2143 if ($color == null) {
2144 return new Securimage_Color($default);
2145 } elseif (is_string($color)) {
2146 try {
2147 return new Securimage_Color($color);
2148 } catch (Exception $e) {
2149 return new Securimage_Color($default);
2150 }
2151 } elseif (is_array($color) && sizeof($color) == 3) {
2152 return new Securimage_Color($color[0], $color[1], $color[2]);
2153 } else {
2154 return new Securimage_Color($default);
2155 }
2156 }
2157
2173 public function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array())
2174 {
2175 // get the current error reporting level
2176 $level = error_reporting();
2177
2178 // if error was supressed or $errno not set in current error level
2179 if ($level == 0 || ($level & $errno) == 0) {
2180 return true;
2181 }
2182
2183 return false;
2184 }
2185}
2186
2187
2198{
2199 public $r;
2200 public $g;
2201 public $b;
2202
2214 public function __construct($color = '#ffffff')
2215 {
2216 $args = func_get_args();
2217
2218 if (sizeof($args) == 0) {
2219 $this->r = 255;
2220 $this->g = 255;
2221 $this->b = 255;
2222 } elseif (sizeof($args) == 1) {
2223 // set based on html code
2224 if (substr($color, 0, 1) == '#') {
2225 $color = substr($color, 1);
2226 }
2227
2228 if (strlen($color) != 3 && strlen($color) != 6) {
2229 throw new InvalidArgumentException(
2230 'Invalid HTML color code passed to Securimage_Color'
2231 );
2232 }
2233
2234 $this->constructHTML($color);
2235 } elseif (sizeof($args) == 3) {
2236 $this->constructRGB($args[0], $args[1], $args[2]);
2237 } else {
2238 throw new InvalidArgumentException(
2239 'Securimage_Color constructor expects 0, 1 or 3 arguments; ' . sizeof($args) . ' given'
2240 );
2241 }
2242 }
2243
2250 protected function constructRGB($red, $green, $blue)
2251 {
2252 if ($red < 0) {
2253 $red = 0;
2254 }
2255 if ($red > 255) {
2256 $red = 255;
2257 }
2258 if ($green < 0) {
2259 $green = 0;
2260 }
2261 if ($green > 255) {
2262 $green = 255;
2263 }
2264 if ($blue < 0) {
2265 $blue = 0;
2266 }
2267 if ($blue > 255) {
2268 $blue = 255;
2269 }
2270
2271 $this->r = $red;
2272 $this->g = $green;
2273 $this->b = $blue;
2274 }
2275
2280 protected function constructHTML($color)
2281 {
2282 if (strlen($color) == 3) {
2283 $red = str_repeat(substr($color, 0, 1), 2);
2284 $green = str_repeat(substr($color, 1, 1), 2);
2285 $blue = str_repeat(substr($color, 2, 1), 2);
2286 } else {
2287 $red = substr($color, 0, 2);
2288 $green = substr($color, 2, 2);
2289 $blue = substr($color, 4, 2);
2290 }
2291
2292 $this->r = hexdec($red);
2293 $this->g = hexdec($green);
2294 $this->b = hexdec($blue);
2295 }
2296}
$result
$n
Definition: RandomTest.php:85
$size
Definition: RandomTest.php:84
$success
Definition: Utf8Test.php:86
global $l
Definition: afr.php:30
exit
Definition: backend.php:16
$default
Definition: build.php:20
$_SESSION["AccountId"]
An exception for terminatinating execution or to throw for unit testing.
__construct($color='#ffffff')
Create a new Securimage_Color object.
constructRGB($red, $green, $blue)
Construct from an rgb triplet.
constructHTML($color)
Construct from an html hex color code.
errorHandler($errno, $errstr, $errfile='', $errline=0, $errcontext=array())
Error handler used when outputting captcha image or audio.
const SI_CAPTCHA_STRING
Definition: securimage.php:191
const SI_DRIVER_MYSQL
Definition: securimage.php:208
validate()
Checks the entered code against the value stored in the session or sqlite database,...
getCode($array=false, $returnExisting=false)
Return the code from the session or sqlite database if used.
Definition: securimage.php:905
check($code)
Check a submitted code against the stored value.
Definition: securimage.php:835
const SI_DRIVER_SQLITE3
Definition: securimage.php:222
saveData()
Save data to session namespace and database if used.
drawLines()
Draws distorted lines on the image.
static getCaptchaId($new=true, array $options=array())
Generate a new captcha ID or retrieve the current ID.
Definition: securimage.php:735
isCodeExpired($creation_time)
Checks to see if the captcha code has expired and cannot be used.
$audio_mix_normalization
Definition: securimage.php:511
saveCodeToDatabase()
Saves the code to the sqlite database.
output()
Sends the appropriate image and cache headers and outputs image to the browser.
getRandomNoiseFile()
allocateColors()
Allocate the colors to be used for the image.
show($background_image='')
Used to serve a captcha image to the browser.
Definition: securimage.php:811
__construct($options=array())
Create a new securimage object, pass options to set in the constructor.
Definition: securimage.php:629
purgeOldCodesFromDatabase()
Deletes old codes from sqlite database.
static getPath()
Return the absolute path to the Securimage directory.
Definition: securimage.php:721
audioError()
Return a wav file saying there was an error generating file.
outputAudioFile()
Output a wav file of the captcha code to the browser.
Definition: securimage.php:851
$text_transparency_percentage
Definition: securimage.php:268
initColor($color, $default)
Convert an html color code to a Securimage_Color.
const SI_DRIVER_PGSQL
Definition: securimage.php:215
drawWord()
Draws the captcha code on the image.
const SI_IMAGE_JPEG
Definition: securimage.php:175
getCodeFromDatabase()
Get a code from the sqlite database for ip address/captchaId.
createCode()
Generates the code or math problem and saves the value to the session.
clearCodeFromDatabase()
Remove an entered code from the database.
openDatabase()
Open sqlite database.
getAudibleCode()
Gets the code and returns the binary audio file for the stored captcha code.
const SI_IMAGE_GIF
Definition: securimage.php:185
doImage()
The main image drawing routing, responsible for constructing the entire image and serving it.
Definition: securimage.php:952
createDatabaseTables()
distortedCopy()
Copies the captcha image to the final image with distortion applied.
setBackground()
The the background color, or background image to be used.
static $_captchaId
Definition: securimage.php:539
static checkByCaptchaId($id, $value, array $options=array())
Validate a captcha code input against a captcha ID.
Definition: securimage.php:765
getBackgroundFromDirectory()
Scan the directory for a background image to use.
generateWAV($letters)
Generate a wav file given the $letters in the code.
canSendHeaders()
Checks to see if headers can be sent and if any error has been output to the browser.
const SI_CAPTCHA_WORDS
Definition: securimage.php:201
const SI_CAPTCHA_MATHEMATIC
Definition: securimage.php:196
generateCode()
Generates a random captcha code from the set character set.
addSignature()
Print signature text on image.
frand()
Return a random float between 0 and 0.9999.
drawNoise()
Draws random noise on the image.
readCodeFromFile($numWords=1)
Gets a captcha code from a wordlist.
const SI_IMAGE_PNG
Definition: securimage.php:180
const FILTER_DEGRADE
Definition: WavFile.php:81
const FILTER_MIX
Definition: WavFile.php:75
const FILTER_NORMALIZE
Definition: WavFile.php:78
$x
Definition: complexTest.php:9
foreach($_REQUEST as $var) foreach(array('_POST'=> 'HTTP_POST_VARS', '_GET'=> 'HTTP_GET_VARS', '_COOKIE'=> 'HTTP_COOKIE_VARS', '_SERVER'=> 'HTTP_SERVER_VARS', '_ENV'=> 'HTTP_ENV_VARS', '_FILES'=> 'HTTP_POST_FILES') as $array=> $other) $step
Definition: cssgen.php:155
$i
Definition: disco.tpl.php:19
$y
Definition: example_007.php:83
$w
$px
$py
$red
Definition: example_030.php:80
$green
Definition: example_030.php:83
$blue
Definition: example_030.php:81
$r
Definition: example_031.php:79
if(!array_key_exists('StateId', $_REQUEST)) $id
$time
Definition: cron.php:21
foreach($paths as $path) $dsn
Definition: migrateto20.php:56
$row
$stmt
Project: Securimage: A PHP class for creating and managing form CAPTCHA images File: securimage....
$query
if(empty($password)) $table
Definition: pwgen.php:24
if(isset($_REQUEST['delete'])) $list
Definition: registry.php:41
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
$start
Definition: bench.php:8
$data
Definition: bench.php:6