captcha.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. <?php
  2. if(
  3. isset($_GET["v"]) === false ||
  4. is_array($_GET["v"]) === true ||
  5. preg_match(
  6. '/^c[0-9]+\.[A-Za-z0-9_]{20}$/',
  7. $_GET["v"]
  8. ) === 0
  9. ){
  10. http_response_code(401);
  11. header("Content-Type: text/plain");
  12. echo "Fuck my feathered cloaca";
  13. die();
  14. }
  15. //header("Content-Type: image/jpeg");
  16. include "data/config.php";
  17. if(config::BOT_PROTECTION !== 1){
  18. header("Content-Type: text/plain");
  19. echo "The IQ test is disabled";
  20. die();
  21. }
  22. $grid = apcu_fetch($_GET["v"]);
  23. if($grid !== false){
  24. // captcha already generated
  25. http_response_code(304); // not modified
  26. die();
  27. }
  28. header("Content-Type: image/jpeg");
  29. header("Last-Modified: Thu, 01 Oct 1970 00:00:00 GMT");
  30. // ** generate captcha data
  31. // get the positions for the answers
  32. // will return between 3 and 6 answer positions
  33. $range = range(0, 15);
  34. $answer_pos = [];
  35. array_splice($range, 0, 1);
  36. $picks = random_int(3, 6);
  37. for($i=0; $i<$picks; $i++){
  38. $answer_pos_tmp =
  39. array_splice(
  40. $range,
  41. random_int(
  42. 0,
  43. 14 - $i
  44. ),
  45. 1
  46. );
  47. $answer_pos[] = $answer_pos_tmp[0];
  48. }
  49. // choose a dataset
  50. $c = count(config::CAPTCHA_DATASET);
  51. $choosen = config::CAPTCHA_DATASET[random_int(0, $c - 1)];
  52. $choices = [];
  53. for($i=0; $i<$c; $i++){
  54. if(config::CAPTCHA_DATASET[$i][0] == $choosen[0]){
  55. continue;
  56. }
  57. $choices[] = config::CAPTCHA_DATASET[$i];
  58. }
  59. // generate grid data
  60. $grid = [];
  61. for($i=0; $i<16; $i++){
  62. if(in_array($i, $answer_pos)){
  63. $grid[] = $choosen;
  64. }else{
  65. $grid[] = $choices[random_int(0, count($choices) - 1)];
  66. }
  67. }
  68. // store grid data for form validation on captcha_gen.php
  69. apcu_store(
  70. $_GET["v"],
  71. $answer_pos,
  72. 60 // we give user 1 minute to solve
  73. );
  74. // generate image
  75. if(random_int(0,1) === 0){
  76. $theme = [
  77. "bg" => "#ebdbb2",
  78. "fg" => "#1d2021"
  79. ];
  80. }else{
  81. $theme = [
  82. "bg" => "#1d2021",
  83. "fg" => "#ebdbb2"
  84. ];
  85. }
  86. $im = new Imagick();
  87. $im->newImage(400, 427, $theme["bg"]);
  88. $im->setImageBackgroundColor($theme["bg"]);
  89. $im->setImageFormat("jpg");
  90. $noise = [
  91. imagick::NOISE_GAUSSIAN,
  92. imagick::NOISE_LAPLACIAN
  93. ];
  94. $distort = [
  95. imagick::DISTORTION_AFFINE,
  96. imagick::DISTORTION_SHEPARDS
  97. ];
  98. $i = 0;
  99. for($y=0; $y<4; $y++){
  100. for($x=0; $x<4; $x++){
  101. $tmp = new Imagick("./data/captcha/" . $grid[$i][0] . "/" . random_int(1, $grid[$i][1]) . ".png");
  102. // convert transparency correctly
  103. $tmp->setImageBackgroundColor("black");
  104. $tmp->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
  105. // randomly mirror
  106. if(random_int(0,1) === 1){
  107. $tmp->flopImage();
  108. }
  109. // distort $tmp
  110. $tmp->distortImage(
  111. $distort[random_int(0,1)],
  112. [
  113. 0, 0,
  114. random_int(-15, 15), random_int(-15, 15),
  115. 100, 0,
  116. random_int(80, 120), random_int(-15, 15),
  117. 100, 100,
  118. random_int(80, 120), random_int(80, 120),
  119. 0, 100,
  120. random_int(-15, 15), random_int(80, 120)
  121. ],
  122. false
  123. );
  124. $tmp->addNoiseImage($noise[random_int(0, 1)]);
  125. // append image
  126. $im->compositeImage($tmp->getImage(), Imagick::COMPOSITE_DEFAULT, $x * 100, ($y * 100) + 27);
  127. $i++;
  128. }
  129. }
  130. // add text
  131. $draw = new ImagickDraw();
  132. $draw->setFontSize(20);
  133. $draw->setFillColor($theme["fg"]);
  134. //$draw->setTextAntialias(false);
  135. $draw->setFont("./data/fonts/captcha.ttf");
  136. $text = "Pick " . $picks . " images of " . str_replace("_", " ", $choosen[0]);
  137. $pos = 200 - ($im->queryFontMetrics($draw, $text)["textWidth"] / 2);
  138. for($i=0; $i<strlen($text); $i++){
  139. $im->annotateImage(
  140. $draw,
  141. $pos,
  142. 20,
  143. random_int(-15, 15),
  144. $text[$i]
  145. );
  146. $pos += $im->queryFontMetrics($draw, $text[$i])["textWidth"];
  147. }
  148. $im->setFormat("jpeg");
  149. $im->setImageCompressionQuality(90);
  150. echo $im->getImageBlob();