functions.php

Created Diff never expires
171 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
867 lines
127 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
823 lines
<?php
<?php
/*
/*
* YOURLS general functions
* YOURLS general functions
*
*
*/
*/


/**
/**
* Make an optimized regexp pattern from a string of characters
* Make an optimized regexp pattern from a string of characters
*
*
* @param string $string
* @param string $string
* @return string
* @return string
*/
*/
function yourls_make_regexp_pattern( $string ) {
function yourls_make_regexp_pattern( $string ) {
// Simple benchmarks show that regexp with smarter sequences (0-9, a-z, A-Z...) are not faster or slower than 0123456789 etc...
// Simple benchmarks show that regexp with smarter sequences (0-9, a-z, A-Z...) are not faster or slower than 0123456789 etc...
// add @ as an escaped character because @ is used as the regexp delimiter in yourls-loader.php
// add @ as an escaped character because @ is used as the regexp delimiter in yourls-loader.php
return preg_quote( $string, '@' );
return preg_quote( $string, '@' );
}
}


/**
/**
* Get client IP Address. Returns a DB safe string.
* Get client IP Address. Returns a DB safe string.
*
*
* @return string
* @return string
*/
*/
function yourls_get_IP() {
function yourls_get_IP() {
$ip = '';
$ip = '';


// Precedence: if set, X-Forwarded-For > HTTP_X_FORWARDED_FOR > HTTP_CLIENT_IP > HTTP_VIA > REMOTE_ADDR
// Precedence: if set, X-Forwarded-For > HTTP_X_FORWARDED_FOR > HTTP_CLIENT_IP > HTTP_VIA > REMOTE_ADDR
$headers = [ 'X-Forwarded-For', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_VIA', 'REMOTE_ADDR' ];
$headers = [ 'X-Forwarded-For', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_VIA', 'REMOTE_ADDR' ];
foreach( $headers as $header ) {
foreach( $headers as $header ) {
if ( !empty( $_SERVER[ $header ] ) ) {
if ( !empty( $_SERVER[ $header ] ) ) {
$ip = $_SERVER[ $header ];
$ip = $_SERVER[ $header ];
break;
break;
}
}
}
}


// headers can contain multiple IPs (X-Forwarded-For = client, proxy1, proxy2). Take first one.
// headers can contain multiple IPs (X-Forwarded-For = client, proxy1, proxy2). Take first one.
if ( strpos( $ip, ',' ) !== false )
if ( strpos( $ip, ',' ) !== false )
$ip = substr( $ip, 0, strpos( $ip, ',' ) );
$ip = substr( $ip, 0, strpos( $ip, ',' ) );


return (string)yourls_apply_filter( 'get_IP', yourls_sanitize_ip( $ip ) );
return (string)yourls_apply_filter( 'get_IP', yourls_sanitize_ip( $ip ) );
}
}


/**
/**
* Get next id a new link will have if no custom keyword provided
* Get next id a new link will have if no custom keyword provided
*
*
* @since 1.0
* @since 1.0
* @return int id of next link
* @return int id of next link
*/
*/
function yourls_get_next_decimal() {
function yourls_get_next_decimal() {
return (int)yourls_apply_filter( 'get_next_decimal', (int)yourls_get_option( 'next_id' ) );
return (int)yourls_apply_filter( 'get_next_decimal', (int)yourls_get_option( 'next_id' ) );
}
}


/**
/**
* Update id for next link with no custom keyword
* Update id for next link with no custom keyword
*
*
* Note: this function relies upon yourls_update_option(), which will return either true or false
* Note: this function relies upon yourls_update_option(), which will return either true or false
* depending upon if there has been an actual MySQL query updating the DB.
* depending upon if there has been an actual MySQL query updating the DB.
* In other words, this function may return false yet this would not mean it has functionally failed
* In other words, this function may return false yet this would not mean it has functionally failed
* In other words I'm not sure if we really need this function to return something :face_with_eyes_looking_up:
* In other words I'm not sure if we really need this function to return something :face_with_eyes_looking_up:
* See issue 2621 for more on this.
* See issue 2621 for more on this.
*
*
* @since 1.0
* @since 1.0
* @param integer $int id for next link
* @param integer $int id for next link
* @return bool true or false depending on if there has been an actual MySQL query. See note above.
* @return bool true or false depending on if there has been an actual MySQL query. See note above.
*/
*/
function yourls_update_next_decimal( $int = 0 ) {
function yourls_update_next_decimal( $int = 0 ) {
$int = ( $int == 0 ) ? yourls_get_next_decimal() + 1 : (int)$int ;
$int = ( $int == 0 ) ? yourls_get_next_decimal() + 1 : (int)$int ;
$update = yourls_update_option( 'next_id', $int );
$update = yourls_update_option( 'next_id', $int );
yourls_do_action( 'update_next_decimal', $int, $update );
yourls_do_action( 'update_next_decimal', $int, $update );
return $update;
return $update;
}
}


/**
/**
* Return XML output.
* Return XML output.
*
*
* @param array $array
* @param array $array
* @return string
* @return string
*/
*/
function yourls_xml_encode( $array ) {
function yourls_xml_encode( $array ) {
return (\Spatie\ArrayToXml\ArrayToXml::convert($array, '', true, 'UTF-8'));
return (\Spatie\ArrayToXml\ArrayToXml::convert($array, '', true, 'UTF-8'));
}
}


/**
/**
* Update click count on a short URL. Return 0/1 for error/success.
* Update click count on a short URL. Return 0/1 for error/success.
*
*
* @param string $keyword
* @param string $keyword
* @param false|int $clicks
* @param false|int $clicks
* @return int 0 or 1 for error/success
* @return int 0 or 1 for error/success
*/
*/
function yourls_update_clicks( $keyword, $clicks = false ) {
function yourls_update_clicks( $keyword, $clicks = false ) {
// Allow plugins to short-circuit the whole function
// Allow plugins to short-circuit the whole function
$pre = yourls_apply_filter( 'shunt_update_clicks', false, $keyword, $clicks );
$pre = yourls_apply_filter( 'shunt_update_clicks', false, $keyword, $clicks );
if ( false !== $pre ) {
if ( false !== $pre ) {
return $pre;
return $pre;
}
}


$keyword = yourls_sanitize_keyword( $keyword );
$keyword = yourls_sanitize_keyword( $keyword );
$table = YOURLS_DB_TABLE_URL;
$table = YOURLS_DB_TABLE_URL;
if ( $clicks !== false && is_int( $clicks ) && $clicks >= 0 ) {
if ( $clicks !== false && is_int( $clicks ) && $clicks >= 0 ) {
$update = "UPDATE `$table` SET `clicks` = :clicks WHERE `keyword` = :keyword";
$update = "UPDATE `$table` SET `clicks` = :clicks WHERE `keyword` = :keyword";
$values = [ 'clicks' => $clicks, 'keyword' => $keyword ];
$values = [ 'clicks' => $clicks, 'keyword' => $keyword ];
} else {
} else {
$update = "UPDATE `$table` SET `clicks` = clicks + 1 WHERE `keyword` = :keyword";
$update = "UPDATE `$table` SET `clicks` = clicks + 1 WHERE `keyword` = :keyword";
$values = [ 'keyword' => $keyword ];
$values = [ 'keyword' => $keyword ];
}
}


// Try and update click count. An error probably means a concurrency problem : just skip the update
// Try and update click count. An error probably means a concurrency problem : just skip the update
try {
try {
$result = yourls_get_db()->fetchAffected($update, $values);
$result = yourls_get_db()->fetchAffected($update, $values);
} catch (Exception $e) {
} catch (Exception $e) {
$result = 0;
$result = 0;
}
}


yourls_do_action( 'update_clicks', $keyword, $result, $clicks );
yourls_do_action( 'update_clicks', $keyword, $result, $clicks );


return $result;
return $result;
}
}




/**
/**
* Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return
* Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return
*
*
* @param string $filter 'bottom', 'last', 'rand' or 'top'
* @param string $filter 'bottom', 'last', 'rand' or 'top'
* @param int $limit Number of links to return
* @param int $limit Number of links to return
* @param int $start Offset to start from
* @param int $start Offset to start from
* @return array Array of links
* @return array Array of links
*/
*/
function yourls_get_stats($filter = 'top', $limit = 10, $start = 0) {
function yourls_get_stats($filter = 'top', $limit = 10, $start = 0) {
switch( $filter ) {
switch( $filter ) {
case 'bottom':
case 'bottom':
$sort_by = '`clicks`';
$sort_by = '`clicks`';
$sort_order = 'asc';
$sort_order = 'asc';
break;
break;
case 'last':
case 'last':
$sort_by = '`timestamp`';
$sort_by = '`timestamp`';
$sort_order = 'desc';
$sort_order = 'desc';
break;
break;
case 'rand':
case 'rand':
case 'random':
case 'random':
$sort_by = 'RAND()';
$sort_by = 'RAND()';
$sort_order = '';
$sort_order = '';
break;
break;
case 'top':
case 'top':
default:
default:
$sort_by = '`clicks`';
$sort_by = '`clicks`';
$sort_order = 'desc';
$sort_order = 'desc';
break;
break;
}
}


// Fetch links
// Fetch links
$limit = intval( $limit );
$limit = intval( $limit );
$start = intval( $start );
$start = intval( $start );
if ( $limit > 0 ) {
if ( $limit > 0 ) {


$table_url = YOURLS_DB_TABLE_URL;
$table_url = YOURLS_DB_TABLE_URL;
$results = yourls_get_db()->fetchObjects( "SELECT * FROM `$table_url` WHERE 1=1 ORDER BY $sort_by $sort_order LIMIT $start, $limit;" );
$results = yourls_get_db()->fetchObjects( "SELECT * FROM `$table_url` WHERE 1=1 ORDER BY $sort_by $sort_order LIMIT $start, $limit;" );


$return = [];
$return = [];
$i = 1;
$i = 1;


foreach ( (array)$results as $res ) {
foreach ( (array)$results as $res ) {
$return['links']['link_'.$i++] = [
$return['links']['link_'.$i++] = [
'shorturl' => yourls_link($res->keyword),
'shorturl' => yourls_link($res->keyword),
'url' => $res->url,
'url' => $res->url,
'title' => $res->title,
'title' => $res->title,
'timestamp'=> $res->timestamp,
'timestamp'=> $res->timestamp,
'ip' => $res->ip,
'ip' => $res->ip,
'clicks' => $res->clicks,
'clicks' => $res->clicks,
];
];
}
}
}
}


$return['stats'] = yourls_get_db_stats();
$return['stats'] = yourls_get_db_stats();


$return['statusCode'] = 200;
$return['statusCode'] = 200;


return yourls_apply_filter( 'get_stats', $return, $filter, $limit, $start );
return yourls_apply_filter( 'get_stats', $return, $filter, $limit, $start );
}
}


/**
/**
* Get total number of URLs and sum of clicks. Input: optional "AND WHERE" clause. Returns array
* Get total number of URLs and sum of clicks. Input: optional "AND WHERE" clause. Returns array
*
*
* The $where parameter will contain additional SQL arguments:
* The $where parameter will contain additional SQL arguments:
* $where['sql'] will concatenate SQL clauses: $where['sql'] = ' AND something = :value AND otherthing < :othervalue';
* $where['sql'] will concatenate SQL clauses: $where['sql'] = ' AND something = :value AND otherthing < :othervalue';
* $where['binds'] will hold the (name => value) placeholder pairs: $where['binds'] = array('value' => $value, 'othervalue' => $value2)
* $where['binds'] will hold the (name => value) placeholder pairs: $where['binds'] = array('value' => $value, 'othervalue' => $value2)
*
*
* @param array $where See comment above
* @param array $where See comment above
* @return array
* @return array
*/
*/
function yourls_get_db_stats( $where = [ 'sql' => '', 'binds' => [] ] ) {
function yourls_get_db_stats( $where = [ 'sql' => '', 'binds' => [] ] ) {
$table_url = YOURLS_DB_TABLE_URL;
$table_url = YOURLS_DB_TABLE_URL;


$totals = yourls_get_db()->fetchObject( "SELECT COUNT(keyword) as count, SUM(clicks) as sum FROM `$table_url` WHERE 1=1 " . $where['sql'] , $where['binds'] );
$totals = yourls_get_db()->fetchObject( "SELECT COUNT(keyword) as count, SUM(clicks) as sum FROM `$table_url` WHERE 1=1 " . $where['sql'] , $where['binds'] );
$return = [ 'total_links' => $totals->count, 'total_clicks' => $totals->sum ];
$return = [ 'total_links' => $totals->count, 'total_clicks' => $totals->sum ];


return yourls_apply_filter( 'get_db_stats', $return, $where );
return yourls_apply_filter( 'get_db_stats', $return, $where );
}
}


/**
/**
* Returns a sanitized a user agent string. Given what I found on http://www.user-agents.org/ it should be OK.
* Returns a sanitized a user agent string. Given what I found on http://www.user-agents.org/ it should be OK.
*
*
* @return string
* @return string
*/
*/
function yourls_get_user_agent() {
function yourls_get_user_agent() {
$ua = '-';
$ua = '-';


if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
$ua = strip_tags( html_entity_decode( $_SERVER['HTTP_USER_AGENT'] ));
$ua = strip_tags( html_entity_decode( $_SERVER['HTTP_USER_AGENT'] ));
$ua = preg_replace('![^0-9a-zA-Z\':., /{}\(\)\[\]\+@&\!\?;_\-=~\*\#]!', '', $ua );
$ua = preg_replace('![^0-9a-zA-Z\':., /{}\(\)\[\]\+@&\!\?;_\-=~\*\#]!', '', $ua );
}
}


return yourls_apply_filter( 'get_user_agent', substr( $ua, 0, 255 ) );
return yourls_apply_filter( 'get_user_agent', substr( $ua, 0, 255 ) );
}
}


