############################################################################## package WWW::HostipInfo; use strict; use LWP::UserAgent; use Carp; use vars qw( $VERSION $DEBUG ); $VERSION = '0.12'; my $GetAPI = 'http://api.hostip.info/get_html.php?position=true&ip='; my $RoughAPI = 'http://api.hostip.info/get_html.php?position=true&ip='; ## PUBLIC METHOD sub new { my ( $class, $ip ) = @_; my $opts = ! defined $ip ? {} : ref( $ip ) eq 'HASH' ? $ip : { ip => $ip } ; my $self = { url => $GetAPI, %$opts, }; bless $self, $class; $self->_init() unless ( $self->ua ); $self; } sub _init { my $self = shift; $self->ua( LWP::UserAgent->new(agent => "WWW::HostipInfo/$VERSION") ); } sub ua { $_[0]->{ua} = $_[1] if(@_ > 1); $_[0]->{ua} } sub ip { $_[0]->{ip} = $_[1] if(@_ > 1); $_[0]->{ip} } sub url { $_[0]->{url} = $_[1] if(@_ > 1); $_[0]->{url} } sub recent_info { $_[0]->{_recent_info}; } sub get_info { my $self = shift; my $ip = shift || $self->ip; my $opt = shift || {}; my $chk = $self->_check_ipaddr($ip); # $chk : 0..invalid ip address, 1..class A - C, 2..class D or E, 3..private if(!$chk){ croak "IP address is required." } elsif($chk == 2){ return $self->_get_info_as_null; } elsif($chk == 3){ return $self->_get_info_as_private } $self->ip($ip); my $url = $self->url; my $response = $self->ua->get($url . $ip); return unless($response->is_success); my $content = $response->content; if($DEBUG){ warn $content; } return $self->_set_info_to_obj($content); } ## PRIVATE METHOD sub _check_ipaddr { return 0 if(!defined $_[1] or $_[1] !~ /^(\d{1,3})\.(\d{1,3})\.\d{1,3}\.\d{1,3}$/); return 2 if($1 >= 224); # class D or E return ($1 == 127 or $1 == 10) ? 3 : ($1 == 192 and $2 == 168) ? 3 : ($1 == 172 and $2 >= 16 and $2 <= 31) ? 3 : 1; } sub _set_info_to_obj { my ( $self, $content ) = @_; my ( $country_code, $city, $lat, $lon, $region, $name, $code ); for my $line ( split/\n/, $content ) { if ( $line =~ /^Country: (.+?) \(([A-Z][A-Z])\)$/ ) { ( $name, $code ) = ( $1, $2 ); } elsif ( $line =~ /^City: ([^,]+)(?:, (\w+))?$/ ) { ($city, $region) = ( $1, $2 ); } elsif ( $line =~ /^Latitude: (.+)$/ ) { $lat = $1; } elsif ( $line =~ /^Longitude: (.+)$/ ) { $lon = $1; } } my ($unknown_city, $unknown_country) = (0,0); if($city =~ /^\([uU]nknown [cC]ity/){ $city = ''; $region = ''; $unknown_city = 1; } if($name =~ /^\([uU]nknown [cC]ountry/){ $name = ''; $unknown_country = 1; } $self->{_recent_info} = bless { _ipaddr => $self->ip, _CountryName => $name, _CountryCode => $code, _City => $city, _Region => ($region || ''), _Latitude => $lat, _Longitude => $lon, _private => 0, _un_city => $unknown_city, _un_country => $unknown_country, }, 'WWW::HostipInfo::Info'; } sub _get_info_as_null { $_[0]->{_recent_info} = bless { _ipaddr => $_[0]->ip, _CountryName => '', _CountryCode => 'XX', _City => '', _Region => undef, _Latitude => undef, _Longitude => undef, _private => 0, _un_city => 1, _un_country => 1, }, 'WWW::HostipInfo::Info'; } sub _get_info_as_private { $_[0]->{_recent_info} = bless { _ipaddr => $_[0]->ip, _CountryName => '', _CountryCode => 'XX', _City => '', _Region => undef, _Latitude => undef, _Longitude => undef, _private => 1, _un_city => 1, _un_country => 1, }, 'WWW::HostipInfo::Info'; } ############################################################################## # Information Class ############################################################################## package WWW::HostipInfo::Info; sub is_private { $_[0]->{_private}; } sub is_guessed { $_[0]->{_guessed}; } sub has_unknown_city { $_[0]->{_un_city}; } sub has_unknown_country { $_[0]->{_un_country}; } sub ip { $_[0]->{_ipaddr}; } sub name { $_[0]->{_CountryName}; } sub code { $_[0]->{_CountryCode}; } sub country_name { $_[0]->{_CountryName}; } sub country_code { $_[0]->{_CountryCode}; } sub country { $_[0]->{_CountryName}; } sub city { $_[0]->{_City}; } sub region { $_[0]->{_Region}; } sub latitude { $_[0]->{_Latitude}; } sub longitude { $_[0]->{_Longitude} } ############################################################################## 1; __END__ =pod =head1 NAME WWW::HostipInfo - get a country and city information from ip address via www.hostip.info API. =head1 SYNOPSIS use WWW::HostipInfo; my $hostip = new WWW::HostipInfo; my $ip = 'xxx.xxx.xxx.xxx'; my $info = $hostip->get_info($ip); if($info->is_private){ warn "This is a private ip address." } my $country_code = $info->code; my $city_name = $info->city; my $region = $info->region; # if any $info = $hostip->recent_info->country_name; # fetch most recent data print WWW::HostipInfo->new($ip)->get_info->city; # shortcut =head1 DESCRIPTION This module gets a country and city information from ip address via L API. =head1 METHODS =head2 new $hostip = WWW::HostipInfo->new(); $hostip = WWW::HostipInfo->new( $ip ); $hostip = WWW::HostipInfo->new( { ip => $ip, url => $url, ua => $user_agent } ); returns a WWW::HostipInfo object. This method can take an ip address or a hash reference optionally. =head2 ip $ip = $hostip->ip([$ip]); setter / getter to the ip address. =head2 url $url = $hostip->url([$url]); setter / getter to the HostipInfo API. Default is C. =head2 ua $user_agent = $hostip->ua([$user_agent]); setter / getter to the user agent. Default is C. =head2 get_info $info = $hostip->get_info(); returns a WWW::HostipInfo::Info object. If the method can't get a information, will return C. =head2 recent_info $info = $hostip->get_info(); returns a C object. =head1 WWW::HostipInfo::Info With C, WWW::HostipInfo object returns WWW::HostipInfo::Info object. =head2 METHODS getters for informations. =over 4 =item country_code return the cuntry code. If private ip address is used, the code is 'XX'. =item code an alias to country_code =item country_name return the cuntry name. =item name an alias to country_name =item country an alias to country_name =item city return the city name as long as it is not unknown. =item region return state code if the coutnry is US. =item latitude =item longitude =item ip =item is_private If private ip address is used, returns true. =item has_unknown_city If the object has no data for city, returns true. =item has_unknown_country If the object has no data for country, returns true. =back =head1 SEE ALSO L, L =head1 AUTHOR Makamaka Hannyaharamitu, Emakamaka[at]cpan.orgE =head1 COPYRIGHT AND LICENSE Copyright 2005, 2009 by Makamaka Hannyaharamitu This library is licensed under GNU GENERAL PUBLIC LICENSE =cut