xref: /plugin/statistics/admin.php (revision bd4217d37e9e3e2daab36e6859def41cb66a20b7)
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&amp;page=statistics&amp;opt=&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$this->start.'">Dashboard</a>';
109        echo '</div></li>';
110
111        echo '<li><div class="li">';
112        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>';
113        echo '</div></li>';
114
115        echo '<li><div class="li">';
116        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>';
117        echo '</div></li>';
118
119        echo '<li><div class="li">';
120        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>';
121        echo '</div></li>';
122
123        echo '<li><div class="li">';
124        echo '<a href="?do=admin&amp;page=statistics&amp;opt=os&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$this->start.'">Operating Systems</a>';
125        echo '</div></li>';
126
127        echo '<li><div class="li">';
128        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>';
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&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$now.'&amp;t='.$now.'&amp;s='.$this->start.'">';
151        echo 'today';
152        echo '</a>';
153        echo '</li>';
154
155        echo '<li>';
156        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$yday.'&amp;t='.$yday.'&amp;s='.$this->start.'">';
157        echo 'yesterday';
158        echo '</a>';
159        echo '</li>';
160
161        echo '<li>';
162        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$week.'&amp;t='.$now.'&amp;s='.$this->start.'">';
163        echo 'last 7 days';
164        echo '</a>';
165        echo '</li>';
166
167        echo '<li>';
168        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$month.'&amp;t='.$now.'&amp;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&amp;f='.$this->from.'&amp;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&amp;f='.$this->from.'&amp;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&amp;f='.$this->from.'&amp;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&amp;f='.$this->from.'&amp;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&amp;f='.$this->from.'&amp;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).' &hellip; '.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