';
}
yourls_do_action( 'post_redirect_javascript', $location );
}
/**
* Return a HTTP status code
*
*/
function yourls_get_HTTP_status( $code ) {
$code = intval( $code );
$headers_desc = array(
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
226 => 'IM Used',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => 'Reserved',
307 => 'Temporary Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
426 => 'Upgrade Required',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
506 => 'Variant Also Negotiates',
507 => 'Insufficient Storage',
510 => 'Not Extended'
);
if ( isset( $headers_desc[$code] ) )
return $headers_desc[$code];
else
return '';
}
/**
* Log a redirect (for stats)
*
* This function does not check for the existence of a valid keyword, in order to save a query. Make sure the keyword
* exists before calling it.
*
* @since 1.4
* @param string $keyword short URL keyword
* @return mixed Result of the INSERT query (1 on success)
*/
function yourls_log_redirect( $keyword ) {
// Allow plugins to short-circuit the whole function
$pre = yourls_apply_filter( 'shunt_log_redirect', false, $keyword );
if ( false !== $pre )
return $pre;
if ( !yourls_do_log_redirect() )
return true;
global $ydb;
$table = YOURLS_DB_TABLE_LOG;
$now = date( 'Y-m-d H:i:s' );
$keyword = yourls_escape( yourls_sanitize_string( $keyword ) );
$referrer = ( isset( $_SERVER['HTTP_REFERER'] ) ? yourls_escape( yourls_sanitize_url( $_SERVER['HTTP_REFERER'] ) ) : 'direct' );
$ua = yourls_escape( yourls_get_user_agent() );
$ip = yourls_escape( yourls_get_IP() );
$location = yourls_escape( yourls_geo_ip_to_countrycode( $ip ) );
return $ydb->query( "INSERT INTO `$table` (click_time, shorturl, referrer, user_agent, ip_address, country_code) VALUES ('$now', '$keyword', '$referrer', '$ua', '$ip', '$location')" );
}
/**
* Check if we want to not log redirects (for stats)
*
*/
function yourls_do_log_redirect() {
return ( !defined( 'YOURLS_NOSTATS' ) || YOURLS_NOSTATS != true );
}
/**
* Converts an IP to a 2 letter country code, using GeoIP database if available in includes/geo/
*
* @since 1.4
* @param string $ip IP or, if empty string, will be current user IP
* @param string $defaut Default string to return if IP doesn't resolve to a country (malformed, private IP...)
* @return string 2 letter country code (eg 'US') or $default
*/
function yourls_geo_ip_to_countrycode( $ip = '', $default = '' ) {
// Allow plugins to short-circuit the Geo IP API
$location = yourls_apply_filter( 'shunt_geo_ip_to_countrycode', false, $ip, $default ); // at this point $ip can be '', check if your plugin hooks in here
if ( false !== $location )
return $location;
if ( $ip == '' )
$ip = yourls_get_IP();
// Use IPv4 or IPv6 DB & functions
if( false === filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
$db = 'GeoIP.dat';
$func = 'geoip_country_code_by_addr';
} else {
$db = 'GeoIPv6.dat';
$func = 'geoip_country_code_by_addr_v6';
}
if ( !file_exists( YOURLS_INC . '/geo/' . $db ) || !file_exists( YOURLS_INC .'/geo/geoip.inc' ) )
return $default;
require_once( YOURLS_INC . '/geo/geoip.inc' );
$gi = geoip_open( YOURLS_INC . '/geo/' . $db, GEOIP_STANDARD );
try {
$location = call_user_func( $func, $gi, $ip );
} catch ( Exception $e ) {
$location = '';
}
geoip_close( $gi );
if( '' == $location )
$location = $default;
return yourls_apply_filter( 'geo_ip_to_countrycode', $location, $ip, $default );
}
/**
* Converts a 2 letter country code to long name (ie AU -> Australia)
*
*/
function yourls_geo_countrycode_to_countryname( $code ) {
// Allow plugins to short-circuit the Geo IP API
$country = yourls_apply_filter( 'shunt_geo_countrycode_to_countryname', false, $code );
if ( false !== $country )
return $country;
// Load the Geo class if not already done
if( !class_exists( 'GeoIP', false ) ) {
$temp = yourls_geo_ip_to_countrycode( '127.0.0.1' );
}
if( class_exists( 'GeoIP', false ) ) {
$geo = new GeoIP;
$id = $geo->GEOIP_COUNTRY_CODE_TO_NUMBER[ $code ];
$long = $geo->GEOIP_COUNTRY_NAMES[ $id ];
return $long;
} else {
return false;
}
}
/**
* Return flag URL from 2 letter country code
*
*/
function yourls_geo_get_flag( $code ) {
if( file_exists( YOURLS_INC.'/geo/flags/flag_'.strtolower($code).'.gif' ) ) {
$img = yourls_match_current_protocol( YOURLS_SITE.'/includes/geo/flags/flag_'.( strtolower( $code ) ).'.gif' );
} else {
$img = false;
}
return yourls_apply_filter( 'geo_get_flag', $img, $code );
}
/**
* Check if an upgrade is needed
*
*/
function yourls_upgrade_is_needed() {
// check YOURLS_DB_VERSION exist && match values stored in YOURLS_DB_TABLE_OPTIONS
list( $currentver, $currentsql ) = yourls_get_current_version_from_sql();
if( $currentsql < YOURLS_DB_VERSION )
return true;
return false;
}
/**
* Get current version & db version as stored in the options DB. Prior to 1.4 there's no option table.
*
*/
function yourls_get_current_version_from_sql() {
$currentver = yourls_get_option( 'version' );
$currentsql = yourls_get_option( 'db_version' );
// Values if version is 1.3
if( !$currentver )
$currentver = '1.3';
if( !$currentsql )
$currentsql = '100';
return array( $currentver, $currentsql);
}
/**
* Read an option from DB (or from cache if available). Return value or $default if not found
*
* Pretty much stolen from WordPress
*
* @since 1.4
* @param string $option Option name. Expected to not be SQL-escaped.
* @param mixed $default Optional value to return if option doesn't exist. Default false.
* @return mixed Value set for the option.
*/
function yourls_get_option( $option_name, $default = false ) {
global $ydb;
// Allow plugins to short-circuit options
$pre = yourls_apply_filter( 'shunt_option_'.$option_name, false );
if ( false !== $pre )
return $pre;
// If option not cached already, get its value from the DB
if ( !isset( $ydb->option[$option_name] ) ) {
$table = YOURLS_DB_TABLE_OPTIONS;
$option_name = yourls_escape( $option_name );
$row = $ydb->get_row( "SELECT `option_value` FROM `$table` WHERE `option_name` = '$option_name' LIMIT 1" );
if ( is_object( $row) ) { // Has to be get_row instead of get_var because of funkiness with 0, false, null values
$value = $row->option_value;
} else { // option does not exist, so we must cache its non-existence
$value = $default;
}
$ydb->option[ $option_name ] = yourls_maybe_unserialize( $value );
}
return yourls_apply_filter( 'get_option_'.$option_name, $ydb->option[$option_name] );
}
/**
* Read all options from DB at once
*
* The goal is to read all options at once and then populate array $ydb->option, to prevent further
* SQL queries if we need to read an option value later.
* It's also a simple check whether YOURLS is installed or not (no option = assuming not installed) after
* a check for DB server reachability has been performed
*
* @since 1.4
*/
function yourls_get_all_options() {
global $ydb;
// Allow plugins to short-circuit all options. (Note: regular plugins are loaded after all options)
$pre = yourls_apply_filter( 'shunt_all_options', false );
if ( false !== $pre )
return $pre;
$table = YOURLS_DB_TABLE_OPTIONS;
$allopt = $ydb->get_results( "SELECT `option_name`, `option_value` FROM `$table` WHERE 1=1" );
foreach( (array)$allopt as $option ) {
$ydb->option[ $option->option_name ] = yourls_maybe_unserialize( $option->option_value );
}
if( property_exists( $ydb, 'option' ) ) {
$ydb->option = yourls_apply_filter( 'get_all_options', $ydb->option );
$ydb->installed = true;
} else {
// Zero option found: either YOURLS is not installed or DB server is dead
if( !yourls_is_db_alive() ) {
yourls_db_dead(); // YOURLS will die here
}
$ydb->installed = false;
}
}
/**
* Update (add if doesn't exist) an option to DB
*
* Pretty much stolen from WordPress
*
* @since 1.4
* @param string $option Option name. Expected to not be SQL-escaped.
* @param mixed $newvalue Option value. Must be serializable if non-scalar. Expected to not be SQL-escaped.
* @return bool False if value was not updated, true otherwise.
*/
function yourls_update_option( $option_name, $newvalue ) {
global $ydb;
$table = YOURLS_DB_TABLE_OPTIONS;
$option_name = trim( $option_name );
if ( empty( $option_name ) )
return false;
// Use clone to break object refs -- see commit 09b989d375bac65e692277f61a84fede2fb04ae3
if ( is_object( $newvalue ) )
$newvalue = clone $newvalue;
$option_name = yourls_escape( $option_name );
$oldvalue = yourls_get_option( $option_name );
// If the new and old values are the same, no need to update.
if ( $newvalue === $oldvalue )
return false;
if ( false === $oldvalue ) {
yourls_add_option( $option_name, $newvalue );
return true;
}
$_newvalue = yourls_escape( yourls_maybe_serialize( $newvalue ) );
yourls_do_action( 'update_option', $option_name, $oldvalue, $newvalue );
$ydb->query( "UPDATE `$table` SET `option_value` = '$_newvalue' WHERE `option_name` = '$option_name'" );
if ( $ydb->rows_affected == 1 ) {
$ydb->option[ $option_name ] = $newvalue;
return true;
}
return false;
}
/**
* Add an option to the DB
*
* Pretty much stolen from WordPress
*
* @since 1.4
* @param string $option Name of option to add. Expected to not be SQL-escaped.
* @param mixed $value Optional option value. Must be serializable if non-scalar. Expected to not be SQL-escaped.
* @return bool False if option was not added and true otherwise.
*/
function yourls_add_option( $name, $value = '' ) {
global $ydb;
$table = YOURLS_DB_TABLE_OPTIONS;
$name = trim( $name );
if ( empty( $name ) )
return false;
// Use clone to break object refs -- see commit 09b989d375bac65e692277f61a84fede2fb04ae3
if ( is_object( $value ) )
$value = clone $value;
$name = yourls_escape( $name );
// Make sure the option doesn't already exist
if ( false !== yourls_get_option( $name ) )
return false;
$_value = yourls_escape( yourls_maybe_serialize( $value ) );
yourls_do_action( 'add_option', $name, $_value );
$ydb->query( "INSERT INTO `$table` (`option_name`, `option_value`) VALUES ('$name', '$_value')" );
$ydb->option[ $name ] = $value;
return true;
}
/**
* Delete an option from the DB
*
* Pretty much stolen from WordPress
*
* @since 1.4
* @param string $option Option name to delete. Expected to not be SQL-escaped.
* @return bool True, if option is successfully deleted. False on failure.
*/
function yourls_delete_option( $name ) {
global $ydb;
$table = YOURLS_DB_TABLE_OPTIONS;
$name = yourls_escape( $name );
// Get the ID, if no ID then return
$option = $ydb->get_row( "SELECT option_id FROM `$table` WHERE `option_name` = '$name'" );
if ( is_null( $option ) || !$option->option_id )
return false;
yourls_do_action( 'delete_option', $name );
$ydb->query( "DELETE FROM `$table` WHERE `option_name` = '$name'" );
unset( $ydb->option[ $name ] );
return true;
}
/**
* Serialize data if needed. Stolen from WordPress
*
* @since 1.4
* @param mixed $data Data that might be serialized.
* @return mixed A scalar data
*/
function yourls_maybe_serialize( $data ) {
if ( is_array( $data ) || is_object( $data ) )
return serialize( $data );
if ( yourls_is_serialized( $data, false ) )
return serialize( $data );
return $data;
}
/**
* Check value to find if it was serialized. Stolen from WordPress
*
* @since 1.4
* @param mixed $data Value to check to see if was serialized.
* @param bool $strict Optional. Whether to be strict about the end of the string. Defaults true.
* @return bool False if not serialized and true if it was.
*/
function yourls_is_serialized( $data, $strict = true ) {
// if it isn't a string, it isn't serialized
if ( ! is_string( $data ) )
return false;
$data = trim( $data );
if ( 'N;' == $data )
return true;
$length = strlen( $data );
if ( $length < 4 )
return false;
if ( ':' !== $data[1] )
return false;
if ( $strict ) {
$lastc = $data[ $length - 1 ];
if ( ';' !== $lastc && '}' !== $lastc )
return false;
} else {
$semicolon = strpos( $data, ';' );
$brace = strpos( $data, '}' );
// Either ; or } must exist.
if ( false === $semicolon && false === $brace )
return false;
// But neither must be in the first X characters.
if ( false !== $semicolon && $semicolon < 3 )
return false;
if ( false !== $brace && $brace < 4 )
return false;
}
$token = $data[0];
switch ( $token ) {
case 's' :
if ( $strict ) {
if ( '"' !== $data[ $length - 2 ] )
return false;
} elseif ( false === strpos( $data, '"' ) ) {
return false;
}
// or else fall through
case 'a' :
case 'O' :
return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
case 'b' :
case 'i' :
case 'd' :
$end = $strict ? '$' : '';
return (bool) preg_match( "/^{$token}:[0-9.E-]+;$end/", $data );
}
return false;
}
/**
* Unserialize value only if it was serialized. Stolen from WP
*
* @since 1.4
* @param string $original Maybe unserialized original, if is needed.
* @return mixed Unserialized data can be any type.
*/
function yourls_maybe_unserialize( $original ) {
if ( yourls_is_serialized( $original ) ) // don't attempt to unserialize data that wasn't serialized going in
return @unserialize( $original );
return $original;
}
/**
* Determine if the current page is private
*
*/
function yourls_is_private() {
$private = false;
if ( defined('YOURLS_PRIVATE') && YOURLS_PRIVATE == true ) {
// Allow overruling for particular pages:
// API
if( yourls_is_API() ) {
if( !defined('YOURLS_PRIVATE_API') || YOURLS_PRIVATE_API != false )
$private = true;
// Infos
} elseif( yourls_is_infos() ) {
if( !defined('YOURLS_PRIVATE_INFOS') || YOURLS_PRIVATE_INFOS !== false )
$private = true;
// Others
} else {
$private = true;
}
}
return yourls_apply_filter( 'is_private', $private );
}
/**
* Show login form if required
*
*/
function yourls_maybe_require_auth() {
if( yourls_is_private() ) {
yourls_do_action( 'require_auth' );
require_once( YOURLS_INC.'/auth.php' );
} else {
yourls_do_action( 'require_no_auth' );
}
}
/**
* Allow several short URLs for the same long URL ?
*
*/
function yourls_allow_duplicate_longurls() {
// special treatment if API to check for WordPress plugin requests
if( yourls_is_API() ) {
if ( isset($_REQUEST['source']) && $_REQUEST['source'] == 'plugin' )
return false;
}
return ( defined( 'YOURLS_UNIQUE_URLS' ) && YOURLS_UNIQUE_URLS == false );
}
/**
* Return array of keywords that redirect to the submitted long URL
*
* @since 1.7
* @param string $longurl long url
* @param string $sort Optional ORDER BY order (can be 'keyword', 'title', 'timestamp' or'clicks')
* @param string $order Optional SORT order (can be 'ASC' or 'DESC')
* @return array array of keywords
*/
function yourls_get_longurl_keywords( $longurl, $sort = 'none', $order = 'ASC' ) {
global $ydb;
$longurl = yourls_escape( yourls_sanitize_url( $longurl ) );
$table = YOURLS_DB_TABLE_URL;
$query = "SELECT `keyword` FROM `$table` WHERE `url` = '$longurl'";
// Ensure sort is a column in database (@TODO: update verification array if database changes)
if ( in_array( $sort, array('keyword','title','timestamp','clicks') ) ) {
$query .= " ORDER BY '".$sort."'";
if ( in_array( $order, array( 'ASC','DESC' ) ) ) {
$query .= " ".$order;
}
}
return yourls_apply_filter( 'get_longurl_keywords', $ydb->get_col( $query ), $longurl );
}
/**
* Check if an IP shortens URL too fast to prevent DB flood. Return true, or die.
*
*/
function yourls_check_IP_flood( $ip = '' ) {
// Allow plugins to short-circuit the whole function
$pre = yourls_apply_filter( 'shunt_check_IP_flood', false, $ip );
if ( false !== $pre )
return $pre;
yourls_do_action( 'pre_check_ip_flood', $ip ); // at this point $ip can be '', check it if your plugin hooks in here
// Raise white flag if installing or if no flood delay defined
if(
( defined('YOURLS_FLOOD_DELAY_SECONDS') && YOURLS_FLOOD_DELAY_SECONDS === 0 ) ||
!defined('YOURLS_FLOOD_DELAY_SECONDS') ||
yourls_is_installing()
)
return true;
// Don't throttle logged in users
if( yourls_is_private() ) {
if( yourls_is_valid_user() === true )
return true;
}
// Don't throttle whitelist IPs
if( defined( 'YOURLS_FLOOD_IP_WHITELIST' ) && YOURLS_FLOOD_IP_WHITELIST ) {
$whitelist_ips = explode( ',', YOURLS_FLOOD_IP_WHITELIST );
foreach( (array)$whitelist_ips as $whitelist_ip ) {
$whitelist_ip = trim( $whitelist_ip );
if ( $whitelist_ip == $ip )
return true;
}
}
$ip = ( $ip ? yourls_sanitize_ip( $ip ) : yourls_get_IP() );
$ip = yourls_escape( $ip );
yourls_do_action( 'check_ip_flood', $ip );
global $ydb;
$table = YOURLS_DB_TABLE_URL;
$lasttime = $ydb->get_var( "SELECT `timestamp` FROM $table WHERE `ip` = '$ip' ORDER BY `timestamp` DESC LIMIT 1" );
if( $lasttime ) {
$now = date( 'U' );
$then = date( 'U', strtotime( $lasttime ) );
if( ( $now - $then ) <= YOURLS_FLOOD_DELAY_SECONDS ) {
// Flood!
yourls_do_action( 'ip_flood', $ip, $now - $then );
yourls_die( yourls__( 'Too many URLs added too fast. Slow down please.' ), yourls__( 'Forbidden' ), 403 );
}
}
return true;
}
/**
* Check if YOURLS is installing
*
* @return bool
* @since 1.6
*/
function yourls_is_installing() {
$installing = defined( 'YOURLS_INSTALLING' ) && YOURLS_INSTALLING == true;
return yourls_apply_filter( 'is_installing', $installing );
}
/**
* Check if YOURLS is upgrading
*
* @return bool
* @since 1.6
*/
function yourls_is_upgrading() {
$upgrading = defined( 'YOURLS_UPGRADING' ) && YOURLS_UPGRADING == true;
return yourls_apply_filter( 'is_upgrading', $upgrading );
}
/**
* Check if YOURLS is installed
*
* Checks property $ydb->installed that is created by yourls_get_all_options()
*
* See inline comment for updating from 1.3 or prior.
*
*/
function yourls_is_installed() {
global $ydb;
$is_installed = ( property_exists( $ydb, 'installed' ) && $ydb->installed == true );
return yourls_apply_filter( 'is_installed', $is_installed );
/* Note: this test won't work on YOURLS 1.3 or older (Aug 2009...)
Should someone complain that they cannot upgrade directly from
1.3 to 1.7: first, laugh at them, then ask them to install 1.6 first.
*/
}
/**
* Generate random string of (int)$length length and type $type (see function for details)
*
*/
function yourls_rnd_string ( $length = 5, $type = 0, $charlist = '' ) {
$str = '';
$length = intval( $length );
// define possible characters
switch ( $type ) {
// custom char list, or comply to charset as defined in config
case '0':
$possible = $charlist ? $charlist : yourls_get_shorturl_charset() ;
break;
// no vowels to make no offending word, no 0/1/o/l to avoid confusion between letters & digits. Perfect for passwords.
case '1':
$possible = "23456789bcdfghjkmnpqrstvwxyz";
break;
// Same, with lower + upper
case '2':
$possible = "23456789bcdfghjkmnpqrstvwxyzBCDFGHJKMNPQRSTVWXYZ";
break;
// all letters, lowercase
case '3':
$possible = "abcdefghijklmnopqrstuvwxyz";
break;
// all letters, lowercase + uppercase
case '4':
$possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
break;
// all digits & letters lowercase
case '5':
$possible = "0123456789abcdefghijklmnopqrstuvwxyz";
break;
// all digits & letters lowercase + uppercase
case '6':
$possible = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
break;
}
$str = substr( str_shuffle( $possible ), 0, $length );
return yourls_apply_filter( 'rnd_string', $str, $length, $type, $charlist );
}
/**
* Return salted string
*
*/
function yourls_salt( $string ) {
$salt = defined('YOURLS_COOKIEKEY') ? YOURLS_COOKIEKEY : md5(__FILE__) ;
return yourls_apply_filter( 'yourls_salt', md5 ($string . $salt), $string );
}
/**
* Add a query var to a URL and return URL. Completely stolen from WP.
*
* Works with one of these parameter patterns:
* array( 'var' => 'value' )
* array( 'var' => 'value' ), $url
* 'var', 'value'
* 'var', 'value', $url
* If $url omitted, uses $_SERVER['REQUEST_URI']
*
* The result of this function call is a URL : it should be escaped before being printed as HTML
*
* @since 1.5
* @param string|array $param1 Either newkey or an associative_array.
* @param string $param2 Either newvalue or oldquery or URI.
* @param string $param3 Optional. Old query or URI.
* @return string New URL query string.
*/
function yourls_add_query_arg() {
$ret = '';
if ( is_array( func_get_arg(0) ) ) {
if ( @func_num_args() < 2 || false === @func_get_arg( 1 ) )
$uri = $_SERVER['REQUEST_URI'];
else
$uri = @func_get_arg( 1 );
} else {
if ( @func_num_args() < 3 || false === @func_get_arg( 2 ) )
$uri = $_SERVER['REQUEST_URI'];
else
$uri = @func_get_arg( 2 );
}
$uri = str_replace( '&', '&', $uri );
if ( $frag = strstr( $uri, '#' ) )
$uri = substr( $uri, 0, -strlen( $frag ) );
else
$frag = '';
if ( preg_match( '|^https?://|i', $uri, $matches ) ) {
$protocol = $matches[0];
$uri = substr( $uri, strlen( $protocol ) );
} else {
$protocol = '';
}
if ( strpos( $uri, '?' ) !== false ) {
$parts = explode( '?', $uri, 2 );
if ( 1 == count( $parts ) ) {
$base = '?';
$query = $parts[0];
} else {
$base = $parts[0] . '?';
$query = $parts[1];
}
} elseif ( !empty( $protocol ) || strpos( $uri, '=' ) === false ) {
$base = $uri . '?';
$query = '';
} else {
$base = '';
$query = $uri;
}
parse_str( $query, $qs );
$qs = yourls_urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string
if ( is_array( func_get_arg( 0 ) ) ) {
$kayvees = func_get_arg( 0 );
$qs = array_merge( $qs, $kayvees );
} else {
$qs[func_get_arg( 0 )] = func_get_arg( 1 );
}
foreach ( (array) $qs as $k => $v ) {
if ( $v === false )
unset( $qs[$k] );
}
$ret = http_build_query( $qs );
$ret = trim( $ret, '?' );
$ret = preg_replace( '#=(&|$)#', '$1', $ret );
$ret = $protocol . $base . $ret . $frag;
$ret = rtrim( $ret, '?' );
return $ret;
}
/**
* Navigates through an array and encodes the values to be used in a URL. Stolen from WP, used in yourls_add_query_arg()
*
*/
function yourls_urlencode_deep( $value ) {
$value = is_array( $value ) ? array_map( 'yourls_urlencode_deep', $value ) : urlencode( $value );
return $value;
}
/**
* Remove arg from query. Opposite of yourls_add_query_arg. Stolen from WP.
*
* The result of this function call is a URL : it should be escaped before being printed as HTML
*
* @since 1.5
* @param string|array $key Query key or keys to remove.
* @param bool|string $query Optional. When false uses the $_SERVER value. Default false.
* @return string New URL query string.
*/
function yourls_remove_query_arg( $key, $query = false ) {
if ( is_array( $key ) ) { // removing multiple keys
foreach ( $key as $k )
$query = yourls_add_query_arg( $k, false, $query );
return $query;
}
return yourls_add_query_arg( $key, false, $query );
}
/**
* Return a time-dependent string for nonce creation
*
*/
function yourls_tick() {
return ceil( time() / YOURLS_NONCE_LIFE );
}
/**
* Create a time limited, action limited and user limited token
*
*/
function yourls_create_nonce( $action, $user = false ) {
if( false == $user )
$user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1';
$tick = yourls_tick();
return substr( yourls_salt($tick . $action . $user), 0, 10 );
}
/**
* Create a nonce field for inclusion into a form
*
*/
function yourls_nonce_field( $action, $name = 'nonce', $user = false, $echo = true ) {
$field = '';
if( $echo )
echo $field."\n";
return $field;
}
/**
* Add a nonce to a URL. If URL omitted, adds nonce to current URL
*
*/
function yourls_nonce_url( $action, $url = false, $name = 'nonce', $user = false ) {
$nonce = yourls_create_nonce( $action, $user );
return yourls_add_query_arg( $name, $nonce, $url );
}
/**
* Check validity of a nonce (ie time span, user and action match).
*
* Returns true if valid, dies otherwise (yourls_die() or die($return) if defined)
* if $nonce is false or unspecified, it will use $_REQUEST['nonce']
*
*/
function yourls_verify_nonce( $action, $nonce = false, $user = false, $return = '' ) {
// get user
if( false == $user )
$user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1';
// get current nonce value
if( false == $nonce && isset( $_REQUEST['nonce'] ) )
$nonce = $_REQUEST['nonce'];
// what nonce should be
$valid = yourls_create_nonce( $action, $user );
if( $nonce == $valid ) {
return true;
} else {
if( $return )
die( $return );
yourls_die( yourls__( 'Unauthorized action or expired link' ), yourls__( 'Error' ), 403 );
}
}
/**
* Converts keyword into short link (prepend with YOURLS base URL)
*
*/
function yourls_link( $keyword = '' ) {
$link = YOURLS_SITE . '/' . yourls_sanitize_keyword( $keyword );
return yourls_apply_filter( 'yourls_link', $link, $keyword );
}
/**
* Converts keyword into stat link (prepend with YOURLS base URL, append +)
*
*/
function yourls_statlink( $keyword = '' ) {
$link = YOURLS_SITE . '/' . yourls_sanitize_keyword( $keyword ) . '+';
if( yourls_is_ssl() )
$link = yourls_set_url_scheme( $link, 'https' );
return yourls_apply_filter( 'yourls_statlink', $link, $keyword );
}
/**
* Check if we're in API mode. Returns bool
*
*/
function yourls_is_API() {
$return = defined( 'YOURLS_API' ) && YOURLS_API == true;
return yourls_apply_filter( 'is_API', $return );
}
/**
* Check if we're in Ajax mode. Returns bool
*
*/
function yourls_is_Ajax() {
$return = defined( 'YOURLS_AJAX' ) && YOURLS_AJAX == true;
return yourls_apply_filter( 'is_Ajax', $return );
}
/**
* Check if we're in GO mode (yourls-go.php). Returns bool
*
*/
function yourls_is_GO() {
$return = defined( 'YOURLS_GO' ) && YOURLS_GO == true;
return yourls_apply_filter( 'is_GO', $return );
}
/**
* Check if we're displaying stats infos (yourls-infos.php). Returns bool
*
*/
function yourls_is_infos() {
$return = defined( 'YOURLS_INFOS' ) && YOURLS_INFOS == true;
return yourls_apply_filter( 'is_infos', $return );
}
/**
* Check if we're in the admin area. Returns bool
*
*/
function yourls_is_admin() {
$return = defined( 'YOURLS_ADMIN' ) && YOURLS_ADMIN == true;
return yourls_apply_filter( 'is_admin', $return );
}
/**
* Check if the server seems to be running on Windows. Not exactly sure how reliable this is.
*
*/
function yourls_is_windows() {
return defined( 'DIRECTORY_SEPARATOR' ) && DIRECTORY_SEPARATOR == '\\';
}
/**
* Check if SSL is required. Returns bool.
*
*/
function yourls_needs_ssl() {
$return = defined('YOURLS_ADMIN_SSL') && YOURLS_ADMIN_SSL == true;
return yourls_apply_filter( 'needs_ssl', $return );
}
/**
* Return admin link, with SSL preference if applicable.
*
*/
function yourls_admin_url( $page = '' ) {
$admin = YOURLS_SITE . '/admin/' . $page;
if( yourls_is_ssl() or yourls_needs_ssl() ) {
$admin = yourls_set_url_scheme( $admin, 'https' );
}
return yourls_apply_filter( 'admin_url', $admin, $page );
}
/**
* Return YOURLS_SITE or URL under YOURLS setup, with SSL preference
*
*/
function yourls_site_url( $echo = true, $url = '' ) {
$url = yourls_get_relative_url( $url );
$url = trim( YOURLS_SITE . '/' . $url, '/' );
// Do not enforce (checking yourls_need_ssl() ) but check current usage so it won't force SSL on non-admin pages
if( yourls_is_ssl() ) {
$url = yourls_set_url_scheme( $url, 'https' );
}
$url = yourls_apply_filter( 'site_url', $url );
if( $echo ) {
echo $url;
}
return $url;
}
/**
* Check if SSL is used, returns bool. Stolen from WP.
*
*/
function yourls_is_ssl() {
$is_ssl = false;
if ( isset( $_SERVER['HTTPS'] ) ) {
if ( 'on' == strtolower( $_SERVER['HTTPS'] ) )
$is_ssl = true;
if ( '1' == $_SERVER['HTTPS'] )
$is_ssl = true;
} elseif ( isset( $_SERVER['SERVER_PORT'] ) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
$is_ssl = true;
}
return yourls_apply_filter( 'is_ssl', $is_ssl );
}
/**
* Get a remote page title
*
* This function returns a string: either the page title as defined in HTML, or the URL if not found
* The function tries to convert funky characters found in titles to UTF8, from the detected charset.
* Charset in use is guessed from HTML meta tag, or if not found, from server's 'content-type' response.
*
* @param string $url URL
* @return string Title (sanitized) or the URL if no title found
*/
function yourls_get_remote_title( $url ) {
// Allow plugins to short-circuit the whole function
$pre = yourls_apply_filter( 'shunt_get_remote_title', false, $url );
if ( false !== $pre )
return $pre;
$url = yourls_sanitize_url( $url );
// Only deal with http(s)://
if( !in_array( yourls_get_protocol( $url ), array( 'http://', 'https://' ) ) )
return $url;
$title = $charset = false;
$max_bytes = yourls_apply_filter( 'get_remote_title_max_byte', 2048 ); // limit data fetching to 2K in order to find a tag
$response = yourls_http_get( $url, array(), array(), array( 'max_bytes' => $max_bytes ) ); // can be a Request object or an error string
if( is_string( $response ) ) {
return $url;
}
// Page content. No content? Return the URL
$content = $response->body;
if( !$content )
return $url;
// look for . No title found? Return the URL
if ( preg_match('/(.*?)<\/title>/is', $content, $found ) ) {
$title = $found[1];
unset( $found );
}
if( !$title )
return $url;
// Now we have a title. We'll try to get proper utf8 from it.
// Get charset as (and if) defined by the HTML meta tag. We should match
//
// or and all possible variations: see https://gist.github.com/ozh/7951236
if ( preg_match( '/]*charset\s*=["\' ]*([a-zA-Z0-9\-_]+)/is', $content, $found ) ) {
$charset = $found[1];
unset( $found );
} else {
// No charset found in HTML. Get charset as (and if) defined by the server response
$_charset = current( $response->headers->getValues( 'content-type' ) );
if( preg_match( '/charset=(\S+)/', $_charset, $found ) ) {
$charset = trim( $found[1], ';' );
unset( $found );
}
}
// Conversion to utf-8 if what we have is not utf8 already
if( strtolower( $charset ) != 'utf-8' && function_exists( 'mb_convert_encoding' ) ) {
// We use @ to remove warnings because mb_ functions are easily bitching about illegal chars
if( $charset ) {
$title = @mb_convert_encoding( $title, 'UTF-8', $charset );
} else {
$title = @mb_convert_encoding( $title, 'UTF-8' );
}
}
// Remove HTML entities
$title = html_entity_decode( $title, ENT_QUOTES, 'UTF-8' );
// Strip out evil things
$title = yourls_sanitize_title( $title, $url );
return yourls_apply_filter( 'get_remote_title', $title, $url );
}
/**
* Quick UA check for mobile devices. Return boolean.
*
*/
function yourls_is_mobile_device() {
// Strings searched
$mobiles = array(
'android', 'blackberry', 'blazer',
'compal', 'elaine', 'fennec', 'hiptop',
'iemobile', 'iphone', 'ipod', 'ipad',
'iris', 'kindle', 'opera mobi', 'opera mini',
'palm', 'phone', 'pocket', 'psp', 'symbian',
'treo', 'wap', 'windows ce', 'windows phone'
);
// Current user-agent
$current = strtolower( $_SERVER['HTTP_USER_AGENT'] );
// Check and return
$is_mobile = ( str_replace( $mobiles, '', $current ) != $current );
return yourls_apply_filter( 'is_mobile_device', $is_mobile );
}
/**
* Get request in YOURLS base (eg in 'http://sho.rt/yourls/abcd' get 'abdc')
*
*/
function yourls_get_request() {
// Allow plugins to short-circuit the whole function
$pre = yourls_apply_filter( 'shunt_get_request', false );
if ( false !== $pre )
return $pre;
static $request = null;
yourls_do_action( 'pre_get_request', $request );
if( $request !== null )
return $request;
// Ignore protocol & www. prefix
$root = str_replace( array( 'https://', 'http://', 'https://www.', 'http://www.' ), '', YOURLS_SITE );
// Case insensitive comparison of the YOURLS root to match both http://Sho.rt/blah and http://sho.rt/blah
$request = preg_replace( "!$root/!i", '', $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], 1 );
// Unless request looks like a full URL (ie request is a simple keyword) strip query string
if( !preg_match( "@^[a-zA-Z]+://.+@", $request ) ) {
$request = current( explode( '?', $request ) );
}
return yourls_apply_filter( 'get_request', $request );
}
/**
* Change protocol to match current scheme used (http or https)
*
*/
function yourls_match_current_protocol( $url, $normal = 'http://', $ssl = 'https://' ) {
if( yourls_is_ssl() )
$url = str_replace( $normal, $ssl, $url );
return yourls_apply_filter( 'match_current_protocol', $url );
}
/**
* Fix $_SERVER['REQUEST_URI'] variable for various setups. Stolen from WP.
*
*/
function yourls_fix_request_uri() {
$default_server_values = array(
'SERVER_SOFTWARE' => '',
'REQUEST_URI' => '',
);
$_SERVER = array_merge( $default_server_values, $_SERVER );
// Fix for IIS when running with PHP ISAPI
if ( empty( $_SERVER['REQUEST_URI'] ) || ( php_sapi_name() != 'cgi-fcgi' && preg_match( '/^Microsoft-IIS\//', $_SERVER['SERVER_SOFTWARE'] ) ) ) {
// IIS Mod-Rewrite
if ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) {
$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];
}
// IIS Isapi_Rewrite
else if ( isset( $_SERVER['HTTP_X_REWRITE_URL'] ) ) {
$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];
} else {
// Use ORIG_PATH_INFO if there is no PATH_INFO
if ( !isset( $_SERVER['PATH_INFO'] ) && isset( $_SERVER['ORIG_PATH_INFO'] ) )
$_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO'];
// Some IIS + PHP configurations puts the script-name in the path-info (No need to append it twice)
if ( isset( $_SERVER['PATH_INFO'] ) ) {
if ( $_SERVER['PATH_INFO'] == $_SERVER['SCRIPT_NAME'] )
$_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO'];
else
$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO'];
}
// Append the query string if it exists and isn't null
if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {
$_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
}
}
}
}
/**
* Shutdown function, runs just before PHP shuts down execution. Stolen from WP
*
*/
function yourls_shutdown() {
yourls_do_action( 'shutdown' );
}
/**
* Auto detect custom favicon in /user directory, fallback to YOURLS favicon, and echo/return its URL
*
*/
function yourls_favicon( $echo = true ) {
static $favicon = null;
if( $favicon !== null ) {
if( $echo ) {
echo $favicon;
}
return $favicon;
}
$custom = null;
// search for favicon.(gif|ico|png|jpg|svg)
foreach( array( 'gif', 'ico', 'png', 'jpg', 'svg' ) as $ext ) {
if( file_exists( YOURLS_USERDIR. '/favicon.' . $ext ) ) {
$custom = 'favicon.' . $ext;
break;
}
}
if( $custom ) {
$favicon = yourls_site_url( false, YOURLS_USERURL . '/' . $custom );
} else {
$favicon = yourls_site_url( false ) . '/images/favicon.gif';
}
if( $echo ) {
echo $favicon;
}
return $favicon;
}
/**
* Check for maintenance mode. If yes, die. See yourls_maintenance_mode(). Stolen from WP.
*
*/
function yourls_check_maintenance_mode() {
$file = YOURLS_ABSPATH . '/.maintenance' ;
if ( !file_exists( $file ) || yourls_is_upgrading() || yourls_is_installing() )
return;
global $maintenance_start;
include_once( $file );
// If the $maintenance_start timestamp is older than 10 minutes, don't die.
if ( ( time() - $maintenance_start ) >= 600 )
return;
// Use any /user/maintenance.php file
if( file_exists( YOURLS_USERDIR.'/maintenance.php' ) ) {
include_once( YOURLS_USERDIR.'/maintenance.php' );
die();
}
// https://www.youtube.com/watch?v=Xw-m4jEY-Ns
$title = yourls__( 'Service temporarily unavailable' );
$message = yourls__( 'Our service is currently undergoing scheduled maintenance.' ) . "
\n
" .
yourls__( 'Things should not last very long, thank you for your patience and please excuse the inconvenience' );
yourls_die( $message, $title , 503 );
}
/**
* Return current admin page, or null if not an admin page
*
* @return mixed string if admin page, null if not an admin page
* @since 1.6
*/
function yourls_current_admin_page() {
if( yourls_is_admin() ) {
$current = substr( yourls_get_request(), 6 );
if( $current === false )
$current = 'index.php'; // if current page is http://sho.rt/admin/ instead of http://sho.rt/admin/index.php
return $current;
}
return null;
}
/**
* Check if a URL protocol is allowed
*
* Checks a URL against a list of whitelisted protocols. Protocols must be defined with
* their complete scheme name, ie 'stuff:' or 'stuff://' (for instance, 'mailto:' is a valid
* protocol, 'mailto://' isn't, and 'http:' with no double slashed isn't either
*
* @since 1.6
* @see yourls_get_protocol()
*
* @param string $url URL to be check
* @param array $protocols Optional. Array of protocols, defaults to global $yourls_allowedprotocols
* @return boolean true if protocol allowed, false otherwise
*/
function yourls_is_allowed_protocol( $url, $protocols = array() ) {
if( ! $protocols ) {
global $yourls_allowedprotocols;
$protocols = $yourls_allowedprotocols;
}
$protocol = yourls_get_protocol( $url );
return yourls_apply_filter( 'is_allowed_protocol', in_array( $protocol, $protocols ), $url, $protocols );
}
/**
* Get protocol from a URL (eg mailto:, http:// ...)
*
* What we liberally call a "protocol" in YOURLS is the scheme name + colon + double slashes if present of a URI. Examples:
* "something://blah" -> "something://"
* "something:blah" -> "something:"
* "something:/blah" -> "something:"
*
* Unit Tests for this function are located in tests/format/urls.php
*
* @since 1.6
*
* @param string $url URL to be check
* @return string Protocol, with slash slash if applicable. Empty string if no protocol
*/
function yourls_get_protocol( $url ) {
preg_match( '!^[a-zA-Z][a-zA-Z0-9\+\.-]+:(//)?!', $url, $matches );
/*
http://en.wikipedia.org/wiki/URI_scheme#Generic_syntax
The scheme name consists of a sequence of characters beginning with a letter and followed by any
combination of letters, digits, plus ("+"), period ("."), or hyphen ("-"). Although schemes are
case-insensitive, the canonical form is lowercase and documents that specify schemes must do so
with lowercase letters. It is followed by a colon (":").
*/
$protocol = ( isset( $matches[0] ) ? $matches[0] : '' );
return yourls_apply_filter( 'get_protocol', $protocol, $url );
}
/**
* Get relative URL (eg 'abc' from 'http://sho.rt/abc')
*
* Treat indifferently http & https. If a URL isn't relative to the YOURLS install, return it as is
* or return empty string if $strict is true
*
* @since 1.6
* @param string $url URL to relativize
* @param bool $strict if true and if URL isn't relative to YOURLS install, return empty string
* @return string URL
*/
function yourls_get_relative_url( $url, $strict = true ) {
$url = yourls_sanitize_url( $url );
// Remove protocols to make it easier
$noproto_url = str_replace( 'https:', 'http:', $url );
$noproto_site = str_replace( 'https:', 'http:', YOURLS_SITE );
// Trim URL from YOURLS root URL : if no modification made, URL wasn't relative
$_url = str_replace( $noproto_site . '/', '', $noproto_url );
if( $_url == $noproto_url )
$_url = ( $strict ? '' : $url );
return yourls_apply_filter( 'get_relative_url', $_url, $url );
}
/**
* Marks a function as deprecated and informs when it has been used. Stolen from WP.
*
* There is a hook deprecated_function that will be called that can be used
* to get the backtrace up to what file and function called the deprecated
* function.
*
* The current behavior is to trigger a user error if YOURLS_DEBUG is true.
*
* This function is to be used in every function that is deprecated.
*
* @since 1.6
* @uses yourls_do_action() Calls 'deprecated_function' and passes the function name, what to use instead,
* and the version the function was deprecated in.
* @uses yourls_apply_filter() Calls 'deprecated_function_trigger_error' and expects boolean value of true to do
* trigger or false to not trigger error.
*
* @param string $function The function that was called
* @param string $version The version of WordPress that deprecated the function
* @param string $replacement Optional. The function that should have been called
*/
function yourls_deprecated_function( $function, $version, $replacement = null ) {
yourls_do_action( 'deprecated_function', $function, $replacement, $version );
// Allow plugin to filter the output error trigger
if ( YOURLS_DEBUG && yourls_apply_filter( 'deprecated_function_trigger_error', true ) ) {
if ( ! is_null( $replacement ) )
trigger_error( sprintf( yourls__('%1$s is deprecated since version %2$s! Use %3$s instead.'), $function, $version, $replacement ) );
else
trigger_error( sprintf( yourls__('%1$s is deprecated since version %2$s with no alternative available.'), $function, $version ) );
}
}
/**
* Return the value if not an empty string
*
* Used with array_filter(), to remove empty keys but not keys with value 0 or false
*
* @since 1.6
* @param mixed $val Value to test against ''
* @return bool True if not an empty string
*/
function yourls_return_if_not_empty_string( $val ) {
return( $val !== '' );
}
/**
* Returns true.
*
* Useful for returning true to filters easily.
*
* @since 1.7.1
* @return bool True.
*/
function yourls_return_true() {
return true;
}
/**
* Returns false.
*
* Useful for returning false to filters easily.
*
* @since 1.7.1
* @return bool False.
*/
function yourls_return_false() {
return false;
}
/**
* Returns 0.
*
* Useful for returning 0 to filters easily.
*
* @since 1.7.1
* @return int 0.
*/
function yourls_return_zero() {
return 0;
}
/**
* Returns an empty array.
*
* Useful for returning an empty array to filters easily.
*
* @since 1.7.1
* @return array Empty array.
*/
function yourls_return_empty_array() {
return array();
}
/**
* Returns null.
*
* Useful for returning null to filters easily.
*
* @since 1.7.1
* @return null Null value.
*/
function yourls_return_null() {
return null;
}
/**
* Returns an empty string.
*
* Useful for returning an empty string to filters easily.
*
* @since 1.7.1
* @return string Empty string.
*/
function yourls_return_empty_string() {
return '';
}
/**
* Add a message to the debug log
*
* When in debug mode ( YOURLS_DEBUG == true ) the debug log is echoed in yourls_html_footer()
* Log messages are appended to $ydb->debug_log array, which is instanciated within class ezSQLcore_YOURLS
*
* @since 1.7
* @param string $msg Message to add to the debug log
* @return string The message itself
*/
function yourls_debug_log( $msg ) {
global $ydb;
$ydb->debug_log[] = $msg;
return $msg;
}
/**
* Explode a URL in an array of ( 'protocol' , 'slashes if any', 'rest of the URL' )
*
* Some hosts trip up when a query string contains 'http://' - see http://git.io/j1FlJg
* The idea is that instead of passing the whole URL to a bookmarklet, eg index.php?u=http://blah.com,
* we pass it by pieces to fool the server, eg index.php?proto=http:&slashes=//&rest=blah.com
*
* Known limitation: this won't work if the rest of the URL itself contains 'http://', for example
* if rest = blah.com/file.php?url=http://foo.com
*
* Sample returns:
*
* with 'mailto:jsmith@example.com?subject=hey' :
* array( 'protocol' => 'mailto:', 'slashes' => '', 'rest' => 'jsmith@example.com?subject=hey' )
*
* with 'http://example.com/blah.html' :
* array( 'protocol' => 'http:', 'slashes' => '//', 'rest' => 'example.com/blah.html' )
*
* @since 1.7
* @param string $url URL to be parsed
* @param array $array Optional, array of key names to be used in returned array
* @return mixed false if no protocol found, array of ('protocol' , 'slashes', 'rest') otherwise
*/
function yourls_get_protocol_slashes_and_rest( $url, $array = array( 'protocol', 'slashes', 'rest' ) ) {
$proto = yourls_get_protocol( $url );
if( !$proto or count( $array ) != 3 )
return false;
list( $null, $rest ) = explode( $proto, $url, 2 );
list( $proto, $slashes ) = explode( ':', $proto );
return array( $array[0] => $proto . ':', $array[1] => $slashes, $array[2] => $rest );
}
/**
* Set URL scheme (to HTTP or HTTPS)
*
* @since 1.7.1
* @param string $url URL
* @param string $scheme scheme, either 'http' or 'https'
* @return string URL with chosen scheme
*/
function yourls_set_url_scheme( $url, $scheme = false ) {
if( $scheme != 'http' && $scheme != 'https' ) {
return $url;
}
return preg_replace( '!^[a-zA-Z0-9\+\.-]+://!', $scheme . '://', $url );
}