1<?php
2/**
3 * Access Counter and Popularity Plugin -- Access Counter
4 *
5 * Original source of this plugin is PukiWiki.
6 *
7 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
8 * @author  HokkaidoPerson <dosankomali@yahoo.co.jp>
9 */
10
11// Original Licenses of this plugin:
12//
13// PukiWiki - Yet another WikiWikiWeb clone
14// $Id: counter.inc.php,v 1.19 2007/02/04 11:14:44 henoheno Exp $
15// Copyright (C)
16//   2002-2005, 2007 PukiWiki Developers Team
17//   2002 Y.MASUI GPL2 http://masui.net/pukiwiki/ masui@masui.net
18// License: GPL2
19//
20// Counter plugin (per page)
21
22// must be run within Dokuwiki
23if(!defined('DOKU_INC')) die();
24
25class syntax_plugin_accscounter_counter extends DokuWiki_Syntax_Plugin {
26
27    function getType(){
28        return 'substition';
29    }
30
31    function getSort(){
32        return 200;
33    }
34
35
36        // Internal function
37        // Return a summary
38        function plugin_counter_get_count($page)
39        {
40            global $ACT;
41            global $USERINFO;
42            global $conf;
43            static $counters = array();
44            static $default;
45
46            $achelper = plugin_load('helper','accscounter');
47            $clientIP = clientIP(true);
48
49            if (! isset($default))
50                $default = array(
51                    'total'     => 0,
52                    'date'      => date('Y/m/d'),
53                    'today'     => 0,
54                    'yesterday' => 0,
55                    'ip'        => '');
56
57            if (page_exists($page) == FALSE) return $default;
58            if (isset($counters[$page])) return $counters[$page];
59
60            // Set default
61            $counters[$page] = $default;
62            $modify = FALSE;
63
64            // Load and handle the exclusion list (IPs and remote hosts)
65            $exlist = str_replace(array("\r\n", "\r", "\n"), "\n", $this->getConf('exclusionList'));
66            $exlist = preg_quote($exlist, '/');
67            $exlist = str_replace('\*', '[0-9A-Za-z.-]+', $exlist);
68            $exlist = str_replace('\?', '[0-9A-Za-z.-]', $exlist);
69            $exlist = str_replace('~', '[0-9]+', $exlist);
70            $exlist = str_replace('\!', '[0-9]', $exlist);
71            $exlist = explode("\n", $exlist);
72
73            $remotehost = gethostbyaddr($clientIP);
74            $excluded = FALSE;
75            foreach ($exlist as $checking) {
76                $prefix = '/^' . $checking . '$/';
77                if (preg_match($prefix, $clientIP)) $excluded = TRUE;
78                if (preg_match($prefix, $remotehost)) $excluded = TRUE;
79            }
80
81            // Check about the country
82            $countries = explode(',', $this->getConf('cntrExclusion'));
83            $countries = array_map('trim', $countries);
84            $countries = array_unique($countries);
85            $countries = array_filter($countries);
86
87            $countriesin = explode(',', $this->getConf('cntrInclusion'));
88            $countriesin = array_map('trim', $countriesin);
89            $countriesin = array_unique($countriesin);
90            $countriesin = array_filter($countriesin);
91
92            // Get a country code related to the user
93            // Ingredients to generate a DNS address
94            $ingr = explode(".", $clientIP);
95            // Compose a "cc.wariate.jp" DNS address
96            $dnsaddr = $ingr[3] . "." . $ingr[2] . "." . $ingr[1] . "." . $ingr[0] . ".cc.wariate.jp";
97
98            // Investigate now
99            $dnsdatas = dns_get_record($dnsaddr, DNS_TXT);
100            if ($dnsdatas !== FALSE) $hiscountry = $dnsdatas[0]["txt"]; else $hiscountry = FALSE;
101
102            // Check now
103            if ($hiscountry !== FALSE) {
104                $hiscountry = utf8_strtolower($hiscountry);
105                foreach ($countries as $checking) {
106                    $checkinglower = utf8_strtolower($checking);
107                    if ($checkinglower == $hiscountry) $excluded = TRUE;
108                }
109                if ($this->getConf('cntrInclusion') != '') {
110                    $included = FALSE;
111                    foreach ($countriesin as $checking) {
112                        $checkinglower = utf8_strtolower($checking);
113                        if ($checkinglower == $hiscountry) $included = TRUE;
114                    }
115                    if ($included != TRUE) $excluded = TRUE;
116                }
117            }
118
119            // Check about IPs reverse lookup
120            if ($this->getConf('reverseLookupFailed') == '1' && $remotehost == $clientIP) {
121                $rexlist = str_replace(array("\r\n", "\r", "\n"), "\n", $this->getConf('reverseLookupException'));
122                $rexlist = preg_quote($rexlist, '/');
123                $rexlist = str_replace('\*', '[0-9]+', $rexlist);
124                $rexlist = str_replace('\?', '[0-9]', $rexlist);
125                $rexlist = explode("\n", $rexlist);
126
127                $cntrexlist = explode(',', $this->getConf('reverseLookupCntrException'));
128                $cntrexlist = array_map('trim', $cntrexlist);
129                $cntrexlist = array_unique($cntrexlist);
130                $cntrexlist = array_filter($cntrexlist);
131
132
133                $excluded = TRUE;
134                foreach ($rexlist as $checking) {
135                    $prefix = '/^' . $checking . '$/';
136                    if (preg_match($prefix, $clientIP)) $excluded = FALSE;
137                }
138
139                if ($hiscountry !== FALSE) {
140                    foreach ($cntrexlist as $checking) {
141                        $checkinglower = utf8_strtolower($checking);
142                        if ($checkinglower == $hiscountry) $excluded = FALSE;
143                    }
144                }
145
146            }
147
148            // Exclude managers and superusers?
149            if ($this->getConf('excludeMgAndSp') == 'mg' && auth_ismanager()) $excluded = TRUE;
150            if ($this->getConf('excludeMgAndSp') == 'sp' && auth_isadmin()) $excluded = TRUE;
151
152            // Check about a list of users and user groups
153            if(auth_isMember($this->getConf('usrExclusion'), $_SERVER['REMOTE_USER'], (array) $USERINFO['grps'])) $excluded = TRUE;
154
155            // Check if the user is a spammer
156            $ishespam = FALSE;
157            $sfspluginok = FALSE;
158            if (! plugin_isdisabled('stopforumspam2')) {
159                if ($helper = plugin_load('helper','stopforumspam2')) {
160                    $sfspluginok = TRUE;
161                    if ($helper->quickipcheck(null, $this->getConf('sfsExFreq'), $this->getConf('sfsExConf'))) {
162                        $ishespam = TRUE;
163                        $excluded = TRUE;
164                    }
165                }
166            }
167
168
169            // Open
170            $file = $achelper->counterFN($page, '.number');
171            if (file_exists($file)) {
172                $fp = io_readFile($file);
173                if ($fp === FALSE) return $this->getLang('err1') . basename($file);
174                $fp = explode("\n", $fp);
175            } else $fp = array();
176
177            // Read
178            if ($fp[0]) $counters[$page]['total'] = $fp[0];
179            if ($fp[1]) $counters[$page]['date'] = $fp[1];
180            if ($fp[2]) $counters[$page]['today'] = $fp[2];
181            if ($fp[3]) $counters[$page]['yesterday'] = $fp[3];
182            if ($fp[4]) $counters[$page]['ip'] = $fp[4];
183
184            // Anothoer day?
185            if ($counters[$page]['date'] != $default['date']) {
186                $modify = TRUE;
187                $is_yesterday = ($counters[$page]['date'] == date('Y/m/d', CURRENT - 24 * 60 * 60));
188                $counters[$page]['ip'] = $clientIP;
189                $counters[$page]['date']      = $default['date'];
190                $counters[$page]['yesterday'] = $is_yesterday ? $counters[$page]['today'] : 0;
191                // Excluded?
192                if ($excluded == FALSE) {
193                    $counters[$page]['today']     = 1;
194                    $counters[$page]['total']++;
195
196                    // Save the log?
197                    switch ($this->getConf('saveLog')) {
198                    case 'pdate' :
199                        $filepageid = str_replace(':','/',$page);
200                        $logfiledir = $conf['cachedir'] . '/accscounterlog/' . utf8_encodeFN($filepageid) . '/';
201                        if (!file_exists($logfiledir)) mkdir($logfiledir, 0777, true);
202                        $logfilename = $logfiledir . utf8_strtolower(date('M-d-Y')) . '.txt';
203                        if ($loghandle = fopen($logfilename, 'a')) {
204                            $logcontent = $clientIP ."(" . date('H:i:s M d, Y') . ")\n";
205                            fwrite($loghandle, $logcontent);
206                            fclose($loghandle);
207                        }
208                        break;
209
210                    case 'ppage' :
211                        $filepageid = str_replace(':','/',$page);
212                        $logfiledir = $conf['cachedir'] . '/accscounterlog/' . utf8_encodeFN($filepageid) . '/';
213                        if (!file_exists($logfiledir)) mkdir($logfiledir, 0777, true);
214                        $logfilename = $logfiledir . 'wholeperiod.txt';
215                        if ($loghandle = fopen($logfilename, 'a')) {
216                            $logcontent = $clientIP ."(" . date('H:i:s M d, Y') . ")\n";
217                            fwrite($loghandle, $logcontent);
218                            fclose($loghandle);
219                        }
220                        break;
221                    }
222                    if (file_exists($achelper->counterFN($page, '.ip'))) {
223                        $ipdata = @file($achelper->counterFN($page, '.ip'));
224                        if ($ipdata != FALSE) {
225                            $inthelog = FALSE;
226                            $newcontents = array();
227                            foreach ($ipdata as $dataline) {
228                                $element = explode('|', $dataline);
229                                $element[0] = trim($element[0]);
230                                $element[1] = trim($element[1]);
231                                if ($clientIP == $element[0]) {
232                                    $inthelog = TRUE;
233                                    $element[1]++;
234                                }
235                                $newcontents[] = $element[0] . '|' . $element[1];
236                            }
237                            if (!$inthelog) $newcontents[] = $clientIP . '|1';
238                            $writing = '';
239                            foreach ($newcontents as $part) {
240                                $writing .= $part . "\n";
241                            }
242                            io_saveFile($achelper->counterFN($page, '.ip'), $writing);
243                        }
244                    } else io_saveFile($achelper->counterFN($page, '.ip'), $clientIP . "|1\n");
245                } else {
246                    $counters[$page]['today']     = 0;
247                }
248            } else if ($counters[$page]['ip'] != $clientIP) {
249                // Not the same host
250                if ($excluded == FALSE) {
251                    $modify = TRUE;
252                    $counters[$page]['ip']        = $clientIP;
253                    $counters[$page]['today']++;
254                    $counters[$page]['total']++;
255
256                    // Save the log?
257                    switch ($this->getConf('saveLog')) {
258                    case 'pdate' :
259                        $filepageid = str_replace(':','/',$page);
260                        $logfiledir = $conf['cachedir'] . '/accscounterlog/' . utf8_encodeFN($filepageid) . '/';
261                        if (!file_exists($logfiledir)) mkdir($logfiledir, 0777, true);
262                        $logfilename = $logfiledir . utf8_strtolower(date('M-d-Y')) . '.txt';
263                        if ($loghandle = fopen($logfilename, 'a')) {
264                            $logcontent = $clientIP ."(" . date('H:i:s M d, Y') . ")\n";
265                            fwrite($loghandle, $logcontent);
266                            fclose($loghandle);
267                        }
268                        break;
269
270                    case 'ppage' :
271                        $filepageid = str_replace(':','/',$page);
272                        $logfiledir = $conf['cachedir'] . '/accscounterlog/' . utf8_encodeFN($filepageid) . '/';
273                        if (!file_exists($logfiledir)) mkdir($logfiledir, 0777, true);
274                        $logfilename = $logfiledir . 'wholeperiod.txt';
275                        if ($loghandle = fopen($logfilename, 'a')) {
276                            $logcontent = $clientIP ."(" . date('H:i:s M d, Y') . ")\n";
277                            fwrite($loghandle, $logcontent);
278                            fclose($loghandle);
279                        }
280                        break;
281                    }
282                    if (file_exists($achelper->counterFN($page, '.ip'))) {
283                        $ipdata = @file($achelper->counterFN($page, '.ip'));
284                        if ($ipdata != FALSE) {
285                            $inthelog = FALSE;
286                            $newcontents = array();
287                            foreach ($ipdata as $dataline) {
288                                $element = explode('|', $dataline);
289                                $element[0] = trim($element[0]);
290                                $element[1] = trim($element[1]);
291                                if ($clientIP == $element[0]) {
292                                    $inthelog = TRUE;
293                                    $element[1]++;
294                                }
295                                $newcontents[] = $element[0] . '|' . $element[1];
296                            }
297                            if (!$inthelog) $newcontents[] = $clientIP . '|1';
298                            $writing = '';
299                            foreach ($newcontents as $part) {
300                                $writing .= $part . "\n";
301                            }
302                            io_saveFile($achelper->counterFN($page, '.ip'), $writing);
303                        }
304                    } else io_saveFile($achelper->counterFN($page, '.ip'), $clientIP . "|1\n");
305                }
306            }
307
308            // Modify
309            if ($ACT == '' or $ACT == 'show') $showing = TRUE;
310
311            if ($modify && $showing == TRUE) {
312                $savedata = '';
313                foreach (array_keys($default) as $key)
314                    $savedata .= $counters[$page][$key] . "\n";
315                io_saveFile($file, $savedata);
316            }
317
318            return $counters[$page];
319        }
320
321
322    //Syntax: {{counter|total(default), today, or yesterday|texts following the number (when it is 0 or 1)|texts following the number (when it is 2 or more)}}
323    //The texts following the number are not required (If entered just {{counter|today or yesterday or total}} or {{counter}}, this will return only the number)
324
325    function connectTo($mode) {
326      $this->Lexer->addSpecialPattern('\{\{counter[^}]*\}\}',$mode,'plugin_accscounter_counter');
327    }
328
329
330    function handle($match, $state, $pos, Doku_Handler $handler){
331
332        return explode('|', substr($match, strlen('{{counter|'), -2));
333
334    }
335
336    function render($mode, Doku_Renderer $renderer, $data) {
337        global $INFO;
338
339        // Get the time zone from conf (if null, it will use the default setting on your server)
340        if ($this->getConf('timezone') != '')  date_default_timezone_set($this->getConf('timezone'));
341
342        // Get current time (local)
343        define('CURRENT', time());
344
345
346        // Main process
347
348        switch ($data[0]) {
349        case ''     :
350        case 'total': $arg = 'total';
351            break;
352        case 'today': $arg = 'today';
353            break;
354        case 'yesterday': $arg = 'yesterday';
355            break;
356        default:
357            $renderer->doc .= htmlspecialchars($this->getLang('err2'));
358            return;
359        }
360
361        $counter = $this->plugin_counter_get_count($INFO['id']);
362        if (gettype($counter) == "string") {
363            $renderer->doc .= $counter;
364        } else if ($counter[$arg] <= 1) {
365            $renderer->doc .= htmlspecialchars($counter[$arg]) .htmlspecialchars($data[1]);
366        } else {
367            $renderer->doc .= htmlspecialchars($counter[$arg]) .htmlspecialchars($data[2]);
368        }
369
370    }
371
372}
373