. */ str_replace('://', urlencode('://'), $query); $url_uri = /* Put it all back together again, after the above modifications. */ $_.'?'.$query; unset($_, $query); // A little housekeeping here. Unset these vars. } $parse = @parse_url($url_uri, $component); // Let PHP work its magic via ``parse_url()``. if($clean_path && is_array($parse) && !empty($parse['path']) && is_string($parse['path'])) $parse['path'] = preg_replace('/\/+/', '/', $parse['path']); return $component !== -1 ? (string)$parse : $parse; } /** * Responsible for all remote communications processed by s2Member. * * Uses ``wp_remote_request()`` through the `WP_Http` class. * * @package s2Member\Utilities * @since 3.5 * * @param string $url Full URL with possible query string parameters. * @param string|array $post_body Optional. Either a string of POST data, or an array. * @param array $args Optional. An array of additional arguments used by ``wp_remote_request()``. * @param bool $return_array Optional. If true, instead of a string, we return an array with elements: * `code` *(http response code)*, `message` *(http response message)*, `headers` *(an array of lowercase headers)*, `body` *(the response body string)*, `response` *(full response array)*. * @return str|array|bool Requested response str|array from remote location *(see ``$return_array`` parameter )*; else (bool)`false` on failure. */ public static function remote($url = FALSE, $post_body = FALSE, $args = FALSE, $return_array = FALSE) { if(!$url || !is_string($url)) return false; $args = !is_array($args) ? array() : $args; $args['s2member'] = WS_PLUGIN__S2MEMBER_VERSION; // s2Member connection. $args['httpversion'] = !isset($args['httpversion']) ? '1.1' : $args['httpversion']; $args['user-agent'] = !isset($args['user-agent']) ? 's2Member v'.WS_PLUGIN__S2MEMBER_VERSION.'; '.home_url() : $args['user-agent']; if(!isset($args['sslverify']) && c_ws_plugin__s2member_utils_conds::is_localhost()) $args['sslverify'] = FALSE; // Force this off on localhost installs. else if(!isset($args['sslverify']) && strcasecmp(self::parse_url($url, PHP_URL_HOST), $_SERVER['HTTP_HOST']) === 0) $args['sslverify'] = FALSE; // Don't require verification when posting to self. if($post_body && (is_array($post_body) || is_string($post_body))) $args = array_merge($args, array('method' => 'POST', 'body' => $post_body)); if(!empty($args['method']) && strcasecmp((string)$args['method'], 'DELETE') === 0 && version_compare(get_bloginfo('version'), '3.4', '<')) add_filter('use_curl_transport', '__return_false', /* ID via priority. */ 111209554); foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v; do_action('ws_plugin__s2member_before_wp_remote_request', get_defined_vars()); unset($__refs, $__v); // Housekeeping. $response = wp_remote_request($url, $args); remove_filter('use_curl_transport', '__return_false', 111209554); if($return_array && !is_wp_error($response) && is_array($response)) { $a = array('code' => (int)wp_remote_retrieve_response_code($response)); $a = array_merge($a, array('message' => wp_remote_retrieve_response_message($response))); $a = array_merge($a, array('headers' => wp_remote_retrieve_headers($response))); $a = array_merge($a, array('body' => wp_remote_retrieve_body($response))); $a = array_merge($a, array('response' => $response)); return $a; // Return array w/ ``$response`` too. } if(!is_wp_error($response) && is_array($response)) return wp_remote_retrieve_body($response); return false; // Remote request failed, return false. } /** * Shortens a long URL, based on s2Member configuration. * * @package s2Member\Utilities * @since 111002 * * @param string $url A full/long URL to be shortened. * @param string $api_sp Optional. A specific URL shortening API to use. Defaults to that which is configured in the s2Member Dashboard. Normally `s2member`, by default. * @param bool $try_backups Defaults to true. If a failure occurs with the first API, we'll try others until we have success. * @param int $expiration Optional. Transient expiration, in seconds, for built-in s2Member short links. * @return str|bool The shortened URL on success, else false on failure. */ public static function shorten($url = '', $api_sp = '', $try_backups = TRUE, $expiration = 0) { $url = $url && is_string($url) ? $url : FALSE; $expiration = (int)$expiration; $apis = array('s2member', 'bitly'); //260612 Supported APIs. $api_sp = $api_sp && is_string($api_sp) ? strtolower($api_sp) : FALSE; $default_url_shortener = $GLOBALS['WS_PLUGIN__']['s2member']['o']['default_url_shortener']; $default_url_shortener_key = $GLOBALS['WS_PLUGIN__']['s2member']['o']['default_url_shortener_key']; $default_custom_str_url_shortener = $GLOBALS['WS_PLUGIN__']['s2member']['o']['default_custom_str_url_shortener']; $api = $api_sp ? $api_sp : $default_url_shortener; $api = ($api === 'tiny_url') ? 's2member' : $api; //260612 TinyURL legacy API is deprecated; use built-in short links instead. if($url && $api) // If specific, use it. Otherwise, try customs, else use the default shortening API. { if(!$api_sp // If not a specific API, give filters a chance to shorten it here. && ($custom_url = trim(apply_filters('ws_plugin__s2member_url_shorten', FALSE, get_defined_vars()))) && stripos($custom_url, 'http') === 0) return ($shorter_url = $custom_url); else if(!$api_sp // If not specific, try custom settings. && stripos($default_custom_str_url_shortener, 'http') === 0 && ($custom_url = trim(self::remote(str_ireplace(array('%%s2_long_url%%', '%%s2_long_url_md5%%'), array(rawurlencode($url), urlencode(md5($url))), $default_custom_str_url_shortener)))) && stripos($custom_url, 'http') === 0) return ($shorter_url = $custom_url); else if($api === 'none') // Don't shorten. return $url; else if($api === 's2member') //260612 Built-in short links. return (($shorter_url = self::short_link($url, $expiration)) ? $shorter_url : $url); else if($api === 'bitly' // Using the Bitly API in this case? && ($bitly_endpoint = 'https://api-ssl.bitly.com/v3/shorten') && ($bitly_endpoint_key = $default_url_shortener_key) // Must be configured by site owner. && ($bitly_endpoint = add_query_arg('access_token', urlencode($bitly_endpoint_key), $bitly_endpoint)) && ($bitly_endpoint = add_query_arg('longUrl', urlencode($url), $bitly_endpoint)) && ($bitly_response = json_decode(trim(self::remote($bitly_endpoint)))) && !empty($bitly_response->data->url) && stripos($bitly_url = $bitly_response->data->url, 'http') === 0) return ($shorter_url = $bitly_url); else if($try_backups && count($apis) > 1) // Try backups? { foreach(array_diff($apis, array($api)) as $_backup_api) if(($_backup_api_url = self::shorten($url, $_backup_api, FALSE, $expiration))) return ($shorter_url = $_backup_api_url); unset($_backup_api, $_backup_api_url); // Housekeeping. } } return FALSE; // Default return value. } /** * Creates a built-in s2Member short link. * * @package s2Member\Utilities * @since 260612 * * @param string $url A full/long s2Member URL to be shortened. * @param int $expiration Optional. Transient expiration, in seconds. * @return str|bool The shortened URL on success, else false on failure. */ public static function short_link($url = '', $expiration = 0) { $url = $url && is_string($url) ? trim($url) : FALSE; $expiration = (int)$expiration; if(!$url || stripos($url, 'http') !== 0) return FALSE; $url_scheme = self::parse_url($url, PHP_URL_SCHEME); if(!in_array(strtolower($url_scheme), array('http', 'https'), TRUE)) return FALSE; $query = trim(self::parse_url($url, PHP_URL_QUERY), '?&='); wp_parse_str($query, $vars); if(empty($vars['s2member_register']) && empty($vars['s2member_sp_access'])) return FALSE; $url_host = self::parse_url($url, PHP_URL_HOST); $home_host = self::parse_url(home_url('/'), PHP_URL_HOST); if(!$url_host || !$home_host || strcasecmp($url_host, $home_host) !== 0) return FALSE; $expiration = ($expiration > 0) ? $expiration : 7 * DAY_IN_SECONDS; for($i = 0; $i < 10; $i++) { $code = wp_generate_password(10, FALSE, FALSE); $transient_key = 's2member_link_'.$code; if(get_transient($transient_key) === FALSE) { if(set_transient($transient_key, $url, $expiration)) return add_query_arg('s2_link', rawurlencode($code), home_url('/')); } } return FALSE; // Default return value. } /** * Redirects built-in s2Member short links. * * @package s2Member\Utilities * @since 260612 * * @attaches-to ``add_action('init');`` * * @return null Exits script execution after redirecting. */ public static function short_link_redirect() { if(empty($_GET['s2_link']) || !is_string($_GET['s2_link'])) return; $code = trim(wp_unslash((string)$_GET['s2_link'])); if(!preg_match('/^[a-zA-Z0-9]{10}$/', $code)) return; $url = get_transient('s2member_link_'.$code); if(!$url || !is_string($url) || stripos($url, 'http') !== 0) return; $url_scheme = self::parse_url($url, PHP_URL_SCHEME); if(!in_array(strtolower($url_scheme), array('http', 'https'), TRUE)) return; $url_host = self::parse_url($url, PHP_URL_HOST); $home_host = self::parse_url(home_url('/'), PHP_URL_HOST); if(!$url_host || !$home_host || strcasecmp($url_host, $home_host) !== 0) return; $query = trim(self::parse_url($url, PHP_URL_QUERY), '?&='); wp_parse_str($query, $vars); if(empty($vars['s2member_register']) && empty($vars['s2member_sp_access'])) return; nocache_headers(); wp_safe_redirect($url, 302); exit; } /** * Removes all s2Member-generated signatures from a full URL, a partial URI, or just a query string. * * @package s2Member\Utilities * @since 111106 * * @param string $url_uri_query A full URL, a partial URI, or just the query string; to remove s2Member-generated signatures from. * @param string $sig_var Optional. The name of the s2Member-generated signature variable. Defaults to `_s2member_sig`. * @return string A full URL, a partial URI, or just the query string; without any s2Member-generated signatures. */ public static function remove_s2member_sigs($url_uri_query = FALSE, $sig_var = FALSE) { $url_uri_query = c_ws_plugin__s2member_utils_strings::trim((string)$url_uri_query, false, '?&='); $sig_var = ($sig_var && is_string($sig_var)) ? $sig_var : '_s2member_sig'; $sigs = array_unique(array($sig_var, '_s2member_sig')); return trim(remove_query_arg($sigs, $url_uri_query), '?&='); } /** * Adds an s2Member-generated signature onto a full URL, a partial URI, or just a query string. * * @package s2Member\Utilities * @since 111106 * * @param string $url_uri_query A full URL, a partial URI, or just a query string; to append the s2Member-generated signature onto. * @param string $sig_var Optional. The name of the s2Member-generated signature variable. Defaults to `_s2member_sig`. * @return string A full URL, a partial URI, or just a query string; with an s2Member-generated signature. */ public static function add_s2member_sig($url_uri_query = FALSE, $sig_var = FALSE) { $url_uri_query = $query = c_ws_plugin__s2member_utils_strings::trim((string)$url_uri_query, false, '?&='); $sig_var = $sig_var && is_string($sig_var) ? $sig_var : '_s2member_sig'; $url_uri_query = $query = c_ws_plugin__s2member_utils_urls::remove_s2member_sigs($url_uri_query, $sig_var); if(preg_match('/^(?:[a-z]+\:\/\/|\/)/i', $url_uri_query)) // Is this a full URL or a partial URI? $query = trim(c_ws_plugin__s2member_utils_urls::parse_url($url_uri_query, PHP_URL_QUERY), '?&='); $key = c_ws_plugin__s2member_utils_encryption::key(); // Obtain key. if($url_uri_query && is_string($query)) // We DO allow empty query strings. So we can sign a URL without one. { wp_parse_str($query, $vars); // Parse the query string into an array of ``$vars``. $vars = c_ws_plugin__s2member_utils_arrays::remove_0b_strings(c_ws_plugin__s2member_utils_strings::trim_deep($vars)); $vars = serialize(c_ws_plugin__s2member_utils_arrays::ksort_deep($vars)); $sig = ($time = time()).'-'.md5($key.$time.$vars); $url_uri_query = add_query_arg($sig_var, urlencode($sig), $url_uri_query); } return $url_uri_query; // Possibly with a ``$sig_var`` variable. } /** * Verifies an s2Member-generated signature; in a full URL, a partial URI, or in just a query string. * * @package s2Member\Utilities * @since 111106 * * @param string $url_uri_query A full URL, a partial URI, or just a query string. Must have an s2Member-generated signature to validate. * @param bool $check_time Optional. Defaults to false. If true, s2Member will also check if the signature has expired, based on ``$exp_secs``. * @param string|int $exp_secs Optional. Defaults to (int)10. If ``$check_time`` is true, s2Member will check if the signature has expired, based on ``$exp_secs``. * @param string $sig_var Optional. The name of the s2Member-generated signature variable. Defaults to `_s2member_sig`. * @return bool True if the s2Member-generated signature is OK, else false. */ public static function s2member_sig_ok($url_uri_query = FALSE, $check_time = FALSE, $exp_secs = FALSE, $sig_var = FALSE) { $url_uri_query = $query = c_ws_plugin__s2member_utils_strings::trim((string)$url_uri_query, false, '?&='); if(preg_match('/^(?:[a-z]+\:\/\/|\/)/i', $url_uri_query)) // Is this a full URL or a partial URI? $query = trim(c_ws_plugin__s2member_utils_urls::parse_url($url_uri_query, PHP_URL_QUERY), '?&='); $check_time = (bool)$check_time; // Check time? $exp_secs = is_numeric($exp_secs) ? (int)$exp_secs : 10; $sig_var = $sig_var && is_string($sig_var) ? $sig_var : '_s2member_sig'; $key = c_ws_plugin__s2member_utils_encryption::key(); // Obtain key. if(preg_match_all('/'.preg_quote($sig_var, '/').'\=([0-9]+)-([^&$]+)/', $query, $sigs)) { $query = c_ws_plugin__s2member_utils_urls::remove_s2member_sigs($query, $sig_var); wp_parse_str($query, $vars); // Parse the query string into an array of ``$vars``. $vars = c_ws_plugin__s2member_utils_arrays::remove_0b_strings(c_ws_plugin__s2member_utils_strings::trim_deep($vars)); $vars = serialize(c_ws_plugin__s2member_utils_arrays::ksort_deep($vars)); $i = count($sigs[1]) - 1; // Last one. $time = $sigs[1][$i]; // Timestamp. $sig = $sigs[2][$i]; // Signature. $valid_sig = md5($key.$time.$vars); if($check_time) // This must NOT be older than ``$exp_secs`` seconds ago. return $sig === $valid_sig && $time >= strtotime('-'.$exp_secs.' seconds'); return $sig === $valid_sig; } return false; // False, it's NOT ok. } } }