/**
/**
* Returns the sanitized referrer submitted by the browser.
* Returns the sanitized referrer submitted by the browser.
*
*
* @return string HTTP Referrer or 'direct' if no referrer was provided
* @return string HTTP Referrer or 'direct' if no referrer was provided
*/
*/
function yourls_get_referrer() {
function yourls_get_referrer( $sanitized_keyword ) {
$referrer = isset( $_SERVER['HTTP_REFERER'] ) ? yourls_sanitize_url_safe( $_SERVER['HTTP_REFERER'] ) : 'direct';
$yourls_site = yourls_get_yourls_site();
$combined_url = $yourls_site . '/' . $sanitized_keyword;

$referrer = isset( $_SERVER['HTTP_REFERER'] ) ? yourls_sanitize_url_safe( $_SERVER['HTTP_REFERER'] ) : $combined_url ;


return yourls_apply_filter( 'get_referrer', substr( $referrer, 0, 200 ) );
return yourls_apply_filter( 'get_referrer', substr( $referrer, 0, 200 ) );
}
}


/**
/**
* Redirect to another page
* Redirect to another page
*
*
* YOURLS redirection, either to internal or external URLs. If headers have not been sent, redirection
* YOURLS redirection, either to internal or external URLs. If headers have not been sent, redirection
* is achieved with PHP's header(). If headers have been sent already and we're not in a command line
* is achieved with PHP's header(). If headers have been sent already and we're not in a command line
* client, redirection occurs with Javascript.
* client, redirection occurs with Javascript.
*
*
* Note: yourls_redirect() does not exit automatically, and should almost always be followed by a call to exit()
* Note: yourls_redirect() does not exit automatically, and should almost always be followed by a call to exit()
* to prevent the script from continuing.
* to prevent the script from continuing.
*
*
* @since 1.4
* @since 1.4
* @param string $location URL to redirect to
* @param string $location URL to redirect to
* @param int $code HTTP status code to send
* @param int $code HTTP status code to send
* @return int 1 for header redirection, 2 for js redirection, 3 otherwise (CLI)
* @return int 1 for header redirection, 2 for js redirection, 3 otherwise (CLI)
*/
*/
function yourls_redirect( $location, $code = 301 ) {
function yourls_redirect( $location, $code = 301 ) {
yourls_do_action( 'pre_redirect', $location, $code );
yourls_do_action( 'pre_redirect', $location, $code );
$location = yourls_apply_filter( 'redirect_location', $location, $code );
$location = yourls_apply_filter( 'redirect_location', $location, $code );
$code = yourls_apply_filter( 'redirect_code', $code, $location );
$code = yourls_apply_filter( 'redirect_code', $code, $location );


// Redirect, either properly if possible, or via Javascript otherwise
// Redirect, either properly if possible, or via Javascript otherwise
/*
if( !headers_sent() ) {
if( !headers_sent() ) {
yourls_status_header( $code );
yourls_status_header( $code );
header( "Location: $location" );
header( "Location: $location" );
return 1;
return 1;
}
}
*/
// By commenting out the section above hopefully it forces a Javascript redirection which allows for logging more info


// Headers sent : redirect with JS if not in CLI
// Headers sent : redirect with JS if not in CLI
if( php_sapi_name() !== 'cli') {
if( php_sapi_name() !== 'cli') {
yourls_redirect_javascript( $location );
yourls_redirect_javascript( $location );
return 2;
return 2;
}
}


// We're in CLI
// We're in CLI
return 3;
return 3;
}
}


