OSDN Git Service

bb306d2b4794d19436b07c339460d4a6096c541c
[pukiwiki/pukiwiki_sandbox.git] / spam / spam_pickup.php
1 <?php
2 // $Id: spam_pickup.php,v 1.56 2007/08/20 14:50:31 henoheno Exp $
3 // Copyright (C) 2006-2007 PukiWiki Developers Team
4 // License: GPL v2 or (at your option) any later version
5 //
6 // Functions for Concept-work of spam-uri metrics
7 //
8
9 // ---------------------
10 // URI pickup
11
12 // Return an array of URIs in the $string
13 // [OK] http://nasty.example.org#nasty_string
14 // [OK] http://nasty.example.org:80/foo/xxx#nasty_string/bar
15 // [OK] ftp://nasty.example.org:80/dfsdfs
16 // [OK] ftp://cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm (from RFC3986)
17 function uri_pickup($string = '')
18 {
19         if (! is_string($string)) return array();
20
21         // Not available for: IDN(ignored)
22         $array = array();
23         preg_match_all(
24                 // scheme://userinfo@host:port/path/or/pathinfo/maybefile.and?query=string#fragment
25                 // Refer RFC3986 (Regex below is not strict)
26                 '#(\b[a-z][a-z0-9.+-]{1,8}):[/\\\]+' .          // 1: Scheme
27                 '(?:' .
28                         '([^\s<>"\'\[\]/\#?@]*)' .              // 2: Userinfo (Username)
29                 '@)?' .
30                 '(' .
31                         // 3: Host
32                         '\[[0-9a-f:.]+\]' . '|' .                               // IPv6([colon-hex and dot]): RFC2732
33                         '(?:[0-9]{1,3}\.){3}[0-9]{1,3}' . '|' . // IPv4(dot-decimal): 001.22.3.44
34                         '[a-z0-9_-][a-z0-9_.-]+[a-z0-9_-]' .    // hostname(FQDN) : foo.example.org
35                 ')' .
36                 '(?::([0-9]*))?' .                                      // 4: Port
37                 '((?:/+[^\s<>"\'\[\]/\#]+)*/+)?' .      // 5: Directory path or path-info
38                 '([^\s<>"\'\[\]\#?]+)?' .                       // 6: File?
39                 '(?:\?([^\s<>"\'\[\]\#]+))?' .          // 7: Query string
40                 '(?:\#([a-z0-9._~%!$&\'()*+,;=:@-]*))?' .       // 8: Fragment
41                 '#i',
42                  $string, $array, PREG_SET_ORDER | PREG_OFFSET_CAPTURE
43         );
44
45         // Format the $array
46         static $parts = array(
47                 1 => 'scheme', 2 => 'userinfo', 3 => 'host', 4 => 'port',
48                 5 => 'path', 6 => 'file', 7 => 'query', 8 => 'fragment'
49         );
50         $default = array('');
51         foreach(array_keys($array) as $uri) {
52                 $_uri = & $array[$uri];
53                 array_rename_keys($_uri, $parts, TRUE, $default);
54                 $offset = $_uri['scheme'][1]; // Scheme's offset = URI's offset
55                 foreach(array_keys($_uri) as $part) {
56                         $_uri[$part] = & $_uri[$part][0];       // Remove offsets
57                 }
58         }
59
60         foreach(array_keys($array) as $uri) {
61                 $_uri = & $array[$uri];
62                 if ($_uri['scheme'] === '') {
63                         unset($array[$uri]);    // Considererd harmless
64                         continue;
65                 }
66                 unset($_uri[0]); // Matched string itself
67                 $_uri['area']['offset'] = $offset;      // Area offset for area_measure()
68         }
69
70         return $array;
71 }
72
73 // Pickupped URI array => An URI (See uri_pickup())
74 // USAGE:
75 //      $pickups = uri_pickup('a string include some URIs');
76 //      $uris = array();
77 //      foreach (array_keys($pickups) as $key) {
78 //              $uris[$key] = uri_pickup_implode($pickups[$key]);
79 //      }
80 function uri_pickup_implode($uri = array())
81 {
82         if (empty($uri) || ! is_array($uri)) return NULL;
83
84         $tmp = array();
85         if (isset($uri['scheme']) && $uri['scheme'] !== '') {
86                 $tmp[] = & $uri['scheme'];
87                 $tmp[] = '://';
88         }
89         if (isset($uri['userinfo']) && $uri['userinfo'] !== '') {
90                 $tmp[] = & $uri['userinfo'];
91                 $tmp[] = '@';
92         }
93         if (isset($uri['host']) && $uri['host'] !== '') {
94                 $tmp[] = & $uri['host'];
95         }
96         if (isset($uri['port']) && $uri['port'] !== '') {
97                 $tmp[] = ':';
98                 $tmp[] = & $uri['port'];
99         }
100         if (isset($uri['path']) && $uri['path'] !== '') {
101                 $tmp[] = & $uri['path'];
102         }
103         if (isset($uri['file']) && $uri['file'] !== '') {
104                 $tmp[] = & $uri['file'];
105         }
106         if (isset($uri['query']) && $uri['query'] !== '') {
107                 $tmp[] = '?';
108                 $tmp[] = & $uri['query'];
109         }
110         if (isset($uri['fragment']) && $uri['fragment'] !== '') {
111                 $tmp[] = '#';
112                 $tmp[] = & $uri['fragment'];
113         }
114
115         return implode('', $tmp);
116 }
117
118
119 // ---------------------
120 // URI normalization
121
122 // Normalize an array of URI arrays
123 // NOTE: Give me the uri_pickup() results
124 function uri_pickup_normalize(& $pickups, $destructive = TRUE)
125 {
126         if (! is_array($pickups)) return $pickups;
127
128         if ($destructive) {
129                 foreach (array_keys($pickups) as $key) {
130                         $_key = & $pickups[$key];
131                         $_key['scheme']   = isset($_key['scheme'])   ? scheme_normalize($_key['scheme']) : '';
132                         $_key['host']     = isset($_key['host'])     ? host_normalize($_key['host'])     : '';
133                         $_key['port']     = isset($_key['port'])     ? port_normalize($_key['port'], $_key['scheme'], FALSE) : '';
134                         $_key['path']     = isset($_key['path'])     ? strtolower(path_normalize($_key['path'])) : '';
135                         $_key['file']     = isset($_key['file'])     ? file_normalize($_key['file'])   : '';
136                         $_key['query']    = isset($_key['query'])    ? query_normalize($_key['query']) : '';
137                         $_key['fragment'] = isset($_key['fragment']) ? strtolower($_key['fragment'])   : '';
138                 }
139         } else {
140                 foreach (array_keys($pickups) as $key) {
141                         $_key = & $pickups[$key];
142                         $_key['scheme']   = isset($_key['scheme'])   ? scheme_normalize($_key['scheme']) : '';
143                         $_key['host']     = isset($_key['host'])     ? strtolower($_key['host'])         : '';
144                         $_key['port']     = isset($_key['port'])     ? port_normalize($_key['port'], $_key['scheme'], FALSE) : '';
145                         $_key['path']     = isset($_key['path'])     ? path_normalize($_key['path']) : '';
146                 }
147         }
148
149         return $pickups;
150 }
151
152 // Scheme normalization: Renaming the schemes
153 // snntp://example.org =>  nntps://example.org
154 // NOTE: Keep the static lists simple. See also port_normalize().
155 function scheme_normalize($scheme = '', $abbrevs_harmfull = TRUE)
156 {
157         // Abbreviations they have no intention of link
158         static $abbrevs = array(
159                 'ttp'   => 'http',
160                 'ttps'  => 'https',
161         );
162
163         // Aliases => normalized ones
164         static $aliases = array(
165                 'pop'   => 'pop3',
166                 'news'  => 'nntp',
167                 'imap4' => 'imap',
168                 'snntp' => 'nntps',
169                 'snews' => 'nntps',
170                 'spop3' => 'pop3s',
171                 'pops'  => 'pop3s',
172         );
173
174         if (! is_string($scheme)) return '';
175
176         $scheme = strtolower($scheme);
177         if (isset($abbrevs[$scheme])) {
178                 $scheme = $abbrevs_harmfull ? $abbrevs[$scheme] : '';
179         }
180         if (isset($aliases[$scheme])) {
181                 $scheme = $aliases[$scheme];
182         }
183
184         return $scheme;
185 }
186
187 // Hostname normlization (Destructive)
188 // www.foo     => www.foo   ('foo' seems TLD)
189 // www.foo.bar => foo.bar
190 // www.10.20   => www.10.20 (Invalid hostname)
191 // NOTE:
192 //   'www' is  mostly used as traditional hostname of WWW server.
193 //   'www.foo.bar' may be identical with 'foo.bar'.
194 function host_normalize($host = '')
195 {
196         if (! is_string($host)) return '';
197
198         $host = strtolower($host);
199         $matches = array();
200         if (preg_match('/^www\.(.+\.[a-z]+)$/', $host, $matches)) {
201                 return $matches[1];
202         } else {
203                 return $host;
204         }
205 }
206
207 // Port normalization: Suppress the (redundant) default port
208 // HTTP://example.org:80/ => http://example.org/
209 // HTTP://example.org:8080/ => http://example.org:8080/
210 // HTTPS://example.org:443/ => https://example.org/
211 function port_normalize($port, $scheme, $scheme_normalize = FALSE)
212 {
213         // Schemes that users _maybe_ want to add protocol-handlers
214         // to their web browsers. (and attackers _maybe_ want to use ...)
215         // Reference: http://www.iana.org/assignments/port-numbers
216         static $array = array(
217                 // scheme => default port
218                 'ftp'     =>    21,
219                 'ssh'     =>    22,
220                 'telnet'  =>    23,
221                 'smtp'    =>    25,
222                 'tftp'    =>    69,
223                 'gopher'  =>    70,
224                 'finger'  =>    79,
225                 'http'    =>    80,
226                 'pop3'    =>   110,
227                 'sftp'    =>   115,
228                 'nntp'    =>   119,
229                 'imap'    =>   143,
230                 'irc'     =>   194,
231                 'wais'    =>   210,
232                 'https'   =>   443,
233                 'nntps'   =>   563,
234                 'rsync'   =>   873,
235                 'ftps'    =>   990,
236                 'telnets' =>   992,
237                 'imaps'   =>   993,
238                 'ircs'    =>   994,
239                 'pop3s'   =>   995,
240                 'mysql'   =>  3306,
241         );
242
243         // intval() converts '0-1' to '0', so preg_match() rejects these invalid ones
244         if (! is_numeric($port) || $port < 0 || preg_match('/[^0-9]/i', $port))
245                 return '';
246
247         $port = intval($port);
248         if ($scheme_normalize) $scheme = scheme_normalize($scheme);
249         if (isset($array[$scheme]) && $port == $array[$scheme])
250                 $port = ''; // Ignore the defaults
251
252         return $port;
253 }
254
255 // Path normalization
256 // http://example.org => http://example.org/
257 // http://example.org#hoge => http://example.org/#hoge
258 // http://example.org/path/a/b/./c////./d => http://example.org/path/a/b/c/d
259 // http://example.org/path/../../a/../back => http://example.org/back
260 function path_normalize($path = '', $divider = '/', $add_root = TRUE)
261 {
262         if (! is_string($divider)) return is_string($path) ? $path : '';
263
264         if ($add_root) {
265                 $first_div = & $divider;
266         } else {
267                 $first_div = '';
268         }
269         if (! is_string($path) || $path == '') return $first_div;
270
271         if (strpos($path, $divider, strlen($path) - strlen($divider)) === FALSE) {
272                 $last_div = '';
273         } else {
274                 $last_div = & $divider;
275         }
276
277         $array = explode($divider, $path);
278
279         // Remove paddings ('//' and '/./')
280         foreach(array_keys($array) as $key) {
281                 if ($array[$key] == '' || $array[$key] == '.') {
282                          unset($array[$key]);
283                 }
284         }
285
286         // Remove back-tracks ('/../')
287         $tmp = array();
288         foreach($array as $value) {
289                 if ($value == '..') {
290                         array_pop($tmp);
291                 } else {
292                         array_push($tmp, $value);
293                 }
294         }
295         $array = & $tmp;
296
297         if (empty($array)) {
298                 return $first_div;
299         } else {
300                 return $first_div . implode($divider, $array) . $last_div;
301         }
302 }
303
304 // DirectoryIndex normalize (Destructive and rough)
305 // TODO: sample.en.ja.html.gz => sample.html
306 function file_normalize($file = 'index.html.en')
307 {
308         static $simple_defaults = array(
309                 'default.htm'   => TRUE,
310                 'default.html'  => TRUE,
311                 'default.asp'   => TRUE,
312                 'default.aspx'  => TRUE,
313                 'index'                 => TRUE,        // Some system can omit the suffix
314         );
315
316         static $content_suffix = array(
317                 // index.xxx, sample.xxx
318                 'htm'   => TRUE,
319                 'html'  => TRUE,
320                 'shtml' => TRUE,
321                 'jsp'   => TRUE,
322                 'php'   => TRUE,
323                 'php3'  => TRUE,
324                 'php4'  => TRUE,
325                 'pl'    => TRUE,
326                 'py'    => TRUE,
327                 'rb'    => TRUE,
328                 'cgi'   => TRUE,
329                 'xml'   => TRUE,
330         );
331
332         static $language_suffix = array(
333                 // Reference: Apache 2.0.59 'AddLanguage' default
334                 'ca'    => TRUE,
335                 'cs'    => TRUE,        // cs
336                 'cz'    => TRUE,        // cs
337                 'de'    => TRUE,
338                 'dk'    => TRUE,        // da
339                 'el'    => TRUE,
340                 'en'    => TRUE,
341                 'eo'    => TRUE,
342                 'es'    => TRUE,
343                 'et'    => TRUE,
344                 'fr'    => TRUE,
345                 'he'    => TRUE,
346                 'hr'    => TRUE,
347                 'it'    => TRUE,
348                 'ja'    => TRUE,
349                 'ko'    => TRUE,
350                 'ltz'   => TRUE,
351                 'nl'    => TRUE,
352                 'nn'    => TRUE,
353                 'no'    => TRUE,
354                 'po'    => TRUE,
355                 'pt'    => TRUE,
356                 'pt-br' => TRUE,
357                 'ru'    => TRUE,
358                 'sv'    => TRUE,
359                 'zh-cn' => TRUE,
360                 'zh-tw' => TRUE,
361
362                 // Reference: Apache 2.0.59 default 'index.html' variants
363                 'ee'    => TRUE,
364                 'lb'    => TRUE,
365                 'var'   => TRUE,
366         );
367
368         static $charset_suffix = array(
369                 // Reference: Apache 2.0.59 'AddCharset' default
370                 'iso8859-1'     => TRUE, // ISO-8859-1
371                 'latin1'        => TRUE, // ISO-8859-1
372                 'iso8859-2'     => TRUE, // ISO-8859-2
373                 'latin2'        => TRUE, // ISO-8859-2
374                 'cen'           => TRUE, // ISO-8859-2
375                 'iso8859-3'     => TRUE, // ISO-8859-3
376                 'latin3'        => TRUE, // ISO-8859-3
377                 'iso8859-4'     => TRUE, // ISO-8859-4
378                 'latin4'        => TRUE, // ISO-8859-4
379                 'iso8859-5'     => TRUE, // ISO-8859-5
380                 'latin5'        => TRUE, // ISO-8859-5
381                 'cyr'           => TRUE, // ISO-8859-5
382                 'iso-ru'        => TRUE, // ISO-8859-5
383                 'iso8859-6'     => TRUE, // ISO-8859-6
384                 'latin6'        => TRUE, // ISO-8859-6
385                 'arb'           => TRUE, // ISO-8859-6
386                 'iso8859-7'     => TRUE, // ISO-8859-7
387                 'latin7'        => TRUE, // ISO-8859-7
388                 'grk'           => TRUE, // ISO-8859-7
389                 'iso8859-8'     => TRUE, // ISO-8859-8
390                 'latin8'        => TRUE, // ISO-8859-8
391                 'heb'           => TRUE, // ISO-8859-8
392                 'iso8859-9'     => TRUE, // ISO-8859-9
393                 'latin9'        => TRUE, // ISO-8859-9
394                 'trk'           => TRUE, // ISO-8859-9
395                 'iso2022-jp'=> TRUE, // ISO-2022-JP
396                 'jis'           => TRUE, // ISO-2022-JP
397                 'iso2022-kr'=> TRUE, // ISO-2022-KR
398                 'kis'           => TRUE, // ISO-2022-KR
399                 'iso2022-cn'=> TRUE, // ISO-2022-CN
400                 'cis'           => TRUE, // ISO-2022-CN
401                 'big5'          => TRUE,
402                 'cp-1251'       => TRUE, // ru, WINDOWS-1251
403                 'win-1251'      => TRUE, // ru, WINDOWS-1251
404                 'cp866'         => TRUE, // ru
405                 'koi8-r'        => TRUE, // ru, KOI8-r
406                 'koi8-ru'       => TRUE, // ru, KOI8-r
407                 'koi8-uk'       => TRUE, // ru, KOI8-ru
408                 'ua'            => TRUE, // ru, KOI8-ru
409                 'ucs2'          => TRUE, // ru, ISO-10646-UCS-2
410                 'ucs4'          => TRUE, // ru, ISO-10646-UCS-4
411                 'utf8'          => TRUE,
412
413                 // Reference: Apache 2.0.59 default 'index.html' variants
414                 'euc-kr'        => TRUE,
415                 'gb2312'        => TRUE,
416         );
417
418         // May uncompress by web browsers on the fly
419         // Must be at the last of the filename
420         // Reference: Apache 2.0.59 'AddEncoding'
421         static $encoding_suffix = array(
422                 'z'             => TRUE,
423                 'gz'    => TRUE,
424         );
425
426         if (! is_string($file)) return '';
427         $_file = strtolower($file);
428         if (isset($simple_defaults[$_file])) return '';
429
430         // Roughly removing language/character-set/encoding suffixes
431         // References:
432         //  * Apache 2 document about 'Content-negotiaton', 'mod_mime' and 'mod_negotiation'
433         //    http://httpd.apache.org/docs/2.0/content-negotiation.html
434         //    http://httpd.apache.org/docs/2.0/mod/mod_mime.html
435         //    http://httpd.apache.org/docs/2.0/mod/mod_negotiation.html
436         //  * http://www.iana.org/assignments/character-sets
437         //  * RFC3066: Tags for the Identification of Languages
438         //    http://www.ietf.org/rfc/rfc3066.txt
439         //  * ISO 639: codes of 'language names'
440         $suffixes = explode('.', $_file);
441         $body = array_shift($suffixes);
442         if ($suffixes) {
443                 // Remove the last .gz/.z
444                 $last_key = end(array_keys($suffixes));
445                 if (isset($encoding_suffix[$suffixes[$last_key]])) {
446                         unset($suffixes[$last_key]);
447                 }
448         }
449         // Cut language and charset suffixes
450         foreach($suffixes as $key => $value){
451                 if (isset($language_suffix[$value]) || isset($charset_suffix[$value])) {
452                         unset($suffixes[$key]);
453                 }
454         }
455         if (empty($suffixes)) return $body;
456
457         // Index.xxx
458         $count = count($suffixes);
459         reset($suffixes);
460         $current = current($suffixes);
461         if ($body == 'index' && $count == 1 && isset($content_suffix[$current])) return '';
462
463         return $file;
464 }
465
466 // Sort query-strings if possible (Destructive and rough)
467 // [OK] &&&&f=d&b&d&c&a=0dd  =>  a=0dd&b&c&d&f=d
468 // [OK] nothing==&eg=dummy&eg=padding&eg=foobar  =>  eg=foobar
469 function query_normalize($string = '', $equal = TRUE, $equal_cutempty = TRUE, $stortolower = TRUE)
470 {
471         if (! is_string($string)) return '';
472         if ($stortolower) $string = strtolower($string);
473
474         $array = explode('&', $string);
475
476         // Remove '&' paddings
477         foreach(array_keys($array) as $key) {
478                 if ($array[$key] == '') {
479                          unset($array[$key]);
480                 }
481         }
482
483         // Consider '='-sepalated input and paddings
484         if ($equal) {
485                 $equals = $not_equals = array();
486                 foreach ($array as $part) {
487                         if (strpos($part, '=') === FALSE) {
488                                  $not_equals[] = $part;
489                         } else {
490                                 list($key, $value) = explode('=', $part, 2);
491                                 $value = ltrim($value, '=');
492                                 if (! $equal_cutempty || $value != '') {
493                                         $equals[$key] = $value;
494                                 }
495                         }
496                 }
497
498                 $array = & $not_equals;
499                 foreach ($equals as $key => $value) {
500                         $array[] = $key . '=' . $value;
501                 }
502                 unset($equals);
503         }
504
505         natsort($array);
506         return implode('&', $array);
507 }
508
509 // ---------------------
510 // Area pickup
511
512 // Pickup all of markup areas
513 function area_pickup($string = '', $method = array())
514 {
515         $area = array();
516         if (empty($method)) return $area;
517
518         // Anchor tag pair by preg_match and preg_match_all()
519         // [OK] <a href></a>
520         // [OK] <a href=  >Good site!</a>
521         // [OK] <a href= "#" >test</a>
522         // [OK] <a href="http://nasty.example.com">visit http://nasty.example.com/</a>
523         // [OK] <a href=\'http://nasty.example.com/\' >discount foobar</a> 
524         // [NG] <a href="http://ng.example.com">visit http://ng.example.com _not_ended_
525         $regex = '#<a\b[^>]*\bhref\b[^>]*>.*?</a\b[^>]*(>)#is';
526         if (isset($method['area_anchor'])) {
527                 $areas = array();
528                 $count = isset($method['asap']) ?
529                         preg_match($regex, $string) :
530                         preg_match_all($regex, $string, $areas);
531                 if (! empty($count)) $area['area_anchor'] = $count;
532         }
533         if (isset($method['uri_anchor'])) {
534                 $areas = array();
535                 preg_match_all($regex, $string, $areas, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
536                 foreach(array_keys($areas) as $_area) {
537                         $areas[$_area] =  array(
538                                 $areas[$_area][0][1], // Area start (<a href>)
539                                 $areas[$_area][1][1], // Area end   (</a>)
540                         );
541                 }
542                 if (! empty($areas)) $area['uri_anchor'] = $areas;
543         }
544
545         // phpBB's "BBCode" pair by preg_match and preg_match_all()
546         // [OK] [url][/url]
547         // [OK] [url]http://nasty.example.com/[/url]
548         // [OK] [link]http://nasty.example.com/[/link]
549         // [OK] [url=http://nasty.example.com]visit http://nasty.example.com/[/url]
550         // [OK] [link http://nasty.example.com/]buy something[/link]
551         $regex = '#\[(url|link)\b[^\]]*\].*?\[/\1\b[^\]]*(\])#is';
552         if (isset($method['area_bbcode'])) {
553                 $areas = array();
554                 $count = isset($method['asap']) ?
555                         preg_match($regex, $string) :
556                         preg_match_all($regex, $string, $areas, PREG_SET_ORDER);
557                 if (! empty($count)) $area['area_bbcode'] = $count;
558         }
559         if (isset($method['uri_bbcode'])) {
560                 $areas = array();
561                 preg_match_all($regex, $string, $areas, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
562                 foreach(array_keys($areas) as $_area) {
563                         $areas[$_area] = array(
564                                 $areas[$_area][0][1], // Area start ([url])
565                                 $areas[$_area][2][1], // Area end   ([/url])
566                         );
567                 }
568                 if (! empty($areas)) $area['uri_bbcode'] = $areas;
569         }
570
571         // Various Wiki syntax
572         // [text_or_uri>text_or_uri]
573         // [text_or_uri:text_or_uri]
574         // [text_or_uri|text_or_uri]
575         // [text_or_uri->text_or_uri]
576         // [text_or_uri text_or_uri] // MediaWiki
577         // MediaWiki: [http://nasty.example.com/ visit http://nasty.example.com/]
578
579         return $area;
580 }
581
582 // If in doubt, it's a little doubtful
583 // if (Area => inside <= Area) $brief += -1
584 function area_measure($areas, & $array, $belief = -1, $a_key = 'area', $o_key = 'offset')
585 {
586         if (! is_array($areas) || ! is_array($array)) return;
587
588         $areas_keys = array_keys($areas);
589         foreach(array_keys($array) as $u_index) {
590                 $offset = isset($array[$u_index][$o_key]) ?
591                         intval($array[$u_index][$o_key]) : 0;
592                 foreach($areas_keys as $a_index) {
593                         if (isset($array[$u_index][$a_key])) {
594                                 $offset_s = intval($areas[$a_index][0]);
595                                 $offset_e = intval($areas[$a_index][1]);
596                                 // [Area => inside <= Area]
597                                 if ($offset_s < $offset && $offset < $offset_e) {
598                                         $array[$u_index][$a_key] += $belief;
599                                 }
600                         }
601                 }
602         }
603 }
604
605
606 // ---------------------
607 // Spam-uri pickup
608
609 // Preprocess: Removing uninterest part for URI detection
610 function spam_uri_removing_hocus_pocus($binary = '', $method = array())
611 {
612         $length = 4 ; // 'http'(1) and '://'(2) and 'fqdn'(1)
613         if (is_array($method)) {
614                 // '<a'(2) or 'href='(5) or '>'(1) or '</a>'(4)
615                 // '[uri'(4) or ']'(1) or '[/uri]'(6) 
616                 if (isset($method['area_anchor']) || isset($method['uri_anchor']) ||
617                     isset($method['area_bbcode']) || isset($method['uri_bbcode']))
618                                 $length = 1;    // Seems not effective
619         }
620
621         // Removing sequential spaces and too short lines
622         $binary = strings($binary, $length, TRUE, FALSE); // Multibyte NOT needed
623
624         // Remove words (has no '<>[]:') between spaces
625         $binary = preg_replace('/[ \t][\w.,()\ \t]+[ \t]/', ' ', $binary);
626
627         return $binary;
628 }
629
630 // Preprocess: Domain exposure callback (See spam_uri_pickup_preprocess())
631 // http://victim.example.org/?foo+site:nasty.example.com+bar
632 // => http://nasty.example.com/?refer=victim.example.org
633 // NOTE: 'refer=' is not so good for (at this time).
634 // Consider about using IP address of the victim, try to avoid that.
635 function _preg_replace_callback_domain_exposure($matches = array())
636 {
637         $result = '';
638
639         // Preserve the victim URI as a complicity or ...
640         if (isset($matches[5])) {
641                 $result =
642                         $matches[1] . '://' .   // scheme
643                         $matches[2] . '/' .             // victim.example.org
644                         $matches[3];                    // The rest of all (before victim)
645         }
646
647         // Flipped URI
648         if (isset($matches[4])) {
649                 $result = 
650                         $matches[1] . '://' .   // scheme
651                         $matches[4] .                   // nasty.example.com
652                         '/?refer=' . strtolower($matches[2]) .  // victim.example.org
653                         ' ' . $result;
654         }
655
656         return $result;
657 }
658
659 // Preprocess: minor-rawurldecode() and adding space(s) and something
660 // to detect/count some URIs _if possible_
661 // NOTE: It's maybe danger to var_dump(result). [e.g. 'javascript:']
662 // [OK] http://victim.example.org/?site:nasty.example.org
663 // [OK] http://victim.example.org/nasty.example.org
664 // [OK] http://victim.example.org/go?http%3A%2F%2Fnasty.example.org
665 // [OK] http://victim.example.org/http://nasty.example.org
666 function spam_uri_pickup_preprocess($string = '', $method = array())
667 {
668         if (! is_string($string)) return '';
669
670         // rawurldecode(), just to catch encoded 'http://path/to/file', not to change '%20' to ' '
671         $string = strtr(
672                 $string,
673                 array(
674                         '%3A' => ':',
675                         '%3a' => ':',
676                         '%2F' => '/',
677                         '%2f' => '/',
678                         '%5C' => '\\',
679                         '%5c' => '\\',
680                 )
681         );
682
683         $string = spam_uri_removing_hocus_pocus($string, $method);
684         //var_dump(htmlspecialchars($string));
685
686         // Domain exposure (simple)
687         // http://victim.example.org/nasty.example.org/path#frag
688         // => http://nasty.example.org/?refer=victim.example.org and original
689         $string = preg_replace(
690                 '#h?ttp://' .
691                 '(' .
692                         'ime\.(?:nu|st)/' . '|' .       // 2ch.net
693                         'link\.toolbot\.com/' . '|' .
694                         'urlx\.org/' . '|' .
695                         'big5.51job.com/gate/big5/'      . '|' .
696                         'big5.china.com/gate/big5/'      . '|' .
697                         'big5.xinhuanet.com/gate/big5/' . '|' .
698                         'bhomiyo.com/en.xliterate/' . '|' .
699                         'google.com/translate_c\?u=(?:http://)?' . '|' .
700                         'web.archive.org/web/2[^/]*/(?:http://)?' .
701                 ')' .
702                 '([a-z0-9.%_-]+\.[a-z0-9.%_-]+)' .      // nasty.example.org
703                 '#i',
704                 'http://$2/?refer=$1 $0',                       // Preserve $0 or remove?
705                 $string
706         );
707
708         // Domain exposure (site:) See _preg_replace_callback_domain_exposure()
709         $string = preg_replace_callback(
710                 array(
711                         '#(h?ttp)://' . // 1:Scheme
712                         // 2:Host
713                         '(' .
714                                 '(?:[a-z0-9_.-]+\.)?[a-z0-9_-]+\.[a-z0-9_-]+' .
715                                 // Something Google: http://www.google.com/supported_domains
716                                 // AltaVista: http://es.altavista.com/web/results?q=site%3Anasty.example.org+foobar
717                                 // Live Search: search.live.com
718                                 // MySpace: http://sads.myspace.com/Modules/Search/Pages/Search.aspx?_snip_&searchString=site:nasty.example.org
719                                 // (also searchresults.myspace.com)
720                                 // alltheweb.com
721                                 // search.bbc.co.uk
722                                 // search.orange.co.uk
723                                 // ...
724                         ')' .
725                         '/' .
726                         '([a-z0-9?=&.%_/\'\\\+-]+)' .                           // 3:path/?query=foo+bar+
727                         '(?:\b|%20)site:([a-z0-9.%_-]+\.[a-z0-9.%_-]+)' .       // 4:site:nasty.example.com
728                         '()' .                                                                          // 5:Preserve or remove?
729                         '#i',
730                 ),
731                 '_preg_replace_callback_domain_exposure',
732                 $string
733         );
734
735         // URI exposure (uriuri => uri uri)
736         $string = preg_replace(
737                 array(
738                         '#(?<! )(?:https?|ftp):/#i',
739                 //      '#[a-z][a-z0-9.+-]{1,8}://#i',
740                 //      '#[a-z][a-z0-9.+-]{1,8}://#i'
741                 ),
742                 ' $0',
743                 $string
744         );
745
746         return $string;
747 }
748
749 // Main function of spam-uri pickup,
750 // A wrapper function of uri_pickup()
751 function spam_uri_pickup($string = '', $method = array())
752 {
753         if (! is_array($method) || empty($method)) {
754                 $method = check_uri_spam_method();
755         }
756
757         $string = spam_uri_pickup_preprocess($string, $method);
758
759         $array  = uri_pickup($string);
760
761         // Area elevation of URIs, for '(especially external)link' intension
762         if (! empty($array)) {
763                 $_method = array();
764                 if (isset($method['uri_anchor'])) $_method['uri_anchor'] = & $method['uri_anchor'];
765                 if (isset($method['uri_bbcode'])) $_method['uri_bbcode'] = & $method['uri_bbcode'];
766                 $areas = area_pickup($string, $_method, TRUE);
767                 if (! empty($areas)) {
768                         $area_shadow = array();
769                         foreach (array_keys($array) as $key) {
770                                 $area_shadow[$key] = & $array[$key]['area'];
771                                 foreach (array_keys($_method) as $_key) {
772                                         $area_shadow[$key][$_key] = 0;
773                                 }
774                         }
775                         foreach (array_keys($_method) as $_key) {
776                                 if (isset($areas[$_key])) {
777                                         area_measure($areas[$_key], $area_shadow, 1, $_key);
778                                 }
779                         }
780                 }
781         }
782
783         // Remove 'offset's for area_measure()
784         foreach(array_keys($array) as $key)
785                 unset($array[$key]['area']['offset']);
786
787         return $array;
788 }
789
790 ?>