b2evolution PHP Cross Reference Blogging Systems

Source: /inc/sessions/model/_hit.class.php - 1812 lines - 45570 bytes - Summary - Text - Print

Description: This file implements the Hit class. This file is part of the evoCore framework - {@link http://evocore.net/} See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements the Hit class.
   4   *
   5   * This file is part of the evoCore framework - {@link http://evocore.net/}
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
  10   *
  11   * {@internal License choice
  12   * - If you have received this file as part of a package, please find the license.txt file in
  13   *   the same folder or the closest folder above for complete license terms.
  14   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  15   *   then you must choose one of the following licenses before using the file:
  16   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  17   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  18   * }}
  19   *
  20   * {@internal Open Source relicensing agreement:
  21   * Daniel HAHLER grants Francois PLANQUE the right to license
  22   * Daniel HAHLER's contributions to this file and the b2evolution project
  23   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  24   * }}
  25   *
  26   * @todo dh> Lazily handle properties through getters (and do not detect/do much in the constructor)!
  27   *
  28   * @package evocore
  29   *
  30   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  31   * @author blueyed: Daniel HAHLER.
  32   * @author fplanque: Francois PLANQUE.
  33   *
  34   * @version $Id: _hit.class.php 6136 2014-03-08 07:59:48Z manuel $
  35   *
  36   */
  37  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  38  
  39  
  40  /**
  41   * A hit to a blog.
  42   *
  43   * @package evocore
  44   */
  45  class Hit
  46  {
  47      /**
  48       * ID in DB, gets set when {@link log()} was called and the hit was logged.
  49       */
  50      var $ID;
  51  
  52      /**
  53       * Is the hit already logged?
  54       * @var boolean
  55       */
  56      var $logged = false;
  57  
  58      /**
  59       * The referer/referrer.
  60       *
  61       * @var string
  62       */
  63      var $referer;
  64  
  65  
  66      /**
  67       * The type of hit.
  68       *
  69       * 'standard'|'rss'|'admin'|'ajax'|'service'
  70       *
  71       * @var string
  72       */
  73      var $hit_type;
  74  
  75      /**
  76       * The type of referer.
  77       *
  78       * Note: "spam" referers do not get logged.
  79       * 'search'|'special'|'referer'|'direct'|'spam'|'self'
  80       *
  81       * @var string
  82       */
  83      var $referer_type;
  84  
  85  
  86      /**
  87       * The dom_type of the referer's base domain in T_basedomains
  88       * 'unknown'|'normal'|'searcheng'|'aggregator'|'email'
  89       * @var string
  90       */
  91      var $dom_type = 'unknown';
  92      /**
  93       * The ID of the referer's base domain in T_basedomains
  94       *
  95       * @var integer
  96       */
  97      var $referer_domain_ID;
  98  
  99      /**
 100       * Is this a reload?
 101       * This gets lazy-filled by {@link is_new_view()}.
 102       * @var boolean
 103       * @access protected
 104       */
 105      var $_is_new_view;
 106  
 107      /**
 108       * Ignore this hit?
 109       * @var boolean
 110       */
 111      var $ignore = false;
 112  
 113      /**
 114       * Remote address (IP).
 115       * @var string
 116       */
 117      var $IP;
 118  
 119      /**
 120       * The user agent.
 121       * @see Hit::get_user_agent()
 122       * @access protected
 123       * @var string
 124       */
 125      var $user_agent;
 126  
 127      /**
 128       * The user agent name, eg "safari"
 129       * @see Hit::get_agent_name()
 130       * @access protected
 131       * @var string
 132       */
 133      var $agent_name;
 134  
 135      /**
 136       * The user agent platform. Either "win", "mac" or "linux".
 137       * @see Hit::get_agent_platform()
 138       * @access protected
 139       * @var string
 140       */
 141      var $agent_platform;
 142  
 143  
 144      /**
 145       * The user's remote host.
 146       * Use {@link get_remote_host()} to access it (lazy filled).
 147       * @var string
 148       * @access protected
 149       */
 150      var $_remoteHost;
 151  
 152      /**
 153       * The user agent type.
 154       *
 155       * 'rss'|'robot'|'browser'|'unknown'
 156       *
 157       * @see Hit::get_agent_type()
 158       * @access protected
 159       * @var string
 160       */
 161      var $agent_type;
 162  
 163      /**
 164       * Array of 2 letter ISO country codes
 165       * This gets lazy-filled by {@link get_country_codes()}.
 166       * @var array
 167       * @access protected
 168       */
 169      var $country_codes;
 170  
 171      /**
 172       * Array of known search engines in format( searchEngineName => URL )
 173       * This gets lazy-filled by {@link get_search_engine_names()}.
 174       * @var array
 175       * @access protected
 176       */
 177      var $search_engine_names;
 178  
 179      /**
 180       * Extracted from search referers:
 181       */
 182      var $_search_params_tried = false;
 183      var $_keyphrase = NULL;
 184      var $_serprank = NULL;
 185      var $_search_engine = NULL;
 186  
 187      /**
 188       * Session ID
 189       */
 190       var $session_id;
 191  
 192       /**
 193       * Hit time
 194       */
 195       var $hit_time;
 196  
 197      /**
 198       * Hit_response_code
 199       */
 200       var $hit_response_code = 200;
 201  
 202      /**
 203       * Hit action
 204       */
 205       var $action;
 206  
 207  
 208  
 209       /**
 210        * Test mode
 211        * The mode used by geneartion of a fake statisctics. In this case test_mode = 1
 212        * fp>vitaliy what is this for?
 213        * Test what & when?
 214        *
 215        */
 216       var $test_mode;
 217  
 218       /**
 219        * Test rss mode
 220        * fp>vitaliy what is this for?
 221        * Test_rss is used for geneartion of fake statistics. In the normal mode
 222        * Hit class determines rss hit using $Skin->type == 'feed'. In the test mode
 223        * skin type doesn't change and that is why the new variable was needed to emulate
 224        * skin type.
 225        */
 226       var $test_rss;
 227  
 228       /**
 229        * Test URI
 230        */
 231       var $test_uri;
 232  
 233      /**
 234       * Constructor
 235       *
 236       * This may INSERT a basedomain and a useragent but NOT the HIT itself!
 237       */
 238  	function Hit( $referer = NULL, $IP = NULL, $session_id= NULL, $hit_time = NULL, $test_mode = NULL , $test_uri = NULL, $user_agent = NULL, $test_admin = NULL, $test_rss = NULL)
 239      {
 240          global $debug;
 241  
 242          if( isset($IP) )
 243          {
 244              $this->IP = $IP;
 245          }
 246          else
 247          {    // Get the first IP in the list of REMOTE_ADDR and HTTP_X_FORWARDED_FOR
 248              $this->IP = get_ip_list( true );
 249          }
 250  
 251          if (!empty($session_id))
 252          {
 253              $this->session_id = $session_id;
 254          }
 255  
 256          if (!empty($hit_time))
 257          {
 258              $this->hit_time = $hit_time;
 259          }
 260  
 261          if (!empty($test_mode))
 262          {
 263              $this->test_mode = $test_mode;
 264          }
 265  
 266          if (!empty($test_uri))
 267          {
 268              $this->test_uri = $test_uri;
 269          }
 270  
 271          if (!empty($user_agent))
 272          {
 273              $this->user_agent = $user_agent;
 274          }
 275  
 276          if (!empty($test_admin))
 277          {
 278              $this->test_admin = $test_admin;
 279          }
 280  
 281          if (!empty($test_rss))
 282          {
 283              $this->test_rss = $test_rss;
 284          }
 285  
 286          $this->hit_type = $this->get_hit_type();
 287  
 288          // Check the REFERER and determine referer_type:
 289          // TODO: dh> move this out of here, too, only if "antispam_block_spam_referers" is true,
 290          //           do something about it (here or somewhere else, but early).
 291          $this->detect_referer( $referer ); // May EXIT if we are set up to block referer spam.
 292  
 293          if( $debug )
 294          {
 295              global $Debuglog;
 296              $Debuglog->add( 'Hit: IP: '.$this->IP, 'request' );
 297              $Debuglog->add( 'Hit: UserAgent: '.$this->get_user_agent(), 'request' );
 298              $Debuglog->add( 'Hit: Referer: '.var_export($this->referer, true).'; type='.$this->referer_type, 'request' );
 299              $Debuglog->add( 'Hit: Remote Host: '.$this->get_remote_host( false ), 'request' );
 300          }
 301      }
 302  
 303  
 304      /**
 305       * Detect admin page
 306       */
 307  	function detect_admin_page()
 308      {
 309          global $Debuglog;
 310          if (empty($this->test_mode) || (!empty($this->test_mode) && !empty($this->test_admin)))
 311          {
 312              if( is_admin_page() )
 313              {    // We are inside of admin, this supersedes 'direct' access
 314                  // NOTE: this is not really a referer type but more a hit type
 315                  // $Debuglog->add( 'Hit: Referer is admin page.', 'request' );
 316                  //$this->referer_type = 'admin';
 317                  return true;
 318              }
 319          }
 320          return false;
 321      }
 322  
 323  
 324      /**
 325       * Get HTTP referrer/referer.
 326       *
 327       * Due to potential non-thread safety with getenv() (fallback), we'd better do this early.
 328       *
 329       * @return string
 330       */
 331  	function get_referer()
 332      {
 333          global $HTTP_REFERER; // might be set by PHP (give highest priority)
 334  
 335          $referer = NULL;
 336          if( isset( $HTTP_REFERER ) )
 337          { // Referer provided by PHP:
 338              $referer = $HTTP_REFERER;
 339          }
 340          else
 341          {
 342              if( isset($_SERVER['HTTP_REFERER']) )
 343              {
 344                  $referer = $_SERVER['HTTP_REFERER'];
 345              }
 346              else
 347              { // Fallback method (not thread safe :[[ ) - this function does not work in ISAPI mode.
 348                  $referer = getenv('HTTP_REFERER');
 349              }
 350          }
 351          return $referer;
 352      }
 353  
 354  
 355      /**
 356       * Get & Analyze referer
 357       *
 358       * Due to potential non-thread safety with getenv() (fallback), we'd better do this early.
 359       *
 360       * referer_type: enum('search', 'blacklist', 'referer', 'direct'); 'spam' gets used internally
 361       */
 362  	function detect_referer( $referer = NULL )
 363      {
 364          global $Debuglog, $debug;
 365          global $self_referer_list, $SpecialList;  // used to detect $referer_type
 366          global $skins_path;
 367          global $Settings;
 368  
 369          if( isset($referer) )
 370          {
 371              $this->referer = $referer;
 372          }
 373          else
 374          {    // Get referer from HTTP request
 375              $this->referer = $this->get_referer();
 376          }
 377  
 378  
 379          if( empty($this->referer) )
 380          {    // NO referer
 381              // This type may be superseeded and set to 'admin'
 382              if( ! $this->detect_admin_page() )
 383              {    // Not an admin page:
 384                  $this->referer_type = 'direct';
 385              }
 386              else
 387              {
 388                  if (empty($this->hit_type))
 389                  {
 390                      $this->hit_type = 'admin';
 391                  }
 392                  $this->referer_type = 'direct';
 393              }
 394              return;
 395          }
 396  
 397  
 398          // ANALYZE referer...
 399  
 400  
 401          // Check self referer list, see {@link $self_referer_list}
 402          // fplanque: we log these (again), because if we didn't we woudln't detect
 403          // reloads on these... and that would be a problem!
 404          foreach( $self_referer_list as $self_referer )
 405          {
 406              $pos = strpos( $this->referer, $self_referer );
 407              // If not starting within in the first 12 chars it's probably an url param as in &url=http://this_blog.com
 408              if( $pos !== false && $pos <= 12
 409                  && ! ($debug && strpos( $this->referer, '/search.html' ) ) ) // search simulation
 410              {
 411                  // This type may be superseeded by admin page
 412                  if( ! $this->detect_admin_page() )
 413                  {    // Not an admin page:
 414                      $Debuglog->add( 'Hit: detect_referer(): self referer ('.$self_referer.')', 'request' );
 415                      $this->referer_type = 'self';
 416                  }
 417                  else
 418                  {
 419                      if (empty($this->hit_type))
 420                      {
 421                          $this->hit_type = 'admin';
 422                      }
 423                      $this->referer_type = 'self';
 424                  }
 425                  return;
 426              }
 427          }
 428  
 429  
 430          // Check Special list, see {@link $SpecialList}
 431          // NOTE: This is NOT the antispam!!
 432          // fplanque: we log these (again), because if we didn't we woudln't detect
 433          // reloads on these... and that would be a problem!
 434          foreach( $SpecialList as $lSpeciallist )
 435          {
 436              $pos = strpos( $this->referer, $lSpeciallist );
 437              // If not starting within in the first 12 chars it's probably an url param as in &url=http://this_blog.com
 438              if( $pos !== false && $pos <= 12 )
 439              {
 440                  // This type may be superseeded by admin page
 441                  // fp> 2009-05-10: because of the 12 char limit above the following is probably no longer needed. Please enable it back if anyone has a problem with admin being detected as blacklist
 442                  // if( ! $this->detect_admin_page() )
 443                  {    // Not an admin page:
 444                      $Debuglog->add( 'Hit: detect_referer(): blacklist ('.$lSpeciallist.')', 'request' );
 445                      $this->referer_type = 'special';
 446                  }
 447                  return;
 448              }
 449          }
 450  
 451  
 452          // Check if the referer is valid and does not match the antispam blacklist:
 453          // NOTE: requests to admin pages should not arrive here, because they should be matched above through $self_referer_list!
 454          load_funcs('_core/_url.funcs.php');
 455          if( $error = validate_url( $this->referer, 'commenting' ) )
 456          { // This is most probably referer spam!!
 457              $Debuglog->add( 'Hit: detect_referer(): '.$error.' (SPAM)', 'hit');
 458              $this->referer_type = 'spam';
 459  
 460              if( $Settings->get('antispam_block_spam_referers') )
 461              { // In order to preserve server resources, we're going to stop processing immediatly (no logging)!!
 462                  require $skins_path.'_403_referer_spam.main.php';    // error & exit
 463                  exit(0); // just in case.
 464                  // THIS IS THE END!!
 465              }
 466  
 467              return; // type "spam"
 468          }
 469  
 470  
 471          // Is the referer a search engine?
 472          if( $this->is_search_referer($this->referer) )
 473          {
 474              $Debuglog->add( 'Hit: detect_referer(): search engine', 'request' );
 475              $this->referer_type = 'search';
 476              return;
 477          }
 478  
 479          $this->referer_type = 'referer';
 480      }
 481  
 482  
 483      /**
 484       * Get referer_domain_ID (ID of the referer in T_basedomains).
 485       *
 486       * @return integer (may be NULL, but should never).
 487       */
 488  	function get_referer_domain_ID()
 489      {
 490          if( ! isset($this->referer_domain_ID) )
 491          {
 492              global $DB;
 493              // Check if we know the base domain:
 494              $referer_basedomain = get_base_domain($this->referer);
 495              if( $referer_basedomain )
 496              {    // This referer has a base domain
 497                  // Check if we have met it before:
 498                  $hit_basedomain = $DB->get_row( '
 499                      SELECT dom_ID
 500                          FROM T_basedomains
 501                       WHERE dom_name = '.$DB->quote($referer_basedomain) );
 502                  if( !empty( $hit_basedomain->dom_ID ) )
 503                  {    // This basedomain has visited before:
 504                      $this->referer_domain_ID = $hit_basedomain->dom_ID;
 505                      // fp> The blacklist handling that was here made no sense.
 506                  }
 507                  else
 508                  {    // This is the first time this base domain visits:
 509  
 510                      // The INSERT below can fail, probably if we get two simultaneous hits (seen in the demo logfiles)
 511                      if ($this->agent_type == 'robot' || $this->hit_type == 'rss')
 512                      {
 513                          $this->dom_type = 'aggregator';
 514                      }
 515                      elseif($this->referer_type == 'search')
 516                      {
 517                          $this->dom_type = 'searcheng';
 518                      }
 519                      elseif($this->referer_type == 'referer' || $this->referer_type == 'self')
 520                      {
 521                          $this->dom_type = 'normal';
 522                      }
 523  
 524  
 525                      $DB->save_error_state();
 526  
 527                      if( $DB->query( '
 528                          INSERT INTO T_basedomains( dom_name, dom_type)
 529                              VALUES( '.$DB->quote($referer_basedomain).', '.$DB->quote($this->dom_type).' )' ) )
 530                      { // INSERTed ok:
 531                          $this->referer_domain_ID = $DB->insert_id;
 532                      }
 533                      else
 534                      { // INSERT failed: see, try to select again (may become/stay NULL)
 535                          $this->referer_domain_ID = $DB->get_var( '
 536                              SELECT dom_ID
 537                                  FROM T_basedomains
 538                               WHERE dom_name = '.$DB->quote($referer_basedomain) );
 539                      }
 540  
 541                      $DB->restore_error_state();
 542                  }
 543              }
 544          }
 545          return $this->referer_domain_ID;
 546      }
 547  
 548  
 549      /**
 550       * Set {@link $user_agent} and detect the browser.
 551       * This function also sets {@link $agent_type}.
 552       */
 553  	function detect_useragent()
 554      {
 555          if( isset($this->agent_type) )
 556          { // already detected.
 557              return;
 558          }
 559  
 560          global $DB, $Debuglog;
 561          global $user_agents;
 562          global $Skin; // to detect agent_type (gets set in /xmlsrv/atom.php for example)
 563  
 564  
 565          // Init is_* members.
 566          $this->is_lynx = false;
 567          $this->is_firefox = false;
 568          $this->is_gecko = false;
 569          $this->is_IE = false;
 570          $this->is_winIE = false;
 571          $this->is_macIE = false;
 572          $this->is_chrome = false;
 573          $this->is_safari = false;
 574          $this->is_opera = false;
 575          $this->is_NS4 = false;
 576  
 577          $this->agent_type = 'unknown';
 578          $this->agent_name = '';
 579          $this->agent_platform = '';
 580  
 581          $user_agent = $this->get_user_agent();
 582  
 583          if( ! empty($user_agent) )
 584          { // detect browser
 585              if( strpos($user_agent, 'Win') !== false)
 586              {
 587                  $this->agent_platform = 'win';
 588              }
 589              elseif( strpos($user_agent, 'Mac') !== false)
 590              {
 591                  $this->agent_platform = 'mac';
 592              }
 593              elseif( strpos($user_agent, 'Linux') !== false)
 594              {
 595                  $this->agent_platform = 'linux';
 596              }
 597              elseif( $browscap = $this->get_browser_caps() )
 598              {
 599                  $platform = isset($browscap->platform) ? $browscap->platform : '';
 600  
 601                  $Debuglog->add( 'Hit:detect_useragent(): Trying to detect platform using browscap', 'request' );
 602                  $Debuglog->add( 'Hit:detect_useragent(): Raw platform string: "'.$platform.'"', 'request' );
 603  
 604                  $platform = strtolower( $platform );
 605                  if( $platform == 'linux' || in_array( substr( $platform, 0, 3 ), array( 'win', 'mac' ) ) )
 606                  {
 607                      $this->agent_platform = $platform;
 608                  }
 609              }
 610  
 611              if(strpos($user_agent, 'Lynx') !== false)
 612              {
 613                  $this->is_lynx = 1;
 614                  $this->agent_name = 'lynx';
 615                  $this->agent_type = 'browser';
 616              }
 617              elseif(strpos($user_agent, 'Firefox/') !== false)
 618              {
 619                  $this->is_firefox = 1;
 620                  $this->agent_name = 'firefox';
 621                  $this->agent_type = 'browser';
 622              }
 623              elseif(strpos($user_agent, 'Gecko/') !== false)    // We don't want to see Safari as Gecko
 624              {    // Tblue> Note: Gecko is only a rendering engine, not a real browser!
 625                  $this->is_gecko = 1;
 626                  $this->agent_name = 'gecko';
 627                  $this->agent_type = 'browser';
 628              }
 629              elseif(strpos($user_agent, 'MSIE') !== false && $this->agent_platform == 'win' )
 630              {
 631                  $this->is_IE = true;
 632                  $this->is_winIE = 1;
 633                  $this->agent_name = 'msie';
 634                  $this->agent_type = 'browser';
 635              }
 636              elseif(strpos($user_agent, 'MSIE') !== false && $this->agent_platform == 'mac' )
 637              {
 638                  $this->is_IE = true;
 639                  $this->is_macIE = 1;
 640                  $this->agent_name = 'msie';
 641                  $this->agent_type = 'browser';
 642              }
 643              elseif(strpos($user_agent, 'Chrome/') !== false)
 644              {
 645                  $this->is_chrome = true;
 646                  $this->agent_name = 'chrome';
 647                  $this->agent_type = 'browser';
 648              }
 649              elseif(strpos($user_agent, 'Safari/') !== false)
 650              {
 651                  $this->is_safari = true;
 652                  $this->agent_name = 'safari';
 653                  $this->agent_type = 'browser';
 654              }
 655              elseif(strpos($user_agent, 'Opera') !== false)
 656              {
 657                  $this->is_opera = 1;
 658                  $this->agent_name = 'opera';
 659                  $this->agent_type = 'browser';
 660              }
 661              elseif(strpos($user_agent, 'Nav') !== false || preg_match('/Mozilla\/4\./', $user_agent))
 662              {
 663                  $this->is_NS4 = 1;
 664                  $this->agent_name = 'nav4';
 665                  $this->agent_type = 'browser';
 666              }
 667          }
 668  
 669          $Debuglog->add( 'Hit:detect_useragent(): Agent name: '.$this->agent_name, 'request' );
 670          $Debuglog->add( 'Hit:detect_useragent(): Agent platform: '.$this->agent_platform, 'request' );
 671  
 672  
 673           // Lookup robots
 674              $match = false;
 675              foreach( $user_agents as $lUserAgent )
 676              {
 677                  if( $lUserAgent[0] == 'robot' && strpos( $this->user_agent, $lUserAgent[1] ) !== false )
 678                  {
 679                      $Debuglog->add( 'Hit:detect_useragent(): robot', 'request' );
 680                      $this->agent_type = 'robot';
 681                      $match = true;
 682                      break;
 683                  }
 684              }
 685  
 686              if( ! $match && ($browscap = $this->get_browser_caps()) &&
 687                  isset($browscap->crawler) && $browscap->crawler )
 688              {
 689                  $Debuglog->add( 'Hit:detect_useragent(): robot (through browscap)', 'request' );
 690                  $this->agent_type = 'robot';
 691              }
 692  
 693          }
 694  
 695  
 696      /**
 697       * Get browser capabilities through {@link get_browser()}.
 698       *
 699       * @return false|object The return value of get_browser().
 700       */
 701  	function get_browser_caps()
 702      {
 703          static $caps = NULL;
 704  
 705          if( $caps === NULL )
 706          {
 707              $caps = @get_browser( $this->get_user_agent() );
 708          }
 709  
 710          return $caps;
 711      }
 712  
 713  
 714      /**
 715       * Log a hit on a blog page / rss feed.
 716       *
 717       * This function should be called at the end of the page, otherwise if the page
 718       * is displaying previous hits, it may display the current one too.
 719       *
 720       * The hit will not be logged in special occasions, see {@link $ignore} and {@link is_good_hit()}.
 721       *
 722       * It will call {@link Hitlist::dbprune()} to do the automatic pruning of old hits in case
 723       * of auto_prune_stats_mode == "page".
 724       *
 725       * @return boolean true if the hit gets logged; false if not
 726       */
 727  	function log()
 728      {
 729          global $Plugins;
 730  
 731          if( $this->logged )
 732          {    // Already logged, don't log twice:
 733              return false;
 734          }
 735  
 736          // Remember we have already attempted to log:
 737          $this->logged = true;
 738  
 739          // Check if this hit should be logged:
 740          if( ! $this->should_be_logged() )
 741          {
 742              return false;
 743          }
 744  
 745          if( $Plugins->trigger_event_first_true('AppendHitLog', array( 'Hit' => &$this ) ) )
 746          {    // Plugin has handled recording
 747              return true;
 748          }
 749  
 750          // Check if this hit should STILL be logged after plugin call:
 751          if( ! $this->should_be_logged() )
 752          {
 753              return false;
 754          }
 755  
 756          // Record the HIT now:
 757          $this->record_the_hit();
 758  
 759          return true;
 760      }
 761  
 762  
 763      /**
 764       * Tell if a HIT should be logged:
 765       *
 766       * @return boolean
 767       */
 768  	function should_be_logged()
 769      {
 770          global $Settings, $Debuglog, $is_admin_page;
 771  
 772          if( $is_admin_page && ! $Settings->get('log_admin_hits') && empty($this->test_mode))
 773          {    // We don't want to log admin hits:
 774              $Debuglog->add( 'Hit: Hit NOT logged, (Admin page logging is disabled)', 'request' );
 775              return false;
 776          }
 777  
 778          if( ! $is_admin_page && ! $Settings->get('log_public_hits') )
 779          {    // We don't want to log public hits:
 780              $Debuglog->add( 'Hit: Hit NOT logged, (Public page logging is disabled)', 'request' );
 781              return false;
 782          }
 783  
 784          if( ($this->referer_type == 'spam' && ! $Settings->get('log_spam_hits')) && empty($this->test_mode) )
 785          {    // We don't want to log referer spam hits:
 786              $Debuglog->add( 'Hit: Hit NOT logged, (Referer spam)', 'request' );
 787              return false;
 788          }
 789  
 790          return true;
 791      }
 792  
 793  
 794      /**
 795       * This records the hit. You should not call this directly, but {@link Hit::log()} instead!
 796       *
 797       * However, if a Plugin registers the {@link Plugin::AppendHitLog() AppendHitLog event}, it
 798       * could be necessary to call this as a shutdown function.
 799       */
 800  	function record_the_hit()
 801      {
 802          global $DB, $Session, $ReqURI, $Blog, $blog, $localtimenow, $Debuglog, $disp, $ctrl, $http_response_code;
 803  
 804          // To log current display and controller the global variables $disp and $ctrl are used. They can be setup while calling of some controller
 805          // or while forming a page. In case if these variables aren't setup, NULL is recorded to the DB.
 806          $Debuglog->add( 'Hit: Recording the hit.', 'request' );
 807  
 808          $this->action = $this->get_action();
 809  
 810          if(empty($this->test_uri))
 811          {
 812              if( !empty($Blog) )
 813              {
 814                  $blog_ID = $Blog->ID;
 815              }
 816              elseif( !empty( $blog ) )
 817              {
 818                  if( ! is_numeric($blog) )
 819                  { // this can be anything given by URL param "blog"! (because it's called on shutdown)
 820                    // see todo in param().
 821                      $blog = NULL;
 822                  }
 823                  $blog_ID = $blog;
 824              }
 825              else
 826              {
 827                  $blog_ID = NULL;
 828              }
 829          }
 830          else
 831          {
 832              $blog_ID = $this->test_uri['blog_id'];
 833              $ReqURI = $this->test_uri['link'];
 834          }
 835  
 836          $hit_uri = substr($ReqURI, 0, 250); // VARCHAR(250) and likely to be longer
 837          $hit_referer = substr($this->referer, 0, 250); // VARCHAR(250) and likely to be longer
 838  
 839          // Extract the keyphrase from search referers:
 840          $keyphrase = $this->get_keyphrase();
 841  
 842          $keyp_ID = NULL;
 843  
 844          if ( empty ($keyphrase) )
 845          {    // No search hit
 846              if ( ! empty($this->test_mode) && !empty($this->test_uri['s']))
 847              {
 848                  $s = $this->test_uri['s'];
 849              }
 850              else
 851              {
 852                  $s = get_param("s");
 853              }
 854              if( !empty($s) && !empty($blog_ID) )
 855              {    // Record Internal Search:
 856                  $keyphrase  = $s;
 857  /*                        load_class('sessions/model/_internal_searches.class.php', 'Internalsearches' );
 858                          $internal_searches = new InternalSearches();
 859                          $internal_searches->set("coll_ID" , $blog_ID);
 860                          $internal_searches->set("hit_ID" , $hit_ID);
 861                          $internal_searches->set("keywords" , get_param("s") );
 862                          $internal_searches->dbinsert();
 863  */            }
 864          }
 865  
 866  
 867  
 868  /*        if( !empty( $keyphrase ) )
 869          {
 870              $DB->begin();
 871  
 872              $sql = 'SELECT keyp_ID
 873                        FROM T_track__keyphrase
 874                       WHERE keyp_phrase = '.$DB->quote($keyphrase);
 875              $keyp_ID = $DB->get_var( $sql, 0, 0, 'Get keyphrase ID' );
 876  
 877              if( empty( $keyp_ID ) )
 878              {
 879                  $sql = 'INSERT INTO T_track__keyphrase( keyp_phrase )
 880                      VALUES ('.$DB->quote($keyphrase).')';
 881                  $DB->query( $sql, 'Add new keyphrase' );
 882                  $keyp_ID = $DB->insert_id;
 883              }
 884          }
 885  */
 886          // Extract the serprank from search referers:
 887          $serprank = $this->get_serprank();
 888  
 889          if (!empty($http_response_code))
 890          {    //  in some cases $http_response_code not set and we can use value by default
 891              $this->hit_response_code = $http_response_code;
 892          }
 893  
 894          if (empty($this->hit_type))
 895          {
 896              global $Skin;
 897  
 898              if( (isset( $Skin ) && $Skin->type == 'feed') || !empty($this->test_rss) )
 899              {
 900                  $this->hit_type = 'rss';
 901              }
 902              else
 903              {
 904                  if ( isset($_SERVER['HTTP_X_REQUESTED_WITH']) && (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') )
 905                  {
 906                      $this->hit_type = 'ajax';
 907                  }
 908                  else
 909                  {
 910                      $this->hit_type = 'standard';
 911                  }
 912              }
 913          }
 914  
 915  
 916          // insert hit into DB table:
 917          if (empty($this->test_mode))
 918          {
 919              $sql = "
 920                  INSERT INTO T_hitlog(
 921                      hit_sess_ID, hit_datetime, hit_uri, hit_disp, hit_ctrl, hit_action, hit_type ,hit_referer_type,
 922                      hit_referer, hit_referer_dom_ID, hit_keyphrase_keyp_ID, hit_keyphrase, hit_serprank, hit_blog_ID, hit_remote_addr, hit_agent_type, hit_response_code )
 923                  VALUES( '".$Session->ID."', FROM_UNIXTIME(".$localtimenow."), '".$DB->escape($hit_uri)."', ".$DB->quote($disp).", ".$DB->quote($ctrl).", ".$DB->quote($this->action).", '".$this->hit_type."', '".$this->referer_type
 924                      ."', '".$DB->escape($hit_referer)."', ".$DB->null($this->get_referer_domain_ID()).', '.$DB->null($keyp_ID).', '.$DB->quote($keyphrase)
 925                      .', '.$DB->null($serprank).', '.$DB->null($blog_ID).", '".$DB->escape( $this->IP )."', '".$this->get_agent_type()."', ".$DB->null($this->hit_response_code).")";
 926          }
 927          else
 928          {
 929              // Test mode
 930              isset($this->test_uri['disp']) ? $test_disp = $this->test_uri['disp'] : $test_disp = NULL;
 931              isset($this->test_uri['ctrl']) ? $test_ctrl = $this->test_uri['ctrl'] : $test_ctrl = NULL;
 932              $this->action = NULL;
 933  
 934              $sql = "
 935                  INSERT INTO T_hitlog(
 936                      hit_sess_ID, hit_datetime, hit_uri, hit_disp, hit_ctrl, hit_action, hit_type, hit_referer_type,
 937                      hit_referer, hit_referer_dom_ID, hit_keyphrase_keyp_ID, hit_keyphrase ,hit_serprank, hit_blog_ID, hit_remote_addr, hit_agent_type, hit_response_code )
 938                  VALUES( '".$this->session_id."', FROM_UNIXTIME(".$this->hit_time."), '".$DB->escape($hit_uri)."', ".$DB->quote($test_disp).", ".$DB->quote($test_ctrl).", ".$DB->quote($this->action).", '".$this->hit_type."', '".$this->referer_type
 939                      ."', '".$DB->escape($hit_referer)."', ".$DB->null($this->get_referer_domain_ID()).', '.$DB->null($keyp_ID).', '.$DB->quote($keyphrase)
 940                      .', '.$DB->null($serprank).', '.$DB->null($blog_ID).", '".$DB->escape( $this->IP )."', '".$this->get_agent_type()."', ".$DB->null($this->hit_response_code).")";
 941  
 942          }
 943  
 944  
 945          $DB->query( $sql, 'Record the hit' );
 946          $hit_ID = $DB->insert_id;
 947  
 948          if( !empty( $keyphrase ) )
 949          {
 950              $DB->commit();
 951          }
 952  
 953          $this->ID = $hit_ID;
 954      }
 955  
 956  
 957      /**
 958       * Get hit type from the uri
 959       * @return mixed Hit type
 960       */
 961  	function get_hit_type()
 962      {
 963          global $ReqURI;
 964          if( $this->hit_type )
 965          {
 966              return $this->hit_type;
 967          }
 968  
 969          $ajax_array = array('htsrv/async.php', 'htsrv/anon_async.php');
 970  
 971          foreach ($ajax_array as $ajax)
 972          {
 973              if (strstr($ReqURI,$ajax))
 974              {
 975                  return 'ajax';
 976              }
 977          }
 978  
 979          if (strstr($ReqURI,'htsrv/'))
 980          {
 981              return 'service';
 982          }
 983          else
 984          {
 985              return NULL;
 986          }
 987      }
 988  
 989      /**
 990       * Get hit action
 991       * @return mixed Hit action
 992       */
 993  	function get_action()
 994      {
 995          global $ReqURI, $action;
 996          if( $this->action )
 997          {
 998              return $this->action;
 999          }
1000  
1001          if (strstr($ReqURI,'htsrv/getfile.php'))
1002          { // special case
1003              return 'getfile';
1004          }
1005          if (!empty ($action))
1006          {
1007              return $action;
1008          }
1009          else
1010          {
1011              return NULL;
1012          }
1013      }
1014  
1015  
1016  
1017      /**
1018       * Get the keyphrase from the referer
1019       */
1020  	function get_keyphrase()
1021      {
1022          if( $this->_search_params_tried )
1023          {
1024              return $this->_keyphrase;
1025          }
1026  
1027          if( $this->referer_type == 'search' )
1028          {
1029              $this->extract_params_from_referer( $this->referer );
1030          }
1031  
1032          return $this->_keyphrase;
1033      }
1034  
1035  
1036      /**
1037       * Get the serprank from the referer
1038       */
1039  	function get_serprank()
1040      {
1041          if( $this->_search_params_tried )
1042          {
1043              return $this->_serprank;
1044          }
1045  
1046          if( $this->referer_type == 'search' )
1047          {
1048              $this->extract_params_from_referer( $this->referer );
1049          }
1050  
1051          return $this->_serprank;
1052      }
1053  
1054  
1055      /**
1056       * Get name of search engine
1057       */
1058  	function get_search_engine()
1059      {
1060          if( $this->_search_params_tried )
1061          {
1062              return $this->_search_engine;
1063          }
1064  
1065          if( $this->referer_type == 'search' )
1066          {
1067              $this->extract_params_from_referer( $this->referer );
1068          }
1069  
1070          return $this->_search_engine;
1071      }
1072  
1073  
1074      /**
1075       * Get the User agent's signature.
1076       *
1077       * @return string False, if not provided or empty, if it included tags.
1078       */
1079  	function get_user_agent()
1080      {
1081          if( ! isset($this->user_agent) )
1082          {
1083              global $HTTP_USER_AGENT; // might be set by PHP, give highest priority
1084  
1085              if( isset($HTTP_USER_AGENT) )
1086              {
1087                  $this->user_agent = $HTTP_USER_AGENT;
1088              }
1089              elseif( isset($_SERVER['HTTP_USER_AGENT']) )
1090              {
1091                  $this->user_agent = $_SERVER['HTTP_USER_AGENT'];
1092              }
1093              else
1094              {
1095                  $this->user_agent = false;
1096              }
1097  
1098              if( $this->user_agent != strip_tags($this->user_agent) )
1099              { // then they have tried something funky, putting HTML or PHP into the user agent
1100                  global $Debuglog;
1101                  $Debuglog->add( 'Hit: detect_useragent(): '.T_('bad char in User Agent'), 'hit');
1102                  $this->user_agent = '';
1103              }
1104              else
1105              {
1106                  $this->user_agent = substr( $this->user_agent, 0, 250 );
1107              }
1108          }
1109          return $this->user_agent;
1110      }
1111  
1112  
1113      /**
1114       * Get the User agent's name.
1115       *
1116       * @return string
1117       */
1118  	function get_agent_name()
1119      {
1120          if( ! isset($this->agent_name) )
1121          {
1122              $this->detect_useragent();
1123          }
1124          return $this->agent_name;
1125      }
1126  
1127  
1128      /**
1129       * Get the User agent's platform.
1130       *
1131       * @return string
1132       */
1133  	function get_agent_platform()
1134      {
1135          if( ! isset($this->agent_platform) )
1136          {
1137              $this->detect_useragent();
1138          }
1139          return $this->agent_platform;
1140      }
1141  
1142  
1143      /**
1144       * Get the User agent's type.
1145       *
1146       * @return string
1147       */
1148  	function get_agent_type()
1149      {
1150          if( ! isset($this->agent_type) )
1151          {
1152              $this->detect_useragent();
1153          }
1154          return $this->agent_type;
1155      }
1156  
1157  
1158      /**
1159       * Get the remote hostname.
1160       *
1161       * @return string
1162       */
1163  	function get_remote_host( $allow_nslookup = false )
1164      {
1165          global $Timer;
1166  
1167          $Timer->resume( 'Hit::get_remote_host' );
1168  
1169          if( is_null($this->_remoteHost) )
1170          {
1171              if( isset( $_SERVER['REMOTE_HOST'] ) )
1172              {
1173                  $this->_remoteHost = $_SERVER['REMOTE_HOST'];
1174              }
1175              elseif( $allow_nslookup )
1176              { // We allowed reverse DNS lookup:
1177                  // This can be terribly time consuming (4/5 seconds!) when there is no reverse dns available!
1178                  // This is the case on many intranets and many users' first time installs!!!
1179                  // Some people end up considering evocore is very slow just because of this line!
1180                  // This cannot be enabled by default.
1181                  $this->_remoteHost = @gethostbyaddr($this->IP);
1182              }
1183              else
1184              {
1185                  $this->_remoteHost = '';
1186              }
1187          }
1188  
1189          $Timer->pause( 'Hit::get_remote_host' );
1190  
1191          return $this->_remoteHost;
1192      }
1193  
1194  
1195  	function get_param_from_string( $string, $param )
1196      {
1197          // Make sure the string does not start with "?", otherwise parse_str adds the question mark to the first param
1198          $string = preg_replace( '~^\?~', '', $string );
1199  
1200          parse_str($string, $array);
1201  
1202          $value = NULL;
1203          if( isset($array[$param]) && trim($array[$param]) != '' )
1204          {
1205              $value = $array[$param];
1206          }
1207          return $value;
1208      }
1209  
1210  
1211      /**
1212       * Get array of 2 letter ISO country codes
1213       *
1214       * @return array
1215       */
1216  	function get_country_codes()
1217      {
1218          global $DB;
1219  
1220          if( is_null( $this->country_codes ) )
1221          {
1222              // sam2kb> Save one DB query on every page load
1223              // $this->country_codes = $DB->get_col('SELECT ctry_code FROM T_regional__country', 0, 'get 2 letter ISO country codes' );
1224  
1225              $countries = '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,
1226                              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,
1227                              cu, cv, cx, cy, cz, de, dj, dk, dm, do, dz, ec, ee, eg, eh, er, es, et, fi, fj, fk, fm, fo, fr, ga,
1228                              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,
1229                              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,
1230                              lb, lc, li, lk, lr, ls, lt, lu, lv, ly, ma, mc, md, me, mf, mg, mh, mk, ml, mm, mn, mo, mp, mq, mr,
1231                              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,
1232                              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,
1233                              sk, sl, sm, sn, so, sr, st, sv, sy, sz, tc, td, tf, tg, th, tj, tk, tl, tm, tn, to, tr, tt, tv, tw,
1234                              tz, ua, ug, um, us, uy, uz, va, vc, ve, vg, vi, vn, vu, wf, ws, ye, yt, za, zm, zw';
1235  
1236              $this->country_codes = array_map( 'trim', explode(',', $countries) );
1237          }
1238          return $this->country_codes;
1239      }
1240  
1241  
1242      /**
1243       * @return array Array of ( searchEngineName => URL )
1244       */
1245  	function get_search_engine_names()
1246      {
1247          global $search_engine_params;
1248  
1249          if( is_null( $this->search_engine_names ) )
1250          {
1251              $this->search_engine_names = array();
1252              foreach( $search_engine_params as $url => $info )
1253              {
1254                  if( !isset($this->search_engine_names[$info[0]]) )
1255                  {    // Do not overwrite existing keys
1256                      $this->search_engine_names[$info[0]] = $url;
1257                  }
1258              }
1259          }
1260          return $this->search_engine_names;
1261      }
1262  
1263  
1264      /**
1265       * Reduce URL to more minimal form.  2 letter country codes are
1266       * replaced by '{}', while other parts are simply removed.
1267       *
1268       * Examples:
1269       *   www.example.com -> example.com
1270       *   search.example.com -> example.com
1271       *   m.example.com -> example.com
1272       *   de.example.com -> {}.example.com
1273       *   example.de -> example.{}
1274       *   example.co.uk -> example.{}
1275       *
1276       * @param string $url
1277       * @return string
1278       */
1279  	function get_lossy_url( $url )
1280      {
1281          static $countries;
1282  
1283          if( !isset($countries) )
1284          {    // Load 2 letter ISO country codes
1285              $countries = implode( '|', $this->get_country_codes() );
1286          }
1287  
1288          // Add not ISO 3166-1 country code top level domains
1289          $other_ccTLDs = ', uk, eu';
1290  
1291          return preg_replace(
1292              array(
1293                  '/^(w+[0-9]*|search)\./',
1294                  '/(^|\.)m\./',
1295                  '/(\.(com|org|net|co|it|edu))?\.('.$countries.$other_ccTLDs.')(\/|$)/',
1296                  '/^('.$countries.$other_ccTLDs.')\./',
1297              ),
1298              array(
1299                  '',
1300                  '$1',
1301                  '.{}$4',
1302                  '{}.',
1303              ),
1304              $url);
1305      }
1306  
1307  
1308      /**
1309       * Determine if a hit is a new view (not reloaded or from a robot).
1310       *
1311       * 'Reloaded' means: visited before from the same user (in a session) or from same IP/user_agent in the
1312       * last {@link $Settings reloadpage_timeout} seconds.
1313       *
1314       * This gets queried by the Item objects before incrementing its view count (if the Item gets viewed
1315       * in total ({@link $dispmore})).
1316       *
1317       * @todo fplanque>> if this is only useful to display who's online or view counts, provide option to disable all those resource consuming gadgets. (Those gadgets should be plugins actually, and they should enable this query only if needed)
1318       *        blueyed>> Move functionality to Plugin (with a hook in Item::content())?!
1319       * @return boolean
1320       */
1321  	function is_new_view()
1322      {
1323          if( $this->get_agent_type() == 'robot' )
1324          {    // Robot requests are not considered as (new) views:
1325              return false;
1326          }
1327  
1328          if( ! isset($this->_is_new_view) )
1329          {
1330              global $current_User;
1331              global $DB, $Debuglog, $Settings, $ReqURI, $localtimenow;
1332              global $Session;
1333  
1334              // Restrict to current user if logged in:
1335              if( ! empty($current_User->ID) )
1336              { // select by user ID: one user counts really just once. May be even faster than the anonymous query below..!?
1337                  $sql = "
1338                      SELECT SQL_NO_CACHE hit_ID FROM T_hitlog
1339                       WHERE hit_sess_ID = ".$Session->ID."
1340                           AND hit_uri = '".$DB->escape( substr($ReqURI, 0, 250) )."'
1341                       LIMIT 1";
1342              }
1343              else
1344              { // select by remote_addr/hit_agent_type:
1345                  $sql = "
1346                      SELECT SQL_NO_CACHE hit_ID
1347                        FROM T_hitlog
1348                       WHERE hit_datetime > '".date( 'Y-m-d H:i:s', $localtimenow - $Settings->get('reloadpage_timeout') )."'
1349                         AND hit_remote_addr = ".$DB->quote( $this->IP )."
1350                         AND hit_uri = '".$DB->escape( substr($ReqURI, 0, 250) )."'
1351                         AND hit_agent_type = ".$DB->quote($this->get_agent_type())."
1352                       LIMIT 1";
1353              }
1354              if( $DB->get_var( $sql, 0, 0, 'Hit: Check for reload' ) )
1355              {
1356                  $Debuglog->add( 'Hit: No new view!', 'request' );
1357                  $this->_is_new_view = false;  // We don't want to log this hit again
1358              }
1359              else
1360              {
1361                  $this->_is_new_view = true;
1362              }
1363          }
1364  
1365          return $this->_is_new_view;
1366      }
1367  
1368  
1369      /**
1370       * Is this a browser reload (F5)?
1371       *
1372       * @return boolean true on reload, false if not.
1373       */
1374  	function is_browser_reload()
1375      {
1376          if( ( isset( $_SERVER['HTTP_CACHE_CONTROL'] ) && strpos( $_SERVER['HTTP_CACHE_CONTROL'], 'max-age=0' ) !== false )
1377              || ( isset( $_SERVER['HTTP_PRAGMA'] ) && $_SERVER['HTTP_PRAGMA'] == 'no-cache' ) )
1378          { // Reload
1379              return true;
1380          }
1381  
1382          return false;
1383      }
1384  
1385  
1386      /*
1387       * Is this a search referer hit?
1388       *
1389       * Note: in some situations it is not possible to detect search keywords (like google redirect URLs),
1390       * and in stats you may see [n.a.] in 'Search keywords' column.
1391       *
1392       * @param string Hit referer
1393       * @param boolean true to return an array, false to return boolean
1394       * @return boolean|array of normalized referer parts: (host, path, query, fragment)
1395       */
1396  	function is_search_referer( $referer, $return_params = false )
1397      {
1398          global $search_engine_params;
1399  
1400          // Load search engine definitions
1401          require_once dirname(__FILE__).'/_search_engines.php';
1402  
1403          // Parse referer
1404          $pu = @parse_url($referer);
1405  
1406          if( ! isset($pu['query']) || ! isset($pu['host']) )
1407          {
1408              return false;
1409          }
1410  
1411          $ref_host = $pu['host'];
1412          $ref_query = isset($pu['query']) ? $pu['query'] : '';
1413          $ref_fragment = isset($pu['fragment']) ? $pu['fragment'] : '';
1414  
1415          // Some search engines (eg. Bing Images) use the same domain
1416          // as an existing search engine (eg. Bing), we must also use the url path
1417          $ref_path = isset($pu['path']) ? $pu['path'] : '';
1418  
1419          $host_pattern = $this->get_lossy_url($ref_host);
1420  
1421          if( array_key_exists($ref_host.$ref_path, $search_engine_params) )
1422          {
1423              $ref_host = $ref_host.$ref_path;
1424          }
1425          elseif( array_key_exists($host_pattern.$ref_path, $search_engine_params) )
1426          {
1427              $ref_host = $host_pattern.$ref_path;
1428          }
1429          elseif( array_key_exists($host_pattern, $search_engine_params) )
1430          {
1431              $ref_host = $host_pattern;
1432          }
1433          elseif( !array_key_exists($ref_host, $search_engine_params) )
1434          {
1435              if( !strncmp($ref_query, 'cx=partner-pub-', 15) )
1436              {    // Google custom search engine
1437                  $ref_host = 'www.google.com/cse';
1438              }
1439              elseif( !strncmp($ref_path, '/pemonitorhosted/ws/results/', 28) )
1440              {    // Private-label search powered by InfoSpace Metasearch
1441                  $ref_host = 'infospace.com';
1442              }
1443              else
1444              {    // Not a search referer
1445                  return false;
1446              }
1447          }
1448  
1449          if( $return_params )
1450          {
1451              return array( $ref_host, $ref_path, $ref_query, $ref_fragment );
1452          }
1453          return true;
1454      }
1455  
1456  
1457      /**
1458       * Extracts a keyword from a raw not encoded URL.
1459       * Will only extract keyword if a known search engine has been detected.
1460       * Returns the keyword:
1461       * - in UTF8: automatically converted from other charsets when applicable
1462       * - strtolowered: "QUErY test!" will return "query test!"
1463       * - trimmed: extra spaces before and after are removed
1464       *
1465       * A list of supported search engines can be found in /inc/sessions/model/_search_engines.php
1466       * The function returns false when a keyword couldn't be found.
1467       *      eg. if the url is "http://www.google.com/partners.html" this will return false,
1468       *       as the google keyword parameter couldn't be found.
1469       *
1470       * @param string URL referer
1471       * @return array|false false if a keyword couldn't be extracted,
1472       *                         or array(
1473       *                             'engine_name' => 'Google',
1474       *                             'keywords' => 'my searched keywords',
1475       *                            'serprank' => 4)
1476       */
1477  	function extract_params_from_referer( $ref )
1478      {
1479          global $Debuglog, $search_engine_params, $evo_charset, $current_charset;
1480  
1481          // Make sure we don't try params extraction twice
1482          $this->_search_params_tried = true;
1483  
1484          @list($ref_host, $ref_path, $query, $fragment) = $this->is_search_referer($ref, true);
1485  
1486          if( empty($ref_host) )
1487          {    // Not a search referer
1488              return false;
1489          }
1490  
1491          $search_engine_name = $search_engine_params[$ref_host][0];
1492  
1493          $keyword_param = NULL;
1494          if( !empty($search_engine_params[$ref_host][1]) )
1495          {
1496              $keyword_param = $search_engine_params[$ref_host][1];
1497          }
1498          if( is_null($keyword_param) )
1499          {    // Get settings from first item in group
1500              $search_engine_names = $this->get_search_engine_names();
1501  
1502              $url = $search_engine_names[$search_engine_name];
1503              $keyword_param = $search_engine_params[$url][1];
1504          }
1505          if( !is_array($keyword_param) )
1506          {
1507              $keyword_param = array($keyword_param);
1508          }
1509  
1510          if( $search_engine_name == 'Google Images' || ($search_engine_name == 'Google' && strpos($ref, '/imgres') !== false) )
1511          {    // Google image search
1512              $search_engine_name = 'Google Images';
1513  
1514              $query = urldecode(trim( $this->get_param_from_string($query, 'prev') ));
1515              $query = str_replace( '&', '&amp;', strstr($query, '?') );
1516          }
1517          elseif( $search_engine_name == 'Google' && (strpos($query, '&as_') !== false || strpos($query, 'as_') === 0) )
1518          { // Google with "as_" param
1519              $keys = array();
1520  
1521              if( $key = $this->get_param_from_string($query, 'as_q') )
1522              {
1523                  array_push($keys, $key);
1524              }
1525              if( $key = $this->get_param_from_string($query, 'as_oq') )
1526              {
1527                  array_push($keys, str_replace('+', ' OR ', $key));
1528              }
1529              if( $key = $this->get_param_from_string($query, 'as_epq') )
1530              {
1531                  array_push($keys, "\"$key\"");
1532              }
1533              if( $key = $this->get_param_from_string($query, 'as_eq') )
1534              {
1535                  array_push($keys, "-$key");
1536              }
1537              $key = trim(urldecode(implode(' ', $keys)));
1538          }
1539  
1540          if( empty($key) )
1541          {    // we haven't extracted a search key with the special cases above...
1542              foreach( $keyword_param as $param )
1543              {
1544                  if( $param[0] == '/' )
1545                  {    // regular expression match
1546                      if( @preg_match($param, $ref, $matches) )
1547                      {
1548                          $key = trim(urldecode($matches[1]));
1549                          break;
1550                      }
1551                  }
1552                  else
1553                  {    // search for keywords now &vname=keyword
1554                      if( $key = $this->get_param_from_string($query, $param) )
1555                      {
1556                          $key = trim(urldecode($key));
1557                          if( !empty($key) ) break;
1558                      }
1559                  }
1560              }
1561          }
1562  
1563          $key_param_in_query = false;
1564          if( empty( $key ) && ! empty( $keyword_param ) )
1565          { // Check if empty key param exists in query, e.g. "/search?q=&other_param=text"
1566              foreach( $keyword_param as $k_param )
1567              {
1568                  if( strpos( $query, '&'.$k_param.'=' ) !== false || strpos( $query, $k_param.'=' ) === 0 )
1569                  { // Key param with empty value exists in query, We can decide this referer url as from search engine
1570                      $key_param_in_query = true;
1571                  }
1572              }
1573          }
1574  
1575          if( empty( $key ) && ! $key_param_in_query )
1576          { // Not a search referer
1577              if( $this->referer_type == 'search' )
1578              { // If the referer was detected as 'search' we need to change it to 'special'
1579                  // to keep search stats clean.
1580                  $this->referer_type = 'special';
1581                  $Debuglog->add( 'Hit: extract_params_from_referer() overrides referer type set by detect_referer(): "search" -> "special"', 'request' );
1582              }
1583  
1584              return false;
1585          }
1586  
1587  
1588          // Convert encoding
1589          if( !empty($search_engine_params[$ref_host][3]) )
1590          {
1591              $ie = $search_engine_params[$ref_host][3];
1592          }
1593          elseif( isset($url) && !empty($search_engine_params[$url][3]) )
1594          {
1595              $ie = $search_engine_params[$url][3];
1596          }
1597          else
1598          {    // Fallback to default encoding
1599              $ie = array('utf-8', 'iso-8859-15');
1600          }
1601  
1602          if( is_array($ie) )
1603          {
1604              if( can_check_encoding() )
1605              {
1606                  foreach( $ie as $test_encoding )
1607                  {
1608                      if( check_encoding($key, $test_encoding) )
1609                      {
1610                          $ie = $test_encoding;
1611                          break;
1612                      }
1613                  }
1614              }
1615              else
1616              {
1617                  $ie = $ie[0];
1618              }
1619          }
1620  
1621          $key = convert_charset($key, $evo_charset, $ie);
1622          // convert to lower string but keep in evo_charset
1623          $saved_charset = $current_charset;
1624          $current_charset = $evo_charset;
1625          $key = evo_strtolower($key);
1626          $current_charset = $saved_charset;
1627  
1628          // Extract the "serp rank"
1629          // Typically http://google.com?s=keyphraz&start=18 returns 18
1630          if( !empty($search_engine_params[$ref_host][4]) )
1631          {
1632              $serp_param = $search_engine_params[$ref_host][4];
1633          }
1634          elseif( isset($url) && !empty($search_engine_params[$url][4]) )
1635          {
1636              $serp_param = $search_engine_params[$url][4];
1637          }
1638          else
1639          {    // Fallback to default params
1640              $serp_param = array('offset','page','start');
1641          }
1642  
1643          if( !is_array($serp_param) )
1644          {
1645              $serp_param = array($serp_param);
1646          }
1647  
1648          if( strpos($search_engine_name, 'Google') !== false )
1649          {    // Append fragment which Google uses in instant search
1650              $query .= '&'.$fragment;
1651          }
1652  
1653          foreach( $serp_param as $param )
1654          {
1655              if( $var = $this->get_param_from_string($query, $param) )
1656              {
1657                  if( ctype_digit($var) )
1658                  {
1659                      $serprank = $var;
1660                      break;
1661                  }
1662              }
1663          }
1664  
1665          $this->_search_engine = $search_engine_name;
1666          $this->_keyphrase = $key;
1667          $this->_serprank = isset($serprank) ? $serprank : NULL;
1668  
1669          return array(
1670                  'engine_name'    => $this->_search_engine,
1671                  'keyphrase'        => $this->_keyphrase,
1672                  'serprank'        => $this->_serprank
1673              );
1674      }
1675  
1676  
1677      /**
1678       * Is this Lynx?
1679       * @return boolean
1680       */
1681  	function is_lynx()
1682      {
1683          if( ! isset($this->is_lynx) )
1684              $this->detect_useragent();
1685          return $this->is_lynx;
1686      }
1687  
1688      /**
1689       * Is this Firefox?
1690       * @return boolean
1691       */
1692  	function is_firefox()
1693      {
1694          if( ! isset($this->is_firefox) )
1695              $this->detect_useragent();
1696          return $this->is_firefox;
1697      }
1698  
1699      /**
1700       * Is this Gecko?
1701       * @return boolean
1702       */
1703  	function is_gecko()
1704      {
1705          if( ! isset($this->is_gecko) )
1706              $this->detect_useragent();
1707          return $this->is_gecko;
1708      }
1709  
1710      /**
1711       * Is this WinIE?
1712       * @return boolean
1713       */
1714  	function is_winIE()
1715      {
1716          if( ! isset($this->is_winIE) )
1717              $this->detect_useragent();
1718          return $this->is_winIE;
1719      }
1720  
1721      /**
1722       * Is this MacIE?
1723       * @return boolean
1724       */
1725  	function is_macIE()
1726      {
1727          if( ! isset($this->is_macIE) )
1728              $this->detect_useragent();
1729          return $this->is_macIE;
1730      }
1731  
1732      /**
1733       * Is this Internet Explorer?
1734       * @return boolean
1735       */
1736  	function is_IE()
1737      {
1738          if( ! isset($this->is_IE) )
1739              $this->detect_useragent();
1740          return $this->is_IE;
1741      }
1742  
1743      /**
1744       * Is this Chrome?
1745       * @return boolean
1746       */
1747  	function is_chrome()
1748      {
1749          if( ! isset($this->is_chrome) )
1750              $this->detect_useragent();
1751          return $this->is_chrome;
1752      }
1753  
1754      /**
1755       * Is this Safari?
1756       * @return boolean
1757       */
1758  	function is_safari()
1759      {
1760          if( ! isset($this->is_safari) )
1761              $this->detect_useragent();
1762          return $this->is_safari;
1763      }
1764  
1765      /**
1766       * Is this Opera?
1767       * @return boolean
1768       */
1769  	function is_opera()
1770      {
1771          if( ! isset($this->is_opera) )
1772              $this->detect_useragent();
1773          return $this->is_opera;
1774      }
1775  
1776      /**
1777       * Is this Netscape4?
1778       * @return boolean
1779       */
1780  	function is_NS4()
1781      {
1782          if( ! isset($this->NS4) )
1783              $this->detect_useragent();
1784          return $this->is_NS4;
1785      }
1786  
1787  
1788      /**
1789       * Is this a browser hit?
1790       * @return boolean
1791       */
1792  	function is_browser()
1793      {
1794          if( ! isset($this->is_browser) )
1795              $this->is_browser = ($this->get_agent_type() == 'browser');
1796          return $this->is_browser;
1797      }
1798  
1799  
1800      /**
1801       * Is this a robot hit?
1802       * @return boolean
1803       */
1804  	function is_robot()
1805      {
1806          if( ! isset($this->is_robot) )
1807              $this->is_robot = ($this->get_agent_type() == 'robot');
1808          return $this->is_robot;
1809      }
1810  }
1811  
1812  ?>

title

Description

title

Description

title

Description

title

title

Body