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