/**
/**
* Redirect to an existing short URL
* Redirect to an existing short URL
*
*
* Redirect client to an existing short URL (no check performed) and execute misc tasks: update
* Redirect client to an existing short URL (no check performed) and execute misc tasks: update
* clicks for short URL, update logs, and send an X-Robots-Tag header to control indexing of a page.
* clicks for short URL, update logs, and send an X-Robots-Tag header to control indexing of a page.
*
*
* @since 1.7.3
* @since 1.7.3
* @param string $url
* @param string $url
* @param string $keyword
* @param string $keyword
* @return void
* @return void
*/
*/
function yourls_redirect_shorturl($url, $keyword) {
function yourls_redirect_shorturl($url, $keyword) {
yourls_do_action( 'redirect_shorturl', $url, $keyword );
yourls_do_action( 'redirect_shorturl', $url, $keyword );


// Attempt to update click count in main table
// Attempt to update click count in main table
yourls_update_clicks( $keyword );
yourls_update_clicks( $keyword );


// Update detailed log for stats
// Update detailed log for stats
yourls_log_redirect( $keyword );
yourls_log_redirect( $keyword );


// Send an X-Robots-Tag header
// Send an X-Robots-Tag header
yourls_robots_tag_header();
yourls_robots_tag_header();


yourls_redirect( $url, 301 );
yourls_redirect( $url, 301 );
}
}


