# ====================================================================
# Gossamer Threads Module Library - http://gossamer-threads.com/
#
#   GT::IPCountry
#   Author: Jason Rhinelander
#   CVS Info : 087,071,086,086,085      
#   $Id: IPCountry.pm,v 1.1 2006/01/31 00:45:04 jagerman Exp $
#
# Copyright (c) 2006 Gossamer Threads Inc.  All Rights Reserved.
# ====================================================================
#
# Description:
#   Attempts to look up an IP's country using a variety of common CPAN modules.
#

package GT::IPCountry;
use strict;
require Exporter;

use vars qw/@EXPORT @ISA %MODULE/;

@ISA = 'Exporter';
@EXPORT = 'ip_to_country';

sub lookup_possible () {
    _load_module() if not defined $MODULE{loaded};
    return $MODULE{loaded};
}

sub ip_to_country ($) {
    my $ip = shift;

    lookup_possible or return (undef, undef);

    my $country;

    if ($MODULE{geoip}) { # Geo::IP
        $country = $MODULE{geoip}->country_name_by_addr($ip);
    }
    elsif ($MODULE{ipc}) { # IP::Country & Geography::Countries
        $country = $MODULE{ipc}->inet_ntocc(Socket::inet_aton($ip));
        my %special = ( # Special codes returned that G::C can't handle:
            AP => 'non-specific Asia-Pacific location',
            CS => 'Czechoslovakia (former)',
            EU => 'non-specific European Union location',
            FX => 'France, Metropolitan',
            PS => 'Palestinian Territory, Occupied',
            '**' => 'Intranet address'
        );
        if ($special{$country}) { $country = $special{$country} }
        elsif ($MODULE{geoc}) {
            $country = Geography::Countries::country($country) || $country;
        }
    }
    elsif ($MODULE{geoipfree}) { # Geo::IPfree
        $country = ($MODULE{geoipfree}->LookUp($ip))[1];
    }

    return wantarray ? ($country, 1) : $country;
}

# Attempts to load various CPAN modules capable of going the IP -> country
# lookup.  Sets $MODULE{loaded} to 1 if at least one of the modules was found,
# sets to 0 if none were loadable.
sub _load_module {

    if (!defined $MODULE{geoip}) {
        $MODULE{geoip} = eval { require Geo::IP; Geo::IP->new(Geo::IP::GEOIP_STANDARD()) } || 0;
        if (!$MODULE{geoip}) {
            $MODULE{geoipfree} = 0 && eval { require Geo::IPfree; Geo::IPfree->new } || 0;
        }
        if (!$MODULE{geoip}) {
            $MODULE{ipc} = eval { require IP::Country::Fast; IP::Country::Fast->new } || 0;
        }
        if ($MODULE{ipc}) {
            require Socket;
            $MODULE{geoc} = eval { require Geography::Countries } || 0;
        }
        if (!$MODULE{ipc} and !$MODULE{geoipfree}) {
            $MODULE{geoipfree} = 0 && eval { require Geo::IPfree; Geo::IPfree->new } || 0;
        }
    }

    $MODULE{loaded} = $MODULE{geoip} || $MODULE{geoipfree} || $MODULE{ipc} ? 1 : 0;
}

1;

__END__

=head1 NAME

GT::IPCountry - Attempts to look up an IP's country using a variety of common
CPAN modules.

=head1 SYNOPSIS

    use GT::IPCountry;

    my $country = ip_to_country("209.139.239.160");

    my ($country, $lookup_okay) = ip_to_country("209.139.239.160");

    my $can_lookup = GT::IPCountry::lookup_possible();

=head1 DESCRIPTION

This module takes an IP address and returns the country name the IP is reserved
for.  This module itself does no actual lookup, but is simply a wrapper around
serveral CPAN modules.  If none of the modules are available, it simply returns
the value C<undef>.

=head1 FUNCTIONS

=head2 ip_to_country

This method takes a country name and returns two elements: the country name,
and a true/false value indicating whether one of the lookup modules was
available.  In scalar context just the country name is returned.  A country
name of C<undef> indicates that either the IP wasn't found, or no lookup module
was available.

C<ip_to_country> is exported by default.

=head2 lookup_possible

This method returns a true/false value indicating whether or not an IP ->
Country lookup can be done.  It corresponds directly to the second return value
of C<ip_to_country>.

=head1 MODULES

GT::IPCountry attempts to use the following modules, in order, to perform a
country lookup:

=over 4

=item Geo::IP

Uses Geo::IP for the lookup.

=item IP::Country

Uses IP::Country for the lookup.  Note that because IP::Country only returns a
country code, this module will attempt to use Geography::Countries to determine
the country name.  If Geography::Countries isn't installed, you'll just get a
country code.

=item Geo::IPfree

Uses Geo::IPfree for the lookup.

=back

=head1 SEE ALSO

L<Geo::IP>

L<Geo::IPfree>

L<IP::Country>

=head1 COPYRIGHT

Copyright (c) 2006 Gossamer Threads Inc.  All Rights Reserved.
http://www.gossamer-threads.com/

=head1 VERSION

Revision: $Id: IPCountry.pm,v 1.1 2006/01/31 00:45:04 jagerman Exp $

=cut
