← All changes
|
includes/core/classes/class-wpfnl-ajax-handler.php
+83
-26
3.12.4
→
3.13.3
View file →
| @@ -23,8 +23,15 @@ | ||
| 23 | 23 | */ |
| 24 | 24 | class Ajax_Handler { |
| 25 | 25 | |
| 26 | 26 | /** |
| 27 | + * Upper bound on notification recipients for a single opt-in submission. | |
| 28 | + * | |
| 29 | + * @since 3.12.14 | |
| 30 | + */ | |
| 31 | + const MAX_NOTIFICATION_RECIPIENTS = 10; | |
| 32 | + | |
| 33 | + /** | |
| 27 | 34 | * Initializes AJAX actions and filters for handling different types of form submissions. |
| 28 | 35 | * Sets up AJAX hooks for various form submission scenarios, including shortcode and Gutenberg submissions. |
| 29 | 36 | * Also hooks into the 'mailmint_after_form_submit_response' filter for processing form submissions. |
| 30 | 37 | */ |
| @@ -47,9 +54,9 @@ | ||
| 47 | 54 | * @return void |
| 48 | 55 | * @since 2.5.7 |
| 49 | 56 | */ |
| 50 | 57 | public function optin_form_submission() { |
| 51 | - // check_ajax_referer( 'optin_form_nonce', 'security' ); | |
| 58 | + check_ajax_referer( 'optin_form_nonce', 'security' ); | |
| 52 | 59 | |
| 53 | 60 | $step_id = isset( $_POST[ 'step_id' ] ) ? $_POST[ 'step_id' ] : ''; |
| 54 | 61 | $postData = isset( $_POST[ 'postData' ] ) ? $_POST[ 'postData' ] : ''; |
| 55 | 62 | |
| @@ -146,9 +153,9 @@ | ||
| 146 | 153 | |
| 147 | 154 | /** |
| 148 | 155 | * User registration |
| 149 | 156 | */ |
| 150 | - if ( !empty( $user_info[ 'optin_allow_registration' ] ) && $user_info[ 'optin_allow_registration' ] == 'yes' ) { | |
| 157 | + if ( !empty( $form[ 'settings' ][ 'allow_registration' ] ) && 'yes' == $form[ 'settings' ][ 'allow_registration' ] ) { | |
| 151 | 158 | |
| 152 | 159 | $this->create_user_optin_allow_registration( $name, $user_info ); |
| 153 | 160 | } |
| 154 | 161 | |
| @@ -155,13 +162,12 @@ | ||
| 155 | 162 | |
| 156 | 163 | /** |
| 157 | 164 | * Recaptcha Check |
| 158 | 165 | */ |
| 159 | - $is_recaptcha = isset( $post_data[ 'wpf-is-recapcha' ] ) ? $post_data[ 'wpf-is-recapcha' ] : 'no'; | |
| 160 | - $recaptcha_site_key = isset( $post_data[ 'wpf-optin-g-token' ] ) ? $post_data[ 'wpf-optin-g-token' ] : ''; | |
| 161 | - $recaptcha_site_secrect = isset( $post_data[ 'wpf-optin-g-secret-key' ] ) ? $post_data[ 'wpf-optin-g-secret-key' ] : ''; | |
| 166 | + $recaptcha = $this->get_trusted_recaptcha_settings(); | |
| 167 | + $recaptcha_response = isset( $post_data[ 'wpf-optin-g-token' ] ) ? $post_data[ 'wpf-optin-g-token' ] : ''; | |
| 162 | 168 | |
| 163 | - $response = $this->send_mail_after_checking_google_recaptcha_response( $is_recaptcha, $recaptcha_site_key, $recaptcha_site_secrect, $admin_email, $admin_email_subject, $user_info, $response ); | |
| 169 | + $response = $this->send_mail_after_checking_google_recaptcha_response( $recaptcha[ 'enabled' ], $recaptcha_response, $recaptcha[ 'secret' ], $admin_email, $admin_email_subject, $user_info, $response ); | |
| 164 | 170 | |
| 165 | 171 | if( \WPFunnels\Integrations\Helper::maybe_enabled() && !empty($form[ 'settings' ]['enable_mm_contact']) && 'yes' === $form[ 'settings' ]['enable_mm_contact'] ){ |
| 166 | 172 | $lists = []; |
| 167 | 173 | $tags = []; |
| @@ -256,12 +262,12 @@ | ||
| 256 | 262 | * @since @since 2.5.7 |
| 257 | 263 | */ |
| 258 | 264 | public function wpfnl_shortcode_optin_submission( $response = [] ) { |
| 259 | 265 | $post = filter_input_array( INPUT_POST ); |
| 260 | - // if ( !wp_verify_nonce( htmlspecialchars( $post[ 'security' ] ?? '' ), 'optin_form_nonce' ) | |
| 261 | - // && !wp_verify_nonce( htmlspecialchars( $post[ 'wp_nonce' ] ?? '' ), 'wp_rest' ) ) { | |
| 262 | - // die( '-1' ); | |
| 263 | - // } | |
| 266 | + if ( !wp_verify_nonce( htmlspecialchars( $post[ 'security' ] ?? '' ), 'optin_form_nonce' ) | |
| 267 | + && !wp_verify_nonce( htmlspecialchars( $post[ 'wp_nonce' ] ?? '' ), 'wp_rest' ) ) { | |
| 268 | + wp_send_json_error( array( 'message' => __( 'Unauthorized request', 'wpfnl' ) ), 403 ); | |
| 269 | + } | |
| 264 | 270 | |
| 265 | 271 | $return = true; |
| 266 | 272 | if ( empty( $response ) ) { |
| 267 | 273 | $response = []; |
| @@ -498,18 +504,49 @@ | ||
| 498 | 504 | * @return array The updated response after processing reCAPTCHA and sending the email. |
| 499 | 505 | * @since 2.8.2 |
| 500 | 506 | */ |
| 501 | 507 | private function process_recaptcha( $post_data, $response, $user_info ) { |
| 502 | - $admin_email = $post_data[ 'admin_email' ] ?? false; | |
| 503 | - $admin_email_subject = $post_data[ 'admin_email_subject' ] ?? 'Opt-in form Submission'; | |
| 504 | - $is_recaptcha = $post_data[ 'wpf-is-recapcha' ] ?? 'false'; | |
| 505 | - $recaptcha_site_key = $post_data[ 'wpf-optin-g-token' ] ?? ''; | |
| 506 | - $recaptcha_site_secrect = $post_data[ 'wpf-optin-g-secret-key' ] ?? ''; | |
| 508 | + $admin_email = $post_data[ 'admin_email' ] ?? false; | |
| 509 | + $admin_email_subject = $post_data[ 'admin_email_subject' ] ?? 'Opt-in form Submission'; | |
| 510 | + $allow_registration = $post_data[ 'optin_allow_registration' ] ?? ''; | |
| 511 | + $token = $post_data[ 'wpfnl_optin_settings_token' ] ?? ''; | |
| 507 | 512 | |
| 508 | - return $this->send_mail_after_checking_google_recaptcha_response( $is_recaptcha, $recaptcha_site_key, $recaptcha_site_secrect, $admin_email, $admin_email_subject, $user_info, $response ); | |
| 513 | + // The recipient and subject are hidden inputs, so they come back as request | |
| 514 | + // data. Honour them only when they are still the ones this form was | |
| 515 | + // rendered with; otherwise there is no notification to send. | |
| 516 | + if ( ! Wpfnl_functions::is_valid_optin_settings_token( $token, (string) $admin_email, (string) $admin_email_subject, (string) $allow_registration ) ) { | |
| 517 | + $admin_email = false; | |
| 518 | + $admin_email_subject = false; | |
| 519 | + } | |
| 520 | + | |
| 521 | + $recaptcha = $this->get_trusted_recaptcha_settings(); | |
| 522 | + $recaptcha_response = $post_data[ 'wpf-optin-g-token' ] ?? ''; | |
| 523 | + | |
| 524 | + return $this->send_mail_after_checking_google_recaptcha_response( $recaptcha[ 'enabled' ], $recaptcha_response, $recaptcha[ 'secret' ], $admin_email, $admin_email_subject, $user_info, $response ); | |
| 509 | 525 | } |
| 510 | 526 | |
| 511 | 527 | /** |
| 528 | + * reCAPTCHA configuration as stored for the site. | |
| 529 | + * | |
| 530 | + * Never read the switch or the secret from a submission: both are rendered | |
| 531 | + * into the form, so a caller can omit the switch or substitute keys for a | |
| 532 | + * challenge they control. | |
| 533 | + * | |
| 534 | + * @return array | |
| 535 | + * @since 3.12.14 | |
| 536 | + */ | |
| 537 | + private function get_trusted_recaptcha_settings() { | |
| 538 | + $settings = Wpfnl_functions::get_recaptcha_settings(); | |
| 539 | + $enabled = isset( $settings[ 'enable_recaptcha' ] ) ? $settings[ 'enable_recaptcha' ] : 'no'; | |
| 540 | + $secret = isset( $settings[ 'recaptcha_site_secret' ] ) ? $settings[ 'recaptcha_site_secret' ] : ''; | |
| 541 | + | |
| 542 | + return array( | |
| 543 | + 'enabled' => ( 'on' === $enabled && $secret ) ? 'on' : 'off', | |
| 544 | + 'secret' => $secret, | |
| 545 | + ); | |
| 546 | + } | |
| 547 | + | |
| 548 | + /** | |
| 512 | 549 | * Gutenberg opt-in form handling |
| 513 | 550 | * |
| 514 | 551 | * @return void |
| 515 | 552 | * @since 2.5.9 |
| @@ -613,9 +650,9 @@ | ||
| 613 | 650 | } |
| 614 | 651 | /** |
| 615 | 652 | * User registration |
| 616 | 653 | */ |
| 617 | - if ( !empty( $post_data[ 'optin_allow_registration' ] ) && $post_data[ 'optin_allow_registration' ] == 'yes' ) { | |
| 654 | + if ( !empty( $block_attr[ 'registration_checkbox' ] ) ) { | |
| 618 | 655 | |
| 619 | 656 | $this->create_user_optin_allow_registration( $name, $post_data ); |
| 620 | 657 | } |
| 621 | 658 | /** |
| @@ -620,13 +657,12 @@ | ||
| 620 | 657 | } |
| 621 | 658 | /** |
| 622 | 659 | * Check google Recaptcha V3 |
| 623 | 660 | */ |
| 624 | - $is_recaptcha = isset( $post_data[ 'wpf-is-recapcha' ] ) ? $post_data[ 'wpf-is-recapcha' ] : 'false'; | |
| 625 | - $recaptcha_site_key = isset( $post_data[ 'wpf-optin-g-token' ] ) ? $post_data[ 'wpf-optin-g-token' ] : ''; | |
| 626 | - $recaptcha_site_secrect = isset( $post_data[ 'wpf-optin-g-secret-key' ] ) ? $post_data[ 'wpf-optin-g-secret-key' ] : ''; | |
| 661 | + $recaptcha = $this->get_trusted_recaptcha_settings(); | |
| 662 | + $recaptcha_response = isset( $post_data[ 'wpf-optin-g-token' ] ) ? $post_data[ 'wpf-optin-g-token' ] : ''; | |
| 627 | 663 | |
| 628 | - $response = $this->send_mail_after_checking_google_recaptcha_response( $is_recaptcha, $recaptcha_site_key, $recaptcha_site_secrect, $admin_email, $admin_email_subject, $post_data, $response ); | |
| 664 | + $response = $this->send_mail_after_checking_google_recaptcha_response( $recaptcha[ 'enabled' ], $recaptcha_response, $recaptcha[ 'secret' ], $admin_email, $admin_email_subject, $post_data, $response ); | |
| 629 | 665 | |
| 630 | 666 | |
| 631 | 667 | $user_data = [ |
| 632 | 668 | 'first_name' => $post_data['first_name'] ?? '', |
| @@ -734,12 +770,17 @@ | ||
| 734 | 770 | * @since 2.5.9 |
| 735 | 771 | */ |
| 736 | 772 | private function send_email_to_admin( $emails = '', $subject = '', $user_info = [] ) { |
| 737 | 773 | $email_array = explode( ",", $emails ); |
| 774 | + | |
| 775 | + // Bound the fan-out and drop anything that is not an address, so a single | |
| 776 | + // submission cannot be turned into a mail relay. | |
| 777 | + $email_array = array_slice( array_filter( array_map( 'trim', $email_array ) ), 0, self::MAX_NOTIFICATION_RECIPIENTS ); | |
| 778 | + | |
| 738 | 779 | if ( is_array( $email_array ) ) { |
| 739 | 780 | foreach( $email_array as $email ) { |
| 740 | 781 | |
| 741 | - if ( $email ) { | |
| 782 | + if ( $email && is_email( $email ) ) { | |
| 742 | 783 | $email = trim( $email ); |
| 743 | 784 | $current_date = date( "d M Y" ); |
| 744 | 785 | $time = new \DateTimeImmutable( 'now', wp_timezone() ); |
| 745 | 786 | $current_time = $time->format( "h:i A" ); |
| @@ -745,18 +786,22 @@ | ||
| 745 | 786 | $current_time = $time->format( "h:i A" ); |
| 746 | 787 | $poweredBy = __( 'WPFunnels', 'wpfnl' ); |
| 747 | 788 | $info = ''; |
| 748 | 789 | if ( isset( $user_info[ 'first_name' ] ) && $user_info[ 'first_name' ] ) { |
| 749 | - $info .= "First Name : {$user_info['first_name']}<br>"; | |
| 790 | + $esc_first_name = esc_html( $user_info[ 'first_name' ] ); | |
| 791 | + $info .= "First Name : {$esc_first_name}<br>"; | |
| 750 | 792 | } |
| 751 | 793 | if ( isset( $user_info[ 'last_name' ] ) && $user_info[ 'last_name' ] ) { |
| 752 | - $info .= "Last Name : {$user_info['last_name']}<br>"; | |
| 794 | + $esc_last_name = esc_html( $user_info[ 'last_name' ] ); | |
| 795 | + $info .= "Last Name : {$esc_last_name}<br>"; | |
| 753 | 796 | } |
| 754 | 797 | if ( isset( $user_info[ 'email' ] ) && $user_info[ 'email' ] ) { |
| 755 | - $info .= "Email : {$user_info['email']}<br><br>"; | |
| 798 | + $esc_email = esc_html( $user_info[ 'email' ] ); | |
| 799 | + $info .= "Email : {$esc_email}<br><br>"; | |
| 756 | 800 | } |
| 757 | 801 | if ( isset( $user_info[ 'phone' ] ) && $user_info[ 'phone' ] ) { |
| 758 | - $info .= "Phone : {$user_info['phone']}<br><br>"; | |
| 802 | + $esc_phone = esc_html( $user_info[ 'phone' ] ); | |
| 803 | + $info .= "Phone : {$esc_phone}<br><br>"; | |
| 759 | 804 | } |
| 760 | 805 | if ( isset( $user_info[ 'web-url' ] ) && $user_info[ 'web-url' ] ) { |
| 761 | 806 | $esc_url = esc_url( $user_info[ 'web-url' ] ); |
| 762 | 807 | $info .= "Website URL : {$esc_url}<br><br>"; |
| @@ -782,8 +827,16 @@ | ||
| 782 | 827 | /** |
| 783 | 828 | * Create User as a subscriber |
| 784 | 829 | * When allow the subscriber option for registration from builder |
| 785 | 830 | * |
| 831 | + * Registration is refused unless the site itself allows it. Every caller | |
| 832 | + * routes through here - the Elementor and Gutenberg submission handlers as | |
| 833 | + * well as the after_optin_submit callback - so the site-wide switch is | |
| 834 | + * enforced at this one choke point rather than at each call site, and any | |
| 835 | + * future caller inherits it. On multisite, core filters | |
| 836 | + * option_users_can_register from the network registration setting, so this | |
| 837 | + * is the correct check there too. | |
| 838 | + * | |
| 786 | 839 | * @param $name |
| 787 | 840 | * @param $post_data |
| 788 | 841 | * |
| 789 | 842 | * @since 2.5.9 |
| @@ -788,8 +841,12 @@ | ||
| 788 | 841 | * |
| 789 | 842 | * @since 2.5.9 |
| 790 | 843 | */ |
| 791 | 844 | public static function create_user_optin_allow_registration( $name, $post_data ) { |
| 845 | + if ( ! get_option( 'users_can_register' ) ) { | |
| 846 | + return; | |
| 847 | + } | |
| 848 | + | |
| 792 | 849 | $exist_user_by_username = get_user_by( 'login', $name ); |
| 793 | 850 | $exist_user_by_email = get_user_by( 'email', $post_data[ 'email' ] ); |
| 794 | 851 | |
| 795 | 852 | if ( empty( $exist_user_by_email ) || empty( $exist_user_by_username ) ) { |