/**
/**
* Send an X-Robots-Tag header. See #3486
* Send an X-Robots-Tag header. See #3486
*
*
* @since 1.9.2
* @since 1.9.2
* @return void
* @return void
*/
*/
function yourls_robots_tag_header() {
function yourls_robots_tag_header() {
// Allow plugins to short-circuit the whole function
// Allow plugins to short-circuit the whole function
$pre = yourls_apply_filter( 'shunt_robots_tag_header', false );
$pre = yourls_apply_filter( 'shunt_robots_tag_header', false );
if ( false !== $pre ) {
if ( false !== $pre ) {
return $pre;
return $pre;
}
}


// By default, we're sending a 'noindex' header
// By default, we're sending a 'noindex' header
$tag = yourls_apply_filter( 'robots_tag_header', 'noindex' );
$tag = yourls_apply_filter( 'robots_tag_header', 'noindex' );
$replace = yourls_apply_filter( 'robots_tag_header_replace', true );
$replace = yourls_apply_filter( 'robots_tag_header_replace', true );
if ( !headers_sent() ) {
if ( !headers_sent() ) {
header( "X-Robots-Tag: $tag", $replace );
header( "X-Robots-Tag: $tag", $replace );
}
}
}
}




/**
/**
* Send headers to explicitly tell browser not to cache content or redirection
* Send headers to explicitly tell browser not to cache content or redirection
*
*
* @since 1.7.10
* @since 1.7.10
* @return void
* @return void
*/
*/
function yourls_no_cache_headers() {
function yourls_no_cache_headers() {
if( !headers_sent() ) {
if( !headers_sent() ) {
header( 'Expires: Thu, 23 Mar 1972 07:00:00 GMT' );
header( 'Expires: Thu, 23 Mar 1972 07:00:00 GMT' );
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
header( 'Pragma: no-cache' );
header( 'Pragma: no-cache' );
}
}
}
}


/**
/**
* Send header to prevent display within a frame from another site (avoid clickjacking)
* Send header to prevent display within a frame from another site (avoid clickjacking)
*
*
* This header makes it impossible for an external site to display YOURLS admin within a frame,
* This header makes it impossible for an external site to display YOURLS admin within a frame,
* which allows for clickjacking.
* which allows for clickjacking.
* See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
* See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
* This said, the whole function is shuntable : legit uses of iframes should be still possible.
* This said, the whole function is shuntable : legit uses of iframes should be still possible.
*
*
* @since 1.8.1
* @since 1.8.1
* @return void|mixed
* @return void|mixed
*/
*/
function yourls_no_frame_header() {
function yourls_no_frame_header() {
// Allow plugins to short-circuit the whole function
// Allow plugins to short-circuit the whole function
$pre = yourls_apply_filter( 'shunt_no_frame_header', false );
$pre = yourls_apply_filter( 'shunt_no_frame_header', false );
if ( false !== $pre ) {
if ( false !== $pre ) {
return $pre;
return $pre;
}
}


if( !headers_sent() ) {
if( !headers_sent() ) {
header( 'X-Frame-Options: SAMEORIGIN' );
header( 'X-Frame-Options: SAMEORIGIN' );
}
}
}
}


/**
/**
* Send a filterable content type header
* Send a filterable content type header
*
*
* @since 1.7
* @since 1.7
* @param string $type content type ('text/html', 'application/json', ...)
* @param string $type content type ('text/html', 'application/json', ...)
* @return bool whether header was sent
* @return bool whether header was sent
*/
*/
function yourls_content_type_header( $type ) {
function yourls_content_type_header( $type ) {
yourls_do_action( 'content_type_header', $type );
yourls_do_action( 'content_type_header', $type );
if( !headers_sent() ) {
if( !headers_sent() ) {
$charset = yourls_apply_filter( 'content_type_header_charset', 'utf-8' );
$charset = yourls_apply_filter( 'content_type_header_charset', 'utf-8' );
header( "Content-Type: $type; charset=$charset" );
header( "Content-Type: $type; charset=$charset" );
return true;
return true;
}
}
return false;
return false;
}
}


/**
/**
* Set HTTP status header
* Set HTTP status header
*
*
* @since 1.4
* @since 1.4
* @param int $code status header code
* @param int $code status header code
* @return bool whether header was sent
* @return bool whether header was sent
*/
*/
function yourls_status_header( $code = 200 ) {
function yourls_status_header( $code = 200 ) {
yourls_do_action( 'status_header', $code );
yourls_do_action( 'status_header', $code );


if( headers_sent() )
if( headers_sent() )
return false;
return false;


$protocol = $_SERVER['SERVER_PROTOCOL'];
$protocol = $_SERVER['SERVER_PROTOCOL'];
if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )
if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )
$protocol = 'HTTP/1.0';
$protocol = 'HTTP/1.0';


$code = intval( $code );
$code = intval( $code );
$desc = yourls_get_HTTP_status( $code );
$desc = yourls_get_HTTP_status( $code );


@header ("$protocol $code $desc"); // This causes problems on IIS and some FastCGI setups
@header ("$protocol $code $desc"); // This causes problems on IIS and some FastCGI setups


return true;
return true;
}
}


