| @@ -56,8 +56,85 @@ | ||
| 56 | 56 | return ($GLOBALS['ws_plugin__s2member_plain_text_pass'] = wp_generate_password()); |
| 57 | 57 | } |
| 58 | 58 | |
| 59 | 59 | /** |
| 60 | + * Returns an error when an email domain cannot receive mail. | |
| 61 | + * | |
| 62 | + * @package s2Member\Registrations | |
| 63 | + * @since 261005 | |
| 64 | + * | |
| 65 | + * @param string $email Email address being validated. | |
| 66 | + * | |
| 67 | + * @return string Error message, or an empty string when allowed/not enabled. | |
| 68 | + */ | |
| 69 | + public static function registration_email_domain_validation_error($email = '') | |
| 70 | + { | |
| 71 | + if(empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['custom_reg_require_valid_email_domain']) || !is_email($email)) | |
| 72 | + return ''; | |
| 73 | + | |
| 74 | + if(self::email_domain_validates($email)) | |
| 75 | + return ''; | |
| 76 | + | |
| 77 | + return _x('Please use an email address with a valid mail domain.', 's2member-front', 's2member'); | |
| 78 | + } | |
| 79 | + | |
| 80 | + /** | |
| 81 | + * Determines whether an email domain can receive mail. | |
| 82 | + * | |
| 83 | + * An MX record is preferred. Without MX, SMTP permits delivery to the domain's | |
| 84 | + * address record, so A/AAAA is accepted as a standards-compatible fallback. | |
| 85 | + * Resolver/runtime failures fail open; an explicit null MX fails closed. | |
| 86 | + * | |
| 87 | + * @package s2Member\Registrations | |
| 88 | + * @since 261005 | |
| 89 | + * | |
| 90 | + * @param string $email Syntactically valid email address. | |
| 91 | + * | |
| 92 | + * @return bool True when mail delivery is possible or DNS cannot be checked safely. | |
| 93 | + */ | |
| 94 | + public static function email_domain_validates($email = '') | |
| 95 | + { | |
| 96 | + $email = trim((string)$email); | |
| 97 | + if(!is_email($email) || !($at = strrpos($email, '@')) || !isset($email[$at + 1])) | |
| 98 | + return TRUE; // Syntax validation belongs to the existing email validator, not this DNS-only check. | |
| 99 | + | |
| 100 | + $domain = strtolower(substr($email, $at + 1)); | |
| 101 | + //261005.0438 Allow deterministic QA/custom resolvers to short-circuit DNS without changing the production fail-open behavior. | |
| 102 | + $pre = apply_filters('ws_plugin__s2member_pre_email_domain_validation', NULL, $domain, $email); | |
| 103 | + if($pre !== NULL) | |
| 104 | + return (bool)$pre; | |
| 105 | + | |
| 106 | + if(!function_exists('dns_get_record')) | |
| 107 | + return TRUE; | |
| 108 | + | |
| 109 | + $mx_records = @dns_get_record($domain, DNS_MX); | |
| 110 | + if($mx_records === FALSE) | |
| 111 | + return TRUE; // Resolver/runtime problem: do not lock out registration. | |
| 112 | + | |
| 113 | + $has_null_mx = FALSE; | |
| 114 | + foreach((array)$mx_records as $_record) | |
| 115 | + if(isset($_record['target'])) | |
| 116 | + { | |
| 117 | + if(strlen(trim((string)$_record['target'], ". \t\n\r\0\x0B"))) | |
| 118 | + return TRUE; | |
| 119 | + $has_null_mx = TRUE; | |
| 120 | + } | |
| 121 | + unset($_record); | |
| 122 | + if($has_null_mx) | |
| 123 | + return FALSE; // Explicit null MX: the domain intentionally does not accept email. | |
| 124 | + | |
| 125 | + $address_records = @dns_get_record($domain, DNS_A | DNS_AAAA); | |
| 126 | + if($address_records === FALSE) | |
| 127 | + return TRUE; // Resolver/runtime problem: fail open. | |
| 128 | + | |
| 129 | + foreach((array)$address_records as $_record) | |
| 130 | + if(!empty($_record['type']) && ($_record['type'] === 'A' || $_record['type'] === 'AAAA')) | |
| 131 | + return TRUE; | |
| 132 | + unset($_record); | |
| 133 | + return FALSE; | |
| 134 | + } | |
| 135 | + | |
| 136 | + /** | |
| 60 | 137 | * Filters WordPress-generated passwords. |
| 61 | 138 | * |
| 62 | 139 | * This can ONLY be fired through `/wp-login.php` on the front-side. |
| 63 | 140 | * Or through `/register` via BuddyPress. |
| @@ -130,8 +207,12 @@ | ||
| 130 | 207 | |
| 131 | 208 | if(!is_admin() && preg_match('/\/wp-login\.php/'.$ci, $_SERVER['REQUEST_URI'])) |
| 132 | 209 | if(is_wp_error($errors) && !empty($_POST) && is_array($_POST)) |
| 133 | 210 | { |
| 211 | + //261005.0438 Validate the core registration email server-side; browser JavaScript cannot reliably inspect DNS. | |
| 212 | + if(($_email_domain_error = self::registration_email_domain_validation_error($user_email))) | |
| 213 | + $errors->add('invalid_email_domain', $_email_domain_error); | |
| 214 | + | |
| 134 | 215 | foreach(c_ws_plugin__s2member_utils_strings::trim_deep(stripslashes_deep($_POST)) as $_key => $_value) |
| 135 | 216 | if(strpos($_key, 'ws_plugin__s2member_custom_reg_field_') === 0) |
| 136 | 217 | $input[str_replace('ws_plugin__s2member_custom_reg_field_', '', $_key)] = $_value; |
| 137 | 218 | |
| @@ -161,23 +242,29 @@ | ||
| 161 | 242 | do_action('ws_plugin__s2member_before_custom_registration_field_errors_4bp', get_defined_vars()); |
| 162 | 243 | unset($__refs, $__v); // Housekeeping. |
| 163 | 244 | |
| 164 | 245 | if(!is_admin() && c_ws_plugin__s2member_utils_conds::bp_is_installed() && bp_is_register_page()) |
| 165 | - if(in_array('registration', $GLOBALS['WS_PLUGIN__']['s2member']['o']['custom_reg_fields_4bp'])) | |
| 166 | - if(apply_filters('ws_plugin__s2member_custom_registration_fields_4bp_display', TRUE, get_defined_vars())) | |
| 167 | - if(!empty($GLOBALS['bp']->signup) && is_object($GLOBALS['bp']->signup) && !empty($_POST) && is_array($_POST)) | |
| 168 | - { | |
| 169 | - foreach(c_ws_plugin__s2member_utils_strings::trim_deep(stripslashes_deep($_POST)) as $_key => $_value) | |
| 170 | - if(strpos($_key, 'ws_plugin__s2member_custom_reg_field_') === 0) | |
| 171 | - $input[str_replace('ws_plugin__s2member_custom_reg_field_', '', $_key)] = $_value; | |
| 246 | + { | |
| 247 | + //261005.0438 On single-site BuddyPress, validate the account email here; Multisite is covered by `wpmu_validate_user_signup` below. | |
| 248 | + if(!is_multisite() && !empty($GLOBALS['bp']->signup) && is_object($GLOBALS['bp']->signup) && !empty($_POST['signup_email']) && ($_email_domain_error = self::registration_email_domain_validation_error(stripslashes((string)$_POST['signup_email'])))) | |
| 249 | + $GLOBALS['bp']->signup->errors['signup_email'] = $_email_domain_error; | |
| 172 | 250 | |
| 173 | - $fields_to_validate = c_ws_plugin__s2member_custom_reg_fields::custom_fields_configured_at_level('auto-detection', 'registration', TRUE); | |
| 174 | - $validation_errors = c_ws_plugin__s2member_custom_reg_fields::validation_errors(!empty($input) ? $input : array(), $fields_to_validate); | |
| 251 | + if(in_array('registration', $GLOBALS['WS_PLUGIN__']['s2member']['o']['custom_reg_fields_4bp'])) | |
| 252 | + if(apply_filters('ws_plugin__s2member_custom_registration_fields_4bp_display', TRUE, get_defined_vars())) | |
| 253 | + if(!empty($GLOBALS['bp']->signup) && is_object($GLOBALS['bp']->signup) && !empty($_POST) && is_array($_POST)) | |
| 254 | + { | |
| 255 | + foreach(c_ws_plugin__s2member_utils_strings::trim_deep(stripslashes_deep($_POST)) as $_key => $_value) | |
| 256 | + if(strpos($_key, 'ws_plugin__s2member_custom_reg_field_') === 0) | |
| 257 | + $input[str_replace('ws_plugin__s2member_custom_reg_field_', '', $_key)] = $_value; | |
| 175 | 258 | |
| 176 | - if($validation_errors) foreach($validation_errors as $_field_var => $_error) | |
| 177 | - $GLOBALS['bp']->signup->errors['custom_reg_field_'.$_field_var] = $_error; | |
| 178 | - unset($_field_var, $_error); | |
| 179 | - } | |
| 259 | + $fields_to_validate = c_ws_plugin__s2member_custom_reg_fields::custom_fields_configured_at_level('auto-detection', 'registration', TRUE); | |
| 260 | + $validation_errors = c_ws_plugin__s2member_custom_reg_fields::validation_errors(!empty($input) ? $input : array(), $fields_to_validate); | |
| 261 | + | |
| 262 | + if($validation_errors) foreach($validation_errors as $_field_var => $_error) | |
| 263 | + $GLOBALS['bp']->signup->errors['custom_reg_field_'.$_field_var] = $_error; | |
| 264 | + unset($_field_var, $_error); | |
| 265 | + } | |
| 266 | + } | |
| 180 | 267 | } |
| 181 | 268 | |
| 182 | 269 | /** |
| 183 | 270 | * Filters Multisite User validation. |
| @@ -213,8 +300,12 @@ | ||
| 213 | 300 | { |
| 214 | 301 | unset($errors->errors['user_name'], $errors->errors['user_email'], $errors->errors['user_email_used']); |
| 215 | 302 | unset($errors->error_data['user_name'], $errors->error_data['user_email'], $errors->error_data['user_email_used']); |
| 216 | 303 | } |
| 304 | + //261005.0438 Enforce mail-domain validity on Multisite account creation after s2Member's existing-user join exception has been handled. | |
| 305 | + if(!c_ws_plugin__s2member_utils_users::ms_user_login_email_exists_but_not_on_blog($result['user_name'], $result['user_email']) && ($_email_domain_error = self::registration_email_domain_validation_error($result['user_email']))) | |
| 306 | + $errors->add('user_email_domain', $_email_domain_error); | |
| 307 | + | |
| 217 | 308 | foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v; |
| 218 | 309 | do_action('ws_plugin__s2member_during_ms_validate_user_signup', get_defined_vars()); |
| 219 | 310 | unset($__refs, $__v); // Housekeeping. |
| 220 | 311 | } |