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 
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  } 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);
979  $code = $this->code;
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)) {
1113  $img = $this->getBackgroundFromDirectory();
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) {
1186  case self::SI_CAPTCHA_MATHEMATIC:
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 
1206  case self::SI_CAPTCHA_WORDS:
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) {
1428  case self::SI_IMAGE_JPEG:
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 {
1587  $code = $this->code;
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();
1659  $code = $this->code;
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) {
1735  $this->purgeOldCodesFromDatabase();
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) {
1746  case self::SI_DRIVER_SQLITE3:
1748  break;
1749 
1750  case self::SI_DRIVER_MYSQL:
1751  case self::SI_DRIVER_PGSQL:
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) {
1769  case self::SI_DRIVER_SQLITE3:
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 
1775  case self::SI_DRIVER_MYSQL:
1776  $query = "SHOW TABLES LIKE $table";
1777  break;
1778 
1779  case self::SI_DRIVER_PGSQL:
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) {
1810  case self::SI_DRIVER_SQLITE3:
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 
1823  case self::SI_DRIVER_MYSQL:
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 
1835  case self::SI_DRIVER_PGSQL:
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 }
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.
if(isset($_REQUEST['delete'])) $list
Definition: registry.php:41
$px
$size
Definition: RandomTest.php:84
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
$stmt
getAudibleCode()
Gets the code and returns the binary audio file for the stored captcha code.
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.
if(!array_key_exists('StateId', $_REQUEST)) $id
$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:811
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.
foreach($paths as $path) $dsn
Definition: migrateto20.php:56
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:905
$w
isCodeExpired($creation_time)
Checks to see if the captcha code has expired and cannot be used.
const FILTER_DEGRADE
Definition: WavFile.php:81
$start
Definition: bench.php:8
addSignature()
Print signature text on image.
$time
Definition: cron.php:21
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
$py
doImage()
The main image drawing routing, responsible for constructing the entire image and serving it...
Definition: securimage.php:952
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
$query
$n
Definition: RandomTest.php:85
static checkByCaptchaId($id, $value, array $options=array())
Validate a captcha code input against a captcha ID.
Definition: securimage.php:765
getRandomNoiseFile()
$default
Definition: build.php:20
$row
outputAudioFile()
Output a wav file of the captcha code to the browser.
Definition: securimage.php:851
output()
Sends the appropriate image and cache headers and outputs image to the browser.
exit
Definition: backend.php:16
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.
$i
Definition: disco.tpl.php:19
__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.
if(empty($password)) $table
Definition: pwgen.php:24
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:835
drawLines()
Draws distorted lines on the image.
$x
Definition: complexTest.php:9
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.
$data
Definition: bench.php:6
createDatabaseTables()