sub load_geoip2()

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);
}