1 |
joku |
63 |
<?php |
2 |
|
|
/* This file is part of BBClone (A PHP based Web Counter on Steroids) |
3 |
|
|
* |
4 |
|
|
* SVN FILE $Id$ |
5 |
|
|
* |
6 |
joku |
381 |
* Copyright (C) 2001-2018, the BBClone Team (see doc/authors.txt for details) |
7 |
joku |
63 |
* |
8 |
|
|
* This program is free software: you can redistribute it and/or modify |
9 |
|
|
* it under the terms of the GNU General Public License as published by |
10 |
|
|
* the Free Software Foundation, either version 3 of the License, or |
11 |
|
|
* (at your option) any later version. |
12 |
|
|
* |
13 |
|
|
* This program is distributed in the hope that it will be useful, |
14 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 |
|
|
* GNU General Public License for more details. |
17 |
|
|
* |
18 |
|
|
* See doc/copying.txt for details |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
//////////// |
22 |
|
|
// Marker // |
23 |
|
|
//////////// |
24 |
|
|
|
25 |
|
|
// Main Class Counter |
26 |
|
|
class bbc_marker { |
27 |
|
|
var $sep, $filename, $ignored, $string; |
28 |
|
|
|
29 |
|
|
// randomly choose a counter file to write to |
30 |
|
|
function bbc_counter_file($cache_path, $counter_pre, $counter_suf) { |
31 |
|
|
global $BBC_COUNTER_FILES; |
32 |
|
|
|
33 |
|
|
mt_srand((double) microtime() * 1000000); |
34 |
|
|
return ($cache_path.$counter_pre.mt_rand(0, ($BBC_COUNTER_FILES - 1)).$counter_suf); |
35 |
|
|
} |
36 |
|
|
|
37 |
|
|
function bbc_known_range($addr, $class_a) { |
38 |
|
|
// look up whether an address is registerred |
39 |
|
|
global $BBC_IP2EXT_PATH; |
40 |
|
|
|
41 |
|
|
$long = sprintf("%u", ip2long($addr)); |
42 |
|
|
$file = $BBC_IP2EXT_PATH.$class_a.".inc"; |
43 |
|
|
$is_valid = false; |
44 |
|
|
|
45 |
|
|
if (!is_readable($file)) return false; |
46 |
|
|
|
47 |
|
|
$fp = fopen($file, "rb"); |
48 |
|
|
|
49 |
|
|
while (($range = fgetcsv($fp, 32, "|")) !== false) { |
50 |
|
|
if (($long >= $range[1]) && ($long < ($range[1] + $range[2]))) { |
51 |
|
|
$is_valid = true; |
52 |
|
|
break; |
53 |
|
|
} |
54 |
|
|
} |
55 |
|
|
fclose($fp); |
56 |
|
|
return ($is_valid ? true : false); |
57 |
|
|
} |
58 |
|
|
|
59 |
|
|
// validates a hostname or ip address |
60 |
|
|
function bbc_valid_ip($addr, $prx = 0) { |
61 |
|
|
$iptest = explode(".", $addr); |
62 |
|
|
$iptest = defined("_rev") ? array_reverse($iptest) : $iptest; |
63 |
|
|
$oct = count($iptest); |
64 |
|
|
|
65 |
|
|
if ($oct != 4) return false; |
66 |
|
|
|
67 |
|
|
for ($i = 0; $i < $oct; $i++) { |
68 |
|
|
$iptest[$i] = trim($iptest[$i]); |
69 |
|
|
|
70 |
|
|
if ((!preg_match(":^[0-9]{1,3}$:", $iptest[$i])) || ($iptest[$i] > 255)) return false; |
71 |
|
|
} |
72 |
|
|
|
73 |
|
|
if (($iptest[0] < 1) || ($iptest[0] > 223) || ($iptest[3] < 1) || ($iptest[3] > 254) || |
74 |
|
|
(($prx) && ($this->bbc_known_range($addr, $iptest[0]) === false))) return false; |
75 |
|
|
|
76 |
|
|
return (defined("_rev") ? implode(".", $iptest) : $addr); |
77 |
|
|
} |
78 |
|
|
|
79 |
|
|
// converts a hexadecimal ip address to the dotted format if applicable |
80 |
|
|
function bbc_hex2ip($str) { |
81 |
|
|
if (!preg_match(":[a-fA-F0-9]{8}:", $str)) return $str; |
82 |
|
|
|
83 |
|
|
$arr = explode(".", wordwrap($str, 2, ".", 2)); |
84 |
|
|
|
85 |
|
|
for ($i = 0, $k = count($arr); $i < $k; $i++) $arr[$i] = trim(hexdec($arr[$i])); |
86 |
|
|
return ($arr[0].".".$arr[1].".".$arr[2].".".$arr[3]); |
87 |
|
|
} |
88 |
|
|
|
89 |
|
|
// returns the first valid host |
90 |
|
|
function bbc_select_host($array) { |
91 |
|
|
arsort($array, SORT_NUMERIC); |
92 |
|
|
|
93 |
|
|
foreach ($array as $key => $val) { |
94 |
|
|
$key = $this->bbc_hex2ip(trim($key)); |
95 |
|
|
|
96 |
|
|
if (($prx = $this->bbc_valid_ip($key, 1)) !== false) return $prx; |
97 |
|
|
} |
98 |
|
|
return false; |
99 |
|
|
} |
100 |
|
|
|
101 |
|
|
// extract the first valid address from a chain |
102 |
|
|
function bbc_unchain_addr($val) { |
103 |
|
|
if (strpos($val, ",") === false) return $val; |
104 |
|
|
|
105 |
|
|
$array = explode(",", $val); |
106 |
|
|
|
107 |
|
|
for ($i = 0, $max = count($array); $i < $max; $i++) $array[$i] = trim($array[$i]); |
108 |
|
|
return $this->bbc_select_host(array_flip($array)); |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
// return the correct remote address |
112 |
|
|
function bbc_get_remote_addr($addr, $reverse) { |
113 |
|
|
$addr = $this->bbc_unchain_addr($addr); |
114 |
|
|
$reverse = $this->bbc_unchain_addr($reverse); |
115 |
|
|
|
116 |
|
|
if ((!empty($reverse)) && ($this->bbc_valid_ip($addr, 1) === false)) return $reverse; |
117 |
|
|
elseif (empty($addr)) return "127.0.0.1"; |
118 |
|
|
else return ((substr($addr, 0, strpos($addr, ".")) == 127) ? "127.0.0.1" : $addr); |
119 |
|
|
} |
120 |
|
|
|
121 |
|
|
// check for client in proxy headers |
122 |
|
|
function bbc_parse_headers() { |
123 |
|
|
if (_BBC_PHP < 410) global $HTTP_SERVER_VARS; |
124 |
|
|
|
125 |
|
|
foreach (((_BBC_PHP < 410) ? $HTTP_SERVER_VARS : $_SERVER) as $key => $val) { |
126 |
|
|
if (!(substr($key, 0, strpos($key, "_")) == "HTTP")) continue; |
127 |
|
|
|
128 |
|
|
if ((stristr($val, " for ") !== false)) { |
129 |
|
|
$tmp = explode(" for ", strtolower($val)); |
130 |
|
|
$tmpval = trim($tmp[count($tmp) - 1]); |
131 |
|
|
$tmpval = $this->bbc_unchain_addr($tmpval); |
132 |
|
|
$chk[$tmpval] = isset($chk[$tmpval]) ? ++$chk[$tmpval] : 1; |
133 |
|
|
} |
134 |
|
|
if ((strpos($key, "_CLIENT") !== false) || (substr($key, -4) == "_FOR")) { |
135 |
|
|
$val = $this->bbc_unchain_addr($val); |
136 |
|
|
$chk[$val] = isset($chk[$val]) ? ++$chk[$val] : 1; |
137 |
|
|
} |
138 |
|
|
// If we find this, the client's ip address needs to be reversed |
139 |
|
|
if (($key == "HTTP_VIA") && (preg_match("|Traffic[ \-]?Server/5\.2\.0|i", $val))) { |
140 |
|
|
!defined("_rev") ? define("_rev", 1) : ""; |
141 |
|
|
} |
142 |
|
|
} |
143 |
|
|
return (!empty($chk) ? $this->bbc_select_host($chk) : false); |
144 |
|
|
} |
145 |
|
|
|
146 |
|
|
// Check if an ip address is matching up against the blacklist |
147 |
|
|
function bbc_is_ignored($blacklist, $client) { |
148 |
|
|
$ipmatch = (empty($blacklist) ? "" : explode(",", $blacklist)); |
149 |
|
|
|
150 |
|
|
if (empty($ipmatch)) return false; |
151 |
|
|
|
152 |
|
|
for($i = count($ipmatch) - 1; $i >= 0; $i--) { |
153 |
|
|
$test = trim($ipmatch[$i]); |
154 |
|
|
|
155 |
|
|
if (substr($client, 0, strlen($test)) === $test) return true; |
156 |
|
|
} |
157 |
|
|
return false; |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
// checking for matching hosts which we have to ignore. We assume that a |
161 |
|
|
// keyword with leading slash implies an uri and everything else a hostname |
162 |
|
|
function bbc_ignore_ref($array) { |
163 |
|
|
global $BBC_IGNORE_REFER; |
164 |
|
|
|
165 |
|
|
if (!empty($BBC_IGNORE_REFER)) { |
166 |
|
|
foreach(explode(",", $BBC_IGNORE_REFER) as $test) { |
167 |
|
|
$test = trim($test); |
168 |
|
|
$is_path = ($test[0] == "/") ? true : false; |
169 |
|
|
|
170 |
|
|
if (stristr(($is_path ? $array[2] : $array[1]), $test) !== false) return true; |
171 |
|
|
} |
172 |
|
|
} |
173 |
|
|
return false; |
174 |
|
|
} |
175 |
|
|
|
176 |
|
|
// checks for a valid url format |
177 |
|
|
function bbc_valid_ref($ref) { |
178 |
|
|
$tmp = explode(":", $ref); |
179 |
|
|
|
180 |
|
|
for ($i = 0, $k = count($tmp); $i < $k; $i++) $tmp[$i] = trim($tmp[$i]); |
181 |
|
|
return (((($tmp[0] == "http") || ($tmp[0] == "https")) && (substr($tmp[1], 0, 2) == "//")) ? true : false); |
182 |
|
|
} |
183 |
|
|
|
184 |
|
|
//converts a referrer to an array with the hostname, ip address and the full referrer |
185 |
|
|
function bbc_parse_ref($ref) { |
186 |
|
|
if (!$this->bbc_valid_ref($ref)) return false; |
187 |
|
|
|
188 |
|
|
// getting rid of stupid user input |
189 |
|
|
$ref = str_replace(":/", "://", preg_replace(":/+:", "/", $ref)); |
190 |
|
|
$ref = preg_replace(":\.+(/|$):", "\\1", $ref); |
191 |
|
|
$ref = substr(strstr($ref, "://"), 3); |
192 |
|
|
|
193 |
|
|
$uri = (($slash = strpos($ref, "/")) !== false) ? substr($ref, $slash) : "/"; |
194 |
|
|
$host_raw = strtolower((($slash !== false) ? substr($ref, 0, $slash) : $ref)); |
195 |
|
|
$host = (($port = strpos($host_raw, ":")) !== false) ? substr($host_raw, 0, $port) : $host_raw; |
196 |
|
|
|
197 |
|
|
return (preg_match("|^[a-zA-Z0-9._\-]{2,64}$|", $host) ? array("http://".$host_raw.$uri, $host, $uri) : false); |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
// determine and filter stuff which came from the local server |
201 |
|
|
function bbc_filter_ref($srvhost, $ref, $srvname, $srvaddr) { |
202 |
|
|
$ref_array = $this->bbc_parse_ref($ref); |
203 |
|
|
|
204 |
|
|
if (is_array($ref_array) && ($this->bbc_ignore_ref($ref_array) !== false)) return "ignored"; |
205 |
|
|
|
206 |
|
|
if (!$ref_array || ($ref_array[1] == $srvaddr) || ($ref_array[1] == $srvname) || |
207 |
|
|
((substr($ref_array[1], 0, 4) == "127.") || (substr($ref_array[1], 0, 2) == "0.")) || |
208 |
|
|
((substr($srvname, 0, 4) == "www.") && (substr($srvname, 4) == $ref_array[1])) || |
209 |
|
|
((substr($ref_array[1], 0, 4) == "www.") && (substr($ref_array[1], 4) == $srvname)) || |
210 |
|
|
(!empty($srvhost) && (($srvhost == $ref_array[1]) || |
211 |
|
|
((substr($srvhost, 0, 4) == "www.") && (substr($srvhost, 4) == $ref_array[1])) || |
212 |
|
|
((substr($ref_array[1], 0, 4) == "www.") && (substr($ref_array[1], 4) == $srvhost))))) { |
213 |
|
|
return "unknown"; |
214 |
|
|
} |
215 |
|
|
else return $ref_array[0]; |
216 |
|
|
} |
217 |
|
|
|
218 |
|
|
// avoid trails of query strings which aren't relevant for page counting |
219 |
|
|
function bbc_filter_uri($script, $pinfo, $uri) { |
220 |
matthys |
264 |
global $BBC_USE_ORIGINAL_URI; |
221 |
|
|
|
222 |
|
|
// check if we should use original uri, else we filter uri by default |
223 |
|
|
if ((!empty($BBC_USE_ORIGINAL_URI))) { |
224 |
|
|
return $uri; |
225 |
|
|
} |
226 |
|
|
// filter -> getting rid of stupid user input |
227 |
joku |
63 |
foreach (array("pinfo", "uri") as $path) { |
228 |
|
|
${$path} = str_replace(":/", "://", preg_replace(":/+:", "/", ${$path})); |
229 |
|
|
${$path} = preg_replace(":\.+(/|$):", "\\1", ${$path}); |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
// On some systems path info is just an alias for the script uri |
233 |
|
|
$pinfo = ($uri == $pinfo) ? 0 : $pinfo; |
234 |
|
|
|
235 |
|
|
$uri = !empty($pinfo) ? substr($uri, 0, (strlen($uri) - strlen($pinfo))) : $uri; |
236 |
|
|
$uri = (basename($uri) !== $script) ? (((($dir = dirname($uri)) == ".") || (empty($dir))) ? "/" : $dir."/") |
237 |
|
|
.$script : $uri; |
238 |
|
|
|
239 |
|
|
$test = explode(".", $script); |
240 |
|
|
$tmp = strtolower(trim($test[0])); |
241 |
|
|
$tmp = ((count($test) == 2) && (($tmp == "index") || ($tmp == "default"))) ? true : false; |
242 |
|
|
|
243 |
|
|
return (($tmp !== false) ? substr($uri, 0, (strrpos($uri, "/") + 1)) : (empty($uri) ? "/" : $uri)); |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
// automatic page name generation in case of not being specified |
247 |
|
|
function bbc_auto_page_name($uri) { |
248 |
|
|
if (!is_string($uri) || empty($uri) || ($uri == "/")) return "index"; |
249 |
|
|
|
250 |
|
|
$uri = (substr($uri, -1) == "/") ? substr($uri, 1, -1) : ((($dot = strrpos($uri, ".")) !== false) ? |
251 |
|
|
substr($uri, 1, --$dot) : substr($uri, 1)); |
252 |
|
|
$uri = strtr($uri, array("/" => " -> ", "_" => " ")); |
253 |
|
|
|
254 |
|
|
return ucwords($uri); |
255 |
|
|
} |
256 |
|
|
|
257 |
|
|
// write the entry |
258 |
|
|
function bbc_write_entry() { |
259 |
|
|
global $BBC_CACHE_PATH; |
260 |
|
|
|
261 |
|
|
$file = $this->filename; |
262 |
|
|
$base = basename($file); |
263 |
|
|
|
264 |
|
|
if (!is_readable($file)) return array($base, "r"); |
265 |
|
|
if (!is_writable($file)) return array($base, "w"); |
266 |
|
|
|
267 |
|
|
$fp = defined("_BBC_DIO") ? dio_open($file, O_RDWR | O_APPEND) : fopen($file, "ab+"); |
268 |
|
|
|
269 |
|
|
if (defined("_BBC_DIO") && (dio_fcntl($fp, F_SETLK, 1) !== -1)) { |
270 |
|
|
dio_write($fp, $this->string); |
271 |
|
|
dio_fcntl($fp, F_SETLK, 0); |
272 |
|
|
|
273 |
|
|
$ok = 1; |
274 |
|
|
} |
275 |
|
|
else { |
276 |
|
|
if (defined("_BBC_SEM") ? ($id = bbc_semlock($file)) : flock($fp, LOCK_EX)) { |
277 |
|
|
fputs($fp, $this->string); |
278 |
|
|
fflush($fp); |
279 |
|
|
defined("_BBC_SEM") ? sem_release($id) : flock($fp, LOCK_UN); |
280 |
|
|
|
281 |
|
|
$ok = 1; |
282 |
|
|
} |
283 |
|
|
} |
284 |
|
|
defined("_BBC_DIO") ? dio_close($fp) : fclose($fp); |
285 |
|
|
|
286 |
|
|
return (isset($ok) ? array($base, "o") : array($base, "l")); |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
// constructor |
290 |
|
|
function bbc_marker() { |
291 |
|
|
if (_BBC_PHP < 410) global $HTTP_SERVER_VARS; |
292 |
|
|
|
293 |
|
|
global $BBC_CACHE_PATH, $BBC_COUNTER_PREFIX, $BBC_COUNTER_SUFFIX, $BBC_IGNORE_IP, $BBC_SEP, $BBC_TIMESTAMP, |
294 |
|
|
$BBC_TIME_OFFSET, $DOCUMENT_ROOT, $HTTP_HOST, $HTTP_X_REMOTECLIENT_IP, $LOCAL_ADDR, $PATH_INFO, |
295 |
|
|
$PHP_SELF, $SCRIPT_FILENAME, $SERVER_NAME; |
296 |
|
|
|
297 |
|
|
$this->sep = $BBC_SEP; |
298 |
|
|
$this->ignored = false; |
299 |
|
|
$this->filename = $this->bbc_counter_file($BBC_CACHE_PATH, $BBC_COUNTER_PREFIX, $BBC_COUNTER_SUFFIX); |
300 |
|
|
|
301 |
|
|
$time = $BBC_TIMESTAMP + ($BBC_TIME_OFFSET * 60); |
302 |
|
|
|
303 |
|
|
// loads of initialisations |
304 |
|
|
$hdr = array("DOCUMENT_ROOT", "HTTP_USER_AGENT", "LOCAL_ADDR", "REMOTE_HOST", "REMOTE_ADDR", "HTTP_HOST", |
305 |
|
|
"HTTP_REFERER", "HTTP_X_REMOTECLIENT_IP", "ORIG_PATH_INFO", "ORIG_PATH_TRANSLATED", |
306 |
|
|
"ORIG_SCRIPT_FILENAME", "PATH_INFO", "PATH_TRANSLATED", "HTTP_PC_REMOTE_ADDR", "PHP_SELF", |
307 |
|
|
"SCRIPT_FILENAME", "SERVER_NAME", "SERVER_ADDR"); |
308 |
|
|
|
309 |
|
|
foreach ($hdr as $str) { |
310 |
|
|
$$str = ((_BBC_PHP < 410) ? !empty($HTTP_SERVER_VARS[$str]) : !empty($_SERVER[$str])) ? |
311 |
|
|
bbc_clean(((_BBC_PHP < 410) ? $HTTP_SERVER_VARS[$str] : $_SERVER[$str]), $BBC_SEP) : false; |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
// determine whether we got the "ORIG_" prefix |
315 |
|
|
foreach (array("PATH_INFO", "PATH_TRANSLATED", "SCRIPT_FILENAME") as $env) { |
316 |
|
|
$$env = !empty(${"ORIG_".$env}) ? ${"ORIG_".$env} : $$env; |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
$filename = (empty($PATH_TRANSLATED) || ($PATH_TRANSLATED == $DOCUMENT_ROOT)) ? basename($SCRIPT_FILENAME) : |
320 |
|
|
basename($PATH_TRANSLATED); |
321 |
|
|
$REMOTE_ADDR = ((stristr(PHP_OS, "darwin") !== false) && !empty($HTTP_PC_REMOTE_ADDR)) ? $HTTP_PC_REMOTE_ADDR : |
322 |
|
|
$REMOTE_ADDR; |
323 |
|
|
$REQUEST_URI = $this->bbc_filter_uri($filename, $PATH_INFO , $PHP_SELF); |
324 |
|
|
$SERVER_ADDR = empty($SERVER_ADDR) ? $LOCAL_ADDR : $SERVER_ADDR; |
325 |
|
|
$SERVER_ADDR = $this->bbc_valid_ip($SERVER_ADDR) ? $SERVER_ADDR : "127.0.0.1"; |
326 |
|
|
$HTTP_USER_AGENT = empty($HTTP_USER_AGENT) ? "unknown" : $HTTP_USER_AGENT; |
327 |
|
|
$HTTP_REFERER = empty($HTTP_REFERER) ? "unknown" : |
328 |
|
|
$this->bbc_filter_ref($HTTP_HOST, $HTTP_REFERER, $SERVER_NAME, $SERVER_ADDR); |
329 |
|
|
// Use a page name even if the user didn't specify it |
330 |
|
|
$page = defined("_BBC_PAGE_NAME") ? bbc_clean(_BBC_PAGE_NAME, $BBC_SEP) : $this->bbc_auto_page_name($REQUEST_URI); |
331 |
|
|
$prx = $this->bbc_parse_headers(); |
332 |
|
|
|
333 |
|
|
if (!empty($prx)) { |
334 |
|
|
$prx_addr = $this->bbc_get_remote_addr($REMOTE_ADDR, $HTTP_X_REMOTECLIENT_IP); |
335 |
|
|
|
336 |
|
|
if (($this->ignored = $this->bbc_is_ignored($BBC_IGNORE_IP, $prx_addr)) !== false) return; |
337 |
|
|
else $REMOTE_ADDR = bbc_clean($prx, $BBC_SEP); |
338 |
|
|
} |
339 |
|
|
else { |
340 |
|
|
$prx_addr = "unknown"; |
341 |
|
|
$REMOTE_ADDR = $this->bbc_get_remote_addr($REMOTE_ADDR, $HTTP_X_REMOTECLIENT_IP); |
342 |
|
|
} |
343 |
|
|
|
344 |
|
|
if (($this->ignored = $this->bbc_is_ignored($BBC_IGNORE_IP, $REMOTE_ADDR)) !== false) return; |
345 |
|
|
|
346 |
|
|
// "unknown" is meant as a placeholder for the hostname, which will be processed at a different location |
347 |
|
|
$this->string = $time.$this->sep.$prx_addr.$this->sep.$REMOTE_ADDR.$this->sep."unknown".$this->sep |
348 |
|
|
.$HTTP_USER_AGENT.$this->sep.$HTTP_REFERER.$this->sep.$REQUEST_URI.$this->sep.$page."\n"; |
349 |
|
|
} |
350 |
|
|
} |
351 |
matthys |
15 |
?> |