in lib/Mail/SpamAssassin/GeoDB.pm [213:414]
sub load_geoip2 {
my ($self, $geodb_opts) = @_;
my ($db, $dbapi, $ok);
my $module = $geodb_opts->{module} || '';
# Warn about fatal errors if this module was specifically requested
my $errwarn = $module =~ /^geoip2/;
my $reader_class;
if ($module eq '' || $module eq 'geoip2') {
eval {
require MaxMind::DB::Reader;
$reader_class = 'MaxMind::DB::Reader';
} or do {
my $err = $@;
$err =~ s/ at .*//s;
$err = "geodb: MaxMind::DB::Reader (GeoIP2) module load failed: $err";
$errwarn ? warn("$err\n") : dbg($err);
if ($module eq 'geoip2') {
return (undef, undef);
}
}
}
if (!defined $reader_class) {
eval {
require IP::Geolocation::MMDB;
$reader_class = 'IP::Geolocation::MMDB';
} or do {
my $err = $@;
$err =~ s/ at .*//s;
$err = "geodb: IP::Geolocation::MMDB (GeoIP2) module load failed: $err";
$errwarn ? warn("$err\n") : dbg($err);
return (undef, undef);
};
}
my %path;
foreach my $dbtype (@geoip_types) {
# skip country if city already loaded
next if $dbtype eq 'country' && $db->{city};
# skip asn if isp already loaded
next if $dbtype eq 'asn' && $db->{isp};
# skip if not needed
next if $geodb_opts->{wanted} && !$geodb_opts->{wanted}->{$dbtype};
# only autosearch if no absolute path given
if (!defined $geodb_opts->{dbs}->{$dbtype}) {
# Try some default locations
PATHS_GEOIP2: foreach my $p (@{$geodb_opts->{search_path}}) {
foreach my $f (@{$geoip2_default_files{$dbtype}}) {
if (-f "$p/$f") {
$path{$dbtype} = "$p/$f";
dbg("geodb: GeoIP2: search found $dbtype $p/$f");
last PATHS_GEOIP2;
}
}
}
} else {
if (!-f $geodb_opts->{dbs}->{$dbtype}) {
dbg("geodb: GeoIP2: $dbtype database requested, but not found: ".
$geodb_opts->{dbs}->{$dbtype});
next;
}
$path{$dbtype} = $geodb_opts->{dbs}->{$dbtype};
}
if (defined $path{$dbtype}) {
eval {
$db->{$dbtype} = $reader_class->new(
file => $path{$dbtype},
);
die "unknown error" unless $db->{$dbtype};
1;
};
if ($@ || !$db->{$dbtype}) {
my $err = $@;
$err =~ s/\s+Trace begun.*//s;
$err =~ s/ at .*//s;
dbg("geodb: GeoIP2: $dbtype load failed: $err");
} else {
dbg("geodb: GeoIP2: loaded $dbtype from $path{$dbtype}");
$ok = 1;
}
} else {
my $from = defined $geodb_opts->{dbs}->{$dbtype} ?
$geodb_opts->{dbs}->{$dbtype} : "default locations";
dbg("geodb: GeoIP2: $dbtype database not found from $from");
}
}
if (!$ok) {
warn("geodb: GeoIP2 requested, but no databases could be loaded\n") if $errwarn;
return (undef, undef)
}
# dbinfo_DBTYPE()
$db->{city} and $dbapi->{dbinfo_city} = sub {
my $m = $_[0]->{db}->{city}->metadata();
return "GeoIP2 city: ".$m->description()->{en}." / ".localtime($m->build_epoch());
};
$db->{country} and $dbapi->{dbinfo_country} = sub {
my $m = $_[0]->{db}->{country}->metadata();
return "GeoIP2 country: ".$m->description()->{en}." / ".localtime($m->build_epoch());
};
$db->{isp} and $dbapi->{dbinfo_isp} = sub {
my $m = $_[0]->{db}->{isp}->metadata();
return "GeoIP2 isp: ".$m->description()->{en}." / ".localtime($m->build_epoch());
};
$db->{asn} and $dbapi->{dbinfo_asn} = sub {
my $m = $_[0]->{db}->{asn}->metadata();
return "GeoIP2 asn: ".$m->description()->{en}." / ".localtime($m->build_epoch());
};
# city()
$db->{city} and $dbapi->{city} = $dbapi->{city_v6} = sub {
my $res = {};
my $city;
eval {
$city = $_[0]->{db}->{city}->record_for_address($_[1]);
1;
} or do {
$@ =~ s/\s+Trace begun.*//s;
dbg("geodb: GeoIP2 city query failed for $_[1]: $@");
return $res;
};
eval {
$res->{city_name} = $city->{city}->{names}->{en};
$res->{country} = $city->{country}->{iso_code};
$res->{country_name} = $city->{country}->{names}->{en};
$res->{continent} = $city->{continent}->{code};
$res->{continent_name} = $city->{continent}->{names}->{en};
1;
};
return $res;
};
# country()
$db->{country} and $dbapi->{country} = $dbapi->{country_v6} = sub {
my $res = {};
my $country;
eval {
$country = $_[0]->{db}->{country}->record_for_address($_[1]);
1;
} or do {
$@ =~ s/\s+Trace begun.*//s;
dbg("geodb: GeoIP2 country query failed for $_[1]: $@");
return $res;
};
eval {
$res->{country} = $country->{country}->{iso_code};
$res->{country_name} = $country->{country}->{names}->{en};
$res->{continent} = $country->{continent}->{code};
$res->{continent_name} = $country->{continent}->{names}->{en};
1;
};
return $res;
};
# isp()
$db->{isp} and $dbapi->{isp} = $dbapi->{isp_v6} = sub {
my $res = {};
my $isp;
eval {
$isp = $_[0]->{db}->{isp}->record_for_address($_[1]);
1;
} or do {
$@ =~ s/\s+Trace begun.*//s;
dbg("geodb: GeoIP2 isp query failed for $_[1]: $@");
return $res;
};
eval {
$res->{asn} = $isp->{autonomous_system_number};
$res->{asn_organization} = $isp->{autonomous_system_organization};
$res->{isp} = $isp->{isp};
$res->{organization} = $isp->{organization};
1;
};
return $res;
};
# asn()
$db->{asn} and $dbapi->{asn} = $dbapi->{asn_v6} = sub {
my $res = {};
my $asn;
eval {
$asn = $_[0]->{db}->{asn}->record_for_address($_[1]);
1;
} or do {
$@ =~ s/\s+Trace begun.*//s;
dbg("geodb: GeoIP2 asn query failed for $_[1]: $@");
return $res;
};
eval {
$res->{asn} = $asn->{autonomous_system_number};
$res->{asn_organization} = $asn->{autonomous_system_organization};
1;
};
return $res;
};
return ($db, $dbapi);
}