/**
/**
* Redirect to another page using Javascript.
* Redirect to another page using Javascript.
* Set optional (bool)$dontwait to false to force manual redirection (make sure a message has been read by user)
* Set optional (bool)$dontwait to false to force manual redirection (make sure a message has been read by user)
*
*
* @param string $location
* @param string $location
* @param bool $dontwait
* @param bool $dontwait
* @return void
* @return void
*/
*/
function yourls_redirect_javascript( $location, $dontwait = true ) {
function yourls_redirect_javascript( $location, $dontwait = true ) {
yourls_do_action( 'pre_redirect_javascript', $location, $dontwait );
yourls_do_action( 'pre_redirect_javascript', $location, $dontwait );
$location = yourls_apply_filter( 'redirect_javascript', $location, $dontwait );
$location = yourls_apply_filter( 'redirect_javascript', $location, $dontwait );
if ( $dontwait ) {
if ( $dontwait ) {
$message = yourls_s( 'if you are not redirected after 10 seconds, please <a href="%s">click here</a>', $location );
$message = yourls_s( 'If you are not redirected after 10 seconds, please <a href="%s">click here</a>.', $location );
echo <<<REDIR
echo <<<REDIR
<script type="text/javascript">
<script type="text/javascript">
window.location="$location";
window.location="$location";
</script>
</script>
<small>($message)</small>
<small>$message</small>
REDIR;
REDIR;
}
} else {
else {
echo '<p>'.yourls_s( 'Please <a href="%s">click here</a>.', $location ).'</p>';
echo '<p>'.yourls_s( 'Please <a href="%s">click here</a>', $location ).'</p>';
}
}
yourls_do_action( 'post_redirect_javascript', $location );
yourls_do_action( 'post_redirect_javascript', $location );
}
}


/**
/**
* Return an HTTP status code
* Return an HTTP status code
*
*
* @param int $code
* @param int $code
* @return string
* @return string
*/
*/
function yourls_get_HTTP_status( $code ) {
function yourls_get_HTTP_status( $code ) {
$code = intval( $code );
$code = intval( $code );
$headers_desc = [
$headers_desc = [
100 => 'Continue',
100 => 'Continue',
101 => 'Switching Protocols',
101 => 'Switching Protocols',
102 => 'Processing',
102 => 'Processing',


200 => 'OK',
200 => 'OK',
201 => 'Created',
201 => 'Created',
202 => 'Accepted',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
203 => 'Non-Authoritative Information',
204 => 'No Content',
204 => 'No Content',
205 => 'Reset Content',
205 => 'Reset Content',
206 => 'Partial Content',
206 => 'Partial Content',
207 => 'Multi-Status',
207 => 'Multi-Status',
226 => 'IM Used',
226 => 'IM Used',


300 => 'Multiple Choices',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
301 => 'Moved Permanently',
302 => 'Found',
302 => 'Found',
303 => 'See Other',
303 => 'See Other',
304 => 'Not Modified',
304 => 'Not Modified',
305 => 'Use Proxy',
305 => 'Use Proxy',
306 => 'Reserved',
306 => 'Reserved',
307 => 'Temporary Redirect',
307 => 'Temporary Redirect',


400 => 'Bad Request',
400 => 'Bad Request',
401 => 'Unauthorized',
401 => 'Unauthorized',
402 => 'Payment Required',
402 => 'Payment Required',
403 => 'Forbidden',
403 => 'Forbidden',
404 => 'Not Found',
404 => 'Not Found',
405 => 'Method Not Allowed',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
408 => 'Request Timeout',
409 => 'Conflict',
409 => 'Conflict',
410 => 'Gone',
410 => 'Gone',
411 => 'Length Required',
411 => 'Length Required',
412 => 'Precondition Failed',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
417 => 'Expectation Failed',
422 => 'Unprocessable Entity',
422 => 'Unprocessable Entity',
423 => 'Locked',
423 => 'Locked',
424 => 'Failed Dependency',
424 => 'Failed Dependency',
426 => 'Upgrade Required',
426 => 'Upgrade Required',


500 => 'Internal Server Error',
500 => 'Internal Server Error',
501 => 'Not Implemented',
501 => 'Not Implemented',
502 => 'Bad Gateway',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
505 => 'HTTP Version Not Supported',
506 => 'Variant Also Negotiates',
506 => 'Variant Also Negotiates',
507 => 'Insufficient Storage',
507 => 'Insufficient Storage',
510 => 'Not Extended'
510 => 'Not Extended'
];
];


return $headers_desc[$code] ?? '';
return $headers_desc[$code] ?? '';
}
}


