ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
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 
191  const SI_CAPTCHA_STRING = 0;
201  const SI_CAPTCHA_WORDS = 2;
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;
241  public $image_type = self::SI_IMAGE_PNG;
242 
247  public $image_bg_color = '#ffffff';
252  public $text_color = '#707070';
257  public $line_color = '#707070';
262  public $noise_color = '#707070';
263 
273  public $use_transparent_text = true;
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 
367  public $database_driver = self::SI_DRIVER_SQLITE3;
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 
442  public $namespace;
443 
448  public $ttf_file;
472  public $audio_path;
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;
611  protected $gdsignaturecolor;
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  } else if ($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) $opts = array_merge($options, $opts);
742  $si = new self($opts);
744  $si->createCode();
745 
746  return $id;
747  } else {
749  }
750  }
751 
763  public static function checkByCaptchaId($id, $value, array $options = array())
764  {
765  $opts = array('captchaId' => $id,
766  'no_session' => true,
767  'use_database' => true);
768 
769  if (sizeof($options) > 0) $opts = array_merge($options, $opts);
770 
771  $si = new self($opts);
772 
773  if ($si->openDatabase()) {
774  $code = $si->getCodeFromDatabase();
775 
776  if (is_array($code)) {
777  $si->code = $code['code'];
778  $si->code_display = $code['code_disp'];
779  }
780 
781  if ($si->check($value)) {
782  $si->clearCodeFromDatabase();
783 
784  return true;
785  } else {
786  return false;
787  }
788  } else {
789  return false;
790  }
791  }
792 
793 
807  public function show($background_image = '')
808  {
809  set_error_handler(array(&$this, 'errorHandler'));
810 
811  if($background_image != '' && is_readable($background_image)) {
812  $this->bgimg = $background_image;
813  }
814 
815  $this->doImage();
816  }
817 
831  public function check($code)
832  {
833  $this->code_entered = $code;
834  $this->validate();
835  return $this->correct_code;
836  }
837 
847  public function outputAudioFile()
848  {
849  set_error_handler(array(&$this, 'errorHandler'));
850 
851  require_once dirname(__FILE__) . '/WavFile.php';
852 
853  try {
854  $audio = $this->getAudibleCode();
855  } catch (Exception $ex) {
856  if (($fp = @fopen(dirname(__FILE__) . '/si.error_log', 'a+')) !== false) {
857  fwrite($fp, date('Y-m-d H:i:s') . ': Securimage audio error "' . $ex->getMessage() . '"' . "\n");
858  fclose($fp);
859  }
860 
861  $audio = $this->audioError();
862  }
863 
864  if ($this->canSendHeaders() || $this->send_headers == false) {
865  if ($this->send_headers) {
866  $uniq = md5(uniqid(microtime()));
867  header("Content-Disposition: attachment; filename=\"securimage_audio-{$uniq}.wav\"");
868  header('Cache-Control: no-store, no-cache, must-revalidate');
869  header('Expires: Sun, 1 Jan 2000 12:00:00 GMT');
870  header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
871  header('Content-type: audio/x-wav');
872 
873  if (extension_loaded('zlib')) {
874  ini_set('zlib.output_compression', true); // compress output if supported by browser
875  } else {
876  header('Content-Length: ' . strlen($audio));
877  }
878  }
879 
880  echo $audio;
881  } else {
882  echo '<hr /><strong>'
883  .'Failed to generate audio file, content has already been '
884  .'output.<br />This is most likely due to misconfiguration or '
885  .'a PHP error was sent to the browser.</strong>';
886  }
887 
888  restore_error_handler();
889 
890  if (!$this->no_exit) exit;
891  }
892 
899  public function getCode($array = false, $returnExisting = false)
900  {
901  $code = '';
902  $time = 0;
903  $disp = 'error';
904 
905  if ($returnExisting && strlen($this->code) > 0) {
906  if ($array) {
907  return array('code' => $this->code,
908  'display' => $this->code_display,
909  'code_display' => $this->code_display,
910  'time' => 0);
911  } else {
912  return $this->code;
913  }
914  }
915 
916  if ($this->no_session != true) {
917  if (isset($_SESSION['securimage_code_value'][$this->namespace]) &&
918  trim($_SESSION['securimage_code_value'][$this->namespace]) != '') {
919  if ($this->isCodeExpired(
920  $_SESSION['securimage_code_ctime'][$this->namespace]) == false) {
921  $code = $_SESSION['securimage_code_value'][$this->namespace];
922  $time = $_SESSION['securimage_code_ctime'][$this->namespace];
923  $disp = $_SESSION['securimage_code_disp'] [$this->namespace];
924  }
925  }
926  }
927 
928  if (empty($code) && $this->use_database) {
929  // no code in session - may mean user has cookies turned off
930  $this->openDatabase();
931  $code = $this->getCodeFromDatabase();
932  } else { /* no code stored in session or sqlite database, validation will fail */ }
933 
934  if ($array == true) {
935  return array('code' => $code, 'ctime' => $time, 'display' => $disp);
936  } else {
937  return $code;
938  }
939  }
940 
944  protected function doImage()
945  {
946  if( ($this->use_transparent_text == true || $this->bgimg != '') && function_exists('imagecreatetruecolor')) {
947  $imagecreate = 'imagecreatetruecolor';
948  } else {
949  $imagecreate = 'imagecreate';
950  }
951 
952  $this->im = $imagecreate($this->image_width, $this->image_height);
953  $this->tmpimg = $imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale);
954 
955  $this->allocateColors();
956  imagepalettecopy($this->tmpimg, $this->im);
957 
958  $this->setBackground();
959 
960  $code = '';
961 
962  if ($this->getCaptchaId(false) !== null) {
963  // a captcha Id was supplied
964 
965  // check to see if a display_value for the captcha image was set
966  if (is_string($this->display_value) && strlen($this->display_value) > 0) {
967  $this->code_display = $this->display_value;
968  $this->code = ($this->case_sensitive) ?
969  $this->display_value :
970  strtolower($this->display_value);
971  $code = $this->code;
972  } else if ($this->openDatabase()) {
973  // no display_value, check the database for existing captchaId
974  $code = $this->getCodeFromDatabase();
975 
976  // got back a result from the database with a valid code for captchaId
977  if (is_array($code)) {
978  $this->code = $code['code'];
979  $this->code_display = $code['code_disp'];
980  $code = $code['code'];
981  }
982  }
983  }
984 
985  if ($code == '') {
986  // if the code was not set using display_value or was not found in
987  // the database, create a new code
988  $this->createCode();
989  }
990 
991  if ($this->noise_level > 0) {
992  $this->drawNoise();
993  }
994 
995  $this->drawWord();
996 
997  if ($this->perturbation > 0 && is_readable($this->ttf_file)) {
998  $this->distortedCopy();
999  }
1000 
1001  if ($this->num_lines > 0) {
1002  $this->drawLines();
1003  }
1004 
1005  if (trim($this->image_signature) != '') {
1006  $this->addSignature();
1007  }
1008 
1009  $this->output();
1010  }
1011 
1015  protected function allocateColors()
1016  {
1017  // allocate bg color first for imagecreate
1018  $this->gdbgcolor = imagecolorallocate($this->im,
1019  $this->image_bg_color->r,
1020  $this->image_bg_color->g,
1021  $this->image_bg_color->b);
1022 
1023  $alpha = intval($this->text_transparency_percentage / 100 * 127);
1024 
1025  if ($this->use_transparent_text == true) {
1026  $this->gdtextcolor = imagecolorallocatealpha($this->im,
1027  $this->text_color->r,
1028  $this->text_color->g,
1029  $this->text_color->b,
1030  $alpha);
1031  $this->gdlinecolor = imagecolorallocatealpha($this->im,
1032  $this->line_color->r,
1033  $this->line_color->g,
1034  $this->line_color->b,
1035  $alpha);
1036  $this->gdnoisecolor = imagecolorallocatealpha($this->im,
1037  $this->noise_color->r,
1038  $this->noise_color->g,
1039  $this->noise_color->b,
1040  $alpha);
1041  } else {
1042  $this->gdtextcolor = imagecolorallocate($this->im,
1043  $this->text_color->r,
1044  $this->text_color->g,
1045  $this->text_color->b);
1046  $this->gdlinecolor = imagecolorallocate($this->im,
1047  $this->line_color->r,
1048  $this->line_color->g,
1049  $this->line_color->b);
1050  $this->gdnoisecolor = imagecolorallocate($this->im,
1051  $this->noise_color->r,
1052  $this->noise_color->g,
1053  $this->noise_color->b);
1054  }
1055 
1056  $this->gdsignaturecolor = imagecolorallocate($this->im,
1057  $this->signature_color->r,
1058  $this->signature_color->g,
1059  $this->signature_color->b);
1060 
1061  }
1062 
1066  protected function setBackground()
1067  {
1068  // set background color of image by drawing a rectangle since imagecreatetruecolor doesn't set a bg color
1069  imagefilledrectangle($this->im, 0, 0,
1070  $this->image_width, $this->image_height,
1071  $this->gdbgcolor);
1072  imagefilledrectangle($this->tmpimg, 0, 0,
1073  $this->image_width * $this->iscale, $this->image_height * $this->iscale,
1074  $this->gdbgcolor);
1075 
1076  if ($this->bgimg == '') {
1077  if ($this->background_directory != null &&
1078  is_dir($this->background_directory) &&
1079  is_readable($this->background_directory))
1080  {
1081  $img = $this->getBackgroundFromDirectory();
1082  if ($img != false) {
1083  $this->bgimg = $img;
1084  }
1085  }
1086  }
1087 
1088  if ($this->bgimg == '') {
1089  return;
1090  }
1091 
1092  $dat = @getimagesize($this->bgimg);
1093  if($dat == false) {
1094  return;
1095  }
1096 
1097  switch($dat[2]) {
1098  case 1: $newim = @imagecreatefromgif($this->bgimg); break;
1099  case 2: $newim = @imagecreatefromjpeg($this->bgimg); break;
1100  case 3: $newim = @imagecreatefrompng($this->bgimg); break;
1101  default: return;
1102  }
1103 
1104  if(!$newim) return;
1105 
1106  imagecopyresized($this->im, $newim, 0, 0, 0, 0,
1107  $this->image_width, $this->image_height,
1108  imagesx($newim), imagesy($newim));
1109  }
1110 
1114  protected function getBackgroundFromDirectory()
1115  {
1116  $images = array();
1117 
1118  if ( ($dh = opendir($this->background_directory)) !== false) {
1119  while (($file = readdir($dh)) !== false) {
1120  if (preg_match('/(jpg|gif|png)$/i', $file)) $images[] = $file;
1121  }
1122 
1123  closedir($dh);
1124 
1125  if (sizeof($images) > 0) {
1126  return rtrim($this->background_directory, '/') . '/' . $images[mt_rand(0, sizeof($images)-1)];
1127  }
1128  }
1129 
1130  return false;
1131  }
1132 
1136  public function createCode()
1137  {
1138  $this->code = false;
1139 
1140  switch($this->captcha_type) {
1141  case self::SI_CAPTCHA_MATHEMATIC:
1142  {
1143  do {
1144  $signs = array('+', '-', 'x');
1145  $left = mt_rand(1, 10);
1146  $right = mt_rand(1, 5);
1147  $sign = $signs[mt_rand(0, 2)];
1148 
1149  switch($sign) {
1150  case 'x': $c = $left * $right; break;
1151  case '-': $c = $left - $right; break;
1152  default: $c = $left + $right; break;
1153  }
1154  } while ($c <= 0); // no negative #'s or 0
1155 
1156  $this->code = $c;
1157  $this->code_display = "$left $sign $right";
1158  break;
1159  }
1160 
1161  case self::SI_CAPTCHA_WORDS:
1162  $words = $this->readCodeFromFile(2);
1163  $this->code = implode(' ', $words);
1164  $this->code_display = $this->code;
1165  break;
1166 
1167  default:
1168  {
1169  if ($this->use_wordlist && is_readable($this->wordlist_file)) {
1170  $this->code = $this->readCodeFromFile();
1171  }
1172 
1173  if ($this->code == false) {
1174  $this->code = $this->generateCode($this->code_length);
1175  }
1176 
1177  $this->code_display = $this->code;
1178  $this->code = ($this->case_sensitive) ? $this->code : strtolower($this->code);
1179  } // default
1180  }
1181 
1182  $this->saveData();
1183  }
1184 
1188  protected function drawWord()
1189  {
1190  $width2 = $this->image_width * $this->iscale;
1191  $height2 = $this->image_height * $this->iscale;
1192 
1193  if (!is_readable($this->ttf_file)) {
1194  imagestring($this->im, 4, 10, ($this->image_height / 2) - 5, 'Failed to load TTF font file!', $this->gdtextcolor);
1195  } else {
1196  if ($this->perturbation > 0) {
1197  $font_size = $height2 * .4;
1198  $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
1199  $tx = $bb[4] - $bb[0];
1200  $ty = $bb[5] - $bb[1];
1201  $x = floor($width2 / 2 - $tx / 2 - $bb[0]);
1202  $y = round($height2 / 2 - $ty / 2 - $bb[1]);
1203 
1204  imagettftext($this->tmpimg, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
1205  } else {
1206  $font_size = $this->image_height * .4;
1207  $bb = imageftbbox($font_size, 0, $this->ttf_file, $this->code_display);
1208  $tx = $bb[4] - $bb[0];
1209  $ty = $bb[5] - $bb[1];
1210  $x = floor($this->image_width / 2 - $tx / 2 - $bb[0]);
1211  $y = round($this->image_height / 2 - $ty / 2 - $bb[1]);
1212 
1213  imagettftext($this->im, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code_display);
1214  }
1215  }
1216 
1217  // DEBUG
1218  //$this->im = $this->tmpimg;
1219  //$this->output();
1220 
1221  }
1222 
1226  protected function distortedCopy()
1227  {
1228  $numpoles = 3; // distortion factor
1229  // make array of poles AKA attractor points
1230  for ($i = 0; $i < $numpoles; ++ $i) {
1231  $px[$i] = mt_rand($this->image_width * 0.2, $this->image_width * 0.8);
1232  $py[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8);
1233  $rad[$i] = mt_rand($this->image_height * 0.2, $this->image_height * 0.8);
1234  $tmp = ((- $this->frand()) * 0.15) - .15;
1235  $amp[$i] = $this->perturbation * $tmp;
1236  }
1237 
1238  $bgCol = imagecolorat($this->tmpimg, 0, 0);
1239  $width2 = $this->iscale * $this->image_width;
1240  $height2 = $this->iscale * $this->image_height;
1241  imagepalettecopy($this->im, $this->tmpimg); // copy palette to final image so text colors come across
1242  // loop over $img pixels, take pixels from $tmpimg with distortion field
1243  for ($ix = 0; $ix < $this->image_width; ++ $ix) {
1244  for ($iy = 0; $iy < $this->image_height; ++ $iy) {
1245  $x = $ix;
1246  $y = $iy;
1247  for ($i = 0; $i < $numpoles; ++ $i) {
1248  $dx = $ix - $px[$i];
1249  $dy = $iy - $py[$i];
1250  if ($dx == 0 && $dy == 0) {
1251  continue;
1252  }
1253  $r = sqrt($dx * $dx + $dy * $dy);
1254  if ($r > $rad[$i]) {
1255  continue;
1256  }
1257  $rscale = $amp[$i] * sin(3.14 * $r / $rad[$i]);
1258  $x += $dx * $rscale;
1259  $y += $dy * $rscale;
1260  }
1261  $c = $bgCol;
1262  $x *= $this->iscale;
1263  $y *= $this->iscale;
1264  if ($x >= 0 && $x < $width2 && $y >= 0 && $y < $height2) {
1265  $c = imagecolorat($this->tmpimg, $x, $y);
1266  }
1267  if ($c != $bgCol) { // only copy pixels of letters to preserve any background image
1268  imagesetpixel($this->im, $ix, $iy, $c);
1269  }
1270  }
1271  }
1272  }
1273 
1277  protected function drawLines()
1278  {
1279  for ($line = 0; $line < $this->num_lines; ++ $line) {
1280  $x = $this->image_width * (1 + $line) / ($this->num_lines + 1);
1281  $x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines;
1282  $y = mt_rand($this->image_height * 0.1, $this->image_height * 0.9);
1283 
1284  $theta = ($this->frand() - 0.5) * M_PI * 0.7;
1286  $len = mt_rand($w * 0.4, $w * 0.7);
1287  $lwid = mt_rand(0, 2);
1288 
1289  $k = $this->frand() * 0.6 + 0.2;
1290  $k = $k * $k * 0.5;
1291  $phi = $this->frand() * 6.28;
1292  $step = 0.5;
1293  $dx = $step * cos($theta);
1294  $dy = $step * sin($theta);
1295  $n = $len / $step;
1296  $amp = 1.5 * $this->frand() / ($k + 5.0 / $len);
1297  $x0 = $x - 0.5 * $len * cos($theta);
1298  $y0 = $y - 0.5 * $len * sin($theta);
1299 
1300  $ldx = round(- $dy * $lwid);
1301  $ldy = round($dx * $lwid);
1302 
1303  for ($i = 0; $i < $n; ++ $i) {
1304  $x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi);
1305  $y = $y0 + $i * $dy - $amp * $dx * sin($k * $i * $step + $phi);
1306  imagefilledrectangle($this->im, $x, $y, $x + $lwid, $y + $lwid, $this->gdlinecolor);
1307  }
1308  }
1309  }
1310 
1314  protected function drawNoise()
1315  {
1316  if ($this->noise_level > 10) {
1317  $noise_level = 10;
1318  } else {
1320  }
1321 
1322  $t0 = microtime(true);
1323 
1324  $noise_level *= 125; // an arbitrary number that works well on a 1-10 scale
1325 
1326  $points = $this->image_width * $this->image_height * $this->iscale;
1327  $height = $this->image_height * $this->iscale;
1328  $width = $this->image_width * $this->iscale;
1329  for ($i = 0; $i < $noise_level; ++$i) {
1330  $x = mt_rand(10, $width);
1331  $y = mt_rand(10, $height);
1332  $size = mt_rand(7, 10);
1333  if ($x - $size <= 0 && $y - $size <= 0) continue; // dont cover 0,0 since it is used by imagedistortedcopy
1334  imagefilledarc($this->tmpimg, $x, $y, $size, $size, 0, 360, $this->gdnoisecolor, IMG_ARC_PIE);
1335  }
1336 
1337  $t1 = microtime(true);
1338 
1339  $t = $t1 - $t0;
1340 
1341  /*
1342  // DEBUG
1343  imagestring($this->tmpimg, 5, 25, 30, "$t", $this->gdnoisecolor);
1344  header('content-type: image/png');
1345  imagepng($this->tmpimg);
1346  exit;
1347  */
1348  }
1349 
1353  protected function addSignature()
1354  {
1355  $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
1356  $textlen = $bbox[2] - $bbox[0];
1357  $x = $this->image_width - $textlen - 5;
1358  $y = $this->image_height - 3;
1359 
1360  imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature);
1361  }
1362 
1366  protected function output()
1367  {
1368  if ($this->canSendHeaders() || $this->send_headers == false) {
1369  if ($this->send_headers) {
1370  // only send the content-type headers if no headers have been output
1371  // this will ease debugging on misconfigured servers where warnings
1372  // may have been output which break the image and prevent easily viewing
1373  // source to see the error.
1374  header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
1375  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
1376  header("Cache-Control: no-store, no-cache, must-revalidate");
1377  header("Cache-Control: post-check=0, pre-check=0", false);
1378  header("Pragma: no-cache");
1379  }
1380 
1381  switch ($this->image_type) {
1382  case self::SI_IMAGE_JPEG:
1383  if ($this->send_headers) header("Content-Type: image/jpeg");
1384  imagejpeg($this->im, null, 90);
1385  break;
1386  case self::SI_IMAGE_GIF:
1387  if ($this->send_headers) header("Content-Type: image/gif");
1388  imagegif($this->im);
1389  break;
1390  default:
1391  if ($this->send_headers) header("Content-Type: image/png");
1392  imagepng($this->im);
1393  break;
1394  }
1395  } else {
1396  echo '<hr /><strong>'
1397  .'Failed to generate captcha image, content has already been '
1398  .'output.<br />This is most likely due to misconfiguration or '
1399  .'a PHP error was sent to the browser.</strong>';
1400  }
1401 
1402  imagedestroy($this->im);
1403  restore_error_handler();
1404 
1405  if (!$this->no_exit) exit;
1406  }
1407 
1413  protected function getAudibleCode()
1414  {
1415  $letters = array();
1416  $code = $this->getCode(true, true);
1417 
1418  if ($code['code'] == '') {
1419  if (strlen($this->display_value) > 0) {
1420  $code = array('code' => $this->display_value, 'display' => $this->display_value);
1421  } else {
1422  $this->createCode();
1423  $code = $this->getCode(true);
1424  }
1425  }
1426 
1427  if (preg_match('/(\d+) (\+|-|x) (\d+)/i', $code['display'], $eq)) {
1428  $math = true;
1429 
1430  $left = $eq[1];
1431  $sign = str_replace(array('+', '-', 'x'), array('plus', 'minus', 'times'), $eq[2]);
1432  $right = $eq[3];
1433 
1434  $letters = array($left, $sign, $right);
1435  } else {
1436  $math = false;
1437 
1438  $length = strlen($code['display']);
1439 
1440  for($i = 0; $i < $length; ++$i) {
1441  $letter = $code['display']{$i};
1442  $letters[] = $letter;
1443  }
1444  }
1445 
1446  try {
1447  return $this->generateWAV($letters);
1448  } catch(Exception $ex) {
1449  throw $ex;
1450  }
1451  }
1452 
1456  protected function readCodeFromFile($numWords = 1)
1457  {
1458  $fp = fopen($this->wordlist_file, 'rb');
1459  if (!$fp) return false;
1460 
1461  $fsize = filesize($this->wordlist_file);
1462  if ($fsize < 128) return false; // too small of a list to be effective
1463 
1464  if ((int)$numWords < 1 || (int)$numWords > 5) $numWords = 1;
1465 
1466  $words = array();
1467  $i = 0;
1468  do {
1469  fseek($fp, mt_rand(0, $fsize - 64), SEEK_SET); // seek to a random position of file from 0 to filesize-64
1470  $data = fread($fp, 64); // read a chunk from our random position
1471  $data = preg_replace("/\r?\n/", "\n", $data);
1472 
1473  $start = @strpos($data, "\n", mt_rand(0, 56)) + 1; // random start position
1474  $end = @strpos($data, "\n", $start); // find end of word
1475 
1476  if ($start === false) {
1477  // picked start position at end of file
1478  continue;
1479  } else if ($end === false) {
1480  $end = strlen($data);
1481  }
1482 
1483  $word = strtolower(substr($data, $start, $end - $start)); // return a line of the file
1484  $words[] = $word;
1485  } while (++$i < $numWords);
1486 
1487  fclose($fp);
1488 
1489  if ($numWords < 2) {
1490  return $words[0];
1491  } else {
1492  return $words;
1493  }
1494  }
1495 
1499  protected function generateCode()
1500  {
1501  $code = '';
1502 
1503  if (function_exists('mb_strlen')) {
1504  for($i = 1, $cslen = mb_strlen($this->charset); $i <= $this->code_length; ++$i) {
1505  $code .= mb_substr($this->charset, mt_rand(0, $cslen - 1), 1, 'UTF-8');
1506  }
1507  } else {
1508  for($i = 1, $cslen = strlen($this->charset); $i <= $this->code_length; ++$i) {
1509  $code .= substr($this->charset, mt_rand(0, $cslen - 1), 1);
1510  }
1511  }
1512 
1513  return $code;
1514  }
1515 
1520  protected function validate()
1521  {
1522  if (!is_string($this->code) || strlen($this->code) == 0) {
1523  $code = $this->getCode();
1524  // returns stored code, or an empty string if no stored code was found
1525  // checks the session and database if enabled
1526  } else {
1527  $code = $this->code;
1528  }
1529 
1530  if ($this->case_sensitive == false && preg_match('/[A-Z]/', $code)) {
1531  // case sensitive was set from securimage_show.php but not in class
1532  // the code saved in the session has capitals so set case sensitive to true
1533  $this->case_sensitive = true;
1534  }
1535 
1536  $code_entered = trim( (($this->case_sensitive) ? $this->code_entered
1537  : strtolower($this->code_entered))
1538  );
1539  $this->correct_code = false;
1540 
1541  if ($code != '') {
1542  if (strpos($code, ' ') !== false) {
1543  // for multi word captchas, remove more than once space from input
1544  $code_entered = preg_replace('/\s+/', ' ', $code_entered);
1545  $code_entered = strtolower($code_entered);
1546  }
1547 
1548  if ($code == $code_entered) {
1549  $this->correct_code = true;
1550  if ($this->no_session != true) {
1551  $_SESSION['securimage_code_value'][$this->namespace] = '';
1552  $_SESSION['securimage_code_ctime'][$this->namespace] = '';
1553  }
1554  $this->clearCodeFromDatabase();
1555  }
1556  }
1557  }
1558 
1562  protected function saveData()
1563  {
1564  if ($this->no_session != true) {
1565  if (isset($_SESSION['securimage_code_value']) && is_scalar($_SESSION['securimage_code_value'])) {
1566  // fix for migration from v2 - v3
1567  unset($_SESSION['securimage_code_value']);
1568  unset($_SESSION['securimage_code_ctime']);
1569  }
1570 
1571  $_SESSION['securimage_code_disp'] [$this->namespace] = $this->code_display;
1572  $_SESSION['securimage_code_value'][$this->namespace] = $this->code;
1573  $_SESSION['securimage_code_ctime'][$this->namespace] = time();
1574  }
1575 
1576  if ($this->use_database) {
1577  $this->saveCodeToDatabase();
1578  }
1579  }
1580 
1584  protected function saveCodeToDatabase()
1585  {
1586  $success = false;
1587  $this->openDatabase();
1588 
1589  if ($this->use_database && $this->pdo_conn) {
1590  $id = $this->getCaptchaId(false);
1591  $ip = $_SERVER['REMOTE_ADDR'];
1592 
1593  if (empty($id)) {
1594  $id = $ip;
1595  }
1596 
1597  $time = time();
1598  $code = $this->code;
1599  $code_disp = $this->code_display;
1600 
1601  // This is somewhat expensive in PDO Sqlite3 (when there is something to delete)
1602  $this->clearCodeFromDatabase();
1603 
1604  $query = "INSERT INTO {$this->database_table} ("
1605  ."id, code, code_display, namespace, created) "
1606  ."VALUES(?, ?, ?, ?, ?)";
1607 
1608  $stmt = $this->pdo_conn->prepare($query);
1609  $success = $stmt->execute(array($id, $code, $code_disp, $this->namespace, $time));
1610 
1611  if (!$success) {
1612  $err = $stmt->errorInfo();
1613  trigger_error("Failed to insert code into database. {$err[1]}: {$err[2]}", E_USER_WARNING);
1614  }
1615  }
1616 
1617  return $success !== false;
1618  }
1619 
1623  protected function openDatabase()
1624  {
1625  $this->pdo_conn = false;
1626 
1627  if ($this->use_database) {
1628  $pdo_extension = 'PDO_' . strtoupper($this->database_driver);
1629 
1630  if (!extension_loaded($pdo_extension)) {
1631  trigger_error("Database support is turned on in Securimage, but the chosen extension $pdo_extension is not loaded in PHP.", E_USER_WARNING);
1632  return false;
1633  }
1634  }
1635 
1636  if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
1637  if (!file_exists($this->database_file)) {
1638  $fp = fopen($this->database_file, 'w+');
1639  if (!$fp) {
1640  $err = error_get_last();
1641  trigger_error("Securimage failed to create SQLite3 database file '{$this->database_file}'. Reason: {$err['message']}", E_USER_WARNING);
1642  return false;
1643  }
1644  fclose($fp);
1645  chmod($this->database_file, 0666);
1646  } else if (!is_writeable($this->database_file)) {
1647  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);
1648  return false;
1649  }
1650  }
1651 
1652  $dsn = $this->getDsn();
1653 
1654  try {
1655  $options = array();
1656  $this->pdo_conn = new PDO($dsn, $this->database_user, $this->database_pass, $options);
1657  } catch (PDOException $pdoex) {
1658  trigger_error("Database connection failed: " . $pdoex->getMessage(), E_USER_WARNING);
1659  return false;
1660  }
1661 
1662  try {
1663  if (!$this->checkTablesExist()) {
1664  // create tables...
1665  $this->createDatabaseTables();
1666  }
1667  } catch (Exception $ex) {
1668  trigger_error($ex->getMessage(), E_USER_WARNING);
1669  $this->pdo_conn = null;
1670  return false;
1671  }
1672 
1673  if (mt_rand(0, 100) / 100.0 == 1.0) {
1674  $this->purgeOldCodesFromDatabase();
1675  }
1676 
1677  return $this->pdo_conn;
1678  }
1679 
1680  protected function getDsn()
1681  {
1682  $dsn = sprintf('%s:', $this->database_driver);
1683 
1684  switch($this->database_driver) {
1685  case self::SI_DRIVER_SQLITE3:
1686  $dsn .= $this->database_file;
1687  break;
1688 
1689  case self::SI_DRIVER_MYSQL:
1690  case self::SI_DRIVER_PGSQL:
1691  $dsn .= sprintf('host=%s;dbname=%s',
1692  $this->database_host,
1693  $this->database_name);
1694  break;
1695 
1696  }
1697 
1698  return $dsn;
1699  }
1700 
1701  protected function checkTablesExist()
1702  {
1703  $table = $this->pdo_conn->quote($this->database_table);
1704 
1705  switch($this->database_driver) {
1706  case self::SI_DRIVER_SQLITE3:
1707  // query row count for sqlite, PRAGMA queries seem to return no
1708  // rowCount using PDO even if there are rows returned
1709  $query = "SELECT COUNT(id) FROM $table";
1710  break;
1711 
1712  case self::SI_DRIVER_MYSQL:
1713  $query = "SHOW TABLES LIKE $table";
1714  break;
1715 
1716  case self::SI_DRIVER_PGSQL:
1717  $query = "SELECT * FROM information_schema.columns WHERE table_name = $table;";
1718  break;
1719  }
1720 
1721  $result = $this->pdo_conn->query($query);
1722 
1723  if (!$result) {
1724  $err = $this->pdo_conn->errorInfo();
1725 
1726  if ($this->database_driver == self::SI_DRIVER_SQLITE3 &&
1727  $err[1] === 1 && strpos($err[2], 'no such table') !== false)
1728  {
1729  return false;
1730  }
1731 
1732  throw new Exception("Failed to check tables: {$err[0]} - {$err[1]}: {$err[2]}");
1733  } else if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
1734  // successful here regardless of row count for sqlite
1735  return true;
1736  } else if ($result->rowCount() == 0) {
1737  return false;
1738  } else {
1739  return true;
1740  }
1741  }
1742 
1743  protected function createDatabaseTables()
1744  {
1745  $queries = array();
1746 
1747  switch($this->database_driver) {
1748  case self::SI_DRIVER_SQLITE3:
1749  $queries[] = "CREATE TABLE \"{$this->database_table}\" (
1750  id VARCHAR(40),
1751  namespace VARCHAR(32) NOT NULL,
1752  code VARCHAR(32) NOT NULL,
1753  code_display VARCHAR(32) NOT NULL,
1754  created INTEGER NOT NULL,
1755  PRIMARY KEY(id, namespace)
1756  )";
1757 
1758  $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created)";
1759  break;
1760 
1761  case self::SI_DRIVER_MYSQL:
1762  $queries[] = "CREATE TABLE `{$this->database_table}` (
1763  `id` VARCHAR(40) NOT NULL,
1764  `namespace` VARCHAR(32) NOT NULL,
1765  `code` VARCHAR(32) NOT NULL,
1766  `code_display` VARCHAR(32) NOT NULL,
1767  `created` INT NOT NULL,
1768  PRIMARY KEY(id, namespace),
1769  INDEX(created)
1770  )";
1771  break;
1772 
1773  case self::SI_DRIVER_PGSQL:
1774  $queries[] = "CREATE TABLE {$this->database_table} (
1775  id character varying(40) NOT NULL,
1776  namespace character varying(32) NOT NULL,
1777  code character varying(32) NOT NULL,
1778  code_display character varying(32) NOT NULL,
1779  created integer NOT NULL,
1780  CONSTRAINT pkey_id_namespace PRIMARY KEY (id, namespace)
1781  )";
1782 
1783  $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created);";
1784  break;
1785  }
1786 
1787  $this->pdo_conn->beginTransaction();
1788 
1789  foreach($queries as $query) {
1790  $result = $this->pdo_conn->query($query);
1791 
1792  if (!$result) {
1793  $err = $this->pdo_conn->errorInfo();
1794  trigger_error("Failed to create table. {$err[1]}: {$err[2]}", E_USER_WARNING);
1795  $this->pdo_conn->rollBack();
1796  $this->pdo_conn = false;
1797  return false;
1798  }
1799  }
1800 
1801  $this->pdo_conn->commit();
1802 
1803  return true;
1804  }
1805 
1813  protected function getCodeFromDatabase()
1814  {
1815  $code = '';
1816 
1817  if ($this->use_database == true && $this->pdo_conn) {
1818  if (Securimage::$_captchaId !== null) {
1819  $query = "SELECT * FROM {$this->database_table} WHERE id = ?";
1820  $stmt = $this->pdo_conn->prepare($query);
1821  $result = $stmt->execute(array(Securimage::$_captchaId));
1822  } else {
1823  $ip = $_SERVER['REMOTE_ADDR'];
1824  $ns = $this->namespace;
1825 
1826  // ip is stored in id column when no captchaId
1827  $query = "SELECT * FROM {$this->database_table} WHERE id = ? AND namespace = ?";
1828  $stmt = $this->pdo_conn->prepare($query);
1829  $result = $stmt->execute(array($ip, $ns));
1830  }
1831 
1832  if (!$result) {
1833  $err = $this->pdo_conn->errorInfo();
1834  trigger_error("Failed to select code from database. {$err[0]}: {$err[1]}", E_USER_WARNING);
1835  } else {
1836  if ( ($row = $stmt->fetch()) !== false ) {
1837  if (false == $this->isCodeExpired($row['created'])) {
1838  if (Securimage::$_captchaId !== null) {
1839  // return an array when using captchaId
1840  $code = array('code' => $row['code'],
1841  'code_disp' => $row['code_display']);
1842  } else {
1843  $code = $row['code'];
1844  }
1845  }
1846  }
1847  }
1848  }
1849 
1850  return $code;
1851  }
1852 
1856  protected function clearCodeFromDatabase()
1857  {
1858  if ($this->pdo_conn) {
1859  $ip = $_SERVER['REMOTE_ADDR'];
1860  $ns = $this->pdo_conn->quote($this->namespace);
1862 
1863  if (empty($id)) {
1864  $id = $ip; // if no captchaId set, IP address is captchaId.
1865  }
1866 
1867  $id = $this->pdo_conn->quote($id);
1868 
1869  $query = sprintf("DELETE FROM %s WHERE id = %s AND namespace = %s",
1870  $this->database_table, $id, $ns);
1871 
1872  $result = $this->pdo_conn->query($query);
1873  if (!$result) {
1874  trigger_error("Failed to delete code from database.", E_USER_WARNING);
1875  }
1876  }
1877  }
1878 
1882  protected function purgeOldCodesFromDatabase()
1883  {
1884  if ($this->use_database && $this->pdo_conn) {
1885  $now = time();
1886  $limit = (!is_numeric($this->expiry_time) || $this->expiry_time < 1) ? 86400 : $this->expiry_time;
1887 
1888  $query = sprintf("DELETE FROM %s WHERE %s - created > %s",
1889  $this->database_table,
1890  $this->pdo_conn->quote($now, PDO::PARAM_INT),
1891  $this->pdo_conn->quote($limit, PDO::PARAM_INT));
1892 
1893  $result = $this->pdo_conn->query($query);
1894  }
1895  }
1896 
1901  protected function isCodeExpired($creation_time)
1902  {
1903  $expired = true;
1904 
1905  if (!is_numeric($this->expiry_time) || $this->expiry_time < 1) {
1906  $expired = false;
1907  } else if (time() - $creation_time < $this->expiry_time) {
1908  $expired = false;
1909  }
1910 
1911  return $expired;
1912  }
1913 
1920  protected function generateWAV($letters)
1921  {
1922  $wavCaptcha = new WavFile();
1923  $first = true; // reading first wav file
1924 
1925  foreach ($letters as $letter) {
1926  $letter = strtoupper($letter);
1927 
1928  try {
1929  $l = new WavFile($this->audio_path . '/' . $letter . '.wav');
1930 
1931  if ($first) {
1932  // set sample rate, bits/sample, and # of channels for file based on first letter
1933  $wavCaptcha->setSampleRate($l->getSampleRate())
1934  ->setBitsPerSample($l->getBitsPerSample())
1935  ->setNumChannels($l->getNumChannels());
1936  $first = false;
1937  }
1938 
1939  // append letter to the captcha audio
1940  $wavCaptcha->appendWav($l);
1941 
1942  // random length of silence between $audio_gap_min and $audio_gap_max
1943  if ($this->audio_gap_max > 0 && $this->audio_gap_max > $this->audio_gap_min) {
1944  $wavCaptcha->insertSilence( mt_rand($this->audio_gap_min, $this->audio_gap_max) / 1000.0 );
1945  }
1946  } catch (Exception $ex) {
1947  // failed to open file, or the wav file is broken or not supported
1948  // 2 wav files were not compatible, different # channels, bits/sample, or sample rate
1949  throw $ex;
1950  }
1951  }
1952 
1953  /********* Set up audio filters *****************************/
1954  $filters = array();
1955 
1956  if ($this->audio_use_noise == true) {
1957  // use background audio - find random file
1958  $noiseFile = $this->getRandomNoiseFile();
1959 
1960  if ($noiseFile !== false && is_readable($noiseFile)) {
1961  try {
1962  $wavNoise = new WavFile($noiseFile, false);
1963  } catch(Exception $ex) {
1964  throw $ex;
1965  }
1966 
1967  // start at a random offset from the beginning of the wavfile
1968  // in order to add more randomness
1969  $randOffset = 0;
1970  if ($wavNoise->getNumBlocks() > 2 * $wavCaptcha->getNumBlocks()) {
1971  $randBlock = mt_rand(0, $wavNoise->getNumBlocks() - $wavCaptcha->getNumBlocks());
1972  $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $wavCaptcha->getNumBlocks() * $wavNoise->getBlockAlign());
1973  } else {
1974  $wavNoise->readWavData();
1975  $randOffset = mt_rand(0, $wavNoise->getNumBlocks() - 1);
1976  }
1977 
1978 
1979  $mixOpts = array('wav' => $wavNoise,
1980  'loop' => true,
1981  'blockOffset' => $randOffset);
1982 
1983  $filters[WavFile::FILTER_MIX] = $mixOpts;
1985  }
1986  }
1987 
1988  if ($this->degrade_audio == true) {
1989  // add random noise.
1990  // any noise level below 95% is intensely distorted and not pleasant to the ear
1991  $filters[WavFile::FILTER_DEGRADE] = mt_rand(95, 98) / 100.0;
1992  }
1993 
1994  if (!empty($filters)) {
1995  $wavCaptcha->filter($filters); // apply filters to captcha audio
1996  }
1997 
1998  return $wavCaptcha->__toString();
1999  }
2000 
2001  public function getRandomNoiseFile()
2002  {
2003  $return = false;
2004 
2005  if ( ($dh = opendir($this->audio_noise_path)) !== false ) {
2006  $list = array();
2007 
2008  while ( ($file = readdir($dh)) !== false ) {
2009  if ($file == '.' || $file == '..') continue;
2010  if (strtolower(substr($file, -4)) != '.wav') continue;
2011 
2012  $list[] = $file;
2013  }
2014 
2015  closedir($dh);
2016 
2017  if (sizeof($list) > 0) {
2018  $file = $list[array_rand($list, 1)];
2019  $return = $this->audio_noise_path . DIRECTORY_SEPARATOR . $file;
2020  }
2021  }
2022 
2023  return $return;
2024  }
2025 
2031  protected function audioError()
2032  {
2033  return @file_get_contents(dirname(__FILE__) . '/audio/en/error.wav');
2034  }
2035 
2041  protected function canSendHeaders()
2042  {
2043  if (headers_sent()) {
2044  // output has been flushed and headers have already been sent
2045  return false;
2046  } else if (strlen((string)ob_get_contents()) > 0) {
2047  // headers haven't been sent, but there is data in the buffer that will break image and audio data
2048  return false;
2049  }
2050 
2051  return true;
2052  }
2053 
2059  function frand()
2060  {
2061  return 0.0001 * mt_rand(0,9999);
2062  }
2063 
2069  protected function initColor($color, $default)
2070  {
2071  if ($color == null) {
2072  return new Securimage_Color($default);
2073  } else if (is_string($color)) {
2074  try {
2075  return new Securimage_Color($color);
2076  } catch(Exception $e) {
2077  return new Securimage_Color($default);
2078  }
2079  } else if (is_array($color) && sizeof($color) == 3) {
2080  return new Securimage_Color($color[0], $color[1], $color[2]);
2081  } else {
2082  return new Securimage_Color($default);
2083  }
2084  }
2085 
2101  public function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array())
2102  {
2103  // get the current error reporting level
2104  $level = error_reporting();
2105 
2106  // if error was supressed or $errno not set in current error level
2107  if ($level == 0 || ($level & $errno) == 0) {
2108  return true;
2109  }
2110 
2111  return false;
2112  }
2113 }
2114 
2115 
2126 {
2127  public $r;
2128  public $g;
2129  public $b;
2130 
2142  public function __construct($color = '#ffffff')
2143  {
2144  $args = func_get_args();
2145 
2146  if (sizeof($args) == 0) {
2147  $this->r = 255;
2148  $this->g = 255;
2149  $this->b = 255;
2150  } else if (sizeof($args) == 1) {
2151  // set based on html code
2152  if (substr($color, 0, 1) == '#') {
2153  $color = substr($color, 1);
2154  }
2155 
2156  if (strlen($color) != 3 && strlen($color) != 6) {
2157  throw new InvalidArgumentException(
2158  'Invalid HTML color code passed to Securimage_Color'
2159  );
2160  }
2161 
2162  $this->constructHTML($color);
2163  } else if (sizeof($args) == 3) {
2164  $this->constructRGB($args[0], $args[1], $args[2]);
2165  } else {
2166  throw new InvalidArgumentException(
2167  'Securimage_Color constructor expects 0, 1 or 3 arguments; ' . sizeof($args) . ' given'
2168  );
2169  }
2170  }
2171 
2178  protected function constructRGB($red, $green, $blue)
2179  {
2180  if ($red < 0) $red = 0;
2181  if ($red > 255) $red = 255;
2182  if ($green < 0) $green = 0;
2183  if ($green > 255) $green = 255;
2184  if ($blue < 0) $blue = 0;
2185  if ($blue > 255) $blue = 255;
2186 
2187  $this->r = $red;
2188  $this->g = $green;
2189  $this->b = $blue;
2190  }
2191 
2196  protected function constructHTML($color)
2197  {
2198  if (strlen($color) == 3) {
2199  $red = str_repeat(substr($color, 0, 1), 2);
2200  $green = str_repeat(substr($color, 1, 1), 2);
2201  $blue = str_repeat(substr($color, 2, 1), 2);
2202  } else {
2203  $red = substr($color, 0, 2);
2204  $green = substr($color, 2, 2);
2205  $blue = substr($color, 4, 2);
2206  }
2207 
2208  $this->r = hexdec($red);
2209  $this->g = hexdec($green);
2210  $this->b = hexdec($blue);
2211  }
2212 }
drawWord()
Draws the captcha code on the image.
const SI_DRIVER_MYSQL
Definition: securimage.php:208
const SI_CAPTCHA_STRING
Definition: securimage.php:191
const FILTER_MIX
Definition: WavFile.php:75
openDatabase()
Open sqlite database.
$px
$size
Definition: RandomTest.php:79
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
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.
drawNoise()
Draws random noise on the image.
static getCaptchaId($new=true, array $options=array())
Generate a new captcha ID or retrieve the current ID.
Definition: securimage.php:735
$_SESSION["AccountId"]
$result
getAudibleCode()
Gets the code and returns the binary audio file for the stored captcha code.
$x
Definition: example_009.php:98
saveData()
Save data to session namespace and database if used.
$blue
Definition: example_030.php:81
const SI_DRIVER_SQLITE3
Definition: securimage.php:222
setBackground()
The the background color, or background image to be used.
Project: Securimage: A PHP class for creating and managing form CAPTCHA images File: securimage...
constructHTML($color)
Construct from an html hex color code.
const FILTER_NORMALIZE
Definition: WavFile.php:78
frand()
Return a random float between 0 and 0.9999.
createCode()
Generates the code or math problem and saves the value to the session.
saveCodeToDatabase()
Saves the code to the sqlite database.
initColor($color, $default)
Convert an html color code to a Securimage_Color.
readCodeFromFile($numWords=1)
Gets a captcha code from a wordlist.
$audio_mix_normalization
Definition: securimage.php:511
generateCode()
Generates a random captcha code from the set character set.
$text_transparency_percentage
Definition: securimage.php:268
show($background_image='')
Used to serve a captcha image to the browser.
Definition: securimage.php:807
const SI_IMAGE_JPEG
Definition: securimage.php:175
const SI_CAPTCHA_WORDS
Definition: securimage.php:201
getBackgroundFromDirectory()
Scan the directory for a background image to use.
validate()
Checks the entered code against the value stored in the session or sqlite database, handles case sensitivity Also clears the stored codes if the code was entered correctly to prevent re-use.
getCode($array=false, $returnExisting=false)
Return the code from the session or sqlite database if used.
Definition: securimage.php:899
$w
isCodeExpired($creation_time)
Checks to see if the captcha code has expired and cannot be used.
const FILTER_DEGRADE
Definition: WavFile.php:81
addSignature()
Print signature text on image.
const SI_CAPTCHA_MATHEMATIC
Definition: securimage.php:196
errorHandler($errno, $errstr, $errfile='', $errline=0, $errcontext=array())
Error handler used when outputting captcha image or audio.
$red
Definition: example_030.php:80
clearCodeFromDatabase()
Remove an entered code from the database.
$r
Definition: example_031.php:79
$success
Definition: Utf8Test.php:86
$y
Definition: example_007.php:83
if(!is_array($argv)) $options
$py
doImage()
The main image drawing routing, responsible for constructing the entire image and serving it...
Definition: securimage.php:944
date( 'd-M-Y', $objPHPExcel->getProperties() ->getCreated())
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
getCodeFromDatabase()
Get a code from the sqlite database for ip address/captchaId.
purgeOldCodesFromDatabase()
Deletes old codes from sqlite database.
$green
Definition: example_030.php:83
Add a drawing to the header
Definition: 04printing.php:69
$n
Definition: RandomTest.php:80
static checkByCaptchaId($id, $value, array $options=array())
Validate a captcha code input against a captcha ID.
Definition: securimage.php:763
getRandomNoiseFile()
Create styles array
The data for the language used.
outputAudioFile()
Output a wav file of the captcha code to the browser.
Definition: securimage.php:847
output()
Sends the appropriate image and cache headers and outputs image to the browser.
allocateColors()
Allocate the colors to be used for the image.
global $l
Definition: afr.php:30
canSendHeaders()
Checks to see if headers can be sent and if any error has been output to the browser.
__construct($options=array())
Create a new securimage object, pass options to set in the constructor.
Definition: securimage.php:629
const SI_IMAGE_GIF
Definition: securimage.php:185
__construct($color='#ffffff')
Create a new Securimage_Color object.
constructRGB($red, $green, $blue)
Construct from an rgb triplet.
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
static $_captchaId
Definition: securimage.php:539
const SI_DRIVER_PGSQL
Definition: securimage.php:215
check($code)
Check a submitted code against the stored value.
Definition: securimage.php:831
drawLines()
Draws distorted lines on the image.
generateWAV($letters)
Generate a wav file given the $letters in the code.
const SI_IMAGE_PNG
Definition: securimage.php:180
distortedCopy()
Copies the captcha image to the final image with distortion applied.
createDatabaseTables()