/modules/recaptcha.php

  1. <?php 
  2.  
  3. class WPCF7_RECAPTCHA extends WPCF7_Service { 
  4.  
  5. const VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'; 
  6.  
  7. private static $instance; 
  8. private $sitekeys; 
  9.  
  10. public static function get_instance() { 
  11. if ( empty( self::$instance ) ) { 
  12. self::$instance = new self; 
  13.  
  14. return self::$instance; 
  15.  
  16. private function __construct() { 
  17. $this->sitekeys = WPCF7::get_option( 'recaptcha' ); 
  18.  
  19. public function get_title() { 
  20. return __( 'reCAPTCHA', 'contact-form-7' ); 
  21.  
  22. public function is_active() { 
  23. $sitekey = $this->get_sitekey(); 
  24. $secret = $this->get_secret( $sitekey ); 
  25. return $sitekey && $secret; 
  26.  
  27. public function get_categories() { 
  28. return array( 'captcha' ); 
  29.  
  30. public function icon() { 
  31.  
  32. public function link() { 
  33. echo sprintf( '<a href="%1$s">%2$s</a>',  
  34. 'https://www.google.com/recaptcha/intro/index.html',  
  35. 'google.com/recaptcha' ); 
  36.  
  37. public function get_sitekey() { 
  38. if ( empty( $this->sitekeys ) || ! is_array( $this->sitekeys ) ) { 
  39. return false; 
  40.  
  41. $sitekeys = array_keys( $this->sitekeys ); 
  42.  
  43. return $sitekeys[0]; 
  44.  
  45. public function get_secret( $sitekey ) { 
  46. $sitekeys = (array) $this->sitekeys; 
  47.  
  48. if ( isset( $sitekeys[$sitekey] ) ) { 
  49. return $sitekeys[$sitekey]; 
  50. } else { 
  51. return false; 
  52.  
  53. public function verify( $response_token ) { 
  54. $is_human = false; 
  55.  
  56. if ( empty( $response_token ) ) { 
  57. return $is_human; 
  58.  
  59. $url = self::VERIFY_URL; 
  60. $sitekey = $this->get_sitekey(); 
  61. $secret = $this->get_secret( $sitekey ); 
  62.  
  63. $response = wp_safe_remote_post( $url, array( 
  64. 'body' => array( 
  65. 'secret' => $secret,  
  66. 'response' => $response_token,  
  67. 'remoteip' => $_SERVER['REMOTE_ADDR'] ) ) ); 
  68.  
  69. if ( 200 != wp_remote_retrieve_response_code( $response ) ) { 
  70. return $is_human; 
  71.  
  72. $response = wp_remote_retrieve_body( $response ); 
  73. $response = json_decode( $response, true ); 
  74.  
  75. $is_human = isset( $response['success'] ) && true == $response['success']; 
  76. return $is_human; 
  77.  
  78. private function menu_page_url( $args = '' ) { 
  79. $args = wp_parse_args( $args, array() ); 
  80.  
  81. $url = menu_page_url( 'wpcf7-integration', false ); 
  82. $url = add_query_arg( array( 'service' => 'recaptcha' ), $url ); 
  83.  
  84. if ( ! empty( $args) ) { 
  85. $url = add_query_arg( $args, $url ); 
  86.  
  87. return $url; 
  88.  
  89. public function load( $action = '' ) { 
  90. if ( 'setup' == $action ) { 
  91. if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) { 
  92. check_admin_referer( 'wpcf7-recaptcha-setup' ); 
  93.  
  94. $sitekey = isset( $_POST['sitekey'] ) ? trim( $_POST['sitekey'] ) : ''; 
  95. $secret = isset( $_POST['secret'] ) ? trim( $_POST['secret'] ) : ''; 
  96.  
  97. if ( $sitekey && $secret ) { 
  98. WPCF7::update_option( 'recaptcha', array( $sitekey => $secret ) ); 
  99. $redirect_to = $this->menu_page_url( array( 
  100. 'message' => 'success' ) ); 
  101. } elseif ( '' === $sitekey && '' === $secret ) { 
  102. WPCF7::update_option( 'recaptcha', null ); 
  103. $redirect_to = $this->menu_page_url( array( 
  104. 'message' => 'success' ) ); 
  105. } else { 
  106. $redirect_to = $this->menu_page_url( array( 
  107. 'action' => 'setup',  
  108. 'message' => 'invalid' ) ); 
  109.  
  110. wp_safe_redirect( $redirect_to ); 
  111. exit(); 
  112.  
  113. public function admin_notice( $message = '' ) { 
  114. if ( 'invalid' == $message ) { 
  115. echo sprintf( 
  116. '<div class="error notice notice-error is-dismissible"><p><strong>%1$s</strong>: %2$s</p></div>',  
  117. esc_html( __( "ERROR", 'contact-form-7' ) ),  
  118. esc_html( __( "Invalid key values.", 'contact-form-7' ) ) ); 
  119.  
  120. if ( 'success' == $message ) { 
  121. echo sprintf( '<div class="updated notice notice-success is-dismissible"><p>%s</p></div>',  
  122. esc_html( __( 'Settings saved.', 'contact-form-7' ) ) ); 
  123.  
  124. public function display( $action = '' ) { 
  125. ?> 
  126. <p><?php echo esc_html( __( "reCAPTCHA is a free service to protect your website from spam and abuse.", 'contact-form-7' ) ); ?></p> 
  127.  
  128. <?php 
  129. if ( 'setup' == $action ) { 
  130. $this->display_setup(); 
  131. return; 
  132.  
  133. if ( $this->is_active() ) { 
  134. $sitekey = $this->get_sitekey(); 
  135. $secret = $this->get_secret( $sitekey ); 
  136. ?> 
  137. <table class="form-table"> 
  138. <tbody> 
  139. <tr> 
  140. <th scope="row"><?php echo esc_html( __( 'Site Key', 'contact-form-7' ) ); ?></th> 
  141. <td class="code"><?php echo esc_html( $sitekey ); ?></td> 
  142. </tr> 
  143. <tr> 
  144. <th scope="row"><?php echo esc_html( __( 'Secret Key', 'contact-form-7' ) ); ?></th> 
  145. <td class="code"><?php echo esc_html( wpcf7_mask_password( $secret ) ); ?></td> 
  146. </tr> 
  147. </tbody> 
  148. </table> 
  149.  
  150. <p><a href="<?php echo esc_url( $this->menu_page_url( 'action=setup' ) ); ?>" class="button"><?php echo esc_html( __( "Reset Keys", 'contact-form-7' ) ); ?></a></p> 
  151.  
  152. <?php 
  153. } else { 
  154. ?> 
  155. <p><?php echo esc_html( __( "To use reCAPTCHA, you need to install an API key pair.", 'contact-form-7' ) ); ?></p> 
  156.  
  157. <p><a href="<?php echo esc_url( $this->menu_page_url( 'action=setup' ) ); ?>" class="button"><?php echo esc_html( __( "Configure Keys", 'contact-form-7' ) ); ?></a></p> 
  158.  
  159. <p><?php echo sprintf( esc_html( __( "For more details, see %s.", 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/recaptcha/', 'contact-form-7' ), __( 'reCAPTCHA', 'contact-form-7' ) ) ); ?></p> 
  160. <?php 
  161.  
  162. public function display_setup() { 
  163. ?> 
  164. <form method="post" action="<?php echo esc_url( $this->menu_page_url( 'action=setup' ) ); ?>"> 
  165. <?php wp_nonce_field( 'wpcf7-recaptcha-setup' ); ?> 
  166. <table class="form-table"> 
  167. <tbody> 
  168. <tr> 
  169. <th scope="row"><label for="sitekey"><?php echo esc_html( __( 'Site Key', 'contact-form-7' ) ); ?></label></th> 
  170. <td><input type="text" aria-required="true" value="" id="sitekey" name="sitekey" class="regular-text code" /></td> 
  171. </tr> 
  172. <tr> 
  173. <th scope="row"><label for="secret"><?php echo esc_html( __( 'Secret Key', 'contact-form-7' ) ); ?></label></th> 
  174. <td><input type="text" aria-required="true" value="" id="secret" name="secret" class="regular-text code" /></td> 
  175. </tr> 
  176. </tbody> 
  177. </table> 
  178.  
  179. <p class="submit"><input type="submit" class="button button-primary" value="<?php echo esc_attr( __( 'Save', 'contact-form-7' ) ); ?>" name="submit" /></p> 
  180. </form> 
  181. <?php 
  182.  
  183. add_action( 'wpcf7_init', 'wpcf7_recaptcha_register_service' ); 
  184.  
  185. function wpcf7_recaptcha_register_service() { 
  186. $integration = WPCF7_Integration::get_instance(); 
  187.  
  188. $categories = array( 
  189. 'captcha' => __( 'CAPTCHA', 'contact-form-7' ) ); 
  190.  
  191. foreach ( $categories as $name => $category ) { 
  192. $integration->add_category( $name, $category ); 
  193.  
  194. $services = array( 
  195. 'recaptcha' => WPCF7_RECAPTCHA::get_instance() ); 
  196.  
  197. foreach ( $services as $name => $service ) { 
  198. $integration->add_service( $name, $service ); 
  199.  
  200. add_action( 'wpcf7_enqueue_scripts', 'wpcf7_recaptcha_enqueue_scripts' ); 
  201.  
  202. function wpcf7_recaptcha_enqueue_scripts() { 
  203. $url = 'https://www.google.com/recaptcha/api.js'; 
  204. $url = add_query_arg( array( 
  205. 'onload' => 'recaptchaCallback',  
  206. 'render' => 'explicit' ), $url ); 
  207.  
  208. wp_register_script( 'google-recaptcha', $url, array(), '2.0', true ); 
  209.  
  210. add_action( 'wp_footer', 'wpcf7_recaptcha_callback_script' ); 
  211.  
  212. function wpcf7_recaptcha_callback_script() { 
  213. if ( ! wp_script_is( 'google-recaptcha', 'enqueued' ) ) { 
  214. return; 
  215.  
  216. ?> 
  217. <script type="text/javascript"> 
  218. var recaptchaWidgets = []; 
  219. var recaptchaCallback = function() { 
  220. var forms = document.getElementsByTagName('form'); 
  221. var pattern = /(^|\s)g-recaptcha(\s|$)/; 
  222.  
  223. for (var i = 0; i < forms.length; i++) { 
  224. var divs = forms[i].getElementsByTagName('div'); 
  225.  
  226. for (var j = 0; j < divs.length; j++) { 
  227. var sitekey = divs[j].getAttribute('data-sitekey'); 
  228.  
  229. if (divs[j].className && divs[j].className.match(pattern) && sitekey) { 
  230. var params = { 
  231. 'sitekey': sitekey,  
  232. 'theme': divs[j].getAttribute('data-theme'),  
  233. 'type': divs[j].getAttribute('data-type'),  
  234. 'size': divs[j].getAttribute('data-size'),  
  235. 'tabindex': divs[j].getAttribute('data-tabindex') 
  236. }; 
  237.  
  238. var callback = divs[j].getAttribute('data-callback'); 
  239.  
  240. if (callback && 'function' == typeof window[callback]) { 
  241. params['callback'] = window[callback]; 
  242.  
  243. var expired_callback = divs[j].getAttribute('data-expired-callback'); 
  244.  
  245. if (expired_callback && 'function' == typeof window[expired_callback]) { 
  246. params['expired-callback'] = window[expired_callback]; 
  247.  
  248. var widget_id = grecaptcha.render(divs[j], params); 
  249. recaptchaWidgets.push(widget_id); 
  250. break; 
  251. </script> 
  252. <?php 
  253.  
  254. add_action( 'wpcf7_init', 'wpcf7_recaptcha_add_form_tag_recaptcha' ); 
  255.  
  256. function wpcf7_recaptcha_add_form_tag_recaptcha() { 
  257. $recaptcha = WPCF7_RECAPTCHA::get_instance(); 
  258.  
  259. if ( $recaptcha->is_active() ) { 
  260. wpcf7_add_form_tag( 'recaptcha', 'wpcf7_recaptcha_form_tag_handler' ); 
  261.  
  262. function wpcf7_recaptcha_form_tag_handler( $tag ) { 
  263. if ( ! wp_script_is( 'google-recaptcha', 'registered' ) ) { 
  264. wpcf7_recaptcha_enqueue_scripts(); 
  265.  
  266. wp_enqueue_script( 'google-recaptcha' ); 
  267.  
  268. $tag = new WPCF7_FormTag( $tag ); 
  269.  
  270. $atts = array(); 
  271.  
  272. $recaptcha = WPCF7_RECAPTCHA::get_instance(); 
  273. $atts['data-sitekey'] = $recaptcha->get_sitekey(); 
  274. $atts['data-theme'] = $tag->get_option( 'theme', '(dark|light)', true ); 
  275. $atts['data-type'] = $tag->get_option( 'type', '(audio|image)', true ); 
  276. $atts['data-size'] = $tag->get_option( 'size', '(compact|normal)', true ); 
  277. $atts['data-tabindex'] = $tag->get_option( 'tabindex', 'int', true ); 
  278. $atts['data-callback'] = $tag->get_option( 'callback', '', true ); 
  279. $atts['data-expired-callback'] = 
  280. $tag->get_option( 'expired_callback', '', true ); 
  281.  
  282. $atts['class'] = $tag->get_class_option( 
  283. wpcf7_form_controls_class( $tag->type, 'g-recaptcha' ) ); 
  284. $atts['id'] = $tag->get_id_option(); 
  285.  
  286. $html = sprintf( '<div %1$s></div>', wpcf7_format_atts( $atts ) ); 
  287. $html .= wpcf7_recaptcha_noscript( 
  288. array( 'sitekey' => $atts['data-sitekey'] ) ); 
  289. $html = sprintf( '<div class="wpcf7-form-control-wrap">%s</div>', $html ); 
  290.  
  291. return $html; 
  292.  
  293. function wpcf7_recaptcha_noscript( $args = '' ) { 
  294. $args = wp_parse_args( $args, array( 
  295. 'sitekey' => '' ) ); 
  296.  
  297. if ( empty( $args['sitekey'] ) ) { 
  298. return; 
  299.  
  300. $url = add_query_arg( 'k', $args['sitekey'],  
  301. 'https://www.google.com/recaptcha/api/fallback' ); 
  302.  
  303. ob_start(); 
  304. ?> 
  305.  
  306. <noscript> 
  307. <div style="width: 302px; height: 422px;"> 
  308. <div style="width: 302px; height: 422px; position: relative;"> 
  309. <div style="width: 302px; height: 422px; position: absolute;"> 
  310. <iframe src="<?php echo esc_url( $url ); ?>" frameborder="0" scrolling="no" style="width: 302px; height:422px; border-style: none;"> 
  311. </iframe> 
  312. </div> 
  313. <div style="width: 300px; height: 60px; border-style: none; bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px; background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px;"> 
  314. <textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px; height: 40px; border: 1px solid #c1c1c1; margin: 10px 25px; padding: 0px; resize: none;"> 
  315. </textarea> 
  316. </div> 
  317. </div> 
  318. </div> 
  319. </noscript> 
  320. <?php 
  321. return ob_get_clean(); 
  322.  
  323. add_filter( 'wpcf7_spam', 'wpcf7_recaptcha_check_with_google', 9 ); 
  324.  
  325. function wpcf7_recaptcha_check_with_google( $spam ) { 
  326. if ( $spam ) { 
  327. return $spam; 
  328.  
  329. $contact_form = wpcf7_get_current_contact_form(); 
  330.  
  331. if ( ! $contact_form ) { 
  332. return $spam; 
  333.  
  334. $tags = $contact_form->scan_form_tags( array( 'type' => 'recaptcha' ) ); 
  335.  
  336. if ( empty( $tags ) ) { 
  337. return $spam; 
  338.  
  339. $recaptcha = WPCF7_RECAPTCHA::get_instance(); 
  340.  
  341. if ( ! $recaptcha->is_active() ) { 
  342. return $spam; 
  343.  
  344. $response_token = wpcf7_recaptcha_response(); 
  345. $spam = ! $recaptcha->verify( $response_token ); 
  346.  
  347. return $spam; 
  348.  
  349. add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_recaptcha', 45 ); 
  350.  
  351. function wpcf7_add_tag_generator_recaptcha() { 
  352. $tag_generator = WPCF7_TagGenerator::get_instance(); 
  353. $tag_generator->add( 'recaptcha', __( 'reCAPTCHA', 'contact-form-7' ),  
  354. 'wpcf7_tag_generator_recaptcha', array( 'nameless' => 1 ) ); 
  355.  
  356. function wpcf7_tag_generator_recaptcha( $contact_form, $args = '' ) { 
  357. $args = wp_parse_args( $args, array() ); 
  358.  
  359. $recaptcha = WPCF7_RECAPTCHA::get_instance(); 
  360.  
  361. if ( ! $recaptcha->is_active() ) { 
  362. ?> 
  363. <div class="control-box"> 
  364. <fieldset> 
  365. <legend><?php echo sprintf( esc_html( __( "To use reCAPTCHA, first you need to install an API key pair. For more details, see %s.", 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/recaptcha/', 'contact-form-7' ), __( 'reCAPTCHA', 'contact-form-7' ) ) ); ?></legend> 
  366. </fieldset> 
  367. </div> 
  368. <?php 
  369.  
  370. return; 
  371.  
  372. $description = __( "Generate a form-tag for a reCAPTCHA widget. For more details, see %s.", 'contact-form-7' ); 
  373.  
  374. $desc_link = wpcf7_link( __( 'https://contactform7.com/recaptcha/', 'contact-form-7' ), __( 'reCAPTCHA', 'contact-form-7' ) ); 
  375.  
  376. ?> 
  377. <div class="control-box"> 
  378. <fieldset> 
  379. <legend><?php echo sprintf( esc_html( $description ), $desc_link ); ?></legend> 
  380.  
  381. <table class="form-table"> 
  382. <tbody> 
  383. <tr> 
  384. <th scope="row"><?php echo esc_html( __( 'Theme', 'contact-form-7' ) ); ?></th> 
  385. <td> 
  386. <fieldset> 
  387. <legend class="screen-reader-text"><?php echo esc_html( __( 'Theme', 'contact-form-7' ) ); ?></legend> 
  388. <label for="<?php echo esc_attr( $args['content'] . '-theme-light' ); ?>"><input type="radio" name="theme" class="option default" id="<?php echo esc_attr( $args['content'] . '-theme-light' ); ?>" value="light" checked="checked" /> <?php echo esc_html( __( 'Light', 'contact-form-7' ) ); ?></label> 
  389. <br /> 
  390. <label for="<?php echo esc_attr( $args['content'] . '-theme-dark' ); ?>"><input type="radio" name="theme" class="option" id="<?php echo esc_attr( $args['content'] . '-theme-dark' ); ?>" value="dark" /> <?php echo esc_html( __( 'Dark', 'contact-form-7' ) ); ?></label> 
  391. </fieldset> 
  392. </td> 
  393. </tr> 
  394.  
  395. <tr> 
  396. <th scope="row"><?php echo esc_html( __( 'Size', 'contact-form-7' ) ); ?></th> 
  397. <td> 
  398. <fieldset> 
  399. <legend class="screen-reader-text"><?php echo esc_html( __( 'Size', 'contact-form-7' ) ); ?></legend> 
  400. <label for="<?php echo esc_attr( $args['content'] . '-size-normal' ); ?>"><input type="radio" name="size" class="option default" id="<?php echo esc_attr( $args['content'] . '-size-normal' ); ?>" value="normal" checked="checked" /> <?php echo esc_html( __( 'Normal', 'contact-form-7' ) ); ?></label> 
  401. <br /> 
  402. <label for="<?php echo esc_attr( $args['content'] . '-size-compact' ); ?>"><input type="radio" name="size" class="option" id="<?php echo esc_attr( $args['content'] . '-size-compact' ); ?>" value="compact" /> <?php echo esc_html( __( 'Compact', 'contact-form-7' ) ); ?></label> 
  403. </fieldset> 
  404. </td> 
  405. </tr> 
  406.  
  407. <tr> 
  408. <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-id' ); ?>"><?php echo esc_html( __( 'Id attribute', 'contact-form-7' ) ); ?></label></th> 
  409. <td><input type="text" name="id" class="idvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-id' ); ?>" /></td> 
  410. </tr> 
  411.  
  412. <tr> 
  413. <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-class' ); ?>"><?php echo esc_html( __( 'Class attribute', 'contact-form-7' ) ); ?></label></th> 
  414. <td><input type="text" name="class" class="classvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-class' ); ?>" /></td> 
  415. </tr> 
  416.  
  417. </tbody> 
  418. </table> 
  419. </fieldset> 
  420. </div> 
  421.  
  422. <div class="insert-box"> 
  423. <input type="text" name="recaptcha" class="tag code" readonly="readonly" onfocus="this.select()" /> 
  424.  
  425. <div class="submitbox"> 
  426. <input type="button" class="button button-primary insert-tag" value="<?php echo esc_attr( __( 'Insert Tag', 'contact-form-7' ) ); ?>" /> 
  427. </div> 
  428. </div> 
  429. <?php 
  430.  
  431. function wpcf7_recaptcha_response() { 
  432. if ( isset( $_POST['g-recaptcha-response'] ) ) { 
  433. return $_POST['g-recaptcha-response']; 
  434.  
  435. return false; 
.