1<?php 2/** 3 * statistics plugin 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <gohr@cosmocode.de> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11 12if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 13require_once(DOKU_PLUGIN.'admin.php'); 14 15/** 16 * All DokuWiki plugins to extend the admin function 17 * need to inherit from this class 18 */ 19class admin_plugin_statistics extends DokuWiki_Admin_Plugin { 20 var $dblink = null; 21 var $opt = ''; 22 var $from = ''; 23 var $to = ''; 24 var $start = ''; 25 var $tlimit = ''; 26 27 /** 28 * return some info 29 */ 30 function getInfo(){ 31 return confToHash(dirname(__FILE__).'/info.txt'); 32 } 33 34 /** 35 * Access for managers allowed 36 */ 37 function forAdminOnly(){ 38 return false; 39 } 40 41 /** 42 * return sort order for position in admin menu 43 */ 44 function getMenuSort() { 45 return 150; 46 } 47 48 /** 49 * handle user request 50 */ 51 function handle() { 52 $this->opt = preg_replace('/[^a-z]+/','',$_REQUEST['opt']); 53 54 $this->start = (int) $_REQUEST['s']; 55 56 // fixme add better sanity checking here: 57 $this->from = preg_replace('/[^\d\-]+/','',$_REQUEST['f']); 58 $this->to = preg_replace('/[^\d\-]+/','',$_REQUEST['t']); 59 if(!$this->from) $this->from = date('Y-m-d'); 60 if(!$this->to) $this->to = date('Y-m-d'); 61 62 //setup limit clause 63 if($this->from != $this->to){ 64 $this->tlimit = "DATE(A.dt) >= DATE('".$this->from."') AND DATE(A.dt) <= DATE('".$this->to."')"; 65 }else{ 66 $this->tlimit = "DATE(A.dt) = DATE('".$this->from."')"; 67 } 68 } 69 70 /** 71 * fixme build statistics here 72 */ 73 function html() { 74 $this->html_toc(); 75 echo '<h1>Access Statistics</h1>'; 76 $this->html_timeselect(); 77 78 switch($this->opt){ 79 case 'country': 80 $this->html_country(); 81 break; 82 case 'page': 83 $this->html_page(); 84 break; 85 case 'browser': 86 $this->html_browser(); 87 break; 88 case 'referer': 89 $this->html_referer(); 90 break; 91 default: 92 $this->html_dashboard(); 93 } 94 } 95 96 function html_toc(){ 97 echo '<div class="toc">'; 98 echo '<div class="tocheader toctoggle" id="toc__header">'; 99 echo 'Detailed Statistics'; 100 echo '</div>'; 101 echo '<div id="toc__inside">'; 102 echo '<ul class="toc">'; 103 104 echo '<li><div class="li">'; 105 echo '<a href="?do=admin&page=statistics&opt=&f='.$this->from.'&t='.$this->to.'&s='.$this->start.'">Dashboard</a>'; 106 echo '</div></li>'; 107 108 echo '<li><div class="li">'; 109 echo '<a href="?do=admin&page=statistics&opt=page&f='.$this->from.'&t='.$this->to.'&s='.$this->start.'">Pages</a>'; 110 echo '</div></li>'; 111 112 echo '<li><div class="li">'; 113 echo '<a href="?do=admin&page=statistics&opt=referer&f='.$this->from.'&t='.$this->to.'&s='.$this->start.'">Incoming Links</a>'; 114 echo '</div></li>'; 115 116 echo '<li><div class="li">'; 117 echo '<a href="?do=admin&page=statistics&opt=browser&f='.$this->from.'&t='.$this->to.'&s='.$this->start.'">Browsers</a>'; 118 echo '</div></li>'; 119 120 echo '<li><div class="li">'; 121 echo '<a href="?do=admin&page=statistics&opt=country&f='.$this->from.'&t='.$this->to.'&s='.$this->start.'">Countries</a>'; 122 echo '</div></li>'; 123 124 echo '</ul>'; 125 echo '</div>'; 126 echo '</div>'; 127 } 128 129 /** 130 * Print the time selection menu 131 */ 132 function html_timeselect(){ 133 $now = date('Y-m-d'); 134 $yday = date('Y-m-d',time()-(60*60*24)); 135 $week = date('Y-m-d',time()-(60*60*24*7)); 136 $month = date('Y-m-d',time()-(60*60*24*30)); 137 138 echo '<div class="plg_stats_timeselect">'; 139 echo '<span>Select the timeframe:</span>'; 140 echo '<ul>'; 141 142 echo '<li>'; 143 echo '<a href="?do=admin&page=statistics&opt='.$this->opt.'&f='.$now.'&t='.$now.'&s='.$this->start.'">'; 144 echo 'today'; 145 echo '</a>'; 146 echo '</li>'; 147 148 echo '<li>'; 149 echo '<a href="?do=admin&page=statistics&opt='.$this->opt.'&f='.$yday.'&t='.$yday.'&s='.$this->start.'">'; 150 echo 'yesterday'; 151 echo '</a>'; 152 echo '</li>'; 153 154 echo '<li>'; 155 echo '<a href="?do=admin&page=statistics&opt='.$this->opt.'&f='.$week.'&t='.$now.'&s='.$this->start.'">'; 156 echo 'last 7 days'; 157 echo '</a>'; 158 echo '</li>'; 159 160 echo '<li>'; 161 echo '<a href="?do=admin&page=statistics&opt='.$this->opt.'&f='.$month.'&t='.$now.'&s='.$this->start.'">'; 162 echo 'last 30 days'; 163 echo '</a>'; 164 echo '</li>'; 165 166 echo '</ul>'; 167 168 169 echo '<form action="" method="get">'; 170 echo '<input type="hidden" name="do" value="admin" />'; 171 echo '<input type="hidden" name="page" value="statistics" />'; 172 echo '<input type="hidden" name="opt" value="'.$this->opt.'" />'; 173 echo '<input type="hidden" name="s" value="'.$this->start.'" />'; 174 echo '<input type="text" name="f" value="'.$this->from.'" class="edit" />'; 175 echo '<input type="text" name="t" value="'.$this->to.'" class="edit" />'; 176 echo '<input type="submit" value="go" class="button" />'; 177 echo '</form>'; 178 179 echo '</div>'; 180 } 181 182 183 /** 184 * Print an introductionary screen 185 */ 186 function html_dashboard(){ 187 echo '<p>This page gives you a quick overview on what is happening in your Wiki. For detailed lists 188 choose a topic from the list.</p>'; 189 190 191 echo '<div class="plg_stats_dashboard">'; 192 193 // general info 194 echo '<div class="plg_stats_top">'; 195 $result = $this->sql_aggregate($this->tlimit); 196 echo '<ul>'; 197 echo '<li><span>'.$result['pageviews'].'</span> page views</li>'; 198 echo '<li><span>'.$result['sessions'].'</span> visitors (sessions)</li>'; 199 echo '<li><span>'.$result['users'].'</span> logged in users</li>'; 200 201 echo '</ul>'; 202 echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/img.php?img=trend&f='.$this->from.'&t='.$this->to.'" />'; 203 echo '</div>'; 204 205 206 // top pages today 207 echo '<div>'; 208 echo '<h2>Most popular pages</h2>'; 209 $result = $this->sql_pages($this->tlimit,$this->start,15); 210 $this->html_resulttable($result); 211 echo '</div>'; 212 213 // top referer today 214 echo '<div>'; 215 echo '<h2>Top incoming links</h2>'; 216 $result = $this->sql_referer($this->tlimit,$this->start,15); 217 $this->html_resulttable($result); 218 echo '</div>'; 219 220 // top countries today 221 echo '<div>'; 222 echo '<h2>Visitor\'s top countries</h2>'; 223 echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/img.php?img=country&f='.$this->from.'&t='.$this->to.'" />'; 224// $result = $this->sql_countries($this->tlimit,$this->start,15); 225// $this->html_resulttable($result,array('','Countries','Count')); 226 echo '</div>'; 227 228 echo '</div>'; 229 } 230 231 function html_country(){ 232 echo '<div class="plg_stats_full">'; 233 echo '<h2>Visitor\'s Countries</h2>'; 234 $result = $this->sql_countries($this->tlimit,$this->start,150); 235 $this->html_resulttable($result); 236 echo '</div>'; 237 } 238 239 function html_page(){ 240 echo '<div class="plg_stats_full">'; 241 echo '<h2>Popular Pages</h2>'; 242 $result = $this->sql_pages($this->tlimit,$this->start,150); 243 $this->html_resulttable($result); 244 echo '</div>'; 245 } 246 247 function html_browser(){ 248 echo '<div class="plg_stats_full">'; 249 echo '<h2>Browser Shootout</h2>'; 250 251 echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/img.php?img=browser&f='.$this->from.'&t='.$this->to.'" />'; 252 253 $result = $this->sql_browsers($this->tlimit,$this->start,150,true); 254 $this->html_resulttable($result); 255 echo '</div>'; 256 } 257 258 function html_referer(){ 259 echo '<div class="plg_stats_full">'; 260 echo '<h2>Incoming Links</h2>'; 261 $result = $this->sql_aggregate($this->tlimit); 262 263 $all = $result['search']+$result['external']+$result['direct']; 264 265 printf("<p>Of all %d external visits, %d (%.1f%%) were bookmarked (direct) accesses, 266 %d (%.1f%%) came from search engines and %d (%.1f%%) were referred through 267 links from other pages.</p>",$all,$result['direct'],(100*$result['direct']/$all), 268 $result['search'],(100*$result['search']/$all),$result['external'], 269 (100*$result['external']/$all)); 270 271 $result = $this->sql_referer($this->tlimit,$this->start,150); 272 $this->html_resulttable($result); 273 echo '</div>'; 274 } 275 276 277 278 /** 279 * Display a result in a HTML table 280 */ 281 function html_resulttable($result,$header=''){ 282 echo '<table>'; 283 if(is_array($header)){ 284 echo '<tr>'; 285 foreach($header as $h){ 286 echo '<th>'.hsc($h).'</th>'; 287 } 288 echo '</tr>'; 289 } 290 291 foreach($result as $row){ 292 echo '<tr>'; 293 foreach($row as $k => $v){ 294 echo '<td class="plg_stats_X'.$k.'">'; 295 if($k == 'page'){ 296 echo '<a href="'.wl($v).'" class="wikilink1">'; 297 echo hsc($v); 298 echo '</a>'; 299 }elseif($k == 'url'){ 300 $url = hsc($v); 301 if(strlen($url) > 45){ 302 $url = substr($url,0,30).' … '.substr($url,-15); 303 } 304 echo '<a href="'.$v.'" class="urlextern">'; 305 echo $url; 306 echo '</a>'; 307 }elseif($k == 'browser'){ 308 include_once(dirname(__FILE__).'/inc/browsers.php'); 309 echo $BrowsersHashIDLib[$v]; 310 }elseif($k == 'bflag'){ 311 include_once(dirname(__FILE__).'/inc/browsers.php'); 312 echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/ico/browser/'.$BrowsersHashIcon[$v].'.png" alt="'.hsc($v).'" />'; 313 }elseif($k == 'cflag'){ 314 echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/ico/flags/'.hsc($v).'.png" alt="'.hsc($v).'" width="18" height="12" />'; 315 }elseif($k == 'html'){ 316 echo $v; 317 }else{ 318 echo hsc($v); 319 } 320 echo '</td>'; 321 } 322 echo '</tr>'; 323 } 324 echo '</table>'; 325 } 326 327 /** 328 * Create an image 329 */ 330 function img_build($img){ 331 include(dirname(__FILE__).'/inc/AGC.class.php'); 332 333 switch($img){ 334 case 'country': 335 // build top countries + other 336 $result = $this->sql_countries($this->tlimit,$this->start,0); 337 $data = array(); 338 $top = 0; 339 foreach($result as $row){ 340 if($top < 7){ 341 $data[$row['country']] = $row['cnt']; 342 }else{ 343 $data['other'] += $row['cnt']; 344 } 345 $top++; 346 } 347 $pie = new AGC(300, 200); 348 $pie->setProp("showkey",true); 349 $pie->setProp("showval",false); 350 $pie->setProp("showgrid",false); 351 $pie->setProp("type","pie"); 352 $pie->setProp("keyinfo",1); 353 $pie->setProp("keysize",8); 354 $pie->setProp("keywidspc",-50); 355 $pie->setProp("key",array_keys($data)); 356 $pie->addBulkPoints(array_values($data)); 357 @$pie->graph(); 358 $pie->showGraph(); 359 break; 360 case 'browser': 361 // build top browsers + other 362 include_once(dirname(__FILE__).'/inc/browsers.php'); 363 364 $result = $this->sql_browsers($this->tlimit,$this->start,0,false); 365 $data = array(); 366 $top = 0; 367 foreach($result as $row){ 368 if($top < 5){ 369 $data[strip_tags($BrowsersHashIDLib[$row['ua_info']])] = $row['cnt']; 370 }else{ 371 $data['other'] += $row['cnt']; 372 } 373 $top++; 374 } 375 $pie = new AGC(300, 200); 376 $pie->setProp("showkey",true); 377 $pie->setProp("showval",false); 378 $pie->setProp("showgrid",false); 379 $pie->setProp("type","pie"); 380 $pie->setProp("keyinfo",1); 381 $pie->setProp("keysize",8); 382 $pie->setProp("keywidspc",-50); 383 $pie->setProp("key",array_keys($data)); 384 $pie->addBulkPoints(array_values($data)); 385 @$pie->graph(); 386 $pie->showGraph(); 387 break; 388 case 'trend': 389 $hours = ($this->from == $this->to); 390 $result = $this->sql_trend($this->tlimit,$hours); 391 $data1 = array(); 392 $data2 = array(); 393 394 $graph = new AGC(400, 150); 395 $graph->setProp("type","bar"); 396 $graph->setProp("showgrid",false); 397 $graph->setProp("barwidth",.8); 398 399 $graph->setColor('color',0,'blue'); 400 $graph->setColor('color',1,'red'); 401 402 if($hours){ 403 //preset $hours 404 for($i=0;$i<24;$i++){ 405 $data1[$i] = 0; 406 $data2[$i] = 0; 407 $graph->setProp("scale",array(' 0h',' 4h',' 8h',' 12h',' 16h',' 20h',' 24h')); 408 } 409 }else{ 410 $graph->setProp("scale",array(next(array_keys($data1)),$this->to)); 411 } 412 413 foreach($result as $row){ 414 $data1[$row['time']] = $row['pageviews']; 415 $data2[$row['time']] = $row['sessions']; 416 } 417 418 foreach($data1 as $key => $val){ 419 $graph->addPoint($val,$key,0); 420 } 421 foreach($data2 as $key => $val){ 422 $graph->addPoint($val,$key,1); 423 } 424 425 @$graph->graph(); 426 $graph->showGraph(); 427 428 default: 429 $this->sendGIF(); 430 } 431 } 432 433 434 /** 435 * Return some aggregated statistics 436 */ 437 function sql_aggregate($tlimit){ 438 $data = array(); 439 440 $sql = "SELECT ref_type, COUNT(*) as cnt 441 FROM ".$this->getConf('db_prefix')."access as A 442 WHERE $tlimit 443 AND ua_type = 'browser' 444 GROUP BY ref_type"; 445 $result = $this->runSQL($sql); 446 447 foreach($result as $row){ 448 if($row['ref_type'] == 'search') $data['search'] = $row['cnt']; 449 if($row['ref_type'] == 'external') $data['external'] = $row['cnt']; 450 if($row['ref_type'] == 'internal') $data['internal'] = $row['cnt']; 451 if($row['ref_type'] == '') $data['direct'] = $row['cnt']; 452 } 453 454 $sql = "SELECT COUNT(DISTINCT session) as sessions, 455 COUNT(session) as views, 456 COUNT(DISTINCT user) as users 457 FROM ".$this->getConf('db_prefix')."access as A 458 WHERE $tlimit 459 AND ua_type = 'browser'"; 460 $result = $this->runSQL($sql); 461 462 $data['users'] = max($result[0]['users'] - 1,0); // subtract empty user 463 $data['sessions'] = $result[0]['sessions']; 464 $data['pageviews'] = $result[0]['views']; 465 466 $sql = "SELECT COUNT(id) as robots 467 FROM ".$this->getConf('db_prefix')."access as A 468 WHERE $tlimit 469 AND ua_type = 'robot'"; 470 $result = $this->runSQL($sql); 471 $data['robots'] = $result[0]['robots']; 472 473 return $data; 474 } 475 476 function sql_trend($tlimit,$hours=false){ 477 if($hours){ 478 $sql = "SELECT HOUR(dt) as time, 479 COUNT(DISTINCT session) as sessions, 480 COUNT(session) as pageviews 481 FROM ".$this->getConf('db_prefix')."access as A 482 WHERE $tlimit 483 AND ua_type = 'browser' 484 GROUP BY HOUR(dt) 485 ORDER BY time"; 486 }else{ 487 $sql = "SELECT DATE(dt) as time, 488 COUNT(DISTINCT session) as sessions, 489 COUNT(session) as pageviews 490 FROM ".$this->getConf('db_prefix')."access as A 491 WHERE $tlimit 492 AND ua_type = 'browser' 493 GROUP BY DATE(dt) 494 ORDER BY time"; 495 } 496 return $this->runSQL($sql); 497 } 498 499 function sql_pages($tlimit,$start=0,$limit=20){ 500 $sql = "SELECT COUNT(*) as cnt, page 501 FROM ".$this->getConf('db_prefix')."access as A 502 WHERE $tlimit 503 AND ua_type = 'browser' 504 GROUP BY page 505 ORDER BY cnt DESC, page". 506 $this->sql_limit($start,$limit); 507 return $this->runSQL($sql); 508 } 509 510 function sql_referer($tlimit,$start=0,$limit=20){ 511 $sql = "SELECT COUNT(*) as cnt, ref as url 512 FROM ".$this->getConf('db_prefix')."access as A 513 WHERE $tlimit 514 AND ua_type = 'browser' 515 AND ref_type = 'external' 516 GROUP BY ref_md5 517 ORDER BY cnt DESC, url". 518 $this->sql_limit($start,$limit); 519 return $this->runSQL($sql); 520 } 521 522 function sql_countries($tlimit,$start=0,$limit=20){ 523 $sql = "SELECT COUNT(*) as cnt, B.code AS cflag, B.country 524 FROM ".$this->getConf('db_prefix')."access as A, 525 ".$this->getConf('db_prefix')."iplocation as B 526 WHERE $tlimit 527 AND A.ip = B.ip 528 GROUP BY B.country 529 ORDER BY cnt DESC, B.country". 530 $this->sql_limit($start,$limit); 531 return $this->runSQL($sql); 532 } 533 534 function sql_browsers($tlimit,$start=0,$limit=20,$ext=true){ 535 if($ext){ 536 $sel = 'ua_info as bflag, ua_info as browser, ua_ver'; 537 $grp = 'ua_info, ua_ver'; 538 }else{ 539 $grp = 'ua_info'; 540 $sel = 'ua_info'; 541 } 542 543 $sql = "SELECT COUNT(*) as cnt, $sel 544 FROM ".$this->getConf('db_prefix')."access as A 545 WHERE $tlimit 546 AND ua_type = 'browser' 547 GROUP BY $grp 548 ORDER BY cnt DESC, ua_info". 549 $this->sql_limit($start,$limit); 550 return $this->runSQL($sql); 551 } 552 553 554 /** 555 * Builds a limit clause 556 */ 557 function sql_limit($start,$limit){ 558 $start = (int) $start; 559 $limit = (int) $limit; 560 if($limit){ 561 return " LIMIT $start,$limit"; 562 }elseif($start){ 563 return " OFFSET $start"; 564 } 565 return ''; 566 } 567 568 /** 569 * Return a link to the DB, opening the connection if needed 570 */ 571 function dbLink(){ 572 // connect to DB if needed 573 if(!$this->dblink){ 574 $this->dblink = mysql_connect($this->getConf('db_server'), 575 $this->getConf('db_user'), 576 $this->getConf('db_password')); 577 if(!$this->dblink){ 578 msg('DB Error: connection failed',-1); 579 return null; 580 } 581 // set utf-8 582 if(!mysql_db_query($this->getConf('db_database'),'set names utf8',$this->dblink)){ 583 msg('DB Error: could not set UTF-8 ('.mysql_error($this->dblink).')',-1); 584 return null; 585 } 586 } 587 return $this->dblink; 588 } 589 590 /** 591 * Simple function to run a DB query 592 */ 593 function runSQL($sql_string) { 594 $link = $this->dbLink(); 595 596 $result = mysql_db_query($this->conf['db_database'],$sql_string,$link); 597 if(!$result){ 598 msg('DB Error: '.mysql_error($link).' '.hsc($sql_string),-1); 599 return null; 600 } 601 602 $resultarray = array(); 603 604 //mysql_db_query returns 1 on a insert statement -> no need to ask for results 605 if ($result != 1) { 606 for($i=0; $i< mysql_num_rows($result); $i++) { 607 $temparray = mysql_fetch_assoc($result); 608 $resultarray[]=$temparray; 609 } 610 mysql_free_result($result); 611 } 612 613 if (mysql_insert_id($link)) { 614 $resultarray = mysql_insert_id($link); //give back ID on insert 615 } 616 617 return $resultarray; 618 } 619 620 /** 621 * Returns a short name for a User Agent and sets type, version and os info 622 */ 623 function ua_info($ua,&$type,&$ver,&$os){ 624 $ua = strtr($ua,' +','__'); 625 $ua = strtolower($ua); 626 627 // common browsers 628 $regvermsie = '/msie([+_ ]|)([\d\.]*)/i'; 629 $regvernetscape = '/netscape.?\/([\d\.]*)/i'; 630 $regverfirefox = '/firefox\/([\d\.]*)/i'; 631 $regversvn = '/svn\/([\d\.]*)/i'; 632 $regvermozilla = '/mozilla(\/|)([\d\.]*)/i'; 633 $regnotie = '/webtv|omniweb|opera/i'; 634 $regnotnetscape = '/gecko|compatible|opera|galeon|safari/i'; 635 636 $name = ''; 637 # IE ? 638 if(preg_match($regvermsie,$ua,$m) && !preg_match($regnotie,$ua)){ 639 $type = 'browser'; 640 $ver = $m[2]; 641 $name = 'msie'; 642 } 643 # Firefox ? 644 elseif (preg_match($regverfirefox,$ua,$m)){ 645 $type = 'browser'; 646 $ver = $m[1]; 647 $name = 'firefox'; 648 } 649 # Subversion ? 650 elseif (preg_match($regversvn,$ua,$m)){ 651 $type = 'rcs'; 652 $ver = $m[1]; 653 $name = 'svn'; 654 } 655 # Netscape 6.x, 7.x ... ? 656 elseif (preg_match($regvernetscape,$ua,$m)){ 657 $type = 'browser'; 658 $ver = $m[1]; 659 $name = 'netscape'; 660 } 661 # Netscape 3.x, 4.x ... ? 662 elseif(preg_match($regvermozilla,$ua,$m) && !preg_match($regnotnetscape,$ua)){ 663 $type = 'browser'; 664 $ver = $m[2]; 665 $name = 'netscape'; 666 }else{ 667 include(dirname(__FILE__).'/inc/browsers.php'); 668 foreach($BrowsersSearchIDOrder as $regex){ 669 if(preg_match('/'.$regex.'/',$ua)){ 670 // it's a browser! 671 $type = 'browser'; 672 $name = strtolower($regex); 673 break; 674 } 675 } 676 } 677 678 // check versions for Safari and Opera 679 if($name == 'safari'){ 680 if(preg_match('/safari\/([\d\.]*)/i',$ua,$match)){ 681 $ver = $BrowsersSafariBuildToVersionHash[$match[1]]; 682 } 683 }elseif($name == 'opera'){ 684 if(preg_match('/opera[\/ ]([\d\.]*)/i',$ua,$match)){ 685 $ver = $match[1]; 686 } 687 } 688 689 690 // check OS for browsers 691 if($type == 'browser'){ 692 include(dirname(__FILE__).'/inc/operating_systems.php'); 693 foreach($OSSearchIDOrder as $regex){ 694 if(preg_match('/'.$regex.'/',$ua)){ 695 $os = $OSHashID[$regex]; 696 break; 697 } 698 } 699 700 } 701 702 // are we done now? 703 if($name) return $name; 704 705 include(dirname(__FILE__).'/inc/robots.php'); 706 foreach($RobotsSearchIDOrder as $regex){ 707 if(preg_match('/'.$regex.'/',$ua)){ 708 // it's a robot! 709 $type = 'robot'; 710 return strtolower($regex); 711 } 712 } 713 714 // dunno 715 return ''; 716 } 717 718 /** 719 * 720 * @fixme: put search engine queries in seperate table here 721 */ 722 function log_search($referer,&$type){ 723 $referer = strtr($referer,' +','__'); 724 $referer = strtolower($referer); 725 726 include(dirname(__FILE__).'/inc/search_engines.php'); 727 728 foreach($SearchEnginesSearchIDOrder as $regex){ 729 if(preg_match('/'.$regex.'/',$referer)){ 730 if(!$NotSearchEnginesKeys[$regex] || 731 !preg_match('/'.$NotSearchEnginesKeys[$regex].'/',$referer)){ 732 // it's a search engine! 733 $type = 'search'; 734 break; 735 } 736 } 737 } 738 if($type != 'search') return; // we're done here 739 740 #fixme now do the keyword magic! 741 } 742 743 /** 744 * Resolve IP to country/city 745 */ 746 function log_ip($ip){ 747 // check if IP already known and up-to-date 748 $sql = "SELECT ip 749 FROM ".$this->getConf('db_prefix')."iplocation 750 WHERE ip ='".addslashes($ip)."' 751 AND lastupd > DATE_SUB(CURDATE(),INTERVAL 30 DAY)"; 752 $result = $this->runSQL($sql); 753 if($result[0]['ip']) return; 754 755 $http = new DokuHTTPClient(); 756 $http->timeout = 10; 757 $data = $http->get('http://api.hostip.info/get_html.php?ip='.$ip); 758 759 if(preg_match('/^Country: (.*?) \((.*?)\)\nCity: (.*?)$/s',$data,$match)){ 760 $country = addslashes(trim($match[1])); 761 $code = addslashes(strtolower(trim($match[2]))); 762 $city = addslashes(trim($match[3])); 763 $host = addslashes(gethostbyaddr($ip)); 764 $ip = addslashes($ip); 765 766 $sql = "REPLACE INTO ".$this->getConf('db_prefix')."iplocation 767 SET ip = '$ip', 768 country = '$country', 769 code = '$code', 770 city = '$city', 771 host = '$host'"; 772 $this->runSQL($sql); 773 } 774 } 775 776 /** 777 * log a page access 778 * 779 * called from log.php 780 */ 781 function log_access(){ 782 if(!$_REQUEST['p']) return; 783 784 # FIXME check referer against blacklist and drop logging for bad boys 785 786 // handle referer 787 $referer = trim($_REQUEST['r']); 788 if($referer){ 789 $ref = addslashes($referer); 790 $ref_md5 = ($ref) ? md5($referer) : ''; 791 if(strpos($referer,DOKU_URL) === 0){ 792 $ref_type = 'internal'; 793 }else{ 794 $ref_type = 'external'; 795 $this->log_search($referer,$ref_type); 796 } 797 }else{ 798 $ref = ''; 799 $ref_md5 = ''; 800 $ref_type = ''; 801 } 802 803 // handle user agent 804 $agent = trim($_SERVER['HTTP_USER_AGENT']); 805 806 $ua = addslashes($agent); 807 $ua_type = ''; 808 $ua_ver = ''; 809 $os = ''; 810 $ua_info = addslashes($this->ua_info($agent,$ua_type,$ua_ver,$os)); 811 812 $page = addslashes($_REQUEST['p']); 813 $ip = addslashes($_SERVER['REMOTE_ADDR']); 814 $sx = (int) $_REQUEST['sx']; 815 $sy = (int) $_REQUEST['sy']; 816 $vx = (int) $_REQUEST['vx']; 817 $vy = (int) $_REQUEST['vy']; 818 $js = (int) $_REQUEST['js']; 819 $user = addslashes($_SERVER['REMOTE_USER']); 820 $session = addslashes(session_id()); 821 822 $sql = "INSERT DELAYED INTO ".$this->getConf('db_prefix')."access 823 SET dt = NOW(), 824 page = '$page', 825 ip = '$ip', 826 ua = '$ua', 827 ua_info = '$ua_info', 828 ua_type = '$ua_type', 829 ua_ver = '$ua_ver', 830 os = '$os', 831 ref = '$ref', 832 ref_md5 = '$ref_md5', 833 ref_type = '$ref_type', 834 screen_x = '$sx', 835 screen_y = '$sy', 836 view_x = '$vx', 837 view_y = '$vy', 838 js = '$js', 839 user = '$user', 840 session = '$session'"; 841 $ok = $this->runSQL($sql); 842 if(is_null($ok)){ 843 global $MSG; 844 print_r($MSG); 845 } 846 847 // resolve the IP 848 $this->log_ip($_SERVER['REMOTE_ADDR']); 849 } 850 851 /** 852 * Just send a 1x1 pixel blank gif to the browser 853 * 854 * @called from log.php 855 * 856 * @author Andreas Gohr <andi@splitbrain.org> 857 * @author Harry Fuecks <fuecks@gmail.com> 858 */ 859 function sendGIF(){ 860 $img = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7'); 861 header('Content-Type: image/gif'); 862 header('Content-Length: '.strlen($img)); 863 header('Connection: Close'); 864 print $img; 865 flush(); 866 // Browser should drop connection after this 867 // Thinks it's got the whole image 868 } 869 870} 871