1 rizwank 1.1 #!/usr/bin/perl
2 #-----------------------------------------------------------------------------
3 # GeoIp_City_Maxmind AWStats plugin
4 # This plugin allow you to add a city report.
5 # Need the licensed city database from Maxmind.
6 #-----------------------------------------------------------------------------
7 # Perl Required Modules: Geo::IP (Geo::IP::PurePerl is not yet supported)
8 #-----------------------------------------------------------------------------
9 # $Revision: 1.8 $ - $Author: eldy $ - $Date: 2005/01/15 01:07:29 $
10
11
12 # <-----
13 # ENTER HERE THE USE COMMAND FOR ALL REQUIRED PERL MODULES
14 if (!eval ('require "Geo/IP.pm";')) {
15 return $@?"Error: $@":"Error: Need Perl module Geo::IP (Geo::IP::PurePerl is not yet supported)";
16 }
17 # ----->
18 use strict;no strict "refs";
19
20
21
22 rizwank 1.1 #-----------------------------------------------------------------------------
23 # PLUGIN VARIABLES
24 #-----------------------------------------------------------------------------
25 # <-----
26 # ENTER HERE THE MINIMUM AWSTATS VERSION REQUIRED BY YOUR PLUGIN
27 # AND THE NAME OF ALL FUNCTIONS THE PLUGIN MANAGE.
28 my $PluginNeedAWStatsVersion="6.2";
29 my $PluginHooksFunctions="AddHTMLMenuLink AddHTMLGraph ShowInfoHost SectionInitHashArray SectionProcessIp SectionProcessHostname SectionReadHistory SectionWriteHistory";
30 # ----->
31
32 # <-----
33 # IF YOUR PLUGIN NEED GLOBAL VARIABLES, THEY MUST BE DECLARED HERE.
34 use vars qw/
35 $geoip_city_maxmind
36 %_city_p
37 %_city_h
38 %_city_k
39 %_city_l
40 %_region_p
41 %_region_h
42 %_region_k
43 rizwank 1.1 %_region_l
44 $MAXNBOFSECTIONGIR
45 %region
46 /;
47 my %countrylib=('ca'=>'Canadian Regions','us'=>'US regions');
48 my %regca=(
49 'AB',"Alberta",
50 'BC',"British Columbia",
51 'MB',"Manitoba",
52 'NB',"New Brunswick",
53 'NF',"Newfoundland",
54 'NS',"Nova Scotia",
55 'NU',"Nunavut",
56 'ON',"Ontario",
57 'PE',"Prince Edward Island",
58 'QC',"Quebec",
59 'SK',"Saskatchewan",
60 'NT',"Northwest Territories",
61 'YT',"Yukon Territory"
62 );
63 my %regus=(
64 rizwank 1.1 'AA',"Armed Forces Americas",
65 'AE',"Armed Forces Europe, Middle East, & Canada",
66 'AK',"Alaska",
67 'AL',"Alabama",
68 'AP',"Armed Forces Pacific",
69 'AR',"Arkansas",
70 'AS',"American Samoa",
71 'AZ',"Arizona",
72 'CA',"California",
73 'CO',"Colorado",
74 'CT',"Connecticut",
75 'DC',"District of Columbia",
76 'DE',"Delaware",
77 'FL',"Florida",
78 'FM',"Federated States of Micronesia",
79 'GA',"Georgia",
80 'GU',"Guam",
81 'HI',"Hawaii",
82 'IA',"Iowa",
83 'ID',"Idaho",
84 'IL',"Illinois",
85 rizwank 1.1 'IN',"Indiana",
86 'KS',"Kansas",
87 'KY',"Kentucky",
88 'LA',"Louisiana",
89 'MA',"Massachusetts",
90 'MD',"Maryland",
91 'ME',"Maine",
92 'MH',"Marshall Islands",
93 'MI',"Michigan",
94 'MN',"Minnesota",
95 'MO',"Missouri",
96 'MP',"Northern Mariana Islands",
97 'MS',"Mississippi",
98 'MT',"Montana",
99 'NC',"North Carolina",
100 'ND',"North Dakota",
101 'NE',"Nebraska",
102 'NH',"New Hampshire",
103 'NJ',"New Jersey",
104 'NM',"New Mexico",
105 'NV',"Nevada",
106 rizwank 1.1 'NY',"New York",
107 'OH',"Ohio",
108 'OK',"Oklahoma",
109 'OR',"Oregon",
110 'PA',"Pennsylvania",
111 'PR',"Puerto Rico",
112 'PW',"Palau",
113 'RI',"Rhode Island",
114 'SC',"South Carolina",
115 'SD',"South Dakota",
116 'TN',"Tennessee",
117 'TX',"Texas",
118 'UT',"Utah",
119 'VA',"Virginia",
120 'VI',"Virgin Islands",
121 'VT',"Vermont",
122 'WA',"Washington",
123 'WV',"West Virginia",
124 'WI',"Wisconsin",
125 'WY',"Wyoming"
126 );
127 rizwank 1.1 my %region=(
128 'ca'=>\%regca,
129 'us'=>\%regus
130 );
131 # ----->
132
133
134 #-----------------------------------------------------------------------------
135 # PLUGIN FUNCTION: Init_pluginname
136 #-----------------------------------------------------------------------------
137 sub Init_geoip_city_maxmind {
138 my $InitParams=shift;
139 my $checkversion=&Check_Plugin_Version($PluginNeedAWStatsVersion);
140 $MAXNBOFSECTIONGIR=10;
141
142 # <-----
143 # ENTER HERE CODE TO DO INIT PLUGIN ACTIONS
144 debug(" Plugin geoip_city_maxmind: InitParams=$InitParams",1);
145 # if ($UpdateStats) {
146 my ($mode,$datafile)=split(/\s+/,$InitParams,2);
147 if (! $datafile) { $datafile="GeoIPCity.dat"; }
148 rizwank 1.1 if ($mode eq '' || $mode eq 'GEOIP_MEMORY_CACHE') { $mode=Geo::IP::GEOIP_MEMORY_CACHE(); }
149 else { $mode=Geo::IP::GEOIP_STANDARD(); }
150 debug(" Plugin geoip_city_maxmind: GeoIP initialized in mode $mode",1);
151 $geoip_city_maxmind = Geo::IP->open($datafile, $mode);
152 # }
153 # ----->
154
155 return ($checkversion?$checkversion:"$PluginHooksFunctions");
156 }
157
158
159 #-----------------------------------------------------------------------------
160 # PLUGIN FUNCTION: AddHTMLMenuLink_pluginname
161 # UNIQUE: NO (Several plugins using this function can be loaded)
162 #-----------------------------------------------------------------------------
163 sub AddHTMLMenuLink_geoip_city_maxmind {
164 my $categ=$_[0];
165 my $menu=$_[1];
166 my $menulink=$_[2];
167 my $menutext=$_[3];
168 # <-----
169 rizwank 1.1 if ($Debug) { debug(" Plugin geoip_city_maxmind: AddHTMLMenuLink"); }
170 if ($categ eq 'who') {
171 $menu->{'plugin_geoip_city_maxmind'}=2.2; # Pos
172 $menulink->{'plugin_geoip_city_maxmind'}=2; # Type of link
173 $menutext->{'plugin_geoip_city_maxmind'}="Cities"; # Text
174 }
175 # ----->
176 return 0;
177 }
178
179
180 #-----------------------------------------------------------------------------
181 # PLUGIN FUNCTION: AddHTMLGraph_pluginname
182 # UNIQUE: NO (Several plugins using this function can be loaded)
183 #-----------------------------------------------------------------------------
184 sub AddHTMLGraph_geoip_city_maxmind {
185 my $categ=$_[0];
186 my $menu=$_[1];
187 my $menulink=$_[2];
188 my $menutext=$_[3];
189 # <-----
190 rizwank 1.1 my $ShowCities='H';
191 $MinHit{'Cities'}=1;
192 my $total_p; my $total_h; my $total_k;
193 my $rest_p; my $rest_h; my $rest_k;
194
195 if ($Debug) { debug(" Plugin geoip_city_maxmind: AddHTMLGraph $categ $menu $menulink $menutext"); }
196 my $title='Cities';
197 &tab_head("$title",19,0,'cities');
198 print "<tr bgcolor=\"#$color_TableBGRowTitle\"><th colspan=\"2\">Cities : ".((scalar keys %_city_h)-($_city_h{'unknown'}?1:0))."</th>";
199 if ($ShowCities =~ /P/i) { print "<th bgcolor=\"#$color_p\" width=\"80\">$Message[56]</th>"; }
200 if ($ShowCities =~ /P/i) { print "<th bgcolor=\"#$color_p\" width=\"80\">$Message[15]</th>"; }
201 if ($ShowCities =~ /H/i) { print "<th bgcolor=\"#$color_h\" width=\"80\">$Message[57]</th>"; }
202 if ($ShowCities =~ /H/i) { print "<th bgcolor=\"#$color_h\" width=\"80\">$Message[15]</th>"; }
203 if ($ShowCities =~ /B/i) { print "<th bgcolor=\"#$color_k\" width=\"80\">$Message[75]</th>"; }
204 if ($ShowCities =~ /L/i) { print "<th width=\"120\">$Message[9]</th>"; }
205 print "</tr>\n";
206 $total_p=$total_h=$total_k=0;
207 my $count=0;
208 &BuildKeyList($MaxRowsInHTMLOutput,$MinHit{'Cities'},\%_city_h,\%_city_h);
209 # Group by country
210 # my @countrylist=('ca','us');
211 rizwank 1.1 # foreach my $country (@countrylist) {
212 # print "<tr>";
213 # print "<td class=\"aws\"><b>".$countrylib{$country}."</b></td>";
214 # if ($ShowCities =~ /P/i) { print "<td> </td>"; }
215 # if ($ShowCities =~ /P/i) { print "<td> </td>"; }
216 # if ($ShowCities =~ /H/i) { print "<td> </td>"; }
217 # if ($ShowCities =~ /H/i) { print "<td> </td>"; }
218 # if ($ShowCities =~ /B/i) { print "<td> </td>"; }
219 # if ($ShowCities =~ /L/i) { print "<td> </td>"; }
220 # print "</tr>\n";
221 foreach my $key (@keylist) {
222 if ($key eq 'unknown') { next; }
223 my ($countrycode,$city)=split('_',$key,2);
224 $city=~tr/_/ /;
225 # if ($countrycode ne $country) { next; }
226 my $p_p; my $p_h;
227 if ($TotalPages) { $p_p=int($_city_p{$key}/$TotalPages*1000)/10; }
228 if ($TotalHits) { $p_h=int($_city_h{$key}/$TotalHits*1000)/10; }
229 print "<tr>";
230 print "<td class=\"aws\">".$DomainsHashIDLib{$countrycode}."</td>";
231 print "<td class=\"aws\">".ucfirst($city)."</td>";
232 rizwank 1.1 if ($ShowCities =~ /P/i) { print "<td>".($_city_p{$key}?$_city_p{$key}:" ")."</td>"; }
233 if ($ShowCities =~ /P/i) { print "<td>".($_city_p{$key}?"$p_p %":' ')."</td>"; }
234 if ($ShowCities =~ /H/i) { print "<td>".($_city_h{$key}?$_city_h{$key}:" ")."</td>"; }
235 if ($ShowCities =~ /H/i) { print "<td>".($_city_h{$key}?"$p_h %":' ')."</td>"; }
236 if ($ShowCities =~ /B/i) { print "<td>".Format_Bytes($_city_k{$key})."</td>"; }
237 if ($ShowCities =~ /L/i) { print "<td>".($_city_p{$key}?Format_Date($_city_l{$key},1):'-')."</td>"; }
238 print "</tr>\n";
239 $total_p += $_city_p{$key}||0;
240 $total_h += $_city_h{$key};
241 $total_k += $_city_k{$key}||0;
242 $count++;
243 }
244 # }
245 if ($Debug) { debug("Total real / shown : $TotalPages / $total_p - $TotalHits / $total_h - $TotalBytes / $total_h",2); }
246 $rest_p=0;
247 $rest_h=$TotalHits-$total_h;
248 $rest_k=0;
249 if ($rest_p > 0 || $rest_h > 0 || $rest_k > 0) { # All other cities
250 # print "<tr>";
251 # print "<td class=\"aws\"> </td>";
252 # if ($ShowCities =~ /P/i) { print "<td> </td>"; }
253 rizwank 1.1 # if ($ShowCities =~ /P/i) { print "<td> </td>"; }
254 # if ($ShowCities =~ /H/i) { print "<td> </td>"; }
255 # if ($ShowCities =~ /H/i) { print "<td> </td>"; }
256 # if ($ShowCities =~ /B/i) { print "<td> </td>"; }
257 # if ($ShowCities =~ /L/i) { print "<td> </td>"; }
258 # print "</tr>\n";
259
260 my $p_p; my $p_h;
261 if ($TotalPages) { $p_p=int($rest_p/$TotalPages*1000)/10; }
262 if ($TotalHits) { $p_h=int($rest_h/$TotalHits*1000)/10; }
263 print "<tr>";
264 print "<td class=\"aws\" colspan=\"2\"><span style=\"color: #$color_other\">$Message[2]/$Message[0]</span></td>";
265 if ($ShowCities =~ /P/i) { print "<td>".($rest_p?$rest_p:" ")."</td>"; }
266 if ($ShowCities =~ /P/i) { print "<td>".($rest_p?"$p_p %":' ')."</td>"; }
267 if ($ShowCities =~ /H/i) { print "<td>".($rest_h?$rest_h:" ")."</td>"; }
268 if ($ShowCities =~ /H/i) { print "<td>".($rest_h?"$p_h %":' ')."</td>"; }
269 if ($ShowCities =~ /B/i) { print "<td>".Format_Bytes($rest_k)."</td>"; }
270 if ($ShowCities =~ /L/i) { print "<td> </td>"; }
271 print "</tr>\n";
272 }
273 &tab_end();
274 rizwank 1.1
275 # ----->
276 return 0;
277 }
278
279
280 #-----------------------------------------------------------------------------
281 # PLUGIN FUNCTION: ShowInfoHost_pluginname
282 # UNIQUE: NO (Several plugins using this function can be loaded)
283 # Function called to add additionnal columns to the Hosts report.
284 # This function is called when building rows of the report (One call for each
285 # row). So it allows you to add a column in report, for example with code :
286 # print "<TD>This is a new cell for $param</TD>";
287 # Parameters: Host name or ip
288 #-----------------------------------------------------------------------------
289 sub ShowInfoHost_geoip_city_maxmind {
290 my $param="$_[0]";
291 # <-----
292 if ($param eq '__title__') {
293 my $NewLinkParams=${QueryString};
294 $NewLinkParams =~ s/(^|&)update(=\w*|$)//i;
295 rizwank 1.1 $NewLinkParams =~ s/(^|&)output(=\w*|$)//i;
296 $NewLinkParams =~ s/(^|&)staticlinks(=\w*|$)//i;
297 $NewLinkParams =~ s/(^|&)framename=[^&]*//i;
298 my $NewLinkTarget='';
299 if ($DetailedReportsOnNewWindows) { $NewLinkTarget=" target=\"awstatsbis\""; }
300 if (($FrameName eq 'mainleft' || $FrameName eq 'mainright') && $DetailedReportsOnNewWindows < 2) {
301 $NewLinkParams.="&framename=mainright";
302 $NewLinkTarget=" target=\"mainright\"";
303 }
304 $NewLinkParams =~ tr/&/&/s; $NewLinkParams =~ s/^&//; $NewLinkParams =~ s/&$//;
305 if ($NewLinkParams) { $NewLinkParams="${NewLinkParams}&"; }
306
307 print "<th width=\"80\">";
308 print "<a href=\"".($ENV{'GATEWAY_INTERFACE'} || !$StaticLinks?XMLEncode("$AWScript?${NewLinkParams}output=plugin_geoip_city_maxmind"):"$PROG$StaticLinks.plugin_geoip_city_maxmind.$StaticExt")."\"$NewLinkTarget>GeoIP<br>City</a>";
309 print "</th>";
310 }
311 elsif ($param) {
312 my $ip=0;
313 my $key;
314 if ($param =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { # IPv4 address
315 $ip=4;
316 rizwank 1.1 $key=$param;
317 }
318 elsif ($param =~ /^[0-9A-F]*:/i) { # IPv6 address
319 $ip=6;
320 $key=$param;
321 }
322 print "<td>";
323 if ($key && $ip==4) {
324 my $record=();
325 $record=$geoip_city_maxmind->record_by_addr($param) if $geoip_city_maxmind;
326 if ($Debug) { debug(" Plugin geoip_city_maxmind: GetCityByIp for $param: [$record]",5); }
327 my $city;
328 $city=$record->city if $record;
329 if ($city) { print "$city"; }
330 else { print "<span style=\"color: #$color_other\">$Message[0]</span>"; }
331 }
332 if ($key && $ip==6) {
333 print "<span style=\"color: #$color_other\">$Message[0]</span>";
334 }
335 if (! $key) {
336 my $record=();
337 rizwank 1.1 $record=$geoip_city_maxmind->record_by_name($param) if $geoip_city_maxmind;
338 if ($Debug) { debug(" Plugin geoip_city_maxmind: GetCityByHostname for $param: [$record]",5); }
339 my $city;
340 $city=$record->city if $record;
341 if ($city) { print "$city"; }
342 else { print "<span style=\"color: #$color_other\">$Message[0]</span>"; }
343 }
344 print "</td>";
345 }
346 else {
347 print "<td> </td>";
348 }
349 return 1;
350 # ----->
351 }
352
353
354 #-----------------------------------------------------------------------------
355 # PLUGIN FUNCTION: SectionInitHashArray_pluginname
356 # UNIQUE: NO (Several plugins using this function can be loaded)
357 #-----------------------------------------------------------------------------
358 rizwank 1.1 sub SectionInitHashArray_geoip_city_maxmind {
359 my $param="$_[0]";
360 # <-----
361 if ($Debug) { debug(" Plugin geoip_city_maxmind: Init_HashArray"); }
362 %_city_p = %_city_h = %_city_k = %_city_l =();
363 # ----->
364 return 0;
365 }
366
367
368 #-----------------------------------------------------------------------------
369 # PLUGIN FUNCTION: SectionProcessIP_pluginname
370 # UNIQUE: NO (Several plugins using this function can be loaded)
371 #-----------------------------------------------------------------------------
372 sub SectionProcessIp_geoip_city_maxmind {
373 my $param="$_[0]"; # Param must be an IP
374 # <-----
375 my $record=();
376 $record=$geoip_city_maxmind->record_by_addr($param) if $geoip_city_maxmind;
377 if ($Debug) { debug(" Plugin geoip_city_maxmind: GetCityByIp for $param: [$record]",5); }
378 my $city=$record->city;
379 rizwank 1.1 # if ($PageBool) { $_city_p{$city}++; }
380 if ($city) {
381 my $countrycity=lc(($record->country_code)."_".$city);
382 $countrycity=~tr/ /_/;
383 $_city_h{$countrycity}++;
384 } else {
385 $_city_h{'unknown'}++;
386 }
387 # if ($timerecord > $_city_l{$city}) { $_city_l{$city}=$timerecord; }
388 # ----->
389 return;
390 }
391
392
393 #-----------------------------------------------------------------------------
394 # PLUGIN FUNCTION: SectionProcessHostname_pluginname
395 # UNIQUE: NO (Several plugins using this function can be loaded)
396 #-----------------------------------------------------------------------------
397 sub SectionProcessHostname_geoip_city_maxmind {
398 my $param="$_[0]"; # Param must be an IP
399 # <-----
400 rizwank 1.1 my $record=();
401 $record=$geoip_city_maxmind->record_by_name($param) if $geoip_city_maxmind;
402 if ($Debug) { debug(" Plugin geoip_city_maxmind: GetCityByName for $param: [$record]",5); }
403 my $city=$record->city;
404 # if ($PageBool) { $_city_p{$city}++; }
405 if ($city) {
406 my $countrycity=lc(($record->country_code)."_".$city);
407 $countrycity=~tr/ /_/;
408 $_city_h{$countrycity}++;
409 } else {
410 $_city_h{'unknown'}++;
411 }
412 # if ($timerecord > $_city_l{$city}) { $_city_l{$city}=$timerecord; }
413 # ----->
414 return;
415 }
416
417
418 #-----------------------------------------------------------------------------
419 # PLUGIN FUNCTION: SectionReadHistory_pluginname
420 # UNIQUE: NO (Several plugins using this function can be loaded)
421 rizwank 1.1 #-----------------------------------------------------------------------------
422 sub SectionReadHistory_geoip_city_maxmind {
423 my $issectiontoload=shift;
424 my $xmlold=shift;
425 my $xmleb=shift;
426 my $countlines=shift;
427 # <-----
428 if ($Debug) { debug(" Plugin geoip_city_maxmind: Begin of PLUGIN_geoip_city_maxmind section"); }
429 my @field=();
430 my $count=0;my $countloaded=0;
431 do {
432 if ($field[0]) {
433 $count++;
434 if ($issectiontoload) {
435 $countloaded++;
436 if ($field[2]) { $_city_h{$field[0]}+=$field[2]; }
437 }
438 }
439 $_=<HISTORY>;
440 chomp $_; s/\r//;
441 @field=split(/\s+/,($xmlold?CleanFromTags($_):$_));
442 rizwank 1.1 $countlines++;
443 }
444 until ($field[0] eq 'END_PLUGIN_geoip_city_maxmind' || $field[0] eq "${xmleb}END_PLUGIN_geoip_city_maxmind" || ! $_);
445 if ($field[0] ne 'END_PLUGIN_geoip_city_maxmind' && $field[0] ne "${xmleb}END_PLUGIN_geoip_city_maxmind") { error("History file is corrupted (End of section PLUGIN not found).\nRestore a recent backup of this file (data for this month will be restored to backup date), remove it (data for month will be lost), or remove the corrupted section in file (data for at least this section will be lost).","","",1); }
446 if ($Debug) { debug(" Plugin geoip_city_maxmind: End of PLUGIN_geoip_city_maxmind section ($count entries, $countloaded loaded)"); }
447 # ----->
448 return 0;
449 }
450
451 #-----------------------------------------------------------------------------
452 # PLUGIN FUNCTION: SectionWriteHistory_pluginname
453 # UNIQUE: NO (Several plugins using this function can be loaded)
454 #-----------------------------------------------------------------------------
455 sub SectionWriteHistory_geoip_city_maxmind {
456 my ($xml,$xmlbb,$xmlbs,$xmlbe,$xmlrb,$xmlrs,$xmlre,$xmleb,$xmlee)=(shift,shift,shift,shift,shift,shift,shift,shift,shift);
457 if ($Debug) { debug(" Plugin geoip_city_maxmind: SectionWriteHistory_geoip_city_maxmind start - ".(scalar keys %_city_h)); }
458 # <-----
459 print HISTORYTMP "\n";
460 if ($xml) { print HISTORYTMP "<section id='plugin_geoip_city_maxmind'><sortfor>$MAXNBOFSECTIONGIR</sortfor><comment>\n"; }
461 print HISTORYTMP "# Plugin key - Pages - Hits - Bandwidth - Last access\n";
462 #print HISTORYTMP "# The $MaxNbOfExtra[$extranum] first number of hits are first\n";
463 rizwank 1.1 $ValueInFile{'plugin_geoip_city_maxmind'}=tell HISTORYTMP;
464 print HISTORYTMP "${xmlbb}BEGIN_PLUGIN_geoip_city_maxmind${xmlbs}".(scalar keys %_city_h)."${xmlbe}\n";
465 &BuildKeyList($MAXNBOFSECTIONGIR,1,\%_city_h,\%_city_h);
466 my %keysinkeylist=();
467 foreach (@keylist) {
468 $keysinkeylist{$_}=1;
469 #my $page=$_city_p{$_}||0;
470 #my $bytes=$_city_k{$_}||0;
471 #my $lastaccess=$_city_l{$_}||'';
472 print HISTORYTMP "${xmlrb}$_${xmlrs}0${xmlrs}", $_city_h{$_}, "${xmlrs}0${xmlrs}0${xmlre}\n"; next;
473 }
474 foreach (keys %_city_h) {
475 if ($keysinkeylist{$_}) { next; }
476 #my $page=$_city_p{$_}||0;
477 #my $bytes=$_city_k{$_}||0;
478 #my $lastaccess=$_city_l{$_}||'';
479 print HISTORYTMP "${xmlrb}$_${xmlrs}0${xmlrs}", $_city_h{$_}, "${xmlrs}0${xmlrs}0${xmlre}\n"; next;
480 }
481 print HISTORYTMP "${xmleb}END_PLUGIN_geoip_city_maxmind${xmlee}\n";
482 # ----->
483 return 0;
484 rizwank 1.1 }
485
486
487
488 1; # Do not remove this line
|