Upload files to ''
This commit is contained in:
parent
e92aacd4e9
commit
037da0a554
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
// No direct call
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
$auth = yourls_is_valid_user();
|
||||
|
||||
if( $auth !== true ) {
|
||||
|
||||
// API mode,
|
||||
if ( yourls_is_API() ) {
|
||||
$format = ( isset($_REQUEST['format']) ? $_REQUEST['format'] : 'xml' );
|
||||
$callback = ( isset($_REQUEST['callback']) ? $_REQUEST['callback'] : '' );
|
||||
yourls_api_output( $format, array(
|
||||
'simple' => $auth,
|
||||
'message' => $auth,
|
||||
'errorCode' => 403,
|
||||
'callback' => $callback,
|
||||
) );
|
||||
|
||||
// Regular mode
|
||||
} else {
|
||||
yourls_login_screen( $auth );
|
||||
}
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
yourls_do_action( 'auth_successful' );
|
||||
|
||||
/*
|
||||
* The following code is a shim that helps users store passwords securely in config.php
|
||||
* by storing a password hash and removing the plaintext.
|
||||
*
|
||||
* TODO: Remove this once real user management is implemented
|
||||
*/
|
||||
|
||||
// Did we just fail at encrypting passwords ?
|
||||
if ( isset( $_GET['dismiss'] ) && $_GET['dismiss'] == 'hasherror' ) {
|
||||
yourls_update_option( 'defer_hashing_error', time() + 86400 * 7 ); // now + 1 week
|
||||
|
||||
} else {
|
||||
|
||||
// Encrypt passwords that are clear text
|
||||
if ( yourls_maybe_hash_passwords() ) {
|
||||
$hash = yourls_hash_passwords_now( YOURLS_CONFIGFILE );
|
||||
if ( $hash === true ) {
|
||||
// Hashing succesful. Remove flag from DB if any.
|
||||
if( yourls_get_option( 'defer_hashing_error' ) )
|
||||
yourls_delete_option( 'defer_hashing_error' );
|
||||
} else {
|
||||
// It failed, display message for first time or if last time was a week ago
|
||||
if ( time() > yourls_get_option( 'defer_hashing_error' ) or !yourls_get_option( 'defer_hashing_error' ) ) {
|
||||
$message = yourls_s( 'Could not auto-encrypt passwords. Error was: "%s".', $hash );
|
||||
$message .= ' ';
|
||||
$message .= yourls_s( '<a href="%s">Get help</a>.', 'http://yourls.org/userpassword' );
|
||||
$message .= '</p><p>';
|
||||
$message .= yourls_s( '<a href="%s">Click here</a> to dismiss this message for one week.', '?dismiss=hasherror' );
|
||||
|
||||
yourls_add_notice( $message );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Connect to DB
|
||||
*
|
||||
* @since 1.0
|
||||
* @return \YOURLS\Database\YDB
|
||||
*/
|
||||
function yourls_db_connect() {
|
||||
global $ydb;
|
||||
|
||||
if ( !defined( 'YOURLS_DB_USER' )
|
||||
or !defined( 'YOURLS_DB_PASS' )
|
||||
or !defined( 'YOURLS_DB_NAME' )
|
||||
or !defined( 'YOURLS_DB_HOST' )
|
||||
) {
|
||||
yourls_die( yourls__( 'Incorrect DB config, please refer to documentation' ), yourls__( 'Fatal error' ), 503 );
|
||||
}
|
||||
|
||||
$dbhost = YOURLS_DB_HOST;
|
||||
$user = YOURLS_DB_USER;
|
||||
$pass = YOURLS_DB_PASS;
|
||||
$dbname = YOURLS_DB_NAME;
|
||||
|
||||
// This action is deprecated
|
||||
yourls_do_action( 'set_DB_driver', 'deprecated' );
|
||||
|
||||
// Get custom port if any
|
||||
if ( false !== strpos( $dbhost, ':' ) ) {
|
||||
list( $dbhost, $dbport ) = explode( ':', $dbhost );
|
||||
$dbhost = sprintf( '%1$s;port=%2$d', $dbhost, $dbport );
|
||||
}
|
||||
|
||||
$charset = yourls_apply_filter( 'db_connect_charset', 'utf8mb4' );
|
||||
|
||||
/**
|
||||
* Data Source Name (dsn) used to connect the DB
|
||||
*
|
||||
* DSN with PDO is something like:
|
||||
* 'mysql:host=123.4.5.6;dbname=test_db;port=3306'
|
||||
* 'sqlite:/opt/databases/mydb.sq3'
|
||||
* 'pgsql:host=192.168.13.37;port=5432;dbname=omgwtf'
|
||||
*/
|
||||
$dsn = sprintf( 'mysql:host=%s;dbname=%s;charset=%s', $dbhost, $dbname, $charset );
|
||||
$dsn = yourls_apply_filter( 'db_connect_custom_dsn', $dsn );
|
||||
|
||||
/**
|
||||
* PDO driver options and attributes
|
||||
*
|
||||
* The PDO constructor is something like:
|
||||
* new PDO( string $dsn, string $username, string $password [, array $options ] )
|
||||
* The driver options are passed to the PDO constructor, eg array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
|
||||
* The attribute options are then set in a foreach($attr as $k=>$v){$db->setAttribute($k, $v)} loop
|
||||
*/
|
||||
$driver_options = yourls_apply_filter( 'db_connect_driver_option', [] ); // driver options as key-value pairs
|
||||
$attributes = yourls_apply_filter( 'db_connect_attributes', [] ); // attributes as key-value pairs
|
||||
|
||||
$ydb = new \YOURLS\Database\YDB( $dsn, $user, $pass, $driver_options, $attributes );
|
||||
$ydb->init();
|
||||
|
||||
// Past this point, we're connected
|
||||
yourls_debug_log( sprintf( 'Connected to database %s on %s ', $dbname, $dbhost ) );
|
||||
|
||||
yourls_debug_mode( YOURLS_DEBUG );
|
||||
|
||||
return $ydb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function : return instance of the DB
|
||||
*
|
||||
* Instead of:
|
||||
* global $ydb;
|
||||
* $ydb->do_stuff()
|
||||
* Prefer :
|
||||
* yourls_get_db()->do_stuff()
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @return \YOURLS\Database\YDB
|
||||
*/
|
||||
function yourls_get_db() {
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_get_db', false );
|
||||
if ( false !== $pre ) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
global $ydb;
|
||||
$ydb = ( isset( $ydb ) ) ? $ydb : yourls_db_connect();
|
||||
return yourls_apply_filter('get_db', $ydb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function : set instance of DB, or unset it
|
||||
*
|
||||
* Instead of:
|
||||
* global $ydb;
|
||||
* $ydb = stuff
|
||||
* Prefer :
|
||||
* yourls_set_db( stuff )
|
||||
* (This is mostly used in the test suite)
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param mixed $db Either a \YOURLS\Database\YDB instance, or anything. If null, the function will unset $ydb
|
||||
* @return void
|
||||
*/
|
||||
function yourls_set_db($db) {
|
||||
global $ydb;
|
||||
|
||||
if (is_null($db)) {
|
||||
unset($ydb);
|
||||
} else {
|
||||
$ydb = $db;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
<?php
|
||||
/*
|
||||
* YOURLS
|
||||
* Functions for the API
|
||||
*
|
||||
* Note about translation : this file should NOT be translation ready
|
||||
* API messages and returns are supposed to be programmatically tested, so default English is expected
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* API function wrapper: Shorten a URL
|
||||
*
|
||||
* @since 1.6
|
||||
* @return array Result of API call
|
||||
*/
|
||||
function yourls_api_action_shorturl() {
|
||||
$url = ( isset( $_REQUEST['url'] ) ? $_REQUEST['url'] : '' );
|
||||
$keyword = ( isset( $_REQUEST['keyword'] ) ? $_REQUEST['keyword'] : '' );
|
||||
$title = ( isset( $_REQUEST['title'] ) ? $_REQUEST['title'] : '' );
|
||||
$return = yourls_add_new_link( $url, $keyword, $title );
|
||||
$return['simple'] = ( isset( $return['shorturl'] ) ? $return['shorturl'] : '' ); // This one will be used in case output mode is 'simple'
|
||||
unset( $return['html'] ); // in API mode, no need for our internal HTML output
|
||||
return yourls_apply_filter( 'api_result_shorturl', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* API function wrapper: Stats about links (XX top, bottom, last, rand)
|
||||
*
|
||||
* @since 1.6
|
||||
* @return array Result of API call
|
||||
*/
|
||||
function yourls_api_action_stats() {
|
||||
$filter = ( isset( $_REQUEST['filter'] ) ? $_REQUEST['filter'] : '' );
|
||||
$limit = ( isset( $_REQUEST['limit'] ) ? $_REQUEST['limit'] : '' );
|
||||
$start = ( isset( $_REQUEST['start'] ) ? $_REQUEST['start'] : '' );
|
||||
return yourls_apply_filter( 'api_result_stats', yourls_api_stats( $filter, $limit, $start ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* API function wrapper: Just the global counts of shorturls and clicks
|
||||
*
|
||||
* @since 1.6
|
||||
* @return array Result of API call
|
||||
*/
|
||||
function yourls_api_action_db_stats() {
|
||||
return yourls_apply_filter( 'api_result_db_stats', yourls_api_db_stats() );
|
||||
}
|
||||
|
||||
/**
|
||||
* API function wrapper: Stats for a shorturl
|
||||
*
|
||||
* @since 1.6
|
||||
* @return array Result of API call
|
||||
*/
|
||||
function yourls_api_action_url_stats() {
|
||||
$shorturl = ( isset( $_REQUEST['shorturl'] ) ? $_REQUEST['shorturl'] : '' );
|
||||
return yourls_apply_filter( 'api_result_url_stats', yourls_api_url_stats( $shorturl ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* API function wrapper: Expand a short link
|
||||
*
|
||||
* @since 1.6
|
||||
* @return array Result of API call
|
||||
*/
|
||||
function yourls_api_action_expand() {
|
||||
$shorturl = ( isset( $_REQUEST['shorturl'] ) ? $_REQUEST['shorturl'] : '' );
|
||||
return yourls_apply_filter( 'api_result_expand', yourls_api_expand( $shorturl ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* API function wrapper: return version numbers
|
||||
*
|
||||
* @since 1.6
|
||||
* @return array Result of API call
|
||||
*/
|
||||
function yourls_api_action_version() {
|
||||
$return['version'] = $return['simple'] = YOURLS_VERSION;
|
||||
if( isset( $_REQUEST['db'] ) && $_REQUEST['db'] == 1 )
|
||||
$return['db_version'] = YOURLS_DB_VERSION;
|
||||
return yourls_apply_filter( 'api_result_version', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output and return API result
|
||||
*
|
||||
* This function will echo (or only return if asked) an array as JSON, JSONP or XML. If the array has a
|
||||
* 'simple' key, it can also output that key as unformatted text if expected output mode is 'simple'
|
||||
*
|
||||
* Most likely, script should not do anything after outputting this
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $mode Expected output mode ('json', 'jsonp', 'xml', 'simple')
|
||||
* @param array $output Array of things to output
|
||||
* @param bool $send_headers Optional, default true: Whether a headers (status, content type) should be sent or not
|
||||
* @param bool $echo Optional, default true: Whether the output should be outputted or just returned
|
||||
* @return string API output, as an XML / JSON / JSONP / raw text string
|
||||
*/
|
||||
function yourls_api_output( $mode, $output, $send_headers = true, $echo = true ) {
|
||||
if( isset( $output['simple'] ) ) {
|
||||
$simple = $output['simple'];
|
||||
unset( $output['simple'] );
|
||||
}
|
||||
|
||||
yourls_do_action( 'pre_api_output', $mode, $output, $send_headers, $echo );
|
||||
|
||||
if( $send_headers ) {
|
||||
if( isset( $output['statusCode'] ) ) {
|
||||
$code = $output['statusCode'];
|
||||
} elseif ( isset( $output['errorCode'] ) ) {
|
||||
$code = $output['errorCode'];
|
||||
} else {
|
||||
$code = 200;
|
||||
}
|
||||
yourls_status_header( $code );
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
switch ( $mode ) {
|
||||
case 'jsonp':
|
||||
if( $send_headers )
|
||||
yourls_content_type_header( 'application/javascript' );
|
||||
|
||||
$callback = isset( $output['callback'] ) ? $output['callback'] : '';
|
||||
$result = $callback . '(' . json_encode( $output ) . ')';
|
||||
break;
|
||||
|
||||
case 'json':
|
||||
if( $send_headers )
|
||||
yourls_content_type_header( 'application/json' );
|
||||
|
||||
$result = json_encode( $output );
|
||||
break;
|
||||
|
||||
case 'xml':
|
||||
if( $send_headers )
|
||||
yourls_content_type_header( 'application/xml' );
|
||||
|
||||
$result = yourls_xml_encode( $output );
|
||||
break;
|
||||
|
||||
case 'simple':
|
||||
default:
|
||||
if( $send_headers )
|
||||
yourls_content_type_header( 'text/plain' );
|
||||
|
||||
$result = isset( $simple ) ? $simple : '';
|
||||
break;
|
||||
}
|
||||
|
||||
if( $echo ) {
|
||||
echo $result;
|
||||
}
|
||||
|
||||
yourls_do_action( 'api_output', $mode, $output, $send_headers, $echo );
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array for API stat requests
|
||||
*
|
||||
* @param string $filter either "top", "bottom" , "rand" or "last"
|
||||
* @param int $limit maximum number of links to return
|
||||
* @param int $start offset
|
||||
* @return array
|
||||
*/
|
||||
function yourls_api_stats($filter = 'top', $limit = 10, $start = 0 ) {
|
||||
$return = yourls_get_stats( $filter, $limit, $start );
|
||||
$return['simple'] = 'Need either XML or JSON format for stats';
|
||||
$return['message'] = 'success';
|
||||
return yourls_apply_filter( 'api_stats', $return, $filter, $limit, $start );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array for counts of shorturls and clicks
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function yourls_api_db_stats() {
|
||||
$return = array(
|
||||
'db-stats' => yourls_get_db_stats(),
|
||||
'statusCode' => 200,
|
||||
'simple' => 'Need either XML or JSON format for stats',
|
||||
'message' => 'success',
|
||||
);
|
||||
|
||||
return yourls_apply_filter( 'api_db_stats', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array for API stat requests
|
||||
*
|
||||
* @param string $shorturl Short URL to check
|
||||
* @return array
|
||||
*/
|
||||
function yourls_api_url_stats( $shorturl ) {
|
||||
$keyword = str_replace( yourls_get_yourls_site() . '/' , '', $shorturl ); // accept either 'http://ozh.in/abc' or 'abc'
|
||||
$keyword = yourls_sanitize_keyword( $keyword );
|
||||
|
||||
$return = yourls_get_keyword_stats( $keyword );
|
||||
$return['simple'] = 'Need either XML or JSON format for stats';
|
||||
return yourls_apply_filter( 'api_url_stats', $return, $shorturl );
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand short url to long url
|
||||
*
|
||||
* @param string $shorturl Short URL to expand
|
||||
* @return array
|
||||
*/
|
||||
function yourls_api_expand( $shorturl ) {
|
||||
$keyword = str_replace( yourls_get_yourls_site() . '/' , '', $shorturl ); // accept either 'http://ozh.in/abc' or 'abc'
|
||||
$keyword = yourls_sanitize_keyword( $keyword );
|
||||
|
||||
$longurl = yourls_get_keyword_longurl( $keyword );
|
||||
|
||||
if( $longurl ) {
|
||||
$return = array(
|
||||
'keyword' => $keyword,
|
||||
'shorturl' => yourls_link($keyword),
|
||||
'longurl' => $longurl,
|
||||
'title' => yourls_get_keyword_title( $keyword ),
|
||||
'simple' => $longurl,
|
||||
'message' => 'success',
|
||||
'statusCode' => 200,
|
||||
);
|
||||
} else {
|
||||
$return = array(
|
||||
'keyword' => $keyword,
|
||||
'simple' => 'not found',
|
||||
'message' => 'Error: short URL not found',
|
||||
'errorCode' => 404,
|
||||
);
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'api_expand', $return, $shorturl );
|
||||
}
|
|
@ -0,0 +1,747 @@
|
|||
<?php
|
||||
/**
|
||||
* Function related to authentication functions and nonces
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Show login form if required
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
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' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for valid user via login form or stored cookie. Returns true or an error message
|
||||
*
|
||||
* @return bool|string|mixed true if valid user, error message otherwise. Can also call yourls_die() or redirect to login page. Oh my.
|
||||
*/
|
||||
function yourls_is_valid_user() {
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_is_valid_user', null );
|
||||
if ( null !== $pre ) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
// $unfiltered_valid : are credentials valid? Boolean value. It's "unfiltered" to allow plugins to eventually filter it.
|
||||
$unfiltered_valid = false;
|
||||
|
||||
// Logout request
|
||||
if( isset( $_GET['action'] ) && $_GET['action'] == 'logout' && isset( $_REQUEST['nonce'] ) ) {
|
||||
// The logout nonce is associated to fake user 'logout' since at this point we don't know the real user
|
||||
yourls_verify_nonce('admin_logout', $_REQUEST['nonce'], 'logout');
|
||||
yourls_do_action( 'logout' );
|
||||
yourls_store_cookie( '' );
|
||||
return yourls__( 'Logged out successfully' );
|
||||
}
|
||||
|
||||
// Check cookies or login request. Login form has precedence.
|
||||
|
||||
yourls_do_action( 'pre_login' );
|
||||
|
||||
// Determine auth method and check credentials
|
||||
if
|
||||
// API only: Secure (no login or pwd) and time limited token
|
||||
// ?timestamp=12345678&signature=md5(totoblah12345678)
|
||||
( yourls_is_API() &&
|
||||
isset( $_REQUEST['timestamp'] ) && !empty($_REQUEST['timestamp'] ) &&
|
||||
isset( $_REQUEST['signature'] ) && !empty($_REQUEST['signature'] )
|
||||
)
|
||||
{
|
||||
yourls_do_action( 'pre_login_signature_timestamp' );
|
||||
$unfiltered_valid = yourls_check_signature_timestamp();
|
||||
}
|
||||
|
||||
elseif
|
||||
// API only: Secure (no login or pwd)
|
||||
// ?signature=md5(totoblah)
|
||||
( yourls_is_API() &&
|
||||
!isset( $_REQUEST['timestamp'] ) &&
|
||||
isset( $_REQUEST['signature'] ) && !empty( $_REQUEST['signature'] )
|
||||
)
|
||||
{
|
||||
yourls_do_action( 'pre_login_signature' );
|
||||
$unfiltered_valid = yourls_check_signature();
|
||||
}
|
||||
|
||||
elseif
|
||||
// API or normal: login with username & pwd
|
||||
( isset( $_REQUEST['username'] ) && isset( $_REQUEST['password'] )
|
||||
&& !empty( $_REQUEST['username'] ) && !empty( $_REQUEST['password'] ) )
|
||||
{
|
||||
yourls_do_action( 'pre_login_username_password' );
|
||||
$unfiltered_valid = yourls_check_username_password();
|
||||
}
|
||||
|
||||
elseif
|
||||
// Normal only: cookies
|
||||
( !yourls_is_API() &&
|
||||
isset( $_COOKIE[ yourls_cookie_name() ] ) )
|
||||
{
|
||||
yourls_do_action( 'pre_login_cookie' );
|
||||
$unfiltered_valid = yourls_check_auth_cookie();
|
||||
}
|
||||
|
||||
// Regardless of validity, allow plugins to filter the boolean and have final word
|
||||
$valid = yourls_apply_filter( 'is_valid_user', $unfiltered_valid );
|
||||
|
||||
// Login for the win!
|
||||
if ( $valid ) {
|
||||
yourls_do_action( 'login' );
|
||||
|
||||
// (Re)store encrypted cookie if needed
|
||||
if ( !yourls_is_API() ) {
|
||||
yourls_store_cookie( YOURLS_USER );
|
||||
|
||||
// Login form : redirect to requested URL to avoid re-submitting the login form on page reload
|
||||
if( isset( $_REQUEST['username'] ) && isset( $_REQUEST['password'] ) && isset( $_SERVER['REQUEST_URI'] ) ) {
|
||||
// The return makes sure we exit this function before waiting for redirection.
|
||||
// See #3189 and note in yourls_redirect()
|
||||
return yourls_redirect( yourls_sanitize_url_safe($_SERVER['REQUEST_URI']) );
|
||||
}
|
||||
}
|
||||
|
||||
// Login successful
|
||||
return true;
|
||||
}
|
||||
|
||||
// Login failed
|
||||
yourls_do_action( 'login_failed' );
|
||||
|
||||
if ( isset( $_REQUEST['username'] ) || isset( $_REQUEST['password'] ) ) {
|
||||
return yourls__( 'Invalid username or password' );
|
||||
} else {
|
||||
return yourls__( 'Please log in' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check auth against list of login=>pwd. Sets user if applicable, returns bool
|
||||
*
|
||||
* @return bool true if login/pwd pair is valid (and sets user if applicable), false otherwise
|
||||
*/
|
||||
function yourls_check_username_password() {
|
||||
global $yourls_user_passwords;
|
||||
|
||||
// If login form (not API), check for nonce
|
||||
if(!yourls_is_API()) {
|
||||
yourls_verify_nonce('admin_login');
|
||||
}
|
||||
|
||||
if( isset( $yourls_user_passwords[ $_REQUEST['username'] ] ) && yourls_check_password_hash( $_REQUEST['username'], $_REQUEST['password'] ) ) {
|
||||
yourls_set_user( $_REQUEST['username'] );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a submitted password sent in plain text against stored password which can be a salted hash
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $submitted_password
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_check_password_hash($user, $submitted_password ) {
|
||||
global $yourls_user_passwords;
|
||||
|
||||
if( !isset( $yourls_user_passwords[ $user ] ) )
|
||||
return false;
|
||||
|
||||
if ( yourls_has_phpass_password( $user ) ) {
|
||||
// Stored password is hashed
|
||||
list( , $hash ) = explode( ':', $yourls_user_passwords[ $user ] );
|
||||
$hash = str_replace( '!', '$', $hash );
|
||||
return ( yourls_phpass_check( $submitted_password, $hash ) );
|
||||
} else if( yourls_has_md5_password( $user ) ) {
|
||||
// Stored password is a salted md5 hash: "md5:<$r = rand(10000,99999)>:<md5($r.'thepassword')>"
|
||||
list( , $salt, ) = explode( ':', $yourls_user_passwords[ $user ] );
|
||||
return( $yourls_user_passwords[ $user ] == 'md5:'.$salt.':'.md5( $salt . $submitted_password ) );
|
||||
} else {
|
||||
// Password stored in clear text
|
||||
return( $yourls_user_passwords[ $user ] === $submitted_password );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite plaintext passwords in config file with hashed versions.
|
||||
*
|
||||
* @since 1.7
|
||||
* @param string $config_file Full path to file
|
||||
* @return true|string if overwrite was successful, an error message otherwise
|
||||
*/
|
||||
function yourls_hash_passwords_now( $config_file ) {
|
||||
if( !is_readable( $config_file ) ) {
|
||||
yourls_debug_log( 'Cannot hash passwords: cannot read file ' . $config_file );
|
||||
return 'cannot read file'; // not sure that can actually happen...
|
||||
}
|
||||
|
||||
if( !is_writable( $config_file ) ) {
|
||||
yourls_debug_log( 'Cannot hash passwords: cannot write file ' . $config_file );
|
||||
return 'cannot write file';
|
||||
}
|
||||
|
||||
$yourls_user_passwords = [];
|
||||
// Include file to read value of $yourls_user_passwords
|
||||
// Temporary suppress error reporting to avoid notices about redeclared constants
|
||||
$errlevel = error_reporting();
|
||||
error_reporting( 0 );
|
||||
require $config_file;
|
||||
error_reporting( $errlevel );
|
||||
|
||||
$configdata = file_get_contents( $config_file );
|
||||
|
||||
if( $configdata == false ) {
|
||||
yourls_debug_log('Cannot hash passwords: file_get_contents() false with ' . $config_file);
|
||||
return 'could not read file';
|
||||
}
|
||||
|
||||
$to_hash = 0; // keep track of number of passwords that need hashing
|
||||
foreach ( $yourls_user_passwords as $user => $password ) {
|
||||
// avoid "deprecated" warning when password is null -- see test case in tests/data/auth/preg_replace_problem.php
|
||||
$password ??= '';
|
||||
if ( !yourls_has_phpass_password( $user ) && !yourls_has_md5_password( $user ) ) {
|
||||
$to_hash++;
|
||||
$hash = yourls_phpass_hash( $password );
|
||||
// PHP would interpret $ as a variable, so replace it in storage.
|
||||
$hash = str_replace( '$', '!', $hash );
|
||||
$quotes = "'" . '"';
|
||||
$pattern = "/[$quotes]${user}[$quotes]\s*=>\s*[$quotes]" . preg_quote( $password, '/' ) . "[$quotes]/";
|
||||
$replace = "'$user' => 'phpass:$hash' /* Password encrypted by YOURLS */ ";
|
||||
$count = 0;
|
||||
$configdata = preg_replace( $pattern, $replace, $configdata, -1, $count );
|
||||
// There should be exactly one replacement. Otherwise, fast fail.
|
||||
if ( $count != 1 ) {
|
||||
yourls_debug_log( "Problem with preg_replace for password hash of user $user" );
|
||||
return 'preg_replace problem';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( $to_hash == 0 ) {
|
||||
yourls_debug_log('Cannot hash passwords: no password found in ' . $config_file);
|
||||
return 'no password found';
|
||||
}
|
||||
|
||||
$success = file_put_contents( $config_file, $configdata );
|
||||
if ( $success === FALSE ) {
|
||||
yourls_debug_log( 'Failed writing to ' . $config_file );
|
||||
return 'could not write file';
|
||||
}
|
||||
|
||||
yourls_debug_log('Successfully encrypted passwords in ' . basename($config_file));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a password hash
|
||||
*
|
||||
* @since 1.7
|
||||
* @param string $password password to hash
|
||||
* @return string hashed password
|
||||
*/
|
||||
function yourls_phpass_hash( $password ) {
|
||||
/**
|
||||
* Filter for hashing algorithm. See https://www.php.net/manual/en/function.password-hash.php
|
||||
* Hashing algos are available if PHP was compiled with it.
|
||||
* PASSWORD_BCRYPT is always available.
|
||||
*/
|
||||
$algo = yourls_apply_filter('hash_algo', PASSWORD_BCRYPT);
|
||||
|
||||
/**
|
||||
* Filter for hashing options. See https://www.php.net/manual/en/function.password-hash.php
|
||||
* A typical option for PASSWORD_BCRYPT would be ['cost' => <int in range 4-31> ]
|
||||
* We're leaving the options at default values, which means a cost of 10 for PASSWORD_BCRYPT.
|
||||
*
|
||||
* If willing to modify this, be warned about the computing time, as there is a 2^n factor.
|
||||
* See https://gist.github.com/ozh/65a75392b7cb254131cc55afd28de99b for examples.
|
||||
*/
|
||||
$options = yourls_apply_filter('hash_options', [] );
|
||||
|
||||
return password_hash($password, $algo, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a password matches a hash
|
||||
*
|
||||
* @since 1.7
|
||||
* @param string $password clear (eg submitted in a form) password
|
||||
* @param string $hash hash
|
||||
* @return bool true if the hash matches the password, false otherwise
|
||||
*/
|
||||
function yourls_phpass_check( $password, $hash ) {
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check to see if any passwords are stored as cleartext.
|
||||
*
|
||||
* @since 1.7
|
||||
* @return bool true if any passwords are cleartext
|
||||
*/
|
||||
function yourls_has_cleartext_passwords() {
|
||||
global $yourls_user_passwords;
|
||||
foreach ( $yourls_user_passwords as $user => $pwdata ) {
|
||||
if ( !yourls_has_md5_password( $user ) && !yourls_has_phpass_password( $user ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user has a md5 hashed password
|
||||
*
|
||||
* Check if a user password is 'md5:[38 chars]'.
|
||||
* TODO: deprecate this when/if we have proper user management with password hashes stored in the DB
|
||||
*
|
||||
* @since 1.7
|
||||
* @param string $user user login
|
||||
* @return bool true if password hashed, false otherwise
|
||||
*/
|
||||
function yourls_has_md5_password( $user ) {
|
||||
global $yourls_user_passwords;
|
||||
return( isset( $yourls_user_passwords[ $user ] )
|
||||
&& substr( $yourls_user_passwords[ $user ], 0, 4 ) == 'md5:'
|
||||
&& strlen( $yourls_user_passwords[ $user ] ) == 42 // http://www.google.com/search?q=the+answer+to+life+the+universe+and+everything
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user's password is hashed with password_hash
|
||||
*
|
||||
* Check if a user password is 'phpass:[lots of chars]'.
|
||||
* (For historical reason we're using 'phpass' as an identifier.)
|
||||
* TODO: deprecate this when/if we have proper user management with password hashes stored in the DB
|
||||
*
|
||||
* @since 1.7
|
||||
* @param string $user user login
|
||||
* @return bool true if password hashed with password_hash, otherwise false
|
||||
*/
|
||||
function yourls_has_phpass_password( $user ) {
|
||||
global $yourls_user_passwords;
|
||||
return( isset( $yourls_user_passwords[ $user ] )
|
||||
&& substr( $yourls_user_passwords[ $user ], 0, 7 ) == 'phpass:'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check auth against encrypted COOKIE data. Sets user if applicable, returns bool
|
||||
*
|
||||
* @return bool true if authenticated, false otherwise
|
||||
*/
|
||||
function yourls_check_auth_cookie() {
|
||||
global $yourls_user_passwords;
|
||||
foreach( $yourls_user_passwords as $valid_user => $valid_password ) {
|
||||
if ( yourls_cookie_value( $valid_user ) === $_COOKIE[ yourls_cookie_name() ] ) {
|
||||
yourls_set_user( $valid_user );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check auth against signature and timestamp. Sets user if applicable, returns bool
|
||||
*
|
||||
* Original usage :
|
||||
* http://sho.rt/yourls-api.php?timestamp=<timestamp>&signature=<md5 hash>&action=...
|
||||
* Since 1.7.7 we allow a `hash` parameter and an arbitrary hashed signature, hashed
|
||||
* with the `hash` function. Examples :
|
||||
* http://sho.rt/yourls-api.php?timestamp=<timestamp>&signature=<sha512 hash>&hash=sha512&action=...
|
||||
* http://sho.rt/yourls-api.php?timestamp=<timestamp>&signature=<crc32 hash>&hash=crc32&action=...
|
||||
*
|
||||
* @since 1.4.1
|
||||
* @return bool False if signature or timestamp missing or invalid, true if valid
|
||||
*/
|
||||
function yourls_check_signature_timestamp() {
|
||||
if( !isset( $_REQUEST['signature'] ) OR empty( $_REQUEST['signature'] )
|
||||
OR !isset( $_REQUEST['timestamp'] ) OR empty( $_REQUEST['timestamp'] )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Exit if the timestamp argument is outdated or invalid
|
||||
if( !yourls_check_timestamp( $_REQUEST['timestamp'] )) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if there is a hash argument, make sure it's part of the availables algos
|
||||
$hash_function = isset($_REQUEST['hash']) ? (string)$_REQUEST['hash'] : 'md5';
|
||||
if( !in_array($hash_function, hash_algos()) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check signature & timestamp against all possible users
|
||||
global $yourls_user_passwords;
|
||||
foreach( $yourls_user_passwords as $valid_user => $valid_password ) {
|
||||
if (
|
||||
hash( $hash_function, $_REQUEST['timestamp'].yourls_auth_signature( $valid_user ) ) === $_REQUEST['signature']
|
||||
or
|
||||
hash( $hash_function, yourls_auth_signature( $valid_user ).$_REQUEST['timestamp'] ) === $_REQUEST['signature']
|
||||
) {
|
||||
yourls_set_user( $valid_user );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Signature doesn't match known user
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check auth against signature. Sets user if applicable, returns bool
|
||||
*
|
||||
* @since 1.4.1
|
||||
* @return bool False if signature missing or invalid, true if valid
|
||||
*/
|
||||
function yourls_check_signature() {
|
||||
if( !isset( $_REQUEST['signature'] ) OR empty( $_REQUEST['signature'] ) )
|
||||
return false;
|
||||
|
||||
// Check signature against all possible users
|
||||
global $yourls_user_passwords;
|
||||
foreach( $yourls_user_passwords as $valid_user => $valid_password ) {
|
||||
if ( yourls_auth_signature( $valid_user ) === $_REQUEST['signature'] ) {
|
||||
yourls_set_user( $valid_user );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Signature doesn't match known user
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate secret signature hash
|
||||
*
|
||||
* @param false|string $username Username to generate signature for, or false to use current user
|
||||
* @return string Signature
|
||||
*/
|
||||
function yourls_auth_signature( $username = false ) {
|
||||
if( !$username && defined('YOURLS_USER') ) {
|
||||
$username = YOURLS_USER;
|
||||
}
|
||||
return ( $username ? substr( yourls_salt( $username ), 0, 10 ) : 'Cannot generate auth signature: no username' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if timestamp is not too old
|
||||
*
|
||||
* @param int $time Timestamp to check
|
||||
* @return bool True if timestamp is valid
|
||||
*/
|
||||
function yourls_check_timestamp( $time ) {
|
||||
$now = time();
|
||||
// Allow timestamp to be a little in the future or the past -- see Issue 766
|
||||
return yourls_apply_filter( 'check_timestamp', abs( $now - (int)$time ) < yourls_get_nonce_life(), $time );
|
||||
}
|
||||
|
||||
/**
|
||||
* Store new cookie. No $user will delete the cookie.
|
||||
*
|
||||
* @param string $user User login, or empty string to delete cookie
|
||||
* @return void
|
||||
*/
|
||||
function yourls_store_cookie( $user = '' ) {
|
||||
|
||||
// No user will delete the cookie with a cookie time from the past
|
||||
if( !$user ) {
|
||||
$time = time() - 3600;
|
||||
} else {
|
||||
$time = time() + yourls_get_cookie_life();
|
||||
}
|
||||
|
||||
$path = yourls_apply_filter( 'setcookie_path', '/' );
|
||||
$domain = yourls_apply_filter( 'setcookie_domain', parse_url( yourls_get_yourls_site(), PHP_URL_HOST ) );
|
||||
$secure = yourls_apply_filter( 'setcookie_secure', yourls_is_ssl() );
|
||||
$httponly = yourls_apply_filter( 'setcookie_httponly', true );
|
||||
|
||||
// Some browsers refuse to store localhost cookie
|
||||
if ( $domain == 'localhost' )
|
||||
$domain = '';
|
||||
|
||||
yourls_do_action( 'pre_setcookie', $user, $time, $path, $domain, $secure, $httponly );
|
||||
|
||||
if ( !headers_sent( $filename, $linenum ) ) {
|
||||
yourls_setcookie( yourls_cookie_name(), yourls_cookie_value( $user ), $time, $path, $domain, $secure, $httponly );
|
||||
} else {
|
||||
// For some reason cookies were not stored: action to be able to debug that
|
||||
yourls_do_action( 'setcookie_failed', $user );
|
||||
yourls_debug_log( "Could not store cookie: headers already sent in $filename on line $linenum" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replacement for PHP's setcookie(), with support for SameSite cookie attribute
|
||||
*
|
||||
* @see https://github.com/GoogleChromeLabs/samesite-examples/blob/master/php.md
|
||||
* @see https://stackoverflow.com/a/59654832/36850
|
||||
* @see https://www.php.net/manual/en/function.setcookie.php
|
||||
*
|
||||
* @since 1.7.7
|
||||
* @param string $name cookie name
|
||||
* @param string $value cookie value
|
||||
* @param int $expire time the cookie expires as a Unix timestamp (number of seconds since the epoch)
|
||||
* @param string $path path on the server in which the cookie will be available on
|
||||
* @param string $domain (sub)domain that the cookie is available to
|
||||
* @param bool $secure if cookie should only be transmitted over a secure HTTPS connection
|
||||
* @param bool $httponly if cookie will be made accessible only through the HTTP protocol
|
||||
* @return bool setcookie() result : false if output sent before, true otherwise. This does not indicate whether the user accepted the cookie.
|
||||
*/
|
||||
function yourls_setcookie($name, $value, $expire, $path, $domain, $secure, $httponly) {
|
||||
$samesite = yourls_apply_filter('setcookie_samesite', 'Lax' );
|
||||
|
||||
return(setcookie($name, $value, array(
|
||||
'expires' => $expire,
|
||||
'path' => $path,
|
||||
'domain' => $domain,
|
||||
'samesite' => $samesite,
|
||||
'secure' => $secure,
|
||||
'httponly' => $httponly,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user name
|
||||
*
|
||||
* @param string $user Username
|
||||
* @return void
|
||||
*/
|
||||
function yourls_set_user( $user ) {
|
||||
if( !defined( 'YOURLS_USER' ) )
|
||||
define( 'YOURLS_USER', $user );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get YOURLS_COOKIE_LIFE value (ie the life span of an auth cookie in seconds)
|
||||
*
|
||||
* Use this function instead of directly using the constant. This way, its value can be modified by plugins
|
||||
* on a per case basis
|
||||
*
|
||||
* @since 1.7.7
|
||||
* @see includes/Config/Config.php
|
||||
* @return integer cookie life span, in seconds
|
||||
*/
|
||||
function yourls_get_cookie_life() {
|
||||
return yourls_apply_filter( 'get_cookie_life', YOURLS_COOKIE_LIFE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get YOURLS_NONCE_LIFE value (ie life span of a nonce in seconds)
|
||||
*
|
||||
* Use this function instead of directly using the constant. This way, its value can be modified by plugins
|
||||
* on a per case basis
|
||||
*
|
||||
* @since 1.7.7
|
||||
* @see includes/Config/Config.php
|
||||
* @see https://en.wikipedia.org/wiki/Cryptographic_nonce
|
||||
* @return integer nonce life span, in seconds
|
||||
*/
|
||||
function yourls_get_nonce_life() {
|
||||
return yourls_apply_filter( 'get_nonce_life', YOURLS_NONCE_LIFE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get YOURLS cookie name
|
||||
*
|
||||
* The name is unique for each install, to prevent mismatch between sho.rt and very.sho.rt -- see #1673
|
||||
*
|
||||
* TODO: when multi user is implemented, the whole cookie stuff should be reworked to allow storing multiple users
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @return string unique cookie name for a given YOURLS site
|
||||
*/
|
||||
function yourls_cookie_name() {
|
||||
return yourls_apply_filter( 'cookie_name', 'yourls_' . yourls_salt( yourls_get_yourls_site() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get auth cookie value
|
||||
*
|
||||
* @since 1.7.7
|
||||
* @param string $user user name
|
||||
* @return string cookie value
|
||||
*/
|
||||
function yourls_cookie_value( $user ) {
|
||||
return yourls_apply_filter( 'set_cookie_value', yourls_salt( $user ?? '' ), $user );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a time-dependent string for nonce creation
|
||||
*
|
||||
* Actually, this returns a float: ceil rounds up a value but is of type float, see https://www.php.net/ceil
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
function yourls_tick() {
|
||||
return ceil( time() / yourls_get_nonce_life() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return hashed string
|
||||
*
|
||||
* This function is badly named, it's not a salt or a salted string : it's a cryptographic hash.
|
||||
*
|
||||
* @since 1.4.1
|
||||
* @param string $string string to salt
|
||||
* @return string hashed string
|
||||
*/
|
||||
function yourls_salt( $string ) {
|
||||
$salt = defined('YOURLS_COOKIEKEY') ? YOURLS_COOKIEKEY : md5(__FILE__) ;
|
||||
return yourls_apply_filter( 'yourls_salt', hash_hmac( yourls_hmac_algo(), $string, $salt), $string );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an available hash_hmac() algorithm
|
||||
*
|
||||
* @since 1.8.3
|
||||
* @return string hash_hmac() algorithm
|
||||
*/
|
||||
function yourls_hmac_algo() {
|
||||
$algo = yourls_apply_filter( 'hmac_algo', 'sha256' );
|
||||
if( !in_array( $algo, hash_hmac_algos() ) ) {
|
||||
$algo = 'sha256';
|
||||
}
|
||||
return $algo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a time limited, action limited and user limited token
|
||||
*
|
||||
* @param string $action Action to create nonce for
|
||||
* @param false|string $user Optional user string, false for current user
|
||||
* @return string Nonce token
|
||||
*/
|
||||
function yourls_create_nonce($action, $user = false ) {
|
||||
if( false === $user ) {
|
||||
$user = defined('YOURLS_USER') ? YOURLS_USER : '-1';
|
||||
}
|
||||
$tick = yourls_tick();
|
||||
$nonce = substr( yourls_salt($tick . $action . $user), 0, 10 );
|
||||
// Allow plugins to alter the nonce
|
||||
return yourls_apply_filter( 'create_nonce', $nonce, $action, $user );
|
||||
}
|
||||
|
||||
/**
|
||||
* Echoes or returns a nonce field for inclusion into a form
|
||||
*
|
||||
* @param string $action Action to create nonce for
|
||||
* @param string $name Optional name of nonce field -- defaults to 'nonce'
|
||||
* @param false|string $user Optional user string, false if unspecified
|
||||
* @param bool $echo True to echo, false to return nonce field
|
||||
* @return string Nonce field
|
||||
*/
|
||||
function yourls_nonce_field($action, $name = 'nonce', $user = false, $echo = true ) {
|
||||
$field = '<input type="hidden" id="'.$name.'" name="'.$name.'" value="'.yourls_create_nonce( $action, $user ).'" />';
|
||||
if( $echo )
|
||||
echo $field."\n";
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a nonce to a URL. If URL omitted, adds nonce to current URL
|
||||
*
|
||||
* @param string $action Action to create nonce for
|
||||
* @param string $url Optional URL to add nonce to -- defaults to current URL
|
||||
* @param string $name Optional name of nonce field -- defaults to 'nonce'
|
||||
* @param false|string $user Optional user string, false if unspecified
|
||||
* @return string URL with nonce added
|
||||
*/
|
||||
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']
|
||||
*
|
||||
* @param string $action
|
||||
* @param false|string $nonce Optional, string: nonce value, or false to use $_REQUEST['nonce']
|
||||
* @param false|string $user Optional, string user, false for current user
|
||||
* @param string $return Optional, string: message to die with if nonce is invalid
|
||||
* @return bool|void True if valid, dies otherwise
|
||||
*/
|
||||
function yourls_verify_nonce($action, $nonce = false, $user = false, $return = '' ) {
|
||||
// Get user
|
||||
if( false === $user ) {
|
||||
$user = defined('YOURLS_USER') ? YOURLS_USER : '-1';
|
||||
}
|
||||
|
||||
// Get nonce value from $_REQUEST if not specified
|
||||
if( false === $nonce && isset( $_REQUEST['nonce'] ) ) {
|
||||
$nonce = $_REQUEST['nonce'];
|
||||
}
|
||||
|
||||
// Allow plugins to short-circuit the rest of the function
|
||||
if (yourls_apply_filter( 'verify_nonce', false, $action, $nonce, $user, $return ) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if YOURLS_USER comes from environment variables
|
||||
*
|
||||
* @since 1.8.2
|
||||
* @return bool true if YOURLS_USER and YOURLS_PASSWORD are defined as environment variables
|
||||
*/
|
||||
function yourls_is_user_from_env() {
|
||||
return yourls_apply_filter('is_user_from_env', getenv('YOURLS_USER') && getenv('YOURLS_PASSWORD'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should hash passwords in the config file
|
||||
*
|
||||
* By default, passwords are hashed. They are not if
|
||||
* - there is no password in clear text in the config file (ie everything is already hashed)
|
||||
* - the user defined constant YOURLS_NO_HASH_PASSWORD is true, see https://docs.yourls.org/guide/essentials/credentials.html#i-don-t-want-to-encrypt-my-password
|
||||
* - YOURLS_USER and YOURLS_PASSWORD are provided by the environment, not the config file
|
||||
*
|
||||
* @since 1.8.2
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_maybe_hash_passwords() {
|
||||
$hash = true;
|
||||
|
||||
if ( !yourls_has_cleartext_passwords()
|
||||
OR (yourls_skip_password_hashing())
|
||||
OR (yourls_is_user_from_env())
|
||||
) {
|
||||
$hash = false;
|
||||
}
|
||||
|
||||
return yourls_apply_filter('maybe_hash_password', $hash );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user setting for skipping password hashing is set
|
||||
*
|
||||
* @since 1.8.2
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_skip_password_hashing() {
|
||||
return yourls_apply_filter('skip_password_hashing', defined('YOURLS_NO_HASH_PASSWORD') && YOURLS_NO_HASH_PASSWORD);
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
/*
|
||||
* YOURLS
|
||||
* Compatibility functions when either missing from older PHP versions or not included by default
|
||||
*/
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
|
||||
/**
|
||||
* json_encode for PHP, should someone run a distro without php-json -- see http://askubuntu.com/questions/361424/
|
||||
*
|
||||
*/
|
||||
if( !function_exists( 'json_encode' ) ) {
|
||||
function json_encode( $array ) {
|
||||
return yourls_array_to_json( $array );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an associative array of arbitrary depth and dimension into JSON representation. Used for compatibility with older PHP builds.
|
||||
*
|
||||
* @param array $array the array to convert.
|
||||
* @return mixed The resulting JSON string, or false if the argument was not an array.
|
||||
* @author Andy Rusterholz
|
||||
* @link http://php.net/json_encode (see comments)
|
||||
*/
|
||||
function yourls_array_to_json( $array ){
|
||||
|
||||
if( !is_array( $array ) ){
|
||||
return false;
|
||||
}
|
||||
|
||||
$associative = count( array_diff( array_keys($array), array_keys( array_keys( $array )) ));
|
||||
if( $associative ){
|
||||
|
||||
$construct = array();
|
||||
foreach( $array as $key => $value ){
|
||||
|
||||
// We first copy each key/value pair into a staging array,
|
||||
// formatting each key and value properly as we go.
|
||||
|
||||
// Format the key:
|
||||
if( is_numeric( $key ) ){
|
||||
$key = "key_$key";
|
||||
}
|
||||
$key = '"'.addslashes( $key ).'"';
|
||||
|
||||
// Format the value:
|
||||
if( is_array( $value )){
|
||||
$value = yourls_array_to_json( $value );
|
||||
} else if( !is_numeric( $value ) || is_string( $value ) ){
|
||||
$value = '"'.addslashes( $value ).'"';
|
||||
}
|
||||
|
||||
// Add to staging array:
|
||||
$construct[] = "$key: $value";
|
||||
}
|
||||
|
||||
// Then we collapse the staging array into the JSON form:
|
||||
$result = "{ " . implode( ", ", $construct ) . " }";
|
||||
|
||||
} else { // If the array is a vector (not associative):
|
||||
|
||||
$construct = array();
|
||||
foreach( $array as $value ){
|
||||
|
||||
// Format the value:
|
||||
if( is_array( $value )){
|
||||
$value = yourls_array_to_json( $value );
|
||||
} else if( !is_numeric( $value ) || is_string( $value ) ){
|
||||
$value = '"'.addslashes($value).'"';
|
||||
}
|
||||
|
||||
// Add to staging array:
|
||||
$construct[] = $value;
|
||||
}
|
||||
|
||||
// Then we collapse the staging array into the JSON form:
|
||||
$result = "[ " . implode( ", ", $construct ) . " ]";
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* BC Math functions (assuming if one doesn't exist, none does)
|
||||
*
|
||||
*/
|
||||
if ( !function_exists( 'bcdiv' ) ) {
|
||||
function bcdiv( $dividend, $divisor ) {
|
||||
$quotient = floor( $dividend/$divisor );
|
||||
return $quotient;
|
||||
}
|
||||
function bcmod( $dividend, $modulo ) {
|
||||
$remainder = $dividend%$modulo;
|
||||
return $remainder;
|
||||
}
|
||||
function bcmul( $left, $right ) {
|
||||
return $left * $right;
|
||||
}
|
||||
function bcadd( $left, $right ) {
|
||||
return $left + $right;
|
||||
}
|
||||
function bcpow( $base, $power ) {
|
||||
return pow( $base, $power );
|
||||
}
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
/*
|
||||
* Functions relative to debugging
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 ) {
|
||||
yourls_do_action( 'debug_log', $msg );
|
||||
// Get the DB object ($ydb), get its profiler (\Aura\Sql\Profiler\Profiler), its logger (\Aura\Sql\Profiler\MemoryLogger) and
|
||||
// pass it a unused argument (loglevel) and the message
|
||||
yourls_get_db()->getProfiler()->getLogger()->log( 'debug', $msg);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the debug log
|
||||
*
|
||||
* @since 1.7.3
|
||||
* @return array
|
||||
*/
|
||||
function yourls_get_debug_log() {
|
||||
return yourls_get_db()->getProfiler()->getLogger()->getMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of SQL queries performed
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function yourls_get_num_queries() {
|
||||
return yourls_apply_filter( 'get_num_queries', yourls_get_db()->get_num_queries() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug mode set
|
||||
*
|
||||
* @since 1.7.3
|
||||
* @param bool $bool Debug on or off
|
||||
* @return void
|
||||
*/
|
||||
function yourls_debug_mode( $bool ) {
|
||||
// log queries if true
|
||||
yourls_get_db()->getProfiler()->setActive( (bool)$bool );
|
||||
|
||||
// report notices if true
|
||||
$level = $bool ? -1 : ( E_ERROR | E_PARSE );
|
||||
error_reporting( $level );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return YOURLS debug mode
|
||||
*
|
||||
* @since 1.7.7
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_get_debug_mode() {
|
||||
return defined( 'YOURLS_DEBUG' ) && YOURLS_DEBUG;
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
<?php
|
||||
/**
|
||||
* Deprecated functions from past YOURLS versions. Don't use them, as they may be
|
||||
* removed in a later version. Use the newer alternatives instead.
|
||||
*
|
||||
* Note to devs: when deprecating a function, move it here. Then check all the places
|
||||
* in core that might be using it, including core plugins.
|
||||
*
|
||||
* Usage : yourls_deprecated_function( 'function_name', 'version', 'replacement' );
|
||||
* Output: "{function_name} is deprecated since version {version}! Use {replacement} instead."
|
||||
*
|
||||
* Usage : yourls_deprecated_function( 'function_name', 'version' );
|
||||
* Output: "{function_name} is deprecated since version {version} with no alternative available."
|
||||
*
|
||||
* @see yourls_deprecated_function()
|
||||
*/
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
|
||||
/**
|
||||
* Return current admin page, or null if not an admin page. Was not used anywhere.
|
||||
*
|
||||
* @return mixed string if admin page, null if not an admin page
|
||||
* @since 1.6
|
||||
* @deprecated 1.9.1
|
||||
*/
|
||||
function yourls_current_admin_page() {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.9.1' );
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP emulation of JS's encodeURI
|
||||
*
|
||||
* @link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURI
|
||||
* @deprecated 1.9.1
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
function yourls_encodeURI($url) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.9.1', '' );
|
||||
// Decode URL all the way
|
||||
$result = yourls_rawurldecode_while_encoded( $url );
|
||||
// Encode once
|
||||
$result = strtr( rawurlencode( $result ), array (
|
||||
'%3B' => ';', '%2C' => ',', '%2F' => '/', '%3F' => '?', '%3A' => ':', '%40' => '@',
|
||||
'%26' => '&', '%3D' => '=', '%2B' => '+', '%24' => '$', '%21' => '!', '%2A' => '*',
|
||||
'%27' => '\'', '%28' => '(', '%29' => ')', '%23' => '#',
|
||||
) );
|
||||
// @TODO:
|
||||
// Known limit: this will most likely break IDN URLs such as http://www.académie-française.fr/
|
||||
// To fully support IDN URLs, advocate use of a plugin.
|
||||
return yourls_apply_filter( 'encodeURI', $result, $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a file is a plugin file
|
||||
*
|
||||
* @deprecated 1.8.3
|
||||
*/
|
||||
function yourls_validate_plugin_file( $file ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.8.3', 'yourls_is_a_plugin_file' );
|
||||
return yourls_is_a_plugin_file($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a unique(ish) hash for a string to be used as a valid HTML id
|
||||
*
|
||||
* @deprecated 1.8.3
|
||||
*/
|
||||
function yourls_string2htmlid( $string ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.8.3', 'yourls_unique_element_id' );
|
||||
return yourls_apply_filter( 'string2htmlid', 'y'.abs( crc32( $string ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search text from query string variables search_protocol, search_slashes and search
|
||||
*
|
||||
* Some servers don't like query strings containing "(ht|f)tp(s)://". A javascript bit
|
||||
* explodes the search text into protocol, slashes and the rest (see JS function
|
||||
* split_search_text_before_search()) and this function glues pieces back together
|
||||
* See issue https://github.com/YOURLS/YOURLS/issues/1576
|
||||
*
|
||||
* @since 1.7
|
||||
* @deprecated 1.8.2
|
||||
* @return string Search string
|
||||
*/
|
||||
function yourls_get_search_text() {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.8.2', 'YOURLS\Views\AdminParams::get_search' );
|
||||
$view_params = new YOURLS\Views\AdminParams();
|
||||
return $view_params->get_search();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current time based on specified type. Stolen from WP.
|
||||
*
|
||||
* The 'mysql' type will return the time in the format for MySQL DATETIME field.
|
||||
* The 'timestamp' type will return the current timestamp.
|
||||
*
|
||||
* If $gmt is set to either '1' or 'true', then both types will use GMT time.
|
||||
* if $gmt is false, the output is adjusted with the GMT offset in the WordPress option.
|
||||
*
|
||||
* @since 1.6
|
||||
* @deprecated 1.7.10
|
||||
*
|
||||
* @param string $type Either 'mysql' or 'timestamp'.
|
||||
* @param int|bool $gmt Optional. Whether to use GMT timezone. Default is false.
|
||||
* @return int|string String if $type is 'gmt', int if $type is 'timestamp'.
|
||||
*/
|
||||
function yourls_current_time( $type, $gmt = 0 ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.10', 'yourls_get_timestamp' );
|
||||
switch ( $type ) {
|
||||
case 'mysql':
|
||||
return ( $gmt ) ? gmdate( 'Y-m-d H:i:s' ) : gmdate( 'Y-m-d H:i:s', yourls_get_timestamp( time() ));
|
||||
case 'timestamp':
|
||||
return ( $gmt ) ? time() : yourls_get_timestamp( time() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lowercase scheme and domain of an URI - see issues 591, 1630, 1889
|
||||
*
|
||||
* Renamed to yourls_normalize_uri() in 1.7.10 because the function now does more than just
|
||||
* lowercasing the scheme and domain.
|
||||
*
|
||||
* @deprecated 1.7.10
|
||||
*
|
||||
*/
|
||||
function yourls_lowercase_scheme_domain( $url ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.10', 'yourls_normalize_uri' );
|
||||
return yourls_normalize_uri( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* The original string sanitize function
|
||||
*
|
||||
* @deprecated 1.7.10
|
||||
*
|
||||
*/
|
||||
function yourls_sanitize_string( $string, $restrict_to_shorturl_charset = false ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.10', 'yourls_sanitize_keyword' );
|
||||
return yourls_sanitize_keyword( $string, $restrict_to_shorturl_charset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return favicon URL (either default or custom)
|
||||
*
|
||||
* @deprecated 1.7.10
|
||||
*
|
||||
*/
|
||||
function yourls_favicon( $echo = true ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.10', 'yourls_get_yourls_favicon_url' );
|
||||
return yourls_get_yourls_favicon_url( $echo );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of stats for a given keyword
|
||||
*
|
||||
* @deprecated 1.7.10
|
||||
*
|
||||
*/
|
||||
function yourls_get_link_stats( $url ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.10', 'yourls_get_keyword_stats' );
|
||||
return yourls_get_keyword_stats( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a long URL already exists in the DB. Return NULL (doesn't exist) or an object with URL informations.
|
||||
*
|
||||
* @since 1.5.1
|
||||
* @deprecated 1.7.10
|
||||
*
|
||||
*/
|
||||
function yourls_url_exists( $url ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.10', 'yourls_long_url_exists' );
|
||||
return yourls_long_url_exists( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return word or words if more than one
|
||||
*
|
||||
*/
|
||||
function yourls_plural( $word, $count=1 ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.6', 'yourls_n' );
|
||||
return $word . ($count > 1 ? 's' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of all shorturls associated to the same long URL. Returns NULL or array of keywords.
|
||||
*
|
||||
*/
|
||||
function yourls_get_duplicate_keywords( $longurl ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7', 'yourls_get_longurl_keywords' );
|
||||
if( !yourls_allow_duplicate_longurls() )
|
||||
return NULL;
|
||||
return yourls_apply_filter( 'get_duplicate_keywords', yourls_get_longurl_keywords ( $longurl ), $longurl );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure a integer is safe
|
||||
*
|
||||
* Note: this function is dumb and dumbly named since it does not intval(). DO NOT USE.
|
||||
*
|
||||
*/
|
||||
function yourls_intval( $int ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7', 'yourls_sanitize_int' );
|
||||
return yourls_escape( $int );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get remote content via a GET request using best transport available
|
||||
*
|
||||
*/
|
||||
function yourls_get_remote_content( $url, $maxlen = 4096, $timeout = 5 ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7', 'yourls_http_get_body' );
|
||||
return yourls_http_get_body( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for yourls_apply_filter because I never remember if it's _filter or _filters
|
||||
*
|
||||
* At first I thought it made semantically more sense but thinking about it, I was wrong. It's one filter.
|
||||
* There may be several function hooked into it, but it still the same one filter.
|
||||
*
|
||||
* @since 1.6
|
||||
* @deprecated 1.7.1
|
||||
*
|
||||
* @param string $hook the name of the YOURLS element or action
|
||||
* @param mixed $value the value of the element before filtering
|
||||
* @return mixed
|
||||
*/
|
||||
function yourls_apply_filters( $hook, $value = '' ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.1', 'yourls_apply_filter' );
|
||||
return yourls_apply_filter( $hook, $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we'll need interface display function (ie not API or redirection)
|
||||
*
|
||||
*/
|
||||
function yourls_has_interface() {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.1' );
|
||||
if( yourls_is_API() or yourls_is_GO() )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a proxy is defined for HTTP requests
|
||||
*
|
||||
* @uses YOURLS_PROXY
|
||||
* @since 1.7
|
||||
* @deprecated 1.7.1
|
||||
* @return bool true if a proxy is defined, false otherwise
|
||||
*/
|
||||
function yourls_http_proxy_is_defined() {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.1', 'yourls_http_get_proxy' );
|
||||
return yourls_apply_filter( 'http_proxy_is_defined', defined( 'YOURLS_PROXY' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays translated string with gettext context
|
||||
*
|
||||
* This function has been renamed yourls_xe() for consistency with other *e() functions
|
||||
*
|
||||
* @see yourls_x()
|
||||
* @since 1.6
|
||||
* @deprecated 1.7.1
|
||||
*
|
||||
* @param string $text Text to translate
|
||||
* @param string $context Context information for the translators
|
||||
* @param string $domain Optional. Domain to retrieve the translated text
|
||||
* @return string Translated context string without pipe
|
||||
*/
|
||||
function yourls_ex( $text, $context, $domain = 'default' ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.1', 'yourls_xe' );
|
||||
echo yourls_xe( $text, $context, $domain );
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a string or an array of strings before DB usage. ALWAYS escape before using in a SQL query. Thanks.
|
||||
*
|
||||
* Deprecated in 1.7.3 because we moved onto using PDO and using built-in escaping functions, instead of
|
||||
* rolling our own.
|
||||
*
|
||||
* @deprecated 1.7.3
|
||||
* @param string|array $data string or array of strings to be escaped
|
||||
* @return string|array escaped data
|
||||
*/
|
||||
function yourls_escape( $data ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.3', 'PDO' );
|
||||
if( is_array( $data ) ) {
|
||||
foreach( $data as $k => $v ) {
|
||||
if( is_array( $v ) ) {
|
||||
$data[ $k ] = yourls_escape( $v );
|
||||
} else {
|
||||
$data[ $k ] = yourls_escape_real( $v );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$data = yourls_escape_real( $data );
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Real" escape. This function should NOT be called directly. Use yourls_escape() instead.
|
||||
*
|
||||
* This function uses a "real" escape if possible, using PDO, MySQL or MySQLi functions,
|
||||
* with a fallback to a "simple" addslashes
|
||||
* If you're implementing a custom DB engine or a custom cache system, you can define an
|
||||
* escape function using filter 'custom_escape_real'
|
||||
*
|
||||
* @since 1.7
|
||||
* @deprecated 1.7.3
|
||||
* @param string $a string to be escaped
|
||||
* @return string escaped string
|
||||
*/
|
||||
function yourls_escape_real( $string ) {
|
||||
yourls_deprecated_function( __FUNCTION__, '1.7.3', 'PDO' );
|
||||
global $ydb;
|
||||
if( isset( $ydb ) && ( $ydb instanceof \YOURLS\Database\YDB ) )
|
||||
return $ydb->escape( $string );
|
||||
|
||||
// YOURLS DB classes have been bypassed by a custom DB engine or a custom cache layer
|
||||
return yourls_apply_filter( 'custom_escape_real', addslashes( $string ), $string );
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
|
@ -0,0 +1,798 @@
|
|||
<?php
|
||||
/*
|
||||
* YOURLS
|
||||
* Function library for anything related to formatting / validating / sanitizing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert an integer (1337) to a string (3jk).
|
||||
*
|
||||
* @param int $num Number to convert
|
||||
* @param string $chars Characters to use for conversion
|
||||
* @return string Converted number
|
||||
*/
|
||||
function yourls_int2string($num, $chars = null) {
|
||||
if( $chars == null )
|
||||
$chars = yourls_get_shorturl_charset();
|
||||
$string = '';
|
||||
$len = strlen( $chars );
|
||||
while( $num >= $len ) {
|
||||
$mod = bcmod( (string)$num, (string)$len );
|
||||
$num = bcdiv( (string)$num, (string)$len );
|
||||
$string = $chars[ $mod ] . $string;
|
||||
}
|
||||
$string = $chars[ intval( $num ) ] . $string;
|
||||
|
||||
return yourls_apply_filter( 'int2string', $string, $num, $chars );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string (3jk) to an integer (1337)
|
||||
*
|
||||
* @param string $string String to convert
|
||||
* @param string $chars Characters to use for conversion
|
||||
* @return string Number (as a string)
|
||||
*/
|
||||
function yourls_string2int($string, $chars = null) {
|
||||
if( $chars == null )
|
||||
$chars = yourls_get_shorturl_charset();
|
||||
$integer = 0;
|
||||
$string = strrev( $string );
|
||||
$baselen = strlen( $chars );
|
||||
$inputlen = strlen( $string );
|
||||
for ($i = 0; $i < $inputlen; $i++) {
|
||||
$index = strpos( $chars, $string[$i] );
|
||||
$integer = bcadd( (string)$integer, bcmul( (string)$index, bcpow( (string)$baselen, (string)$i ) ) );
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'string2int', $integer, $string, $chars );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a unique string to be used as a valid HTML id
|
||||
*
|
||||
* @since 1.8.3
|
||||
* @param string $prefix Optional prefix
|
||||
* @return string The unique string
|
||||
*/
|
||||
function yourls_unique_element_id($prefix = 'yid') {
|
||||
static $id_counter = 0;
|
||||
return yourls_apply_filter( 'unique_element_id', $prefix . (string)++$id_counter );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure a link keyword (ie "1fv" as in "http://sho.rt/1fv") is acceptable
|
||||
*
|
||||
* If we are ADDING or EDITING a short URL, the keyword must comply to the short URL charset: every
|
||||
* character that doesn't belong to it will be removed.
|
||||
* But otherwise we must have a more conservative approach: we could be checking for a keyword that
|
||||
* was once valid but now the short URL charset has changed. In such a case, we are treating the keyword for what
|
||||
* it is: just a part of a URL, hence sanitize it as a URL.
|
||||
*
|
||||
* @param string $keyword short URL keyword
|
||||
* @param bool $restrict_to_shorturl_charset Optional, default false. True if we want the keyword to comply to short URL charset
|
||||
* @return string The sanitized keyword
|
||||
*/
|
||||
function yourls_sanitize_keyword( $keyword, $restrict_to_shorturl_charset = false ) {
|
||||
if( $restrict_to_shorturl_charset === true ) {
|
||||
// make a regexp pattern with the shorturl charset, and remove everything but this
|
||||
$pattern = yourls_make_regexp_pattern( yourls_get_shorturl_charset() );
|
||||
$valid = (string) substr( preg_replace( '![^'.$pattern.']!', '', $keyword ), 0, 199 );
|
||||
} else {
|
||||
$valid = yourls_sanitize_url( $keyword );
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'sanitize_string', $valid, $keyword, $restrict_to_shorturl_charset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a page title. No HTML per W3C http://www.w3.org/TR/html401/struct/global.html#h-7.4.2
|
||||
*
|
||||
*
|
||||
* @since 1.5
|
||||
* @param string $unsafe_title Title, potentially unsafe
|
||||
* @param string $fallback Optional fallback if after sanitization nothing remains
|
||||
* @return string Safe title
|
||||
*/
|
||||
function yourls_sanitize_title( $unsafe_title, $fallback = '' ) {
|
||||
$title = $unsafe_title;
|
||||
$title = strip_tags( $title );
|
||||
$title = preg_replace( "/\s+/", ' ', trim( $title ) );
|
||||
|
||||
if ( '' === $title || false === $title ) {
|
||||
$title = $fallback;
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'sanitize_title', $title, $unsafe_title, $fallback );
|
||||
}
|
||||
|
||||
/**
|
||||
* A few sanity checks on the URL. Used for redirection or DB.
|
||||
* For redirection when you don't trust the URL ($_SERVER variable, query string), see yourls_sanitize_url_safe()
|
||||
* For display purpose, see yourls_esc_url()
|
||||
*
|
||||
* @param string $unsafe_url unsafe URL
|
||||
* @param array $protocols Optional allowed protocols, default to global $yourls_allowedprotocols
|
||||
* @return string Safe URL
|
||||
*/
|
||||
function yourls_sanitize_url( $unsafe_url, $protocols = array() ) {
|
||||
$url = yourls_esc_url( $unsafe_url, 'redirection', $protocols );
|
||||
return yourls_apply_filter( 'sanitize_url', $url, $unsafe_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* A few sanity checks on the URL, including CRLF. Used for redirection when URL to be sanitized is critical and cannot be trusted.
|
||||
*
|
||||
* Use when critical URL comes from user input or environment variable. In such a case, this function will sanitize
|
||||
* it like yourls_sanitize_url() but will also remove %0A and %0D to prevent CRLF injection.
|
||||
* Still, some legit URLs contain %0A or %0D (see issue 2056, and for extra fun 1694, 1707, 2030, and maybe others)
|
||||
* so we're not using this function unless it's used for internal redirection when the target location isn't
|
||||
* hardcoded, to avoid XSS via CRLF
|
||||
*
|
||||
* @since 1.7.2
|
||||
* @param string $unsafe_url unsafe URL
|
||||
* @param array $protocols Optional allowed protocols, default to global $yourls_allowedprotocols
|
||||
* @return string Safe URL
|
||||
*/
|
||||
function yourls_sanitize_url_safe( $unsafe_url, $protocols = array() ) {
|
||||
$url = yourls_esc_url( $unsafe_url, 'safe', $protocols );
|
||||
return yourls_apply_filter( 'sanitize_url_safe', $url, $unsafe_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a replacement while a string is found, eg $subject = '%0%0%0DDD', $search ='%0D' -> $result =''
|
||||
*
|
||||
* Stolen from WP's _deep_replace
|
||||
*
|
||||
* @param string|array $search Needle, or array of needles.
|
||||
* @param string $subject Haystack.
|
||||
* @return string The string with the replaced values.
|
||||
*/
|
||||
function yourls_deep_replace($search, $subject ){
|
||||
$found = true;
|
||||
while($found) {
|
||||
$found = false;
|
||||
foreach( (array) $search as $val ) {
|
||||
while( strpos( $subject, $val ) !== false ) {
|
||||
$found = true;
|
||||
$subject = str_replace( $val, '', $subject );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure an integer is a valid integer (PHP's intval() limits to too small numbers)
|
||||
*
|
||||
* @param int $int Integer to check
|
||||
* @return string Integer as a string
|
||||
*/
|
||||
function yourls_sanitize_int($int ) {
|
||||
return ( substr( preg_replace( '/[^0-9]/', '', strval( $int ) ), 0, 20 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize an IP address
|
||||
* No check on validity, just return a sanitized string
|
||||
*
|
||||
* @param string $ip IP address
|
||||
* @return string IP address
|
||||
*/
|
||||
function yourls_sanitize_ip($ip ) {
|
||||
return preg_replace( '/[^0-9a-fA-F:., ]/', '', $ip );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure a date is m(m)/d(d)/yyyy, return false otherwise
|
||||
*
|
||||
* @param string $date Date to check
|
||||
* @return false|mixed Date in format m(m)/d(d)/yyyy or false if invalid
|
||||
*/
|
||||
function yourls_sanitize_date($date ) {
|
||||
if( !preg_match( '!^\d{1,2}/\d{1,2}/\d{4}$!' , $date ) ) {
|
||||
return false;
|
||||
}
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a date for SQL search. Return false if malformed input.
|
||||
*
|
||||
* @param string $date Date
|
||||
* @return false|string String in Y-m-d format for SQL search or false if malformed input
|
||||
*/
|
||||
function yourls_sanitize_date_for_sql($date) {
|
||||
if( !yourls_sanitize_date( $date ) )
|
||||
return false;
|
||||
return date( 'Y-m-d', strtotime( $date ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return trimmed string, optionally append '[...]' if string is too long
|
||||
*
|
||||
* @param string $string String to trim
|
||||
* @param int $length Maximum length of string
|
||||
* @param string $append String to append if trimmed
|
||||
* @return string Trimmed string
|
||||
*/
|
||||
function yourls_trim_long_string($string, $length = 60, $append = '[...]') {
|
||||
$newstring = $string;
|
||||
if ( mb_strlen( $newstring ) > $length ) {
|
||||
$newstring = mb_substr( $newstring, 0, $length - mb_strlen( $append ), 'UTF-8' ) . $append;
|
||||
}
|
||||
return yourls_apply_filter( 'trim_long_string', $newstring, $string, $length, $append );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a version number (1.4.1-whatever-RC1 -> 1.4.1)
|
||||
*
|
||||
* The regexp searches for the first digits, then a period, then more digits and periods, and discards
|
||||
* all the rest.
|
||||
* For instance, 'mysql-5.5-beta' and '5.5-RC1' return '5.5'
|
||||
*
|
||||
* @since 1.4.1
|
||||
* @param string $version Version number
|
||||
* @return string Sanitized version number
|
||||
*/
|
||||
function yourls_sanitize_version( $version ) {
|
||||
preg_match( '/([0-9]+\.[0-9.]+).*$/', $version, $matches );
|
||||
$version = isset($matches[1]) ? trim($matches[1], '.') : '';
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a filename (no Win32 stuff)
|
||||
*
|
||||
* @param string $file File name
|
||||
* @return string|null Sanitized file name (or null if it's just backslashes, ok...)
|
||||
*/
|
||||
function yourls_sanitize_filename($file) {
|
||||
$file = str_replace( '\\', '/', $file ); // sanitize for Win32 installs
|
||||
$file = preg_replace( '|/+|' ,'/', $file ); // remove any duplicate slash
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string seems to be UTF-8. Stolen from WP.
|
||||
*
|
||||
* @param string $str String to check
|
||||
* @return bool Whether string seems valid UTF-8
|
||||
*/
|
||||
function yourls_seems_utf8($str) {
|
||||
$length = strlen( $str );
|
||||
for ( $i=0; $i < $length; $i++ ) {
|
||||
$c = ord( $str[ $i ] );
|
||||
if ( $c < 0x80 ) $n = 0; # 0bbbbbbb
|
||||
elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
|
||||
elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
|
||||
elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
|
||||
elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
|
||||
elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
|
||||
else return false; # Does not match any model
|
||||
for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
|
||||
if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check for PCRE /u modifier support. Stolen from WP.
|
||||
*
|
||||
* Just in case "PCRE is not compiled with PCRE_UTF8" which seems to happen
|
||||
* on some distros
|
||||
*
|
||||
* @since 1.7.1
|
||||
*
|
||||
* @return bool whether there's /u support or not
|
||||
*/
|
||||
function yourls_supports_pcre_u() {
|
||||
static $utf8_pcre;
|
||||
if( !isset( $utf8_pcre ) ) {
|
||||
$utf8_pcre = (bool) @preg_match( '/^./u', 'a' );
|
||||
}
|
||||
return $utf8_pcre;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for invalid UTF8 in a string. Stolen from WP
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $string The text which is to be checked.
|
||||
* @param boolean $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false.
|
||||
* @return string The checked text.
|
||||
*/
|
||||
function yourls_check_invalid_utf8( $string, $strip = false ) {
|
||||
$string = (string) $string;
|
||||
|
||||
if ( 0 === strlen( $string ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// We can't demand utf8 in the PCRE installation, so just return the string in those cases
|
||||
if ( ! yourls_supports_pcre_u() ) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
// preg_match fails when it encounters invalid UTF8 in $string
|
||||
if ( 1 === @preg_match( '/^./us', $string ) ) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
// Attempt to strip the bad chars if requested (not recommended)
|
||||
if ( $strip && function_exists( 'iconv' ) ) {
|
||||
return iconv( 'utf-8', 'utf-8', $string );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number of special characters into their HTML entities. Stolen from WP.
|
||||
*
|
||||
* Specifically deals with: &, <, >, ", and '.
|
||||
*
|
||||
* $quote_style can be set to ENT_COMPAT to encode " to
|
||||
* ", or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $string The text which is to be encoded.
|
||||
* @param mixed $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES.
|
||||
* @param boolean $double_encode Optional. Whether to encode existing html entities. Default is false.
|
||||
* @return string The encoded text with HTML entities.
|
||||
*/
|
||||
function yourls_specialchars( $string, $quote_style = ENT_NOQUOTES, $double_encode = false ) {
|
||||
$string = (string) $string;
|
||||
|
||||
if ( 0 === strlen( $string ) )
|
||||
return '';
|
||||
|
||||
// Don't bother if there are no specialchars - saves some processing
|
||||
if ( ! preg_match( '/[&<>"\']/', $string ) )
|
||||
return $string;
|
||||
|
||||
// Account for the previous behaviour of the function when the $quote_style is not an accepted value
|
||||
if ( empty( $quote_style ) )
|
||||
$quote_style = ENT_NOQUOTES;
|
||||
elseif ( ! in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) )
|
||||
$quote_style = ENT_QUOTES;
|
||||
|
||||
$charset = 'UTF-8';
|
||||
|
||||
$_quote_style = $quote_style;
|
||||
|
||||
if ( $quote_style === 'double' ) {
|
||||
$quote_style = ENT_COMPAT;
|
||||
$_quote_style = ENT_COMPAT;
|
||||
} elseif ( $quote_style === 'single' ) {
|
||||
$quote_style = ENT_NOQUOTES;
|
||||
}
|
||||
|
||||
// Handle double encoding ourselves
|
||||
if ( $double_encode ) {
|
||||
$string = @htmlspecialchars( $string, $quote_style, $charset );
|
||||
} else {
|
||||
// Decode & into &
|
||||
$string = yourls_specialchars_decode( $string, $_quote_style );
|
||||
|
||||
// Guarantee every &entity; is valid or re-encode the &
|
||||
$string = yourls_kses_normalize_entities( $string );
|
||||
|
||||
// Now re-encode everything except &entity;
|
||||
$string = preg_split( '/(&#?x?[0-9a-z]+;)/i', $string, -1, PREG_SPLIT_DELIM_CAPTURE );
|
||||
|
||||
for ( $i = 0; $i < count( $string ); $i += 2 )
|
||||
$string[$i] = @htmlspecialchars( $string[$i], $quote_style, $charset );
|
||||
|
||||
$string = implode( '', $string );
|
||||
}
|
||||
|
||||
// Backwards compatibility
|
||||
if ( 'single' === $_quote_style )
|
||||
$string = str_replace( "'", ''', $string );
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number of HTML entities into their special characters. Stolen from WP.
|
||||
*
|
||||
* Specifically deals with: &, <, >, ", and '.
|
||||
*
|
||||
* $quote_style can be set to ENT_COMPAT to decode " entities,
|
||||
* or ENT_QUOTES to do both " and '. Default is ENT_NOQUOTES where no quotes are decoded.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $string The text which is to be decoded.
|
||||
* @param mixed $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old _wp_specialchars() values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES.
|
||||
* @return string The decoded text without HTML entities.
|
||||
*/
|
||||
function yourls_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) {
|
||||
$string = (string) $string;
|
||||
|
||||
if ( 0 === strlen( $string ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Don't bother if there are no entities - saves a lot of processing
|
||||
if ( strpos( $string, '&' ) === false ) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
// Match the previous behaviour of _wp_specialchars() when the $quote_style is not an accepted value
|
||||
if ( empty( $quote_style ) ) {
|
||||
$quote_style = ENT_NOQUOTES;
|
||||
} elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) {
|
||||
$quote_style = ENT_QUOTES;
|
||||
}
|
||||
|
||||
// More complete than get_html_translation_table( HTML_SPECIALCHARS )
|
||||
$single = array( ''' => '\'', ''' => '\'' );
|
||||
$single_preg = array( '/�*39;/' => ''', '/�*27;/i' => ''' );
|
||||
$double = array( '"' => '"', '"' => '"', '"' => '"' );
|
||||
$double_preg = array( '/�*34;/' => '"', '/�*22;/i' => '"' );
|
||||
$others = array( '<' => '<', '<' => '<', '>' => '>', '>' => '>', '&' => '&', '&' => '&', '&' => '&' );
|
||||
$others_preg = array( '/�*60;/' => '<', '/�*62;/' => '>', '/�*38;/' => '&', '/�*26;/i' => '&' );
|
||||
|
||||
$translation = $translation_preg = [];
|
||||
|
||||
if ( $quote_style === ENT_QUOTES ) {
|
||||
$translation = array_merge( $single, $double, $others );
|
||||
$translation_preg = array_merge( $single_preg, $double_preg, $others_preg );
|
||||
} elseif ( $quote_style === ENT_COMPAT || $quote_style === 'double' ) {
|
||||
$translation = array_merge( $double, $others );
|
||||
$translation_preg = array_merge( $double_preg, $others_preg );
|
||||
} elseif ( $quote_style === 'single' ) {
|
||||
$translation = array_merge( $single, $others );
|
||||
$translation_preg = array_merge( $single_preg, $others_preg );
|
||||
} elseif ( $quote_style === ENT_NOQUOTES ) {
|
||||
$translation = $others;
|
||||
$translation_preg = $others_preg;
|
||||
}
|
||||
|
||||
// Remove zero padding on numeric entities
|
||||
$string = preg_replace( array_keys( $translation_preg ), array_values( $translation_preg ), $string );
|
||||
|
||||
// Replace characters according to translation table
|
||||
return strtr( $string, $translation );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escaping for HTML blocks. Stolen from WP
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
function yourls_esc_html( $text ) {
|
||||
$safe_text = yourls_check_invalid_utf8( $text );
|
||||
$safe_text = yourls_specialchars( $safe_text, ENT_QUOTES );
|
||||
return yourls_apply_filter( 'esc_html', $safe_text, $text );
|
||||
}
|
||||
|
||||
/**
|
||||
* Escaping for HTML attributes. Stolen from WP
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
function yourls_esc_attr( $text ) {
|
||||
$safe_text = yourls_check_invalid_utf8( $text );
|
||||
$safe_text = yourls_specialchars( $safe_text, ENT_QUOTES );
|
||||
return yourls_apply_filter( 'esc_attr', $safe_text, $text );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and cleans a URL before printing it. Stolen from WP.
|
||||
*
|
||||
* A number of characters are removed from the URL. If the URL is for displaying
|
||||
* (the default behaviour) ampersands are also replaced.
|
||||
*
|
||||
* This function by default "escapes" URL for display purpose (param $context = 'display') but can
|
||||
* take extra steps in URL sanitization. See yourls_sanitize_url() and yourls_sanitize_url_safe()
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $url The URL to be cleaned.
|
||||
* @param string $context 'display' or something else. Use yourls_sanitize_url() for database or redirection usage.
|
||||
* @param array $protocols Optional. Array of allowed protocols, defaults to global $yourls_allowedprotocols
|
||||
* @return string The cleaned $url
|
||||
*/
|
||||
function yourls_esc_url( $url, $context = 'display', $protocols = array() ) {
|
||||
// trim first -- see #1931
|
||||
$url = trim( $url );
|
||||
|
||||
// make sure there's only one 'http://' at the beginning (prevents pasting a URL right after the default 'http://')
|
||||
$url = str_replace(
|
||||
array( 'http://http://', 'http://https://' ),
|
||||
array( 'http://', 'https://' ),
|
||||
$url
|
||||
);
|
||||
|
||||
if ( '' == $url )
|
||||
return $url;
|
||||
|
||||
$original_url = $url;
|
||||
|
||||
// force scheme and domain to lowercase - see issues 591 and 1630
|
||||
$url = yourls_normalize_uri( $url );
|
||||
|
||||
$url = preg_replace( '|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\\x80-\\xff]|i', '', $url );
|
||||
// Previous regexp in YOURLS was '|[^a-z0-9-~+_.?\[\]\^#=!&;,/:%@$\|*`\'<>"()\\x80-\\xff\{\}]|i'
|
||||
// TODO: check if that was it too destructive
|
||||
|
||||
// If $context is 'safe', an extra step is taken to make sure no CRLF injection is possible.
|
||||
// To be used when $url can be forged by evil user (eg it's from a $_SERVER variable, a query string, etc..)
|
||||
if ( 'safe' == $context ) {
|
||||
$strip = array( '%0d', '%0a', '%0D', '%0A' );
|
||||
$url = yourls_deep_replace( $strip, $url );
|
||||
}
|
||||
|
||||
// Replace ampersands and single quotes only when displaying.
|
||||
if ( 'display' == $context ) {
|
||||
$url = yourls_kses_normalize_entities( $url );
|
||||
$url = str_replace( '&', '&', $url );
|
||||
$url = str_replace( "'", ''', $url );
|
||||
}
|
||||
|
||||
// If there's a protocol, make sure it's OK
|
||||
if( yourls_get_protocol($url) !== '' ) {
|
||||
if ( ! is_array( $protocols ) or ! $protocols ) {
|
||||
global $yourls_allowedprotocols;
|
||||
$protocols = yourls_apply_filter( 'esc_url_protocols', $yourls_allowedprotocols );
|
||||
// Note: $yourls_allowedprotocols is also globally filterable in functions-kses.php/yourls_kses_init()
|
||||
}
|
||||
|
||||
if ( !yourls_is_allowed_protocol( $url, $protocols ) )
|
||||
return '';
|
||||
|
||||
// I didn't use KSES function kses_bad_protocol() because it doesn't work the way I liked (returns //blah from illegal://blah)
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'esc_url', $url, $original_url, $context );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Normalize a URI : lowercase scheme and domain, convert IDN to UTF8
|
||||
*
|
||||
* All in one example: 'HTTP://XN--mgbuq0c.Com/AbCd' -> 'http://طارق.com/AbCd'
|
||||
* See issues 591, 1630, 1889, 2691
|
||||
*
|
||||
* This function is trickier than what seems to be needed at first
|
||||
*
|
||||
* First, we need to handle several URI types: http://example.com, mailto:ozh@ozh.ozh, facetime:user@example.com, and so on, see
|
||||
* yourls_kses_allowed_protocols() in functions-kses.php
|
||||
* The general rule is that the scheme ("stuff://" or "stuff:") is case insensitive and should be lowercase. But then, depending on the
|
||||
* scheme, parts of what follows the scheme may or may not be case sensitive.
|
||||
*
|
||||
* Second, simply using parse_url() and its opposite http_build_url() is a pretty unsafe process:
|
||||
* - parse_url() can easily trip up on malformed or weird URLs
|
||||
* - exploding a URL with parse_url(), lowercasing some stuff, and glueing things back with http_build_url() does not handle well
|
||||
* "stuff:"-like URI [1] and can result in URLs ending modified [2][3]. We don't want to *validate* URI, we just want to lowercase
|
||||
* what is supposed to be lowercased.
|
||||
*
|
||||
* So, to be conservative, this function:
|
||||
* - lowercases the scheme
|
||||
* - does not lowercase anything else on "stuff:" URI
|
||||
* - tries to lowercase only scheme and domain of "stuff://" URI
|
||||
*
|
||||
* [1] http_build_url(parse_url("mailto:ozh")) == "mailto:///ozh"
|
||||
* [2] http_build_url(parse_url("http://blah#omg")) == "http://blah/#omg"
|
||||
* [3] http_build_url(parse_url("http://blah?#")) == "http://blah/"
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @param string $url URL
|
||||
* @return string URL with lowercase scheme and protocol
|
||||
*/
|
||||
function yourls_normalize_uri( $url ) {
|
||||
$scheme = yourls_get_protocol( $url );
|
||||
|
||||
if ('' == $scheme) {
|
||||
// Scheme not found, malformed URL? Something else? Not sure.
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Case 1 : scheme like "stuff:", as opposed to "stuff://"
|
||||
* Examples: "mailto:joe@joe.com" or "bitcoin:15p1o8vnWqNkJBJGgwafNgR1GCCd6EGtQR?amount=1&label=Ozh"
|
||||
* In this case, we only lowercase the scheme, because depending on it, things after should or should not be lowercased
|
||||
*/
|
||||
if (substr($scheme, -2, 2) != '//') {
|
||||
$url = str_replace( $scheme, strtolower( $scheme ), $url );
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Case 2 : scheme like "stuff://" (eg "http://example.com/" or "ssh://joe@joe.com")
|
||||
* Here we lowercase the scheme and domain parts
|
||||
*/
|
||||
$parts = parse_url($url);
|
||||
|
||||
// Most likely malformed stuff, could not parse : we'll just lowercase the scheme and leave the rest untouched
|
||||
if (false == $parts) {
|
||||
$url = str_replace( $scheme, strtolower( $scheme ), $url );
|
||||
return $url;
|
||||
}
|
||||
|
||||
// URL seems parsable, let's do the best we can
|
||||
$lower = array();
|
||||
$lower['scheme'] = strtolower( $parts['scheme'] );
|
||||
if( isset( $parts['host'] ) ) {
|
||||
// Convert domain to lowercase, with mb_ to preserve UTF8
|
||||
$lower['host'] = mb_strtolower($parts['host']);
|
||||
/**
|
||||
* Convert IDN domains to their UTF8 form so that طارق.net and xn--mgbuq0c.net
|
||||
* are considered the same. Explicitely mention option and variant to avoid notice
|
||||
* on PHP 7.2 and 7.3
|
||||
*/
|
||||
$lower['host'] = idn_to_utf8($lower['host'], IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46);
|
||||
}
|
||||
|
||||
$url = http_build_url($url, $lower);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escape single quotes, htmlspecialchar " < > &, and fix line endings. Stolen from WP.
|
||||
*
|
||||
* Escapes text strings for echoing in JS. It is intended to be used for inline JS
|
||||
* (in a tag attribute, for example onclick="..."). Note that the strings have to
|
||||
* be in single quotes. The filter 'js_escape' is also applied here.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $text The text to be escaped.
|
||||
* @return string Escaped text.
|
||||
*/
|
||||
function yourls_esc_js( $text ) {
|
||||
$safe_text = yourls_check_invalid_utf8( $text );
|
||||
$safe_text = yourls_specialchars( $safe_text, ENT_COMPAT );
|
||||
$safe_text = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", stripslashes( $safe_text ) );
|
||||
$safe_text = str_replace( "\r", '', $safe_text );
|
||||
$safe_text = str_replace( "\n", '\\n', addslashes( $safe_text ) );
|
||||
return yourls_apply_filter( 'esc_js', $safe_text, $text );
|
||||
}
|
||||
|
||||
/**
|
||||
* Escaping for textarea values. Stolen from WP.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
function yourls_esc_textarea( $text ) {
|
||||
$safe_text = htmlspecialchars( $text, ENT_QUOTES );
|
||||
return yourls_apply_filter( 'esc_textarea', $safe_text, $text );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds backslashes before letters and before a number at the start of a string. Stolen from WP.
|
||||
*
|
||||
* @since 1.6
|
||||
* @param string $string Value to which backslashes will be added.
|
||||
* @return string String with backslashes inserted.
|
||||
*/
|
||||
function yourls_backslashit($string) {
|
||||
$string = preg_replace('/^([0-9])/', '\\\\\\\\\1', (string)$string);
|
||||
$string = preg_replace('/([a-z])/i', '\\\\\1', (string)$string);
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string seems to be urlencoded
|
||||
*
|
||||
* We use rawurlencode instead of urlencode to avoid messing with '+'
|
||||
*
|
||||
* @since 1.7
|
||||
* @param string $string
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_is_rawurlencoded( $string ) {
|
||||
return rawurldecode( $string ) != $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* rawurldecode a string till it's not encoded anymore
|
||||
*
|
||||
* Deals with multiple encoding (eg "%2521" => "%21" => "!").
|
||||
* See https://github.com/YOURLS/YOURLS/issues/1303
|
||||
*
|
||||
* @since 1.7
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
function yourls_rawurldecode_while_encoded( $string ) {
|
||||
$string = rawurldecode( $string );
|
||||
if( yourls_is_rawurlencoded( $string ) ) {
|
||||
$string = yourls_rawurldecode_while_encoded( $string );
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts readable Javascript code into a valid bookmarklet link
|
||||
*
|
||||
* Uses https://github.com/ozh/bookmarkletgen
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @param string $code Javascript code
|
||||
* @return string Bookmarklet link
|
||||
*/
|
||||
function yourls_make_bookmarklet( $code ) {
|
||||
$book = new \Ozh\Bookmarkletgen\Bookmarkletgen;
|
||||
return $book->crunch( $code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a timestamp, plus or minus the time offset if defined
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param string|int $timestamp a timestamp
|
||||
* @return int a timestamp, plus or minus offset if defined
|
||||
*/
|
||||
function yourls_get_timestamp( $timestamp ) {
|
||||
$offset = yourls_get_time_offset();
|
||||
$timestamp_offset = (int)$timestamp + ($offset * 3600);
|
||||
|
||||
return yourls_apply_filter( 'get_timestamp', $timestamp_offset, $timestamp, $offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time offset, as defined in config, filtered
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @return int Time offset
|
||||
*/
|
||||
function yourls_get_time_offset() {
|
||||
$offset = defined('YOURLS_HOURS_OFFSET') ? (int)YOURLS_HOURS_OFFSET : 0;
|
||||
return yourls_apply_filter( 'get_time_offset', $offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a date() format for a full date + time, filtered
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param string $format Date format string
|
||||
* @return string Date format string
|
||||
*/
|
||||
function yourls_get_datetime_format( $format ) {
|
||||
return yourls_apply_filter( 'get_datetime_format', (string)$format );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a date() format for date (no time), filtered
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param string $format Date format string
|
||||
* @return string Date format string
|
||||
*/
|
||||
function yourls_get_date_format( $format ) {
|
||||
return yourls_apply_filter( 'get_date_format', (string)$format );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a date() format for a time (no date), filtered
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param string $format Date format string
|
||||
* @return string Date format string
|
||||
*/
|
||||
function yourls_get_time_format( $format ) {
|
||||
return yourls_apply_filter( 'get_time_format', (string)$format );
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
/**
|
||||
* Function relative to the geolocation functions (ip <-> country <-> flags), currently
|
||||
* tied to Maxmind's GeoIP but this should evolve to become more pluggable
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 $default 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 ( yourls_apply_filter( 'geo_use_cloudflare', true )
|
||||
&& !empty( $_SERVER[ 'HTTP_CF_IPCOUNTRY' ] ) && preg_match( '/^[A-Z]{2}$/', $_SERVER[ 'HTTP_CF_IPCOUNTRY' ] ) ) {
|
||||
return $_SERVER[ 'HTTP_CF_IPCOUNTRY' ];
|
||||
}
|
||||
|
||||
if ( empty( $ip ) ) {
|
||||
$ip = yourls_get_IP();
|
||||
}
|
||||
|
||||
// Allow plugins to stick to YOURLS internals but provide another DB
|
||||
$db = yourls_apply_filter( 'geo_ip_path_to_db', YOURLS_INC.'/geo/GeoLite2-Country.mmdb' );
|
||||
if ( !is_readable( $db ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
try {
|
||||
$reader = new \GeoIp2\Database\Reader( $db );
|
||||
$record = $reader->country( $ip );
|
||||
$location = $record->country->isoCode; // eg 'US'
|
||||
}
|
||||
catch ( \Exception $e ) {
|
||||
/*
|
||||
Unused for now, Exception and $e->getMessage() can be one of :
|
||||
|
||||
- Exception: \GeoIp2\Exception\AddressNotFoundException
|
||||
When: valid IP not found
|
||||
Error message: "The address 10.0.0.30 is not in the database"
|
||||
|
||||
- Exception: \InvalidArgumentException
|
||||
When: IP is not valid, or DB not readable
|
||||
Error message: "The value "10.0.0.300" is not a valid IP address", "The file "/path/to/GeoLite2-Country.mmdb" does not exist or is not readable"
|
||||
|
||||
- Exception: \MaxMind\Db\Reader\InvalidDatabaseException
|
||||
When: DB is readable but is corrupt or invalid
|
||||
Error message: "The MaxMind DB file's search tree is corrupt"
|
||||
|
||||
- or obviously \Exception for any other error (?)
|
||||
*/
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'geo_ip_to_countrycode', empty( $location ) ? $default : $location, $ip, $default );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 2 letter country code to long name (ie AU -> Australia)
|
||||
*
|
||||
* This associative array is the one used by MaxMind internal functions, it may differ from other lists (eg "A1" does
|
||||
* not universally stand for "Anon proxy")
|
||||
*
|
||||
* @since 1.4
|
||||
* @param string $code 2 letter country code, eg 'FR'
|
||||
* @return string Country long name (eg 'France') or an empty string if not found
|
||||
*/
|
||||
function yourls_geo_countrycode_to_countryname( $code ) {
|
||||
// Allow plugins to short-circuit the function
|
||||
$country = yourls_apply_filter( 'shunt_geo_countrycode_to_countryname', false, $code );
|
||||
if ( false !== $country ) {
|
||||
return $country;
|
||||
}
|
||||
|
||||
// Weeeeeeeeeeee
|
||||
$countries = [
|
||||
'A1' => 'Anonymous Proxy', 'A2' => 'Satellite Provider', 'AD' => 'Andorra', 'AE' => 'United Arab Emirates', 'AF' => 'Afghanistan',
|
||||
'AG' => 'Antigua and Barbuda', 'AI' => 'Anguilla', 'AL' => 'Albania', 'AM' => 'Armenia', 'AO' => 'Angola',
|
||||
'AP' => 'Asia/Pacific Region', 'AQ' => 'Antarctica', 'AR' => 'Argentina', 'AS' => 'American Samoa', 'AT' => 'Austria',
|
||||
'AU' => 'Australia', 'AW' => 'Aruba', 'AX' => 'Aland Islands', 'AZ' => 'Azerbaijan', 'BA' => 'Bosnia and Herzegovina',
|
||||
'BB' => 'Barbados', 'BD' => 'Bangladesh', 'BE' => 'Belgium', 'BF' => 'Burkina Faso', 'BG' => 'Bulgaria',
|
||||
'BH' => 'Bahrain', 'BI' => 'Burundi', 'BJ' => 'Benin', 'BL' => 'Saint Barthelemy', 'BM' => 'Bermuda',
|
||||
'BN' => 'Brunei Darussalam', 'BO' => 'Bolivia', 'BQ' => 'Bonaire, Saint Eustatius and Saba', 'BR' => 'Brazil', 'BS' => 'Bahamas',
|
||||
'BT' => 'Bhutan', 'BV' => 'Bouvet Island', 'BW' => 'Botswana', 'BY' => 'Belarus', 'BZ' => 'Belize',
|
||||
'CA' => 'Canada', 'CC' => 'Cocos (Keeling) Islands', 'CD' => 'Congo, The Democratic Republic of the', 'CF' => 'Central African Republic', 'CG' => 'Congo',
|
||||
'CH' => 'Switzerland', 'CI' => 'Cote D\'Ivoire', 'CK' => 'Cook Islands', 'CL' => 'Chile', 'CM' => 'Cameroon',
|
||||
'CN' => 'China', 'CO' => 'Colombia', 'CR' => 'Costa Rica', 'CU' => 'Cuba', 'CV' => 'Cape Verde',
|
||||
'CW' => 'Curacao', 'CX' => 'Christmas Island', 'CY' => 'Cyprus', 'CZ' => 'Czech Republic', 'DE' => 'Germany',
|
||||
'DJ' => 'Djibouti', 'DK' => 'Denmark', 'DM' => 'Dominica', 'DO' => 'Dominican Republic', 'DZ' => 'Algeria',
|
||||
'EC' => 'Ecuador', 'EE' => 'Estonia', 'EG' => 'Egypt', 'EH' => 'Western Sahara', 'ER' => 'Eritrea',
|
||||
'ES' => 'Spain', 'ET' => 'Ethiopia', 'EU' => 'Europe', 'FI' => 'Finland', 'FJ' => 'Fiji',
|
||||
'FK' => 'Falkland Islands (Malvinas)', 'FM' => 'Micronesia, Federated States of', 'FO' => 'Faroe Islands', 'FR' => 'France', 'GA' => 'Gabon',
|
||||
'GB' => 'United Kingdom', 'GD' => 'Grenada', 'GE' => 'Georgia', 'GF' => 'French Guiana', 'GG' => 'Guernsey',
|
||||
'GH' => 'Ghana', 'GI' => 'Gibraltar', 'GL' => 'Greenland', 'GM' => 'Gambia', 'GN' => 'Guinea',
|
||||
'GP' => 'Guadeloupe', 'GQ' => 'Equatorial Guinea', 'GR' => 'Greece', 'GS' => 'South Georgia and the South Sandwich Islands', 'GT' => 'Guatemala',
|
||||
'GU' => 'Guam', 'GW' => 'Guinea-Bissau', 'GY' => 'Guyana', 'HK' => 'Hong Kong', 'HM' => 'Heard Island and McDonald Islands',
|
||||
'HN' => 'Honduras', 'HR' => 'Croatia', 'HT' => 'Haiti', 'HU' => 'Hungary', 'ID' => 'Indonesia',
|
||||
'IE' => 'Ireland', 'IL' => 'Israel', 'IM' => 'Isle of Man', 'IN' => 'India', 'IO' => 'British Indian Ocean Territory',
|
||||
'IQ' => 'Iraq', 'IR' => 'Iran, Islamic Republic of', 'IS' => 'Iceland', 'IT' => 'Italy', 'JE' => 'Jersey',
|
||||
'JM' => 'Jamaica', 'JO' => 'Jordan', 'JP' => 'Japan', 'KE' => 'Kenya', 'KG' => 'Kyrgyzstan',
|
||||
'KH' => 'Cambodia', 'KI' => 'Kiribati', 'KM' => 'Comoros', 'KN' => 'Saint Kitts and Nevis', 'KP' => 'Korea, Democratic People\'s Republic of',
|
||||
'KR' => 'Korea, Republic of', 'KW' => 'Kuwait', 'KY' => 'Cayman Islands', 'KZ' => 'Kazakhstan', 'LA' => 'Lao People\'s Democratic Republic',
|
||||
'LB' => 'Lebanon', 'LC' => 'Saint Lucia', 'LI' => 'Liechtenstein', 'LK' => 'Sri Lanka', 'LR' => 'Liberia',
|
||||
'LS' => 'Lesotho', 'LT' => 'Lithuania', 'LU' => 'Luxembourg', 'LV' => 'Latvia', 'LY' => 'Libya',
|
||||
'MA' => 'Morocco', 'MC' => 'Monaco', 'MD' => 'Moldova, Republic of', 'ME' => 'Montenegro', 'MF' => 'Saint Martin',
|
||||
'MG' => 'Madagascar', 'MH' => 'Marshall Islands', 'MK' => 'Macedonia', 'ML' => 'Mali', 'MM' => 'Myanmar',
|
||||
'MN' => 'Mongolia', 'MO' => 'Macau', 'MP' => 'Northern Mariana Islands', 'MQ' => 'Martinique', 'MR' => 'Mauritania',
|
||||
'MS' => 'Montserrat', 'MT' => 'Malta', 'MU' => 'Mauritius', 'MV' => 'Maldives', 'MW' => 'Malawi',
|
||||
'MX' => 'Mexico', 'MY' => 'Malaysia', 'MZ' => 'Mozambique', 'NA' => 'Namibia', 'NC' => 'New Caledonia',
|
||||
'NE' => 'Niger', 'NF' => 'Norfolk Island', 'NG' => 'Nigeria', 'NI' => 'Nicaragua', 'NL' => 'Netherlands',
|
||||
'NO' => 'Norway', 'NP' => 'Nepal', 'NR' => 'Nauru', 'NU' => 'Niue', 'NZ' => 'New Zealand',
|
||||
'O1' => 'Other', 'OM' => 'Oman', 'PA' => 'Panama', 'PE' => 'Peru', 'PF' => 'French Polynesia',
|
||||
'PG' => 'Papua New Guinea', 'PH' => 'Philippines', 'PK' => 'Pakistan', 'PL' => 'Poland', 'PM' => 'Saint Pierre and Miquelon',
|
||||
'PN' => 'Pitcairn Islands', 'PR' => 'Puerto Rico', 'PS' => 'Palestinian Territory', 'PT' => 'Portugal', 'PW' => 'Palau',
|
||||
'PY' => 'Paraguay', 'QA' => 'Qatar', 'RE' => 'Reunion', 'RO' => 'Romania', 'RS' => 'Serbia',
|
||||
'RU' => 'Russian Federation', 'RW' => 'Rwanda', 'SA' => 'Saudi Arabia', 'SB' => 'Solomon Islands', 'SC' => 'Seychelles',
|
||||
'SD' => 'Sudan', 'SE' => 'Sweden', 'SG' => 'Singapore', 'SH' => 'Saint Helena', 'SI' => 'Slovenia',
|
||||
'SJ' => 'Svalbard and Jan Mayen', 'SK' => 'Slovakia', 'SL' => 'Sierra Leone', 'SM' => 'San Marino', 'SN' => 'Senegal',
|
||||
'SO' => 'Somalia', 'SR' => 'Suriname', 'SS' => 'South Sudan', 'ST' => 'Sao Tome and Principe', 'SV' => 'El Salvador',
|
||||
'SX' => 'Sint Maarten (Dutch part)', 'SY' => 'Syrian Arab Republic', 'SZ' => 'Swaziland', 'TC' => 'Turks and Caicos Islands', 'TD' => 'Chad',
|
||||
'TF' => 'French Southern Territories', 'TG' => 'Togo', 'TH' => 'Thailand', 'TJ' => 'Tajikistan', 'TK' => 'Tokelau',
|
||||
'TL' => 'Timor-Leste', 'TM' => 'Turkmenistan', 'TN' => 'Tunisia', 'TO' => 'Tonga', 'TR' => 'Turkey',
|
||||
'TT' => 'Trinidad and Tobago', 'TV' => 'Tuvalu', 'TW' => 'Taiwan', 'TZ' => 'Tanzania, United Republic of', 'UA' => 'Ukraine',
|
||||
'UG' => 'Uganda', 'UM' => 'United States Minor Outlying Islands', 'US' => 'United States', 'UY' => 'Uruguay', 'UZ' => 'Uzbekistan',
|
||||
'VA' => 'Holy See (Vatican City State)', 'VC' => 'Saint Vincent and the Grenadines', 'VE' => 'Venezuela', 'VG' => 'Virgin Islands, British', 'VI' => 'Virgin Islands, U.S.',
|
||||
'VN' => 'Vietnam', 'VU' => 'Vanuatu', 'WF' => 'Wallis and Futuna', 'WS' => 'Samoa', 'YE' => 'Yemen',
|
||||
'YT' => 'Mayotte', 'ZA' => 'South Africa', 'ZM' => 'Zambia', 'ZW' => 'Zimbabwe',
|
||||
];
|
||||
|
||||
$code = strtoupper( $code );
|
||||
|
||||
return yourls_apply_filter( 'geo_countrycode_to_countryname', isset( $countries[ $code ] ) ? $countries[ $code ] : '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return flag URL from 2 letter country code
|
||||
* @param string $code
|
||||
* @return string
|
||||
*/
|
||||
function yourls_geo_get_flag( $code ) {
|
||||
if ( !file_exists( YOURLS_INC.'/geo/flags/flag_'.strtolower( $code ).'.gif' ) ) {
|
||||
$code = '';
|
||||
}
|
||||
$img = yourls_match_current_protocol( yourls_get_yourls_site().'/includes/geo/flags/flag_'.strtolower( $code ).'.gif' );
|
||||
return (string)yourls_apply_filter( 'geo_get_flag', $img, $code );
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,528 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Functions that relate to HTTP requests
|
||||
*
|
||||
* On functions using the 3rd party library Requests:
|
||||
* Their goal here is to provide convenient wrapper functions to the Requests library. There are
|
||||
* 2 types of functions for each METHOD, where METHOD is 'get' or 'post' (implement more as needed)
|
||||
* - yourls_http_METHOD() :
|
||||
* Return a complete Response object (with ->body, ->headers, ->status_code, etc...) or
|
||||
* a simple string (error message)
|
||||
* - yourls_http_METHOD_body() :
|
||||
* Return a string (response body) or null if there was an error
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
|
||||
use WpOrg\Requests\Requests;
|
||||
|
||||
/**
|
||||
* Perform a GET request, return response object or error string message
|
||||
*
|
||||
* Notable object properties: body, headers, status_code
|
||||
*
|
||||
* @since 1.7
|
||||
* @see yourls_http_request
|
||||
* @param string $url URL to request
|
||||
* @param array $headers HTTP headers to send
|
||||
* @param array $data GET data
|
||||
* @param array $options Options to pass to Requests
|
||||
* @return mixed Response object, or error string
|
||||
*/
|
||||
function yourls_http_get( $url, $headers = array(), $data = array(), $options = array() ) {
|
||||
return yourls_http_request( 'GET', $url, $headers, $data, $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a GET request, return body or null if there was an error
|
||||
*
|
||||
* @since 1.7
|
||||
* @see yourls_http_request
|
||||
* @param string $url URL to request
|
||||
* @param array $headers HTTP headers to send
|
||||
* @param array $data GET data
|
||||
* @param array $options Options to pass to Requests
|
||||
* @return mixed String (page body) or null if error
|
||||
*/
|
||||
function yourls_http_get_body( $url, $headers = array(), $data = array(), $options = array() ) {
|
||||
$return = yourls_http_get( $url, $headers, $data, $options );
|
||||
return isset( $return->body ) ? $return->body : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a POST request, return response object
|
||||
*
|
||||
* Notable object properties: body, headers, status_code
|
||||
*
|
||||
* @since 1.7
|
||||
* @see yourls_http_request
|
||||
* @param string $url URL to request
|
||||
* @param array $headers HTTP headers to send
|
||||
* @param array $data POST data
|
||||
* @param array $options Options to pass to Requests
|
||||
* @return mixed Response object, or error string
|
||||
*/
|
||||
function yourls_http_post( $url, $headers = array(), $data = array(), $options = array() ) {
|
||||
return yourls_http_request( 'POST', $url, $headers, $data, $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a POST request, return body
|
||||
*
|
||||
* Wrapper for yourls_http_request()
|
||||
*
|
||||
* @since 1.7
|
||||
* @see yourls_http_request
|
||||
* @param string $url URL to request
|
||||
* @param array $headers HTTP headers to send
|
||||
* @param array $data POST data
|
||||
* @param array $options Options to pass to Requests
|
||||
* @return mixed String (page body) or null if error
|
||||
*/
|
||||
function yourls_http_post_body( $url, $headers = array(), $data = array(), $options = array() ) {
|
||||
$return = yourls_http_post( $url, $headers, $data, $options );
|
||||
return isset( $return->body ) ? $return->body : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get proxy information
|
||||
*
|
||||
* @uses YOURLS_PROXY YOURLS_PROXY_USERNAME YOURLS_PROXY_PASSWORD
|
||||
* @since 1.7.1
|
||||
* @return mixed false if no proxy is defined, or string like '10.0.0.201:3128' or array like ('10.0.0.201:3128', 'username', 'password')
|
||||
*/
|
||||
function yourls_http_get_proxy() {
|
||||
$proxy = false;
|
||||
|
||||
if( defined( 'YOURLS_PROXY' ) ) {
|
||||
$proxy = YOURLS_PROXY;
|
||||
if( defined( 'YOURLS_PROXY_USERNAME' ) && defined( 'YOURLS_PROXY_PASSWORD' ) ) {
|
||||
$proxy = array( YOURLS_PROXY, YOURLS_PROXY_USERNAME, YOURLS_PROXY_PASSWORD );
|
||||
}
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'http_get_proxy', $proxy );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of hosts that should bypass the proxy
|
||||
*
|
||||
* @uses YOURLS_PROXY_BYPASS_HOSTS
|
||||
* @since 1.7.1
|
||||
* @return mixed false if no host defined, or string like "example.com, *.mycorp.com"
|
||||
*/
|
||||
function yourls_http_get_proxy_bypass_host() {
|
||||
$hosts = defined( 'YOURLS_PROXY_BYPASS_HOSTS' ) ? YOURLS_PROXY_BYPASS_HOSTS : false;
|
||||
|
||||
return yourls_apply_filter( 'http_get_proxy_bypass_host', $hosts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Default HTTP requests options for YOURLS
|
||||
*
|
||||
* For a list of all available options, see function request() in /includes/Requests/Requests.php
|
||||
*
|
||||
* @since 1.7
|
||||
* @return array Options
|
||||
*/
|
||||
function yourls_http_default_options() {
|
||||
$options = array(
|
||||
'timeout' => yourls_apply_filter( 'http_default_options_timeout', 3 ),
|
||||
'useragent' => yourls_http_user_agent(),
|
||||
'follow_redirects' => true,
|
||||
'redirects' => 3,
|
||||
);
|
||||
|
||||
if( yourls_http_get_proxy() ) {
|
||||
$options['proxy'] = yourls_http_get_proxy();
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'http_default_options', $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether URL should be sent through the proxy server.
|
||||
*
|
||||
* Concept stolen from WordPress. The idea is to allow some URLs, including localhost and the YOURLS install itself,
|
||||
* to be requested directly and bypassing any defined proxy.
|
||||
*
|
||||
* @uses YOURLS_PROXY
|
||||
* @uses YOURLS_PROXY_BYPASS_HOSTS
|
||||
* @since 1.7
|
||||
* @param string $url URL to check
|
||||
* @return bool true to request through proxy, false to request directly
|
||||
*/
|
||||
function yourls_send_through_proxy( $url ) {
|
||||
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_send_through_proxy', null, $url );
|
||||
if ( null !== $pre )
|
||||
return $pre;
|
||||
|
||||
$check = @parse_url( $url );
|
||||
|
||||
if( !isset( $check['host'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Malformed URL, can not process, but this could mean ssl, so let through anyway.
|
||||
if ( $check === false )
|
||||
return true;
|
||||
|
||||
// Self and loopback URLs are considered local (':' is parse_url() host on '::1')
|
||||
$home = parse_url( yourls_get_yourls_site() );
|
||||
$local = array( 'localhost', '127.0.0.1', '127.1', '[::1]', ':', $home['host'] );
|
||||
|
||||
if( in_array( $check['host'], $local ) )
|
||||
return false;
|
||||
|
||||
$bypass = yourls_http_get_proxy_bypass_host();
|
||||
|
||||
if( $bypass === false OR $bypass === '' ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Build array of hosts to bypass
|
||||
static $bypass_hosts;
|
||||
static $wildcard_regex = false;
|
||||
if ( null == $bypass_hosts ) {
|
||||
$bypass_hosts = preg_split( '|\s*,\s*|', $bypass );
|
||||
|
||||
if ( false !== strpos( $bypass, '*' ) ) {
|
||||
$wildcard_regex = array();
|
||||
foreach ( $bypass_hosts as $host ) {
|
||||
$wildcard_regex[] = str_replace( '\*', '.+', preg_quote( $host, '/' ) );
|
||||
if ( false !== strpos( $host, '*' ) ) {
|
||||
$wildcard_regex[] = str_replace( '\*\.', '', preg_quote( $host, '/' ) );
|
||||
}
|
||||
}
|
||||
$wildcard_regex = '/^(' . implode( '|', $wildcard_regex ) . ')$/i';
|
||||
}
|
||||
}
|
||||
|
||||
if ( !empty( $wildcard_regex ) )
|
||||
return !preg_match( $wildcard_regex, $check['host'] );
|
||||
else
|
||||
return !in_array( $check['host'], $bypass_hosts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a HTTP request, return response object
|
||||
*
|
||||
* @since 1.7
|
||||
* @param string $type HTTP request type (GET, POST)
|
||||
* @param string $url URL to request
|
||||
* @param array $headers Extra headers to send with the request
|
||||
* @param array $data Data to send either as a query string for GET requests, or in the body for POST requests
|
||||
* @param array $options Options for the request (see /includes/Requests/Requests.php:request())
|
||||
* @return object WpOrg\Requests\Response object
|
||||
*/
|
||||
function yourls_http_request( $type, $url, $headers, $data, $options ) {
|
||||
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_yourls_http_request', null, $type, $url, $headers, $data, $options );
|
||||
if ( null !== $pre )
|
||||
return $pre;
|
||||
|
||||
$options = array_merge( yourls_http_default_options(), $options );
|
||||
|
||||
if( yourls_http_get_proxy() && !yourls_send_through_proxy( $url ) ) {
|
||||
unset( $options['proxy'] );
|
||||
}
|
||||
|
||||
// filter everything
|
||||
$type = yourls_apply_filter('http_request_type', $type);
|
||||
$url = yourls_apply_filter('http_request_url', $url);
|
||||
$headers = yourls_apply_filter('http_request_headers', $headers);
|
||||
$data = yourls_apply_filter('http_request_data', $data);
|
||||
$options = yourls_apply_filter('http_request_options', $options);
|
||||
|
||||
try {
|
||||
$result = Requests::request( $url, $headers, $data, $type, $options );
|
||||
} catch( \WpOrg\Requests\Exception $e ) {
|
||||
$result = yourls_debug_log( $e->getMessage() . ' (' . $type . ' on ' . $url . ')' );
|
||||
};
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return funky user agent string
|
||||
*
|
||||
* @since 1.5
|
||||
* @return string UA string
|
||||
*/
|
||||
function yourls_http_user_agent() {
|
||||
return yourls_apply_filter( 'http_user_agent', 'YOURLS v'.YOURLS_VERSION.' +http://yourls.org/ (running on '.yourls_get_yourls_site().')' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check api.yourls.org if there's a newer version of YOURLS
|
||||
*
|
||||
* This function collects various stats to help us improve YOURLS. See the blog post about it:
|
||||
* http://blog.yourls.org/2014/01/on-yourls-1-7-and-api-yourls-org/
|
||||
* Results of requests sent to api.yourls.org are stored in option 'core_version_checks' and is an object
|
||||
* with the following properties:
|
||||
* - failed_attempts : number of consecutive failed attempts
|
||||
* - last_attempt : time() of last attempt
|
||||
* - last_result : content retrieved from api.yourls.org during previous check
|
||||
* - version_checked : installed YOURLS version that was last checked
|
||||
*
|
||||
* @since 1.7
|
||||
* @return mixed JSON data if api.yourls.org successfully requested, false otherwise
|
||||
*/
|
||||
function yourls_check_core_version() {
|
||||
|
||||
global $yourls_user_passwords;
|
||||
|
||||
$checks = yourls_get_option( 'core_version_checks' );
|
||||
|
||||
// Invalidate check data when YOURLS version changes
|
||||
if ( is_object( $checks ) && YOURLS_VERSION != $checks->version_checked ) {
|
||||
$checks = false;
|
||||
}
|
||||
|
||||
if( !is_object( $checks ) ) {
|
||||
$checks = new stdClass;
|
||||
$checks->failed_attempts = 0;
|
||||
$checks->last_attempt = 0;
|
||||
$checks->last_result = '';
|
||||
$checks->version_checked = YOURLS_VERSION;
|
||||
}
|
||||
|
||||
// Total number of links and clicks
|
||||
list( $total_urls, $total_clicks ) = array_values(yourls_get_db_stats());
|
||||
|
||||
// The collection of stuff to report
|
||||
$stuff = array(
|
||||
// Globally uniquish site identifier
|
||||
// This uses const YOURLS_SITE and not yourls_get_yourls_site() to prevent creating another id for an already known install
|
||||
'md5' => md5( YOURLS_SITE . YOURLS_ABSPATH ),
|
||||
|
||||
// Install information
|
||||
'failed_attempts' => $checks->failed_attempts,
|
||||
'yourls_site' => defined( 'YOURLS_SITE' ) ? yourls_get_yourls_site() : 'unknown',
|
||||
'yourls_version' => defined( 'YOURLS_VERSION' ) ? YOURLS_VERSION : 'unknown',
|
||||
'php_version' => PHP_VERSION,
|
||||
'mysql_version' => yourls_get_db()->mysql_version(),
|
||||
'locale' => yourls_get_locale(),
|
||||
|
||||
// custom DB driver if any, and useful common PHP extensions
|
||||
'db_driver' => defined( 'YOURLS_DB_DRIVER' ) ? YOURLS_DB_DRIVER : 'unset',
|
||||
'db_ext_pdo' => extension_loaded( 'PDO' ) ? 1 : 0,
|
||||
'db_ext_mysql' => extension_loaded( 'mysql' ) ? 1 : 0,
|
||||
'db_ext_mysqli' => extension_loaded( 'mysqli' ) ? 1 : 0,
|
||||
'ext_curl' => extension_loaded( 'curl' ) ? 1 : 0,
|
||||
|
||||
// Config information
|
||||
'yourls_private' => defined( 'YOURLS_PRIVATE' ) && YOURLS_PRIVATE ? 1 : 0,
|
||||
'yourls_unique' => defined( 'YOURLS_UNIQUE_URLS' ) && YOURLS_UNIQUE_URLS ? 1 : 0,
|
||||
'yourls_url_convert' => defined( 'YOURLS_URL_CONVERT' ) ? YOURLS_URL_CONVERT : 'unknown',
|
||||
|
||||
// Usage information
|
||||
'num_users' => count( $yourls_user_passwords ),
|
||||
'num_active_plugins' => yourls_has_active_plugins(),
|
||||
'num_pages' => defined( 'YOURLS_PAGEDIR' ) ? count( (array) glob( YOURLS_PAGEDIR .'/*.php') ) : 0,
|
||||
'num_links' => $total_urls,
|
||||
'num_clicks' => $total_clicks,
|
||||
);
|
||||
|
||||
$stuff = yourls_apply_filter( 'version_check_stuff', $stuff );
|
||||
|
||||
// Send it in
|
||||
$url = 'http://api.yourls.org/core/version/1.1/';
|
||||
if( yourls_can_http_over_ssl() ) {
|
||||
$url = yourls_set_url_scheme($url, 'https');
|
||||
}
|
||||
$req = yourls_http_post( $url, array(), $stuff );
|
||||
|
||||
$checks->last_attempt = time();
|
||||
$checks->version_checked = YOURLS_VERSION;
|
||||
|
||||
// Unexpected results ?
|
||||
if( is_string( $req ) or !$req->success ) {
|
||||
$checks->failed_attempts = $checks->failed_attempts + 1;
|
||||
yourls_update_option( 'core_version_checks', $checks );
|
||||
if( is_string($req) ) {
|
||||
yourls_debug_log('Version check failed: ' . $req);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse response
|
||||
$json = json_decode( trim( $req->body ) );
|
||||
|
||||
if( yourls_validate_core_version_response($json) ) {
|
||||
// All went OK - mark this down
|
||||
$checks->failed_attempts = 0;
|
||||
$checks->last_result = $json;
|
||||
yourls_update_option( 'core_version_checks', $checks );
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
// Request returned actual result, but not what we expected
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure response from api.yourls.org is valid
|
||||
*
|
||||
* 1) we should get a json object with two following properties:
|
||||
* 'latest' => a string representing a YOURLS version number, eg '1.2.3'
|
||||
* 'zipurl' => a string for a zip package URL, from github, eg 'https://api.github.com/repos/YOURLS/YOURLS/zipball/1.2.3'
|
||||
* 2) 'latest' and version extracted from 'zipurl' should match
|
||||
* 3) the object should not contain any other key
|
||||
*
|
||||
* @since 1.7.7
|
||||
* @param object $json JSON object to check
|
||||
* @return bool true if seems legit, false otherwise
|
||||
*/
|
||||
function yourls_validate_core_version_response($json) {
|
||||
return (
|
||||
yourls_validate_core_version_response_keys($json)
|
||||
&& $json->latest === yourls_sanitize_version($json->latest)
|
||||
&& $json->zipurl === yourls_sanitize_url($json->zipurl)
|
||||
&& $json->latest === yourls_get_version_from_zipball_url($json->zipurl)
|
||||
&& yourls_is_valid_github_repo_url($json->zipurl)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version number from Github zipball URL (last part of URL, really)
|
||||
*
|
||||
* @since 1.8.3
|
||||
* @param string $zipurl eg 'https://api.github.com/repos/YOURLS/YOURLS/zipball/1.2.3'
|
||||
* @return string
|
||||
*/
|
||||
function yourls_get_version_from_zipball_url($zipurl) {
|
||||
$version = '';
|
||||
$parts = explode('/', parse_url(yourls_sanitize_url($zipurl), PHP_URL_PATH) ?? '');
|
||||
// expect at least 1 slash in path, return last part
|
||||
if( count($parts) > 1 ) {
|
||||
$version = end($parts);
|
||||
}
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if URL is from YOURLS/YOURLS repo on github
|
||||
*
|
||||
* @since 1.8.3
|
||||
* @param string $url URL to check
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_is_valid_github_repo_url($url) {
|
||||
$url = yourls_sanitize_url($url);
|
||||
return (
|
||||
join('.',array_slice(explode('.', parse_url($url, PHP_URL_HOST) ?? ''), -2, 2)) === 'github.com'
|
||||
// explodes on '.' (['api','github','com']) and keeps the last two elements
|
||||
// to make sure domain is either github.com or one of its subdomain (api.github.com for instance)
|
||||
// TODO: keep an eye on Github API to make sure it doesn't change some day to another domain (githubapi.com, ...)
|
||||
&& substr( parse_url($url, PHP_URL_PATH), 0, 21 ) === '/repos/YOURLS/YOURLS/'
|
||||
// make sure path starts with '/repos/YOURLS/YOURLS/'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if object has only expected keys 'latest' and 'zipurl' containing strings
|
||||
*
|
||||
* @since 1.8.3
|
||||
* @param object $json
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_validate_core_version_response_keys($json) {
|
||||
$keys = array('latest', 'zipurl');
|
||||
return (
|
||||
count(array_diff(array_keys((array)$json), $keys)) === 0
|
||||
&& isset($json->latest)
|
||||
&& isset($json->zipurl)
|
||||
&& is_string($json->latest)
|
||||
&& is_string($json->zipurl)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we want to check for a newer YOURLS version (and check if applicable)
|
||||
*
|
||||
* Currently checks are performed every 24h and only when someone is visiting an admin page.
|
||||
* In the future (1.8?) maybe check with cronjob emulation instead.
|
||||
*
|
||||
* @since 1.7
|
||||
* @return bool true if a check was needed and successfully performed, false otherwise
|
||||
*/
|
||||
function yourls_maybe_check_core_version() {
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter('shunt_maybe_check_core_version', null);
|
||||
if (null !== $pre) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
if (yourls_skip_version_check()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!yourls_is_admin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$checks = yourls_get_option( 'core_version_checks' );
|
||||
|
||||
/* We don't want to check if :
|
||||
- last_result is set (a previous check was performed)
|
||||
- and it was less than 24h ago (or less than 2h ago if it wasn't successful)
|
||||
- and version checked matched version running
|
||||
Otherwise, we want to check.
|
||||
*/
|
||||
if( !empty( $checks->last_result )
|
||||
AND
|
||||
(
|
||||
( $checks->failed_attempts == 0 && ( ( time() - $checks->last_attempt ) < 24 * 3600 ) )
|
||||
OR
|
||||
( $checks->failed_attempts > 0 && ( ( time() - $checks->last_attempt ) < 2 * 3600 ) )
|
||||
)
|
||||
AND ( $checks->version_checked == YOURLS_VERSION )
|
||||
)
|
||||
return false;
|
||||
|
||||
// We want to check if there's a new version
|
||||
$new_check = yourls_check_core_version();
|
||||
|
||||
// Could not check for a new version, and we don't have ancient data
|
||||
if( false == $new_check && !isset( $checks->last_result->latest ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user setting for skipping version check is set
|
||||
*
|
||||
* @since 1.8.2
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_skip_version_check() {
|
||||
return yourls_apply_filter('skip_version_check', defined('YOURLS_NO_VERSION_CHECK') && YOURLS_NO_VERSION_CHECK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if server can perform HTTPS requests, return bool
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @return bool whether the server can perform HTTP requests over SSL
|
||||
*/
|
||||
function yourls_can_http_over_ssl() {
|
||||
$ssl_curl = $ssl_socket = false;
|
||||
|
||||
if( function_exists( 'curl_exec' ) ) {
|
||||
$curl_version = curl_version();
|
||||
$ssl_curl = ( $curl_version['features'] & CURL_VERSION_SSL );
|
||||
}
|
||||
|
||||
if( function_exists( 'stream_socket_client' ) ) {
|
||||
$ssl_socket = extension_loaded( 'openssl' ) && function_exists( 'openssl_x509_parse' );
|
||||
}
|
||||
|
||||
return ( $ssl_curl OR $ssl_socket );
|
||||
}
|
|
@ -0,0 +1,384 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Echoes an image tag of Google Charts map from sorted array of 'country_code' => 'number of visits' (sort by DESC)
|
||||
*
|
||||
* @param array $countries Array of 'country_code' => 'number of visits'
|
||||
* @param string $id Optional HTML element ID
|
||||
* @return void
|
||||
*/
|
||||
function yourls_stats_countries_map($countries, $id = null) {
|
||||
|
||||
yourls_do_action( 'pre_stats_countries_map' );
|
||||
|
||||
// if $id is null then assign a random string
|
||||
if( $id === null )
|
||||
$id = uniqid ( 'yourls_stats_map_' );
|
||||
|
||||
$data = array_merge( array( 'Country' => 'Hits' ), $countries );
|
||||
$data = yourls_google_array_to_data_table( $data );
|
||||
|
||||
$options = array(
|
||||
'backgroundColor' => "white",
|
||||
'colorAxis' => "{colors:['A8D0ED','99C4E4','8AB8DB','7BACD2','6BA1C9','5C95C0','4D89B7','3E7DAE','2E72A5','1F669C']}",
|
||||
'width' => "550",
|
||||
'height' => "340",
|
||||
'theme' => 'maximized'
|
||||
);
|
||||
$options = yourls_apply_filter( 'stats_countries_map_options', $options );
|
||||
|
||||
$map = yourls_google_viz_code( 'GeoChart', $data, $options, $id );
|
||||
|
||||
echo yourls_apply_filter( 'stats_countries_map', $map, $countries, $options, $id );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Echoes an image tag of Google Charts pie from sorted array of 'data' => 'value' (sort by DESC). Optional $limit = (integer) limit list of X first countries, sorted by most visits
|
||||
*
|
||||
* @param array $data Array of 'data' => 'value'
|
||||
* @param int $limit Optional limit list of X first countries
|
||||
* @param $size Optional size of the image
|
||||
* @param $id Optional HTML element ID
|
||||
* @return void
|
||||
*/
|
||||
function yourls_stats_pie($data, $limit = 10, $size = '340x220', $id = null) {
|
||||
|
||||
yourls_do_action( 'pre_stats_pie' );
|
||||
|
||||
// if $id is null then assign a random string
|
||||
if( $id === null )
|
||||
$id = uniqid ( 'yourls_stats_pie_' );
|
||||
|
||||
// Trim array: $limit first item + the sum of all others
|
||||
if ( count( $data ) > $limit ) {
|
||||
$i= 0;
|
||||
$trim_data = array( 'Others' => 0 );
|
||||
foreach( $data as $item=>$value ) {
|
||||
$i++;
|
||||
if( $i <= $limit ) {
|
||||
$trim_data[$item] = $value;
|
||||
} else {
|
||||
$trim_data['Others'] += $value;
|
||||
}
|
||||
}
|
||||
$data = $trim_data;
|
||||
}
|
||||
|
||||
// Scale items
|
||||
$_data = yourls_scale_data( $data );
|
||||
|
||||
list($width, $height) = explode( 'x', $size );
|
||||
|
||||
$options = array(
|
||||
'theme' => 'maximized',
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'colors' => "['A8D0ED','99C4E4','8AB8DB','7BACD2','6BA1C9','5C95C0','4D89B7','3E7DAE','2E72A5','1F669C']",
|
||||
'legend' => 'none',
|
||||
'chartArea' => '{top: "5%", height: "90%"}',
|
||||
'pieSliceText' => 'label',
|
||||
);
|
||||
$options = yourls_apply_filter( 'stats_pie_options', $options );
|
||||
|
||||
$script_data = array_merge( array( 'Country' => 'Value' ), $_data );
|
||||
$script_data = yourls_google_array_to_data_table( $script_data );
|
||||
|
||||
$pie = yourls_google_viz_code( 'PieChart', $script_data, $options, $id );
|
||||
|
||||
echo yourls_apply_filter( 'stats_pie', $pie, $data, $limit, $size, $options, $id );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build a list of all daily values between d1/m1/y1 to d2/m2/y2.
|
||||
*
|
||||
* @param array $dates
|
||||
* @return array[] Array of arrays of days, months, years
|
||||
*/
|
||||
function yourls_build_list_of_days($dates) {
|
||||
/* Say we have an array like:
|
||||
$dates = array (
|
||||
2009 => array (
|
||||
'08' => array (
|
||||
29 => 15,
|
||||
30 => 5,
|
||||
),
|
||||
'09' => array (
|
||||
'02' => 3,
|
||||
'03' => 5,
|
||||
'04' => 2,
|
||||
'05' => 99,
|
||||
),
|
||||
),
|
||||
)
|
||||
*/
|
||||
|
||||
if( !$dates )
|
||||
return array();
|
||||
|
||||
// Get first & last years from our range. In our example: 2009 & 2009
|
||||
$first_year = key( $dates );
|
||||
$_keys = array_keys( $dates );
|
||||
$last_year = end( $_keys );
|
||||
reset( $dates );
|
||||
|
||||
// Get first & last months from our range. In our example: 08 & 09
|
||||
$first_month = key( $dates[ $first_year ] );
|
||||
$_keys = array_keys( $dates[ $last_year ] );
|
||||
$last_month = end( $_keys );
|
||||
reset( $dates );
|
||||
|
||||
// Get first & last days from our range. In our example: 29 & 05
|
||||
$first_day = key( $dates[ $first_year ][ $first_month ] );
|
||||
$_keys = array_keys( $dates[ $last_year ][ $last_month ] );
|
||||
$last_day = end( $_keys );
|
||||
|
||||
unset( $_keys );
|
||||
|
||||
// Now build a list of all years (2009), month (08 & 09) and days (all from 2009-08-29 to 2009-09-05)
|
||||
$list_of_years = array();
|
||||
$list_of_months = array();
|
||||
$list_of_days = array();
|
||||
for ( $year = $first_year; $year <= $last_year; $year++ ) {
|
||||
$_year = sprintf( '%04d', $year );
|
||||
$list_of_years[ $_year ] = $_year;
|
||||
$current_first_month = ( $year == $first_year ? $first_month : '01' );
|
||||
$current_last_month = ( $year == $last_year ? $last_month : '12' );
|
||||
for ( $month = $current_first_month; $month <= $current_last_month; $month++ ) {
|
||||
$_month = sprintf( '%02d', $month );
|
||||
$list_of_months[ $_month ] = $_month;
|
||||
$current_first_day = ( $year == $first_year && $month == $first_month ? $first_day : '01' );
|
||||
$current_last_day = ( $year == $last_year && $month == $last_month ? $last_day : yourls_days_in_month( $month, $year) );
|
||||
for ( $day = $current_first_day; $day <= $current_last_day; $day++ ) {
|
||||
$day = sprintf( '%02d', $day );
|
||||
$key = date( 'M d, Y', mktime( 0, 0, 0, $_month, $day, $_year ) );
|
||||
$list_of_days[ $key ] = isset( $dates[$_year][$_month][$day] ) ? $dates[$_year][$_month][$day] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'list_of_days' => $list_of_days,
|
||||
'list_of_months' => $list_of_months,
|
||||
'list_of_years' => $list_of_years,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Echoes an image tag of Google Charts line graph from array of values (eg 'number of clicks').
|
||||
*
|
||||
* $legend1_list & legend2_list are values used for the 2 x-axis labels. $id is an HTML/JS id
|
||||
*
|
||||
* @param array $values Array of values (eg 'number of clicks')
|
||||
* @param string $id HTML element id
|
||||
* @return void
|
||||
*/
|
||||
function yourls_stats_line($values, $id = null) {
|
||||
|
||||
yourls_do_action( 'pre_stats_line' );
|
||||
|
||||
// if $id is null then assign a random string
|
||||
if( $id === null )
|
||||
$id = uniqid ( 'yourls_stats_line_' );
|
||||
|
||||
// If we have only 1 day of data, prepend a fake day with 0 hits for a prettier graph
|
||||
if ( count( $values ) == 1 )
|
||||
array_unshift( $values, 0 );
|
||||
|
||||
// Keep only a subset of values to keep graph smooth
|
||||
$values = yourls_array_granularity( $values, 30 );
|
||||
|
||||
$data = array_merge( array( 'Time' => 'Hits' ), $values );
|
||||
$data = yourls_google_array_to_data_table( $data );
|
||||
|
||||
$options = array(
|
||||
"legend" => "none",
|
||||
"pointSize" => "3",
|
||||
"theme" => "maximized",
|
||||
"curveType" => "function",
|
||||
"width" => 430,
|
||||
"height" => 220,
|
||||
"hAxis" => "{minTextSpacing: 80, maxTextLines: 1, maxAlternation: 1}",
|
||||
"vAxis" => "{minValue: 0, format: '#'}",
|
||||
"colors" => "['#2a85b3']",
|
||||
);
|
||||
$options = yourls_apply_filter( 'stats_line_options', $options );
|
||||
|
||||
$lineChart = yourls_google_viz_code( 'LineChart', $data, $options, $id );
|
||||
|
||||
echo yourls_apply_filter( 'stats_line', $lineChart, $values, $options, $id );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the number of days in a month. From php.net.
|
||||
*
|
||||
* @param int $month
|
||||
* @param int $year
|
||||
* @return int
|
||||
*/
|
||||
function yourls_days_in_month($month, $year) {
|
||||
// calculate number of days in a month
|
||||
return $month == 2 ? ( $year % 4 ? 28 : ( $year % 100 ? 29 : ( $year % 400 ? 28 : 29 ) ) ) : ( ( $month - 1 ) % 7 % 2 ? 30 : 31 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get max value from date array of 'Aug 12, 2012' = '1337'
|
||||
*
|
||||
* @param array $list_of_days
|
||||
* @return array
|
||||
*/
|
||||
function yourls_stats_get_best_day($list_of_days) {
|
||||
$max = max( $list_of_days );
|
||||
foreach( $list_of_days as $k=>$v ) {
|
||||
if ( $v == $max )
|
||||
return array( 'day' => $k, 'max' => $max );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return domain of a URL
|
||||
*
|
||||
* @param string $url
|
||||
* @param bool $include_scheme
|
||||
* @return string
|
||||
*/
|
||||
function yourls_get_domain($url, $include_scheme = false) {
|
||||
$parse = @parse_url( $url ); // Hiding ugly stuff coming from malformed referrer URLs
|
||||
|
||||
// Get host & scheme. Fall back to path if not found.
|
||||
$host = isset( $parse['host'] ) ? $parse['host'] : '';
|
||||
$scheme = isset( $parse['scheme'] ) ? $parse['scheme'] : '';
|
||||
$path = isset( $parse['path'] ) ? $parse['path'] : '';
|
||||
if( !$host )
|
||||
$host = $path;
|
||||
|
||||
if ( $include_scheme && $scheme )
|
||||
$host = $scheme.'://'.$host;
|
||||
|
||||
return $host;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return favicon URL
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
function yourls_get_favicon_url($url) {
|
||||
return yourls_match_current_protocol( '//www.google.com/s2/favicons?domain=' . yourls_get_domain( $url, false ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale array of data from 0 to 100 max
|
||||
*
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
function yourls_scale_data($data ) {
|
||||
$max = max( $data );
|
||||
if( $max > 100 ) {
|
||||
foreach( $data as $k=>$v ) {
|
||||
$data[$k] = intval( $v / $max * 100 );
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tweak granularity of array $array: keep only $grain values.
|
||||
*
|
||||
* This make less accurate but less messy graphs when too much values.
|
||||
* See https://developers.google.com/chart/image/docs/gallery/line_charts?hl=en#data-granularity
|
||||
*
|
||||
* @param array $array
|
||||
* @param int $grain
|
||||
* @param bool $preserve_max
|
||||
* @return array
|
||||
*/
|
||||
function yourls_array_granularity($array, $grain = 100, $preserve_max = true) {
|
||||
if ( count( $array ) > $grain ) {
|
||||
$max = max( $array );
|
||||
$step = intval( count( $array ) / $grain );
|
||||
$i = 0;
|
||||
// Loop through each item and unset except every $step (optional preserve the max value)
|
||||
foreach( $array as $k=>$v ) {
|
||||
$i++;
|
||||
if ( $i % $step != 0 ) {
|
||||
if ( $preserve_max == false ) {
|
||||
unset( $array[$k] );
|
||||
} else {
|
||||
if ( $v < $max )
|
||||
unset( $array[$k] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform data array to data table for Google API
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
function yourls_google_array_to_data_table($data){
|
||||
$str = "var data = google.visualization.arrayToDataTable([\n";
|
||||
foreach( $data as $label => $values ){
|
||||
if( !is_array( $values ) ) {
|
||||
$values = array( $values );
|
||||
}
|
||||
$str .= "\t['$label',";
|
||||
foreach( $values as $value ){
|
||||
if( !is_numeric( $value ) && strpos( $value, '[' ) !== 0 && strpos( $value, '{' ) !== 0 ) {
|
||||
$value = "'$value'";
|
||||
}
|
||||
$str .= "$value";
|
||||
}
|
||||
$str .= "],\n";
|
||||
}
|
||||
$str = substr( $str, 0, -2 ) . "\n"; // remove the trailing comma/return, reappend the return
|
||||
$str .= "]);\n"; // wrap it up
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return javascript code that will display the Google Chart
|
||||
*
|
||||
* @param string $graph_type
|
||||
* @param string $data
|
||||
* @param var $options
|
||||
* @param string $id
|
||||
* @return string
|
||||
*/
|
||||
function yourls_google_viz_code($graph_type, $data, $options, $id ) {
|
||||
$function_name = 'yourls_graph' . $id;
|
||||
$code = "\n<script id=\"$function_name\" type=\"text/javascript\">\n";
|
||||
$code .= "function $function_name() { \n";
|
||||
|
||||
$code .= "$data\n";
|
||||
|
||||
$code .= "var options = {\n";
|
||||
foreach( $options as $field => $value ) {
|
||||
if( !is_numeric( $value ) && strpos( $value, '[' ) !== 0 && strpos( $value, '{' ) !== 0 ) {
|
||||
$value = "\"$value\"";
|
||||
}
|
||||
$code .= "\t'$field': $value,\n";
|
||||
}
|
||||
$code = substr( $code, 0, -2 ) . "\n"; // remove the trailing comma/return, reappend the return
|
||||
$code .= "\t}\n";
|
||||
|
||||
$code .= "new google.visualization.$graph_type( document.getElementById('visualization_$id') ).draw( data, options );";
|
||||
$code .= "}\n";
|
||||
$code .= "google.setOnLoadCallback( $function_name );\n";
|
||||
$code .= "</script>\n";
|
||||
$code .= "<div id=\"visualization_$id\"></div>\n";
|
||||
|
||||
return $code;
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Check if we have PDO installed, returns bool
|
||||
*
|
||||
* @since 1.7.3
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_check_PDO() {
|
||||
return extension_loaded('pdo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if server has MySQL 5.0+
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_check_database_version() {
|
||||
return ( version_compare( '5.0', yourls_get_database_version() ) <= 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DB version
|
||||
*
|
||||
* @since 1.7
|
||||
* @return string sanitized DB version
|
||||
*/
|
||||
function yourls_get_database_version() {
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_get_database_version', false );
|
||||
if ( false !== $pre ) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
return yourls_sanitize_version(yourls_get_db()->mysql_version());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if PHP > 7.2
|
||||
*
|
||||
* As of 1.8 we advertise YOURLS as being 7.4+ but it should work on 7.2 (although untested)
|
||||
* so we don't want to strictly enforce a limitation that may not be necessary.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_check_php_version() {
|
||||
return version_compare( PHP_VERSION, '7.2.0', '>=' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if server is an Apache
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_is_apache() {
|
||||
if( !array_key_exists( 'SERVER_SOFTWARE', $_SERVER ) )
|
||||
return false;
|
||||
return (
|
||||
strpos( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) !== false
|
||||
|| strpos( $_SERVER['SERVER_SOFTWARE'], 'LiteSpeed' ) !== false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if server is running IIS
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_is_iis() {
|
||||
return ( array_key_exists( 'SERVER_SOFTWARE', $_SERVER ) ? ( strpos( $_SERVER['SERVER_SOFTWARE'], 'IIS' ) !== false ) : false );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create .htaccess or web.config. Returns boolean
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_create_htaccess() {
|
||||
$host = parse_url( yourls_get_yourls_site() );
|
||||
$path = ( isset( $host['path'] ) ? $host['path'] : '' );
|
||||
|
||||
if ( yourls_is_iis() ) {
|
||||
// Prepare content for a web.config file
|
||||
$content = array(
|
||||
'<?'.'xml version="1.0" encoding="UTF-8"?>',
|
||||
'<configuration>',
|
||||
' <system.webServer>',
|
||||
' <security>',
|
||||
' <requestFiltering allowDoubleEscaping="true" />',
|
||||
' </security>',
|
||||
' <rewrite>',
|
||||
' <rules>',
|
||||
' <rule name="YOURLS" stopProcessing="true">',
|
||||
' <match url="^(.*)$" ignoreCase="false" />',
|
||||
' <conditions>',
|
||||
' <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />',
|
||||
' <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />',
|
||||
' </conditions>',
|
||||
' <action type="Rewrite" url="'.$path.'/yourls-loader.php" appendQueryString="true" />',
|
||||
' </rule>',
|
||||
' </rules>',
|
||||
' </rewrite>',
|
||||
' </system.webServer>',
|
||||
'</configuration>',
|
||||
);
|
||||
|
||||
$filename = YOURLS_ABSPATH.'/web.config';
|
||||
$marker = 'none';
|
||||
|
||||
} else {
|
||||
// Prepare content for a .htaccess file
|
||||
$content = array(
|
||||
'<IfModule mod_rewrite.c>',
|
||||
'RewriteEngine On',
|
||||
'RewriteBase '.$path.'/',
|
||||
'RewriteCond %{REQUEST_FILENAME} !-f',
|
||||
'RewriteCond %{REQUEST_FILENAME} !-d',
|
||||
'RewriteRule ^.*$ '.$path.'/yourls-loader.php [L]',
|
||||
'</IfModule>',
|
||||
);
|
||||
|
||||
$filename = YOURLS_ABSPATH.'/.htaccess';
|
||||
$marker = 'YOURLS';
|
||||
|
||||
}
|
||||
|
||||
return ( yourls_insert_with_markers( $filename, $marker, $content ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert text into a file between BEGIN/END markers, return bool. Stolen from WP
|
||||
*
|
||||
* Inserts an array of strings into a file (eg .htaccess ), placing it between
|
||||
* BEGIN and END markers. Replaces existing marked info. Retains surrounding
|
||||
* data. Creates file if none exists.
|
||||
*
|
||||
* @since 1.3
|
||||
*
|
||||
* @param string $filename
|
||||
* @param string $marker
|
||||
* @param array $insertion
|
||||
* @return bool True on write success, false on failure.
|
||||
*/
|
||||
function yourls_insert_with_markers( $filename, $marker, $insertion ) {
|
||||
if ( !file_exists( $filename ) || is_writeable( $filename ) ) {
|
||||
if ( !file_exists( $filename ) ) {
|
||||
$markerdata = '';
|
||||
} else {
|
||||
$markerdata = explode( "\n", implode( '', file( $filename ) ) );
|
||||
}
|
||||
|
||||
if ( !$f = @fopen( $filename, 'w' ) )
|
||||
return false;
|
||||
|
||||
$foundit = false;
|
||||
if ( $markerdata ) {
|
||||
$state = true;
|
||||
foreach ( $markerdata as $n => $markerline ) {
|
||||
if ( strpos( $markerline, '# BEGIN ' . $marker ) !== false )
|
||||
$state = false;
|
||||
if ( $state ) {
|
||||
if ( $n + 1 < count( $markerdata ) )
|
||||
fwrite( $f, "{$markerline}\n" );
|
||||
else
|
||||
fwrite( $f, "{$markerline}" );
|
||||
}
|
||||
if ( strpos( $markerline, '# END ' . $marker ) !== false ) {
|
||||
if ( $marker != 'none' )
|
||||
fwrite( $f, "# BEGIN {$marker}\n" );
|
||||
if ( is_array( $insertion ) )
|
||||
foreach ( $insertion as $insertline )
|
||||
fwrite( $f, "{$insertline}\n" );
|
||||
if ( $marker != 'none' )
|
||||
fwrite( $f, "# END {$marker}\n" );
|
||||
$state = true;
|
||||
$foundit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !$foundit ) {
|
||||
if ( $marker != 'none' )
|
||||
fwrite( $f, "\n\n# BEGIN {$marker}\n" );
|
||||
foreach ( $insertion as $insertline )
|
||||
fwrite( $f, "{$insertline}\n" );
|
||||
if ( $marker != 'none' )
|
||||
fwrite( $f, "# END {$marker}\n\n" );
|
||||
}
|
||||
fclose( $f );
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create MySQL tables. Return array( 'success' => array of success strings, 'errors' => array of error strings )
|
||||
*
|
||||
* @since 1.3
|
||||
* @return array An array like array( 'success' => array of success strings, 'errors' => array of error strings )
|
||||
*/
|
||||
function yourls_create_sql_tables() {
|
||||
// Allow plugins (most likely a custom db.php layer in user dir) to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_yourls_create_sql_tables', null );
|
||||
// your filter function should return an array of ( 'success' => $success_msg, 'error' => $error_msg ), see below
|
||||
if ( null !== $pre ) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
$ydb = yourls_get_db();
|
||||
|
||||
$error_msg = array();
|
||||
$success_msg = array();
|
||||
|
||||
// Create Table Query
|
||||
$create_tables = array();
|
||||
$create_tables[YOURLS_DB_TABLE_URL] =
|
||||
'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_URL.'` ('.
|
||||
'`keyword` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT "",'.
|
||||
'`url` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,'.
|
||||
'`title` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,'.
|
||||
'`timestamp` timestamp NOT NULL DEFAULT current_timestamp(),'.
|
||||
'`ip` varchar(41) COLLATE utf8mb4_unicode_ci NOT NULL,'.
|
||||
'`clicks` int(10) unsigned NOT NULL,'.
|
||||
'PRIMARY KEY (`keyword`),'.
|
||||
'KEY `ip` (`ip`),'.
|
||||
'KEY `timestamp` (`timestamp`)'.
|
||||
') DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;';
|
||||
|
||||
$create_tables[YOURLS_DB_TABLE_OPTIONS] =
|
||||
'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_OPTIONS.'` ('.
|
||||
'`option_id` bigint(20) unsigned NOT NULL auto_increment,'.
|
||||
'`option_name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL default "",'.
|
||||
'`option_value` longtext COLLATE utf8mb4_unicode_ci NOT NULL,'.
|
||||
'PRIMARY KEY (`option_id`,`option_name`),'.
|
||||
'KEY `option_name` (`option_name`)'.
|
||||
') AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;';
|
||||
|
||||
$create_tables[YOURLS_DB_TABLE_LOG] =
|
||||
'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_LOG.'` ('.
|
||||
'`click_id` int(11) NOT NULL auto_increment,'.
|
||||
'`click_time` datetime NOT NULL,'.
|
||||
'`shorturl` varchar(100) BINARY NOT NULL,'.
|
||||
'`referrer` varchar(200) NOT NULL,'.
|
||||
'`user_agent` varchar(255) NOT NULL,'.
|
||||
'`ip_address` varchar(41) NOT NULL,'.
|
||||
'`country_code` char(2) NOT NULL,'.
|
||||
'PRIMARY KEY (`click_id`),'.
|
||||
'KEY `shorturl` (`shorturl`)'.
|
||||
') AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;';
|
||||
|
||||
|
||||
$create_table_count = 0;
|
||||
|
||||
yourls_debug_mode(true);
|
||||
|
||||
// Create tables
|
||||
foreach ( $create_tables as $table_name => $table_query ) {
|
||||
$ydb->perform( $table_query );
|
||||
$create_success = $ydb->fetchAffected( "SHOW TABLES LIKE '$table_name'" );
|
||||
if( $create_success ) {
|
||||
$create_table_count++;
|
||||
$success_msg[] = yourls_s( "Table '%s' created.", $table_name );
|
||||
} else {
|
||||
$error_msg[] = yourls_s( "Error creating table '%s'.", $table_name );
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes the option table
|
||||
if( !yourls_initialize_options() )
|
||||
$error_msg[] = yourls__( 'Could not initialize options' );
|
||||
|
||||
// Insert sample links
|
||||
if( !yourls_insert_sample_links() )
|
||||
$error_msg[] = yourls__( 'Could not insert sample short URLs' );
|
||||
|
||||
// Check results of operations
|
||||
if ( sizeof( $create_tables ) == $create_table_count ) {
|
||||
$success_msg[] = yourls__( 'YOURLS tables successfully created.' );
|
||||
} else {
|
||||
$error_msg[] = yourls__( 'Error creating YOURLS tables.' );
|
||||
}
|
||||
|
||||
return array( 'success' => $success_msg, 'error' => $error_msg );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the option table
|
||||
*
|
||||
* Each yourls_update_option() returns either true on success (option updated) or false on failure (new value == old value, or
|
||||
* for some reason it could not save to DB).
|
||||
* Since true & true & true = 1, we cast it to boolean type to return true (or false)
|
||||
*
|
||||
* @since 1.7
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_initialize_options() {
|
||||
return ( bool ) (
|
||||
yourls_update_option( 'version', YOURLS_VERSION )
|
||||
& yourls_update_option( 'db_version', YOURLS_DB_VERSION )
|
||||
& yourls_update_option( 'next_id', 1 )
|
||||
& yourls_update_option( 'active_plugins', array() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the URL table with a few sample links
|
||||
*
|
||||
* @since 1.7
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_insert_sample_links() {
|
||||
$link1 = yourls_add_new_link( 'https://blog.yourls.org/', 'yourlsblog', 'YOURLS\' Blog' );
|
||||
$link2 = yourls_add_new_link( 'https://yourls.org/', 'yourls', 'YOURLS: Your Own URL Shortener' );
|
||||
$link3 = yourls_add_new_link( 'https://ozh.org/', 'ozh', 'ozh.org' );
|
||||
return ( bool ) (
|
||||
$link1['status'] == 'success'
|
||||
& $link2['status'] == 'success'
|
||||
& $link3['status'] == 'success'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggle maintenance mode. Inspired from WP. Returns true for success, false otherwise
|
||||
*
|
||||
* @param bool $maintenance True to enable, false to disable
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
function yourls_maintenance_mode( $maintenance = true ) {
|
||||
|
||||
$file = YOURLS_ABSPATH . '/.maintenance' ;
|
||||
|
||||
// Turn maintenance mode on : create .maintenance file
|
||||
if ( (bool)$maintenance ) {
|
||||
if ( ! ( $fp = @fopen( $file, 'w' ) ) )
|
||||
return false;
|
||||
|
||||
$maintenance_string = '<?php $maintenance_start = ' . time() . '; ?>';
|
||||
@fwrite( $fp, $maintenance_string );
|
||||
@fclose( $fp );
|
||||
@chmod( $file, 0644 ); // Read and write for owner, read for everybody else
|
||||
|
||||
// Not sure why the fwrite would fail if the fopen worked... Just in case
|
||||
return( is_readable( $file ) );
|
||||
|
||||
// Turn maintenance mode off : delete the .maintenance file
|
||||
} else {
|
||||
return @unlink($file);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,778 @@
|
|||
<?php
|
||||
/**
|
||||
* YOURLS modification of a small subset from WordPress' KSES implementation.
|
||||
* Straight from the Let's Not Reinvent The Wheel department.
|
||||
*/
|
||||
|
||||
/**
|
||||
* kses 0.2.2 - HTML/XHTML filter that only allows some elements and attributes
|
||||
* Copyright (C) 2002, 2003, 2005 Ulf Harnhammar
|
||||
*
|
||||
* This program is free software and open source software; you can redistribute
|
||||
* it and/or modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the License,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
* [kses strips evil scripts!]
|
||||
*
|
||||
* @version 0.2.2
|
||||
* @copyright (C) 2002, 2003, 2005
|
||||
* @author Ulf Harnhammar <http://advogato.org/person/metaur/>
|
||||
*
|
||||
* @package External
|
||||
* @subpackage KSES
|
||||
*
|
||||
*/
|
||||
|
||||
/* NOTE ABOUT GLOBALS
|
||||
* Two globals are defined: $yourls_allowedentitynames and $yourls_allowedprotocols
|
||||
* - $yourls_allowedentitynames is used internally in KSES functions to sanitize HTML entities
|
||||
* - $yourls_allowedprotocols is used in various parts of YOURLS, not just in KSES, albeit being defined here
|
||||
* Two globals are not defined and unused at this moment: $yourls_allowedtags_all and $yourls_allowedtags
|
||||
* The code for these vars is here and ready for any future use
|
||||
*/
|
||||
|
||||
// Populate after plugins have loaded to allow user defined values
|
||||
yourls_add_action( 'plugins_loaded', 'yourls_kses_init' );
|
||||
|
||||
/**
|
||||
* Init KSES globals if not already defined (by a plugin)
|
||||
*
|
||||
* @since 1.6
|
||||
* @return void
|
||||
*/
|
||||
function yourls_kses_init() {
|
||||
global $yourls_allowedentitynames, $yourls_allowedprotocols;
|
||||
|
||||
if( ! $yourls_allowedentitynames ) {
|
||||
$yourls_allowedentitynames = yourls_apply_filter( 'kses_allowed_entities', yourls_kses_allowed_entities() );
|
||||
}
|
||||
|
||||
if( ! $yourls_allowedprotocols ) {
|
||||
$yourls_allowedprotocols = yourls_apply_filter( 'kses_allowed_protocols', yourls_kses_allowed_protocols() );
|
||||
}
|
||||
|
||||
/** See NOTE ABOUT GLOBALS **
|
||||
|
||||
if( ! $yourls_allowedtags_all ) {
|
||||
$yourls_allowedtags_all = yourls_kses_allowed_tags_all();
|
||||
$yourls_allowedtags_all = array_map( '_yourls_add_global_attributes', $yourls_allowedtags_all );
|
||||
$yourls_allowedtags_all = yourls_apply_filter( 'kses_allowed_tags_all', $yourls_allowedtags_all );
|
||||
} else {
|
||||
// User defined: let's sanitize
|
||||
$yourls_allowedtags_all = yourls_kses_array_lc( $yourls_allowedtags_all );
|
||||
}
|
||||
|
||||
if( ! $yourls_allowedtags ) {
|
||||
$yourls_allowedtags = yourls_kses_allowed_tags();
|
||||
$yourls_allowedtags = array_map( '_yourls_add_global_attributes', $yourls_allowedtags );
|
||||
$yourls_allowedtags = yourls_apply_filter( 'kses_allowed_tags', $yourls_allowedtags );
|
||||
} else {
|
||||
// User defined: let's sanitize
|
||||
$yourls_allowedtags = yourls_kses_array_lc( $yourls_allowedtags );
|
||||
}
|
||||
|
||||
/**/
|
||||
}
|
||||
|
||||
/**
|
||||
* Kses global for all allowable HTML tags.
|
||||
*
|
||||
* Complete (?) list of HTML tags. Keep this function available for any plugin or
|
||||
* future feature that will want to display lots of HTML.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @return array All tags
|
||||
*/
|
||||
function yourls_kses_allowed_tags_all() {
|
||||
return array(
|
||||
'address' => array(),
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'rel' => true,
|
||||
'rev' => true,
|
||||
'name' => true,
|
||||
'target' => true,
|
||||
),
|
||||
'abbr' => array(),
|
||||
'acronym' => array(),
|
||||
'area' => array(
|
||||
'alt' => true,
|
||||
'coords' => true,
|
||||
'href' => true,
|
||||
'nohref' => true,
|
||||
'shape' => true,
|
||||
'target' => true,
|
||||
),
|
||||
'article' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'aside' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'b' => array(),
|
||||
'big' => array(),
|
||||
'blockquote' => array(
|
||||
'cite' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'br' => array(),
|
||||
'button' => array(
|
||||
'disabled' => true,
|
||||
'name' => true,
|
||||
'type' => true,
|
||||
'value' => true,
|
||||
),
|
||||
'caption' => array(
|
||||
'align' => true,
|
||||
),
|
||||
'cite' => array(
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
),
|
||||
'code' => array(),
|
||||
'col' => array(
|
||||
'align' => true,
|
||||
'char' => true,
|
||||
'charoff' => true,
|
||||
'span' => true,
|
||||
'dir' => true,
|
||||
'valign' => true,
|
||||
'width' => true,
|
||||
),
|
||||
'del' => array(
|
||||
'datetime' => true,
|
||||
),
|
||||
'dd' => array(),
|
||||
'details' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'open' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'div' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'dl' => array(),
|
||||
'dt' => array(),
|
||||
'em' => array(),
|
||||
'fieldset' => array(),
|
||||
'figure' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'figcaption' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'font' => array(
|
||||
'color' => true,
|
||||
'face' => true,
|
||||
'size' => true,
|
||||
),
|
||||
'footer' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'form' => array(
|
||||
'action' => true,
|
||||
'accept' => true,
|
||||
'accept-charset' => true,
|
||||
'enctype' => true,
|
||||
'method' => true,
|
||||
'name' => true,
|
||||
'target' => true,
|
||||
),
|
||||
'h1' => array(
|
||||
'align' => true,
|
||||
),
|
||||
'h2' => array(
|
||||
'align' => true,
|
||||
),
|
||||
'h3' => array(
|
||||
'align' => true,
|
||||
),
|
||||
'h4' => array(
|
||||
'align' => true,
|
||||
),
|
||||
'h5' => array(
|
||||
'align' => true,
|
||||
),
|
||||
'h6' => array(
|
||||
'align' => true,
|
||||
),
|
||||
'header' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'hgroup' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'hr' => array(
|
||||
'align' => true,
|
||||
'noshade' => true,
|
||||
'size' => true,
|
||||
'width' => true,
|
||||
),
|
||||
'i' => array(),
|
||||
'img' => array(
|
||||
'alt' => true,
|
||||
'align' => true,
|
||||
'border' => true,
|
||||
'height' => true,
|
||||
'hspace' => true,
|
||||
'longdesc' => true,
|
||||
'vspace' => true,
|
||||
'src' => true,
|
||||
'usemap' => true,
|
||||
'width' => true,
|
||||
),
|
||||
'ins' => array(
|
||||
'datetime' => true,
|
||||
'cite' => true,
|
||||
),
|
||||
'kbd' => array(),
|
||||
'label' => array(
|
||||
'for' => true,
|
||||
),
|
||||
'legend' => array(
|
||||
'align' => true,
|
||||
),
|
||||
'li' => array(
|
||||
'align' => true,
|
||||
),
|
||||
'map' => array(
|
||||
'name' => true,
|
||||
),
|
||||
'menu' => array(
|
||||
'type' => true,
|
||||
),
|
||||
'nav' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'p' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'pre' => array(
|
||||
'width' => true,
|
||||
),
|
||||
'q' => array(
|
||||
'cite' => true,
|
||||
),
|
||||
's' => array(),
|
||||
'span' => array(
|
||||
'dir' => true,
|
||||
'align' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'section' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'small' => array(),
|
||||
'strike' => array(),
|
||||
'strong' => array(),
|
||||
'sub' => array(),
|
||||
'summary' => array(
|
||||
'align' => true,
|
||||
'dir' => true,
|
||||
'lang' => true,
|
||||
'xml:lang' => true,
|
||||
),
|
||||
'sup' => array(),
|
||||
'table' => array(
|
||||
'align' => true,
|
||||
'bgcolor' => true,
|
||||
'border' => true,
|
||||
'cellpadding' => true,
|
||||
'cellspacing' => true,
|
||||
'dir' => true,
|
||||
'rules' => true,
|
||||
'summary' => true,
|
||||
'width' => true,
|
||||
),
|
||||
'tbody' => array(
|
||||
'align' => true,
|
||||
'char' => true,
|
||||
'charoff' => true,
|
||||
'valign' => true,
|
||||
),
|
||||
'td' => array(
|
||||
'abbr' => true,
|
||||
'align' => true,
|
||||
'axis' => true,
|
||||
'bgcolor' => true,
|
||||
'char' => true,
|
||||
'charoff' => true,
|
||||
'colspan' => true,
|
||||
'dir' => true,
|
||||
'headers' => true,
|
||||
'height' => true,
|
||||
'nowrap' => true,
|
||||
'rowspan' => true,
|
||||
'scope' => true,
|
||||
'valign' => true,
|
||||
'width' => true,
|
||||
),
|
||||
'textarea' => array(
|
||||
'cols' => true,
|
||||
'rows' => true,
|
||||
'disabled' => true,
|
||||
'name' => true,
|
||||
'readonly' => true,
|
||||
),
|
||||
'tfoot' => array(
|
||||
'align' => true,
|
||||
'char' => true,
|
||||
'charoff' => true,
|
||||
'valign' => true,
|
||||
),
|
||||
'th' => array(
|
||||
'abbr' => true,
|
||||
'align' => true,
|
||||
'axis' => true,
|
||||
'bgcolor' => true,
|
||||
'char' => true,
|
||||
'charoff' => true,
|
||||
'colspan' => true,
|
||||
'headers' => true,
|
||||
'height' => true,
|
||||
'nowrap' => true,
|
||||
'rowspan' => true,
|
||||
'scope' => true,
|
||||
'valign' => true,
|
||||
'width' => true,
|
||||
),
|
||||
'thead' => array(
|
||||
'align' => true,
|
||||
'char' => true,
|
||||
'charoff' => true,
|
||||
'valign' => true,
|
||||
),
|
||||
'title' => array(),
|
||||
'tr' => array(
|
||||
'align' => true,
|
||||
'bgcolor' => true,
|
||||
'char' => true,
|
||||
'charoff' => true,
|
||||
'valign' => true,
|
||||
),
|
||||
'tt' => array(),
|
||||
'u' => array(),
|
||||
'ul' => array(
|
||||
'type' => true,
|
||||
),
|
||||
'ol' => array(
|
||||
'start' => true,
|
||||
'type' => true,
|
||||
),
|
||||
'var' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kses global for default allowable HTML tags. TODO: trim down to necessary only.
|
||||
*
|
||||
* Short list of HTML tags used in YOURLS core for display
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @return array Allowed tags
|
||||
*/
|
||||
function yourls_kses_allowed_tags() {
|
||||
return array(
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'title' => true,
|
||||
),
|
||||
'abbr' => array(
|
||||
'title' => true,
|
||||
),
|
||||
'acronym' => array(
|
||||
'title' => true,
|
||||
),
|
||||
'b' => array(),
|
||||
'blockquote' => array(
|
||||
'cite' => true,
|
||||
),
|
||||
'cite' => array(),
|
||||
'code' => array(),
|
||||
'del' => array(
|
||||
'datetime' => true,
|
||||
),
|
||||
'em' => array(),
|
||||
'i' => array(),
|
||||
'q' => array(
|
||||
'cite' => true,
|
||||
),
|
||||
'strike' => array(),
|
||||
'strong' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kses global for allowable HTML entities.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @return array Allowed entities
|
||||
*/
|
||||
function yourls_kses_allowed_entities() {
|
||||
return array(
|
||||
'nbsp', 'iexcl', 'cent', 'pound', 'curren', 'yen',
|
||||
'brvbar', 'sect', 'uml', 'copy', 'ordf', 'laquo',
|
||||
'not', 'shy', 'reg', 'macr', 'deg', 'plusmn',
|
||||
'acute', 'micro', 'para', 'middot', 'cedil', 'ordm',
|
||||
'raquo', 'iquest', 'Agrave', 'Aacute', 'Acirc', 'Atilde',
|
||||
'Auml', 'Aring', 'AElig', 'Ccedil', 'Egrave', 'Eacute',
|
||||
'Ecirc', 'Euml', 'Igrave', 'Iacute', 'Icirc', 'Iuml',
|
||||
'ETH', 'Ntilde', 'Ograve', 'Oacute', 'Ocirc', 'Otilde',
|
||||
'Ouml', 'times', 'Oslash', 'Ugrave', 'Uacute', 'Ucirc',
|
||||
'Uuml', 'Yacute', 'THORN', 'szlig', 'agrave', 'aacute',
|
||||
'acirc', 'atilde', 'auml', 'aring', 'aelig', 'ccedil',
|
||||
'egrave', 'eacute', 'ecirc', 'euml', 'igrave', 'iacute',
|
||||
'icirc', 'iuml', 'eth', 'ntilde', 'ograve', 'oacute',
|
||||
'ocirc', 'otilde', 'ouml', 'divide', 'oslash', 'ugrave',
|
||||
'uacute', 'ucirc', 'uuml', 'yacute', 'thorn', 'yuml',
|
||||
'quot', 'amp', 'lt', 'gt', 'apos', 'OElig',
|
||||
'oelig', 'Scaron', 'scaron', 'Yuml', 'circ', 'tilde',
|
||||
'ensp', 'emsp', 'thinsp', 'zwnj', 'zwj', 'lrm',
|
||||
'rlm', 'ndash', 'mdash', 'lsquo', 'rsquo', 'sbquo',
|
||||
'ldquo', 'rdquo', 'bdquo', 'dagger', 'Dagger', 'permil',
|
||||
'lsaquo', 'rsaquo', 'euro', 'fnof', 'Alpha', 'Beta',
|
||||
'Gamma', 'Delta', 'Epsilon', 'Zeta', 'Eta', 'Theta',
|
||||
'Iota', 'Kappa', 'Lambda', 'Mu', 'Nu', 'Xi',
|
||||
'Omicron', 'Pi', 'Rho', 'Sigma', 'Tau', 'Upsilon',
|
||||
'Phi', 'Chi', 'Psi', 'Omega', 'alpha', 'beta',
|
||||
'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta',
|
||||
'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi',
|
||||
'omicron', 'pi', 'rho', 'sigmaf', 'sigma', 'tau',
|
||||
'upsilon', 'phi', 'chi', 'psi', 'omega', 'thetasym',
|
||||
'upsih', 'piv', 'bull', 'hellip', 'prime', 'Prime',
|
||||
'oline', 'frasl', 'weierp', 'image', 'real', 'trade',
|
||||
'alefsym', 'larr', 'uarr', 'rarr', 'darr', 'harr',
|
||||
'crarr', 'lArr', 'uArr', 'rArr', 'dArr', 'hArr',
|
||||
'forall', 'part', 'exist', 'empty', 'nabla', 'isin',
|
||||
'notin', 'ni', 'prod', 'sum', 'minus', 'lowast',
|
||||
'radic', 'prop', 'infin', 'ang', 'and', 'or',
|
||||
'cap', 'cup', 'int', 'sim', 'cong', 'asymp',
|
||||
'ne', 'equiv', 'le', 'ge', 'sub', 'sup',
|
||||
'nsub', 'sube', 'supe', 'oplus', 'otimes', 'perp',
|
||||
'sdot', 'lceil', 'rceil', 'lfloor', 'rfloor', 'lang',
|
||||
'rang', 'loz', 'spades', 'clubs', 'hearts', 'diams',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kses global for allowable protocols.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @return array Allowed protocols
|
||||
*/
|
||||
function yourls_kses_allowed_protocols() {
|
||||
// More or less common stuff in links. From http://en.wikipedia.org/wiki/URI_scheme
|
||||
return array(
|
||||
// Common
|
||||
'http://', 'https://', 'ftp://',
|
||||
'file://', 'smb://',
|
||||
'sftp://',
|
||||
'feed:', 'feed://',
|
||||
'mailto:',
|
||||
'news:', 'nntp://',
|
||||
|
||||
// Old school bearded geek
|
||||
'gopher://', 'telnet://', 'finger://',
|
||||
'nntp://', 'worldwind://',
|
||||
|
||||
// Dev
|
||||
'ssh://', 'svn://', 'svn+ssh://', 'git://', 'cvs://',
|
||||
'apt:',
|
||||
'market://', // Google Play
|
||||
'view-source:',
|
||||
|
||||
// P2P
|
||||
'ed2k://', 'magnet:', 'udp://',
|
||||
|
||||
// Streaming stuff
|
||||
'mms://', 'lastfm://', 'spotify:', 'rtsp://',
|
||||
|
||||
// Text & voice
|
||||
'aim:', 'facetime://', 'gtalk:', 'xmpp:',
|
||||
'irc://', 'ircs://', 'mumble://',
|
||||
'callto:', 'skype:', 'sip:',
|
||||
'teamspeak://', 'tel:', 'ventrilo://', 'xfire:',
|
||||
'ymsgr:', 'tg://', 'whatsapp://',
|
||||
|
||||
// Misc
|
||||
'steam:', 'steam://',
|
||||
'bitcoin:',
|
||||
'ldap://', 'ldaps://',
|
||||
|
||||
// Purposedly removed for security
|
||||
/*
|
||||
'about:', 'chrome://', 'chrome-extension://',
|
||||
'javascript:',
|
||||
'data:',
|
||||
*/
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts and fixes HTML entities.
|
||||
*
|
||||
* This function normalizes HTML entities. It will convert "AT&T" to the correct
|
||||
* "AT&T", ":" to ":", "&#XYZZY;" to "&#XYZZY;" and so on.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $string Content to normalize entities
|
||||
* @return string Content with normalized entities
|
||||
*/
|
||||
function yourls_kses_normalize_entities($string) {
|
||||
# Disarm all entities by converting & to &
|
||||
|
||||
$string = str_replace('&', '&', $string);
|
||||
|
||||
# Change back the allowed entities in our entity whitelist
|
||||
|
||||
$string = preg_replace_callback('/&([A-Za-z]{2,8});/', 'yourls_kses_named_entities', $string);
|
||||
$string = preg_replace_callback('/&#(0*[0-9]{1,7});/', 'yourls_kses_normalize_entities2', $string);
|
||||
$string = preg_replace_callback('/&#[Xx](0*[0-9A-Fa-f]{1,6});/', 'yourls_kses_normalize_entities3', $string);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for yourls_kses_normalize_entities() regular expression.
|
||||
*
|
||||
* This function only accepts valid named entity references, which are finite,
|
||||
* case-sensitive, and highly scrutinized by HTML and XML validators.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param array $matches preg_replace_callback() matches array
|
||||
* @return string Correctly encoded entity
|
||||
*/
|
||||
function yourls_kses_named_entities($matches) {
|
||||
global $yourls_allowedentitynames;
|
||||
|
||||
if ( empty($matches[1]) )
|
||||
return '';
|
||||
|
||||
$i = $matches[1];
|
||||
return ( ( ! in_array($i, $yourls_allowedentitynames) ) ? "&$i;" : "&$i;" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for yourls_kses_normalize_entities() regular expression.
|
||||
*
|
||||
* This function helps yourls_kses_normalize_entities() to only accept 16-bit values
|
||||
* and nothing more for &#number; entities.
|
||||
*
|
||||
* @access private
|
||||
* @since 1.6
|
||||
*
|
||||
* @param array $matches preg_replace_callback() matches array
|
||||
* @return string Correctly encoded entity
|
||||
*/
|
||||
function yourls_kses_normalize_entities2($matches) {
|
||||
if ( empty($matches[1]) )
|
||||
return '';
|
||||
|
||||
$i = $matches[1];
|
||||
if (yourls_valid_unicode($i)) {
|
||||
$i = str_pad(ltrim($i,'0'), 3, '0', STR_PAD_LEFT);
|
||||
$i = "&#$i;";
|
||||
} else {
|
||||
$i = "&#$i;";
|
||||
}
|
||||
|
||||
return $i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for yourls_kses_normalize_entities() for regular expression.
|
||||
*
|
||||
* This function helps yourls_kses_normalize_entities() to only accept valid Unicode
|
||||
* numeric entities in hex form.
|
||||
*
|
||||
* @access private
|
||||
* @since 1.6
|
||||
*
|
||||
* @param array $matches preg_replace_callback() matches array
|
||||
* @return string Correctly encoded entity
|
||||
*/
|
||||
function yourls_kses_normalize_entities3($matches) {
|
||||
if ( empty($matches[1]) )
|
||||
return '';
|
||||
|
||||
$hexchars = $matches[1];
|
||||
return ( ( ! yourls_valid_unicode(hexdec($hexchars)) ) ? "&#x$hexchars;" : '&#x'.ltrim($hexchars,'0').';' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to add global attributes to a tag in the allowed html list.
|
||||
*
|
||||
* @since 1.6
|
||||
* @access private
|
||||
*
|
||||
* @param array $value An array of attributes.
|
||||
* @return array The array of attributes with global attributes added.
|
||||
*/
|
||||
function _yourls_add_global_attributes( $value ) {
|
||||
$global_attributes = array(
|
||||
'class' => true,
|
||||
'id' => true,
|
||||
'style' => true,
|
||||
'title' => true,
|
||||
);
|
||||
|
||||
if ( true === $value )
|
||||
$value = array();
|
||||
|
||||
if ( is_array( $value ) )
|
||||
return array_merge( $value, $global_attributes );
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to determine if a Unicode value is valid.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param int $i Unicode value
|
||||
* @return bool True if the value was a valid Unicode number
|
||||
*/
|
||||
function yourls_valid_unicode($i) {
|
||||
return ( $i == 0x9 || $i == 0xa || $i == 0xd ||
|
||||
($i >= 0x20 && $i <= 0xd7ff) ||
|
||||
($i >= 0xe000 && $i <= 0xfffd) ||
|
||||
($i >= 0x10000 && $i <= 0x10ffff) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through an array and changes the keys to all lower case.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param array $inarray Unfiltered array
|
||||
* @return array Fixed array with all lowercase keys
|
||||
*/
|
||||
function yourls_kses_array_lc($inarray) {
|
||||
$outarray = array ();
|
||||
|
||||
foreach ( (array) $inarray as $inkey => $inval) {
|
||||
$outkey = strtolower($inkey);
|
||||
$outarray[$outkey] = array ();
|
||||
|
||||
foreach ( (array) $inval as $inkey2 => $inval2) {
|
||||
$outkey2 = strtolower($inkey2);
|
||||
$outarray[$outkey][$outkey2] = $inval2;
|
||||
} # foreach $inval
|
||||
} # foreach $inarray
|
||||
|
||||
return $outarray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all entities to their character counterparts.
|
||||
*
|
||||
* This function decodes numeric HTML entities (A and A). It doesn't do
|
||||
* anything with other entities like ä, but we don't need them in the URL
|
||||
* protocol whitelisting system anyway.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $string Content to change entities
|
||||
* @return string Content after decoded entities
|
||||
*/
|
||||
function yourls_kses_decode_entities($string) {
|
||||
$string = preg_replace_callback('/&#([0-9]+);/', '_yourls_kses_decode_entities_chr', $string);
|
||||
$string = preg_replace_callback('/&#[Xx]([0-9A-Fa-f]+);/', '_yourls_kses_decode_entities_chr_hexdec', $string);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regex callback for yourls_kses_decode_entities()
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param array $match preg match
|
||||
* @return string
|
||||
*/
|
||||
function _yourls_kses_decode_entities_chr( $match ) {
|
||||
return chr( $match[1] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Regex callback for yourls_kses_decode_entities()
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param array $match preg match
|
||||
* @return string
|
||||
*/
|
||||
function _yourls_kses_decode_entities_chr_hexdec( $match ) {
|
||||
return chr( hexdec( $match[1] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any null characters in $string.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
function yourls_kses_no_null($string) {
|
||||
$string = preg_replace( '/\0+/', '', $string );
|
||||
$string = preg_replace( '/(\\\\0)+/', '', $string );
|
||||
|
||||
return $string;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,276 @@
|
|||
<?php
|
||||
/*
|
||||
* Functions relative to how YOURLS handle some links
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*
|
||||
* @param array|string $value The array or string to be encoded.
|
||||
* @return array|string
|
||||
*/
|
||||
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 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts keyword into short link (prepend with YOURLS base URL) or stat link (sho.rt/abc+)
|
||||
*
|
||||
* This function does not check for a valid keyword.
|
||||
* The resulting link is normalized to allow for IDN translation to UTF8
|
||||
*
|
||||
* @param string $keyword Short URL keyword
|
||||
* @param bool $stats Optional, true to return a stat link (eg sho.rt/abc+)
|
||||
* @return string Short URL, or keyword stat URL
|
||||
*/
|
||||
function yourls_link( $keyword = '', $stats = false ) {
|
||||
$keyword = yourls_sanitize_keyword($keyword);
|
||||
if( $stats === true ) {
|
||||
$keyword = $keyword . '+';
|
||||
}
|
||||
$link = yourls_normalize_uri( yourls_get_yourls_site() . '/' . $keyword );
|
||||
|
||||
if( yourls_is_ssl() ) {
|
||||
$link = yourls_set_url_scheme( $link, 'https' );
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'yourls_link', $link, $keyword );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts keyword into stat link (prepend with YOURLS base URL, append +)
|
||||
*
|
||||
* This function does not make sure the keyword matches an actual short URL
|
||||
*
|
||||
* @param string $keyword Short URL keyword
|
||||
* @return string Short URL stat link
|
||||
*/
|
||||
function yourls_statlink( $keyword = '' ) {
|
||||
$link = yourls_link( $keyword, true );
|
||||
return yourls_apply_filter( 'yourls_statlink', $link, $keyword );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return admin link, with SSL preference if applicable.
|
||||
*
|
||||
* @param string $page Page name, eg "index.php"
|
||||
* @return string
|
||||
*/
|
||||
function yourls_admin_url( $page = '' ) {
|
||||
$admin = yourls_get_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
|
||||
*
|
||||
* @param bool $echo Echo if true, or return if false
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
function yourls_site_url($echo = true, $url = '' ) {
|
||||
$url = yourls_get_relative_url( $url );
|
||||
$url = trim( yourls_get_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get YOURLS_SITE value, trimmed and filtered
|
||||
*
|
||||
* In addition of being filtered for plugins to hack this, this function is mostly here
|
||||
* to help people entering "sho.rt/" instead of "sho.rt" in their config
|
||||
*
|
||||
* @since 1.7.7
|
||||
* @return string YOURLS_SITE, trimmed and filtered
|
||||
*/
|
||||
function yourls_get_yourls_site() {
|
||||
return yourls_apply_filter('get_yourls_site', trim(YOURLS_SITE, '/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change protocol of a URL to HTTPS if we are currently on HTTPS
|
||||
*
|
||||
* This function is used to avoid insert 'http://' images or scripts in a page when it's served through HTTPS,
|
||||
* to avoid "mixed content" errors.
|
||||
* So:
|
||||
* - if you are on http://sho.rt/, 'http://something' and 'https://something' are left untouched.
|
||||
* - if you are on https:/sho.rt/, 'http://something' is changed to 'https://something'
|
||||
*
|
||||
* So, arguably, this function is poorly named. It should be something like yourls_match_current_protocol_if_we_re_on_https
|
||||
*
|
||||
* @since 1.5.1
|
||||
* @param string $url a URL
|
||||
* @param string $normal Optional, the standard scheme (defaults to 'http://')
|
||||
* @param string $ssl Optional, the SSL scheme (defaults to 'https://')
|
||||
* @return string the modified URL, if applicable
|
||||
*/
|
||||
function yourls_match_current_protocol( $url, $normal = 'http://', $ssl = 'https://' ) {
|
||||
// we're only doing something if we're currently serving through SSL and the input URL begins with 'http://' or 'https://'
|
||||
if( yourls_is_ssl() && in_array( yourls_get_protocol($url), array('http://', 'https://') ) ) {
|
||||
$url = str_replace( $normal, $ssl, $url );
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'match_current_protocol', $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto detect custom favicon in /user directory, fallback to YOURLS favicon, and echo/return its URL
|
||||
*
|
||||
* This function supersedes function yourls_favicon(), deprecated in 1.7.10, with a better naming.
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param bool $echo true to echo, false to silently return
|
||||
* @return string favicon URL
|
||||
*
|
||||
*/
|
||||
function yourls_get_yourls_favicon_url( $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.svg';
|
||||
}
|
||||
|
||||
$favicon = yourls_apply_filter('get_favicon_url', $favicon);
|
||||
|
||||
if( $echo ) {
|
||||
echo $favicon;
|
||||
}
|
||||
return $favicon;
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
<?php
|
||||
/*
|
||||
* Functions to deal with the option API
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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_name 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 ) {
|
||||
// Allow plugins to short-circuit options
|
||||
$pre = yourls_apply_filter( 'shunt_option_'.$option_name, false );
|
||||
if ( false !== $pre ) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
$option = new \YOURLS\Database\Options(yourls_get_db());
|
||||
$value = $option->get($option_name, $default);
|
||||
|
||||
return yourls_apply_filter( 'get_option_'.$option_name, $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return void
|
||||
*/
|
||||
function yourls_get_all_options() {
|
||||
// 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;
|
||||
}
|
||||
|
||||
$options = new \YOURLS\Database\Options(yourls_get_db());
|
||||
|
||||
if ($options->get_all_options() === false) {
|
||||
// Zero option found but no unexpected error so far: YOURLS isn't installed
|
||||
yourls_set_installed(false);
|
||||
return;
|
||||
}
|
||||
|
||||
yourls_set_installed(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update (add if doesn't exist) an option to DB
|
||||
*
|
||||
* Pretty much stolen from WordPress
|
||||
*
|
||||
* @since 1.4
|
||||
* @param string $option_name 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 ) {
|
||||
$option = new \YOURLS\Database\Options(yourls_get_db());
|
||||
$update = $option->update($option_name, $newvalue);
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an option to the DB
|
||||
*
|
||||
* Pretty much stolen from WordPress
|
||||
*
|
||||
* @since 1.4
|
||||
* @param string $name 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 = '' ) {
|
||||
$option = new \YOURLS\Database\Options(yourls_get_db());
|
||||
$add = $option->add($name, $value);
|
||||
|
||||
return $add;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an option from the DB
|
||||
*
|
||||
* Pretty much stolen from WordPress
|
||||
*
|
||||
* @since 1.4
|
||||
* @param string $name 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 ) {
|
||||
$option = new \YOURLS\Database\Options(yourls_get_db());
|
||||
$delete = $option->delete($name);
|
||||
|
||||
return $delete;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,941 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The filter/plugin API is located in this file, which allows for creating filters
|
||||
* and hooking functions, and methods. The functions or methods will be run when
|
||||
* the filter is called.
|
||||
*
|
||||
* Any of the syntaxes explained in the PHP documentation for the
|
||||
* {@link https://www.php.net/manual/en/language.types.callable.php 'callback'}
|
||||
* type are valid.
|
||||
*
|
||||
* This API is heavily inspired by the one I implemented in Zenphoto 1.3, which was heavily inspired by the one used in WordPress.
|
||||
*
|
||||
* @author Ozh
|
||||
* @since 1.5
|
||||
*/
|
||||
|
||||
/**
|
||||
* This global var will collect filters with the following structure:
|
||||
* $yourls_filters['hook']['array of priorities']['serialized function names']['array of ['array (functions, accepted_args, filter or action)]']
|
||||
*
|
||||
* Real life example :
|
||||
* print_r($yourls_filters) :
|
||||
* Array
|
||||
* (
|
||||
* [plugins_loaded] => Array
|
||||
* (
|
||||
* [10] => Array
|
||||
* (
|
||||
* [yourls_kses_init] => Array
|
||||
* (
|
||||
* [function] => yourls_kses_init
|
||||
* [accepted_args] => 1
|
||||
* [type] => action
|
||||
* )
|
||||
* [yourls_tzp_config] => Array
|
||||
* (
|
||||
* [function] => yourls_tzp_config
|
||||
* [accepted_args] => 1
|
||||
* [type] => action
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* [admin_menu] => Array
|
||||
* (
|
||||
* [10] => Array
|
||||
* (
|
||||
* [ozh_show_db] => Array
|
||||
* (
|
||||
* [function] => ozh_show_db
|
||||
* [accepted_args] =>
|
||||
* [type] => filter
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* @var array $yourls_filters
|
||||
*/
|
||||
if ( !isset( $yourls_filters ) ) {
|
||||
$yourls_filters = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This global var will collect 'done' actions with the following structure:
|
||||
* $yourls_actions['hook'] => number of time this action was done
|
||||
*
|
||||
* @var array $yourls_actions
|
||||
*/
|
||||
if ( !isset( $yourls_actions ) ) {
|
||||
$yourls_actions = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a filtering function
|
||||
*
|
||||
* Typical use:
|
||||
* yourls_add_filter('some_hook', 'function_handler_for_hook');
|
||||
*
|
||||
* @link https://docs.yourls.org/development/plugins.html
|
||||
* @param string $hook the name of the YOURLS element to be filtered or YOURLS action to be triggered
|
||||
* @param callable $function_name the name of the function that is to be called.
|
||||
* @param int $priority optional. Used to specify the order in which the functions associated with a
|
||||
* particular action are executed (default=10, lower=earlier execution, and functions
|
||||
* with the same priority are executed in the order in which they were added to the
|
||||
* filter)
|
||||
* @param int $accepted_args optional. The number of arguments the function accept (default is the number
|
||||
* provided).
|
||||
* @param string $type
|
||||
* @global array $yourls_filters Storage for all of the filters
|
||||
* @return void
|
||||
*/
|
||||
function yourls_add_filter( $hook, $function_name, $priority = 10, $accepted_args = NULL, $type = 'filter' ) {
|
||||
global $yourls_filters;
|
||||
// At this point, we cannot check if the function exists, as it may well be defined later (which is OK)
|
||||
$id = yourls_filter_unique_id($function_name);
|
||||
|
||||
$yourls_filters[ $hook ][ $priority ][ $id ] = [
|
||||
'function' => $function_name,
|
||||
'accepted_args' => $accepted_args,
|
||||
'type' => $type,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks a function on to a specific action.
|
||||
*
|
||||
* Actions are the hooks that YOURLS launches at specific points
|
||||
* during execution, or when specific events occur. Plugins can specify that
|
||||
* one or more of its PHP functions are executed at these points, using the
|
||||
* Action API.
|
||||
*
|
||||
* Typical use:
|
||||
* yourls_add_action('some_hook', 'function_handler_for_hook');
|
||||
*
|
||||
* @link https://docs.yourls.org/development/plugins.html
|
||||
* @param string $hook The name of the action to which the $function_to_add is hooked.
|
||||
* @param callable $function_name The name of the function you wish to be called.
|
||||
* @param int $priority Optional. Used to specify the order in which the functions associated with a particular action
|
||||
* are executed (default: 10). Lower numbers correspond with earlier execution, and functions
|
||||
* with the same priority are executed in the order in which they were added to the action.
|
||||
* @param int $accepted_args Optional. The number of arguments the function accept (default 1).
|
||||
* @return void
|
||||
*/
|
||||
function yourls_add_action( $hook, $function_name, $priority = 10, $accepted_args = 1 ) {
|
||||
yourls_add_filter( $hook, $function_name, $priority, $accepted_args, 'action' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Unique ID for storage and retrieval.
|
||||
*
|
||||
* Simply using a function name is not enough, as several functions can have the same name when they are enclosed in classes.
|
||||
* Possible ways to attach a function to a hook (filter or action):
|
||||
* - strings:
|
||||
* yourls_add_filter('my_hook_test', 'my_callback_function');
|
||||
* yourls_add_filter('my_hook_test', 'My_Class::my_callback_function');
|
||||
*
|
||||
* - arrays:
|
||||
* yourls_add_filter('my_hook_test', array('My_Class','my_callback_function'));
|
||||
* yourls_add_filter('my_hook_test', array($class_instance, 'my_callback_function'));
|
||||
*
|
||||
* - objects:
|
||||
* yourls_add_filter('my_hook_test', $class_instance_with_invoke_method);
|
||||
* yourls_add_filter('my_hook_test', $my_callback_function);
|
||||
*
|
||||
* @link https://docs.yourls.org/development/hooks.html
|
||||
* @param string|array|object $function The callable used in a filter or action.
|
||||
* @return string unique ID for usage as array key
|
||||
*/
|
||||
function yourls_filter_unique_id($function) {
|
||||
// If given a string (function name)
|
||||
if ( is_string( $function ) ) {
|
||||
return $function;
|
||||
}
|
||||
|
||||
if ( is_object( $function ) ) {
|
||||
// Closures are implemented as objects
|
||||
$function = [ $function, '' ];
|
||||
}
|
||||
else {
|
||||
$function = (array)$function;
|
||||
}
|
||||
|
||||
// Object Class Calling
|
||||
if ( is_object( $function[0] ) ) {
|
||||
return spl_object_hash( $function[0] ).$function[1];
|
||||
}
|
||||
|
||||
// Last case, static Calling : $function[0] is a string (Class Name) and $function[1] is a string (Method Name)
|
||||
return $function[0].'::'.$function[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a filtering operation on a value or an event.
|
||||
*
|
||||
* Typical use:
|
||||
*
|
||||
* 1) Modify a variable if a function is attached to hook 'yourls_hook'
|
||||
* $yourls_var = "default value";
|
||||
* $yourls_var = yourls_apply_filter( 'yourls_hook', $yourls_var );
|
||||
*
|
||||
* 2) Trigger functions is attached to event 'yourls_event'
|
||||
* yourls_apply_filter( 'yourls_event' );
|
||||
* (see yourls_do_action() )
|
||||
*
|
||||
* Returns a value which may have been modified by a filter.
|
||||
*
|
||||
* @global array $yourls_filters storage for all of the filters
|
||||
* @param string $hook the name of the YOURLS element or action
|
||||
* @param mixed $value the value of the element before filtering
|
||||
* @param true|mixed $is_action true if the function is called by yourls_do_action() - otherwise may be the second parameter of an arbitrary number of parameters
|
||||
* @return mixed
|
||||
*/
|
||||
function yourls_apply_filter( $hook, $value = '', $is_action = false ) {
|
||||
global $yourls_filters;
|
||||
|
||||
$args = func_get_args();
|
||||
|
||||
// Do 'all' filters first. We check if $is_action to avoid calling `yourls_call_all_hooks()` twice
|
||||
if ( !$is_action && isset($yourls_filters['all']) ) {
|
||||
yourls_call_all_hooks('filter', $hook, $args);
|
||||
}
|
||||
|
||||
// If we have no hook attached to that filter, just return unmodified $value
|
||||
if ( !isset( $yourls_filters[ $hook ] ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Sort filters by priority
|
||||
ksort( $yourls_filters[ $hook ] );
|
||||
|
||||
// Loops through each filter
|
||||
reset( $yourls_filters[ $hook ] );
|
||||
do {
|
||||
foreach ( (array)current( $yourls_filters[ $hook ] ) as $the_ ) {
|
||||
$_value = '';
|
||||
if ( !is_null($the_[ 'function' ]) ) {
|
||||
$args[ 1 ] = $value;
|
||||
$count = $the_[ 'accepted_args' ];
|
||||
if ( is_null( $count ) ) {
|
||||
$_value = call_user_func_array( $the_[ 'function' ], array_slice( $args, 1 ) );
|
||||
}
|
||||
else {
|
||||
$_value = call_user_func_array( $the_[ 'function' ], array_slice( $args, 1, (int)$count ) );
|
||||
}
|
||||
}
|
||||
if ( $the_[ 'type' ] == 'filter' ) {
|
||||
$value = $_value;
|
||||
}
|
||||
}
|
||||
} while ( next( $yourls_filters[ $hook ] ) !== false );
|
||||
|
||||
// Return the value - this will be actually used only for filters, not for actions (see `yourls_do_action()`)
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an action triggered by a YOURLS event.
|
||||
*
|
||||
* @param string $hook the name of the YOURLS action
|
||||
* @param mixed $arg action arguments
|
||||
* @return void
|
||||
*/
|
||||
function yourls_do_action( $hook, $arg = '' ) {
|
||||
global $yourls_actions, $yourls_filters;
|
||||
|
||||
// Keep track of actions that are "done"
|
||||
if ( !isset( $yourls_actions ) ) {
|
||||
$yourls_actions = [];
|
||||
}
|
||||
if ( !isset( $yourls_actions[ $hook ] ) ) {
|
||||
$yourls_actions[ $hook ] = 1;
|
||||
}
|
||||
else {
|
||||
++$yourls_actions[ $hook ];
|
||||
}
|
||||
|
||||
$args = [];
|
||||
if ( is_array( $arg ) && 1 == count( $arg ) && isset( $arg[ 0 ] ) && is_object( $arg[ 0 ] ) ) { // array(&$this)
|
||||
$args[] =& $arg[ 0 ];
|
||||
}
|
||||
else {
|
||||
$args[] = $arg;
|
||||
}
|
||||
|
||||
for ( $a = 2 ; $a < func_num_args() ; $a++ ) {
|
||||
$args[] = func_get_arg( $a );
|
||||
}
|
||||
|
||||
// Do 'all' actions first
|
||||
if ( isset($yourls_filters['all']) ) {
|
||||
yourls_call_all_hooks('action', $hook, $args);
|
||||
}
|
||||
|
||||
yourls_apply_filter( $hook, $args, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number times an action is fired.
|
||||
*
|
||||
* @param string $hook Name of the action hook.
|
||||
* @return int The number of times action hook <tt>$hook</tt> is fired
|
||||
*/
|
||||
function yourls_did_action( $hook ) {
|
||||
global $yourls_actions;
|
||||
return empty( $yourls_actions[ $hook ] ) ? 0 : $yourls_actions[ $hook ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the 'all' hook, with all of the arguments or parameters that were used for the hook
|
||||
*
|
||||
* Internal function used by yourls_do_action() and yourls_apply_filter() - not meant to be used from
|
||||
* outside these functions.
|
||||
* This is mostly a debugging function to understand the flow of events.
|
||||
* See https://docs.yourls.org/development/debugging.html to learn how to use the 'all' hook
|
||||
*
|
||||
* @link https://docs.yourls.org/development/debugging.html
|
||||
* @since 1.8.1
|
||||
* @param string $type Either 'action' or 'filter'
|
||||
* @param string $hook The hook name, eg 'plugins_loaded'
|
||||
* @param mixed $args Variable-length argument lists that were passed to the action or filter
|
||||
* @return void
|
||||
*/
|
||||
function yourls_call_all_hooks($type, $hook, ...$args) {
|
||||
global $yourls_filters;
|
||||
|
||||
// Loops through each filter or action hooked with the 'all' hook
|
||||
reset( $yourls_filters['all'] );
|
||||
do {
|
||||
foreach ( (array) current($yourls_filters['all']) as $the_ )
|
||||
// Call the hooked function only if it's hooked to the current type of hook (eg 'filter' or 'action')
|
||||
if ( $the_['type'] == $type && !is_null($the_['function']) ) {
|
||||
call_user_func_array( $the_['function'], array($type, $hook, $args) );
|
||||
/**
|
||||
* Note that we don't return a value here, regardless of $type being an action (obviously) but also
|
||||
* a filter. Indeed it would not make sense to actually "filter" and return values when we're
|
||||
* feeding the same function every single hook in YOURLS, no matter their parameters.
|
||||
*/
|
||||
}
|
||||
|
||||
} while ( next($yourls_filters['all']) !== false );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a function from a specified filter hook.
|
||||
*
|
||||
* This function removes a function attached to a specified filter hook. This
|
||||
* method can be used to remove default functions attached to a specific filter
|
||||
* hook and possibly replace them with a substitute.
|
||||
*
|
||||
* To remove a hook, the $function_to_remove and $priority arguments must match
|
||||
* when the hook was added.
|
||||
*
|
||||
* @global array $yourls_filters storage for all of the filters
|
||||
* @param string $hook The filter hook to which the function to be removed is hooked.
|
||||
* @param callable $function_to_remove The name of the function which should be removed.
|
||||
* @param int $priority optional. The priority of the function (default: 10).
|
||||
* @return bool Whether the function was registered as a filter before it was removed.
|
||||
*/
|
||||
function yourls_remove_filter( $hook, $function_to_remove, $priority = 10 ) {
|
||||
global $yourls_filters;
|
||||
|
||||
$function_to_remove = yourls_filter_unique_id($function_to_remove);
|
||||
|
||||
$remove = isset( $yourls_filters[ $hook ][ $priority ][ $function_to_remove ] );
|
||||
|
||||
if ( $remove === true ) {
|
||||
unset ( $yourls_filters[ $hook ][ $priority ][ $function_to_remove ] );
|
||||
if ( empty( $yourls_filters[ $hook ][ $priority ] ) ) {
|
||||
unset( $yourls_filters[ $hook ] );
|
||||
}
|
||||
}
|
||||
return $remove;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a function from a specified action hook.
|
||||
*
|
||||
* @see yourls_remove_filter()
|
||||
* @since 1.7.1
|
||||
* @param string $hook The action hook to which the function to be removed is hooked.
|
||||
* @param callable $function_to_remove The name of the function which should be removed.
|
||||
* @param int $priority optional. The priority of the function (default: 10).
|
||||
* @return bool Whether the function was registered as an action before it was removed.
|
||||
*/
|
||||
function yourls_remove_action( $hook, $function_to_remove, $priority = 10 ) {
|
||||
return yourls_remove_filter( $hook, $function_to_remove, $priority );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all functions from a specified action hook.
|
||||
*
|
||||
* @see yourls_remove_all_filters()
|
||||
* @since 1.7.1
|
||||
* @param string $hook The action to remove hooks from
|
||||
* @param int|false $priority optional. The priority of the functions to remove
|
||||
* @return bool true when it's finished
|
||||
*/
|
||||
function yourls_remove_all_actions( $hook, $priority = false ) {
|
||||
return yourls_remove_all_filters( $hook, $priority );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all functions from a specified filter hook.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @param string $hook The filter to remove hooks from
|
||||
* @param int|false $priority optional. The priority of the functions to remove
|
||||
* @return bool true when it's finished
|
||||
*/
|
||||
function yourls_remove_all_filters( $hook, $priority = false ) {
|
||||
global $yourls_filters;
|
||||
|
||||
if ( isset( $yourls_filters[ $hook ] ) ) {
|
||||
if ( $priority === false ) {
|
||||
unset( $yourls_filters[ $hook ] );
|
||||
}
|
||||
elseif ( isset( $yourls_filters[ $hook ][ $priority ] ) ) {
|
||||
unset( $yourls_filters[ $hook ][ $priority ] );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return filters for a specific hook.
|
||||
*
|
||||
* If hook has filters (or actions, see yourls_has_action()), this will return an array priorities => callbacks.
|
||||
* See the structure of yourls_filters on top of this file for details.
|
||||
*
|
||||
* @since 1.8.3
|
||||
* @param string $hook The hook to retrieve filters for
|
||||
* @return array
|
||||
*/
|
||||
function yourls_get_filters($hook) {
|
||||
global $yourls_filters;
|
||||
return $yourls_filters[$hook] ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return actions for a specific hook.
|
||||
*
|
||||
* @since 1.8.3
|
||||
* @param string $hook The hook to retrieve actions for
|
||||
* @return array
|
||||
*/
|
||||
function yourls_get_actions($hook) {
|
||||
return yourls_get_filters($hook);
|
||||
}
|
||||
/**
|
||||
* Check if any filter has been registered for a hook.
|
||||
*
|
||||
* @since 1.5
|
||||
* @global array $yourls_filters storage for all of the filters
|
||||
* @param string $hook The name of the filter hook.
|
||||
* @param callable|false $function_to_check optional. If specified, return the priority of that function on this hook or false if not attached.
|
||||
* @return int|bool Optionally returns the priority on that hook for the specified function.
|
||||
*/
|
||||
function yourls_has_filter( $hook, $function_to_check = false ) {
|
||||
global $yourls_filters;
|
||||
|
||||
$has = !empty( $yourls_filters[ $hook ] );
|
||||
if ( false === $function_to_check || false === $has ) {
|
||||
return $has;
|
||||
}
|
||||
|
||||
if ( !$idx = yourls_filter_unique_id($function_to_check) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( array_keys( $yourls_filters[ $hook ] ) as $priority ) {
|
||||
if ( isset( $yourls_filters[ $hook ][ $priority ][ $idx ] ) ) {
|
||||
return $priority;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if any action has been registered for a hook.
|
||||
*
|
||||
* @since 1.5
|
||||
* @param string $hook
|
||||
* @param callable|false $function_to_check
|
||||
* @return bool|int
|
||||
*/
|
||||
function yourls_has_action( $hook, $function_to_check = false ) {
|
||||
return yourls_has_filter( $hook, $function_to_check );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of active plugins
|
||||
*
|
||||
* @return int Number of activated plugins
|
||||
*/
|
||||
function yourls_has_active_plugins() {
|
||||
return count( yourls_get_db()->get_plugins() );
|
||||
}
|
||||
|
||||
/**
|
||||
* List plugins in /user/plugins
|
||||
*
|
||||
* @return array Array of [/plugindir/plugin.php]=>array('Name'=>'Ozh', 'Title'=>'Hello', )
|
||||
*/
|
||||
function yourls_get_plugins() {
|
||||
$plugins = (array)glob( YOURLS_PLUGINDIR.'/*/plugin.php' );
|
||||
|
||||
if ( is_array( $plugins ) ) {
|
||||
foreach ( $plugins as $key => $plugin ) {
|
||||
$plugins[ yourls_plugin_basename( $plugin ) ] = yourls_get_plugin_data( $plugin );
|
||||
unset( $plugins[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
return empty( $plugins ) ? [] : $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a plugin is active
|
||||
*
|
||||
* @param string $plugin Physical path to plugin file
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_is_active_plugin( $plugin ) {
|
||||
return yourls_has_active_plugins() > 0 ?
|
||||
in_array( yourls_plugin_basename( $plugin ), yourls_get_db()->get_plugins() )
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a plugin header
|
||||
*
|
||||
* The plugin header has the following form:
|
||||
* /*
|
||||
* Plugin Name: <plugin name>
|
||||
* Plugin URI: <plugin home page>
|
||||
* Description: <plugin description>
|
||||
* Version: <plugin version number>
|
||||
* Author: <author name>
|
||||
* Author URI: <author home page>
|
||||
* * /
|
||||
*
|
||||
* Or in the form of a phpdoc block
|
||||
* /**
|
||||
* * Plugin Name: <plugin name>
|
||||
* * Plugin URI: <plugin home page>
|
||||
* * Description: <plugin description>
|
||||
* * Version: <plugin version number>
|
||||
* * Author: <author name>
|
||||
* * Author URI: <author home page>
|
||||
* * /
|
||||
*
|
||||
* @since 1.5
|
||||
* @param string $file Physical path to plugin file
|
||||
* @return array Array of 'Field'=>'Value' from plugin comment header lines of the form "Field: Value"
|
||||
*/
|
||||
function yourls_get_plugin_data( $file ) {
|
||||
$fp = fopen( $file, 'r' ); // assuming $file is readable, since yourls_load_plugins() filters this
|
||||
$data = fread( $fp, 8192 ); // get first 8kb
|
||||
fclose( $fp );
|
||||
|
||||
// Capture all the header within first comment block
|
||||
if ( !preg_match( '!.*?/\*(.*?)\*/!ms', $data, $matches ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Capture each line with "Something: some text"
|
||||
unset( $data );
|
||||
$lines = preg_split( "[\n|\r]", $matches[ 1 ] );
|
||||
unset( $matches );
|
||||
|
||||
$plugin_data = [];
|
||||
foreach ( $lines as $line ) {
|
||||
if ( !preg_match( '!(\s*)?\*?(\s*)?(.*?):\s+(.*)!', $line, $matches ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$plugin_data[ trim($matches[3]) ] = yourls_esc_html(trim($matches[4]));
|
||||
}
|
||||
|
||||
return $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include active plugins
|
||||
*
|
||||
* This function includes every 'YOURLS_PLUGINDIR/plugin_name/plugin.php' found in option 'active_plugins'
|
||||
* It will return a diagnosis array with the following keys:
|
||||
* (bool)'loaded' : true if plugin(s) loaded, false otherwise
|
||||
* (string)'info' : extra information
|
||||
*
|
||||
* @since 1.5
|
||||
* @return array Array('loaded' => bool, 'info' => string)
|
||||
*/
|
||||
function yourls_load_plugins() {
|
||||
// Don't load plugins when installing or updating
|
||||
if ( yourls_is_installing() OR yourls_is_upgrading() OR !yourls_is_installed() ) {
|
||||
return [
|
||||
'loaded' => false,
|
||||
'info' => 'install/upgrade'
|
||||
];
|
||||
}
|
||||
|
||||
$active_plugins = (array)yourls_get_option( 'active_plugins' );
|
||||
if ( empty( $active_plugins ) ) {
|
||||
return [
|
||||
'loaded' => false,
|
||||
'info' => 'no active plugin'
|
||||
];
|
||||
}
|
||||
|
||||
$plugins = [];
|
||||
foreach ( $active_plugins as $key => $plugin ) {
|
||||
$file = YOURLS_PLUGINDIR . '/' . $plugin;
|
||||
if ( yourls_is_a_plugin_file($file) && yourls_activate_plugin_sandbox( $file ) === true ) {
|
||||
$plugins[] = $plugin;
|
||||
unset( $active_plugins[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
// Replace active plugin list with list of plugins we just activated
|
||||
yourls_get_db()->set_plugins( $plugins );
|
||||
$info = count( $plugins ).' activated';
|
||||
|
||||
// $active_plugins should be empty now, if not, a plugin could not be found, or is erroneous : remove it
|
||||
$missing_count = count( $active_plugins );
|
||||
if ( $missing_count > 0 ) {
|
||||
yourls_update_option( 'active_plugins', $plugins );
|
||||
$message = yourls_n( 'Could not find and deactivate plugin :', 'Could not find and deactivate plugins :', $missing_count );
|
||||
$missing = '<strong>'.implode( '</strong>, <strong>', $active_plugins ).'</strong>';
|
||||
yourls_add_notice( $message.' '.$missing );
|
||||
$info .= ', '.$missing_count.' removed';
|
||||
}
|
||||
|
||||
return [
|
||||
'loaded' => true,
|
||||
'info' => $info
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a file is a plugin file
|
||||
*
|
||||
* This doesn't check if the file is a valid PHP file, only that it's correctly named.
|
||||
*
|
||||
* @since 1.5
|
||||
* @param string $file Full pathname to a file
|
||||
* @return bool
|
||||
*/
|
||||
function yourls_is_a_plugin_file($file) {
|
||||
return false === strpos( $file, '..' )
|
||||
&& false === strpos( $file, './' )
|
||||
&& 'plugin.php' === substr( $file, -10 )
|
||||
&& is_readable( $file );
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a plugin
|
||||
*
|
||||
* @since 1.5
|
||||
* @param string $plugin Plugin filename (full or relative to plugins directory)
|
||||
* @return string|true string if error or true if success
|
||||
*/
|
||||
function yourls_activate_plugin( $plugin ) {
|
||||
// validate file
|
||||
$plugin = yourls_plugin_basename( $plugin );
|
||||
$plugindir = yourls_sanitize_filename( YOURLS_PLUGINDIR );
|
||||
if ( !yourls_is_a_plugin_file($plugindir . '/' . $plugin ) ) {
|
||||
return yourls__( 'Not a valid plugin file' );
|
||||
}
|
||||
|
||||
// check not activated already
|
||||
$ydb = yourls_get_db();
|
||||
if ( yourls_is_active_plugin( $plugin ) ) {
|
||||
return yourls__( 'Plugin already activated' );
|
||||
}
|
||||
|
||||
// attempt activation.
|
||||
$attempt = yourls_activate_plugin_sandbox( $plugindir.'/'.$plugin );
|
||||
if( $attempt !== true ) {
|
||||
return yourls_s( 'Plugin generated unexpected output. Error was: <br/><pre>%s</pre>', $attempt );
|
||||
}
|
||||
|
||||
// so far, so good: update active plugin list
|
||||
$ydb->add_plugin( $plugin );
|
||||
yourls_update_option( 'active_plugins', $ydb->get_plugins() );
|
||||
yourls_do_action( 'activated_plugin', $plugin );
|
||||
yourls_do_action( 'activated_'.$plugin );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin activation sandbox
|
||||
*
|
||||
* @since 1.8.3
|
||||
* @param string $pluginfile Plugin filename (full path)
|
||||
* @return string|true string if error or true if success
|
||||
*/
|
||||
function yourls_activate_plugin_sandbox( $pluginfile ) {
|
||||
try {
|
||||
include_once $pluginfile;
|
||||
return true;
|
||||
} catch ( \Throwable $e ) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate a plugin
|
||||
*
|
||||
* @since 1.5
|
||||
* @param string $plugin Plugin filename (full relative to plugins directory)
|
||||
* @return string|true string if error or true if success
|
||||
*/
|
||||
function yourls_deactivate_plugin( $plugin ) {
|
||||
$plugin = yourls_plugin_basename( $plugin );
|
||||
|
||||
// Check plugin is active
|
||||
if ( !yourls_is_active_plugin( $plugin ) ) {
|
||||
return yourls__( 'Plugin not active' );
|
||||
}
|
||||
|
||||
// Check if we have an uninstall file - load if so
|
||||
$uninst_file = YOURLS_PLUGINDIR . '/' . dirname($plugin) . '/uninstall.php';
|
||||
if ( file_exists($uninst_file) ) {
|
||||
define('YOURLS_UNINSTALL_PLUGIN', true);
|
||||
$attempt = yourls_activate_plugin_sandbox( $uninst_file );
|
||||
if( $attempt !== true ) {
|
||||
return yourls_s( 'Plugin generated unexpected output. Error was: <br/><pre>%s</pre>', $attempt );
|
||||
}
|
||||
}
|
||||
|
||||
// Deactivate the plugin
|
||||
$ydb = yourls_get_db();
|
||||
$plugins = $ydb->get_plugins();
|
||||
$key = array_search( $plugin, $plugins );
|
||||
if ( $key !== false ) {
|
||||
array_splice( $plugins, $key, 1 );
|
||||
}
|
||||
|
||||
$ydb->set_plugins( $plugins );
|
||||
yourls_update_option( 'active_plugins', $plugins );
|
||||
yourls_do_action( 'deactivated_plugin', $plugin );
|
||||
yourls_do_action( 'deactivated_'.$plugin );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path of a plugin file, relative to the plugins directory
|
||||
*
|
||||
* @since 1.5
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
function yourls_plugin_basename( $file ) {
|
||||
return trim( str_replace( yourls_sanitize_filename( YOURLS_PLUGINDIR ), '', yourls_sanitize_filename( $file ) ), '/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL of the directory a plugin
|
||||
*
|
||||
* @since 1.5
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
function yourls_plugin_url( $file ) {
|
||||
$url = YOURLS_PLUGINURL.'/'.yourls_plugin_basename( $file );
|
||||
if ( yourls_is_ssl() or yourls_needs_ssl() ) {
|
||||
$url = str_replace( 'http://', 'https://', $url );
|
||||
}
|
||||
return (string)yourls_apply_filter( 'plugin_url', $url, $file );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build list of links to plugin admin pages, if any
|
||||
*
|
||||
* @since 1.5
|
||||
* @return array Array of arrays of URL and anchor of plugin admin pages, or empty array if no plugin page
|
||||
*/
|
||||
function yourls_list_plugin_admin_pages() {
|
||||
$plugin_links = [];
|
||||
foreach ( yourls_get_db()->get_plugin_pages() as $plugin => $page ) {
|
||||
$plugin_links[ $plugin ] = [
|
||||
'url' => yourls_admin_url( 'plugins.php?page='.$page[ 'slug' ] ),
|
||||
'anchor' => $page[ 'title' ],
|
||||
];
|
||||
}
|
||||
return $plugin_links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a plugin administration page
|
||||
*
|
||||
* @since 1.5
|
||||
* @param string $slug
|
||||
* @param string $title
|
||||
* @param callable $function
|
||||
* @return void
|
||||
*/
|
||||
function yourls_register_plugin_page( $slug, $title, $function ) {
|
||||
yourls_get_db()->add_plugin_page( $slug, $title, $function );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle plugin administration page
|
||||
*
|
||||
* @since 1.5
|
||||
* @param string $plugin_page
|
||||
* @return void
|
||||
*/
|
||||
function yourls_plugin_admin_page( $plugin_page ) {
|
||||
// Check the plugin page is actually registered
|
||||
$pages = yourls_get_db()->get_plugin_pages();
|
||||
if ( !isset( $pages[ $plugin_page ] ) ) {
|
||||
yourls_die( yourls__( 'This page does not exist. Maybe a plugin you thought was activated is inactive?' ), yourls__( 'Invalid link' ) );
|
||||
}
|
||||
|
||||
// Check the plugin page function is actually callable
|
||||
$page_function = $pages[ $plugin_page ][ 'function' ];
|
||||
if (!is_callable($page_function)) {
|
||||
yourls_die( yourls__( 'This page cannot be displayed because the displaying function is not callable.' ), yourls__( 'Invalid code' ) );
|
||||
}
|
||||
|
||||
// Draw the page itself
|
||||
yourls_do_action( 'load-'.$plugin_page );
|
||||
yourls_html_head( 'plugin_page_'.$plugin_page, $pages[ $plugin_page ][ 'title' ] );
|
||||
yourls_html_logo();
|
||||
yourls_html_menu();
|
||||
|
||||
$page_function( );
|
||||
|
||||
yourls_html_footer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function: Sort plugins
|
||||
*
|
||||
* @link http://php.net/uasort
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @since 1.5
|
||||
* @param array $plugin_a
|
||||
* @param array $plugin_b
|
||||
* @return int 0, 1 or -1, see uasort()
|
||||
*/
|
||||
function yourls_plugins_sort_callback( $plugin_a, $plugin_b ) {
|
||||
$orderby = yourls_apply_filter( 'plugins_sort_callback', 'Plugin Name' );
|
||||
$order = yourls_apply_filter( 'plugins_sort_callback', 'ASC' );
|
||||
|
||||
$a = isset( $plugin_a[ $orderby ] ) ? $plugin_a[ $orderby ] : '';
|
||||
$b = isset( $plugin_b[ $orderby ] ) ? $plugin_b[ $orderby ] : '';
|
||||
|
||||
if ( $a == $b ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( 'DESC' == $order ) {
|
||||
return ( $a < $b ) ? 1 : -1;
|
||||
}
|
||||
else {
|
||||
return ( $a < $b ) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown function, runs just before PHP shuts down execution. Stolen from WP
|
||||
*
|
||||
* This function is automatically tied to the script execution end at startup time, see
|
||||
* var $actions->register_shutdown in includes/Config/Init.php
|
||||
*
|
||||
* You can use this function to fire one or several actions when the PHP execution ends.
|
||||
* Example of use:
|
||||
* yourls_add_action('shutdown', 'my_plugin_action_this');
|
||||
* yourls_add_action('shutdown', 'my_plugin_action_that');
|
||||
* // functions my_plugin_action_this() and my_plugin_action_that() will be triggered
|
||||
* // after YOURLS is completely executed
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @since 1.5.1
|
||||
* @return void
|
||||
*/
|
||||
function yourls_shutdown() {
|
||||
yourls_do_action( 'shutdown' );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 '';
|
||||
}
|
|
@ -0,0 +1,636 @@
|
|||
<?php
|
||||
/*
|
||||
* Functions relative to short URLs: adding, editing, etc
|
||||
* (either proper short URLs ("http://sho.rt/abc") or "keywords" (the "abc" part)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Add a new link in the DB, either with custom keyword, or find one
|
||||
*
|
||||
* The return array will contain at least the following keys:
|
||||
* status: string, 'success' or 'fail'
|
||||
* message: string, a descriptive localized message of what happened in any case
|
||||
* code: string, a short descriptivish and untranslated message describing what happened
|
||||
* errorCode: string, a HTTP status code
|
||||
* statusCode: string, a HTTP status code
|
||||
* Depending on the operation, it will contain any of the following keys:
|
||||
* url: array, the short URL creation information, with keys: 'keyword', 'url', 'title', 'date', 'ip', 'clicks'
|
||||
* title: string, the URL title
|
||||
* shorturl: string, the proper short URL in full (eg 'http://sho.rt/abc')
|
||||
* html: string, the HTML part used by the ajax to update the page display if any
|
||||
*
|
||||
* For compatibility with early consumers and third parties, when people asked for various data and data formats
|
||||
* before the internal API was really structured, the return array now collects several redundant information.
|
||||
*
|
||||
* @param string $url URL to shorten
|
||||
* @param string $keyword optional "keyword"
|
||||
* @param string $title option title
|
||||
* @return array array with error/success state and short URL information
|
||||
*/
|
||||
function yourls_add_new_link( $url, $keyword = '', $title = '' ) {
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_add_new_link', false, $url, $keyword, $title );
|
||||
if ( false !== $pre ) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
/**
|
||||
* The result array.
|
||||
*/
|
||||
$return = [
|
||||
// Always present :
|
||||
'status' => '',
|
||||
'code' => '',
|
||||
'message' => '',
|
||||
'errorCode' => '',
|
||||
'statusCode' => '',
|
||||
];
|
||||
|
||||
// Sanitize URL
|
||||
$url = yourls_sanitize_url( $url );
|
||||
if ( !$url || $url == 'http://' || $url == 'https://' ) {
|
||||
$return['status'] = 'fail';
|
||||
$return['code'] = 'error:nourl';
|
||||
$return['message'] = yourls__( 'Missing or malformed URL' );
|
||||
$return['errorCode'] = $return['statusCode'] = '400'; // 400 Bad Request
|
||||
|
||||
return yourls_apply_filter( 'add_new_link_fail_nourl', $return, $url, $keyword, $title );
|
||||
}
|
||||
|
||||
// Prevent DB flood
|
||||
$ip = yourls_get_IP();
|
||||
yourls_check_IP_flood( $ip );
|
||||
|
||||
// Prevent internal redirection loops: cannot shorten a shortened URL
|
||||
if (yourls_is_shorturl($url)) {
|
||||
$return['status'] = 'fail';
|
||||
$return['code'] = 'error:noloop';
|
||||
$return['message'] = yourls__( 'URL is a short URL' );
|
||||
$return['errorCode'] = $return['statusCode'] = '400'; // 400 Bad Request
|
||||
return yourls_apply_filter( 'add_new_link_fail_noloop', $return, $url, $keyword, $title );
|
||||
}
|
||||
|
||||
yourls_do_action( 'pre_add_new_link', $url, $keyword, $title );
|
||||
|
||||
// Check if URL was already stored and we don't accept duplicates
|
||||
if ( !yourls_allow_duplicate_longurls() && ($url_exists = yourls_long_url_exists( $url )) ) {
|
||||
yourls_do_action( 'add_new_link_already_stored', $url, $keyword, $title );
|
||||
|
||||
$return['status'] = 'fail';
|
||||
$return['code'] = 'error:url';
|
||||
$return['url'] = array( 'keyword' => $url_exists->keyword, 'url' => $url, 'title' => $url_exists->title, 'date' => $url_exists->timestamp, 'ip' => $url_exists->ip, 'clicks' => $url_exists->clicks );
|
||||
$return['message'] = /* //translators: eg "http://someurl/ already exists (short URL: sho.rt/abc)" */ yourls_s('%s already exists in database (short URL: %s)',
|
||||
yourls_trim_long_string($url), preg_replace('!https?://!', '', yourls_get_yourls_site()) . '/'. $url_exists->keyword );
|
||||
$return['title'] = $url_exists->title;
|
||||
$return['shorturl'] = yourls_link($url_exists->keyword);
|
||||
$return['errorCode'] = $return['statusCode'] = '400'; // 400 Bad Request
|
||||
|
||||
return yourls_apply_filter( 'add_new_link_already_stored_filter', $return, $url, $keyword, $title );
|
||||
}
|
||||
|
||||
// Sanitize provided title, or fetch one
|
||||
if( isset( $title ) && !empty( $title ) ) {
|
||||
$title = yourls_sanitize_title( $title );
|
||||
} else {
|
||||
$title = yourls_get_remote_title( $url );
|
||||
}
|
||||
$title = yourls_apply_filter( 'add_new_title', $title, $url, $keyword );
|
||||
|
||||
// Custom keyword provided : sanitize and make sure it's free
|
||||
if ($keyword) {
|
||||
yourls_do_action( 'add_new_link_custom_keyword', $url, $keyword, $title );
|
||||
|
||||
$keyword = yourls_sanitize_keyword( $keyword, true );
|
||||
$keyword = yourls_apply_filter( 'custom_keyword', $keyword, $url, $title );
|
||||
|
||||
if ( !yourls_keyword_is_free( $keyword ) ) {
|
||||
// This shorturl either reserved or taken already
|
||||
$return['status'] = 'fail';
|
||||
$return['code'] = 'error:keyword';
|
||||
$return['message'] = yourls_s( 'Short URL %s already exists in database or is reserved', $keyword );
|
||||
$return['errorCode'] = $return['statusCode'] = '400'; // 400 Bad Request
|
||||
|
||||
return yourls_apply_filter( 'add_new_link_keyword_exists', $return, $url, $keyword, $title );
|
||||
}
|
||||
|
||||
// Create random keyword
|
||||
} else {
|
||||
yourls_do_action( 'add_new_link_create_keyword', $url, $keyword, $title );
|
||||
|
||||
$id = yourls_get_next_decimal();
|
||||
|
||||
do {
|
||||
$keyword = yourls_int2string( $id );
|
||||
$keyword = yourls_apply_filter( 'random_keyword', $keyword, $url, $title );
|
||||
$id++;
|
||||
} while ( !yourls_keyword_is_free($keyword) );
|
||||
|
||||
yourls_update_next_decimal($id);
|
||||
}
|
||||
|
||||
// We should be all set now. Store the short URL !
|
||||
|
||||
$timestamp = date( 'Y-m-d H:i:s' );
|
||||
|
||||
try {
|
||||
if (yourls_insert_link_in_db( $url, $keyword, $title )){
|
||||
// everything ok, populate needed vars
|
||||
$return['url'] = array('keyword' => $keyword, 'url' => $url, 'title' => $title, 'date' => $timestamp, 'ip' => $ip );
|
||||
$return['status'] = 'success';
|
||||
$return['message'] = /* //translators: eg "http://someurl/ added to DB" */ yourls_s( '%s added to database', yourls_trim_long_string( $url ) );
|
||||
$return['title'] = $title;
|
||||
$return['html'] = yourls_table_add_row( $keyword, $url, $title, $ip, 0, time() );
|
||||
$return['shorturl'] = yourls_link($keyword);
|
||||
$return['statusCode'] = 200; // 200 OK
|
||||
} else {
|
||||
// unknown database error, couldn't store result
|
||||
$return['status'] = 'fail';
|
||||
$return['code'] = 'error:db';
|
||||
$return['message'] = yourls_s( 'Error saving url to database' );
|
||||
$return['errorCode'] = $return['statusCode'] = '500'; // 500 Internal Server Error
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Keyword supposed to be free but the INSERT caused an exception: most likely we're facing a
|
||||
// concurrency problem. See Issue 2538.
|
||||
$return['status'] = 'fail';
|
||||
$return['code'] = 'error:concurrency';
|
||||
$return['message'] = $e->getMessage();
|
||||
$return['errorCode'] = $return['statusCode'] = '503'; // 503 Service Unavailable
|
||||
}
|
||||
|
||||
yourls_do_action( 'post_add_new_link', $url, $keyword, $title, $return );
|
||||
|
||||
return yourls_apply_filter( 'add_new_link', $return, $url, $keyword, $title );
|
||||
}
|
||||
/**
|
||||
* Determine the allowed character set in short URLs
|
||||
*
|
||||
* @return string Acceptable charset for short URLS keywords
|
||||
*/
|
||||
function yourls_get_shorturl_charset() {
|
||||
if ( defined( 'YOURLS_URL_CONVERT' ) && in_array( YOURLS_URL_CONVERT, [ 62, 64 ] ) ) {
|
||||
$charset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
}
|
||||
else {
|
||||
// defined to 36, or wrongly defined
|
||||
$charset = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'get_shorturl_charset', $charset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a URL a short URL? Accept either 'http://sho.rt/abc' or 'abc'
|
||||
*
|
||||
* @param string $shorturl short URL
|
||||
* @return bool true if registered short URL, false otherwise
|
||||
*/
|
||||
function yourls_is_shorturl( $shorturl ) {
|
||||
// TODO: make sure this function evolves with the feature set.
|
||||
|
||||
$is_short = false;
|
||||
|
||||
// Is $shorturl a URL (http://sho.rt/abc) or a keyword (abc) ?
|
||||
if( yourls_get_protocol( $shorturl ) ) {
|
||||
$keyword = yourls_get_relative_url( $shorturl );
|
||||
} else {
|
||||
$keyword = $shorturl;
|
||||
}
|
||||
|
||||
// Check if it's a valid && used keyword
|
||||
if( $keyword && $keyword == yourls_sanitize_keyword( $keyword ) && yourls_keyword_is_taken( $keyword ) ) {
|
||||
$is_short = true;
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'is_shorturl', $is_short, $shorturl );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a given keyword is reserved (ie reserved URL or an existing page). Returns bool
|
||||
*
|
||||
* @param string $keyword Short URL keyword
|
||||
* @return bool True if keyword reserved, false if free to be used
|
||||
*/
|
||||
function yourls_keyword_is_reserved( $keyword ) {
|
||||
global $yourls_reserved_URL;
|
||||
$keyword = yourls_sanitize_keyword( $keyword );
|
||||
$reserved = false;
|
||||
|
||||
if ( in_array( $keyword, $yourls_reserved_URL)
|
||||
or yourls_is_page($keyword)
|
||||
or is_dir( YOURLS_ABSPATH ."/$keyword" )
|
||||
)
|
||||
$reserved = true;
|
||||
|
||||
return yourls_apply_filter( 'keyword_is_reserved', $reserved, $keyword );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a link in the DB
|
||||
*
|
||||
* @param string $keyword Short URL keyword
|
||||
* @return int Number of links deleted
|
||||
*/
|
||||
function yourls_delete_link_by_keyword( $keyword ) {
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_delete_link_by_keyword', null, $keyword );
|
||||
if ( null !== $pre ) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
$keyword = yourls_sanitize_keyword($keyword);
|
||||
$delete = yourls_get_db()->fetchAffected("DELETE FROM `$table` WHERE `keyword` = :keyword", array('keyword' => $keyword));
|
||||
yourls_do_action( 'delete_link', $keyword, $delete );
|
||||
return $delete;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL query to insert a new link in the DB. Returns boolean for success or failure of the inserting
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $keyword
|
||||
* @param string $title
|
||||
* @return bool true if insert succeeded, false if failed
|
||||
*/
|
||||
function yourls_insert_link_in_db($url, $keyword, $title = '' ) {
|
||||
$url = yourls_sanitize_url($url);
|
||||
$keyword = yourls_sanitize_keyword($keyword);
|
||||
$title = yourls_sanitize_title($title);
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
$ip = yourls_get_IP();
|
||||
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
$binds = array(
|
||||
'keyword' => $keyword,
|
||||
'url' => $url,
|
||||
'title' => $title,
|
||||
'timestamp' => $timestamp,
|
||||
'ip' => $ip,
|
||||
);
|
||||
$insert = yourls_get_db()->fetchAffected("INSERT INTO `$table` (`keyword`, `url`, `title`, `timestamp`, `ip`, `clicks`) VALUES(:keyword, :url, :title, :timestamp, :ip, 0);", $binds);
|
||||
|
||||
yourls_do_action( 'insert_link', (bool)$insert, $url, $keyword, $title, $timestamp, $ip );
|
||||
|
||||
return (bool)$insert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a long URL already exists in the DB. Return NULL (doesn't exist) or an object with URL informations.
|
||||
*
|
||||
* This function supersedes function yourls_url_exists(), deprecated in 1.7.10, with a better naming.
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param string $url URL to check if already shortened
|
||||
* @return mixed NULL if does not already exist in DB, or object with URL information as properties (eg keyword, url, title, ...)
|
||||
*/
|
||||
function yourls_long_url_exists( $url ) {
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_url_exists', false, $url );
|
||||
if ( false !== $pre ) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
$url = yourls_sanitize_url($url);
|
||||
$url_exists = yourls_get_db()->fetchObject("SELECT * FROM `$table` WHERE `url` = :url", array('url'=>$url));
|
||||
|
||||
if ($url_exists === false) {
|
||||
$url_exists = NULL;
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'url_exists', $url_exists, $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a link
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $keyword
|
||||
* @param string $newkeyword
|
||||
* @param string $title
|
||||
* @return array Result of the edit and link information if successful
|
||||
*/
|
||||
function yourls_edit_link($url, $keyword, $newkeyword='', $title='' ) {
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_edit_link', null, $keyword, $url, $keyword, $newkeyword, $title );
|
||||
if ( null !== $pre )
|
||||
return $pre;
|
||||
|
||||
$ydb = yourls_get_db();
|
||||
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
$url = yourls_sanitize_url($url);
|
||||
$keyword = yourls_sanitize_keyword($keyword);
|
||||
$title = yourls_sanitize_title($title);
|
||||
$newkeyword = yourls_sanitize_keyword($newkeyword, true);
|
||||
|
||||
if(!$url OR !$newkeyword) {
|
||||
$return['status'] = 'fail';
|
||||
$return['message'] = yourls__( 'Long URL or Short URL cannot be blank' );
|
||||
return yourls_apply_filter( 'edit_link', $return, $url, $keyword, $newkeyword, $title );
|
||||
}
|
||||
|
||||
$old_url = $ydb->fetchValue("SELECT `url` FROM `$table` WHERE `keyword` = :keyword", array('keyword' => $keyword));
|
||||
|
||||
// Check if new URL is not here already
|
||||
if ( $old_url != $url && !yourls_allow_duplicate_longurls() ) {
|
||||
$new_url_already_there = intval($ydb->fetchValue("SELECT COUNT(keyword) FROM `$table` WHERE `url` = :url;", array('url' => $url)));
|
||||
} else {
|
||||
$new_url_already_there = false;
|
||||
}
|
||||
|
||||
// Check if the new keyword is not here already
|
||||
if ( $newkeyword != $keyword ) {
|
||||
$keyword_is_ok = yourls_keyword_is_free( $newkeyword );
|
||||
} else {
|
||||
$keyword_is_ok = true;
|
||||
}
|
||||
|
||||
yourls_do_action( 'pre_edit_link', $url, $keyword, $newkeyword, $new_url_already_there, $keyword_is_ok );
|
||||
|
||||
// All clear, update
|
||||
if ( ( !$new_url_already_there || yourls_allow_duplicate_longurls() ) && $keyword_is_ok ) {
|
||||
$sql = "UPDATE `$table` SET `url` = :url, `keyword` = :newkeyword, `title` = :title WHERE `keyword` = :keyword";
|
||||
$binds = array('url' => $url, 'newkeyword' => $newkeyword, 'title' => $title, 'keyword' => $keyword);
|
||||
$update_url = $ydb->fetchAffected($sql, $binds);
|
||||
if( $update_url ) {
|
||||
$return['url'] = array( 'keyword' => $newkeyword,
|
||||
'shorturl' => yourls_link($newkeyword),
|
||||
'url' => yourls_esc_url($url),
|
||||
'display_url' => yourls_esc_html(yourls_trim_long_string($url)),
|
||||
'title' => yourls_esc_attr($title),
|
||||
'display_title' => yourls_esc_html(yourls_trim_long_string( $title ))
|
||||
);
|
||||
$return['status'] = 'success';
|
||||
$return['message'] = yourls__( 'Link updated in database' );
|
||||
} else {
|
||||
$return['status'] = 'fail';
|
||||
$return['message'] = /* //translators: "Error updating http://someurl/ (Shorturl: http://sho.rt/blah)" */ yourls_s( 'Error updating %s (Short URL: %s)', yourls_esc_html(yourls_trim_long_string($url)), $keyword ) ;
|
||||
}
|
||||
|
||||
// Nope
|
||||
} else {
|
||||
$return['status'] = 'fail';
|
||||
$return['message'] = yourls__( 'URL or keyword already exists in database' );
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'edit_link', $return, $url, $keyword, $newkeyword, $title, $new_url_already_there, $keyword_is_ok );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a title link (no checks for duplicates etc..)
|
||||
*
|
||||
* @param string $keyword
|
||||
* @param string $title
|
||||
* @return int number of rows updated
|
||||
*/
|
||||
function yourls_edit_link_title( $keyword, $title ) {
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_edit_link_title', null, $keyword, $title );
|
||||
if ( null !== $pre ) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
$keyword = yourls_sanitize_keyword( $keyword );
|
||||
$title = yourls_sanitize_title( $title );
|
||||
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
$update = yourls_get_db()->fetchAffected("UPDATE `$table` SET `title` = :title WHERE `keyword` = :keyword;", array('title' => $title, 'keyword' => $keyword));
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if keyword id is free (ie not already taken, and not reserved). Return bool.
|
||||
*
|
||||
* @param string $keyword short URL keyword
|
||||
* @return bool true if keyword is taken (ie there is a short URL for it), false otherwise
|
||||
*/
|
||||
function yourls_keyword_is_free( $keyword ) {
|
||||
$free = true;
|
||||
if ( yourls_keyword_is_reserved( $keyword ) or yourls_keyword_is_taken( $keyword, false ) ) {
|
||||
$free = false;
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'keyword_is_free', $free, $keyword );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a keyword matches a "page"
|
||||
*
|
||||
* @see https://docs.yourls.org/guide/extend/pages.html
|
||||
* @since 1.7.10
|
||||
* @param string $keyword Short URL $keyword
|
||||
* @return bool true if is page, false otherwise
|
||||
*/
|
||||
function yourls_is_page($keyword) {
|
||||
return yourls_apply_filter( 'is_page', file_exists( YOURLS_PAGEDIR . "/$keyword.php" ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a keyword is taken (ie there is already a short URL with this id). Return bool.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* Check if a keyword is taken (ie there is already a short URL with this id). Return bool.
|
||||
*
|
||||
* @param string $keyword short URL keyword
|
||||
* @param bool $use_cache optional, default true: do we want to use what is cached in memory, if any, or force a new SQL query
|
||||
* @return bool true if keyword is taken (ie there is a short URL for it), false otherwise
|
||||
*/
|
||||
function yourls_keyword_is_taken( $keyword, $use_cache = true ) {
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_keyword_is_taken', false, $keyword );
|
||||
if ( false !== $pre ) {
|
||||
return $pre;
|
||||
}
|
||||
|
||||
$taken = false;
|
||||
// To check if a keyword is already associated with a short URL, we fetch all info matching that keyword. This
|
||||
// will save a query in case of a redirection in yourls-go.php because info will be cached
|
||||
if ( yourls_get_keyword_infos($keyword, $use_cache) ) {
|
||||
$taken = true;
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'keyword_is_taken', $taken, $keyword );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of all information associated with keyword. Returns false if keyword not found. Set optional $use_cache to false to force fetching from DB
|
||||
*
|
||||
* Sincere apologies to native English speakers, we are aware that the plural of 'info' is actually 'info', not 'infos'.
|
||||
* This function yourls_get_keyword_infos() returns all information, while function yourls_get_keyword_info() (no 's') return only
|
||||
* one information. Blame YOURLS contributors whose mother tongue is not English :)
|
||||
*
|
||||
* @since 1.4
|
||||
* @param string $keyword Short URL keyword
|
||||
* @param bool $use_cache Default true, set to false to force fetching from DB
|
||||
* @return false|object false if not found, object with URL properties if found
|
||||
*/
|
||||
function yourls_get_keyword_infos( $keyword, $use_cache = true ) {
|
||||
$ydb = yourls_get_db();
|
||||
$keyword = yourls_sanitize_keyword( $keyword );
|
||||
|
||||
yourls_do_action( 'pre_get_keyword', $keyword, $use_cache );
|
||||
|
||||
if( $ydb->has_infos($keyword) && $use_cache === true ) {
|
||||
return yourls_apply_filter( 'get_keyword_infos', $ydb->get_infos($keyword), $keyword );
|
||||
}
|
||||
|
||||
yourls_do_action( 'get_keyword_not_cached', $keyword );
|
||||
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
$infos = $ydb->fetchObject("SELECT * FROM `$table` WHERE `keyword` = :keyword", array('keyword' => $keyword));
|
||||
|
||||
if( $infos ) {
|
||||
$infos = (array)$infos;
|
||||
$ydb->set_infos($keyword, $infos);
|
||||
} else {
|
||||
// is NULL if not found
|
||||
$infos = false;
|
||||
$ydb->set_infos($keyword, false);
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'get_keyword_infos', $infos, $keyword );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return information associated with a keyword (eg clicks, URL, title...). Optional $notfound = string default message if nothing found
|
||||
*
|
||||
* @param string $keyword Short URL keyword
|
||||
* @param string $field Field to return (eg 'url', 'title', 'ip', 'clicks', 'timestamp', 'keyword')
|
||||
* @param false|string $notfound Optional string to return if keyword not found
|
||||
* @return mixed|string
|
||||
*/
|
||||
function yourls_get_keyword_info($keyword, $field, $notfound = false ) {
|
||||
|
||||
// Allow plugins to short-circuit the whole function
|
||||
$pre = yourls_apply_filter( 'shunt_get_keyword_info', false, $keyword, $field, $notfound );
|
||||
if ( false !== $pre )
|
||||
return $pre;
|
||||
|
||||
$keyword = yourls_sanitize_keyword( $keyword );
|
||||
$infos = yourls_get_keyword_infos( $keyword );
|
||||
|
||||
$return = $notfound;
|
||||
if ( isset( $infos[ $field ] ) && $infos[ $field ] !== false )
|
||||
$return = $infos[ $field ];
|
||||
|
||||
return yourls_apply_filter( 'get_keyword_info', $return, $keyword, $field, $notfound );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return title associated with keyword. Optional $notfound = string default message if nothing found
|
||||
*
|
||||
* @param string $keyword Short URL keyword
|
||||
* @param false|string $notfound Optional string to return if keyword not found
|
||||
* @return mixed|string
|
||||
*/
|
||||
function yourls_get_keyword_title( $keyword, $notfound = false ) {
|
||||
return yourls_get_keyword_info( $keyword, 'title', $notfound );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return long URL associated with keyword. Optional $notfound = string default message if nothing found
|
||||
*
|
||||
* @param string $keyword Short URL keyword
|
||||
* @param false|string $notfound Optional string to return if keyword not found
|
||||
* @return mixed|string
|
||||
*/
|
||||
function yourls_get_keyword_longurl( $keyword, $notfound = false ) {
|
||||
return yourls_get_keyword_info( $keyword, 'url', $notfound );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of clicks on a keyword. Optional $notfound = string default message if nothing found
|
||||
*
|
||||
* @param string $keyword Short URL keyword
|
||||
* @param false|string $notfound Optional string to return if keyword not found
|
||||
* @return mixed|string
|
||||
*/
|
||||
function yourls_get_keyword_clicks( $keyword, $notfound = false ) {
|
||||
return yourls_get_keyword_info( $keyword, 'clicks', $notfound );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return IP that added a keyword. Optional $notfound = string default message if nothing found
|
||||
*
|
||||
* @param string $keyword Short URL keyword
|
||||
* @param false|string $notfound Optional string to return if keyword not found
|
||||
* @return mixed|string
|
||||
*/
|
||||
function yourls_get_keyword_IP( $keyword, $notfound = false ) {
|
||||
return yourls_get_keyword_info( $keyword, 'ip', $notfound );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return timestamp associated with a keyword. Optional $notfound = string default message if nothing found
|
||||
*
|
||||
* @param string $keyword Short URL keyword
|
||||
* @param false|string $notfound Optional string to return if keyword not found
|
||||
* @return mixed|string
|
||||
*/
|
||||
function yourls_get_keyword_timestamp( $keyword, $notfound = false ) {
|
||||
return yourls_get_keyword_info( $keyword, 'timestamp', $notfound );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of stats for a given keyword
|
||||
*
|
||||
* This function supersedes function yourls_get_link_stats(), deprecated in 1.7.10, with a better naming.
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param string $shorturl short URL keyword
|
||||
* @return array stats
|
||||
*/
|
||||
function yourls_get_keyword_stats( $shorturl ) {
|
||||
$table_url = YOURLS_DB_TABLE_URL;
|
||||
$shorturl = yourls_sanitize_keyword( $shorturl );
|
||||
|
||||
$res = yourls_get_db()->fetchObject("SELECT * FROM `$table_url` WHERE `keyword` = :keyword", array('keyword' => $shorturl));
|
||||
|
||||
if( !$res ) {
|
||||
// non existent link
|
||||
$return = array(
|
||||
'statusCode' => 404,
|
||||
'message' => 'Error: short URL not found',
|
||||
);
|
||||
} else {
|
||||
$return = array(
|
||||
'statusCode' => 200,
|
||||
'message' => 'success',
|
||||
'link' => array(
|
||||
'shorturl' => yourls_link($res->keyword),
|
||||
'url' => $res->url,
|
||||
'title' => $res->title,
|
||||
'timestamp'=> $res->timestamp,
|
||||
'ip' => $res->ip,
|
||||
'clicks' => $res->clicks,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'get_link_stats', $return, $shorturl );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of keywords that redirect to the submitted long URL
|
||||
*
|
||||
* @since 1.7
|
||||
* @param string $longurl long url
|
||||
* @param string $order Optional SORT order (can be 'ASC' or 'DESC')
|
||||
* @return array array of keywords
|
||||
*/
|
||||
function yourls_get_longurl_keywords( $longurl, $order = 'ASC' ) {
|
||||
$longurl = yourls_sanitize_url($longurl);
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
$sql = "SELECT `keyword` FROM `$table` WHERE `url` = :url";
|
||||
|
||||
if (in_array($order, array('ASC','DESC'))) {
|
||||
$sql .= " ORDER BY `keyword` ".$order;
|
||||
}
|
||||
|
||||
return yourls_apply_filter( 'get_longurl_keywords', yourls_get_db()->fetchCol($sql, array('url'=>$longurl)), $longurl );
|
||||
}
|
|
@ -0,0 +1,453 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Upgrade YOURLS and DB schema
|
||||
*
|
||||
* Note to devs : prefer update function names using the SQL version, eg yourls_update_to_506(),
|
||||
* rather than using the YOURLS version number, eg yourls_update_to_18(). This is to allow having
|
||||
* multiple SQL update during the dev cycle of the same Y0URLS version
|
||||
*
|
||||
* @param string|int $step
|
||||
* @param string $oldver
|
||||
* @param string $newver
|
||||
* @param string|int $oldsql
|
||||
* @param string|int $newsql
|
||||
* @return void
|
||||
*/
|
||||
function yourls_upgrade($step, $oldver, $newver, $oldsql, $newsql ) {
|
||||
|
||||
/**
|
||||
* Sanitize input. Two notes :
|
||||
* - they should already be sanitized in the caller, eg admin/upgrade.php
|
||||
* (but hey, let's make sure)
|
||||
* - some vars may not be used at the moment
|
||||
* (and this is ok, they are here in case a future upgrade procedure needs them)
|
||||
*/
|
||||
$step = intval($step);
|
||||
$oldsql = intval($oldsql);
|
||||
$newsql = intval($newsql);
|
||||
$oldver = yourls_sanitize_version($oldver);
|
||||
$newver = yourls_sanitize_version($newver);
|
||||
|
||||
yourls_maintenance_mode(true);
|
||||
|
||||
// special case for 1.3: the upgrade is a multi step procedure
|
||||
if( $oldsql == 100 ) {
|
||||
yourls_upgrade_to_14( $step );
|
||||
}
|
||||
|
||||
// other upgrades which are done in a single pass
|
||||
switch( $step ) {
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
if( $oldsql < 210 )
|
||||
yourls_upgrade_to_141();
|
||||
|
||||
if( $oldsql < 220 )
|
||||
yourls_upgrade_to_143();
|
||||
|
||||
if( $oldsql < 250 )
|
||||
yourls_upgrade_to_15();
|
||||
|
||||
if( $oldsql < 482 )
|
||||
yourls_upgrade_482(); // that was somewhere 1.5 and 1.5.1 ...
|
||||
|
||||
if( $oldsql < 506 ) {
|
||||
/**
|
||||
* 505 was the botched update with the wrong collation, see #2766
|
||||
* 506 is the updated collation.
|
||||
* We want :
|
||||
* people on 505 to update to 506
|
||||
* people before 505 to update to the FIXED complete upgrade
|
||||
*/
|
||||
if( $oldsql == 505 ) {
|
||||
yourls_upgrade_505_to_506();
|
||||
} else {
|
||||
yourls_upgrade_to_506();
|
||||
}
|
||||
}
|
||||
|
||||
yourls_redirect_javascript( yourls_admin_url( "upgrade.php?step=3" ) );
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Update options to reflect latest version
|
||||
yourls_update_option( 'version', YOURLS_VERSION );
|
||||
yourls_update_option( 'db_version', YOURLS_DB_VERSION );
|
||||
yourls_maintenance_mode(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/************************** 1.6 -> 1.8 **************************/
|
||||
|
||||
/**
|
||||
* Update to 506, just the fix for people who had updated to master on 1.7.10
|
||||
*
|
||||
*/
|
||||
function yourls_upgrade_505_to_506() {
|
||||
echo "<p>Updating DB. Please wait...</p>";
|
||||
// Fix collation which was wrongly set at first to utf8mb4_unicode_ci
|
||||
$query = sprintf('ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;', YOURLS_DB_TABLE_URL);
|
||||
|
||||
try {
|
||||
yourls_get_db()->perform($query);
|
||||
} catch (\Exception $e) {
|
||||
echo "<p class='error'>Unable to update the DB.</p>";
|
||||
echo "<p>Could not change collation. You will have to fix things manually :(. The error was
|
||||
<pre>";
|
||||
echo $e->getMessage();
|
||||
echo "/n</pre>";
|
||||
die();
|
||||
}
|
||||
|
||||
echo "<p class='success'>OK!</p>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Update to 506
|
||||
*
|
||||
*/
|
||||
function yourls_upgrade_to_506() {
|
||||
$ydb = yourls_get_db();
|
||||
$error_msg = [];
|
||||
|
||||
echo "<p>Updating DB. Please wait...</p>";
|
||||
|
||||
$queries = array(
|
||||
'database charset' => sprintf('ALTER DATABASE `%s` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;', YOURLS_DB_NAME),
|
||||
'options charset' => sprintf('ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;', YOURLS_DB_TABLE_OPTIONS),
|
||||
'short URL varchar' => sprintf("ALTER TABLE `%s` CHANGE `keyword` `keyword` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '';", YOURLS_DB_TABLE_URL),
|
||||
'short URL type url' => sprintf("ALTER TABLE `%s` CHANGE `url` `url` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL;", YOURLS_DB_TABLE_URL),
|
||||
'short URL type title' => sprintf("ALTER TABLE `%s` CHANGE `title` `title` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", YOURLS_DB_TABLE_URL),
|
||||
'short URL charset' => sprintf('ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;', YOURLS_DB_TABLE_URL),
|
||||
);
|
||||
|
||||
foreach($queries as $what => $query) {
|
||||
try {
|
||||
$ydb->perform($query);
|
||||
} catch (\Exception $e) {
|
||||
$error_msg[] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
if( $error_msg ) {
|
||||
echo "<p class='error'>Unable to update the DB.</p>";
|
||||
echo "<p>You will have to manually fix things, sorry for the inconvenience :(</p>";
|
||||
echo "<p>The errors were:
|
||||
<pre>";
|
||||
foreach( $error_msg as $error ) {
|
||||
echo "$error\n";
|
||||
}
|
||||
echo "</pre>";
|
||||
die();
|
||||
}
|
||||
|
||||
echo "<p class='success'>OK!</p>";
|
||||
}
|
||||
|
||||
/************************** 1.5 -> 1.6 **************************/
|
||||
|
||||
/**
|
||||
* Upgrade r482
|
||||
*
|
||||
*/
|
||||
function yourls_upgrade_482() {
|
||||
// Change URL title charset to UTF8
|
||||
$table_url = YOURLS_DB_TABLE_URL;
|
||||
$sql = "ALTER TABLE `$table_url` CHANGE `title` `title` TEXT CHARACTER SET utf8;";
|
||||
yourls_get_db()->perform( $sql );
|
||||
echo "<p>Updating table structure. Please wait...</p>";
|
||||
}
|
||||
|
||||
/************************** 1.4.3 -> 1.5 **************************/
|
||||
|
||||
/**
|
||||
* Main func for upgrade from 1.4.3 to 1.5
|
||||
*
|
||||
*/
|
||||
function yourls_upgrade_to_15( ) {
|
||||
// Create empty 'active_plugins' entry in the option if needed
|
||||
if( yourls_get_option( 'active_plugins' ) === false )
|
||||
yourls_add_option( 'active_plugins', array() );
|
||||
echo "<p>Enabling the plugin API. Please wait...</p>";
|
||||
|
||||
// Alter URL table to store titles
|
||||
$table_url = YOURLS_DB_TABLE_URL;
|
||||
$sql = "ALTER TABLE `$table_url` ADD `title` TEXT AFTER `url`;";
|
||||
yourls_get_db()->perform( $sql );
|
||||
echo "<p>Updating table structure. Please wait...</p>";
|
||||
|
||||
// Update .htaccess
|
||||
yourls_create_htaccess();
|
||||
echo "<p>Updating .htaccess file. Please wait...</p>";
|
||||
}
|
||||
|
||||
/************************** 1.4.1 -> 1.4.3 **************************/
|
||||
|
||||
/**
|
||||
* Main func for upgrade from 1.4.1 to 1.4.3
|
||||
*
|
||||
*/
|
||||
function yourls_upgrade_to_143( ) {
|
||||
// Check if we have 'keyword' (borked install) or 'shorturl' (ok install)
|
||||
$ydb = yourls_get_db();
|
||||
$table_log = YOURLS_DB_TABLE_LOG;
|
||||
$sql = "SHOW COLUMNS FROM `$table_log`";
|
||||
$cols = $ydb->fetchObjects( $sql );
|
||||
if ( $cols[2]->Field == 'keyword' ) {
|
||||
$sql = "ALTER TABLE `$table_log` CHANGE `keyword` `shorturl` VARCHAR( 200 ) BINARY;";
|
||||
$ydb->query( $sql );
|
||||
}
|
||||
echo "<p>Structure of existing tables updated. Please wait...</p>";
|
||||
}
|
||||
|
||||
/************************** 1.4 -> 1.4.1 **************************/
|
||||
|
||||
/**
|
||||
* Main func for upgrade from 1.4 to 1.4.1
|
||||
*
|
||||
*/
|
||||
function yourls_upgrade_to_141( ) {
|
||||
// Kill old cookies from 1.3 and prior
|
||||
setcookie('yourls_username', '', time() - 3600 );
|
||||
setcookie('yourls_password', '', time() - 3600 );
|
||||
// alter table URL
|
||||
yourls_alter_url_table_to_141();
|
||||
// recreate the htaccess file if needed
|
||||
yourls_create_htaccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter table URL to 1.4.1
|
||||
*
|
||||
*/
|
||||
function yourls_alter_url_table_to_141() {
|
||||
$table_url = YOURLS_DB_TABLE_URL;
|
||||
$alter = "ALTER TABLE `$table_url` CHANGE `keyword` `keyword` VARCHAR( 200 ) BINARY, CHANGE `url` `url` TEXT BINARY ";
|
||||
yourls_get_db()->perform( $alter );
|
||||
echo "<p>Structure of existing tables updated. Please wait...</p>";
|
||||
}
|
||||
|
||||
|
||||
/************************** 1.3 -> 1.4 **************************/
|
||||
|
||||
/**
|
||||
* Main func for upgrade from 1.3-RC1 to 1.4
|
||||
*
|
||||
*/
|
||||
function yourls_upgrade_to_14( $step ) {
|
||||
|
||||
switch( $step ) {
|
||||
case 1:
|
||||
// create table log & table options
|
||||
// update table url structure
|
||||
// update .htaccess
|
||||
yourls_create_tables_for_14(); // no value returned, assuming it went OK
|
||||
yourls_alter_url_table_to_14(); // no value returned, assuming it went OK
|
||||
$clean = yourls_clean_htaccess_for_14(); // returns bool
|
||||
$create = yourls_create_htaccess(); // returns bool
|
||||
if ( !$create )
|
||||
echo "<p class='warning'>Please create your <tt>.htaccess</tt> file (I could not do it for you). Please refer to <a href='http://yourls.org/htaccess'>http://yourls.org/htaccess</a>.";
|
||||
yourls_redirect_javascript( yourls_admin_url( "upgrade.php?step=2&oldver=1.3&newver=1.4&oldsql=100&newsql=200" ), $create );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// convert each link in table url
|
||||
yourls_update_table_to_14();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// update table url structure part 2: recreate indexes
|
||||
yourls_alter_url_table_to_14_part_two();
|
||||
// update version & db_version & next_id in the option table
|
||||
// attempt to drop YOURLS_DB_TABLE_NEXTDEC
|
||||
yourls_update_options_to_14();
|
||||
// Now upgrade to 1.4.1
|
||||
yourls_redirect_javascript( yourls_admin_url( "upgrade.php?step=1&oldver=1.4&newver=1.4.1&oldsql=200&newsql=210" ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update options to reflect new version
|
||||
*
|
||||
*/
|
||||
function yourls_update_options_to_14() {
|
||||
yourls_update_option( 'version', '1.4' );
|
||||
yourls_update_option( 'db_version', '200' );
|
||||
|
||||
if( defined('YOURLS_DB_TABLE_NEXTDEC') ) {
|
||||
$table = YOURLS_DB_TABLE_NEXTDEC;
|
||||
$next_id = yourls_get_db()->fetchValue("SELECT `next_id` FROM `$table`");
|
||||
yourls_update_option( 'next_id', $next_id );
|
||||
yourls_get_db()->perform( "DROP TABLE `$table`" );
|
||||
} else {
|
||||
yourls_update_option( 'next_id', 1 ); // In case someone mistakenly deleted the next_id constant or table too early
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new tables for YOURLS 1.4: options & log
|
||||
*
|
||||
*/
|
||||
function yourls_create_tables_for_14() {
|
||||
$ydb = yourls_get_db();
|
||||
|
||||
$queries = array();
|
||||
|
||||
$queries[YOURLS_DB_TABLE_OPTIONS] =
|
||||
'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_OPTIONS.'` ('.
|
||||
'`option_id` int(11) unsigned NOT NULL auto_increment,'.
|
||||
'`option_name` varchar(64) NOT NULL default "",'.
|
||||
'`option_value` longtext NOT NULL,'.
|
||||
'PRIMARY KEY (`option_id`,`option_name`),'.
|
||||
'KEY `option_name` (`option_name`)'.
|
||||
');';
|
||||
|
||||
$queries[YOURLS_DB_TABLE_LOG] =
|
||||
'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_LOG.'` ('.
|
||||
'`click_id` int(11) NOT NULL auto_increment,'.
|
||||
'`click_time` datetime NOT NULL,'.
|
||||
'`shorturl` varchar(200) NOT NULL,'.
|
||||
'`referrer` varchar(200) NOT NULL,'.
|
||||
'`user_agent` varchar(255) NOT NULL,'.
|
||||
'`ip_address` varchar(41) NOT NULL,'.
|
||||
'`country_code` char(2) NOT NULL,'.
|
||||
'PRIMARY KEY (`click_id`),'.
|
||||
'KEY `shorturl` (`shorturl`)'.
|
||||
');';
|
||||
|
||||
foreach( $queries as $query ) {
|
||||
$ydb->perform( $query ); // There's no result to be returned to check if table was created (except making another query to check table existence, which we'll avoid)
|
||||
}
|
||||
|
||||
echo "<p>New tables created. Please wait...</p>";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter table structure, part 1 (change schema, drop index)
|
||||
*
|
||||
*/
|
||||
function yourls_alter_url_table_to_14() {
|
||||
$ydb = yourls_get_db();
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
|
||||
$alters = array();
|
||||
$results = array();
|
||||
$alters[] = "ALTER TABLE `$table` CHANGE `id` `keyword` VARCHAR( 200 ) NOT NULL";
|
||||
$alters[] = "ALTER TABLE `$table` CHANGE `url` `url` TEXT NOT NULL";
|
||||
$alters[] = "ALTER TABLE `$table` DROP PRIMARY KEY";
|
||||
|
||||
foreach ( $alters as $query ) {
|
||||
$ydb->perform( $query );
|
||||
}
|
||||
|
||||
echo "<p>Structure of existing tables updated. Please wait...</p>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter table structure, part 2 (recreate indexes after the table is up to date)
|
||||
*
|
||||
*/
|
||||
function yourls_alter_url_table_to_14_part_two() {
|
||||
$ydb = yourls_get_db();
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
|
||||
$alters = array();
|
||||
$alters[] = "ALTER TABLE `$table` ADD PRIMARY KEY ( `keyword` )";
|
||||
$alters[] = "ALTER TABLE `$table` ADD INDEX ( `ip` )";
|
||||
$alters[] = "ALTER TABLE `$table` ADD INDEX ( `timestamp` )";
|
||||
|
||||
foreach ( $alters as $query ) {
|
||||
$ydb->perform( $query );
|
||||
}
|
||||
|
||||
echo "<p>New table index created</p>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert each link from 1.3 (id) to 1.4 (keyword) structure
|
||||
*
|
||||
*/
|
||||
function yourls_update_table_to_14() {
|
||||
$ydb = yourls_get_db();
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
|
||||
// Modify each link to reflect new structure
|
||||
$chunk = 45;
|
||||
$from = isset($_GET['from']) ? intval( $_GET['from'] ) : 0 ;
|
||||
$total = yourls_get_db_stats();
|
||||
$total = $total['total_links'];
|
||||
|
||||
$sql = "SELECT `keyword`,`url` FROM `$table` WHERE 1=1 ORDER BY `url` ASC LIMIT $from, $chunk ;";
|
||||
|
||||
$rows = $ydb->fetchObjects($sql);
|
||||
|
||||
$count = 0;
|
||||
$queries = 0;
|
||||
foreach( $rows as $row ) {
|
||||
$keyword = $row->keyword;
|
||||
$url = $row->url;
|
||||
$newkeyword = yourls_int2string( $keyword );
|
||||
if( true === $ydb->perform("UPDATE `$table` SET `keyword` = '$newkeyword' WHERE `url` = '$url';") ) {
|
||||
$queries++;
|
||||
} else {
|
||||
echo "<p>Huho... Could not update rown with url='$url', from keyword '$keyword' to keyword '$newkeyword'</p>"; // Find what went wrong :/
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
|
||||
// All done for this chunk of queries, did it all go as expected?
|
||||
$success = true;
|
||||
if( $count != $queries ) {
|
||||
$success = false;
|
||||
$num = $count - $queries;
|
||||
echo "<p>$num error(s) occured while updating the URL table :(</p>";
|
||||
}
|
||||
|
||||
if ( $count == $chunk ) {
|
||||
// there are probably other rows to convert
|
||||
$from = $from + $chunk;
|
||||
$remain = $total - $from;
|
||||
echo "<p>Converted $chunk database rows ($remain remaining). Continuing... Please do not close this window until it's finished!</p>";
|
||||
yourls_redirect_javascript( yourls_admin_url( "upgrade.php?step=2&oldver=1.3&newver=1.4&oldsql=100&newsql=200&from=$from" ), $success );
|
||||
} else {
|
||||
// All done
|
||||
echo '<p>All rows converted! Please wait...</p>';
|
||||
yourls_redirect_javascript( yourls_admin_url( "upgrade.php?step=3&oldver=1.3&newver=1.4&oldsql=100&newsql=200" ), $success );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean .htaccess as it existed before 1.4. Returns boolean
|
||||
*
|
||||
*/
|
||||
function yourls_clean_htaccess_for_14() {
|
||||
$filename = YOURLS_ABSPATH.'/.htaccess';
|
||||
|
||||
$result = false;
|
||||
if( is_writeable( $filename ) ) {
|
||||
$contents = implode( '', file( $filename ) );
|
||||
// remove "ShortURL" block
|
||||
$contents = preg_replace( '/# BEGIN ShortURL.*# END ShortURL/s', '', $contents );
|
||||
// comment out deprecated RewriteRule
|
||||
$find = 'RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization},L]';
|
||||
$replace = "# You can safely remove this 5 lines block -- it's no longer used in YOURLS\n".
|
||||
"# $find";
|
||||
$contents = str_replace( $find, $replace, $contents );
|
||||
|
||||
// Write cleaned file
|
||||
$f = fopen( $filename, 'w' );
|
||||
fwrite( $f, $contents );
|
||||
fclose( $f );
|
||||
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/* Bootstrap YOURLS
|
||||
*
|
||||
* This file initialize everything needed for YOURLS
|
||||
* If you need to bootstrap YOURLS (ie access its functions and features) simply include this file.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// Set up YOURLS config
|
||||
|
||||
$config = new \YOURLS\Config\Config;
|
||||
/* The following require has to be at global level so the variables inside config.php, including user defined if any,
|
||||
* are registered in the global scope. If this require is moved in \YOURLS\Config\Config, $yourls_user_passwords for
|
||||
* instance isn't registered.
|
||||
*/
|
||||
if (!defined('YOURLS_CONFIGFILE')) {
|
||||
define('YOURLS_CONFIGFILE', $config->find_config());
|
||||
}
|
||||
require_once YOURLS_CONFIGFILE;
|
||||
$config->define_core_constants();
|
||||
|
||||
// Initialize YOURLS with default behaviors
|
||||
|
||||
$init_defaults = new \YOURLS\Config\InitDefaults;
|
||||
new \YOURLS\Config\Init($init_defaults);
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* YOURLS version
|
||||
*
|
||||
* Must be one of the following :
|
||||
* MAJOR.MINOR (eg 1.8)
|
||||
* MAJOR.MINOR.PATCH (1.8.1)
|
||||
* MAJOR.MINOR-SOMETHING (1.8-dev)
|
||||
* MAJOR.MINOR.PATCH-SOMETHING (1.8.1-donotuse)
|
||||
*
|
||||
*/
|
||||
define( 'YOURLS_VERSION', '1.9.1' );
|
||||
|
||||
/**
|
||||
* YOURLS DB version. Increments when changes are made to the DB schema, to trigger a DB update
|
||||
*
|
||||
* Must be a string of an integer.
|
||||
*
|
||||
*/
|
||||
define( 'YOURLS_DB_VERSION', '506' );
|
Loading…
Reference in New Issue