/**
/**
* Log a redirect (for stats)
* 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
* 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.
* exists before calling it.
*
*
* @since 1.4
* @since 1.4
* @param string $keyword short URL keyword
* @param string $keyword short URL keyword
* @return mixed Result of the INSERT query (1 on success)
* @return mixed Result of the INSERT query (1 on success)
*/
*/
function yourls_log_redirect( $keyword ) {
function yourls_log_redirect( $keyword ) {
// Allow plugins to short-circuit the whole function
// Allow plugins to short-circuit the whole function
$pre = yourls_apply_filter( 'shunt_log_redirect', false, $keyword );
$pre = yourls_apply_filter( 'shunt_log_redirect', false, $keyword );
if ( false !== $pre ) {
if ( false !== $pre ) {
return $pre;
return $pre;
}
}


if (!yourls_do_log_redirect()) {
if (!yourls_do_log_redirect()) {
return true;
return true;
}
}


$table = YOURLS_DB_TABLE_LOG;
$table = YOURLS_DB_TABLE_LOG;
$ip = yourls_get_IP();
$ip = yourls_get_IP();
$sanitized_keyword = yourls_sanitize_keyword($keyword);
echo <<<EOD
<script>
// Orientation
var orientation = window.orientation;
//console.log("Orientation: ", orientation);
// Language
var language = navigator.language;
//console.log("Browser Language: ", language);
// Touch Screen
var isTouchScreen =
"ontouchstart" in window || navigator.maxTouchPoints > 0;
//console.log("Touch Screen: ", (isTouchScreen ? "Yes" : "No"));
// Screen Size
var width = screen.width;
var height = screen.height;
//console.log("Screen Size: ", width, "x", height);
// Incognito Mode
var isIncognito = false;
try {
isIncognito = window.sessionStorage.getItem("test");
window.sessionStorage.setItem("test", "test");
} catch (e) {
isIncognito = true;
}
//console.log("Incognito Mode: ", (isIncognito ? "Yes" : "No"));
// Ad Blocker
var adBlockEnabled = false;
async function detectAdBlock() {
const googleAdUrl = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js";
try {
await fetch(new Request(googleAdUrl)).catch(_ => adBlockEnabled = true);
} catch (e) {
adBlockEnabled = true;
} finally {
//console.log("AdBlock Enabled: ", (adBlockEnabled ? "Yes" : "No"));
}
}
detectAdBlock();
if ('getBattery' in navigator) {
// The getBattery() API is supported
navigator.getBattery().then((battery) => {
var batteryLevel = battery.level * 100;
//console.log("Battery Level: ", batteryLevel, "%");
// Construct deviceData string inside the battery promise
var deviceData =
"Ori:" + orientation +
" Lang:" + language +
" Touch:" + (isTouchScreen ? "Yes" : "No") +
" Bat:" + batteryLevel +
"% Incog:" + (isIncognito ? "Yes" : "No") +
" AdBlock:" + (adBlockEnabled ? "Yes" : "No") +
" Size:" + width + "x" + height;
//console.log(deviceData);
// Set a cookie with the device information to be fetched later
document.cookie = "deviceinfo=" + encodeURIComponent(deviceData) + "; path=/; SameSite=Lax; Secure";
});
} else {
// The getBattery() API is not supported
// Construct the deviceData string
var deviceData =
"Ori:" + orientation +
" Lang:" + language +
" Touch:" + (isTouchScreen ? "Yes" : "No") +
" Bat:undefined" +
" Incog:" + (isIncognito ? "Yes" : "No") +
" AdBlock:" + (adBlockEnabled ? "Yes" : "No") +
" Size:" + width + "x" + height;
//console.log(deviceData);
// Set a cookie with the device information to be fetched later
document.cookie = "deviceinfo=" + encodeURIComponent(deviceData) + "; path=/; SameSite=Lax; Secure";
}
</script>
EOD;
if (isset($_COOKIE['deviceinfo'])) {
// Retrieve the device information from the cookie
$deviceinfo = $_COOKIE['deviceinfo'];
// Unset or reset the cookie
setcookie('deviceinfo', '', time() - 3600, '/'); // expire the cookie
}
// Get the real referrer
$referrer = substr( yourls_get_referrer($sanitized_keyword), 0, 200 );
// Append the device info to it
$referrer_device = $referrer . " - " . $deviceinfo;
// Debugging: Print raw data to browser console
echo '<script>';
echo 'console.log("' . $referrer_device . '");';
echo '</script>';
$binds = [
$binds = [
'now' => date( 'Y-m-d H:i:s' ),
'now' => date( 'Y-m-d H:i:s' ),
'keyword' => yourls_sanitize_keyword($keyword),
'keyword' => $sanitized_keyword,
'referrer' => substr( yourls_get_referrer(), 0, 200 ),
'referrer' => $referrer_device,
'ua' => substr(yourls_get_user_agent(), 0, 255),
'ua' => substr(yourls_get_user_agent(), 0, 255),
'ip' => $ip,
'ip' => $ip,
'location' => yourls_geo_ip_to_countrycode($ip),
'location' => yourls_geo_ip_to_countrycode($ip),
];
];


// Try and log. An error probably means a concurrency problem : just skip the logging
// Try and log. An error probably means a concurrency problem : just skip the logging
try {
try {
$result = yourls_get_db()->fetchAffected("INSERT INTO `$table` (click_time, shorturl, referrer, user_agent, ip_address, country_code) VALUES (:now, :keyword, :referrer, :ua, :ip, :location)", $binds );
$result = yourls_get_db()->fetchAffected("INSERT INTO `$table` (click_time, shorturl, referrer, user_agent, ip_address, country_code) VALUES (:now, :keyword, :referrer, :ua, :ip, :location)", $binds );
} catch (Exception $e) {
} catch (Exception $e) {
$result = 0;
$result = 0;
}
}


return $result;
return $result;
}
}


/**
/**
* Check if we want to not log redirects (for stats)
* Check if we want to not log redirects (for stats)
*
*
* @return bool
* @return bool
*/
*/
function yourls_do_log_redirect() {
function yourls_do_log_redirect() {
return ( !defined( 'YOURLS_NOSTATS' ) || YOURLS_NOSTATS != true );
return ( !defined( 'YOURLS_NOSTATS' ) || YOURLS_NOSTATS != true );
}
}


/**
/**
* Check if an upgrade is needed
* Check if an upgrade is needed
*
*
* @return bool
* @return bool
*/
*/
function yourls_upgrade_is_needed() {
function yourls_upgrade_is_needed() {
// check YOURLS_DB_VERSION exist && match values stored in YOURLS_DB_TABLE_OPTIONS
// check YOURLS_DB_VERSION exist && match values stored in YOURLS_DB_TABLE_OPTIONS
list( $currentver, $currentsql ) = yourls_get_current_version_from_sql();
list( $currentver, $currentsql ) = yourls_get_current_version_from_sql();
if ( $currentsql < YOURLS_DB_VERSION ) {
if ( $currentsql < YOURLS_DB_VERSION ) {
return true;
return true;
}
}


// Check if YOURLS_VERSION exist && match value stored in YOURLS_DB_TABLE_OPTIONS, update DB if required
// Check if YOURLS_VERSION exist && match value stored in YOURLS_DB_TABLE_OPTIONS, update DB if required
if ( $currentver < YOURLS_VERSION ) {
if ( $currentver < YOURLS_VERSION ) {
yourls_update_option( 'version', YOURLS_VERSION );
yourls_update_option( 'version', YOURLS_VERSION );
}
}


return false;
return false;
}
}


