Файл: IPBMafia.ru_IPB_3.4.6_Final_Rus _Nulled/board/upload/admin/sources/base/core.php
Строк: 4823
<?php
/**
* <pre>
* Invision Power Services
* IP.Board v3.4.6
* Static Classes for IP.Board 3
*
* These classes are not required as objects. We have grouped
* together several singletons to prevent multiple file loads
* Last Updated: $Date: 2013-09-26 22:37:28 -0400 (Thu, 26 Sep 2013) $
* </pre>
*
* @author $Author: rashbrook $
* @copyright (c) 2001 - 2009 Invision Power Services, Inc.
* @license http://www.invisionpower.com/company/standards.php#license
* @package IP.Board
* @link http://www.invisionpower.com
* @since 12th March 2002
* @version $Revision: 12370 $
*
* @author Matt
*/
/* Non setting settings defaults.
* Do not edit here, edit them in your conf_global.php */
$_ipsPowerSettings = array( 'status_sidebar_show_x' => 5,
'ipb_disable_group_psformat' => 0,
'tags_max_truncated_len' => 35,
'max_bbcodes_per_post' => 500,
'postpage_contents' => '5,10,15,20,25,30,35,40',
'topicpage_contents' => '5,10,15,20,25,30,35,40',
'member_photo_crop' => 100,
'posting_allow_rte' => 1, # Will look to redirect old editor methods to new and will remove this (legacy compatibility)
'like_notifications_limit' => 1000,
'actidx_override' => 0,
'signature_line_length' => 200,
'show_x_page_link' => 2,
'post_order_column' => 'pid', // Other valid values: 'post_date'
'assume_css_written' => 0, // Assume CSS has been written even with safe mode skins enabled - prevents lookup to see if file exists
'forum_search_groupby' => 0, // If enabled, forum searches will perform a group by which returns more/better results, but is much more resource hungry and may cause problems on large boards
);
class IPSAdCodeDefault
{
/**
* Registry Object Shortcuts
*
* @var object
*/
public $registry;
public $DB;
public $settings;
public $request;
public $lang;
public $member;
public $memberData;
public $cache;
public $caches;
/**
* Ad code to overwrite the global header code
*
* @var string
*/
public $headerCode = '';
/**
* Ad code to overwrite the global footer code
*
* @var string
*/
public $footerCode = '';
public function __construct( ipsRegistry $registry )
{
/* Make object */
$this->registry = $registry;
$this->DB = $this->registry->DB();
$this->settings =& $this->registry->fetchSettings();
$this->request =& $this->registry->fetchRequest();
$this->lang = $this->registry->getClass('class_localization');
$this->member = $this->registry->member();
$this->memberData =& $this->registry->member()->fetchMemberData();
$this->cache = $this->registry->cache();
$this->caches =& $this->registry->cache()->fetchCaches();
}
/**
* Basic functionality
*/
public function userCanViewAds()
{
return false;
}
}
/**
* Deletion Log Class
*/
class IPSDeleteLog
{
/**
* Add entry to the delete log
*
* @param int Object ID
* @param string Object Type
* @param string Reason for addition
* @param array Array of member data of user adding log entry
*/
public static function addEntry( $id, $type, $reason, $memberData )
{
if ( $id AND $type AND is_array( $memberData ) AND $memberData['member_id'] )
{
ipsRegistry::DB()->replace( 'core_soft_delete_log', array( 'sdl_obj_id' => $id,
'sdl_obj_key' => $type,
'sdl_obj_reason' => $reason,
'sdl_obj_member_id' => $memberData['member_id'],
'sdl_obj_date' => time(),
'sdl_locked' => 0 ), array( 'sdl_obj_id', 'sdl_obj_key' ) );
return TRUE;
}
return FALSE;
}
/**
* Remove entres to the delete log
*
* @param array Object IDs
* @param string Object Type
* @param boolean Force deletion (used when deleting topics/posts/etc)
*/
public static function removeEntries( $ids, $type, $forceDelete=false )
{
if ( is_array( $ids ) AND count( $ids ) AND $type )
{
$ids = IPSLib::cleanIntArray( $ids );
/* if we're not a global mod, then lock these not remove unless we're deleting stuff */
if ( ! ipsRegistry::member()->getProperty('g_is_supmod') AND $forceDelete === false )
{
ipsRegistry::DB()->update( 'core_soft_delete_log', array( 'sdl_locked' => 1 ), 'sdl_obj_id IN (' . implode( ',', $ids ) . ') AND sdl_obj_key='' . $type . ''' );
}
else
{
ipsRegistry::DB()->delete( 'core_soft_delete_log', 'sdl_obj_id IN (' . implode( ',', $ids ) . ') AND sdl_obj_key='' . $type . ''' );
}
return TRUE;
}
return FALSE;
}
/**
* Fetch entries from the delete log
*
* @param array Object IDs
* @param string Object Type
* @param boolean Parse Member Data
*/
public static function fetchEntries( $ids, $type, $parseMember=true )
{
$return = array();
if ( is_array( $ids ) AND count( $ids ) AND $type )
{
$ids = IPSLib::cleanIntArray( $ids );
ipsRegistry::DB()->build( array( 'select' => 'l.*',
'from' => array( 'core_soft_delete_log' => 'l' ),
'where' => 'sdl_obj_id IN (' . implode( ',', $ids ) . ') AND sdl_obj_key='' . $type . ''',
'add_join' => array( array( 'select' => 'm.*',
'from' => array( 'members' => 'm' ),
'where' => 'l.sdl_obj_member_id=m.member_id' ),
array( 'select' => 'p.*',
'from' => array( 'profile_portal' => 'p' ),
'where' => 'l.sdl_obj_member_id=p.pp_member_id' ) ) ) );
$i = ipsRegistry::DB()->execute();
while( $row = ipsRegistry::DB()->fetch( $i ) )
{
if ( $parseMember )
{
$row['member'] = IPSMember::buildDisplayData( $row );
}
$return[ $row['sdl_obj_id'] ] = $row;
}
return $return;
}
return array();
}
}
class IPSContentCache
{
/**
* Keep track of what tables are linked to which key
*
* @var array
*/
protected static $_tables = array( 'post' => 'content_cache_posts',
'sig' => 'content_cache_sigs' );
/**
* Keep track of what settings are linked to which key
*
* @var array
*/
protected static $_settings = array( 'post' => 'cc_posts',
'sig' => 'cc_sigs' );
/**
* Check to see whether content caching is enabled
*
* @return boolean
*/
static public function isEnabled()
{
return ( ipsRegistry::$settings['cc_on'] AND ( ipsRegistry::$settings['cc_posts'] OR ipsRegistry::$settings['cc_sigs'] ) ) ? TRUE : FALSE;
}
/**
* Check to see whether we have a valid type
*
* @param string Content Type (post/sig/etc)
* @return boolean
*/
static public function isValidType( $type )
{
return isset( self::$_tables[ $type ] ) ? TRUE : FALSE;
}
/**
* Fetch correct table name based on type
* Assumes isValidType has been run
*
* @param string Content Type (post/sig/etc)
* @return boolean
*/
static public function fetchTableName( $type )
{
return self::$_tables[ $type ];
}
/**
* Fetch correct setting value based on type
* Assumes isValidType has been run
*
* @param string Content Type (post/sig/etc)
* @return boolean
*/
static public function fetchSettingValue( $type )
{
return ipsRegistry::$settings[ self::$_settings[ $type ] ];
}
/**
* Add data to the cache
*
* @param int Content ID
* @param string Content Type (post/sig/etc)
* @param string Content
* @param boolean Already had preDb/preDisplay run. It FALSE, assumed preDb has been run and no HTML will be parsed but smilies and bbcode will be
* @return bool
*/
static public function update( $id, $type, $content, $parsed=TRUE )
{
/* Enabled?? */
if ( ! self::isEnabled() )
{
return FALSE;
}
/* Valid type?? */
if ( ! self::isValidType( $type ) )
{
return FALSE;
}
/* Search engine? */
if ( ipsRegistry::member()->is_not_human === TRUE )
{
return FALSE;
}
/* Init */
$parsingSection = 'topics';
if ( $content AND $parsed !== TRUE )
{
/* What are we parsing? */
switch( $type )
{
case 'post':
$parsingSection = 'topics';
break;
case 'sig':
$parsingSection = 'signatures';
break;
}
/* Set up parser */
IPSText::getTextClass( 'bbcode' )->parse_smilies = 1;
IPSText::getTextClass( 'bbcode' )->parse_html = 0;
IPSText::getTextClass( 'bbcode' )->parse_nl2br = 1;
IPSText::getTextClass( 'bbcode' )->parse_bbcode = 1;
IPSText::getTextClass( 'bbcode' )->parsing_section = $parsingSection;
IPSText::getTextClass( 'bbcode' )->parsing_mgroup = ipsRegistry::member()->getProperty( 'member_group_id' );
IPSText::getTextClass( 'bbcode' )->parsing_mgroup_others = ipsRegistry::member()->getProperty( 'mgroup_others' );
/* Format */
$content = IPSText::getTextClass( 'bbcode' )->preDisplayParse( IPSText::getTextClass( 'bbcode' )->preDbParse( $content ) );
}
if ( $content )
{
ipsRegistry::DB()->setDataType( 'cache_content', 'string' );
ipsRegistry::DB()->replace( self::fetchTableName( $type ), array( 'cache_content_id' => $id,
'cache_content' => $content,
'cache_updated' => time() ), array( 'cache_content_id' ) );
}
else
{
/* No content, then drop it */
self::drop( $type, $id );
}
return TRUE;
}
/**
* Drop data from the cache
* If no ID is passed, it'll drop all caches for the supplied 'type'
*
* @param string Content Type (post/sig/etc)
* @param int/array [Content ID]
* @return bool
*/
static public function drop( $type, $id=0 )
{
if ( ! self::isEnabled() )
{
return FALSE;
}
/* Valid type?? */
if ( ! self::isValidType( $type ) )
{
return FALSE;
}
if ( $id )
{
if ( is_array( $id ) )
{
$id = implode( ',', $id );
}
ipsRegistry::DB()->delete( self::fetchTableName( $type ), "cache_content_id IN (" . $id . ")" );
}
else
{
ipsRegistry::DB()->delete( self::fetchTableName( $type ) );
}
return TRUE;
}
/**
* Remove all "type" data from the cache
*
* @param string [Content Type (post/sig/etc)]
* @return int Number of rows affected
*/
static public function truncate( $type='' )
{
if ( ! self::isEnabled() )
{
return 0;
}
/* Valid type?? */
if ( $type AND ! self::isValidType( $type ) )
{
return 0;
}
$affected = 0;
if ( $type )
{
$count = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as total', 'from' => self::fetchTableName( $type ) ) );
ipsRegistry::DB()->delete( self::fetchTableName( $type ) );
$affected = $count['total']; // ipsRegistry::DB()->getAffectedRows(); - With no where clause, mysql_affected_rows always returns 0
}
else
{
foreach( self::$_tables as $type => $name )
{
$count = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as total', 'from' => $name ) );
ipsRegistry::DB()->delete( $name );
$affected += $count['total']; // $affected += ipsRegistry::DB()->getAffectedRows(); - With no where clause, mysql_affected_rows always returns 0
}
}
return intval( $affected );
}
/**
* Count the number of cached items
*
* @param string [Content Type (post/sig/etc)]
* @return int Combined number of items
*/
static public function count( $type='' )
{
if ( ! self::isEnabled() )
{
return FALSE;
}
/* Valid type?? */
if ( $type AND ! self::isValidType( $type ) )
{
return FALSE;
}
$count = 0;
if ( $type )
{
$row = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as c', 'from' => self::fetchTableName( $type ) ) );
$count = intval( $row['c'] );
}
else
{
foreach( self::$_tables as $type => $name )
{
$row = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as c', 'from' => $name ) );
$count += intval( $row['c'] );
}
}
return intval( $count );
}
/**
* Prune items back to X days
*
* If no type is supplied, all types are pruned
*
* @param string [Content Type (post/sig/etc)]
* @return int Number of rows affected
*/
static public function prune( $type='' )
{
if ( ! self::isEnabled() )
{
return FALSE;
}
/* Valid type?? */
if ( $type AND ! self::isValidType( $type ) )
{
return FALSE;
}
$affected = 0;
if ( $type )
{
$time = time() - ( self::fetchSettingValue( $type ) * 86400 );
ipsRegistry::DB()->delete( self::fetchTableName( $type ), "cache_updated <" . $time );
$affected = ipsRegistry::DB()->getAffectedRows();
}
else
{
foreach( self::$_tables as $type => $name )
{
$time = time() - ( self::fetchSettingValue( $type ) * 86400 );
ipsRegistry::DB()->delete( self::fetchTableName( $type ), "cache_updated <" . $time );
$affected += ipsRegistry::DB()->getAffectedRows();
}
}
return intval( ipsRegistry::DB()->getAffectedRows() );
}
/**
* Fetch table join
*
* Cheap way of grabbing the join on the cache table so that your code
* doesn't have to check for whether we're using the cache or not, etc
*
* @param string Content Type (post/sig/etc)
* @param string Join field (eg 'p.pid')
* @param string [Table alias - default 'cca']
* @param string [Table join type - default 'left']
* @param string [Custom select so that fields can be aliased, etc]
* @return bool
*/
static public function join( $type, $joinField, $alias='cca', $joinType='left', $customSelect='' )
{
if ( ! self::isEnabled() )
{
return FALSE;
}
/* Valid type?? */
if ( ! self::isValidType( $type ) )
{
return FALSE;
}
return array( 'select' => ( $customSelect ) ? $customSelect : $alias.'.*',
'from' => array( self::fetchTableName( $type ) => $alias ),
'where' => $alias . '.cache_content_id=' . $joinField,
'type' => $joinType );
}
}
/**
* Experimental class for storing options as bitwise
*
* @author Matt
*/
class IPSBWOptions
{
/**
* Convert a bit field into an array of options
*
* @param int Bitwise option
* @param string Type of options to decipher (user / groups / etc)
* @param string App
* @return array
* <code>$options = IPSBWOptions::thawOptions( 18, 'user', 'forums' );</code>
*/
static public function thaw( $bitfield, $type, $app='global' )
{
/* INIT */
$bitfield = intval($bitfield);
$array = array();
/* Generate bitwise array */
$bitArray = self::_getBitWiseArray( $type, $app );
if ( ! $bitArray OR ! count( $bitArray ) )
{
return array();
}
/* Build options */
foreach( $bitArray as $key => $bitvalue )
{
if ( $bitfield & intval( $bitvalue ) )
{
$array[ $key ] = 1;
}
else
{
$array[ $key ] = 0;
}
}
return $array;
}
/**
* Build an SQL query bit
*
* @param string Field (field name as assigned by thaw)
* @param string SQL field
* @param string Type (members, groups, etc )
* @param string App (global, forums, etc)
* @param string Type of SQL query (add/remove/has)
* @return string Formatted SQL field
*/
static public function sql( $bitField, $sqlField, $type, $app='global', $sql='has' )
{
/* Generate int sign */
switch( $sql )
{
default:
case 'has':
$_sign = '&';
break;
case 'remove':
$_sign = '-';
break;
case 'add':
$_sign = '+';
break;
case 'invert':
$_sign = '^';
break;
}
/* Generate bitwise array */
$bitArray = self::_getBitWiseArray( $type, $app );
/* Do it.. .*/
if ( in_array( $bitField, array_keys( $bitArray ) ) )
{
if ( $sql == 'has' )
{
return '( ' . $sqlField . ' ' . $_sign . ' ' . $bitArray[ $bitField ] . ' ) != 0';
}
else
{
return '( ' . $sqlField . ' ' . $_sign . ' ' . $bitArray[ $bitField ] . ' )';
}
}
else
{
return FALSE;
}
}
/**
* Freeze options
* Converts an array of options array( 'key' => 0 ... ) into an int for saving in a DB field
*
* @param array Array of key => values to save
* @param string Type of options to save
* @param string App
* @return int
*/
static public function freeze( $toSave, $type, $app='global' )
{
/* INIT */
$int = 0;
/* Generate bitwise array */
$bitArray = self::_getBitWiseArray( $type, $app );
if ( ! $bitArray OR ! count( $bitArray ) )
{
return 0;
}
foreach( $bitArray as $key => $value )
{
if ( isset( $toSave[ $key ] ) )
{
if ( $toSave[ $key ] == 1 )
{
$int += $value;
}
}
}
return intval( $int );
}
/**
* Fetch and build the bitwise array
*
* @param string Array key to return
* @return array
*/
static protected function _getBitWiseArray( $type, $app )
{
$bitArray = array();
$allOptions = ipsRegistry::fetchBitWiseOptions( $app );
if ( is_array( $allOptions ) )
{
if ( isset( $allOptions[ $type ] ) AND is_array( $allOptions[ $type ] ) )
{
$n = 1;
foreach( $allOptions[ $type ] as $key )
{
$bitArray[ $key ] = $n;
$n *= 2;
}
}
}
return $bitArray;
}
}
/**
* Time Class
*
* Class for handling timestamps
*
*/
class IPSTime
{
/**
* Current timestamp
*
* @var integer
*/
protected static $timestamp = IPS_UNIX_TIME_NOW;
/**
* Number of seconds in a minute
*
* @var integer
*/
protected static $minute = 60;
/**
* Number of seconds in a hour
*
* @var integer
*/
protected static $hour = 3600;
/**
* Number of seconds in a day
*
* @var integer
*/
protected static $day = 86400;
/**
* Number of seconds in a week
*
* @var integer
*/
protected static $week = 604800;
/**
* Number of seconds in a year
*
* @var integer
*/
protected static $year = 220752000;
/**
* Months with 31 days
*
* @var array
*/
protected static $months_31 = array( 01, 03, 05, 07, 08, 10, 12 );
/**
* Months with 30 days
*
* @var array
*/
protected static $months_30 = array( 04, 06, 09, 11 );
/**
* time_class::dmy_format()
* Generates a time stamp for the day/month/year
*
* @param integer [$ts] Timestamp to format, self::$timestamp used if none specified
* @return @e void
*/
static public function dmy_format( $ts=0 )
{
/* Set the timestamp */
$_ts = ( $ts ) ? $ts : self::$timestamp;
/* Break it into dmy format */
$_ts = date( "m,d,Y", $_ts );
$_ts = explode( ",", $_ts );
/* Return the timestamp */
return mktime( 0, 0, 0, $_ts[0], $_ts[1], $_ts[2] );
}
/**
* time_class::time_ago()
* Returns how long ago the specified time stamp was
*
* @param integer $ts Timestamp to format
* @return @e void
*/
static public function time_ago( $ts )
{
if( $ts == time() )
{
return '--';
}
if( $ts < 60 )
{
$plural = ( $ts == 1 ) ? '' : 's';
return sprintf( "%0d", $ts ) . " second$plural";
}
else if( $ts < self::$hour )
{
$plural = ( sprintf("%0d", ( $ts / self::$minute ) ) == 1 ) ? '' : 's';
return sprintf("%0d", ( $ts / self::$minute ) ) . " minute$plural";
}
else if( $ts < self::$day )
{
$plural = ( sprintf("%0d", ( $ts / self::$hour ) ) == 1 ) ? '' : 's';
return sprintf("%0d", ( $ts / self::$hour ) ) . " hour$plural";
}
else
{
$plural = ( sprintf("%0d", ( $ts / self::$day ) ) == 1 ) ? '' : 's';
return sprintf("%0d", ( $ts / self::$day ) ) . " day$plural";
}
}
/**
* Set the timestamp
*
* @param int New timestamp
* @return @e void
*/
static public function setTimestamp( $time )
{
self::$timestamp = $time;
}
/**
* Get the timestamp
*
* @return int Timestamp
*/
static public function getTimestamp()
{
return self::$timestamp;
}
/**
* time_class::add_minutes()
* Adds the specified number of minutes to the timestamp
*
* @param integer [$num] Number of minutes to add, 1 by default
* @return @e void
*/
static public function add_minutes( $num=1 )
{
self::$timestamp += self::$minute * $num;
}
/**
* time_class::add_hours()
* Adds the specified number of hours to the timestamp
*
* @param integer [$num] Number of hours to add, 1 by default
* @return @e void
*/
static public function add_hours( $num=1 )
{
self::$timestamp += self::$hour * $num;
}
/**
* time_class::add_days()
* Adds the specified number of days to the timestamp
*
* @param integer [$num] Number of days to add, 1 by default
* @return @e void
*/
static public function add_days( $num=1 )
{
self::$timestamp += self::$day * $num;
}
/**
* time_class::add_weeks()
* Adds the specified number of weeks to the timestamp
*
* @param integer [$num] Number of weeks to add, 1 by default
* @return @e void
*/
static public function add_weeks( $num=1 )
{
self::$timestamp += self::$week * $num;
}
/**
* time_class::add_month()
* Adds a single month to the current timestamp, takes into account leap years
*
* @return @e void
*/
static public function add_month()
{
$daysInMonth = date( 't', self::$timestamp );
self::$timestamp += self::add_days( intval( $daysInMonth ) );
}
/**
* time_class::add_months()
* Adds the specified number of months to the timestamp
*
* @param integer $num Number of months to add, 1 by default
* @return @e void
*/
static public function add_months( $num=1 )
{
for( $i = 0; $i < $num; $i++ )
{
self::add_month();
}
}
/**
* time_class::add_years()
* Adds the specified number of years to the timestamp
*
* @param integer $num Number of years to add, 1 by default
* @return @e void
*/
static public function add_years( $num=1 )
{
for( $i = 0; $i < $num; $i++ )
{
self::add_months( 12 );
//self::remove_days( 1 ); http://community.invisionpower.com/resources/bugs.html/_/ip-nexus/1-year-subscription-lose-one-day-r41507
}
}
/**
* time_class::remove_days()
* Removes the specified number of days to the timestamp
*
* @param integer [$num] Number of days to remove, 1 by default
* @return @e void
*/
static public function remove_days( $num=1 )
{
self::$timestamp -= self::$day * $num;
}
/**
* Convert unix timestamp into: (no leading zeros)
* array( 'day' => x, 'month' => x, 'year' => x, 'hour' => x, 'minute' => x );
* Written into separate static public function to allow for timezone to be used easily
*
* @param integer [$unix] Timestamp
* @return array Date parts
*/
static public function unixstamp_to_human( $unix=0 )
{
$tmp = gmdate( 'j,n,Y,G,i', $unix );
list( $day, $month, $year, $hour, $min ) = explode( ',', $tmp );
return array( 'day' => $day,
'month' => $month,
'year' => $year,
'hour' => $hour,
'minute' => $min );
}
/**
* Convert unix timestamp into mmddyyyy
*
* @param integer [$unix] Timestamp
* @param string [$sep] Separator
* @return string mm/dd/yyyy
*/
static public function unixstamp_to_mmddyyyy( $unix=0, $sep='/' )
{
if ( ! $unix )
{
return "";
}
$date = self::unixstamp_to_human( $unix );
return sprintf("%02d{$sep}%02d{$sep}%04d", $date['month'], $date['day'], $date['year'] );
}
/**
* Convert mmddyyyy into unix timestamp
*
* @param string [$date] Date
* @param string [$sep] Separator
* @param bool [$checkdate] Whether to validate date or not
* @return integer Timestamp
*/
static public function mmddyyyy_to_unixstamp( $date='', $sep='/', $checkdate=true )
{
if ( ! $date )
{
return "";
}
list( $month, $day, $year ) = explode( $sep, $date );
if ( $checkdate )
{
if ( ! checkdate( $month, $day, $year ) )
{
return "";
}
}
return self::human_to_unixstamp( $day, $month, $year, 0, 0 );
}
/**
* Wrapper for gmmktime (separated for timezone management)
*
* @param integer $day Day
* @param integer $month Month
* @param integer $year Year
* @param integer $hour Hour
* @param integer $minute Minute
* @return integer Timestamp
*/
static public function human_to_unixstamp( $day, $month, $year, $hour, $minute )
{
return gmmktime( intval($hour), intval($minute), 0, intval($month), intval($day), intval($year) );
}
/**
* My gmmktime() - PHP func seems buggy
*
* @param integer $hour Hour
* @param integer $min Minute
* @param integer $sec Second
* @param integer $month Month
* @param integer $day Day
* @param integer $year Year
* @return integer Timestamp
* @since 2.0
*/
static public function date_gmmktime( $hour=0, $min=0, $sec=0, $month=0, $day=0, $year=0 )
{
// Calculate UTC time offset
$offset = date( 'Z' );
// Generate server based timestamp
$time = mktime( $hour, $min, $sec, $month, $day, $year );
// Calculate DST on / off
$dst = intval( date( 'I', $time ) - date( 'I' ) );
return $offset + ($dst * 3600) + $time;
}
/**
* Hand rolled GETDATE method
*
* getdate doesn't work apparently as it doesn't take into account
* the offset, even when fed a GMT timestamp.
*
* @param integer Unix date
* @return array 0, seconds, minutes, hours, mday, wday, mon, year, yday, weekday, month
* @since 2.0
*/
static public function date_getgmdate( $gmt_stamp )
{
//$tmp = gmdate( 'j,n,Y,G,i,s,w,z,l,F,W,M', $gmt_stamp );
$format = '%e,%m,%Y,%H,%M,%S,%u,%j,%A,%B,%W,%b';
//-----------------------------------------
// Some flags not available on Windows
// @see http://www.php.net/manual/en/function.strftime.php#53340
//-----------------------------------------
if( strpos( strtolower( PHP_OS ), 'win' ) === 0 )
{
$mapping = array(
'%e' => sprintf("%' 2d", date("j", $gmt_stamp)),
'%u' => ($w = date("w", $gmt_stamp)) ? $w : 7,
);
$format = str_replace( array_keys($mapping), array_values($mapping), $format );
}
$tmp = gmstrftime( $format, $gmt_stamp );
list( $day, $month, $year, $hour, $min, $seconds, $wday, $yday, $weekday, $fmon, $week, $smon ) = explode( ',', $tmp );
return array( 0 => $gmt_stamp,
"seconds" => $seconds, // Numeric representation of seconds 0 to 59
"minutes" => $min, // Numeric representation of minutes 0 to 59
"hours" => $hour, // Numeric representation of hours 0 to 23
"mday" => trim($day), // Numeric representation of the day of the month 1 to 31
"wday" => $wday, // Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
"mon" => $month, // Numeric representation of a month 1 through 12
"year" => $year, // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
"yday" => $yday, // Numeric representation of the day of the year 0 through 365
"weekday" => $weekday, // A full textual representation of the day of the week Sunday through Saturday
"month" => $fmon, // A full textual representation of a month, such as January or Mar
"week" => $week, // Week of the year
"smonth" => $smon,
"smon" => $smon
);
}
}
/**
* IPSLib
*
* Dumping ground for functions that don't fit anywhere else
*
*/
class IPSLib
{
/**
* FURL Templates
*
* @param array
*/
static protected $_furlTemplates = array();
/**
* Search configs
*
* @param array
*/
static protected $_searchConfigs = array();
/**
* Log in methods
*
* @param array
*/
static protected $_lims = array();
/**
* Returns the class name to be instantiated, the class file will already be included
*
* @param string $filePath File location of the class (leave an empty string if you've already loaded the main file)
* @param string $className Name of the class
* @param string $app Application (defaults to 'core')
* @param bool $supressErrors If true, will require file with @ operator. Useful for third-party libraries which may throw PHP notices
* @return string Class Name
*/
static public function loadLibrary( $filePath, $className, $app='core', $supressErrors=false )
{
/* Get the class */
if ( $filePath != '' )
{
if ( $supressErrors )
{
@require_once( $filePath );/*noLibHook*/
}
else
{
require_once( $filePath );/*noLibHook*/
}
}
/* Check for hooks */
$hooksCache = ipsRegistry::cache()->getCache('hooks');
if( isset( $hooksCache['libraryHooks'][ $app ][ $className ] ) AND is_array( $hooksCache['libraryHooks'][ $app ][ $className ] ) AND count( $hooksCache['libraryHooks'][ $app ][ $className ] ) )
{
foreach( $hooksCache['libraryHooks'][ $app ][ $className ] as $classOverloader )
{
/* Hooks: Do we have the hook file? */
if( is_file( IPS_HOOKS_PATH . $classOverloader['filename'] ) )
{
require_once( IPS_HOOKS_PATH . $classOverloader['filename'] );/*noLibHook*/
if( class_exists( $classOverloader['className'] ) )
{
/* Hooks: We have the hook file and the class exists - reset the classname to load */
$className = $classOverloader['className'];
}
}
}
}
/* Return Class Name */
return $className;
}
/**
* Returns the class name to be instantiated, the class file will already be included
*
* @param string $filePath File location of the class
* @param string $className Name of the class
* @return string Class Name
*/
static public function loadActionOverloader( $filePath, $className )
{
/* Get the class */
require_once( $filePath );/*noLibHook*/
/* Hooks: Are we overloading this class? */
$hooksCache = ipsRegistry::cache()->getCache('hooks');
if( isset( $hooksCache['commandHooks'][ $className ] ) AND is_array( $hooksCache['commandHooks'][ $className ] ) AND count( $hooksCache['commandHooks'][ $className ] ) )
{
foreach( $hooksCache['commandHooks'][ $className ] as $classOverloader )
{
/* Hooks: Do we have the hook file? */
if( is_file( IPS_HOOKS_PATH . $classOverloader['filename'] ) )
{
require_once( IPS_HOOKS_PATH . $classOverloader['filename'] );/*noLibHook*/
if( class_exists( $classOverloader['className'] ) )
{
/* Hooks: We have the hook file and the class exists - reset the classname to load */
$className = $classOverloader['className'];
}
}
}
}
/* Return Class Name */
return $className;
}
/**
* Checks if there are any data hooks to run
*
* @param array $dataArray Data to be passed into the hooks
* @param string $hookLocation Location the data was sent from
* @return @e void
*/
static public function doDataHooks( &$dataArray, $hookLocation )
{
/* Loop through the cache */
$hooksCache = ipsRegistry::cache()->getCache( 'hooks' );
if( isset($hooksCache['dataHooks'][ $hookLocation ]) AND is_array( $hooksCache['dataHooks'][ $hookLocation ] ) AND count( $hooksCache['dataHooks'][ $hookLocation ] ) )
{
foreach( $hooksCache['dataHooks'][ $hookLocation ] as $r )
{
/* Check for hook file */
if( is_file( IPS_HOOKS_PATH . $r['filename'] ) )
{
/* Check for hook class */
require_once( IPS_HOOKS_PATH . $r['filename'] );/*noLibHook*/
if( class_exists( $r['className'] ) )
{
/* Create and run the hook */
$_hook = new $r['className'];
$newArray = $_hook->handleData( $dataArray );
/* Make sure the array isn't wiped out */
if( is_array( $newArray ) && count( $newArray ) )
{
$dataArray = $newArray;
}
}
}
}
}
}
/**
* Returns an array of data hook locations
*
* @return array
*/
static public function getDataHookLocations()
{
$_locations = array();
/* Loop all apps and get back our locations! */
foreach( ipsRegistry::$applications as $app_dir => $application )
{
$dataHookLocations = array();
if( is_file( IPSLib::getAppDir( $app_dir ) . '/extensions/dataHookLocations.php' ) )
{
require_once( IPSLib::getAppDir( $app_dir ) . '/extensions/dataHookLocations.php' );/*noLibHook*/
if ( is_array($dataHookLocations) && count($dataHookLocations) )
{
$_locations = array_merge( $_locations, $dataHookLocations );
}
}
}
return $_locations;
}
/**
* Checks to see if there is a template hook installed at the specified location
*
*
* @param string $group
* @param array $id
* @return bool
*/
static public function locationHasHooks( $group, $ids )
{
/* Return right away if we don't have an ids to check */
if( ! is_array( $ids ) || ! count( $ids ) )
{
return false;
}
/* Reformat the cache on the first call, to save processing later */
static $formattedCache = array();
if( !isset($formattedCache[ $group ]) )
{
$formattedCache[ $group ] = array();
$hookCache = ipsRegistry::cache()->getCache( 'hooks' );
if ( isset( $hookCache['templateHooks'][ $group ] ) AND is_array($hookCache['templateHooks'][ $group ]) AND count($hookCache['templateHooks'][ $group ]) )
{
foreach( $hookCache['templateHooks'][ $group ] as $_hook )
{
$formattedCache[ $group ][] = $_hook['id'];
}
}
}
/* Use formatted cache to check */
if( count( $formattedCache[ $group ] ) )
{
foreach( $ids as $id )
{
if( in_array( $id, $formattedCache[ $group ] ) )
{
return true;
}
}
}
return false;
}
/**
* Quickly determines if we've got VK enabled and set up
*
* @access public
* @return boolean
*/
static public function vkontakte_enabled()
{
return ( ipsRegistry::$settings['vk_enabled'] AND ipsRegistry::$settings['vk_api_id'] AND ipsRegistry::$settings['vk_secret'] ) ? TRUE : FALSE;
}
/**
* Central setlocale method so we can adjust as needed
*
* @param string Locale to set
* @return @e void
* @link http://community.invisionpower.com/tracker/issue-16386-language-locale-gives-error/
* @link http://community.invisionpower.com/tracker/issue-18424-change-lang-locale/
*/
static public function setlocale( $locale='' )
{
if( !$locale )
{
return;
}
$dodgy = array( 'tr_', 'az_', 'crh_', 'ku_', 'tt_', 'turkish' );
foreach( $dodgy as $lc )
{
if ( stripos( $locale, $lc ) !== FALSE )
{
setlocale( LC_COLLATE, $locale );
setlocale( LC_MONETARY, $locale );
setlocale( LC_NUMERIC, $locale );
setlocale( LC_TIME, $locale );
@setlocale( LC_MESSAGES, $locale );
return;
}
}
setlocale( LC_ALL, $locale );
}
/**
* Quickly determines if we've got FB enabled and set up
*
* @return boolean
*/
static public function fbc_enabled()
{
return ( ipsRegistry::$settings['fbc_enable'] AND ipsRegistry::$settings['fbc_appid'] AND ipsRegistry::$settings['fbc_secret'] ) ? TRUE : FALSE;
}
/**
* Quickly determines if we've got twitter enabled and set up
*
* @return boolean
*/
static public function twitter_enabled()
{
return ( ipsRegistry::$settings['tc_enabled'] AND ipsRegistry::$settings['tc_token'] AND ipsRegistry::$settings['tc_secret'] ) ? TRUE : FALSE;
}
/**
* Quickly determines if we've got other log in enabled and set up
*
* @return boolean
*/
static public function loginMethod_enabled( $method )
{
if ( ! count( self::$_lims ) )
{
if ( is_array( ipsRegistry::cache()->getCache('login_methods') ) )
{
$cache = ipsRegistry::cache()->getCache('login_methods');
foreach( $cache as $lim )
{
self::$_lims[ $lim['login_folder_name'] ] = $lim['login_folder_name'];
}
}
}
switch( $method )
{
case 'vkontakte':
return self::vkontakte_enabled();
break;
case 'facebook':
return self::fbc_enabled();
break;
case 'twitter':
return self::twitter_enabled();
break;
default:
return in_array( $method, self::$_lims ) ? true : false;
break;
}
}
/**
* Loop through the input request and create an array of ids based on a string prefix
*
* @param string Prefix
* @return array
*/
static public function fetchInputAsArray( $prefix='' )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$ids = array();
if( !$prefix )
{
return $ids;
}
//-----------------------------------------
// GET from checkboxes
//-----------------------------------------
foreach ( ipsRegistry::$request as $k => $v )
{
if ( $v && preg_match( "/^" . $prefix . '(d+)$/', $k, $match ) )
{
$ids[] = $match[1];
}
}
$ids = self::cleanIntArray( $ids );
return $ids;
}
/**
* Little function to return the version number data
*
* Handy to use when dealing with IN_DEV, etc
* Uses the constant where available
*
* @param string App ( Default 'core')
* @return array array( 'long' => x, 'human' => x )
*/
static public function fetchVersionNumber( $app='core' )
{
if ( ! defined( IPB_VERSION ) OR ! defined( IPB_LONG_VERSION ) )
{
require_once( IPS_ROOT_PATH . 'setup/sources/base/setup.php' );/*noLibHook*/
$XMLVersions = IPSSetUp::fetchXmlAppVersions( $app );
$tmp = $XMLVersions;
krsort( $tmp );
foreach( $tmp as $long => $human )
{
$return = array( 'long' => $long, 'human' => $human );
break;
}
}
else
{
$return = array( 'long' => IPB_LONG_VERSION, 'human' => IPB_VERSION );
}
return $return;
}
/**
* Cheeky little function to locate group table fields from other apps
*
* @return array Array of fields from different apps
*/
static function fetchNonDefaultGroupFields()
{
$fields = array();
foreach( array( 'calendar', 'gallery', 'blog', 'downloads', 'ccs', 'ipchat', 'nexus', 'ipseo' ) as $app )
{
$_file = IPSLib::getAppDir( $app ) . '/setup/versions/install/sql/' . $app . '_mysql_tables.php';
if ( is_file( $_file ) )
{
$TABLE = array();
require( $_file );/*noLibHook*/
foreach( $TABLE as $t )
{
if ( preg_match( '#^ALTER TABLEs+?groupss+?ADDs+?(S+?)s#i', $t, $match ) )
{
$fields[] = $match[1];
}
}
}
}
return $fields;
}
/**
* Update settings
*
* @param array array('conf_key' => 'new value')
* @return true/false
*/
static public function updateSettings( $update=array(), $parseEditorValues=FALSE )
{
//-----------------------------------------
// Load the settings we need to update
//-----------------------------------------
$fields = array_keys( $update );
ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'core_sys_conf_settings', 'where' => "conf_key IN ('" . implode( "','", $fields ) . "')" ) );
ipsRegistry::DB()->execute();
$db_fields = array();
while ( $r = ipsRegistry::DB()->fetch() )
{
$db_fields[ $r['conf_key'] ] = $r;
}
if ( empty( $db_fields ) )
{
return false;
}
/* We have to examine $_POST as the custom PHP for some settings
still sets that rather than $value */
$oldPostData = $_POST;
//-----------------------------------------
// Update values
//-----------------------------------------
foreach( $db_fields as $key => $data )
{
$value = null;
/* Init */
if ( !is_array( $update[ $key ] ) )
{
if ( $update[ $key ] != $data['conf_default'] )
{
$value = str_replace( "'", "'", IPSText::stripslashes( $update[ $key ] ) );
$value = $value == '' ? "{blank}" : $value;
}
else
{
$value = $data['conf_default'];
}
}
/* Evaluate PHP */
if ( $data['conf_evalphp'] )
{
$data['conf_evalphp'] = str_replace( '\', '\', $data['conf_evalphp'] );
$save = 1;
$data['conf_evalphp'] = str_replace( '$this->registry', 'ipsRegistry::instance()', $data['conf_evalphp'] );
$data['conf_evalphp'] = str_replace( '$this->cache', 'ipsRegistry::cache()', $data['conf_evalphp'] );
$data['conf_evalphp'] = str_replace( '$this->DB', 'ipsRegistry::DB()', $data['conf_evalphp'] );
$data['conf_evalphp'] = str_replace( '$this->settings', 'ipsRegistry::$settings', $data['conf_evalphp'] );
$data['conf_evalphp'] = str_replace( '$this->lang', 'ipsRegistry::getClass('class_localization')', $data['conf_evalphp'] );
eval( $data['conf_evalphp'] );
if ( $_POST[ $key ] !== $oldPostData[ $key ] )
{
$value = $_POST[ $key ];
}
/* Was value set? */
if ( $value === null )
{
$value = str_replace( "'", "'", IPSText::stripslashes( $update[ $key ] ) );
$value = $value == '' ? "{blank}" : $value;
}
}
/* Parse */
if ( $parseEditorValues and $data['conf_type'] == 'editor' )
{
IPSText::getTextClass('bbcode')->bypass_badwords = 1;
IPSText::getTextClass('bbcode')->parse_smilies = 1;
IPSText::getTextClass('bbcode')->parse_html = 1;
IPSText::getTextClass('bbcode')->parse_bbcode = 1;
IPSText::getTextClass('bbcode')->parse_nl2br = 1;
if( trim($value) == '<br>' OR trim($value) == '<br />' )
{
$value = '';
}
else
{
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/editor/composite.php', 'classes_editor_composite' );
$editor = new $classToLoad();
$value = $editor->process( $value );
}
}
/* Strip new lines */
$value = str_replace( "r", "", $value );
/* Save */
if ( $value != $data['conf_default'] )
{
ipsRegistry::DB()->update( 'core_sys_conf_settings', array( 'conf_value' => $value ), 'conf_id=' . $data['conf_id'] );
}
else if( ( isset( $update[ $key ] ) && $update[ $key ] == '' ) || ( $update[ $key ] == $data['conf_default'] ) || $data['conf_value'] != '' )
{
ipsRegistry::DB()->update( 'core_sys_conf_settings', array( 'conf_value' => '' ), 'conf_id=' . $data['conf_id'] );
}
}
//-----------------------------------------
// Recache
//-----------------------------------------
ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'core_sys_conf_settings', 'where' => 'conf_add_cache=1' ) );
$info = ipsRegistry::DB()->execute();
while ( $r = ipsRegistry::DB()->fetch($info) )
{
$value = $r['conf_value'] != "" ? $r['conf_value'] : $r['conf_default'];
if ( $value == '{blank}' )
{
$value = '';
}
$settings[ $r['conf_key'] ] = $value;
}
ipsRegistry::cache()->setCache( 'settings', $settings, array( 'array' => 1 ) );
//-----------------------------------------
// Return
//-----------------------------------------
return true;
}
/**
* Retrieve the default language
*
* @return string Default language id (most likely a number)
*/
static public function getDefaultLanguage()
{
$cache = ipsRegistry::cache()->getCache('lang_data');
if( !count($cache) OR !is_array($cache) )
{
ipsRegistry::getClass('class_localization')->rebuildLanguagesCache();
$cache = ipsRegistry::cache()->getCache('lang_data');
}
$_default = 1;
foreach( $cache as $_lang )
{
if( $_lang['lang_default'] )
{
$_default = $_lang['lang_id'];
break;
}
}
return $_default;
}
/**
* Build a global caches array from the database
*
* @return @e array Array of global caches
*/
static public function buildGlobalCachesArray()
{
/* INIT */
$globalCaches = array();
/* Get apps with global caches */
ipsRegistry::DB()->build( array( 'select' => 'app_global_caches',
'from' => 'core_applications',
'where' => "app_enabled=1 AND app_global_caches != '' AND app_global_caches " . ipsRegistry::DB()->buildIsNull( false )
) );
ipsRegistry::DB()->execute();
while( $gc = ipsRegistry::DB()->fetch() )
{
$caches = explode( ',', $gc['app_global_caches'] );
foreach( $caches as $key )
{
if ( $key )
{
$globalCaches[] = $key;
}
}
}
/* Get hooks with global caches */
ipsRegistry::DB()->build( array( 'select' => 'hook_global_caches',
'from' => 'core_hooks',
'where' => "hook_enabled=1 AND hook_global_caches != '' AND hook_global_caches " . ipsRegistry::DB()->buildIsNull( false )
) );
ipsRegistry::DB()->execute();
while( $gc = ipsRegistry::DB()->fetch() )
{
$caches = explode( ',', $gc['hook_global_caches'] );
foreach( $caches as $key )
{
if ( $key )
{
$globalCaches[] = $key;
}
}
}
/* Let's have only unique values and return */
$globalCaches = array_unique( $globalCaches );
return $globalCaches;
}
/**
* Cache global caches
*
* @return @e boolean
*
* @exceptions
* CANNOT_WRITE Cannot write to cache file
* NO_DATA_TO_WRITE No data to write
*/
static public function cacheGlobalCaches()
{
/* Init vars */
$data = '';
$globalCaches = self::buildGlobalCachesArray();
/* Got any caches? */
if ( count( $globalCaches ) )
{
$_date = gmdate( 'r', time() );
$_var = var_export( $globalCaches, TRUE );
$data = <<<EOF
<?php
/**
* Global Caches cache. Do not attempt to modify this file.
* Please modify the relevant setting for each application or hook
*
* Written: {$_date}
*
* Why? Because Tera says so this time :P
*/
$GLOBAL_CACHES = {$_var};
EOF;
}
/* Got data to write? */
if ( $data )
{
if ( @file_put_contents( GLOBAL_CACHE_PATH, $data ) )
{
return TRUE;
}
else
{
throw new Exception( 'CANNOT_WRITE' );
}
}
else
{
/* No data? Delete any current file as well */
@unlink( GLOBAL_CACHE_PATH );
throw new Exception( 'NO_DATA_TO_WRITE' );
}
}
/**
* Build furl templates from FURL extensions
*
* @return array
*/
static public function buildFurlTemplates()
{
/* INIT */
$apps = array();
$_SEOTEMPLATES = array();
static $_apps = array();
/* Done this already? */
if ( self::$_furlTemplates )
{
return self::$_furlTemplates;
}
/**
* Get app data and cache - 1 query is better than 1 per app
*/
ipsRegistry::DB()->build( array( 'select' => 'app_directory, app_public_title, app_enabled', 'from' => 'core_applications' ) );
ipsRegistry::DB()->execute();
while( $_r = ipsRegistry::DB()->fetch() )
{
$_apps[ $_r['app_directory'] ] = $_r;
}
/* Because this is called before the cache is unpacked, we need to expensively grab all app dirs */
foreach( array( 'applications', 'applications_addon/ips', 'applications_addon/other' ) as $folder )
{
try
{
foreach( new DirectoryIterator( IPS_ROOT_PATH . $folder ) as $file )
{
if ( ! $file->isDot() AND $file->isDir() )
{
$_name = $file->getFileName();
if ( substr( $_name, 0, 1 ) != '.' )
{
/* Check if this app is enabled before including the templates.. */
$_check = isset($_apps[ $_name ]) ? $_apps[ $_name ] : array( 'app_public_title' => '', 'app_enabled' => 0 );
if ( $_check['app_public_title'] && $_check['app_enabled'] )
{
$apps[ $folder . '/' . $_name ] = $_name;
}
}
}
}
} catch ( Exception $e ) {}
}
/* First, add in core stuffs */
ipsRegistry::_loadCoreVariables();
$templates = ipsRegistry::_fetchCoreVariables('templates');
if ( is_array( $templates ) )
{
foreach( $templates as $key => $data )
{
self::$_furlTemplates[ $key ] = $data;
}
}
/* Loop over the applications and build */
foreach( $apps as $path => $app_dir )
{
$furltemplatesfile = IPS_ROOT_PATH . $path;
if(IPB_USE_ONLY_ID_FURL && is_file( $furltemplatesfile.'/extensions/furlTemplatesID.php'))
{
$furltemplatesfile.='/extensions/furlTemplatesID.php';
}
else
{
$furltemplatesfile.='/extensions/furlTemplates.php';
}
if ( is_file( $furltemplatesfile ) )
{
$_SEOTEMPLATES = array();
require( $furltemplatesfile );/*noLibHook*/
if ( is_array( $_SEOTEMPLATES ) && count( $_SEOTEMPLATES ) )
{
foreach( $_SEOTEMPLATES as $key => $data )
{
self::$_furlTemplates[ $key ] = $data;
}
}
}
}
/* Return for anyone else */
return self::$_furlTemplates;
}
/**
* Cache templates from FURL extensions
*
* @return boolean
* @exceptions
* CANNOT_WRITE Cannot write to cache file
* NO_DATA_TO_WRITE No data to write
*/
static public function cacheFurlTemplates()
{
if ( ! count( self::$_furlTemplates ) )
{
self::buildFurlTemplates();
}
if ( count( self::$_furlTemplates ) )
{
$_date = gmdate( 'r', time() );
$_var = var_export( self::$_furlTemplates, TRUE );
$data = <<<EOF
<?php
/**
* FURL Templates cache. Do not attempt to modify this file.
* Please modify the relevant 'furlTemplates.php' file in /{app}/extensions/furlTemplates.php
* and rebuild from the Admin CP
*
* Written: {$_date}
*
* Why? Because Matt says so.
*/
$templates = {$_var};
?>
EOF;
if ( ! @file_put_contents( FURL_CACHE_PATH, $data ) )
{
throw new Exception( 'CANNOT_WRITE' );
}
}
else
{
throw new Exception( 'NO_DATA_TO_WRITE' );
}
return TRUE;
}
/**
* Recursively cleans keys and values and
* inserts them into the input array
*
* @param mixed Input data
* @param array Storage array for cleaned data
* @param integer Current iteration
* @return array Cleaned data
*/
static public function parseIncomingRecursively( &$data, $input=array(), $iteration = 0 )
{
// Crafty hacker could send something like &foo[][][][][][]....to kill Apache process
// We should never have an input array deeper than 20..
if ( $iteration >= 20 )
{
return $input;
}
if ( ! is_array( $data ) )
{
return $input;
}
foreach( $data as $k => $v )
{
if ( is_array( $v ) )
{
$input[ $k ] = self::parseIncomingRecursively( $data[ $k ], array(), $iteration + 1 );
}
else
{
$k = IPSText::parseCleanKey( $k );
$v = IPSText::parseCleanValue( $v, false );
$input[ $k ] = $v;
}
}
return $input;
}
/**
* Recursively cleans values after settings have been loaded.
* Necessary for certain functions (such as whether to strip space chars or not)
*
* @param mixed Input data
* @param integer Current iteration
* @return array Cleaned data
*/
static public function postParseIncomingRecursively( $request, $iteration = 0 )
{
// Crafty hacker could send something like &foo[][][][][][]....to kill Apache process
// We should never have an input array deeper than 20..
if ( $iteration >= 20 OR !is_array($request) )
{
return $request;
}
foreach( $request as $k => $v )
{
if ( is_array( $v ) )
{
$request[ $k ] = self::postParseIncomingRecursively( $v, ++$iteration );
}
else
{
$v = IPSText::postParseCleanValue( $v );
$request[ $k ] = $v;
}
}
return $request;
}
/**
* Performs basic cleaning, Null characters, etc
*
* @param array Input data
* @return array Cleaned data
*/
static public function cleanGlobals( &$data, $iteration = 0 )
{
// Crafty hacker could send something like &foo[][][][][][]....to kill Apache process
// We should never have an input array deeper than 10..
if ( $iteration >= 10 )
{
return;
}
foreach( $data as $k => $v )
{
if ( is_array( $v ) )
{
self::cleanGlobals( $data[ $k ], ++$iteration );
}
else
{
# Null byte characters
$v = str_replace( chr('0') , '', $v );
$v = str_replace( "" , '', $v );
$v = str_replace( "x00" , '', $v );
// @link http://community.invisionpower.com/tracker/issue-21188-post-processor-eating-characters/
//$v = str_replace( '%00' , '', $v );
# File traversal
$v = str_replace( "../", "../", $v );
/* RTL override */
$v = str_replace( '‮', '', $v );
$data[ $k ] = $v;
}
}
}
/**
* Fetch emoticons as JSON for editors, etc
*
* @param string Directory for emos [optional]
* @param bool Include emoticons not marked clickable
* @return string JSON
*/
static public function fetchEmoticonsAsJson( $emoDir='', $nonClickable=false )
{
$emoDir = ( $emoDir ) ? $emoDir : ipsRegistry::getClass('output')->skin['set_emo_dir'];
$emoArray = array();
$emoString = '';
$smilie_id = 0;
foreach( ipsRegistry::cache()->getCache('emoticons') as $elmo )
{
if ( $elmo['emo_set'] != $emoDir )
{
continue;
}
if ( ! $elmo['clickable'] AND !$nonClickable )
{
continue;
}
$smilie_id++;
//-----------------------------------------
// Make single quotes as URL's with html entites in them
// are parsed by the browser, so ' causes JS error :o
//-----------------------------------------
if ( strstr( $elmo['typed'], "'" ) )
{
$in_delim = '"';
}
else
{
$in_delim = "'";
}
$emoArray[] = $in_delim . addslashes($elmo['typed']) . $in_delim . ' : "' . $smilie_id . ','.$elmo['image'].'"';
}
//-----------------------------------------
// Finish up smilies...
//-----------------------------------------
if ( count( $emoArray ) )
{
$emoString = implode( ",n", $emoArray );
}
return $emoString;
}
/**
* Fetch bbcode as JSON for editors, etc
*
* @return string JSON
*/
static public function fetchBbcodeAsJson( $filter=array() )
{
$bbcodes = array();
$currentBbcodes = ipsRegistry::cache()->getCache('bbcode');
if ( $filter['noParseOnly'] == 1 )
{
/* Find no parse codes */
$noParse = array();
foreach( $currentBbcodes as $bbcode )
{
/* Allowed this BBCode? */
if ( $bbcode['bbcode_no_parsing'] )
{
/* CODE is a special case */
if ( $bbcode['bbcode_tag'] != 'code' )
{
$noParse[] = $bbcode['bbcode_tag'];
}
}
}
return json_encode($noParse);
}
/* Normal method */
$protectedBbcodes = array('right', 'left', 'center', 'b', 'i', 'u', 'url', 'img', 'quote', 'indent', 'snapback',
'list', 'strike', 'sub', 'sup', 'email', 'color', 'size', 'font' );
/* Remove protected bbcodes */
foreach( $protectedBbcodes as $_key )
{
unset( $currentBbcodes[ $_key ] );
}
/* Get all others */
foreach( $currentBbcodes as $bbcode )
{
if ( $bbcode['bbcode_groups'] != 'all' )
{
$pass = false;
$groups = array_diff( explode( ',', $bbcode['bbcode_groups'] ), array('') );
$mygroups = array( ipsRegistry::member()->getProperty('member_group_id') );
$mygroups = array_diff( array_merge( $mygroups, explode( ',', IPSText::cleanPermString( ipsRegistry::member()->getProperty('mgroup_others') ) ) ), array('') );
foreach( $groups as $g_id )
{
if( in_array( $g_id, $mygroups ) )
{
$pass = true;
break;
}
}
if ( ! $pass )
{
continue;
}
}
if ( ! empty( $filter['skip'] ) && is_array( $filter['skip'] ) )
{
if ( in_array( $bbcode['bbcode_tag'], $filter['skip'] ) )
{
continue;
}
}
$bbcodes[ $bbcode['bbcode_tag'] ] = array(
'id' => $bbcode['bbcode_id'],
'title' => $bbcode['bbcode_title'],
'desc' => $bbcode['bbcode_desc'],
'tag' => $bbcode['bbcode_tag'],
'useoption' => $bbcode['bbcode_useoption'],
'example' => $bbcode['bbcode_example'],
'switch_option' => $bbcode['bbcode_switch_option'],
'menu_option_text' => $bbcode['bbcode_menu_option_text'],
'menu_content_text' => $bbcode['bbcode_menu_content_text'],
'single_tag' => $bbcode['bbcode_single_tag'],
'optional_option' => $bbcode['bbcode_optional_option'],
'aliases' => explode( ',', $bbcode['bbcode_aliases'] ),
'image' => $bbcode['bbcode_image'],
);
}
return IPSText::simpleJsonEncode($bbcodes);
}
/**
* Runs the specified member sync module, takes a variable number of arguments.
*
* @param string $module The module to run, ex: onCreateAccount, onRegisterForm, etc
* @param mixed ... Remaining params should match the module being called. ex: array of member data for onCreateAccount,
* or an id and email for onEmailChange
* @return @e void
*/
static public function runMemberSync( $module )
{
/* ipsRegistry::$applications only contains apps with a public title #15785 */
$app_cache = ipsRegistry::cache()->getCache('app_cache');
/* Params */
$params = func_get_args();
array_shift( $params );
/* Loop through applications */
foreach( $app_cache as $app_dir => $app )
{
/* Only if app enabled... */
if ( $app['app_enabled'] )
{
/* Setup */
$_file = self::getAppDir( $app['app_directory'] ) . '/extensions/memberSync.php';
/* Check for the file */
if ( is_file( $_file ) )
{
/* Get the file */
$_class = self::loadLibrary( $_file, $app['app_directory'] . 'MemberSync', $app['app_directory'] );
/* Check for the class */
if ( class_exists( $_class ) )
{
/* Create an object */
$_obj = new $_class();
/* Check for the module */
if ( method_exists( $_obj, $module ) )
{
/* Call it */
call_user_func_array( array( $_obj, $module ), $params );
IPSDebug::addLogMessage( $app_dir . '-' . $module, 'mem' );
}
}
}
}
}
/* IPS Connect Group Change */
if ( $module == 'onCompleteAccount' )
{
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/handlers/han_login.php', 'han_login' );
$han_login = new $classToLoad( ipsRegistry::instance() );
$han_login->init();
$han_login->validateAccount( $params[0] );
}
}
/**
* Pick the highest number from an array
* Used in classItemMarking.. figured it might be useful elsewhere...
*
* @param array Array of numbers
* @return integer Highest number in the array
*/
static public function fetchHighestNumber( $array )
{
if ( is_array( $array ) )
{
$_array = array();
foreach( $array as $number )
{
$_array[] = intval( $number );
}
sort( $_array );
return intval( array_pop( $_array ) );
}
else
{
return 0;
}
}
/**
* Hand-rolled 'is_writable' function to overcome
* annoyances with the PHP in built version
* Based on user notes at php.net (is_writable function comments)
*
* @param string Path to check
* @return boolean
*/
static public function isWritable( $path )
{
if ( substr( $path, -1 ) == '/' )
{
return self::isWritable( $path . uniqid( mt_rand() ) . '.tmp');
}
else if ( is_dir( $path ) )
{
return self::isWritable( $path.'/'.uniqid( mt_rand() ) . '.tmp');
}
$e = file_exists( $path );
$f = @fopen( $path, 'a' );
if ( $f === FALSE )
{
return FALSE;
}
fclose ($f );
if ( $e === FALSE )
{
unlink($path);
}
return TRUE;
}
/**
* Acts like PHPs next() but if the pointer is at the end of the array or it finds a false
* value, then it rewinds the array and starts over
*
* @param array Reference to an array
* @return mixed Next value in the array
*/
static public function next( &$array )
{
if ( ! is_array( $array ) )
{
return FALSE;
}
$next = next( $array );
if ( ! $next || $next === FALSE )
{
reset( $array );
$next = next( $array );
}
return $next;
}
/**
* Function to naturally sort an array by keys
*
* @param array Array to sort
* @return array Sorted array
*/
static public function knatsort( $array )
{
$_a = array_keys( $array );
$_b = array();
natsort( $_a );
foreach( $_a as $__a )
{
$_b[ $__a ] = $array[ $__a ];
}
return $_b;
}
/**
* Merges arrays like array_merge_recursive but replaces indentical keys
*
* @param array Array to merge
* @param array Array to merge
* @param array ...
* @return array Merged array
*/
static public function arrayMergeRecursive()
{
$arrays = func_get_args();
$base = array_shift( $arrays );
if ( ! is_array($base) )
{
$base = empty($base) ? array() : array($base);
}
foreach( $arrays as $append )
{
if ( ! is_array( $append ) )
{
$append = array( $append );
}
foreach( $append as $key => $value)
{
if ( ! array_key_exists( $key, $base ) and ! is_numeric( $key ) )
{
$base[$key] = $append[$key];
continue;
}
if ( is_array($value) or is_array( $base[$key] ) )
{
$base[$key] = self::arrayMergeRecursive( $base[$key], $append[$key] );
}
else if ( is_numeric( $key ) )
{
if ( ! in_array( $value, $base ) )
{
$base[] = $value;
}
}
else
{
$base[$key] = $value;
}
}
}
return $base;
}
/**
* Merge two arrays. Unlike array_merge, however, duplicate keys are ignored rather than overwritten.
*
* @param array
* @param array
* @return array
*/
static public function mergeArrays( $array1, $array2 )
{
$returnArray = $array1;
foreach( $array2 as $_key => $_value )
{
if( !array_key_exists( $_key, $returnArray ) )
{
$returnArray[ $_key ] = $_value;
}
}
return $returnArray;
}
/**
* arraySearchLoose
*
* @param string "Needle"
* @param array Array of text to search
* @return mixed Key of array, or false on failure
*/
static public function arraySearchLoose( $needle, $haystack )
{
if( !is_array( $haystack ) OR !count($haystack) OR ! $needle )
{
return false;
}
foreach( $haystack as $k => $v )
{
if( $v AND stripos( $v, $needle ) !== false )
{
return $k;
}
}
return false;
}
/**
* Get the application title. Uses lang file keys if present.
*
* @param string application
* @param bool Force public title
* @return string Text to show for application title
*/
static public function getAppTitle( $app, $forcePublic=false )
{
if ( ! $app )
{
return '';
}
return isset( ipsRegistry::getClass('class_localization')->words[ $app . '_display_title' ] ) ?
ipsRegistry::getClass('class_localization')->words[ $app . '_display_title' ] :
( ( IN_ACP AND !$forcePublic ) ? ipsRegistry::$applications[ $app ]['app_title'] : ipsRegistry::$applications[ $app ]['app_public_title'] );
}
/**
* Generates the app [ -> module ] path. The module is optional, if module is not
* specified then just the app dir is returned. If this is called from the ACP and module
* is present, then it'll return modules_admin, otherwise modules_public
*
* @param string application
* @param string module (optional)
* @return mixed Directory to app or module (or false if error)
*/
static public function getAppDir( $app, $module='' )
{
$location = '';
if ( ! $app OR !is_string($app) )
{
return FALSE;
}
/* Ok, chicken and egg scenario. Applications has not been set up - most likely because
we're using this function before the caches have been loaded and unpacked.
So we guess based on folder names.... */
if ( ! is_array( ipsRegistry::$applications ) OR ! count( ipsRegistry::$applications ) OR ! isset( ipsRegistry::$applications[ $app ] ) )
{
$location = self::extractAppLocationKey( $app );
}
else
{
$location = ipsRegistry::$applications[ $app ]['app_location'];
}
$pathBit = IPS_ROOT_PATH . 'applications';
switch ( $location )
{
default:
case 'root':
$pathBit .= '/' . $app;
break;
case 'ips':
$pathBit .= '_addon/ips/' . $app;
break;
case 'other':
$pathBit .= '_addon/other/' . $app;
break;
}
if ( $module )
{
$modulesFolder = ( IPS_AREA != 'admin' ) ? 'modules_public' : 'modules_admin';
return $pathBit . "/" . $modulesFolder . "/" . $module;
}
else
{
return $pathBit;
}
}
/**
* Extracts app_location from app key
*
* @param string File path
* @return string root, ips, other
*/
static public function extractAppLocationKey( $app )
{
/* Test core apps first... */
if ( is_dir( IPS_ROOT_PATH . 'applications/' . $app ) )
{
$location = 'root';
}
else if ( is_dir( IPS_ROOT_PATH . 'applications_addon/ips/' . $app ) )
{
$location = 'ips';
}
else
{
$location = 'other';
}
return $location;
}
/**
* Generates the app folder name, either "applications" or "applications_addon"
*
* @param string application
* @return mixed Directory to app or module (or false if error)
*/
static public function getAppFolder( $app )
{
if ( ! $app OR ! isset(ipsRegistry::$applications[ $app ]) )
{
return FALSE;
}
switch ( ipsRegistry::$applications[ $app ]['app_location'] )
{
default:
case 'root':
$pathBit = 'applications';
break;
case 'ips':
$pathBit = 'applications_addon/ips';
break;
case 'other':
$pathBit = 'applications_addon/other';
break;
}
return $pathBit;
}
/**
* Determines if the application can be searched
*
* @param string $app Application key
* @return bool
*/
static public function appIsSearchable( $app, $type='search' )
{
/* Init */
$_ck = '';
/* map config */
switch( strtolower( $type ) )
{
default:
case 'search':
$_ck = 'can_search';
break;
case 'vnc':
case 'newcontent':
case 'viewnewcontent':
$_ck = 'can_viewNewContent';
break;
case 'active':
case 'activecontent':
$_ck = 'can_activeContent';
break;
case 'usercontent':
case 'users':
case 'user':
$_ck = 'can_userContent';
break;
case 'vncwithfollowfilter':
$_ck = 'can_vnc_filter_by_followed';
break;
case 'vncwithunreadcontent':
$_ck = 'can_vnc_unread_content';
break;
case 'tags':
$_ck = 'can_searchTags';
break;
}
/* got anything? */
if ( ! is_array( self::$_searchConfigs ) OR ! count( self::$_searchConfigs ) )
{
$_needRebuild = false;
foreach( ipsRegistry::$applications as $_app => $data )
{
/* use the cache if we can */
if ( ! IN_DEV AND isset( ipsRegistry::$applications[$_app]['search'] ) AND is_array( ipsRegistry::$applications[$_app]['search'] ) AND count( ipsRegistry::$applications[$_app]['search'] ) )
{
if( IPSLib::appIsInstalled( $_app ) )
{
self::$_searchConfigs[ $_app ] = ipsRegistry::$applications[$_app]['search'];
}
}
else
{
$_file = IPSLib::getAppDir( $_app ) . '/extensions/search/config.php';
if ( IPSLib::appIsInstalled( $_app ) AND is_file( $_file ) )
{
$CONFIG = array();
require( $_file );/*noLibHook*/
if ( is_array( $CONFIG ) AND count( $CONFIG ) )
{
self::$_searchConfigs[ $_app ] = $CONFIG;
unset( $CONFIG );
$_needRebuild = true;
}
}
}
}
}
/* Do we need to rebuild application cache because we checked for search configs manually? */
if( $_needRebuild )
{
ipsRegistry::cache()->rebuildCache( 'app_cache', 'global' );
}
/* return */
if ( isset( self::$_searchConfigs[ $app ] ) AND is_array( self::$_searchConfigs[ $app ] ) AND count( self::$_searchConfigs[ $app ] ) )
{
return ( self::$_searchConfigs[ $app ][ $_ck ] ) ? true : false;
}
return false;
}
/**
* Checks to see if the given application is currently installed and enabled
*
* @param string $app
* @return bool
*/
static public function appIsInstalled( $app, $checkEnabled=true )
{
if ( isset( ipsRegistry::$applications[$app] ) )
{
if( $checkEnabled )
{
if( ipsRegistry::$applications[$app]['app_enabled'] )
{
return TRUE;
}
}
else
{
return TRUE;
}
}
return FALSE;
}
/**
* Verify an app supports an extension
*
* @param string Application
* @param array Array of extensions to check
* @return bool
*/
static public function appSupportsExtension( $app, $extensions=array() )
{
if( !$app )
{
return false;
}
if( !count($extensions) )
{
return true;
}
$application = ipsRegistry::$applications[ $app ];
if( $application['app_directory'] )
{
$c = 0;
foreach( $extensions as $e )
{
if ( ! empty( $application['extensions'][ $e ] ) )
{
$c++;
}
}
if ( $c == count( $extensions ) )
{
return true;
}
}
return false;
}
/**
* Get all enabled applications
*
* @param array Array of extensions an application must have to be returned
* @return Array
*/
static public function getEnabledApplications( $extensions=array(), $forceNotFromCache=FALSE )
{
$apps = array();
$extensions = is_string( $extensions ) ? array( $extensions ) : ( is_array( $extensions ) ? $extensions : array() );
static $cache = array();
$_key = md5( implode( ',', $extensions ) );
if( $cache[ $_key ] and !$forceNotFromCache )
{
return $cache[ $_key ];
}
if ( ! is_array( ipsRegistry::$applications ) OR ! count( ipsRegistry::$applications ) )
{
return array();
}
foreach( ipsRegistry::$applications as $appDir => $appData )
{
if ( self::appIsInstalled( $appDir, true ) )
{
if ( count($extensions) )
{
if ( is_array( $appData['extensions'] ) )
{
$c = 0;
foreach( $extensions as $e )
{
if ( ! empty( $appData['extensions'][ $e ] ) )
{
$c++;
}
}
if ( $c == count( $extensions ) )
{
$apps[ $appDir ] = $appData;
}
}
}
else
{
$apps[ $appDir ] = $appData;
}
}
}
$cache[ $_key ] = $apps;
return $cache[ $_key ];
}
/**
* Check to see if the givem module is currently installed and enabled
*
* @param string $module module_key
* @param string [$app] app_key, current application by default
* @return bool
*/
static public function moduleIsEnabled( $module, $app='' )
{
$app = $app ? $app : ipsRegistry::$current_application;
if ( !empty( ipsRegistry::$modules[$app] ) )
{
foreach( ipsRegistry::$modules[$app] as $_m )
{
if ( $_m['sys_module_key'] == $module )
{
return $_m['sys_module_visible'] == 1;
}
}
}
// Undefined, retun true
return TRUE;
}
/**
* Grab max post upload
*
* @return integer Max post size
*/
static public function getMaxPostSize()
{
$max_file_size = 16777216;
$tmp = 0;
$_post = @ini_get('post_max_size');
$_upload = @ini_get('upload_max_filesize');
if ( $_upload > $_post )
{
$tmp = $_post;
}
else
{
$tmp = $_upload;
}
if ( $tmp )
{
$max_file_size = $tmp;
unset($tmp);
preg_match( '#^(d+)(w+)$#', strtolower($max_file_size), $match );
if( $match[2] == 'g' )
{
$max_file_size = intval( $max_file_size ) * 1024 * 1024 * 1024;
}
else if ( $match[2] == 'm' )
{
$max_file_size = intval( $max_file_size ) * 1024 * 1024;
}
else if ( $match[2] == 'k' )
{
$max_file_size = intval( $max_file_size ) * 1024;
}
else
{
$max_file_size = intval( $max_file_size );
}
}
return $max_file_size;
}
/**
* Convert strlen to bytes
*
* @param integer string length (no chars)
* @return integer Bytes
* @since 2.0
*/
static public function strlenToBytes( $strlen=0 )
{
$dh = pow(10, 0);
return round( $strlen / ( pow(1024, 0) / $dh ) ) / $dh;
}
/**
* Takes a number of bytes and formats in k or MB as required
*
* @param string Size, in bytes
* @param boolean TRUE = no language class avaiable (during start up, debug, etc)
* @return string Size, in MB, KB or bytes, whichever is closest
*/
static public function sizeFormat($bytes="", $noLang=FALSE)
{
$retval = "";
if ( $noLang === FALSE )
{
$lang['sf_gb'] = ipsRegistry::getClass('class_localization')->words['sf_gb'] ? ipsRegistry::getClass('class_localization')->words['sf_gb'] : 'gb';
$lang['sf_mb'] = ipsRegistry::getClass('class_localization')->words['sf_mb'] ? ipsRegistry::getClass('class_localization')->words['sf_mb'] : 'mb';
$lang['sf_k'] = ipsRegistry::getClass('class_localization')->words['sf_k'] ? ipsRegistry::getClass('class_localization')->words['sf_k'] : 'kb';
$lang['sf_bytes'] = ipsRegistry::getClass('class_localization')->words['sf_bytes'] ? ipsRegistry::getClass('class_localization')->words['sf_bytes'] : 'b';
}
else
{
$lang['sf_gb'] = 'gb';
$lang['sf_mb'] = 'mb';
$lang['sf_k'] = 'kb';
$lang['sf_bytes'] = 'b';
}
if ( $bytes >= 1073741824 )
{
$retval = round($bytes / 1073741824 * 100 ) / 100 . $lang['sf_gb'];
}
else if ($bytes >= 1048576)
{
$retval = round($bytes / 1048576 * 100 ) / 100 . $lang['sf_mb'];
}
else if ($bytes >= 1024)
{
$retval = round($bytes / 1024 * 100 ) / 100 . $lang['sf_k'];
}
else
{
$retval = $bytes . $lang['sf_bytes'];
}
return $retval;
}
/**
* Makes int based arrays safe
* XSS Fix: Ticket: 24360 (Problem with cookies allowing SQL code in keys)
*
* @param array Array
* @return array Array (Cleaned)
* @since 2.1.4(A)
*/
static public function cleanIntArray( $array=array() )
{
$return = array();
if ( is_array( $array ) and count( $array ) )
{
foreach( $array as $k => $v )
{
$return[ intval($k) ] = intval($v);
}
}
return $return;
}
/**
* Loads an interface. Abstracted incase we change location / method
* of loading an interface
*
* @param string File name
* @return @e void
* @since 3.0.0
*/
static public function loadInterface( $filename )
{
//-----------------------------------------
// Very simple, currently.
//-----------------------------------------
require_once( IPS_ROOT_PATH . 'sources/interfaces/' . $filename );/*noLibHook*/
}
/**
* Scale a remote image
*
* @param string URL
* @param int Max width
* @param int Max height
* @return string width='#' height='#' string
*/
static public function getTemplateDimensions( $image, $width, $height )
{
if( empty( $width ) AND empty( $height ) )
{
return;
}
if( !$image )
{
return;
}
//-----------------------------------------
// Checking image dimensions via disk instead
// of http is faster...can we try that..?
//-----------------------------------------
if( strpos( $image, ipsRegistry::$settings['board_url'] ) === 0 )
{
$image = DOC_IPS_ROOT_PATH . str_replace( ipsRegistry::$settings['board_url'], '', $image );
}
//-----------------------------------------
// Dimensions
// If set maxwidth and no maxheight, then we want the script to
// reduce based on width only. And vice-versa.
//-----------------------------------------
$maxWidth = ( $width ) ? intval($width) : 1000000000;
$maxHeight = ( $height ) ? intval($height) : 1000000000;
//-----------------------------------------
// Existing dims
//-----------------------------------------
$_dims = @getimagesize( $image );
if( !$_dims[0] )
{
return;
}
$_newDims = IPSLib::scaleImage( array(
'cur_width' => $_dims[0],
'cur_height' => $_dims[1],
'max_width' => $maxWidth,
'max_height' => $maxHeight,
) );
//-----------------------------------------
// Process the tag and return the data
//-----------------------------------------
return " width='{$_newDims['img_width']}' height='{$_newDims['img_height']}'";
}
/**
* Given current dimensions + max dimensions, return scaled image dimensions constrained to maximums
*
* @param array Current dimensions + max dimensions
* @return array New image dimensions
* @since 2.0
*/
public static function scaleImage($arg)
{
// max_width, max_height, cur_width, cur_height
$ret = array(
'img_width' => $arg['cur_width'],
'img_height' => $arg['cur_height']
);
if ( $arg['cur_width'] > $arg['max_width'] )
{
$ret['img_width'] = $arg['max_width'];
$ret['img_height'] = ceil( ( $arg['cur_height'] * ( ( $arg['max_width'] * 100 ) / $arg['cur_width'] ) ) / 100 );
$arg['cur_height'] = $ret['img_height'];
$arg['cur_width'] = $ret['img_width'];
}
if ( $arg['cur_height'] > $arg['max_height'] )
{
$ret['img_height'] = $arg['max_height'];
$ret['img_width'] = ceil( ( $arg['cur_width'] * ( ( $arg['max_height'] * 100 ) / $arg['cur_height'] ) ) / 100 );
}
return $ret;
}
/**
* Retrieve all IP addresses a user (or multiple users) have used
*
* @param string Where clause for ip address
* @param string Defaults to 'All', otherwise specify which tables to check (comma separated)
* @return array Multi-dimensional array of found IP addresses in which sections
*/
static public function findIPAddresses( $ip_where, $tables_to_check='all' )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$ip_addresses = array();
$tables = array(
'admin_logs' => array( 'member_id', 'ip_address', 'ctime' ),
'dnames_change' => array( 'dname_member_id', 'dname_ip_address', 'dname_date' ),
'members' => array( 'member_id', 'ip_address', 'joined' ),
'message_posts' => array( 'msg_author_id', 'msg_ip_address', 'msg_date' ),
'moderator_logs' => array( 'member_id', 'ip_address', 'ctime' ),
'posts' => array( 'author_id', 'ip_address', 'post_date', array( 'pid' ) ),
'member_status_updates' => array( 'status_author_id', 'status_author_ip', 'status_date' ),
'profile_ratings' => array( 'rating_by_member_id', 'rating_ip_address', '' ),
//'sessions' => array( 'member_id', 'ip_address', 'running_time' ),
'topic_ratings' => array( 'rating_member_id', 'rating_ip_address', '' ),
'validating' => array( 'member_id', 'ip_address', 'entry_date' ),
'voters' => array( 'member_id', 'ip_address', 'vote_date', array( 'tid' ) ),
'error_logs' => array( 'log_member', 'log_ip_address', 'log_date' ),
);
//-----------------------------------------
// Check apps
// @see http://forums.invisionpower.com/tracker/issue-16966-members-download-manag/
//-----------------------------------------
foreach( ipsRegistry::$applications as $app_dir => $data )
{
if( is_file( IPSLib::getAppDir( $app_dir ) . "/extensions/coreExtensions.php") )
{
$classX = IPSLib::loadLibrary( IPSLib::getAppDir( $app_dir ) . "/extensions/coreExtensions.php", $app_dir . '_findIpAddress', $app_dir );
if( class_exists( $classX ) )
{
$ipLookup = new $classX( ipsRegistry::instance() );
if( method_exists( $ipLookup, 'getTables' ) )
{
$tables = array_merge( $tables, $ipLookup->getTables() );
}
}
}
}
//-----------------------------------------
// Got tables?
//-----------------------------------------
$_tables = explode( ',', $tables_to_check );
if( !is_array($_tables) OR !count($_tables) )
{
return array();
}
//-----------------------------------------
// Loop through them and grab the IPs
//-----------------------------------------
foreach( $tables as $tablename => $fields )
{
if( $tables_to_check == 'all' OR in_array( $tablename, $_tables ) )
{
$extra = '';
$ids = array();
if( $tablename == 'members' )
{
if( $fields[2] )
{
$extra = ', ' . $fields[2] . ' as date';
}
if( $fields[3] AND is_array($fields[3]) )
{
$extra .= ', ' . implode( ', ', $fields[3] );
}
ipsRegistry::DB()->build( array(
'select' => $fields[1] . $extra . ', member_id',
'from' => $tablename,
'where' => $fields[1] . $ip_where,
'group' => 'member_id, ip_address, joined',
'order' => 'joined DESC',
'limit' => array( 250 ),
) );
}
else
{
if( $fields[2] )
{
$extra = ', c.' . $fields[2] . ' as date';
}
if( $fields[3] AND is_array($fields[3]) )
{
$extra .= ', c.' . implode( ', c.', $fields[3] );
}
$extra .= ', c.' . $fields[1] . ' as ip_address, c.' . $fields[0];
ipsRegistry::DB()->build( array(
'select' => 'c.' . $fields[1] . $extra,
'from' => array( $tablename => 'c' ),
'where' => 'c.' . $fields[1] . $ip_where,
'order' => $fields[2] ? 'c.' . $fields[2] . ' DESC' : 'c.' . $fields[0] . ' DESC',
'group' => 'c.' . $fields[0] . ', c.' . $fields[1],
'limit' => array( 250 ),
'add_join' => array(
array(
'select' => 'm.member_id, m.members_display_name, m.email, m.posts, m.joined',
'from' => array( 'members' => 'm' ),
'where' => 'm.member_id=c.' . $fields[0],
'type' => 'left',
)
)
) );
}
ipsRegistry::DB()->execute();
$i = 0;
while( $r = ipsRegistry::DB()->fetch() )
{
if ( $r[ $fields[0] ] )
{
$ids[] = $r[ $fields[0] ];
}
if( $r[ $fields[1] ] )
{
$rawData[ ++$i ] = $r;
}
}
/* Get members */
$members = IPSMember::load( $ids, 'core' );
if ( is_array( $rawData ) and count ( $rawData ) )
{
foreach( $rawData as $idx => $data )
{
if ( $data[ $fields[0] ] && is_array( $members[ $data[ $fields[0] ] ] ) )
{
$ip_addresses[ $tablename ][ $idx ] = array_merge( $data, $members[ $data[ $fields[0] ] ] );
}
}
}
}
}
//-----------------------------------------
// Here are your IPs kind sir. kthxbai
//-----------------------------------------
return $ip_addresses;
}
/**
* Display a strip of share links
*
* @param string Document title (can be left blank and it will attempt to self-discover)
* @param array Addition params: url, cssClass, group [string template group], bit [string template bit], skip [array of share_keys to skip]
* @return string HAITHTEEEMEL
*/
static public function shareLinks( $title='', $params=array() )
{
$url = isset( $params['url'] ) ? $params['url'] : '';
$cssClass = isset( $params['cssClass'] ) ? $params['cssClass'] : 'topic_share left';
$group = isset( $params['group'] ) ? $params['group'] : 'global';
$bit = isset( $params['bit'] ) ? $params['bit'] : 'shareLinks';
$skip = isset( $params['skip'] ) ? $params['skip'] : array();
$override = isset( $params['overrideApp'] ) ? $params['overrideApp'] : '';
/* Disabled? */
if ( ! ipsRegistry::$settings['sl_enable'] )
{
return '';
}
/* Disable for bots */
if( ipsRegistry::member()->is_not_human )
{
return '';
}
$canon = ipsRegistry::getClass('output')->fetchRootDocUrl();
$url = ( $url ) ? $url : ipsRegistry::$settings['this_url'];
$raw = $url;
$canon = IPSText::base64_encode_urlSafe( ( $canon ) ? $canon : $url );
$title = IPSText::base64_encode_urlSafe( $title );
$url = IPSText::base64_encode_urlSafe( $url );
$cache = ipsRegistry::cache()->getCache('sharelinks');
if ( ! $cache OR ! is_array( $cache ) )
{
ipsRegistry::cache()->rebuildCache('sharelinks', 'global' );
$cache = ipsRegistry::cache()->getCache('sharelinks');
}
/* Check for required canonical urls or not */
foreach( $cache as $key => $data )
{
if ( is_array( $skip ) AND in_array( $key, $skip ) )
{
unset( $cache[ $key ] );
}
else
{
$cache[ $key ]['_rawUrl'] = $raw;
$cache[ $key ]['overrideApp'] = $override;
$cache[ $key ]['_url'] = $data['share_canonical'] ? $canon : $url;
}
}
return ipsRegistry::getClass('output')->getTemplate( $group )->$bit( $cache, $title, $canon, $cssClass );
}
/**
* Quick function to see if a string is serialized
* End up using something similar throughout the code
*
* @param string String to test
* @return boolean
*/
static public function isSerialized( $string )
{
if ( ! is_string( $string ) OR ! trim( $string ) )
{
return false;
}
/* Don't allow objects */
if ( preg_match( "#^(i|s|a|d):(.*)#si", $string ) !== false )
{
return true;
}
return false;
}
/**
* mixed safe_unserialize(string $serialized)
* Safely unserialize, that is only unserialize string, numbers and arrays, not objects
*
* @license Public Domain
* @author dcz (at) phpbb-seo (dot) com
*/
static public function safeUnserialize( $serialized )
{
// unserialize will return false for object declared with small cap o
// as well as if there is any ws between O and :
if ( is_string( $serialized ) && strpos( $serialized, " " ) === false )
{
if ( strpos( $serialized, 'O:' ) === false )
{
// the easy case, nothing to worry about
// let unserialize do the job
return @unserialize( $serialized );
}
else if ( ! preg_match('/(^|;|{|})O:[+-0-9]+:"/', $serialized ) )
{
// in case we did have a string with O: in it,
// but it was not a true serialized object
return @unserialize( $serialized );
}
}
return false;
}
/**
* Check for an IPv4 address
*
* @param string IP address
* @return boolean
*/
static public function validateIPv4( $IP )
{
if (function_exists('filter_var'))
{
return ( filter_var( $IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) ? true : false;
}
else
{
preg_match( '/^(d{1,3}).(d{1,3}).(d{1,3}).(d{1,3})$/', $IP, $match );
return ( $match[0] ) ? true : false;
}
}
/**
* Check an IPv6 address
*
* @link http://crisp.tweakblogs.net/blog/3049/ipv6-validation-more-caveats.html
* @param string IP address
* @return boolean
*/
static public function validateIPv6( $IP )
{
if ( strlen($IP) < 3 )
{
return $IP == '::';
}
if ( strpos( $IP, '.' ) )
{
$lastcolon = strrpos($IP, ':');
if ( ! ( $lastcolon && self::validateIPv4( substr( $IP, $lastcolon + 1) ) ) )
{
return false;
}
$IP = substr( $IP, 0, $lastcolon ) . ':0:0';
}
if ( strpos( $IP, '::' ) === false )
{
return preg_match('/A(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}z/i', $IP);
}
$colonCount = substr_count($IP, ':');
if ( $colonCount < 8 )
{
return preg_match('/A(?::|(?:[a-f0-9]{1,4}:)+):(?:(?:[a-f0-9]{1,4}:)*[a-f0-9]{1,4})?z/i', $IP);
}
// special case with ending or starting double colon
if ( $colonCount == 8 )
{
return preg_match('/A(?:::)?(?:[a-f0-9]{1,4}:){6}[a-f0-9]{1,4}(?:::)?z/i', $IP);
}
return false;
}
/**
* Has an active license or not
* @param boolean Allow perpetual license to count.
* @return boolean
*/
static public function hasActiveLicense( $allowPerpetual=true )
{
//return true;
$licenseData = ipsRegistry::cache()->getCache( 'licenseData' );
if ( ( ! $licenseData OR ! $licenseData['key']['_expires'] OR $licenseData['key']['_expires'] < IPS_UNIX_TIME_NOW OR $licenseData['key']['_expires'] == 9999999999 ) )
{
return false;
}
if ( $allowPerpetual !== true )
{
if ( stristr( $licenseData['name'], 'perpetual' ) )
{
return false;
}
}
return true;
}
/**
* Is the archive DB on another server?
* @return boolean
*/
static public function isUsingRemoteArchiveDB()
{
return ( ipsRegistry::$settings['archive_remote_sql_database'] && ipsRegistry::$settings['archive_remote_sql_user'] ) ? true : false;
}
}
/**
* IPSDebug
*
* Only useful when developing
*/
class IPSDebug
{
/**
* Memory debug array
*
* @var array Memory debug info
*/
static public $memory_debug = array();
/**
* Messages
*
* @var array Messages
*/
static protected $_messages = array();
/**
* Turn off constructor
*
* @return @e void
*/
private function __construct() {}
/**
* Start time
*
* @var integer Start time
*/
static protected $_starttime;
/**
* Add message
*
* @param string
* @return @e void
*/
static public function addMessage( $message )
{
self::$_messages[] = $message;
}
/**
* View any string as a hexdump.
*
* This is most commonly used to view binary data from streams
* or sockets while debugging, but can be used to view any string
* with non-viewable characters.
*
* @version 1.3.2
* @author Aidan Lister <aidan@php.net>
* @author Peter Waller <iridum@php.net>
* @link http://aidanlister.com/2004/04/viewing-binary-data-as-a-hexdump-in-php/
* @param string $data The string to be dumped
* @param bool $htmloutput Set to false for non-HTML output
* @param bool $uppercase Set to true for uppercase hex
* @param bool $return Set to true to return the dump
*/
static public function hexdump( $data, $htmloutput = true, $uppercase = false, $return = false )
{
// Init
$hexi = '';
$ascii = '';
$dump = ( $htmloutput === true ) ? '<pre>' : '';
$offset = 0;
$len = strlen( $data );
// Upper or lower case hexadecimal
$x = ( $uppercase === false ) ? 'x' : 'X';
// Iterate string
for( $i = $j = 0 ; $i < $len ; $i ++ )
{
// Convert to hexidecimal
$hexi .= sprintf( "%02$x ", ord( $data[$i] ) );
// Replace non-viewable bytes with '.'
if ( ord( $data[$i] ) >= 32 )
{
$ascii .= ( $htmloutput === true ) ? htmlentities( $data[$i] ) : $data[$i];
}
else
{
$ascii .= '.';
}
// Add extra column spacing
if ( $j === 7 )
{
$hexi .= ' ';
$ascii .= ' ';
}
// Add row
if ( ++ $j === 16 || $i === $len - 1 )
{
// Join the hexi / ascii output
$dump .= sprintf( "%04$x %-49s %s", $offset, $hexi, $ascii );
// Reset vars
$hexi = $ascii = '';
$offset += 16;
$j = 0;
// Add newline
if ( $i !== $len - 1 )
{
$dump .= "n";
}
}
}
// Finish dump
$dump .= $htmloutput === true ? '</pre>' : '';
$dump .= "n";
// Output method
if ( $return === false )
{
echo $dump;
}
else
{
return $dump;
}
}
/**
* Print a var in plain mode
* @param mixed $var
*/
static public function plainPrint( $var )
{
header( 'Content-type: text/plain; charset=UTF-8');
print var_export( $var, true );
exit();
}
/**
* Send a FirePHP message
*
* @param string $method Method to call
* @param string $vars Parameters to pass
* @return @e void
* @link http://www.firephp.org/HQ/
*/
static public function fireBug( $method, $parameters=array() )
{
if ( IN_DEV )
{
try
{
if( !class_exists( 'FB' ) )
{
require_once( IPS_KERNEL_PATH . '/FirePHPCore/fb.php' );/*noLibHook*/
}
if( $method == 'registerExceptionHandler' )
{
$firephp = FirePHP::getInstance(true);
$firephp->registerExceptionHandler();
}
if( $method == 'registerErrorHandler' )
{
$firephp = FirePHP::getInstance(true);
$firephp->registerErrorHandler();
}
if( method_exists( 'FB', $method ) )
{
$function = 'FB::' . $method;
call_user_func_array( $function, $parameters );
}
}
catch( Exception $e ) { }
}
}
/**
* Prettify a debug backtrace
*
* @param array Data from backtrace
* @return string
*/
static public function prettifyBackTrace( $debug )
{
$_dString = '';
if ( is_array( $debug ) and count( $debug ) )
{
foreach( $debug as $idx => $data )
{
/* Remove non-essential items */
if ( $data['class'] == 'dbMain' OR $data['class'] == 'ips_DBRegistry' OR $data['class'] == 'ipsRegistry' OR $data['class'] == 'ipsController' OR $data['class'] == 'ipsCommand' OR $data['class'] == 'db_driver_mysql' )
{
continue;
}
$_dbString[ $idx ] = array( 'file' => $data['file'],
'line' => $data['line'],
'function' => $data['function'],
'class' => $data['class'] );
}
}
if ( count( $_dbString ) )
{
$_error_string .= "n .--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------.";
$_error_string .= "n | File | Function | Line No. |";
$_error_string .= "n |----------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------|";
foreach( $_dbString as $i => $data )
{
if ( defined('DOC_IPS_ROOT_PATH') )
{
$data['file'] = str_replace( DOC_IPS_ROOT_PATH, '', $data['file'] );
}
/* Reset */
$data['func'] = "[" . $data['class'] . '].' . $data['function'];
/* Pad right */
$data['file'] = str_pad( $data['file'], 75 );
$data['func'] = str_pad( $data['func'], 78 );
$data['line'] = str_pad( $data['line'], 18 );
$_error_string .= "n | " . $data['file'] . "| " . $data['func'] . '| ' . $data['line'] . '|';
$_error_string .= "n '----------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------'";
}
}
return $_error_string;
}
/**
* Custom error handler
*
* @param integer Error number
* @param string Error string
* @param string Error file
* @param string Error line number
* @return @e void
*/
static public function errorHandler( $errno, $errstr, $errfile, $errline )
{
/* Did we turn off errors with @? */
if ( ! error_reporting() )
{
return;
}
/* Are we truly debugging? */
if ( IPS_ERROR_CAPTURE === FALSE )
{
return;
}
$errfile = str_replace( @getcwd(), "", $errfile );
$log = false;
$message = "> [$errno] $errstrn> > Line: $errlinen> > File: $errfile";
/* What do we have? */
switch ($errno)
{
case E_ERROR:
$log = true;
echo "<b>IPB ERROR</b> [$errno] $errstr (Line: $errline of $errfile)<br />n";
exit(1);
break;
case E_WARNING:
$log = true;
echo "<b>IPB WARNING</b> [$errno] $errstr (Line: $errline of $errfile)<br />n";
break;
case E_NOTICE:
$log = true;
break;
default:
return FALSE;
//Do nothing
break;
}
/* Logging? */
if ( $log )
{
if ( IPS_ERROR_CAPTURE === TRUE )
{
self::addLogMessage( $message, "phpNotices", false, true );
}
else
{
foreach( explode( ',', IPS_ERROR_CAPTURE ) as $class )
{
if ( preg_match( '#/' . preg_quote( $class, '#' ) . '.#', $errfile ) )
{
self::addLogMessage( $message, "phpNotices", false, true );
break;
}
}
}
}
}
/**
* Add a message to the log file
* Handy for __destruct stuff, etc
*
* @param string Message to add
* @param string Which file to add it to
* @param mixed False, or an array of vars to include in log
* @param bool Force log even if IPS_LOG_ALL is off - handy for on-the-fly debugging
* @param bool Unlink file before writing
* @return @e void
*/
static public function addLogMessage( $message, $file='debugLog', $array=FALSE, $force=FALSE, $unlink=FALSE )
{
/* Make sure IN_DEV is on to prevent logs filling up where people forget to turn it off... */
if ( ( defined( 'IPS_LOG_ALL' ) AND IPS_LOG_ALL === TRUE ) OR $force === TRUE )
{
if ( $unlink === TRUE )
{
@unlink( DOC_IPS_ROOT_PATH . 'cache/' . $file . '.cgi' );
}
/* Array to dump? */
if ( is_array( $array ) )
{
$message .= "n" . var_export( $array, TRUE );
}
$message = "n" . str_repeat( '-', 80 ) . "n> Time: " . time() . ' / ' . gmdate( 'r' ) . "n> URL: " . $_SERVER['REQUEST_URI'] . "n> " . $message;
@file_put_contents( DOC_IPS_ROOT_PATH . 'cache/' . $file . '.cgi', $message, FILE_APPEND );
}
}
/**
* Return messages
*
* @return array Stored messages
*/
static public function getMessages()
{
return self::$_messages;
}
/**
* Displays a templating error
* Only used when IN_DEV is on
*
* @param string Complete PHP error string
* @param string Text evaluated by PHP
* @return @e void
*/
static public function showTemplateError( $errorText, $evalCode )
{
$output = array();
$count = 0;
$openDiv = '<div style="width:95%;text-align:left; margin-auto; padding:10px; white-space:pre;border:1px solid black; background:#eee;font-family:'Courier New', Courier, Geneva;font-size:0.8em">';
$lineNumber = 0;
/* Convert text into lines */
$evalCode = preg_replace( "#r#", "n", $evalCode );
$lines = explode( "n", $evalCode );
if ( count( $lines ) )
{
foreach( $lines as $l )
{
$count++;
$output[ $count ] = htmlspecialchars($l);
}
}
/* Anything we can deal with? */
if ( strstr( $errorText, "eval()'d code" ) )
{
preg_match( '#eval()'d code</b> on line <b>(d+?)</b>#', $errorText, $matches );
if ( $matches[1] )
{
$lineNumber = $matches[1];
$output[ $lineNumber ] = "<span style='background:yellow;color:red;font-weight:bold'>" . $output[ $lineNumber ] . "</span>";
if ( $lineNumber > 20 )
{
$_lineNumber = $lineNumber - 20;
$output[ $_lineNumber ] = "<a name='line{$lineNumber}'></a>" . $output[ $_lineNumber ];
}
}
}
if ( count( $output ) )
{
if ( $lineNumber )
{
print "<h4>Parse Error on line: <a href='#line{$lineNumber}'>" . $lineNumber . "</a></h4>";
}
else
{
print "<h4>" . $errorText . "</h4>";
}
print $openDiv;
foreach( $output as $number => $data )
{
print "<span style='color:#BBB'>".$number."</span>" . ' : ' . $data . "<br />";
}
print "</div>";
exit();
}
/* Still here? */
print "<h4>" . $errorText . "</h4>";
print htmlspecialchars( $evalCode );
exit();
}
/**
* Get current memory usage
*
* @return integer Current memory usage
*/
static public function getMemoryDebugFlag()
{
if ( IPS_MEMORY_START AND function_exists( 'memory_get_usage' ) )
{
return memory_get_usage();
}
}
/**
* Set a memory debug flag
*
* @param string Comment to set
* @param integer Memory usage to compare against
* @return int Memory used
*/
static public function setMemoryDebugFlag( $comment, $init_usage=0 )
{
if ( IPS_MEMORY_START AND function_exists( 'memory_get_usage' ) )
{
$_END = memory_get_usage();
$_USED = $_END - $init_usage;
self::$memory_debug[] = array( $comment, $_USED );
return $_USED;
}
}
/**
* Start a timer
*
* @return @e void
*/
static public function startTimer()
{
$mtime = microtime ();
$mtime = explode (' ', $mtime);
$mtime = $mtime[1] + $mtime[0];
self::$_starttime = $mtime;
}
/**
* Stop the timer
*
* @return integer Length of time
*/
static public function endTimer()
{
$mtime = microtime ();
$mtime = explode (' ', $mtime);
$mtime = $mtime[1] + $mtime[0];
$endtime = $mtime;
$totaltime = round (($endtime - self::$_starttime), 5);
return $totaltime;
}
/**
* Start a timer (return value instead of storing locally)
*
* @return integer Time
*/
static public function startTimerInstance()
{
$mtime = microtime ( true );
$mtime = explode (' ', $mtime);
$mtime = isset( $mtime[1] ) ? $mtime[1] + $mtime[0] : $mtime[0];
return $mtime;
}
/**
* Stop the timer (compare against provided time instead of stored time)
*
* @param integer Start time
* @return integer Length of time
*/
static public function endTimerInstance( $startTime=0 )
{
$mtime = microtime ( true );
$mtime = explode (' ', $mtime);
$mtime = isset( $mtime[1] ) ? $mtime[1] + $mtime[0] : $mtime[0];
$endtime = $mtime;
$totaltime = round (($endtime - $startTime), 5);
return $totaltime;
}
/**
* Retrieve server load and update cache if appropriate
*
* @return string Server load
*/
static public function getServerLoad()
{
$load_limit = "--";
//-----------------------------------------
// Check cache first...
//-----------------------------------------
$cache = ipsRegistry::instance()->cache()->getCache('systemvars');
if( $cache['loadlimit'] )
{
$loadinfo = explode( "-", $cache['loadlimit'] );
if ( intval($loadinfo[1]) > (time() - 30) )
{
//-----------------------------------------
// Cache is less than 30 secs old, use it
//-----------------------------------------
$server_load_found = 1;
$load_limit = $loadinfo[0];
}
}
//-----------------------------------------
// No cache or it's old, check real time
//-----------------------------------------
if( !$server_load_found )
{
# @ supressor stops warning in > 4.3.2 with open_basedir restrictions
if ( @file_exists('/proc/loadavg') )
{
if ( $fh = @fopen( '/proc/loadavg', 'r' ) )
{
$data = @fread( $fh, 6 );
@fclose( $fh );
$load_avg = explode( " ", $data );
$load_limit = trim($load_avg[0]);
}
}
else if( strpos( strtolower( PHP_OS ), 'win' ) === 0 )
{
/*---------------------------------------------------------------
| typeperf is an exe program that is included with Win NT,
| XP Pro, and 2K3 Server. It can be installed on 2K from the
| 2K Resource kit. It will return the real time processor
| Percentage, but will take 1 second processing time to do so.
| This is why we shall cache it, and check only every 2 mins.
|
| Can also be obtained from COM, but it's extremely slow...
---------------------------------------------------------------*/
$serverstats = @shell_exec('typeperf "Processor(_Total)% Processor Time" -sc 1');
if( $serverstats )
{
$server_reply = explode( "n", str_replace( "r", "", $serverstats ) );
$serverstats = array_slice( $server_reply, 2, 1 );
$statline = explode( ",", str_replace( '"', '', $serverstats[0] ) );
$load_limit = round( $statline[1], 4 );
}
}
else
{
if ( $serverstats = @exec("uptime") )
{
preg_match( '/(?:averages)?: ([0-9.]+)(,|)[s]+([0-9.]+)(,|)[s]+([0-9.]+)/', $serverstats, $load );
$load_limit = $load[1];
}
}
$cache['loadlimit'] = $load_limit . "-" . time();
if( $load_limit )
{
ipsRegistry::instance()->cache()->setCache( 'systemvars', $cache, array( 'array' => 1 ) );
}
}
return $load_limit;
}
}
/**
* IPSCookie
*
* This deals with saving and writing cookies
*/
class IPSCookie
{
/**
* Sensitive cookies
*
* @var array Sensitive cookies
*/
static public $sensitive_cookies = array();
/**
* Handle cookies internally
* so that when you SET one it is available to GET in the same process
*
* @var array
*/
static protected $_cookiesSet = array();
/**
* Set a cookie.
*
* Abstract layer allows us to do some checking, etc
*
* @param string Cookie name
* @param string Cookie value
* @param integer Is sticky flag
* @param integer Number of days to expire cookie in
* @param bool If false, will set a cookie on the entire domain
* @param bool If $local is false, controls if the cookie prefix should be used
* @return @e void
* @since 2.0
*/
static public function set( $name, $value="", $sticky=1, $expires_x_days=0, $local=TRUE, $usePrefix=FALSE )
{
//-----------------------------------------
// Check
//-----------------------------------------
if ( !empty( ipsRegistry::$settings['no_print_header'] ) )
{
return;
}
/* Update internal array */
self::$_cookiesSet[ $name ] = $value;
//-----------------------------------------
// Auto serialize arrays
//-----------------------------------------
if ( is_array( $value ) )
{
$value = json_encode( $value );
}
//-----------------------------------------
// Set vars
//-----------------------------------------
if ( $sticky == 1 )
{
$expires = time() + 60*60*24*365;
}
else if ( $expires_x_days )
{
$expires = time() + ( $expires_x_days * 86400 );
}
else
{
$expires = FALSE;
}
//-----------------------------------------
// Finish up...
//-----------------------------------------
ipsRegistry::$settings['cookie_domain'] = ipsRegistry::$settings['cookie_domain'] == "" ? "" : ipsRegistry::$settings['cookie_domain'] ;
ipsRegistry::$settings['cookie_path'] = ipsRegistry::$settings['cookie_path'] == "" ? "/" : ipsRegistry::$settings['cookie_path'] ;
$_name = ( ( $local or $usePrefix ) ? ipsRegistry::$settings['cookie_id'] : '' ) . $name;
$_path = ( $local ? ipsRegistry::$settings['cookie_path'] : '/' );
if ( $local or substr( ipsRegistry::$settings['cookie_domain'], 0, 1 ) === '.' )
{
$_domain = ipsRegistry::$settings['cookie_domain'];
}
else
{
$parsedUrl = parse_url( ipsRegistry::$settings['board_url'] );
/* Test to make sure we're not using an IP address or hostname (such as localhost, etc) */
if ( ! strstr( $parsedUrl['host'], '.' ) || IPSLib::validateIPv4( $parsedUrl['host'] ) || IPSLib::validateIPv4( $parsedUrl['host'] ) )
{
$_domain = '';
}
else
{
$_domain = array();
foreach( array_reverse( explode( '.', $parsedUrl['host'] ) ) as $bit )
{
$_domain[] = $bit;
if ( ! in_array( $bit, array( 'aero', 'asia', 'biz', 'cat', 'com', 'coop', 'edu', 'gov', 'info', 'int', 'jobs', 'mil', 'mobi', 'museum', 'name', 'net', 'org', 'pro', 'tel', 'travel', 'ac', 'ad', 'ae', 'af', 'ag', 'ai', 'al', 'am', 'an', 'ao', 'aq', 'ar', 'as', 'at', 'au', 'aw', 'ax', 'az', 'ba', 'bb', 'bd', 'be', 'bf', 'bg', 'bh', 'bi', 'bj', 'bl', 'bm', 'bn', 'bo', 'br', 'bs', 'bt', 'bv', 'bw', 'by', 'bz', 'ca', 'cc', 'cd', 'cf', 'cg', 'ch', 'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'cr', 'cu', 'cv', 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm', 'do', 'dz', 'ec', 'ee', 'eg', 'eh', 'er', 'es', 'et', 'eu', 'fi', 'fj', 'fk', 'fm', 'fo', 'fr', 'ga', 'gb', 'gd', 'ge', 'gf', 'gg', 'gh', 'gi', 'gl', 'gm', 'gn', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk', 'hm', 'hn', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'im', 'in', 'io', 'iq', 'ir', 'is', 'it', 'je', 'jm', 'jo', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp', 'kr', 'kw', 'ky', 'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu', 'lv', 'ly', 'ma', 'mc', 'md', 'me', 'mg', 'mh', 'mk', 'ml', 'mm', 'mn', 'mo', 'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'mv', 'mw', 'mx', 'my', 'mz', 'na', 'nc', 'ne', 'nf', 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om', 'pa', 'pe', 'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'ps', 'pt', 'pw', 'py', 'qa', 're', 'ro', 'rs', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd', 'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl', 'sm', 'sn', 'so', 'sr', 'st', 'su', 'sv', 'sy', 'sz', 'tc', 'td', 'tf', 'tg', 'th', 'tj', 'tk', 'tl', 'tm', 'tn', 'to', 'tp', 'tr', 'tt', 'tv', 'tw', 'tz', 'ua', 'ug', 'uk', 'um', 'us', 'uy', 'uz', 'va', 'vc', 've', 'vg', 'vi', 'vn', 'vu', 'wf', 'ws', 'ye', 'yt', 'yu', 'za', 'zm', 'zw' ) ) )
{
break;
}
}
$_domain = '.' . implode( '.', array_reverse( $_domain ) );
}
}
//-----------------------------------------
// Set the cookie
//-----------------------------------------
if ( in_array( $name, self::$sensitive_cookies ) )
{
if ( PHP_VERSION < 5.2 )
{
if ( ipsRegistry::$settings['cookie_domain'] )
{
@setcookie( $_name, $value, $expires, $_path, $_domain . '; HttpOnly' );
}
else
{
@setcookie( $_name, $value, $expires, $_path );
}
}
else
{
@setcookie( $_name, $value, $expires, $_path, $_domain, NULL, TRUE );
}
}
else
{
@setcookie( $_name, $value, $expires, $_path, $_domain );
}
}
/**
* Get a cookie.
* Abstract layer allows us to do some checking, etc
*
* @param string Cookie name
* @param boolean Use prefix?
* @return mixed
* @since 2.0
*/
static public function get( $name, $usePrefix = true )
{
$_name = ( $usePrefix ? ipsRegistry::$settings['cookie_id'].$name : $name );
/* Check internal data first */
if ( isset( self::$_cookiesSet[ $name ] ) )
{
return self::$_cookiesSet[ $name ];
}
else if ( isset( $_COOKIE[ $_name ] ) )
{
$_value = $_COOKIE[ $_name ];
$_couldBeJson = stripslashes( urldecode( $_value ) );
$_test = json_decode( $_couldBeJson, true );
if ( is_array( $_test ) )
{
return IPSLib::parseIncomingRecursively( $_test );
}
else
{
return IPSText::parseCleanValue( urldecode( $_value ) );
}
}
else
{
return FALSE;
}
}
}
/**
* IPSText
*
* This deals with cleaning and parsing text items.
*/
class IPSText
{
/**
* Class Convert Object
*
* @var object
*/
static protected $classConvertCharset;
/**
* Default document character set
*
* @var string Character set
*/
static public $gb_char_set = 'UTF-8';
/**
* Remove dodgy control characters?
*
* @var boolean Remove emulated spaces (e.g. alt+160)
*/
static public $strip_space_chr = true;
/**
* Classes
*
* @var array
*/
static protected $_internalClasses = array();
/**
* Ensure no one can create this as an object
*
* @return @e void
*/
private function __construct() {}
/**
* Cleans/gets a file extension
*
* @param string $file
* @return string
*/
static public function getFileExtension( $string )
{
return ( strstr( $string, '.' ) ) ? strtolower( str_replace( ".", "", substr( $string, strrpos( $string, '.' ) ) ) ) : strtolower( $string );
}
/**
* Unconvert smilies
*
* @param string Raw text
* @return string Converted text
*/
public static function unconvertSmilies( $txt )
{
//-----------------------------------------
// Unconvert smilies
//-----------------------------------------
$txt = str_replace( "<#EMO_DIR#>", "<#EMO_DIR>", $txt );
preg_match_all( "#(<img(?:[^>]+?)class=['"]bbc_emoticon["'](?:[^>]+?)alt=['"](.+?)["'](?:[^>]+?)?>)#is", $txt, $matches );
if( is_array($matches[1]) AND count($matches[1]) )
{
foreach( $matches[1] as $index => $value )
{
if ( count( ipsRegistry::cache()->getCache('emoticons') ) > 0 )
{
foreach( ipsRegistry::cache()->getCache('emoticons') as $row )
{
$_emoCode = str_replace( '<', '<', str_replace( '>', '>', $row['typed'] ) );
if( $matches[2][ $index ] == $_emoCode )
{
/* We need to make sure emoticons are wrapped in spaces so they are parsed properly */
//$txt = str_replace( $value, ' ' . $_emoCode . ' ', $txt );
/* We are no longer matching opening/closing "space" so no need to add it */
$txt = str_replace( $value, $_emoCode, $txt );
continue 2;
}
}
}
}
}
$txt = str_replace( "<#EMO_DIR>", "<#EMO_DIR#>", $txt );
return $txt;
}
/**
* Simple JSON encode for when its not possible to convert data
* into UTF-8 (for example polls that display the contents, etc)
* This should only used for light lifting.
*
* @param array Simple array
* @return object
*/
static public function simpleJsonEncode( $array )
{
$final = array();
if ( is_array( $array ) )
{
foreach( $array as $k => $v )
{
$k = str_replace( '"', '"', $k );
if ( is_array( $v ) )
{
$v = self::simpleJsonEncode( $v );
}
else
{
$v = str_replace( '"', '"', $v );
$v = str_replace( "n", 'n', str_replace( "r", '', $v ) );
$v = '"' . $v . '"';
}
$final[] = '"' . $k . '":' . $v . '';
}
return '{' . implode( ",", $final ) . '}';
}
}
static public function jsonEncodeForTemplate( $data )
{
/* Using UTF-8 - it's an easy thing */
if ( IPS_DOC_CHAR_SET == 'UTF-8' )
{
$return = @json_encode( $data );
return ( $return ) ? $return : '{}';
}
if( is_array( $data ) )
{
/* convert */
array_walk_recursive( $data, array( 'IPSText', 'arrayWalkCallbackConvert' ) );
}
$jsonEncoded = json_encode( $data );
return IPSText::convertCharsets( $jsonEncoded, "UTF-8", IPS_DOC_CHAR_SET );
}
/**
* Get helper classes
* Used here to allow classes to be loaded and used as-and-when they're needed
*
* @param mixed Name of item requested
* @return object
*/
static public function getTextClass( $name )
{
if ( isset( self::$_internalClasses[ $name ] ) && is_object( self::$_internalClasses[ $name ] ) )
{
return self::$_internalClasses[ $name ];
}
else
{
switch( $name )
{
default:
case 'bbcode':
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . "sources/handlers/han_parse_bbcode.php", 'parseBbcode' );
$_class = new $classToLoad( ipsRegistry::instance() );
$_class->allow_update_caches = 1;
$_class->bypass_badwords = ipsRegistry::instance()->member() ? intval( ipsRegistry::instance()->member()->getProperty('g_bypass_badwords') ) : 0;
break;
case 'email':
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . "sources/handlers/han_email.php", 'hanEmail' );
$_class = new $classToLoad( ipsRegistry::instance() );
$_class->init();
break;
}
if ( is_object( $_class ) )
{
self::$_internalClasses[ $name ] = $_class;
return self::$_internalClasses[ $name ];
}
}
}
/**
* Encode for saving data into the DB that will be exported to XML
*
* Mostly used to ensure that data designed for UTF-8 XML files is actually stored as UTF-8 from
* 'flat' files that may not be saved as UTF-8.
*
* @param string Data in
* @return string Data out
*/
static public function encodeForXml( $string )
{
if ( function_exists( 'mb_detect_encoding' ) )
{
$encoding = mb_detect_encoding( $string );
if ( $encoding != 'UTF-8' )
{
$string = IPSText::convertCharsets( $string, $encoding );
}
}
elseif ( strtolower( IPS_DOC_CHAR_SET ) == 'utf-8' )
{
$string = utf8_encode( $string );
}
return $string;
}
/**
* Convert russian mounth names to english
*
* @access public
* @param string Date
* @return string Converted date
*/
static public function monthNameRu2En( $text )
{
return str_replace( array('Январь','Февраль','Март','Апрель','Май','Июнь,','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'), array('January','February','March','April','May','June','July','August','September','October','November','December'), $text );
}
static public function transliterate( $text )
{
return str_ireplace( array( '%D0%B0', '%D0%B1', '%D0%B2', '%D0%B3', '%D0%B4', '%D0%B5', '%D1%91', '%D0%B6', '%D0%B7', '%D0%B8', '%D0%B9', '%D0%BA', '%D0%BB', '%D0%BC', '%D0%BD', '%D0%BE', '%D0%BF', '%D1%80', '%D1%81', '%D1%82', '%D1%83', '%D1%84', '%D1%85', '%D1%86', '%D1%87', '%D1%88', '%D1%89', '%D1%8D', '%D1%8E', '%D1%8F', '%D1%8B', '%D1%8C', '%D1%8A', '%D0%90', '%D0%91', '%D0%92', '%D0%93', '%D0%94', '%D0%95', '%D0%81', '%D0%96', '%D0%97', '%D0%98', '%D0%99', '%D0%9A', '%D0%9B', '%D0%9C', '%D0%9D', '%D0%9E', '%D0%9F', '%D0%A0', '%D0%A1', '%D0%A2', '%D0%A3', '%D0%A4', '%D0%A5', '%D0%A6', '%D0%A7', '%D0%A8', '%D0%A9', '%D0%AD', '%D0%AE', '%D0%AF', '%D0%AB', '%D0%AC', '%D0%AA', 'а', 'б', 'в', 'г', 'д', 'е', 'ё', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', 'э', 'ю', 'я', 'ы', 'ь', 'ъ' ), array('a', 'b', 'v', 'g', 'd', 'e', 'yo', 'zh', 'z', 'i', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'u', 'f', 'kh', 'tc', 'ch', 'sh', 'sch', 'e', 'iu', 'ia', 'y', '', '', 'A', 'B', 'V', 'G', 'D', 'E', 'YO', 'ZH', 'Z', 'I', 'I', 'K', 'L', 'M', 'N', 'O', 'P', 'R', 'S', 'T', 'U', 'F', 'KH', 'TC', 'CH', 'SH', 'SCH', 'E', 'IU', 'IA', 'Y', '', '', 'a', 'b', 'v', 'g', 'd', 'e', 'yo', 'zh', 'z', 'i', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'u', 'f', 'kh', 'tc', 'ch', 'sh', 'sch', 'e', 'iu', 'ia', 'y', '', ''), $text);
}
static public function makeSeoTransliterate( $text )
{
if (!IPB_USE_SEO_TRANSLIT )
{
return $text;
}
/*if ( IPB_USE_ONLY_ID_FURL )
{
return '-';
}*/
if ((is_array($text)) && (count($text)))
{
foreach( $text as $k => $v)
{
$text[$k] = self::makeSeoTransliterate( $v );
}
return $text;
}
else if ( ( substr( $text, 0, 2 ) == '%%' AND substr( $text, -2 ) == '%%' ) )
{
return $text;
}
else
{
$text = self::mbstrtolower($text);
$text = self::transliterate( $text );
//$text = preg_replace('#[^%a-z0-9 ._-]#', '', $text);
}
return $text;
}
/**
* Make an SEO title for use in the URL
* We parse them even if friendly urls are off so that the data is there when you do switch it on
*
* @param string Raw SEO title or text
* @return string Cleaned up SEO title
*/
static public function makeSeoTitle( $text )
{
if ( ! $text )
{
return '';
}
/* Strip all HTML tags first */
$text = strip_tags($text);
/* Remove specific hex characters (/,<,>,#) as it confuses redirect engine */
$text = preg_replace( '#%(2f|3c|3e|23)#i', '', $text );
/* Preserve other %data */
$text = preg_replace('#%([a-fA-F0-9][a-fA-F0-9])#', '-xx-$1-xx-', $text);
$text = str_replace( array( '%', '`' ), '', $text);
$text = preg_replace('#-xx-([a-fA-F0-9][a-fA-F0-9])-xx-#', '%$1', $text);
/* Convert accented chars */
if ( IPS_DOC_CHAR_SET != 'UTF-8' )
{
/* http://community.invisionpower.com/resources/bugs.html/_/ip-board/i-broke-furls-urls-with-accents-r41236 */
/* AJAX requests have HTML entities, so convert to accents then romanize */
if ( strstr( $text, '&#' ) )
{
$text = html_entity_decode( $text, ENT_NOQUOTES, 'UTF-8' );
}
$text = self::convertAccents($text);
}
/* Convert it */
if ( self::isUTF8( $text ) )
{
if ( function_exists('mb_strtolower') )
{
$text = mb_strtolower($text, 'UTF-8');
}
$text = self::utf8Encode( $text, 250 );
}
/* Finish off */
$text = self::mbstrtolower($text);
if ( strtolower( IPS_DOC_CHAR_SET ) == 'utf-8' )
{
$text = preg_replace( '#&.+?;#' , '', $text );
$text = preg_replace( '#[^%a-z0-9 _-]#', '', $text );
}
else
{
$text = str_replace( array( '"', '&'), '', $text );
$text = preg_replace( '#&[#a-z0-9]{2,6};#i', '', $text );
$text = preg_replace( '#[^%&#;a-z0-9 _-]#', '', $text );
}
$text = str_replace( array( '`', ' ', '+', '.', '?', '_', '#', '&' ), '-', $text );
$text = preg_replace( "#-{2,}#", '-', $text );
$text = trim($text, '-');
$text = self::makeSeoTransliterate( $text );
IPSDebug::addMessage( "<span style='color:red'>makeSeoTitle ($text) called</span>" );
return ( $text ) ? $text : '-';
}
/**
* Seems like UTF-8?
* hmdker at gmail dot com {@link php.net/utf8_encode}
*
* @param string Raw text
* @return boolean
*/
static public function isUTF8($str) {
$c=0; $b=0;
$bits=0;
$len=strlen($str);
for($i=0; $i<$len; $i++)
{
$c=ord($str[$i]);
if($c > 128)
{
if(($c >= 254)) return false;
elseif($c >= 252) $bits=6;
elseif($c >= 248) $bits=5;
elseif($c >= 240) $bits=4;
elseif($c >= 224) $bits=3;
elseif($c >= 192) $bits=2;
else return false;
if(($i+$bits) > $len) return false;
while( $bits > 1 )
{
$i++;
$b = ord($str[$i]);
if($b < 128 || $b > 191) return false;
$bits--;
}
}
}
return true;
}
/**
* Converts accented characters into their plain alphabetic counterparts
*
* @param string Raw text
* @return string Cleaned text
*/
static public function convertAccents($string)
{
if ( ! preg_match('/[x80-xff]/', $string) )
{
return $string;
}
/* language abstracted? */
$romanization = IPS_ROOT_PATH . 'extensions/romanization.php';
$className = 'core_romanization';
if ( is_file( $romanization ) )
{
require_once( $romanization );
$rmz = new $className();
return $rmz->process( $string );
}
if ( self::isUTF8( $string) )
{
$_chr = array(
/* Latin-1 Supplement */
chr(195).chr(128) => 'Ae', chr(195).chr(129) => 'A',
chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
chr(195).chr(150) => 'Oe', chr(195).chr(153) => 'U',
chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
chr(195).chr(156) => 'Ue', chr(195).chr(157) => 'Y',
chr(195).chr(159) => 'ss', chr(195).chr(160) => 'a',
chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
chr(195).chr(163) => 'a', chr(195).chr(164) => 'ae',
chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
chr(195).chr(181) => 'o', chr(195).chr(182) => 'oe',
chr(195).chr(185) => 'u', chr(195).chr(186) => 'u',
chr(195).chr(187) => 'u', chr(195).chr(188) => 'ue',
chr(195).chr(189) => 'y', chr(195).chr(191) => 'y',
/* Latin Extended-A */
chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
chr(197).chr(146) => 'OE', chr(197).chr(147) => 'oe',
chr(197).chr(148) => 'R', chr(197).chr(149) => 'r',
chr(197).chr(150) => 'R', chr(197).chr(151) => 'r',
chr(197).chr(152) => 'R', chr(197).chr(153) => 'r',
chr(197).chr(154) => 'S', chr(197).chr(155) => 's',
chr(197).chr(156) => 'S', chr(197).chr(157) => 's',
chr(197).chr(158) => 'S', chr(197).chr(159) => 's',
chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
/* Euro Sign */
chr(226).chr(130).chr(172) => 'E',
/* GBP (Pound) Sign */
chr(194).chr(163) => '' );
$string = strtr($string, $_chr);
}
else
{
$_chr = array();
$_dblChars = array();
/* We assume ISO-8859-1 if not UTF-8 */
$_chr['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
.chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
.chr(195).chr(199).chr(200).chr(201).chr(202)
.chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
.chr(211).chr(212).chr(213).chr(217).chr(218)
.chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
.chr(231).chr(232).chr(233).chr(234).chr(235)
.chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
.chr(244).chr(245).chr(249).chr(250).chr(251)
.chr(252).chr(253).chr(255).chr(191).chr(182).chr(179).chr(166)
.chr(230).chr(198).chr(175).chr(172).chr(188)
.chr(163).chr(161).chr(177);
$_chr['out'] = "EfSZszYcYuAAAACEEEEIIIINOOOOUUUUYaaaaceeeeiiiinoooouuuuyyzslScCZZzLAa";
$string = strtr( $string, $_chr['in'], $_chr['out'] );
$_dblChars['in'] = array( chr(140), chr(156), chr(196), chr(197), chr(198), chr(208), chr(214), chr(216), chr(222), chr(223), chr(228), chr(229), chr(230), chr(240), chr(246), chr(248), chr(254));
$_dblChars['out'] = array('Oe', 'oe', 'Ae', 'Aa', 'Ae', 'DH', 'Oe', 'Oe', 'TH', 'ss', 'ae', 'aa', 'ae', 'dh', 'oe', 'oe', 'th');
$string = str_replace($_dblChars['in'], $_dblChars['out'], $string);
}
return $string;
}
/**
* Manually utf8 encode to a specific length
* Based on notes found at php.net
*
* @param string Raw text
* @param int Length
* @return string
*/
static public function utf8Encode( $string, $len=0 )
{
$_unicode = '';
$_values = array();
$_nOctets = 1;
$_unicodeLength = 0;
$stringLength = strlen( $string );
for ( $i = 0 ; $i < $stringLength ; $i++ )
{
$value = ord( $string[ $i ] );
if ( $value < 128 )
{
if ( $len && ( $_unicodeLength >= $len ) )
{
break;
}
$_unicode .= chr($value);
$_unicodeLength++;
}
else
{
if ( count( $_values ) == 0 )
{
$_nOctets = ( $value < 224 ) ? 2 : 3;
}
$_values[] = $value;
if ( $len && ( $_unicodeLength + ($_nOctets * 3) ) > $len )
{
break;
}
if ( count( $_values ) == $_nOctets )
{
if ( $_nOctets == 3 )
{
$_unicode .= '%' . dechex($_values[0]) . '%' . dechex($_values[1]) . '%' . dechex($_values[2]);
$_unicodeLength += 9;
}
else
{
$_unicode .= '%' . dechex($_values[0]) . '%' . dechex($_values[1]);
$_unicodeLength += 6;
}
$_values = array();
$_nOctets = 1;
}
}
}
return $_unicode;
}
/**
* Converts UTF-8 into HTML entities (xxx;) for correct display in browsers
*
* @param string UTF8 Encoded string
* @return string ..converted into HTML entities (similar to what a browser does with POST)
*/
public static function utf8ToEntities($string)
{
/*
* @see http://en.wikipedia.org/wiki/UTF-8#Description
* @link http://community.invisionpower.com/tracker/issue-23681-possible-addition/
*/
# Four-byte chars
$string = preg_replace( "/([360-364])([200-277])([200-277])([200-277])/e", "'&#' . ( ( ord('\1') - 240 ) * 262144 + ( ord('\2') - 128 ) * 4096 + ( ord('\3') - 128 ) * 64 + ( ord('\4') - 128 ) ) . ';'", $string );
/* Three byte chars */
$string = preg_replace( "/([340-357])([200-277])([200-277])/e", "'&#'.((ord('\1')-224)*4096 + (ord('\2')-128)*64 + (ord('\3')-128)).';'", $string );
/* Two byte chars */
$string = preg_replace("/([300-337])([200-277])/e", "'&#'.((ord('\1')-192)*64+(ord('\2')-128)).';'", $string);
return $string;
}
/**
* Strips out all non UTF-8 characters from a string
* This is best used when you have already converted / got UTF-8 data
* @param string In
* @return string Cleaned
*/
public static function stripNonUtf8( $string )
{
$string = preg_replace('/[x00-x08x10x0Bx0Cx0E-x19x7F]|(?<=^|[x00-x7F])[x80-xBF]+|([xC0xC1]|[xF0-xFF])[x80-xBF]*'.
'|[xC2-xDF]((?![x80-xBF])|[x80-xBF]{2,})|[xE0-xEF](([x80-xBF](?![x80-xBF]))|(?![x80-xBF]{2})|[x80-xBF]{3,})/', '?', $string );
$string = preg_replace('/xE0[x80-x9F][x80-xBF]|xED[xA0-xBF][x80-xBF]/S','?', $string );
return $string;
}
/**
* Decodes long named HTML entities in a string é without affecting other HTML entities (< etc) etc
* @param string Raw string
* @param string Converted string
*/
public static function decodeNamedHtmlEntities( $string )
{
/* Some manual conversions */
$manual = array( 'Alpha' => 'A', 'Beta' => 'B', 'alpha' => 'a', 'beta' => 'b' );
foreach( $manual as $o => $r )
{
$string = str_replace( '&' . $o . ';', $r, $string );
}
preg_match_all( '#&([^;s]+?);#', $string, $matches );
$entities = array();
$notList = array( 'amp', 'para', 'middot', 'gt', 'lt' );
foreach( $matches[0] as $word )
{
if ( preg_match( '#^&([a-zA-Z]{2,8});$#', $word, $match ) )
{
if ( ! in_array( $match[1], $notList ) )
{
$entities[] = $word;
}
}
}
/* -15 is the same as -1 but with Euro plus French and Finnish */
$charSet = ( IPS_DOC_CHAR_SET == 'ISO-8859-1' ) ? 'ISO-8859-15' : IPS_DOC_CHAR_SET;
$translated = @explode( ' ', @html_entity_decode( @implode( ' ', $entities ), ENT_NOQUOTES, $charSet ) );
if ( is_array( $translated ) AND count( $translated ) AND count( $translated ) == count( $entities ) )
{
return str_replace( $entities, $translated, $string );
}
return $string;
}
/**
* Returns an MD5 hash of content which has whitespace stripped.
* This is used in some classes to determine if content has changed without
* whitespace changes triggering it.
*
* @param string Incoming text
* @return string MD5 hash of whitespace stripped content
*/
public static function contentToMd5( $t )
{
return md5( trim( preg_replace( '#[stnr]#', "", $t ) ) );
}
/**
* Replace Recursively
*
* @param string Text to search in
* @param string Opening text to search for. (Example: <a href=)
* @param string Closing text to search for. (Example: >)
* @param mixed Call back function that handles the replacement. If using a class, pass array( $classname, $function ) THIS MUST BE A STATIC FUNCTION
* @return string Replaced text
* <code>
* # We want to replace all instances of <a href="http://www.domain.com"> with <a href="javascript:goLoad('domain.com')">
* $text = IPSText::replaceRecursively( $text, "<a href=", ">", array( 'myClass', 'replaceIt' ) );
* class myClass {
* static function replaceIt( $text, $openText, $closeText )
* {
* # $text contains the matched text between the tags, eg: "http://www.domain.com"
* # $openText contains the searched for opening, eg: <a href
* # $closeText contains the searched for closing, eg: >
* # Remove http...
* $text = str_replace( 'http://', '', $text )
* # Remove quotes
* $text = str_replace( array( '"', "'" ), '', $text );
* return '"javascript:goLoad('' . $text . '')"';
* }
* }
* </code>
*/
public static function replaceRecursively( $text, $textOpen, $textClose, $callBackFunction )
{
//----------------------------------------
// INIT
//----------------------------------------
# Tag specifics
$foundOpenText_pointer = 0;
$foundCloseText_pointer = 0;
$foundOpenTextRecurse_pointer = 0;
//----------------------------------------
// Keep the server busy for a while
//----------------------------------------
while ( 1 == 1 )
{
# Reset pointer
$startOfTextAfterOpenText_pointer = 0;
# See if we have any 'textOpen' at all
$foundOpenText_pointer = strpos( $text, $textOpen, $foundCloseText_pointer );
# No?
if ( $foundOpenText_pointer === FALSE )
{
break;
}
# Do we have any close text?
$foundCloseText_pointer = strpos( $text, $textClose, $foundOpenText_pointer );
# No?
if ( $foundCloseText_pointer === FALSE )
{
return $text;
}
# Reset pointer for text between the open and close text
$startOfTextAfterOpenText_pointer = $foundOpenText_pointer + strlen( $textOpen );
# Check recursively
$foundOpenTextRecurse_pointer = $startOfTextAfterOpenText_pointer;
while ( 1 == 1 )
{
# Got any open text again?
$foundOpenTextRecurse_pointer = strpos( $text, $textOpen, $foundOpenTextRecurse_pointer );
# No?
if ( $foundOpenTextRecurse_pointer === FALSE OR $foundOpenTextRecurse_pointer >= $foundCloseText_pointer )
{
break;
}
# Yes! Reset recursive pointer
$foundCloseTextRecurse_pointer = $foundCloseText_pointer + strlen( $textClose );
# Yes! Reset close normal pointer to next close tag FROM the last found close point
$foundCloseText_pointer = strpos( $text, $textClose, $foundCloseTextRecurse_pointer );
# Make sure we have a closing text
if ( $foundCloseText_pointer === FALSE )
{
return $text;
}
$foundOpenTextRecurse_pointer += strlen( $textOpen );
}
# This is the text between the open text and close text
$foundText = substr( $text, $startOfTextAfterOpenText_pointer, $foundCloseText_pointer - $startOfTextAfterOpenText_pointer );
# Recurse
if ( strpos( $foundText, $textOpen ) !== FALSE )
{
$foundText = IPSText::replaceRecursively( $foundText, $textOpen, $textClose, $callBackFunction );
}
# Run the call back...
$_newText = call_user_func( $callBackFunction, $foundText, $textOpen, $textClose );
# Run the replacement
$text = substr_replace( $text, $_newText, $foundOpenText_pointer, ( $foundCloseText_pointer - $foundOpenText_pointer ) + strlen( $textClose ) );
# Reset pointer
$foundCloseText_pointer = $foundOpenText_pointer + strlen($_newText);
}
return $text;
}
/**
* Reset Text Classes
*
* @param string Classname to search for
* @return boolean True if successful, false if not
*/
static public function resetTextClass( $name )
{
if ( ! is_object( self::$_internalClasses[ $name ] ) )
{
return false;
}
switch( $name )
{
default:
case 'bbcode':
self::$_internalClasses[ $name ]->allow_cache_updates = 1;
self::$_internalClasses[ $name ]->bypass_badwords = intval( ipsRegistry::instance()->member()->getProperty('g_bypass_badwords') );
self::$_internalClasses[ $name ]->parse_smilies = 1;
self::$_internalClasses[ $name ]->parse_nl2br = 1;
self::$_internalClasses[ $name ]->parse_html = 0;
self::$_internalClasses[ $name ]->parse_bbcode = 1;
self::$_internalClasses[ $name ]->parsing_section = 'post';
self::$_internalClasses[ $name ]->error = '';
self::$_internalClasses[ $name ]->parsing_mgroup = '';
self::$_internalClasses[ $name ]->parsing_mgroup_others = '';
break;
}
return true;
}
/**
* Clean _GET _POST key
*
* @param string Key name
* @return string Cleaned key name
* @since 2.1
*/
static public function parseCleanKey($key)
{
if ( $key === "" )
{
return "";
}
$key = htmlspecialchars( urldecode($key) );
$key = str_replace( ".." , "" , $key );
$key = preg_replace( '/__(.+?)__/' , "" , $key );
$key = preg_replace( '/^([w.-_]+)$/', "$1", $key );
return $key;
}
/**
* Clean _GET _POST value
*
* @param string Input
* @param bool Also run postParseCleanValue
* @return string Cleaned Input
* @since 2.1
*/
static public function parseCleanValue( $val, $postParse=true )
{
if ( $val == "" )
{
return "";
}
$val = str_replace( " ", " ", IPSText::stripslashes($val) );
# Convert all carriage return combos
$val = str_replace( array( "rn", "nr", "r" ), "n", $val );
$val = str_replace( "&" , "&" , $val );
$val = str_replace( "<!--" , "<!--" , $val );
$val = str_replace( "-->" , "-->" , $val );
$val = str_ireplace( "<script" , "<script" , $val );
$val = str_replace( ">" , ">" , $val );
$val = str_replace( "<" , "<" , $val );
$val = str_replace( '"' , """ , $val );
$val = str_replace( "n" , "<br />" , $val ); // Convert literal newlines
$val = str_replace( "$" , "$" , $val );
$val = str_replace( "!" , "!" , $val );
$val = str_replace( "'" , "'" , $val ); // IMPORTANT: It helps to increase sql query safety.
if ( IPS_ALLOW_UNICODE )
{
$val = preg_replace("/&#([0-9]+);/s", "&#\1;", $val );
//-----------------------------------------
// Try and fix up HTML entities with missing ;
//-----------------------------------------
$val = preg_replace( '/&#(d+?)([^d;])/i', "&#\1;\2", $val );
}
//-----------------------------------------
// Shortcut to auto run other cleaning
//-----------------------------------------
if( $postParse )
{
$val = IPSText::postParseCleanValue( $val );
}
return $val;
}
/**
* Clean _GET _POST value after settings loaded
*
* @param string Input
* @return string Cleaned Input
* @since 2.1
*/
static public function postParseCleanValue($val)
{
if ( $val == "" )
{
return "";
}
/* This looks wrong but it's correct. During FURL set up in registry this function is called before settings are loaded
* and we want to strip hidden chars in this instance, so.. */
if ( ! isset( ipsRegistry::$settings['strip_space_chr'] ) OR ipsRegistry::$settings['strip_space_chr'] )
{
$val = IPSText::removeControlCharacters( $val );
}
return $val;
}
/**
* Check email address to see if it seems valid
*
* @param string Email address
* @return boolean
* @since 2.0
*/
static public function checkEmailAddress( $email = "" )
{
//$email = trim($email);
//$email = str_replace( " ", "", $email );
//-----------------------------------------
// Check we're not passing an array
// (path disclosure prevention)
//-----------------------------------------
if ( is_array( $email ) )
{
return FALSE;
}
//-----------------------------------------
// Check for more than 1 @ symbol
//-----------------------------------------
if ( substr_count( $email, '@' ) > 1 )
{
return FALSE;
}
if ( preg_match( '#[;#nr*'"<>&%!(){}[]?\/s,]#', $email ) )
{
return FALSE;
}
/* tld increased to 32 characters as per RFC - http://community.invisionpower.com/resources/bugs.html/_/ip-board/ipstextcheckemailaddress-does-not-match-new-2013-tlds-r41518 */
else if ( preg_match( '/^.+@([?)[a-zA-Z0-9-.]+.([a-zA-Z]{2,32}|[0-9]{1,4})(]?)$/', $email) )
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* Function to trim text around a word or phrase
*
* @param string $haystack Text
* @param string $needle Phrase
* @return string
*/
static public function truncateTextAroundPhrase( $haystack, $needle )
{
/* Base on words */
$haystack = explode( " ", $haystack );
if( count( $haystack ) > 21 )
{
$_term_at = IPSLib::arraySearchLoose( $needle, $haystack );
if( $_term_at - 11 > 0 )
{
$begin = array_splice( $haystack, 0, $_term_at - 11 );
/* The term position will have changed now */
$_term_at = IPSLib::arraySearchLoose( $needle, $haystack );
}
if( $_term_at + 11 < count( $haystack ) )
{
$end = array_splice( $haystack, $_term_at + 11, count( $haystack ) );
}
}
else
{
$begin = array();
$end = array();
}
$haystack = implode( " ", $haystack );
if( is_array( $begin ) && count( $begin ) )
{
$haystack = '...' . $haystack;
}
if( is_array( $end ) && count( $end ) )
{
$haystack = $haystack . '...';
}
return $haystack;
}
/**
* Replaces text with highlighted blocks
*
* @param string Incoming Content
* @param string HL attribute
* @return string Formatted text
* @since 2.2.0
*/
static public function searchHighlight( $text, $highlight )
{
/* No highlight to do (1)? No point in wasting time then.. */
if ( $highlight == '' || ! is_string( $highlight ) )
{
return $text;
}
$highlight = self::parseCleanValue( urldecode( $highlight ) );
/* No highlight to do (2)? No point in wasting time then.. */
if ( $highlight == '' )
{
return $text;
}
/* Init some more vars */
$loosematch = 1;//strstr( $highlight, '*' ) ? 1 : 0;
$isPhrase = preg_match( '#("|&(amp;)?quot;)#', $highlight );
$keywords = str_replace( '*', '', str_replace( "+", " ", str_replace( "++", "+", str_replace( '-', '', trim($highlight) ) ) ) );
$keywords = str_replace( '"', '', str_replace( '\', '\', str_replace( '&quot;', '', $keywords ) ) );
$word_array = array();
$endmatch = "(.)?";
$beginmatch = "(.)?";
//-----------------------------------------
// Get rid of links first...
//-----------------------------------------
$_storedUrls = array();
preg_match_all( "/<a href=['"](.+?)["']([^>]*?)>/is", $text, $_urls );
for ( $i = 0; $i < count($_urls[0]); $i++ )
{
$_bleh = md5( uniqid( microtime(), true ) );
$text = str_replace( $_urls[0][$i], "--URL::{$_bleh}-- ", $text );
$_storedUrls[ $_bleh ] = $_urls[0][$i];
}
//-----------------------------------------
// Go!
//-----------------------------------------
if ( $keywords )
{
if ( preg_match("/,(and|or),/i", $keywords) )
{
while ( preg_match('/s+(and|or)s+/i', $keywords, $match) )
{
$word_array = explode( " ".$match[1]." ", $keywords );
$keywords = str_replace( $match[0], '', $keywords );
}
}
else if ( ! $isPhrase && strstr( $keywords, ' ' ) )
{
$word_array = explode( ' ', str_replace( ' ', ' ', $keywords ) );
}
else
{
$word_array[] = $keywords;
}
if ( ! $loosematch )
{
$beginmatch = '(^|s|>|;|])';
$endmatch = '(s|,|.|!|<br|&|$)';
}
if ( is_array($word_array) )
{
/* We'll use this to match against, so we don't break images with the term in the image name */
$textForMatch = strip_tags( IPSText::getTextClass( 'bbcode' )->stripAllTags( $text ) );
foreach ( $word_array as $keywords )
{
/* We don't want to highlight small words, they're usually noise and it can produce memory errors with single chars being highlighted
* Correction: We don't want to highlight them unless user used double quotes in search term */
if( strpos( $highlight, '&quot;' ) === false AND strlen( $keywords ) < ipsRegistry::$settings['min_search_word'] )
{
continue;
}
/* Make sure we're not trying to process an empty keyword */
if( ! $keywords )
{
continue;
}
preg_match_all( "/{$beginmatch}(".preg_quote($keywords, '/')."){$endmatch}/is", $textForMatch, $matches );
for ( $i = 0; $i < count($matches[0]); $i++ )
{
$text = str_replace( $matches[0][$i], $matches[1][$i] . "<span class='searchlite'>" . $matches[2][$i] . "</span>" . $matches[3][$i], $text );
/* Bug #39439 */
//$text = str_ireplace( $matches[0][$i], $matches[1][$i] . "<span class='searchlite'>" . $matches[2][$i] . "</span>" . $matches[3][$i], $text );
}
}
}
}
//-----------------------------------------
// Fix links
//-----------------------------------------
if( count($_storedUrls) )
{
foreach( $_storedUrls as $k => $v )
{
$text = str_replace( "--URL::{$k}-- ", $v, $text );
}
}
return $text;
}
/**
* Check a URL to make sure it's not all hacky
*
* @param string Input String
* @return boolean
* @since 2.1.0
*/
static public function xssCheckUrl( $url )
{
// This causes problems if people submit bbcode with urlencoded items that are valid
// e.g.: http://www.google.com/search?q=site%3Aipb3preview.ipslink.com+-%22Viewing+Profile%22
// %22 gets changed into " and then this fails, even though this is a valid url
// $url = trim( urldecode( $url ) );
$url = trim( $url );
/* Test for http://%XX */
if ( stristr( $url, 'http://%' ) )
{
return FALSE;
}
/* Test for http://&XX */
if ( stristr( $url, 'http://&' ) )
{
return FALSE;
}
if ( ! preg_match( '#^(http|https|news|ftp)://(?:[^<>"]+|[a-z0-9/._- !&#;,%+?:=]+)$#iU', $url ) )
{
return FALSE;
}
return TRUE;
}
/**
* Here we can do some generic checking for XSS
* This should not be considered fool proof, though can provide
* a centralized point for maintenance and checking
* @param string $txt
* @return string
*/
static public function xssMakeJavascriptSafe( $txt )
{
$txt = preg_replace( "/(j)avascript/i" , "\1avascript", $txt );
//$txt = str_ireplace( "alert" , "alert" , $txt );
//$txt = preg_replace( "/(b)(e)(h)(a)(v)(i)(o)(r)/is" , "\1\2\3<b></b>\4\5\6\7\8" , $txt );
$txt = preg_replace( '/(e)((/*.*?*/)*)x((/*.*?*/)*)p((/*.*?*/)*)r((/*.*?*/)*)e((/*.*?*/)*)s((/*.*?*/)*)s((/*.*?*/)*)i((/*.*?*/)*)o((/*.*?*/)*)n/is' , "\1xp<b></b>ression" , $txt );
$txt = preg_replace( '/(e)((\|\)*)x((\|\)*)p((\|\)*)r((\|\)*)e((\|\)*)s((\|\)*)s((\|\)*)i((\|\)*)o((\|\)*)n/is' , "\1xp<b></b>ression" , $txt );
$txt = preg_replace( '/m((\|\)*)o((\|\)*)z((\|\)*)-((\|\)*)b((\|\)*)i((\|\)*)n((\|\)*)d((\|\)*)i((\|\)*)n((\|\)*)g/is' , "moz-<b></b>binding" , $txt );
$txt = str_ireplace( "about:" , "about:" , $txt );
$txt = str_ireplace( "<body" , "<body" , $txt );
$txt = str_ireplace( "<html" , "<html" , $txt );
$txt = str_ireplace( "document." , "document." , $txt );
$txt = str_ireplace( "window." , "window." , $txt );
$event_handlers = array( 'mouseover', 'mouseout', 'mouseup', 'mousemove', 'mousedown', 'mouseenter', 'mouseleave', 'mousewheel',
'contextmenu', 'click', 'dblclick', 'load', 'unload', 'submit', 'blur', 'focus', 'resize', 'scroll',
'change', 'reset', 'select', 'selectionchange', 'selectstart', 'start', 'stop', 'keydown', 'keyup',
'keypress', 'abort', 'error', 'dragdrop', 'move', 'moveend', 'movestart', 'activate', 'afterprint',
'afterupdate', 'beforeactivate', 'beforecopy', 'beforecut', 'beforedeactivate', 'beforeeditfocus',
'beforepaste', 'beforeprint', 'beforeunload', 'begin', 'bounce', 'cellchange', 'controlselect',
'copy', 'cut', 'paste', 'dataavailable', 'datasetchanged', 'datasetcomplete', 'deactivate', 'drag',
'dragend', 'dragleave', 'dragenter', 'dragover', 'drop', 'end', 'errorupdate', 'filterchange', 'finish',
'focusin', 'focusout', 'help', 'layoutcomplete', 'losecapture', 'mediacomplete', 'mediaerror', 'outofsync',
'pause', 'propertychange', 'progress', 'readystatechange', 'repeat', 'resizeend', 'resizestart', 'resume',
'reverse', 'rowsenter', 'rowexit', 'rowdelete', 'rowinserted', 'seek', 'syncrestored', 'timeerror',
'trackchange', 'urlflip',
);
foreach( $event_handlers as $handler )
{
$txt = str_ireplace( 'on' . $handler, 'on' . $handler, $txt );
}
return $txt;
}
/**
* Strip URLs from stuff
*
* @param string Input string
* @return string Output string
*/
static public function stripUrls( $txt )
{
/* Start off by attempting to strip <a href=""></a> */
$txt = preg_replace( '#<a(?:[^"']+?)hrefs{0,}=s{0,}("|'|"|"|'|"|')([^<]+?)</a>#i', "", $txt );
/* Now grab any non linked items */
$txt = preg_replace( '#(http|https|news|ftp)://(?:[^<>["s]+|[a-z0-9/._-!&#;,%+?:=]+)#i', "", $txt );
return $txt;
}
/**
* Returns a cleaned MD5 hash
*
* @param string Input String
* @return string Parsed string
* @since 2.1
*/
static public function md5Clean( $text )
{
return preg_replace( "/[^a-zA-Z0-9]/", "" , substr( $text, 0, 32 ) );
}
/**
* Convert unicode entities
*
* @param string Input to convert (in the form of %u00E9 (example))
* @param bool Force to utf-8 (useful if you want to run convertCharsets() on it after)
* @return string UTF-8 (or html entity) encoded content
*/
static public function convertUnicode( $t, $forceUtf8=false )
{
$t = rawurldecode( $t );
/* Need this function? */
if ( ! strstr( $t, '%u' ) )
{
return $t;
}
if ( strtolower(IPS_DOC_CHAR_SET) == 'utf-8' || $forceUtf8 )
{
return preg_replace_callback( '#%u([0-9A-F]{1,4})#i', array( self, '_convertHexToUtf8' ), utf8_encode($t) );
}
else
{
return preg_replace_callback( '#%u([0-9A-F]{1,4})#i', create_function( '$matches', "return '&#' . hexdec($matches[1]) . ';';" ), $t );
}
}
/**
* Convert decimal character code to utf-8
*
* @param integer Character code
* @return string Character
*/
static protected function _convertToUtf8( $int=0 )
{
$return = '';
if ( $int < 0 )
{
return chr(0);
}
else if ( $int <= 0x007f )
{
$return .= chr($int);
}
else if ( $int <= 0x07ff )
{
$return .= chr(0xc0 | ($int >> 6));
$return .= chr(0x80 | ($int & 0x003f));
}
else if ( $int <= 0xffff )
{
$return .= chr(0xe0 | ($int >> 12));
$return .= chr(0x80 | (($int >> 6) & 0x003f));
$return .= chr(0x80 | ($int & 0x003f));
}
else if ( $int <= 0x10ffff )
{
$return .= chr(0xf0 | ($int >> 18));
$return .= chr(0x80 | (($int >> 12) & 0x3f));
$return .= chr(0x80 | (($int >> 6) & 0x3f));
$return .= chr(0x80 | ($int & 0x3f));
}
else
{
return chr(0);
}
return $return;
}
/**
* Wrapper for dec_char_ref_to_utf8
*
* @param array Hex character code
* @return string Character
*/
static protected function _convertHexToUtf8( $matches )
{
return self::_convertToUtf8( hexdec( $matches[1] ) );
}
/**
* Callback function for array_walk_recursive to convert each entry
*
* @param mixed Value
* @param string Array key
* @return void
*/
static public function arrayWalkCallbackConvert( &$value, $key )
{
if( is_string($value) )
{
$value = IPSText::convertCharsets( $value, IPS_DOC_CHAR_SET, "UTF-8" );
}
}
/**
* Convert a string between charsets
*
* @param string Input String
* @param string Current char set
* @param string Destination char set
* @return string Parsed string
* @since 2.1.0
* @todo [Future] If an error is set in classConvertCharset, show it or log it somehow
*/
static public function convertCharsets( $text, $original_cset, $destination_cset="UTF-8" )
{
define( 'CONVERT_JSU_TO_ENTITY', true );
$original_cset = strtolower($original_cset);
$destination_cset = strtolower( $destination_cset );
$t = $text;
//-----------------------------------------
// Not the same?
//-----------------------------------------
if ( $destination_cset == $original_cset )
{
return $t;
}
//-----------------------------------------
// Are these only latin chars? If so, no conversion should be needed.
//-----------------------------------------
if( preg_match( '/^([a-zA-Z0-9_-.:;[]]+?)$/', $t ) )
{
return $text;
}
if ( ! is_object( self::$classConvertCharset ) )
{
require_once( IPS_KERNEL_PATH.'/classConvertCharset.php' );/*noLibHook*/
self::$classConvertCharset = new classConvertCharset();
if ( ipsRegistry::$settings['charset_conv_method'] == 'mb' AND function_exists( 'mb_convert_encoding' ) )
{
self::$classConvertCharset->method = 'mb';
}
else if ( ipsRegistry::$settings['charset_conv_method'] == 'iconv' AND function_exists( 'iconv' ) )
{
self::$classConvertCharset->method = 'iconv';
}
else if ( ipsRegistry::$settings['charset_conv_method'] == 'recode' AND function_exists( 'recode_string' ) )
{
self::$classConvertCharset->method = 'recode';
}
else
{
self::$classConvertCharset->method = 'internal';
}
}
$text = self::$classConvertCharset->convertEncoding( $text, $original_cset, $destination_cset );
/* Experimental - convert u#### to html entity */
if( $destination_cset != 'utf-8' AND $text AND CONVERT_JSU_TO_ENTITY )
{
preg_match_all( '#\u([0-9]{4})#ims', $text, $matches );
if( is_array($matches) AND count($matches) )
{
foreach( $matches[1] as $k => $v )
{
$v = "&#" . hexdec( ltrim( $v, '0' ) ) . ";";
$text = str_replace( $matches[0][$k], $v, $text );
}
}
}
return $text ? $text : $t;
}
/**
* Truncate a HTML string without breaking HTML entites
*
* @param string Input String
* @param integer Desired min. length
* @return string Parsed string
* @since 2.0
*/
static public function truncate($text, $limit=30)
{
$orig = $text;
$text = str_replace( '&' , '&', $text );
$text = str_replace( '"', '"', $text );
$text = str_replace( '>', '>', $text );
$text = str_replace( '<', '<', $text );
$string_length = self::mbstrlen( $text );
if ( $string_length > $limit)
{
$text = trim( self::mbsubstr( $text, 0, $limit - 3 ) ). '...';
}
else
{
$text = preg_replace( "/&(#{0,}([a-zA-Z0-9]+?)?)?$/", '', $text );
}
// Let's just use the original string if the truncated one is longer or same length
return ( self::mbstrlen( $text ) >= $string_length ) ? $orig : $text;
}
/**
* MB strtolower
*
* @param string Input String
* @return string Parsed string
*/
static public function mbstrtolower( $text )
{
if ( function_exists('mb_list_encodings') AND function_exists('mb_strtolower') )
{
$valid_encodings = array();
$valid_encodings = mb_list_encodings();
if ( count($valid_encodings) )
{
if ( in_array( strtoupper(IPS_DOC_CHAR_SET), $valid_encodings ) )
{
$text = mb_strtolower( $text, strtoupper(IPS_DOC_CHAR_SET) );
}
}
else
{
$text = strtolower( $text );
}
}
else
{
$text = strtolower( $text );
}
return $text;
}
/**
* Substr support for this without mb_substr
*
* @param string Input String
* @param integer Desired min. length
* @return string Parsed string
* @since 2.0
*/
static public function mbsubstr( $text, $start=0, $limit=30 )
{
$text = str_replace( '&' , '&', $text );
$text = str_replace( '"', '"', $text );
$text = str_replace( '>', '>', $text );
$text = str_replace( '<', '<', $text );
//-----------------------------------------
// Got multibyte?
//-----------------------------------------
if( function_exists('mb_list_encodings') )
{
$valid_encodings = array();
$valid_encodings = mb_list_encodings();
if( count($valid_encodings) )
{
if( in_array( strtoupper(IPS_DOC_CHAR_SET), $valid_encodings ) )
{
$text = mb_substr( $text, $start, $limit, strtoupper(IPS_DOC_CHAR_SET) );
$text = preg_replace( "/&(#{0,}([a-zA-Z0-9]+?)?)?$/", '', $text );
return $text;
}
}
}
//-----------------------------------------
// Handrolled method
//-----------------------------------------
$string_length = self::mbstrlen( $text );
//-----------------------------------------
// Negative start?
//-----------------------------------------
if( $start < 0 )
{
$start = $string_length + $start;
}
//-----------------------------------------
// Do it!
//-----------------------------------------
if ( $string_length > $limit )
{
if( strtoupper(IPS_DOC_CHAR_SET) == 'UTF-8' )
{
// Multi-byte support
//$text = preg_replace('#^(?:[x00-x7F]|[xC0-xFF][x80-xBF]+){0,0}'.
// '((?:[x00-x7F]|[xC0-xFF][x80-xBF]+){'.intval($start).','.intval($limit).'}).*#s',
// '$1',$text);
/**
* @link http://www.php.net/manual/en/function.substr.php#55107
*/
preg_match_all( "/./su", $text, $ar );
$text = implode( "", array_slice( $ar[0], $start, $limit ) );
}
else
{
$text = substr( $text, $start, $limit );
}
$text = preg_replace( "/&(#{0,}([a-zA-Z0-9]+?)?)?$/", '', $text );
}
else
{
$text = preg_replace( "/&(#{0,}([a-zA-Z0-9]+?)?)?$/", '', $text );
}
return $text;
}
/**
* mb_stripos - uses mb functions if available
*
* @param string Input String
* @param integer Desired min. length
* @return string Parsed string
* @since 2.0
*/
static public function mbstripos( $text, $string, $start=0 )
{
// Do we have multi-byte functions?
if( function_exists('mb_list_encodings') AND function_exists('mb_stripos') )
{
$valid_encodings = array();
$valid_encodings = mb_list_encodings();
if( count($valid_encodings) )
{
if( in_array( strtoupper(IPS_DOC_CHAR_SET), $valid_encodings ) )
{
return @mb_stripos( $text, $string, $start, strtoupper(IPS_DOC_CHAR_SET) );
}
}
}
// No? Fallback to normal stripos
return stripos( $text, $string, $start );
}
/**
* Clean a string to remove all non alphanumeric characters
*
* @param string Input String
* @param string Additional tags
* @return string Parsed string
* @since 2.1
*/
static public function alphanumericalClean( $text, $additional_tags="" )
{
if ( $additional_tags )
{
$additional_tags = preg_quote( $additional_tags, "/" );
}
return preg_replace( '/[^a-zA-Z0-9-_' . $additional_tags . "]/", "" , $text );
}
/**
* Get the true length of a multi-byte character string
*
* @param string Input String
* @return integer String length
* @since 2.1
*/
static public function mbstrlen( $t )
{
if( function_exists( 'mb_list_encodings' ) )
{
$encodings = mb_list_encodings();
if( in_array( strtoupper(IPS_DOC_CHAR_SET), array_map( 'strtoupper', $encodings ) ) )
{
return mb_strlen( $t, IPS_DOC_CHAR_SET );
}
}
return strlen( preg_replace("/&#([0-9]+);/", "-", self::stripslashes( $t ) ) );
}
/**
* Convert text for use in a textarea
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function textToForm( $t="" )
{
// Use forward look up to only convert & not {
//$t = preg_replace("/&(?!#[0-9]+;)/s", '&', $t );
$t = str_replace( "&" , "&" , $t );
$t = str_replace( "<" , "<" , $t );
$t = str_replace( ">" , ">" , $t );
$t = str_replace( '"' , """ , $t );
$t = str_replace( "'" , ''' , $t );
if ( IN_ACP )
{
$t = str_replace( "\", "\" , $t );
}
return $t; // A nice cup of?
}
/**
* Cleaned form data back to text
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function formToText($t="")
{
$t = str_replace( "&" , "&", $t );
$t = str_replace( "<" , "<", $t );
$t = str_replace( ">" , ">", $t );
$t = str_replace( """ , '"', $t );
$t = str_replace( "'" , "'", $t );
$t = str_replace( "../" , "../", $t );
if ( IN_ACP )
{
//$t = str_replace( '\' , '\\', $t );
$t = str_replace( '\' ,'\', $t );
}
return $t;
}
/**
* Attempt to make slashes safe for use in DB (not really needed now?)
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function safeslashes($t="")
{
return str_replace( '\', "\\", self::stripslashes($t) );
}
/**
* Remove slashes if magic_quotes enabled
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function stripslashes($t)
{
if( is_array($t) )
{
return $t;
}
if ( IPS_MAGIC_QUOTES )
{
$t = stripslashes($t);
}
$t = preg_replace( '/\(?!&#|?#)/', "\", $t );
return $t;
}
/**
* Strip the attachment tag from data
*
* @param string Incoming text
* @return string Text with any attach tags removed
*/
static public function stripAttachTag( $text )
{
return preg_replace( '#[attachment=(d+?):(?:[^]]+?)]#is', '', $text );
}
/**
* Convert text for use in a textarea
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function raw2form($t="")
{
$t = str_replace( '$', "$", $t);
if ( IPS_MAGIC_QUOTES )
{
$t = stripslashes($t);
}
$t = preg_replace( '/\(?!&#|?#)/', "\", $t );
//---------------------------------------
// Make sure macros aren't converted
//---------------------------------------
$t = preg_replace( "/<{(.+?)}>/", "<{\1}>", $t );
return $t;
}
/**
* htmlspecialchars including entities
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function htmlspecialchars($t="")
{
// Use forward look up to only convert & not {
$t = preg_replace("/&(?!#[0-9]+;)/s", '&', $t );
$t = str_replace( "<", "<" , $t );
$t = str_replace( ">", ">" , $t );
$t = str_replace( '"', """, $t );
$t = str_replace( "'", ''', $t );
return $t; // A nice cup of?
}
/**
* unhtmlspecialchars including multi-byte characters
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function UNhtmlspecialchars($t="")
{
$t = str_replace( "&" , "&", $t );
$t = str_replace( "&" , "&", $t );
$t = str_replace( "<" , "<", $t );
$t = str_replace( ">" , ">", $t );
$t = str_replace( """, '"', $t );
$t = str_replace( "'", "'", $t );
$t = str_replace( "'" , "'", $t );
$t = str_replace( "!" , "!", $t );
$t = str_replace( """ , '"', $t );
$t = str_replace( "$", '$', $t );
return $t;
}
/**
* Remove leading comma from comma delim string
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function trimLeadingComma($t)
{
return ltrim( $t, ',' );
}
/**
* Remove trailing comma from comma delim string
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function trimTrailingComma($t)
{
return rtrim( $t, ',' );
}
/**
* Remove dupe commas from comma delim string
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function cleanComma($t)
{
return preg_replace( "/,{2,}/", ",", $t );
}
/**
* Clean perm string (wrapper for comma cleaners)
*
* @param string Input String
* @return string Parsed string
* @since 2.0
*/
static public function cleanPermString($t)
{
$t = self::cleanComma($t);
$t = self::trimLeadingComma($t);
$t = self::trimTrailingComma($t);
return $t;
}
/**
* Convert HTML line break tags to n
*
* @param string Input text
* @return string Parsed text
* @since 2.0
*/
static public function br2nl($t="")
{
//print nl2br(htmlspecialchars($t)).'<br>--------------------------------<br>';
$t = str_replace( array( "r", "n" ), '', $t );
$t = str_ireplace( array( "<br />", "<br>" ), "n", $t );
//$t = preg_replace( "#(?:n|r)?<br />(?:n|r)?#", "n", $t );
//$t = preg_replace( "#(?:n|r)?<br>(?:n|r)?#" , "n", $t );
//print nl2br(htmlspecialchars($t)).'<br>--------------------------------<br>';
return $t;
}
/**
* Removes control characters (hidden spaces)
*
* @param string Input String
* @return intger String length
* @since 2.1
*/
static public function removeControlCharacters( $t )
{
/* This looks wrong but it's correct. During FURL set up in registry this function is called before settings are loaded
* and we want to strip hidden chars in this instance, so.. */
if ( ! isset( ipsRegistry::$settings['strip_space_chr'] ) OR ipsRegistry::$settings['strip_space_chr'] )
{
/**
* @see http://en.wikipedia.org/wiki/Space_(punctuation)
* @see http://www.ascii.cl/htmlcodes.htm
*/
if ( strtolower(IPS_DOC_CHAR_SET) == 'utf-8' )
{
$t = str_replace(chr(194).chr(160), chr(32), $t );
return $t;
}
else
{
$t = str_replace( chr(160), ' ', $t );
$t = str_replace( chr(173), ' ', $t );
//$t = str_replace( chr(240), ' ', $t ); -> latin small letter eth
//$t = str_replace( chr(0xA0), "", $t ); //Remove sneaky spaces Same as chr 160
//$t = str_replace( chr(0x2004), "", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0x2005), "", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0x2006), "", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0x2009), "", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0x200A), "", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0x200B), "", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0x200C), "", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0x200D), " ", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0x202F), " ", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0x205F), " ", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0x2060), "", $t ); //Remove sneaky spaces
//$t = str_replace( chr(0xFEFF), "", $t ); //Remove sneaky spaces
}
}
return $t;
}
/**
* URL encode safe with IPB FURLS
* @param string $data
* @return string Data
*/
static public function urlencode_furlSafe( $data )
{
$data = str_replace( '/', '%2F', $data );
$data = str_replace( '%2B', '+', $data );
return str_replace( '+', '%2B', urlencode( $data ) );
}
/**
* URL encode safe with IPB FURLS
* @param string $data
* @return string Data
*/
static public function urldecode_furlSafe( $data )
{
$data = str_replace( '%25', '%', $data );
$data = str_replace( '%2B', '+', $data );
return urldecode( $data );
}
/**
* Base64 encode for URLs
*
* @param string Data
* @return string Data
*/
static public function base64_encode_urlSafe( $data )
{
return strtr( base64_encode( $data ), '+/=', '-_,' );
}
/**
* Base64 decode for URLs
*
* @param string Data
* @return string Data
*/
static public function base64_decode_urlSafe( $data )
{
return base64_decode( strtr( $data, '-_,', '+/=' ) );
}
/**
* Returns current emoticon directory...
* @return string
*/
static public function getEmoticonDirectory()
{
if ( IN_ACP )
{
$image_set = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'set_image_dir, set_emo_dir', 'from' => 'skin_collections', 'where' => 'set_is_default=1' ) );
return $image_set['set_emo_dir'];
}
else
{
return ipsRegistry::getClass('output')->skin['set_emo_dir'];
}
}
/**
* The crazy crap we have to deal with. User enters squiggly char, it gets turned into € or Á but there is
* no matching character in the doc set so we have to try and decode manually
* @param string Raw string
* @return string Converted string
*/
static public function allowUtf8CharsWhenNotUsingUtf8Doc( $string )
{
/* If we're not using UTF-8, lets try and handle encoded data. */
if ( IPS_DOC_CHAR_SET != 'UTF-8' )
{
/* First, convert any left over ´ style chars */
preg_match_all( '/&([a-zA-Z]{5,8});/', $string, $matches );
foreach( $matches[1] as $word )
{
$converted = str_replace( '&', '&', self::convertEntity( array( 1 => $word ) ) );
if ( $converted )
{
$string = str_replace( '&' . $word . ';', $converted, $string );
}
}
preg_match_all( '/&#([0-9]{3,6});/', $string, $matches );
foreach( $matches[1] as $word )
{
if ( $word > 127 )
{
$string = str_replace( '&#' . $word . ';', '&#' . $word . ';', $string );
}
}
}
return $string;
}
/**
* Converts numeric entities to their named equivalents
*
* @param string
* @return string
*/
static public function convertNumericEntityToNamed( $string )
{
/* If we're not using UTF-8, lets try and handle encoded data. */
if ( IPS_DOC_CHAR_SET != 'UTF-8' )
{
/* First, convert any left over ´ style chars */
preg_match_all( '/&#([0-9]{3,8});/', $string, $matches );
foreach( $matches[1] as $word )
{
if ( $word > 127 )
{
$converted = self::convertEntity( array( 1 => $word ), true );
if ( $converted )
{
$string = str_replace( '&#' . $word . ';', $converted, $string );
}
}
}
}
return $string;
}
/**
* Function to convert named entities to numeric entities
*
* @param array $matches Results from preg_match call
* @return string
* @link http://www.lazycat.org/php-convert-entities.php
*/
static public function convertEntity( $matches, $flip=false )
{
static $table = array(/*'quot' => '"','amp' => '&','lt' => '<','gt' => '>',*/'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' => '∫','there4' => '∴','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' => '♦','nbsp' => ' ','iexcl' => '¡','cent' => '¢','pound' => '£',
'curren' => '¤','yen' => '¥','brvbar' => '¦','sect' => '§','uml' => '¨','copy' => '©','ordf' => 'ª','laquo' => '«','not' => '¬','shy' => '­',
'reg' => '®','macr' => '¯','deg' => '°','plusmn' => '±','sup2' => '²','sup3' => '³','acute' => '´','micro' => 'µ','para' => '¶','middot' => '·',
'cedil' => '¸','sup1' => '¹','ordm' => 'º','raquo' => '»','frac14' => '¼','frac12' => '½','frac34' => '¾','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' => 'ÿ' );
if ( $flip === true )
{
$tmp = array_flip( $table );
$lookup = '&#' . $matches[1] . ';';
return isset( $tmp[ $lookup ] ) ? '&' . $tmp[ $lookup ] . ';' : $lookup;
}
return isset( $table[ $matches[1] ] ) ? $table[ $matches[1] ] : '&' . $matches[1] . ';';
}
/**
* Wrapper for PHPs strip_tags. Makes < and > not part of tags safe
* Based on @link http://www.php.net/manual/en/function.strip-tags.php#89309
* @param string $text
* @param string $allowed
*/
static public function stripTags( $text, $allowed='' )
{
$strs = explode( '<', $text );
$res = $strs[0];
for( $i=1 ; $i < count( $strs ) ; $i++ )
{
if ( ! strpos( $strs[$i], '>' ) )
{
$res = $res . '' . $strs[$i];
}
else
{
$res = $res . '<' . $strs[$i];
}
}
$text = strip_tags( $res, $allowed );
return str_replace( '', '<', $text );
}
}