File Coverage

blib/lib/Geo/IPfree.pm
Criterion Covered Total %
statement 41 153 26.8
branch 3 48 6.2
condition 0 11 0.0
subroutine 9 16 56.2
pod 10 10 100.0
total 63 238 26.4


line stmt bran cond sub pod time code
1             package Geo::IPfree;
2 3     3   2597 use 5.006;
  3         13  
3 3     3   16 use strict;
  3         4  
  3         90  
4 3     3   23 use warnings;
  3         6  
  3         113  
5              
6 3     3   1321376 use Memoize;
  3         9834  
  3         301  
7 3     3   35 use Carp qw();
  3         6  
  3         10297  
8              
9             require Exporter;
10             our @ISA = qw(Exporter);
11              
12             our $VERSION = '1.151940';
13              
14             our @EXPORT = qw(LookUp LoadDB);
15             our @EXPORT_OK = @EXPORT;
16              
17             my $DEFAULT_DB = 'ipscountry.dat';
18             my $cache_expire = 5000;
19             my @baseX = (
20             0 .. 9,
21             'A' .. 'Z',
22             'a' .. 'z',
23             split( m{}, q(.,;'"`<>{}[]=+-~*@#%$&!?) )
24             );
25              
26             my ( %baseX, $base, $THIS, %countrys );
27              
28             {
29             my $c = 0;
30             %baseX = map { $_ => ( $c++ ) } @baseX;
31             $base = @baseX;
32              
33             my @data;
34             while ( ) {
35             last if m{^__END__};
36             chomp;
37             push @data, split m{ }, $_, 2;
38             }
39             %countrys = @data;
40             }
41              
42             sub new {
43 0     0 1 0 my ( $class, $db_file ) = @_;
44              
45 0 0 0     0 if ( !defined $_[ 0 ] || $_[ 0 ] !~ /^[\w:]+$/ ) {
46 0         0 $class = 'Geo::IPfree';
47 0         0 $db_file = $_[ 0 ];
48             }
49              
50 0         0 my $this = bless( {}, $class );
51              
52 0 0       0 if ( !defined $db_file ) { $db_file = _find_db_file(); }
  0         0  
53              
54 0         0 $this->LoadDB( $db_file );
55              
56 0         0 $this->Clean_Cache();
57 0         0 $this->{ cache } = 1;
58              
59 0         0 return $this;
60             }
61              
62             sub _find_db_file {
63             my @locations = (
64             qw(/usr/local/share /usr/local/share/GeoIPfree),
65 0     0   0 map { $_, "$_/Geo" } @INC
  0         0  
66             );
67              
68             # lastly, find where this module was loaded, and try that dir
69 0         0 my ( $lib ) = ( $INC{ 'Geo/IPfree.pm' } =~ /^(.*?)[\\\/]+[^\\\/]+$/gs );
70 0         0 push @locations, $lib;
71              
72 0         0 for my $file ( map { "$_/$DEFAULT_DB" } @locations ) {
  0         0  
73 0 0       0 return $file if -e $file;
74             }
75             }
76              
77             sub LoadDB {
78 0     0 1 0 my $this = shift;
79 0         0 my ( $db_file ) = @_;
80              
81 0 0       0 if ( -d $db_file ) { $db_file .= "/$DEFAULT_DB"; }
  0         0  
82              
83 0 0       0 if ( !-s $db_file ) {
84 0         0 Carp::croak( "Can't load database, blank or not there: $db_file" );
85             }
86              
87 0         0 my $buffer = '';
88 0 0       0 open( my $handler, '<', $db_file )
89             || Carp::croak( "Failed to open database file $db_file for read!" );
90 0         0 binmode( $handler );
91 0         0 $this->{ dbfile } = $db_file;
92              
93 0 0       0 delete $this->{ pos } if $this->{ pos };
94              
95 0         0 while ( read( $handler, $buffer, 1, length( $buffer ) ) ) {
96 0 0       0 if ( $buffer =~ /##headers##(\d+)##$/s ) {
    0          
97 0         0 my $headers;
98 0         0 read( $handler, $headers, $1 );
99 0         0 my ( %head ) = ( $headers =~ /(\d+)=(\d+)/gs );
100 0         0 $this->{ pos }{ $_ } = $head{ $_ } for keys %head;
101 0         0 $buffer = '';
102             }
103             elsif ( $buffer =~ /##start##$/s ) {
104 0         0 $this->{ start } = tell( $handler );
105 0         0 last;
106             }
107             }
108              
109 0         0 $this->{ searchorder } = [ sort { $a <=> $b } keys %{ $this->{ pos } } ];
  0         0  
  0         0  
110 0         0 $this->{ handler } = $handler;
111             }
112              
113             sub LookUp {
114 0     0 1 0 my $this;
115              
116 0 0       0 if ( $#_ == 0 ) {
117 0 0       0 if ( !$THIS ) { $THIS = Geo::IPfree->new(); }
  0         0  
118 0         0 $this = $THIS;
119             }
120 0         0 else { $this = shift; }
121              
122 0         0 my ( $ip ) = @_;
123              
124 0         0 $ip =~ s/\.+/\./gs;
125 0         0 $ip =~ s/^\.//;
126 0         0 $ip =~ s/\.$//;
127              
128 0 0       0 if ( $ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ ) {
129 0         0 $ip = nslookup( $ip );
130             }
131              
132 0 0       0 return unless length $ip;
133              
134             ## Since the last class is always from the same country, will try 0 and cache 0:
135 0         0 my $ip_class = $ip;
136 0         0 $ip_class =~ s/\.\d+$/\.0/;
137              
138 0 0 0     0 if ( $this->{ cache } && $this->{ CACHE }{ $ip_class } ) {
139 0         0 return ( @{ $this->{ CACHE }{ $ip_class } }, $ip_class );
  0         0  
140             }
141              
142 0         0 my $ipnb = ip2nb( $ip_class );
143              
144 0         0 my $buf_pos = 0;
145              
146 0         0 foreach my $Key ( @{ $this->{ searchorder } } ) {
  0         0  
147 0 0       0 if ( $ipnb <= $Key ) { $buf_pos = $this->{ pos }{ $Key }; last; }
  0         0  
  0         0  
148             }
149              
150 0         0 my ( $buffer, $country, $iprange );
151              
152             ## Will use the DB in the memory:
153 0 0       0 if ( $this->{ FASTER } ) {
154 0         0 while ( $buf_pos < $this->{ DB_SIZE } ) {
155 0         0 $buffer = substr( $this->{ DB }, $buf_pos, 7 );
156 0         0 $country = substr( $buffer, 0, 2 );
157 0         0 $iprange = baseX2dec( substr( $buffer, 2, 5 ) );
158 0         0 $buf_pos += 7;
159 0 0       0 last if $ipnb >= $iprange;
160             }
161             }
162             ## Will read the DB in the disk:
163             else {
164 0 0       0 seek( $this->{ handler }, 0, 0 )
165             if $] < 5.006001; ## Fix bug on Perl 5.6.0
166 0         0 seek( $this->{ handler }, $buf_pos + $this->{ start }, 0 );
167 0         0 while ( read( $this->{ handler }, $buffer, 7 ) ) {
168 0         0 $country = substr( $buffer, 0, 2 );
169 0         0 $iprange = baseX2dec( substr( $buffer, 2 ) );
170 0 0       0 last if $ipnb >= $iprange;
171             }
172             }
173              
174 0 0       0 if ( $this->{ cache } ) {
175 0 0       0 if( $this->{ CACHE_COUNT } > $cache_expire ) {
176 0         0 keys %{ $this->{ CACHE } };
  0         0  
177 0         0 my( $d_key ) = each( %{ $this->{ CACHE } } );
  0         0  
178 0         0 delete $this->{ CACHE }{ $d_key };
179             }
180             else {
181 0         0 $this->{ CACHE_COUNT }++;
182             }
183 0         0 $this->{ CACHE }{ $ip_class } = [ $country, $countrys{ $country } ];
184             }
185              
186 0         0 return ( $country, $countrys{ $country }, $ip_class );
187             }
188              
189             sub Faster {
190 0     0 1 0 my $this = shift;
191 0         0 my $handler = $this->{ handler };
192              
193 0         0 seek( $handler, 0, 0 ); ## Fix bug on Perl 5.6.0
194 0         0 seek( $handler, $this->{ start }, 0 );
195              
196 0         0 $this->{ DB } = do { local $/; <$handler>; };
  0         0  
  0         0  
197 0         0 $this->{ DB_SIZE } = length( $this->{ DB } );
198              
199 0         0 memoize( 'dec2baseX' );
200 0         0 memoize( 'baseX2dec' );
201              
202 0         0 $this->{ FASTER } = 1;
203             }
204              
205             sub Clean_Cache {
206 0     0 1 0 my $this = shift;
207 0         0 $this->{ CACHE_COUNT } = 0;
208 0         0 delete $this->{ CACHE };
209 0         0 return 1;
210             }
211              
212             sub nslookup {
213 0     0 1 0 my ( $host, $last_lookup ) = @_;
214 0         0 require Socket;
215 0   0     0 my $iaddr = Socket::inet_aton( $host ) || '';
216 0         0 my @ip = unpack( 'C4', $iaddr );
217              
218 0 0 0     0 return nslookup( "www.${host}", 1 ) if !@ip && !$last_lookup;
219 0         0 return join( '.', @ip );
220             }
221              
222             sub ip2nb {
223 1     1 1 963 my @ip = split( /\./, $_[ 0 ] );
224 1         10 return ( $ip[ 0 ] << 24 ) + ( $ip[ 1 ] << 16 ) + ( $ip[ 2 ] << 8 )
225             + $ip[ 3 ];
226             }
227              
228             sub nb2ip {
229 1     1 1 2 my ( $input ) = @_;
230 1         2 my @ip;
231              
232 1         8 while ( $input > 1 ) {
233 4         7 my $int = int( $input / 256 );
234 4         6 push @ip, $input - ( $int << 8 );
235 4         9 $input = $int;
236             }
237              
238 1 50       3 push @ip, $input if $input > 0;
239 1         3 push @ip, ( 0 ) x ( 4 - @ip );
240              
241 1         8 return join( '.', reverse @ip );
242             }
243              
244             sub dec2baseX {
245 86     86 1 48262 my ( $dec ) = @_;
246 86         118 my @base;
247              
248 86         218 while ( $dec > 1 ) {
249 84         245 my $int = int( $dec / $base );
250 84         165 push @base, $dec - $int * $base;
251 84         197 $dec = $int;
252             }
253              
254 86 100       159 push @base, $dec if $dec > 0;
255 86         204 push @base, ( 0 ) x ( 5 - @base );
256              
257 86         189 return join( '', map { $baseX[ $_ ] } reverse @base );
  430         1344  
258             }
259              
260             sub baseX2dec {
261 86     86 1 160 my ( $input ) = @_;
262              
263 86         278 my @digits = reverse split( '', $input );
264 86         89 my $dec = 0;
265              
266 86         255 foreach ( 0 .. @digits - 1 ) {
267 86         315 $dec += $baseX{ $digits[ $_ ] } * ( $base**$_ );
268             }
269              
270 86         454 return $dec;
271             }
272              
273             1;
274              
275             __DATA__