/**
/**
* Get current version & db version as stored in the options DB. Prior to 1.4 there's no option table.
* Get current version & db version as stored in the options DB. Prior to 1.4 there's no option table.
*
*
* @return array
* @return array
*/
*/
function yourls_get_current_version_from_sql() {
function yourls_get_current_version_from_sql() {
$currentver = yourls_get_option( 'version' );
$currentver = yourls_get_option( 'version' );
$currentsql = yourls_get_option( 'db_version' );
$currentsql = yourls_get_option( 'db_version' );


// Values if version is 1.3
// Values if version is 1.3
if ( !$currentver ) {
if ( !$currentver ) {
$currentver = '1.3';
$currentver = '1.3';
}
}
if ( !$currentsql ) {
if ( !$currentsql ) {
$currentsql = '100';
$currentsql = '100';
}
}


return [ $currentver, $currentsql ];
return [ $currentver, $currentsql ];
}
}


/**
/**
* Determine if the current page is private
* Determine if the current page is private
*
*
* @return bool
* @return bool
*/
*/
function yourls_is_private() {
function yourls_is_private() {
$private = defined( 'YOURLS_PRIVATE' ) && YOURLS_PRIVATE;
$private = defined( 'YOURLS_PRIVATE' ) && YOURLS_PRIVATE;


if ( $private ) {
if ( $private ) {


// Allow overruling for particular pages:
// Allow overruling for particular pages:


// API
// API
if ( yourls_is_API() && defined( 'YOURLS_PRIVATE_API' ) ) {
if ( yourls_is_API() && defined( 'YOURLS_PRIVATE_API' ) ) {
$private = YOURLS_PRIVATE_API;
$private = YOURLS_PRIVATE_API;
}
}
// Stat pages
// Stat pages
elseif ( yourls_is_infos() && defined( 'YOURLS_PRIVATE_INFOS' ) ) {
elseif ( yourls_is_infos() && defined( 'YOURLS_PRIVATE_INFOS' ) ) {
$private = YOURLS_PRIVATE_INFOS;
$private = YOURLS_PRIVATE_INFOS;
}
}
// Others future cases ?
// Others future cases ?
}
}


return yourls_apply_filter( 'is_private', $private );
return yourls_apply_filter( 'is_private', $private );
}
}


/**
/**
* Allow several short URLs for the same long URL ?
* Allow several short URLs for the same long URL ?
*
*
* @return bool
* @return bool
*/
*/
function yourls_allow_duplicate_longurls() {
function yourls_allow_duplicate_longurls() {
// special treatment if API to check for WordPress plugin requests
// special treatment if API to check for WordPress plugin requests
if ( yourls_is_API() && isset( $_REQUEST[ 'source' ] ) && $_REQUEST[ 'source' ] == 'plugin' ) {
if ( yourls_is_API() && isset( $_REQUEST[ 'source' ] ) && $_REQUEST[ 'source' ] == 'plugin' ) {
return false;
return false;
}
}


return yourls_apply_filter('allow_duplicate_longurls', defined('YOURLS_UNIQUE_URLS') && !YOURLS_UNIQUE_URLS);
return yourls_apply_filter('allow_duplicate_longurls', defined('YOURLS_UNIQUE_URLS') && !YOURLS_UNIQUE_URLS);
}
}


/**
/**
* Check if an IP shortens URL too fast to prevent DB flood. Return true, or die.
* Check if an IP shortens URL too fast to prevent DB flood. Return true, or die.
*
*
* @param string $ip
* @param string $ip
* @return bool|mixed|string
* @return bool|mixed|string
*/
*/
function yourls_check_IP_flood( $ip = '' ) {
function yourls_check_IP_flood( $ip = '' ) {


// Allow plugins to short-circuit the whole function
// Allow plugins to short-circuit the whole function
$pre = yourls_apply_filter( 'shunt_check_IP_flood', false, $ip );
$pre = yourls_apply_filter( 'shunt_check_IP_flood', false, $ip );
if ( false !== $pre )
if ( false !== $pre )
return $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
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
// Raise white flag if installing or if no flood delay defined
if(
if(
( defined('YOURLS_FLOOD_DELAY_SECONDS') && YOURLS_FLOOD_DELAY_SECONDS === 0 ) ||
( defined('YOURLS_FLOOD_DELAY_SECONDS') && YOURLS_FLOOD_DELAY_SECONDS === 0 ) ||
!defined('YOURLS_FLOOD_DELAY_SECONDS') ||
!defined('YOURLS_FLOOD_DELAY_SECONDS') ||
yourls_is_installing()
yourls_is_installing()
)
)
return true;
return true;


// Don't throttle logged in users
// Don't throttle logged in users
if( yourls_is_private() ) {
if( yourls_is_private() ) {
if( yourls_is_valid_user() === true )
if( yourls_is_valid_user() === true )
return true;
return true;
}
}


// Don't throttle whitelist IPs
// Don't throttle whitelist IPs
if( defined( 'YOURLS_FLOOD_IP_WHITELIST' ) && YOURLS_FLOOD_IP_WHITELIST ) {
if( defined( 'YOURLS_FLOOD_IP_WHITELIST' ) && YOURLS_FLOOD_IP_WHITELIST ) {
$whitelist_ips = explode( ',', YOURLS_FLOOD_IP_WHITELIST );
$whitelist_ips = explode( ',', YOURLS_FLOOD_IP_WHITELIST );
foreach( (array)$whitelist_ips as $whitelist_ip ) {
foreach( (array)$whitelist_ips as $whitelist_ip ) {
$whitelist_ip = trim( $whitelist_ip );
$whitelist_ip = trim( $whitelist_ip );
if ( $whitelist_ip == $ip )
if ( $whitelist_ip == $ip )
return true;
return true;
}
}
}
}


$ip = ( $ip ? yourls_sanitize_ip( $ip ) : yourls_get_IP() );
$ip = ( $ip ? yourls_sanitize_ip( $ip ) : yourls_get_IP() );


yourls_do_action( 'check_ip_flood', $ip );
yourls_do_action( 'check_ip_flood', $ip );


$table = YOURLS_DB_TABLE_URL;
$table = YOURLS_DB_TABLE_URL;
$lasttime = yourls_get_db()->fetchValue( "SELECT `timestamp` FROM $table WHERE `ip` = :ip ORDER BY `timestamp` DESC LIMIT 1", [ 'ip' => $ip ] );
$lasttime = yourls_get_db()->fetchValue( "SELECT `timestamp` FROM $table WHERE `ip` = :ip ORDER BY `timestamp` DESC LIMIT 1", [ 'ip' => $ip ] );
if( $lasttime ) {
if( $lasttime ) {
$now = date( 'U' );
$now = date( 'U' );
$then = date( 'U', strtotime( $lasttime ) );
$then = date( 'U', strtotime( $lasttime ) );
if( ( $now - $then ) <= YOURLS_FLOOD_DELAY_SECONDS ) {
if( ( $now - $then ) <= YOURLS_FLOOD_DELAY_SECONDS ) {
// Flood!
// Flood!
yourls_do_action( 'ip_flood', $ip, $now - $then );
yourls_do_action( 'ip_flood', $ip, $now - $then );
yourls_die( yourls__( 'Too many URLs added too fast. Slow down please.' ), yourls__( 'Too Many Requests' ), 429 );
yourls_die( yourls__( 'Too many URLs added too fast. Slow down please.' ), yourls__( 'Too Many Requests' ), 429 );
}
}
}
}


return true;
return true;
}
}


