1#!/usr/local/bin/perl
2# extended geoip.pm by Sven Strickroth <email@cs-ware.de>
3#-----------------------------------------------------------------------------
4# GeoIP Maxmind AWStats plugin with IPv6 support
5# This plugin allow you to get country report with countries detected
6# from a Geographical database (GeoIP internal database) instead of domain
7# hostname suffix.
8# This works with IPv4 and also IPv6
9# Needs the IPv6 country database from Maxmind (free).
10#-----------------------------------------------------------------------------
11# Perl Required Modules: Geo::IP (Geo::IP::PurePerl does not support IPv6 yet)
12#-----------------------------------------------------------------------------
13
14# <-----
15# ENTER HERE THE USE COMMAND FOR ALL REQUIRED PERL MODULES
16use vars qw/ $type /;
17$type='geoip';
18if (!eval ('require "Geo/IP.pm";')) {
19	$error1=$@;
20#	$type='geoippureperl';
21#	if (!eval ('require "Geo/IP/PurePerl.pm";')) {
22#		$error2=$@;
23#		$ret=($error1||$error2)?"Error:\n$error1$error2":"";
24		$ret.="Error: Need Perl module Geo::IP";
25		return $ret;
26#	}
27} else {
28        Geo::IP->VERSION >= 1.40 || die("Requires Geo/IP.pm >= 1.40");
29}
30# ----->
31#use strict;
32no strict "refs";
33
34
35
36#-----------------------------------------------------------------------------
37# PLUGIN VARIABLES
38#-----------------------------------------------------------------------------
39# <-----
40# ENTER HERE THE MINIMUM AWSTATS VERSION REQUIRED BY YOUR PLUGIN
41# AND THE NAME OF ALL FUNCTIONS THE PLUGIN MANAGE.
42my $PluginNeedAWStatsVersion="5.4";
43my $PluginHooksFunctions="GetCountryCodeByAddr GetCountryCodeByName ShowInfoHost";
44my $PluginName = "geoip6";
45my $LoadedOverride=0;
46my $OverrideFile="";
47my %TmpDomainLookup;
48# ----->
49
50# <-----
51# IF YOUR PLUGIN NEED GLOBAL VARIABLES, THEY MUST BE DECLARED HERE.
52use vars qw/
53$gi
54/;
55# ----->
56
57
58#-----------------------------------------------------------------------------
59# PLUGIN FUNCTION: Init_pluginname
60#-----------------------------------------------------------------------------
61sub Init_geoip6 {
62	my $InitParams=shift;
63	my $checkversion=&Check_Plugin_Version($PluginNeedAWStatsVersion);
64
65	# <-----
66	# ENTER HERE CODE TO DO INIT PLUGIN ACTIONS
67	debug(" Plugin $PluginName: InitParams=$InitParams",1);
68   	my ($mode,$tmpdatafile)=split(/\s+/,$InitParams,2);
69   	my ($datafile,$override)=split(/\+/,$tmpdatafile,2);
70   	if (! $datafile) { $datafile="$PluginName.dat"; }
71    else { $datafile =~ s/%20/ /g; }
72	if ($type eq 'geoippureperl') {
73		if ($mode eq '' || $mode eq 'GEOIP_MEMORY_CACHE')  { $mode=Geo::IP::PurePerl::GEOIP_MEMORY_CACHE(); }
74		else { $mode=Geo::IP::PurePerl::GEOIP_STANDARD(); }
75	} else {
76		if ($mode eq '' || $mode eq 'GEOIP_MEMORY_CACHE')  { $mode=Geo::IP::GEOIP_MEMORY_CACHE(); }
77		else { $mode=Geo::IP::GEOIP_STANDARD(); }
78	}
79	if ($override){$OverrideFile=$override;}
80	%TmpDomainLookup=();
81	debug(" Plugin $PluginName: GeoIP initialized type=$type mode=$mode override=$override",1);
82	if ($type eq 'geoippureperl') {
83		$gi = Geo::IP::PurePerl->open($datafile, $mode);
84	} else {
85		$gi = Geo::IP->open($datafile, $mode);
86	}
87
88# Fails on some GeoIP version
89# 	debug(" Plugin geoip6: GeoIP initialized database_info=".$gi->database_info());
90	# ----->
91
92	return ($checkversion?$checkversion:"$PluginHooksFunctions");
93}
94
95
96#-----------------------------------------------------------------------------
97# PLUGIN FUNCTION: GetCountryCodeByAddr_pluginname
98# UNIQUE: YES (Only one plugin using this function can be loaded)
99# GetCountryCodeByAddr is called to translate an ip into a country code in lower case.
100#-----------------------------------------------------------------------------
101sub GetCountryCodeByAddr_geoip6 {
102    my $param="$_[0]";
103	# <-----
104	if (! $param) { return ''; }
105	my $searchkey;
106	if ($param =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { # IPv4 address
107	        $searchkey = '::ffff:'.$param;
108        } else {
109                $searchkey = $param;
110        }
111	my $res= TmpLookup_geoip6($param);
112	if (! $res) {
113		$res=lc($gi->country_code_by_addr_v6($searchkey)) || 'unknown';
114		$TmpDomainLookup{$param}=$res;
115 		if ($Debug) { debug("  Plugin $PluginName: GetCountryCodeByAddr for $searchkey: [$res]",5); }
116	}
117	elsif ($Debug) { debug("  Plugin $PluginName: GetCountryCodeByAddr for $param: Already resolved to [$res]",5); }
118	# ----->
119	return $res;
120}
121
122
123#-----------------------------------------------------------------------------
124# PLUGIN FUNCTION: GetCountryCodeByName_pluginname
125# UNIQUE: YES (Only one plugin using this function can be loaded)
126# GetCountryCodeByName is called to translate a host name into a country code in lower case.
127#-----------------------------------------------------------------------------
128sub GetCountryCodeByName_geoip6 {
129    my $param="$_[0]";
130	# <-----
131	if (! $param) { return ''; }
132	my $res = TmpLookup_geoip6($param);
133	if (! $res) {
134		$res=lc($gi->country_code_by_name_v6($param)) || 'unknown';
135		$TmpDomainLookup{$param}=$res;
136		if ($Debug) { debug("  Plugin $PluginName: GetCountryCodeByName for $param: [$res]",5); }
137	}
138	elsif ($Debug) { debug("  Plugin $PluginName: GetCountryCodeByName for $param: Already resolved to [$res]",5); }
139	# ----->
140	return $res;
141}
142
143
144#-----------------------------------------------------------------------------
145# PLUGIN FUNCTION: ShowInfoHost_pluginname
146# UNIQUE: NO (Several plugins using this function can be loaded)
147# Function called to add additionnal columns to the Hosts report.
148# This function is called when building rows of the report (One call for each
149# row). So it allows you to add a column in report, for example with code :
150#   print "<TD>This is a new cell for $param</TD>";
151# Parameters: Host name or ip
152#-----------------------------------------------------------------------------
153sub ShowInfoHost_geoip6 {
154    my $param="$_[0]";
155	# <-----
156	if ($param eq '__title__') {
157    	my $NewLinkParams=${QueryString};
158    	$NewLinkParams =~ s/(^|&)update(=\w*|$)//i;
159    	$NewLinkParams =~ s/(^|&)output(=\w*|$)//i;
160    	$NewLinkParams =~ s/(^|&)staticlinks(=\w*|$)//i;
161    	$NewLinkParams =~ s/(^|&)framename=[^&]*//i;
162    	my $NewLinkTarget='';
163    	if ($DetailedReportsOnNewWindows) { $NewLinkTarget=" target=\"awstatsbis\""; }
164    	if (($FrameName eq 'mainleft' || $FrameName eq 'mainright') && $DetailedReportsOnNewWindows < 2) {
165    		$NewLinkParams.="&framename=mainright";
166    		$NewLinkTarget=" target=\"mainright\"";
167    	}
168    	$NewLinkParams =~ tr/&/&/s; $NewLinkParams =~ s/^&//; $NewLinkParams =~ s/&$//;
169    	if ($NewLinkParams) { $NewLinkParams="${NewLinkParams}&"; }
170
171		print "<th width=\"80\">";
172        print "<a href=\"#countries\">GeoIP<br />Country</a>";
173        print "</th>";
174	}
175	elsif ($param) {
176        my $ip=0;
177		my $key;
178		if ($param =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {	# IPv4 address
179		    $ip=4;
180			$key='::ffff:'.$param;
181		}
182		elsif ($param =~ /^[0-9A-F]*:/i) {						# IPv6 address
183		    $ip=6;
184			$key=$param;
185		}
186		print "<td>";
187		if ($key) {
188			my $res = TmpLookup_geoip6($param);
189        	if (!$res && $gi) {
190        	        $res=lc($gi->country_code_by_addr_v6($key));
191                }
192        	if ($Debug) { debug("  Plugin $PluginName: GetCountryByIp for $key: [$res]",5); }
193		    if ($res) { print $DomainsHashIDLib{$res}?$DomainsHashIDLib{$res}:"<span style=\"color: #$color_other\">$Message[0]</span>"; }
194		    else { print "<span style=\"color: #$color_other\">$Message[0]</span>"; }
195		}
196		else {
197			my $res = TmpLookup_geoip6($param);
198        	if (!$res){$res=lc($gi->country_code_by_name_v6($param)) if $gi;}
199        	if ($Debug) { debug("  Plugin $PluginName: GetCountryByHostname for $param: [$res]",5); }
200		    if ($res) { print $DomainsHashIDLib{$res}?$DomainsHashIDLib{$res}:"<span style=\"color: #$color_other\">$Message[0]</span>"; }
201		    else { print "<span style=\"color: #$color_other\">$Message[0]</span>"; }
202		}
203		print "</td>";
204	}
205	else {
206		print "<td>&nbsp;</td>";
207	}
208	return 1;
209	# ----->
210}
211
212#-----------------------------------------------------------------------------
213# PLUGIN FUNCTION: LoadOverrideFile
214# Attempts to load a comma delimited file that will override the GeoIP database
215# Useful for Intranet records
216# CSV format: IP,2-char Country code
217#-----------------------------------------------------------------------------
218sub LoadOverrideFile_geoip6{
219	my $filetoload="";
220	if ($OverrideFile){
221		if (!open(GEOIPFILE, $OverrideFile)){
222			debug("Plugin $PluginName: Unable to open override file: $OverrideFile");
223			$LoadedOverride = 1;
224			return;
225		}
226	}else{
227		my $conf = (exists(&Get_Config_Name) ? Get_Config_Name() : $SiteConfig);
228		if ($conf && open(GEOIPFILE,"$DirData/$PluginName.$conf.txt"))	{ $filetoload="$DirData/$PluginName.$conf.txt"; }
229		elsif (open(GEOIPFILE,"$DirData/$PluginName.txt"))	{ $filetoload="$DirData/$PluginName.txt"; }
230		else { debug("No override file \"$DirData/$PluginName.txt\": $!"); }
231	}
232	if ($filetoload)
233	{
234		# This is the fastest way to load with regexp that I know
235		while (<GEOIPFILE>){
236			chomp $_;
237			s/\r//;
238			my @record = split(",", $_);
239			# replace quotes if they were used in the file
240			foreach (@record){ $_ =~ s/"//g; }
241			# store in hash
242			$TmpDomainLookup{$record[0]} = $record[1];
243		}
244		close GEOIPFILE;
245        debug(" Plugin $PluginName: Overload file loaded: ".(scalar keys %TmpDomainLookup)." entries found.");
246	}
247	$LoadedOverride = 1;
248	return;
249}
250
251#-----------------------------------------------------------------------------
252# PLUGIN FUNCTION: TmpLookup
253# Searches the temporary hash for the parameter value and returns the corresponding
254# GEOIP entry
255#-----------------------------------------------------------------------------
256sub TmpLookup_geoip6(){
257	$param = shift;
258	if (!$LoadedOverride){&LoadOverrideFile_geoip6();}
259	#my $val;
260	#if ($gi &&
261	#(($type eq 'geoip' && $gi->VERSION >= 1.30) ||
262	#  $type eq 'geoippureperl' && $gi->VERSION >= 1.17)){
263	#	$val = $TmpDomainLookup{$gi->get_ip_address($param)};
264	#}
265    #else {$val = $TmpDomainLookup{$param};}
266    #return $val || '';
267    return $TmpDomainLookup{$param}||'';
268}
269
2701;	# Do not remove this line
271