/modules/really-simple-captcha.php

  1. <?php 
  2. /** 
  3. ** A base module for [captchac] and [captchar] 
  4. **/ 
  5.  
  6. /** form_tag handler */ 
  7.  
  8. add_action( 'wpcf7_init', 'wpcf7_add_form_tag_captcha' ); 
  9.  
  10. function wpcf7_add_form_tag_captcha() { 
  11. wpcf7_add_form_tag( array( 'captchac', 'captchar' ),  
  12. 'wpcf7_captcha_form_tag_handler', array( 'name-attr' => true ) ); 
  13.  
  14. function wpcf7_captcha_form_tag_handler( $tag ) { 
  15. $tag = new WPCF7_FormTag( $tag ); 
  16.  
  17. if ( 'captchac' == $tag->type && ! class_exists( 'ReallySimpleCaptcha' ) ) { 
  18. return '<em>' . __( 'To use CAPTCHA, you need <a href="http://wordpress.org/extend/plugins/really-simple-captcha/">Really Simple CAPTCHA</a> plugin installed.', 'contact-form-7' ) . '</em>'; 
  19.  
  20. if ( empty( $tag->name ) ) { 
  21. return ''; 
  22.  
  23. $validation_error = wpcf7_get_validation_error( $tag->name ); 
  24.  
  25. $class = wpcf7_form_controls_class( $tag->type ); 
  26.  
  27. if ( 'captchac' == $tag->type ) { // CAPTCHA-Challenge (image) 
  28. $class .= ' wpcf7-captcha-' . $tag->name; 
  29.  
  30. $atts = array(); 
  31.  
  32. $atts['class'] = $tag->get_class_option( $class ); 
  33. $atts['id'] = $tag->get_id_option(); 
  34.  
  35. $op = array( // Default 
  36. 'img_size' => array( 72, 24 ),  
  37. 'base' => array( 6, 18 ),  
  38. 'font_size' => 14,  
  39. 'font_char_width' => 15 ); 
  40.  
  41. $op = array_merge( $op, wpcf7_captchac_options( $tag->options ) ); 
  42.  
  43. if ( ! $filename = wpcf7_generate_captcha( $op ) ) { 
  44. return ''; 
  45.  
  46. if ( ! empty( $op['img_size'] ) ) { 
  47. if ( isset( $op['img_size'][0] ) ) { 
  48. $atts['width'] = $op['img_size'][0]; 
  49.  
  50. if ( isset( $op['img_size'][1] ) ) { 
  51. $atts['height'] = $op['img_size'][1]; 
  52.  
  53. $atts['alt'] = 'captcha'; 
  54. $atts['src'] = wpcf7_captcha_url( $filename ); 
  55.  
  56. $atts = wpcf7_format_atts( $atts ); 
  57.  
  58. $prefix = substr( $filename, 0, strrpos( $filename, '.' ) ); 
  59.  
  60. $html = sprintf( 
  61. '<input type="hidden" name="_wpcf7_captcha_challenge_%1$s" value="%2$s" /><img %3$s />',  
  62. $tag->name, $prefix, $atts ); 
  63.  
  64. return $html; 
  65.  
  66. } elseif ( 'captchar' == $tag->type ) { // CAPTCHA-Response (input) 
  67. if ( $validation_error ) { 
  68. $class .= ' wpcf7-not-valid'; 
  69.  
  70. $atts = array(); 
  71.  
  72. $atts['size'] = $tag->get_size_option( '40' ); 
  73. $atts['maxlength'] = $tag->get_maxlength_option(); 
  74. $atts['minlength'] = $tag->get_minlength_option(); 
  75.  
  76. if ( $atts['maxlength'] && $atts['minlength'] 
  77. && $atts['maxlength'] < $atts['minlength'] ) { 
  78. unset( $atts['maxlength'], $atts['minlength'] ); 
  79.  
  80. $atts['class'] = $tag->get_class_option( $class ); 
  81. $atts['id'] = $tag->get_id_option(); 
  82. $atts['tabindex'] = $tag->get_option( 'tabindex', 'int', true ); 
  83. $atts['autocomplete'] = 'off'; 
  84. $atts['aria-invalid'] = $validation_error ? 'true' : 'false'; 
  85.  
  86. $value = (string) reset( $tag->values ); 
  87.  
  88. if ( wpcf7_is_posted() ) { 
  89. $value = ''; 
  90.  
  91. if ( $tag->has_option( 'placeholder' ) 
  92. || $tag->has_option( 'watermark' ) ) { 
  93. $atts['placeholder'] = $value; 
  94. $value = ''; 
  95.  
  96. $atts['value'] = $value; 
  97. $atts['type'] = 'text'; 
  98. $atts['name'] = $tag->name; 
  99.  
  100. $atts = wpcf7_format_atts( $atts ); 
  101.  
  102. $html = sprintf( 
  103. '<span class="wpcf7-form-control-wrap %1$s"><input %2$s />%3$s</span>',  
  104. sanitize_html_class( $tag->name ), $atts, $validation_error ); 
  105.  
  106. return $html; 
  107.  
  108.  
  109. /** Validation filter */ 
  110.  
  111. add_filter( 'wpcf7_validate_captchar', 'wpcf7_captcha_validation_filter', 10, 2 ); 
  112.  
  113. function wpcf7_captcha_validation_filter( $result, $tag ) { 
  114. $tag = new WPCF7_FormTag( $tag ); 
  115.  
  116. $type = $tag->type; 
  117. $name = $tag->name; 
  118.  
  119. $captchac = '_wpcf7_captcha_challenge_' . $name; 
  120.  
  121. $prefix = isset( $_POST[$captchac] ) ? (string) $_POST[$captchac] : ''; 
  122. $response = isset( $_POST[$name] ) ? (string) $_POST[$name] : ''; 
  123. $response = wpcf7_canonicalize( $response ); 
  124.  
  125. if ( 0 == strlen( $prefix ) || ! wpcf7_check_captcha( $prefix, $response ) ) { 
  126. $result->invalidate( $tag, wpcf7_get_message( 'captcha_not_match' ) ); 
  127.  
  128. if ( 0 != strlen( $prefix ) ) { 
  129. wpcf7_remove_captcha( $prefix ); 
  130.  
  131. return $result; 
  132.  
  133.  
  134. /** Ajax echo filter */ 
  135.  
  136. add_filter( 'wpcf7_ajax_onload', 'wpcf7_captcha_ajax_refill' ); 
  137. add_filter( 'wpcf7_ajax_json_echo', 'wpcf7_captcha_ajax_refill' ); 
  138.  
  139. function wpcf7_captcha_ajax_refill( $items ) { 
  140. if ( ! is_array( $items ) ) 
  141. return $items; 
  142.  
  143. $fes = wpcf7_scan_form_tags( array( 'type' => 'captchac' ) ); 
  144.  
  145. if ( empty( $fes ) ) 
  146. return $items; 
  147.  
  148. $refill = array(); 
  149.  
  150. foreach ( $fes as $fe ) { 
  151. $name = $fe['name']; 
  152. $options = $fe['options']; 
  153.  
  154. if ( empty( $name ) ) 
  155. continue; 
  156.  
  157. $op = wpcf7_captchac_options( $options ); 
  158. if ( $filename = wpcf7_generate_captcha( $op ) ) { 
  159. $captcha_url = wpcf7_captcha_url( $filename ); 
  160. $refill[$name] = $captcha_url; 
  161.  
  162. if ( ! empty( $refill ) ) 
  163. $items['captcha'] = $refill; 
  164.  
  165. return $items; 
  166.  
  167.  
  168. /** Messages */ 
  169.  
  170. add_filter( 'wpcf7_messages', 'wpcf7_captcha_messages' ); 
  171.  
  172. function wpcf7_captcha_messages( $messages ) { 
  173. return array_merge( $messages, array( 'captcha_not_match' => array( 
  174. 'description' => __( "The code that sender entered does not match the CAPTCHA", 'contact-form-7' ),  
  175. 'default' => __( 'Your entered code is incorrect.', 'contact-form-7' ) 
  176. ) ) ); 
  177.  
  178.  
  179. /** Tag generator */ 
  180.  
  181. add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_captcha', 46 ); 
  182.  
  183. function wpcf7_add_tag_generator_captcha() { 
  184. if ( ! wpcf7_use_really_simple_captcha() ) { 
  185. return; 
  186.  
  187. $tag_generator = WPCF7_TagGenerator::get_instance(); 
  188. $tag_generator->add( 'captcha',  
  189. __( 'CAPTCHA (Really Simple CAPTCHA)', 'contact-form-7' ),  
  190. 'wpcf7_tag_generator_captcha' ); 
  191.  
  192. function wpcf7_tag_generator_captcha( $contact_form, $args = '' ) { 
  193. $args = wp_parse_args( $args, array() ); 
  194.  
  195. if ( ! class_exists( 'ReallySimpleCaptcha' ) ) { 
  196. ?> 
  197. <div class="control-box"> 
  198. <fieldset> 
  199. <legend><?php echo sprintf( esc_html( __( "To use CAPTCHA, you first need to install and activate %s plugin.", 'contact-form-7' ) ), wpcf7_link( 'http://wordpress.org/extend/plugins/really-simple-captcha/', 'Really Simple CAPTCHA' ) ); ?></legend> 
  200. </fieldset> 
  201. </div> 
  202. <?php 
  203.  
  204. return; 
  205.  
  206. $description = __( "Generate form-tags for a CAPTCHA image and corresponding response input field. For more details, see %s.", 'contact-form-7' ); 
  207.  
  208. $desc_link = wpcf7_link( __( 'https://contactform7.com/captcha/', 'contact-form-7' ), __( 'CAPTCHA', 'contact-form-7' ) ); 
  209.  
  210. ?> 
  211. <div class="control-box"> 
  212. <fieldset> 
  213. <legend><?php echo sprintf( esc_html( $description ), $desc_link ); ?></legend> 
  214.  
  215. <table class="form-table"> 
  216. <tbody> 
  217. <tr> 
  218. <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-name' ); ?>"><?php echo esc_html( __( 'Name', 'contact-form-7' ) ); ?></label></th> 
  219. <td><input type="text" name="name" class="tg-name oneline" id="<?php echo esc_attr( $args['content'] . '-name' ); ?>" /></td> 
  220. </tr> 
  221. </tbody> 
  222. </table> 
  223.  
  224. <table class="form-table scope captchac"> 
  225. <caption><?php echo esc_html( __( "Image settings", 'contact-form-7' ) ); ?></caption> 
  226. <tbody> 
  227. <tr> 
  228. <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-captchac-id' ); ?>"><?php echo esc_html( __( 'Id attribute', 'contact-form-7' ) ); ?></label></th> 
  229. <td><input type="text" name="id" class="idvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-captchac-id' ); ?>" /></td> 
  230. </tr> 
  231.  
  232. <tr> 
  233. <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-captchac-class' ); ?>"><?php echo esc_html( __( 'Class attribute', 'contact-form-7' ) ); ?></label></th> 
  234. <td><input type="text" name="class" class="classvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-captchac-class' ); ?>" /></td> 
  235. </tr> 
  236. </tbody> 
  237. </table> 
  238.  
  239. <table class="form-table scope captchar"> 
  240. <caption><?php echo esc_html( __( "Input field settings", 'contact-form-7' ) ); ?></caption> 
  241. <tbody> 
  242. <tr> 
  243. <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-captchar-id' ); ?>"><?php echo esc_html( __( 'Id attribute', 'contact-form-7' ) ); ?></label></th> 
  244. <td><input type="text" name="id" class="idvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-captchar-id' ); ?>" /></td> 
  245. </tr> 
  246.  
  247. <tr> 
  248. <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-captchar-class' ); ?>"><?php echo esc_html( __( 'Class attribute', 'contact-form-7' ) ); ?></label></th> 
  249. <td><input type="text" name="class" class="classvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-captchar-class' ); ?>" /></td> 
  250. </tr> 
  251. </tbody> 
  252. </table> 
  253. </fieldset> 
  254. </div> 
  255.  
  256. <div class="insert-box"> 
  257. <input type="text" name="captcha" class="tag code" readonly="readonly" onfocus="this.select()" /> 
  258.  
  259. <div class="submitbox"> 
  260. <input type="button" class="button button-primary insert-tag" value="<?php echo esc_attr( __( 'Insert Tag', 'contact-form-7' ) ); ?>" /> 
  261. </div> 
  262. </div> 
  263. <?php 
  264.  
  265.  
  266. /** Warning message */ 
  267.  
  268. add_action( 'wpcf7_admin_warnings', 'wpcf7_captcha_display_warning_message' ); 
  269.  
  270. function wpcf7_captcha_display_warning_message() { 
  271. if ( ! $contact_form = wpcf7_get_current_contact_form() ) { 
  272. return; 
  273.  
  274. $has_tags = (bool) $contact_form->scan_form_tags( 
  275. array( 'type' => array( 'captchac' ) ) ); 
  276.  
  277. if ( ! $has_tags ) { 
  278. return; 
  279.  
  280. if ( ! class_exists( 'ReallySimpleCaptcha' ) ) { 
  281. return; 
  282.  
  283. $uploads_dir = wpcf7_captcha_tmp_dir(); 
  284. wpcf7_init_captcha(); 
  285.  
  286. if ( ! is_dir( $uploads_dir ) || ! wp_is_writable( $uploads_dir ) ) { 
  287. $message = sprintf( __( 'This contact form contains CAPTCHA fields, but the temporary folder for the files (%s) does not exist or is not writable. You can create the folder or change its permission manually.', 'contact-form-7' ), $uploads_dir ); 
  288.  
  289. echo '<div class="notice notice-warning"><p>' . esc_html( $message ) . '</p></div>'; 
  290.  
  291. if ( ! function_exists( 'imagecreatetruecolor' ) || ! function_exists( 'imagettftext' ) ) { 
  292. $message = __( 'This contact form contains CAPTCHA fields, but the necessary libraries (GD and FreeType) are not available on your server.', 'contact-form-7' ); 
  293.  
  294. echo '<div class="notice notice-warning"><p>' . esc_html( $message ) . '</p></div>'; 
  295.  
  296.  
  297. /** CAPTCHA functions */ 
  298.  
  299. function wpcf7_init_captcha() { 
  300. static $captcha = null; 
  301.  
  302. if ( $captcha ) { 
  303. return $captcha; 
  304.  
  305. if ( class_exists( 'ReallySimpleCaptcha' ) ) { 
  306. $captcha = new ReallySimpleCaptcha(); 
  307. } else { 
  308. return false; 
  309.  
  310. $dir = trailingslashit( wpcf7_captcha_tmp_dir() ); 
  311.  
  312. $captcha->tmp_dir = $dir; 
  313.  
  314. if ( is_callable( array( $captcha, 'make_tmp_dir' ) ) ) { 
  315. $result = $captcha->make_tmp_dir(); 
  316.  
  317. if ( ! $result ) { 
  318. return false; 
  319.  
  320. return $captcha; 
  321.  
  322. if ( wp_mkdir_p( $dir ) ) { 
  323. $htaccess_file = $dir . '.htaccess'; 
  324.  
  325. if ( file_exists( $htaccess_file ) ) { 
  326. return $captcha; 
  327.  
  328. if ( $handle = @fopen( $htaccess_file, 'w' ) ) { 
  329. fwrite( $handle, 'Order deny, allow' . "\n" ); 
  330. fwrite( $handle, 'Deny from all' . "\n" ); 
  331. fwrite( $handle, '<Files ~ "^[0-9A-Za-z]+\\.(jpeg|gif|png)$">' . "\n" ); 
  332. fwrite( $handle, ' Allow from all' . "\n" ); 
  333. fwrite( $handle, '</Files>' . "\n" ); 
  334. fclose( $handle ); 
  335. } else { 
  336. return false; 
  337.  
  338. return $captcha; 
  339.  
  340. function wpcf7_captcha_tmp_dir() { 
  341. if ( defined( 'WPCF7_CAPTCHA_TMP_DIR' ) ) 
  342. return WPCF7_CAPTCHA_TMP_DIR; 
  343. else 
  344. return wpcf7_upload_dir( 'dir' ) . '/wpcf7_captcha'; 
  345.  
  346. function wpcf7_captcha_tmp_url() { 
  347. if ( defined( 'WPCF7_CAPTCHA_TMP_URL' ) ) 
  348. return WPCF7_CAPTCHA_TMP_URL; 
  349. else 
  350. return wpcf7_upload_dir( 'url' ) . '/wpcf7_captcha'; 
  351.  
  352. function wpcf7_captcha_url( $filename ) { 
  353. $url = trailingslashit( wpcf7_captcha_tmp_url() ) . $filename; 
  354.  
  355. if ( is_ssl() && 'http:' == substr( $url, 0, 5 ) ) { 
  356. $url = 'https:' . substr( $url, 5 ); 
  357.  
  358. return apply_filters( 'wpcf7_captcha_url', esc_url_raw( $url ) ); 
  359.  
  360. function wpcf7_generate_captcha( $options = null ) { 
  361. if ( ! $captcha = wpcf7_init_captcha() ) { 
  362. return false; 
  363.  
  364. if ( ! is_dir( $captcha->tmp_dir ) || ! wp_is_writable( $captcha->tmp_dir ) ) 
  365. return false; 
  366.  
  367. $img_type = imagetypes(); 
  368. if ( $img_type & IMG_PNG ) 
  369. $captcha->img_type = 'png'; 
  370. elseif ( $img_type & IMG_GIF ) 
  371. $captcha->img_type = 'gif'; 
  372. elseif ( $img_type & IMG_JPG ) 
  373. $captcha->img_type = 'jpeg'; 
  374. else 
  375. return false; 
  376.  
  377. if ( is_array( $options ) ) { 
  378. if ( isset( $options['img_size'] ) ) 
  379. $captcha->img_size = $options['img_size']; 
  380. if ( isset( $options['base'] ) ) 
  381. $captcha->base = $options['base']; 
  382. if ( isset( $options['font_size'] ) ) 
  383. $captcha->font_size = $options['font_size']; 
  384. if ( isset( $options['font_char_width'] ) ) 
  385. $captcha->font_char_width = $options['font_char_width']; 
  386. if ( isset( $options['fg'] ) ) 
  387. $captcha->fg = $options['fg']; 
  388. if ( isset( $options['bg'] ) ) 
  389. $captcha->bg = $options['bg']; 
  390.  
  391. $prefix = wp_rand(); 
  392. $captcha_word = $captcha->generate_random_word(); 
  393. return $captcha->generate_image( $prefix, $captcha_word ); 
  394.  
  395. function wpcf7_check_captcha( $prefix, $response ) { 
  396. if ( ! $captcha = wpcf7_init_captcha() ) { 
  397. return false; 
  398.  
  399. return $captcha->check( $prefix, $response ); 
  400.  
  401. function wpcf7_remove_captcha( $prefix ) { 
  402. if ( ! $captcha = wpcf7_init_captcha() ) { 
  403. return false; 
  404.  
  405. if ( preg_match( '/[^0-9]/', $prefix ) ) // Contact Form 7 generates $prefix with wp_rand() 
  406. return false; 
  407.  
  408. $captcha->remove( $prefix ); 
  409.  
  410. add_action( 'template_redirect', 'wpcf7_cleanup_captcha_files', 20 ); 
  411.  
  412. function wpcf7_cleanup_captcha_files() { 
  413. if ( ! $captcha = wpcf7_init_captcha() ) { 
  414. return false; 
  415.  
  416. if ( is_callable( array( $captcha, 'cleanup' ) ) ) 
  417. return $captcha->cleanup(); 
  418.  
  419. $dir = trailingslashit( wpcf7_captcha_tmp_dir() ); 
  420.  
  421. if ( ! is_dir( $dir ) || ! is_readable( $dir ) || ! wp_is_writable( $dir ) ) 
  422. return false; 
  423.  
  424. if ( $handle = @opendir( $dir ) ) { 
  425. while ( false !== ( $file = readdir( $handle ) ) ) { 
  426. if ( ! preg_match( '/^[0-9]+\.(php|txt|png|gif|jpeg)$/', $file ) ) 
  427. continue; 
  428.  
  429. $stat = @stat( $dir . $file ); 
  430. if ( $stat['mtime'] + 3600 < time() ) // 3600 secs == 1 hour 
  431. @unlink( $dir . $file ); 
  432. closedir( $handle ); 
  433.  
  434. function wpcf7_captchac_options( $options ) { 
  435. if ( ! is_array( $options ) ) 
  436. return array(); 
  437.  
  438. $op = array(); 
  439. $image_size_array = preg_grep( '%^size:[smlSML]$%', $options ); 
  440.  
  441. if ( $image_size = array_shift( $image_size_array ) ) { 
  442. preg_match( '%^size:([smlSML])$%', $image_size, $is_matches ); 
  443. switch ( strtolower( $is_matches[1] ) ) { 
  444. case 's': 
  445. $op['img_size'] = array( 60, 20 ); 
  446. $op['base'] = array( 6, 15 ); 
  447. $op['font_size'] = 11; 
  448. $op['font_char_width'] = 13; 
  449. break; 
  450. case 'l': 
  451. $op['img_size'] = array( 84, 28 ); 
  452. $op['base'] = array( 6, 20 ); 
  453. $op['font_size'] = 17; 
  454. $op['font_char_width'] = 19; 
  455. break; 
  456. case 'm': 
  457. default: 
  458. $op['img_size'] = array( 72, 24 ); 
  459. $op['base'] = array( 6, 18 ); 
  460. $op['font_size'] = 14; 
  461. $op['font_char_width'] = 15; 
  462.  
  463. $fg_color_array = preg_grep( '%^fg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', $options ); 
  464. if ( $fg_color = array_shift( $fg_color_array ) ) { 
  465. preg_match( '%^fg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', $fg_color, $fc_matches ); 
  466. if ( 3 == strlen( $fc_matches[1] ) ) { 
  467. $r = substr( $fc_matches[1], 0, 1 ); 
  468. $g = substr( $fc_matches[1], 1, 1 ); 
  469. $b = substr( $fc_matches[1], 2, 1 ); 
  470. $op['fg'] = array( hexdec( $r . $r ), hexdec( $g . $g ), hexdec( $b . $b ) ); 
  471. } elseif ( 6 == strlen( $fc_matches[1] ) ) { 
  472. $r = substr( $fc_matches[1], 0, 2 ); 
  473. $g = substr( $fc_matches[1], 2, 2 ); 
  474. $b = substr( $fc_matches[1], 4, 2 ); 
  475. $op['fg'] = array( hexdec( $r ), hexdec( $g ), hexdec( $b ) ); 
  476.  
  477. $bg_color_array = preg_grep( '%^bg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', $options ); 
  478. if ( $bg_color = array_shift( $bg_color_array ) ) { 
  479. preg_match( '%^bg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', $bg_color, $bc_matches ); 
  480. if ( 3 == strlen( $bc_matches[1] ) ) { 
  481. $r = substr( $bc_matches[1], 0, 1 ); 
  482. $g = substr( $bc_matches[1], 1, 1 ); 
  483. $b = substr( $bc_matches[1], 2, 1 ); 
  484. $op['bg'] = array( hexdec( $r . $r ), hexdec( $g . $g ), hexdec( $b . $b ) ); 
  485. } elseif ( 6 == strlen( $bc_matches[1] ) ) { 
  486. $r = substr( $bc_matches[1], 0, 2 ); 
  487. $g = substr( $bc_matches[1], 2, 2 ); 
  488. $b = substr( $bc_matches[1], 4, 2 ); 
  489. $op['bg'] = array( hexdec( $r ), hexdec( $g ), hexdec( $b ) ); 
  490.  
  491. return $op; 
.