/**
/**
* Check if YOURLS is installing
* Check if YOURLS is installing
*
*
* @since 1.6
* @since 1.6
* @return bool
* @return bool
*/
*/
function yourls_is_installing() {
function yourls_is_installing() {
return (bool)yourls_apply_filter( 'is_installing', defined( 'YOURLS_INSTALLING' ) && YOURLS_INSTALLING );
return (bool)yourls_apply_filter( 'is_installing', defined( 'YOURLS_INSTALLING' ) && YOURLS_INSTALLING );
}
}


/**
/**
* Check if YOURLS is upgrading
* Check if YOURLS is upgrading
*
*
* @since 1.6
* @since 1.6
* @return bool
* @return bool
*/
*/
function yourls_is_upgrading() {
function yourls_is_upgrading() {
return (bool)yourls_apply_filter( 'is_upgrading', defined( 'YOURLS_UPGRADING' ) && YOURLS_UPGRADING );
return (bool)yourls_apply_filter( 'is_upgrading', defined( 'YOURLS_UPGRADING' ) && YOURLS_UPGRADING );
}
}


/**
/**
* Check if YOURLS is installed
* Check if YOURLS is installed
*
*
* Checks property $ydb->installed that is created by yourls_get_all_options()
* Checks property $ydb->installed that is created by yourls_get_all_options()
*
*
* See inline comment for updating from 1.3 or prior.
* See inline comment for updating from 1.3 or prior.
*
*
* @return bool
* @return bool
*/
*/
function yourls_is_installed() {
function yourls_is_installed() {
return (bool)yourls_apply_filter( 'is_installed', yourls_get_db()->is_installed() );
return (bool)yourls_apply_filter( 'is_installed', yourls_get_db()->is_installed() );
}
}


/**
/**
* Set installed state
* Set installed s
*
* @since 1.7.3
* @param bool $bool whether YOURLS is installed or not
* @return void
*/
function yourls_set_installed( $bool ) {
yourls_get_db()->set_installed( $bool );
}

/**
* Generate random string of (int)$length length and type $type (see function for details)
*
* @param int $length
* @param int $type
* @param string $charlist
* @return mixed|string
*/
function yourls_rnd_string ( $length = 5, $type = 0, $charlist = '' ) {
$length = intval( $length );

// define possible characters
switch ( $type ) {

// 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;

// custom char list, or comply to charset as defined in config
default:
case '0':
$possible = $charlist ? $charlist : yourls_get_shorturl_charset();
break;
}

$str = substr( str_shuffle( $possible ), 0, $length );
return yourls_apply_filter( 'rnd_string', $str, $length, $type, $charlist );
}

/**
* Check if we're in API mode.
*
* @return bool
*/
function yourls_is_API() {
return (bool)yourls_apply_filter( 'is_API', defined( 'YOURLS_API' ) && YOURLS_API );
}

/**
* Check if we're in Ajax mode.
*
* @return bool
*/
function yourls_is_Ajax() {
return (bool)yourls_apply_filter( 'is_Ajax', defined( 'YOURLS_AJAX' ) && YOURLS_AJAX );
}

/**
* Check if we're in GO mode (yourls-go.php).
*
* @return bool
*/
function yourls_is_GO() {
return (bool)yourls_apply_filter( 'is_GO', defined( 'YOURLS_GO' ) && YOURLS_GO );
}

/**
* Check if we're displaying stats infos (yourls-infos.php). Returns bool
*
* @return bool
*/
function yourls_is_infos() {
return (bool)yourls_apply_filter( 'is_infos', defined( 'YOURLS_INFOS' ) && YOURLS_INFOS );
}

/**
* Check if we're in the admin area. Returns bool. Does not relate with user rights.
*
* @return bool
*/
function yourls_is_admin() {
return (bool)yourls_apply_filter( 'is_admin', defined( 'YOURLS_ADMIN' ) && YOURLS_ADMIN );
}

/**
* Check if the server seems to be running on Windows. Not exactly sure how reliable this is.
*
* @return bool
*/
function yourls_is_windows() {
return defined( 'DIRECTORY_SEPARATOR' ) && DIRECTORY_SEPARATOR == '\\';
}

/**
* Check if SSL is required.
*
* @return bool
*/
function yourls_needs_ssl() {
return (bool)yourls_apply_filter( 'needs_ssl', defined( 'YOURLS_ADMIN_SSL' ) && YOURLS_ADMIN_SSL );
}

/**
* Check if SSL is used. Stolen from WP.
*
* @return bool
*/
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[ 'HTTP_X_FORWARDED_PROTO' ] ) ) {
if ( 'https' == strtolower( $_SERVER[ 'HTTP_X_FORWARDED_PROTO' ] ) ) {
$is_ssl = true;
}
}
elseif ( isset( $_SERVER[ 'SERVER_PORT' ] ) && ( '443' == $_SERVER[ 'SERVER_PORT' ] ) ) {
$is_ssl = true;
}
return (bool)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 cha