add( 'URI scheme «'.$scheme.'» not allowed!', 'error' ); return $verbose ? sprintf( T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme) ) : T_('URI scheme not allowed.'); } preg_match( '~^(mailto):(.*?)(\?.*)?$~', $url, $match ); if( ! $match ) { return $verbose ? sprintf( T_('Invalid email link: %s.'), htmlspecialchars($url) ) : T_('Invalid email link.'); } elseif( ! is_email($match[2]) ) { return $verbose ? sprintf( T_('Supplied email address (%s) is invalid.'), htmlspecialchars($match[2]) ) : T_('Invalid email address.'); } } elseif( substr($url, 0, 6) == 'clsid:' ) { // clsid:link if( ! in_array( 'clsid', $allowed_uri_schemes ) ) { // Scheme not allowed $scheme = 'clsid:'; $Debuglog->add( 'URI scheme «'.$scheme.'» not allowed!', 'error' ); return $verbose ? sprintf( T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme) ) : T_('URI scheme not allowed.'); } if( ! preg_match( '~^(clsid):([a-fA-F0-9\-]+)$~', $url, $match) ) { return T_('Invalid class ID format'); } } elseif( substr($url, 0, 11) == 'javascript:' ) { // javascript: // Basically there could be anything here if( ! in_array( 'javascript', $allowed_uri_schemes ) ) { // Scheme not allowed $scheme = 'javascript:'; $Debuglog->add( 'URI scheme «'.$scheme.'» not allowed!', 'error' ); return $verbose ? sprintf( T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme) ) : T_('URI scheme not allowed.'); } preg_match( '~^(javascript):~', $url, $match ); } else { // convert URL to IDN: $url = idna_encode($url); if( ! preg_match('~^ # start ([a-z][a-z0-9+.\-]*) # scheme :// # authorize absolute URLs only ( // not present in clsid: -- problem? ; mailto: handled above) (\w+(:\w+)?@)? # username or username and password (optional) ( localhost | [a-z0-9]([a-z0-9\-])* # Don t allow anything too funky like entities \. # require at least 1 dot [a-z0-9]([a-z0-9.\-])+ # Don t allow anything too funky like entities ) (:[0-9]+)? # optional port specification .* # allow anything in the path (including spaces - used in FileManager - but no newlines). $~ix', $url, $match) ) { // Cannot validate URL structure $Debuglog->add( 'URL «'.$url.'» does not match url pattern!', 'error' ); return $verbose ? sprintf( T_('Invalid URL format (%s).'), htmlspecialchars($url) ) : T_('Invalid URL format.'); } $scheme = strtolower($match[1]); if( ! in_array( $scheme, $allowed_uri_schemes ) ) { // Scheme not allowed $Debuglog->add( 'URI scheme «'.$scheme.'» not allowed!', 'error' ); return $verbose ? sprintf( T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme) ) : T_('URI scheme not allowed.'); } } } else { // URL is relative.. if( $context == 'commenting' || $context == 'download_src' || $context == 'http-https' ) { // We do not allow relative URLs in comments and download urls return $verbose ? sprintf( T_('URL "%s" must be absolute.'), htmlspecialchars($url) ) : T_('URL must be absolute.'); } $char = substr( $url, 0, 1 ); if( $char != '/' && $char != '#' ) { // must start with a slash or hash (for HTML anchors to the same page) return $verbose ? sprintf( T_('URL "%s" must be a full path starting with "/" or an anchor starting with "#".'), htmlspecialchars($url) ) : T_('URL must be a full path starting with "/" or an anchor starting with "#".'); } } if( $antispam_check ) { // Search for blocked keywords: if( $block = antispam_check($url) ) { // Log into system log syslog_insert( sprintf( 'Antispam: URL "%s" not allowed. The URL contains blacklisted word "%s".', htmlspecialchars($url), $block ), 'error' ); return $verbose ? sprintf( T_('URL "%s" not allowed: blacklisted word "%s".'), htmlspecialchars($url), $block ) : T_('URL not allowed'); } } return false; // OK } /** * Get allowed URI schemes for a given context. * @param string Context ("posting", "commenting", "download_src", "http-https") * @return array */ function get_allowed_uri_schemes( $context = 'posting' ) { /** * @var User */ global $current_User; $schemes = array( 'http', 'https' ); if( $context == 'http-https' ) { // for context == 'http-https' we accepts only http, https. return $schemes; } $schemes[] = 'ftp'; if( $context == 'download_src' ) { // for context == 'download_src' we also accepts ftp. return $schemes; } $schemes = array_merge( $schemes, array( 'gopher', 'nntp', 'news', 'mailto', 'irc', 'aim', 'icq' ) ); if( $context == 'commenting' ) { return $schemes; } // for context == 'posting' we MAY allow additional "DANGEROUS" schemes: if( !empty( $current_User ) ) { // Add additional permissions the current User may have: $Group = & $current_User->get_Group(); if( $Group->perm_xhtml_javascript ) { $schemes[] = 'javascript'; } if( $Group->perm_xhtml_objects ) { $schemes[] = 'clsid'; } } return $schemes; } /** * Get the last HTTP status code received by the HTTP/HTTPS wrapper of PHP. * * @param array The $http_response_header array (by reference). * @return integer|boolean False if no HTTP status header could be found, * the HTTP status code otherwise. */ function _http_wrapper_last_status( & $headers ) { for( $i = count( $headers ) - 1; $i >= 0; --$i ) { if( preg_match( '|^HTTP/\d+\.\d+ (\d+)|', $headers[$i], $matches ) ) { return $matches[1]; } } return false; } /** * Fetch remote page * * Attempt to retrieve a remote page using a HTTP GET request, first with * cURL, then fsockopen, then fopen. * * cURL gets skipped, if $max_size_kb is requested, since there appears to be no * method to control this. * {@internal (CURLOPT_READFUNCTION maybe? But it has not been called for me.. seems * to affect sending, not fetching?!)}} * * @todo dh> Should we try remaining methods, if the previous one(s) failed? * @todo Tblue> Also allow HTTP POST. * * @param string URL * @param array Info (by reference) * 'error': holds error message, if any * 'status': HTTP status (e.g. 200 or 404) * 'used_method': Used method ("curl", "fopen", "fsockopen" or null if no method * is available) * @param integer Timeout (default: 15 seconds) * @param integer Maximum size in kB * @param array Additional parameters * @return string|false The remote page as a string; false in case of error */ function fetch_remote_page( $url, & $info, $timeout = NULL, $max_size_kb = NULL, $params = array() ) { global $outgoing_proxy_hostname, $outgoing_proxy_port, $outgoing_proxy_username, $outgoing_proxy_password; $params = array_merge( array( 'method' => 'GET', 'content_type' => '', 'fields' => '', // Array or string of POST/GET fields ), $params ); $info = array( 'error' => '', 'status' => NULL, 'mimetype' => NULL, 'used_method' => NULL, ); if( ! isset($timeout) ) $timeout = 15; if( extension_loaded('curl') && ! $max_size_kb ) // dh> I could not find an option to support "maximum size" for curl (to abort during download => memory limit). { // CURL: $info['used_method'] = 'curl'; $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, $url ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout ); curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout ); if( $params['method'] == 'POST' ) { // Use POST method: curl_setopt( $ch, CURLOPT_POST, true ); } if( ! empty( $params['fields'] ) ) { // Add fields for the request: curl_setopt( $ch, CURLOPT_POSTFIELDS, $params['fields'] ); } // Set proxy: if( !empty($outgoing_proxy_hostname) ) { curl_setopt( $ch, CURLOPT_PROXY, $outgoing_proxy_hostname ); curl_setopt( $ch, CURLOPT_PROXYPORT, $outgoing_proxy_port ); curl_setopt( $ch, CURLOPT_PROXYUSERPWD, $outgoing_proxy_username.':'.$outgoing_proxy_password ); } @curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); // made silent due to possible errors with safe_mode/open_basedir(?) curl_setopt( $ch, CURLOPT_MAXREDIRS, 3 ); $r = curl_exec( $ch ); $info['mimetype'] = curl_getinfo( $ch, CURLINFO_CONTENT_TYPE ); $info['status'] = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); $info['error'] = curl_error( $ch ); if( ( $errno = curl_errno( $ch ) ) ) { $info['error'] .= ' (#'.$errno.')'; } curl_close( $ch ); return $r; } if( function_exists( 'fsockopen' ) ) // may have been disabled { // FSOCKOPEN: $info['used_method'] = 'fsockopen'; if ( ( $url_parsed = @parse_url( $url ) ) === false || ! isset( $url_parsed['host'] ) ) { $info['error'] = NT_( 'Could not parse URL' ); return false; } if( isset( $url_parsed['scheme'] ) && $url_parsed['scheme'] == 'https' ) { // Special params for https urls: $host_prefix = 'ssl://'; $default_port = 443; } else { // Default params for normal urls: $host_prefix = ''; $default_port = 80; } $host = $url_parsed['host']; $port = empty( $url_parsed['port'] ) ? $default_port : $url_parsed['port']; $path = empty( $url_parsed['path'] ) ? '/' : $url_parsed['path']; if( ! empty( $url_parsed['query'] ) ) { $path .= '?'.$url_parsed['query']; } if( ! empty( $params['fields'] ) ) { // Convert fields array to string: $url_fields_string = ( is_array( $params['fields'] ) ? http_build_query( $params['fields'] ) : $params['fields'] ); } $out = $params['method'].' '.$path.' HTTP/1.1'."\r\n"; $out .= 'Host: '.$host; if( ! empty( $url_parsed['port'] ) ) { // we don't want to add :80 if not specified. remote end may not resolve it. (e-g b2evo multiblog does not) $out .= ':'.$port; } $out .= "\r\n"; if( ! empty( $params['content_type'] ) ) { $out .= 'Content-type: '.$params['content_type']."\r\n"; } if( ! empty( $url_fields_string ) ) { $out .= 'Content-length: '.strlen( $url_fields_string )."\r\n"; } $out .= 'Connection: close'."\r\n\r\n"; if( ! empty( $url_fields_string ) ) { // Append fields to the request: $out .= $url_fields_string; } $fp = @fsockopen( $host_prefix.$host, $port, $errno, $errstr, $timeout ); if( ! $fp ) { $info['error'] = $errstr.' (#'.$errno.')'; return false; } // Send request: fwrite( $fp, $out ); // Set timeout for data: if( function_exists( 'stream_set_timeout' ) ) { stream_set_timeout( $fp, $timeout ); // PHP 4.3.0 } else { socket_set_timeout( $fp, $timeout ); // PHP 4 } // Read response: $r = ''; // First line: $s = fgets( $fp ); if( ! preg_match( '~^HTTP/\d+\.\d+ (\d+)~', $s, $match ) ) { $info['error'] = NT_( 'Invalid response' ).'.'; fclose( $fp ); return false; } while( ! feof( $fp ) ) { $r .= fgets( $fp ); if( $max_size_kb && evo_bytes($r) >= $max_size_kb*1024 ) { $info['error'] = NT_( sprintf( 'Maximum size of %d kB reached.', $max_size_kb ) ); return false; } } fclose($fp); if ( ( $pos = strpos( $r, "\r\n\r\n" ) ) === false ) { $info['error'] = NT_( 'Could not locate end of headers' ); return false; } // Remember headers to extract info at the end $headers = explode("\r\n", substr($r, 0, $pos)); $info['status'] = $match[1]; $r = substr( $r, $pos + 4 ); } elseif( ini_get( 'allow_url_fopen' ) ) { // URL FOPEN: $info['used_method'] = 'fopen'; $url_http_params = array(); if( ! empty( $params['content_type'] ) ) { // Header of the request: $url_http_params['header'] = 'Content-type: '.$params['content_type']."\r\n"; } if( $params['method'] != 'GET' ) { // Method of the request: $url_http_params['method'] = $params['method']; } if( ! empty( $params['fields'] ) ) { // Additional fields of the request: $url_http_params['content'] = http_build_query( $params['fields'] ); } if( empty( $url_http_params ) ) { // Open simple URL: $fp = @fopen( $url, 'r' ); } else { // Open URL with additional params: $url_context = stream_context_create( array( 'http' => $url_http_params ) ); $fp = @fopen( $url, 'r', false, $url_context ); } if( ! $fp ) { if( isset( $http_response_header ) && ( $code = _http_wrapper_last_status( $http_response_header ) ) !== false ) { // fopen() returned false because it got a bad HTTP code: $info['error'] = NT_( 'Invalid response' ); $info['status'] = $code; return false; } $info['error'] = NT_( 'fopen() failed' ); return false; } // Check just to be sure: else if ( ! isset( $http_response_header ) || ( $code = _http_wrapper_last_status( $http_response_header ) ) === false ) { $info['error'] = NT_( 'Invalid response' ); return false; } else { // Used to get info at the end $headers = $http_response_header; // Retrieve contents $r = ''; while( ! feof( $fp ) ) { $r .= fgets( $fp ); if( $max_size_kb && evo_bytes($r) >= $max_size_kb*1024 ) { $info['error'] = NT_( sprintf( 'Maximum size of %d kB reached.', $max_size_kb ) ); return false; } } $info['status'] = $code; } fclose( $fp ); } // Extract mimetype info from the headers (for fsockopen/fopen) if( isset($r) ) { foreach($headers as $header) { $header = strtolower($header); if( substr($header, 0, 13) == 'content-type:' ) { $info['mimetype'] = trim(substr($header, 13)); break; // only looking for mimetype } } if( $info['mimetype'] == 'application/json' && strpos( $r, '{' ) !== false && preg_match( '/^[^\{]*(\{.+\})[^\}]*$/', $r, $match ) ) { // Fix response in JSON format, so it must be started with "{" and ended with "}": $r = $match[1]; } return $r; } // All failed: $info['error'] = NT_( 'No method available to access URL!' ); return false; } /** * Get $url with the same protocol (http/https) as $other_url. * * @param string URL * @param string other URL (defaults to {@link $ReqHost}) * @return string */ function url_same_protocol( $url, $other_url = NULL ) { if( is_null($other_url) ) { global $ReqHost; $other_url = $ReqHost; } // change protocol of $url to same of admin ('https' <=> 'http') if( substr( $url, 0, 7 ) == 'http://' ) { if( substr( $other_url, 0, 8 ) == 'https://' ) { $url = 'https://'.substr( $url, 7 ); } } elseif( substr( $url, 0, 8 ) == 'https://' ) { if( substr( $other_url, 0, 7 ) == 'http://' ) { $url = 'http://'.substr( $url, 8 ); } } return $url; } /** * Add param(s) at the end of an URL, using either "?" or "&" depending on existing url * * @param string existing url * @param string|array Params to add (string as-is) or array, which gets urlencoded. * @param string delimiter to use for more params */ function url_add_param( $url, $param, $glue = '&' ) { if( empty( $param ) ) { return $url; } if( ( $anchor_pos = strpos( $url, '#' ) ) !== false ) { // There's an "#anchor" in the URL $anchor = substr( $url, $anchor_pos ); $url = substr( $url, 0, $anchor_pos ); } else { // URL without "#anchor" $anchor = ''; } // Handle array use case if( is_array( $param ) ) { // list of key => value pairs $param_list = array(); foreach( $param as $k => $v ) { $param_list[] = get_param_urlencoded( $k, $v, $glue ); } $param = implode( $glue, $param_list ); } if( strpos( $url, '?' ) !== false ) { // There are already params in the URL $r = $url; if( substr( $url, -1 ) != '?' && substr( $param, 0, 1 ) != '#' ) { // the "?" is not the last char AND "#" is not first char of param $r .= $glue; } return $r.$param.$anchor; } // These are the first params return $url.'?'.$param.$anchor; } /** * Add a tail (starting with "/") at the end of an URL before any params (starting with "?") * * @param string existing url * @param string tail to add */ function url_add_tail( $url, $tail ) { $parts = explode( '?', $url ); if( substr($parts[0], -1) == '/' ) { $parts[0] = substr($parts[0], 0, -1); } if( isset($parts[1]) ) { return $parts[0].$tail.'?'.$parts[1]; } return $parts[0].$tail; } /** * Create a crumb param to be passed in action urls... * * @access public * @param string crumb_name */ function url_crumb( $crumb_name ) { return 'crumb_'.$crumb_name.'='.get_crumb($crumb_name); } /** * Get crumb via {@link $Session}. * @access public * @param string crumb_name * @return string */ function get_crumb( $crumb_name ) { global $Session; return isset( $Session ) ? $Session->create_crumb( $crumb_name ) : ''; } /** * Try to make $url relative to $target_url, if scheme, host, user and pass matches. * * This is useful for redirect_to params, to keep them short and avoid mod_security * rejecting the request as "Not Acceptable" (whole URL as param). * * @param string URL to handle * @param string URL where we want to make $url relative to * @return string */ function url_rel_to_same_host( $url, $target_url ) { // Prepend fake scheme to URLs starting with "//" (relative to current protocol), since // parse_url fails to handle them correctly otherwise (recognizes them as path-only) $mangled_url = substr($url, 0, 2) == '//' ? 'noprotocolscheme:'.$url : $url; if( substr($target_url, 0, 2) == '//' ) $target_url = 'noprotocolscheme:'.$target_url; $parsed_url = @parse_url( $mangled_url ); if( ! $parsed_url ) { // invalid url return $url; } if( empty($parsed_url['scheme']) || empty($parsed_url['host']) ) { // no protocol or host information return $url; } $target_url = @parse_url( $target_url ); if( ! $target_url ) { // invalid url return $url; } if( ! empty($target_url['scheme']) && $target_url['scheme'] != $parsed_url['scheme'] && $parsed_url['scheme'] != 'noprotocolscheme' ) { // scheme/protocol is different return $url; } if( ! empty($target_url['host']) ) { if( empty($target_url['scheme']) || $target_url['host'] != $parsed_url['host'] ) { // target has no scheme (but a host) or hosts differ return $url; } if( @$target_url['port'] != @$parsed_url['port'] ) return $url; if( @$target_url['user'] != @$parsed_url['user'] ) return $url; if( @$target_url['pass'] != @$parsed_url['pass'] ) return $url; } // We can make the URL relative: $r = ''; if( isset($parsed_url['path']) && strlen($parsed_url['path']) ) $r .= $parsed_url['path']; if( isset($parsed_url['query']) && strlen($parsed_url['query']) ) $r .= '?'.$parsed_url['query']; if( isset($parsed_url['fragment']) && strlen($parsed_url['fragment']) ) $r .= '#'.$parsed_url['fragment']; return $r; } /** * Make an $url absolute according to $host, if it is not absolute yet. * * @param string URL * @param string Base (including protocol, e.g. 'http://example.com'); autodedected * @return string */ function url_absolute( $url, $base = NULL ) { load_funcs('_ext/_url_rel2abs.php'); if( is_absolute_url($url) ) { // URL is already absolute return $url; } if( empty($base) ) { // Detect current page base global $Collection, $Blog, $ReqHost, $base_tag_set, $baseurl; if( $base_tag_set ) { // tag is set $base = $base_tag_set; } else { if( ! empty( $Blog ) ) { // Get original blog skin, not passed with 'tempskin' param $SkinCache = & get_SkinCache(); if( ($Skin = $SkinCache->get_by_ID( $Blog->get_skin_ID(), false )) !== false ) { $base = $Blog->get_local_skins_url().$Skin->folder.'/'; } else { // Skin not set: $base = $Blog->gen_baseurl(); } } else { // We are displaying a general page that is not specific to a blog: $base = $ReqHost; } } } if( ($absurl = url_to_absolute($url, $base)) === false ) { // Return relative URL in case of error $absurl = $url; } return $absurl; } /** * Make links in $s absolute. * * It searches for "src" and "href" HTML tag attributes and makes the absolute. * * @uses url_absolute() * @param string content * @param string Hostname including scheme, e.g. http://example.com; defaults to $ReqHost * @return string */ function make_rel_links_abs( $s, $host = NULL ) { load_class( '_core/model/_urlhelper.class.php', 'UrlHelper' ); $url_helper = new UrlHelper( $host ); $s = preg_replace_callback( '~(<[^>]+?)\b((?:src|href)\s*=\s*)(["\'])?([^\\3]+?)(\\3)~i', array( $url_helper, 'callback' ), $s ); return $s; } /** * Display an URL, constrained to a max length * * @param string * @param integer */ function disp_url( $url, $max_length = NULL ) { if( !empty($max_length) && utf8_strlen($url) > $max_length ) { $disp_url = htmlspecialchars(substr( $url, 0, $max_length-1 )).'…'; } else { $disp_url = htmlspecialchars($url); } echo ''.$disp_url.''; } /** * Is a given URL absolute? * Note: "//foo/bar" is absolute - leaving the protocol out. * * @param string URL * @return boolean */ function is_absolute_url( $url ) { load_funcs('_ext/_url_rel2abs.php'); if( ($parsed_url = split_url($url)) !== false ) { if( !empty($parsed_url['scheme']) || !empty($parsed_url['host']) ) { return true; } } return false; } /** * Compare two given URLs, if they are the same. * This converts all urlencoded chars (e.g. "%AA") to lowercase. * It appears that some webservers use lowercase for the chars (Apache), * while others use uppercase (lighttpd). * * @param string First URL * @param string Second URL * @param boolean TRUE to make the compared URLs same even if have a different protocols http or https * @return boolean */ function is_same_url( $a, $b, $ignore_http_protocol = FALSE ) { $a = preg_replace_callback('~%[0-9A-F]{2}~', '_is_same_url_callback', $a); $b = preg_replace_callback('~%[0-9A-F]{2}~', '_is_same_url_callback', $b); if( $ignore_http_protocol ) { $re = "/^https?\:\/\/(.*)/i"; $subst = "$1"; $a = preg_replace( $re, $subst, $a ); $b = preg_replace( $re, $subst, $b ); } return $a == $b; } /** * Callback for preg_replace_callback in is_same_url() */ function _is_same_url_callback( $matches ) { return strtolower( $matches[0] ); } /** * IDNA-Encode URL to Punycode. * @param string URL * @return string Encoded URL (ASCII) */ function idna_encode( $url ) { global $evo_charset; $url_utf8 = convert_charset( $url, 'utf-8', $evo_charset ); load_class('_ext/idna/_idna_convert.class.php', 'idna_convert' ); $IDNA = new idna_convert(); //echo '['.$url_utf8.'] '; $url = $IDNA->encode( $url_utf8 ); /* if( $idna_error = $IDNA->get_last_error() ) { echo $idna_error; } */ // echo '['.$url.']
'; return $url; } /** * Decode IDNA puny-code ("xn--..") to UTF-8 name. * * @param string * @return string The decoded puny-code ("xn--..") (UTF8!) */ function idna_decode( $url ) { load_class('_ext/idna/_idna_convert.class.php', 'idna_convert' ); $IDNA = new idna_convert(); return $IDNA->decode($url); } /** * Get disp urls for Frontoffice part OR ctrl urls for Backoffice * * @param string specific sub entry url * @param string additional params */ function get_dispctrl_url( $dispctrl, $params = '' ) { global $Collection, $Blog; if( $params != '' ) { $params = '&'.$params; } if( is_admin_page() || empty( $Blog ) ) { // Backoffice part global $current_User; if( is_logged_in() && $current_User->check_perm( 'admin', 'restricted' ) && $current_User->check_status( 'can_access_admin' ) ) { // User must has an access to backoffice global $admin_url; return url_add_param( $admin_url, 'ctrl='.$dispctrl.$params ); } else { // Return empty because user has no access return NULL; } } if( in_array( $dispctrl, array( 'threads', 'messages', 'contacts', 'msgform' ) ) ) { // Get this url through Blog function, because it can be linked to other blog return $Blog->get( $dispctrl.'url' ).$params; } else { // Use current blog url return url_add_param( $Blog->gen_blogurl(), 'disp='.$dispctrl.$params ); } } /** * Get link tag * * @param string Url * @param string Link Text * @param string Link class * @param integer Max length of url when url is used as link text * @return string HTML link tag */ function get_link_tag( $url, $text = '', $class = '', $max_url_length = 50 ) { if( empty( $text ) ) { // Link text is empty, Use url $text = $url; if( strlen( $text ) > $max_url_length ) { // Crop url text $text = substr( $text, 0, $max_url_length ).'…'; } } $link_attrs = array( 'href' => str_replace( '&', '&', $url ) ); if( ! empty( $class ) ) { if( strpos( $class, '.' ) === false ) { // Simple class name $link_attrs['class'] = $class; } else { // This class name is used for email template $link_attrs['style'] = emailskin_style( $class, false ); } } return ''.$text.''; } /** * Get part of url, Based on function parse_url() * * @param string URL * @param string Part name: * scheme - e.g. http * host * port * user * pass * path * query - after the question mark ? * fragment - after the hashmark # * @return string Part of url */ function url_part( $url, $part ) { $url_data = @parse_url( $url ); if( $url_data && ! empty( $url_data[ $part ] ) ) { return $url_data[ $part ]; } return ''; } /** * Check if the check_url has the same domains as the main_url * Note: check_url may also be a subdomain of the main_url * * @param string main url to compare domain with * @param string the url which needs to be checked * @return boolean true in case of the same main domain, false otherwise */ function url_check_same_domain( $main_url, $check_url ) { $main_url_host = url_part( $main_url, 'host' ); $check_url_host = url_part( $check_url, 'host' ); // Check same domain $same_domain = ( ( $check_url_host == null ) || ( $check_url_host == $main_url_host ) ); // Check subdomain return $same_domain || ( substr( $check_url_host, - ( strlen( $main_url_host ) + 1 ) ) == '.'.$main_url_host ); } ?>