xref: /plugin/statistics/admin.php (revision 75fa767de5b14be2623c6c33982b34f2eab2100e)
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&amp;page=statistics&amp;opt=&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$this->start.'">Dashboard</a>';
106        echo '</div></li>';
107
108        echo '<li><div class="li">';
109        echo '<a href="?do=admin&amp;page=statistics&amp;opt=page&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$this->start.'">Pages</a>';
110        echo '</div></li>';
111
112        echo '<li><div class="li">';
113        echo '<a href="?do=admin&amp;page=statistics&amp;opt=referer&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$this->start.'">Incoming Links</a>';
114        echo '</div></li>';
115
116        echo '<li><div class="li">';
117        echo '<a href="?do=admin&amp;page=statistics&amp;opt=browser&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$this->start.'">Browsers</a>';
118        echo '</div></li>';
119
120        echo '<li><div class="li">';
121        echo '<a href="?do=admin&amp;page=statistics&amp;opt=country&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;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&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$now.'&amp;t='.$now.'&amp;s='.$this->start.'">';
144        echo 'today';
145        echo '</a>';
146        echo '</li>';
147
148        echo '<li>';
149        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$yday.'&amp;t='.$yday.'&amp;s='.$this->start.'">';
150        echo 'yesterday';
151        echo '</a>';
152        echo '</li>';
153
154        echo '<li>';
155        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$week.'&amp;t='.$now.'&amp;s='.$this->start.'">';
156        echo 'last 7 days';
157        echo '</a>';
158        echo '</li>';
159
160        echo '<li>';
161        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$month.'&amp;t='.$now.'&amp;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&amp;f='.$this->from.'&amp;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&amp;f='.$this->from.'&amp;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&amp;f='.$this->from.'&amp;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).' &hellip; '.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