* Invision Power Services * IP.Board v3.2.3 * Share links library. * Created by Matt Mecham * Last Updated: $Date: 2011-10-07 22:31:51 -0400 (Fri, 07 Oct 2011) $ * * * @author $Author: bfarber $ * @copyright (c) 2001 - 2009 Invision Power Services, Inc. * @license This is NULLED! * @package IP.Board * @link http://hatynka.in * @version $Rev: 9588 $ * */ class share_links { /**#@+ * Registry Object Shortcuts * * @access protected * @var object */ protected $registry; protected $DB; protected $settings; protected $request; protected $lang; protected $member; protected $memberData; protected $cache; protected $caches; /**#@-*/ /** * Plug in object * * @access protected * @var object */ protected $_plugin; protected $_pluginKey = ''; /** * Construct. * @access public * @param object Registry * @param string Plug in key * @return @e void */ public function __construct( $registry, $plugin ) { /* 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(); /* Store */ $this->_pluginKey = $plugin; try { $this->_initPlugin( $plugin ); } catch ( Exception $e ) { return $e->getMessage(); } /* Verify moderator perms are set */ $this->memberData = IPSMember::setUpModerator( $this->memberData ); } /** * Share the link. * * @access public * @param string Document title * @param string Document URL */ public function share( $title, $url ) { /* Disabled? */ if ( ! $this->settings['sl_enable'] ) { return false; } /* Ensure title is correctly de-html-ized */ $title = IPSText::UNhtmlspecialchars( $title ); if ( is_object( $this->_plugin ) ) { /* Grab URL details */ $data = $this->deconstructUrl( $url ); /* Requires a permission check? */ if ( $data['data_app'] AND method_exists( $this->_plugin, 'requiresPermissionCheck' ) ) { if ( $this->_plugin->requiresPermissionCheck( $data ) !== false ) { $_file = IPSLib::getAppDir( $data['data_app'] ) . '/extensions/coreExtensions.php'; $_result = false; /* Check for the file */ if ( is_file( $_file ) ) { /* Get the file */ $_class = IPSLib::loadLibrary( $_file, $data['data_app'] . 'ShareLinks', $data['data_app'] ); /* Check for the class */ if ( class_exists( $_class ) ) { /* Create an object */ $_obj = new $_class(); /* Check for the module */ if ( method_exists( $_obj, 'permissionCheck' ) ) { if ( $_obj->permissionCheck( $data ) !== false ) { $_result = true; } } } } if ( $_result === false ) { $this->registry->getClass('output')->showError('no_permission'); } } } /* Log it */ $this->log( $url, $title, $data ); /* In almost all cases, there is no data to return as the plugin redirects or posts an action */ $this->_plugin->share( $title, $url ); } return false; } /** * Init the plug in * * @access protected * @param string Plug in */ protected function _initPlugin( $plugin='' ) { $_file = IPS_ROOT_PATH . 'sources/classes/share/plugins/' . IPSText::alphanumericalClean( $plugin ) . '.php'; /* Goddit? */ if ( is_file( $_file ) ) { $classToLoad = IPSLib::loadLibrary( $_file, 'sl_' . $plugin ); $this->_plugin = new $classToLoad( $this->registry ); } else { throw new Exception( 'NO_SUCH_PLUGIN' ); } } /** * Log a URL. Simple really. * * @access public * @param string URL * @return nufink */ public function deconstructUrl( $url ) { /* init */ $data = array( 'data_app' => '', 'data_type' => '', 'data_primary_id' => 0, 'data_secondary_id' => 0 ); /* Try FURL first */ $ret = $this->_checkForFurl( $url ); if ( $ret === false OR ! count( $ret ) ) { $ret = $this->_checkForApps( $this->_explodeUrl( $url ) ); } else if ( is_array( $ret ) AND count( $ret ) AND ! $ret['app'] ) { /* Is a shorter redirect based link, so use app processing... */ $_url = array(); foreach( $ret as $k => $v ) { $_url[] = $k . '=' . $v; } $ret = $this->_checkForApps( $this->_explodeUrl( implode( '&', $_url ) ) ); } /* Try again */ if ( is_array( $ret ) AND count( $ret ) AND $ret['data_app'] ) { $data = $ret; } return $data; } /** * Log a URL. Simple really. * * @access public * @param string URL * @return nufink */ public function log( $url, $title, $urlParts=array() ) { /* If we're a 'bot, sod awf */ if ( $this->member->is_not_human ) { return; } /* Ensure we're not jabbing our podgy fingers on the refresh button We don't check the link 'cos you can any ?param on the end. member_id + share service should do */ if ( $this->memberData['member_id'] ) { $check = $this->DB->buildAndFetch( array( 'select' => 'log_id', 'from' => 'core_share_links_log', 'where' => '(log_member_id=' . $this->memberData['member_id'] . ' OR log_ip_address=\'' . $this->member->ip_address . '\') AND log_share_key=\'' . $this->_pluginKey . '\' AND log_date > ' . ( time() - 60 ) ) ); } else { $check = $this->DB->buildAndFetch( array( 'select' => 'log_id', 'from' => 'core_share_links_log', 'where' => 'log_ip_address=\'' . $this->member->ip_address . '\' AND log_share_key=\'' . $this->_pluginKey . '\' AND log_date > ' . ( time() - 60 ) ) ); } if ( $check['log_id'] ) { return; } /* Log */ $this->DB->insert( 'core_share_links_log', array( 'log_date' => time(), 'log_member_id' => $this->memberData['member_id'], 'log_url' => $url, 'log_title' => $title, 'log_ip_address' => $this->member->ip_address, 'log_share_key' => $this->_pluginKey, 'log_data_app' => $urlParts['data_app'], 'log_data_type' => $urlParts['data_type'], 'log_data_primary_id' => intval($urlParts['data_primary_id']), 'log_data_secondary_id' => intval($urlParts['data_secondary_id']) ) ); /* Rebuild caches */ $this->rebuildCaches(); } /** * Build some caches of stuff. * * @access public * */ public function rebuildCaches() { /* INIT */ $caches = array( 'mostitems' => array(), 'mostrecent' => array(), 'mosttypes' => array() ); /* Delete caches */ $this->DB->delete( 'core_share_links_caches', "cache_key IN ('mostitems', 'mostrecent', 'mosttypes')" ); /* Most shared */ $this->DB->build( array( 'select' => 'COUNT(*) as count, log_data_app, log_data_type, log_data_primary_id', 'from' => 'core_share_links_log', 'group' => 'log_data_app, log_data_type, log_data_primary_id', 'order' => 'count DESC', 'limit' => array( 0, 10 ) ) ); $this->DB->execute(); while( $row = $this->DB->fetch() ) { $caches['mostitems'][] = $row; } /* Most recent shares */ $this->DB->build( array( 'select' => 'log_id, log_data_app, log_data_type, log_data_primary_id', 'from' => 'core_share_links_log', 'order' => 'log_date DESC', 'limit' => array( 0, 10 ) ) ); $this->DB->execute(); while( $row = $this->DB->fetch() ) { $caches['mostrecent'][] = $row; } /* Most used share type*/ $this->DB->build( array( 'select' => 'COUNT(*) as count, log_share_key', 'from' => 'core_share_links_log', 'group' => 'log_share_key', 'order' => 'count DESC', 'limit' => array( 0, 10 ) ) ); $this->DB->execute(); while( $row = $this->DB->fetch() ) { $caches['mosttypes'][] = $row; } /* Save 'em */ foreach( $caches as $key => $data ) { $this->DB->insert( 'core_share_links_caches', array( 'cache_key' => $key, 'cache_date' => time(), 'cache_data' => serialize( $data ) ) ); } } /** * Try and deconstruct the link via app specific processing * * @access protected * @param array Array or URL bits * @return array Array of request data or false */ protected function _checkForApps( $url ) { $app_cache = $this->cache->getCache('app_cache'); /* Loop through applications */ foreach( $app_cache as $app_dir => $app ) { /* Only if app enabled... */ if ( $app['app_enabled'] ) { /* Setup */ $_file = IPSLib::getAppDir( $app['app_directory'] ) . '/extensions/coreExtensions.php'; /* Check for the file */ if( is_file( $_file ) ) { /* Get the file */ $_class = IPSLib::loadLibrary( $_file, $app['app_directory'] . 'ShareLinks', $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, 'deconstructUrl' ) ) { /* Call it */ $ret = $_obj->deconstructUrl( $url ); if ( is_array( $ret ) AND $ret['data_app'] ) { return $ret; } } } } } } return array( 'data_app' => '' ); } /** * Try and deconstruct the link if it's a FURRY FURL * * @access protected * @param string Incoming URL * @return array Array of request data or false */ protected function _checkForFurl( $url ) { $_urlBits = array(); $_toTest = $url; $templates = array(); /* Grab FURL data... */ if ( ! IN_DEV AND is_file( DOC_IPS_ROOT_PATH . 'cache/furlCache.php' ) ) { $templates = array(); include( DOC_IPS_ROOT_PATH . 'cache/furlCache.php' );/*noLibHook*/ $_seoTemplates = $templates; } else { /* Attempt to write it */ $_seoTemplates = IPSLib::buildFurlTemplates(); try { IPSLib::cacheFurlTemplates(); } catch( Exception $e ) {} } if ( is_array( $_seoTemplates ) AND count( $_seoTemplates ) ) { foreach( $_seoTemplates as $key => $data ) { if ( empty( $data['in']['regex'] ) ) { continue; } if ( preg_match( $data['in']['regex'], $_toTest, $matches ) ) { if ( is_array( $data['in']['matches'] ) ) { foreach( $data['in']['matches'] as $_replace ) { $k = IPSText::parseCleanKey( $_replace[0] ); if ( strpos( $_replace[1], '$' ) !== false ) { $v = IPSText::parseCleanValue( $matches[ intval( str_replace( '$', '', $_replace[1] ) ) ] ); } else { $v = IPSText::parseCleanValue( $_replace[1] ); } $_urlBits[ $k ] = $v; } } if ( strpos( $_toTest, $_seoTemplates['__data__']['varBlock'] ) !== false ) { $_parse = substr( $_toTest, strpos( $_toTest, $_seoTemplates['__data__']['varBlock'] ) + strlen( $_seoTemplates['__data__']['varBlock'] ) ); $_data = explode( $_seoTemplates['__data__']['varSep'], $_parse ); $_c = 0; foreach( $_data as $_v ) { if ( ! $_c ) { $k = IPSText::parseCleanKey( $_v ); $v = ''; $_c++; } else { $v = IPSText::parseCleanValue( $_v ); $_c = 0; $_urlBits[ $k ] = $v; } } } break; } } //----------------------------------------- // If using query string furl, extract any // secondary query string. // Ex: http://localhost/index.php?/path/file.html?key=value // Will pull the key=value properly //----------------------------------------- $_qmCount = substr_count( $_toTest, '?' ); if ( $_qmCount > 1 ) { $_secondQueryString = substr( $_toTest, strrpos( $_toTest, '?' ) + 1 ); $_secondParams = explode( '&', $_secondQueryString ); if( count($_secondParams) ) { foreach( $_secondParams as $_param ) { list( $k, $v ) = explode( '=', $_param ); $k = IPSText::parseCleanKey( $k ); $v = IPSText::parseCleanValue( $v ); $_urlBits[ $k ] = $v; } } } /* Process URL bits for extra ? in them */ if ( is_array( $_urlBits ) AND count( $_urlBits ) ) { foreach( $_urlBits as $k => $v ) { if ( strstr( $v, '?') ) { list( $rvalue, $more ) = explode( '?', $v ); if ( $rvalue AND $more ) { /* Reset key with correct value */ $_v = $rvalue; $_urlBits[ $k ] = $_v; /* Now add in the other value */ if ( strstr( $more, '=' ) ) { list( $_k, $_v ) = explode( '=', $more ); if ( $_k and $_v ) { $_urlBits[ $_k ] = $_v; } } } } } } } return ( count($_urlBits) ) ? $_urlBits : false; } /** * Explode a URL (foo=bar&baz=umm) into array( 'foo' => 'bar', .... ) * * @access protected * @param string URL * @return array Values */ protected function _explodeUrl( $url ) { $url = str_replace( '&', '&', $url ); $ret = array(); if ( strstr( $url, '?' ) ) { list( $_u, $url ) = explode( '?', $url ); } foreach( explode( '&', $url ) as $bit ) { list($k, $v) = explode( '=', $bit ); if ( $k and $v ) { $ret[ trim( $k ) ] = trim( $v ); } } return $ret; } }