diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cbb3a13
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.rpm
+*.log
+*spec-20*
+*.tar.gz
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..05283fa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+# Makefile for source rpm: smeserver-geoip
+# $Id: Makefile,v 1.1 2020/10/06 13:45:08 brianr Exp $
+NAME := smeserver-geoip
+SPECFILE = $(firstword $(wildcard *.spec))
+
+define find-makefile-common
+for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done
+endef
+
+MAKEFILE_COMMON := $(shell $(find-makefile-common))
+
+ifeq ($(MAKEFILE_COMMON),)
+# attept a checkout
+define checkout-makefile-common
+test -f CVS/Root && { cvs -Q -d $$(cat CVS/Root) checkout common && echo "common/Makefile.common" ; } || { echo "ERROR: I can't figure out how to checkout the 'common' module." ; exit -1 ; } >&2
+endef
+
+MAKEFILE_COMMON := $(shell $(checkout-makefile-common))
+endif
+
+include $(MAKEFILE_COMMON)
diff --git a/README.md b/README.md
index e05422e..ecf8ed1 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,18 @@
-# smeserver-geoip
+# smeserver-geoip
-SMEServer Koozali developed git repo for smeserver-geoip smecontribs
\ No newline at end of file
+SMEServer Koozali developed git repo for smeserver-geoip smecontribs
+
+## Wiki
+
https://wiki.koozali.org/GeoIP
+
https://wiki.koozali.org/Xt_geoip
+
https://wiki.koozali.org/Qpsmtpd:ident/geoip
+
https://wiki.koozali.org/Xt_geoip/fr
+
+## Bugzilla
+Show list of outstanding bugs: [here](https://bugs.koozali.org/buglist.cgi?component=smeserver-geoip&product=SME%20Contribs&query_format=advanced&limit=0&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=CONFIRMED)
+
+## Description
+
+
*This description has been generated by an LLM AI system and cannot be relied on to be fully correct.*
+*Once it has been checked, then this comment will be deleted*
+
diff --git a/contriborbase b/contriborbase
new file mode 100644
index 0000000..9b7fd51
--- /dev/null
+++ b/contriborbase
@@ -0,0 +1 @@
+contribs10
diff --git a/createlinks b/createlinks
new file mode 100644
index 0000000..2d04b2b
--- /dev/null
+++ b/createlinks
@@ -0,0 +1,70 @@
+#! /usr/bin/perl -w
+
+use esmith::Build::CreateLinks qw(:all);
+
+#--------------------------------------------------
+# actions for geoip-update event
+#--------------------------------------------------
+
+
+#for my $event (qw(geoip-update))
+#{
+#templates2events("/var/service/qpsmtpd/config/badcountries", $event);
+#event_link("smeserver-geopip-download-action", $event, "10");
+#}
+
+
+
+my $event = "geoip-update";
+
+foreach (qw(
+ /var/service/qpsmtpd/config/badcountries
+ /etc/GeoIP.conf
+ /etc/crontab
+ /var/service/qpsmtpd/config/peers/0
+ ))
+{
+ templates2events("$_", qw(
+ geoip-update
+ smeserver-geoip-update
+ ));
+}
+
+event_link("smeserver-geopip-download-action", $event, "10");
+
+event_link("smeserver-geopip-download-action", 'smeserver-geoip-update', "10");
+
+#--------------------------------------------------
+# actions for email-update event
+#--------------------------------------------------
+
+#for my $event (qw(
+# bootstrap-console-save
+# email-update))
+
+#{
+#templates2events("/var/service/qpsmtpd/config/badcountries", $event);
+#}
+
+#my $event = "email-update";
+
+foreach (qw(
+ /var/service/qpsmtpd/config/badcountries
+ /etc/GeoIP.conf
+ /etc/crontab
+ /var/service/qpsmtpd/config/peers/0
+ ))
+
+{
+ templates2events("$_", qw(
+ post-upgrade
+ bootstrap-console-save
+ console-save
+ email-update
+ ));
+}
+
+event_services('smeserver-geoip-update', 'qpsmtpd' => 'restart', 'sqpsmtpd' => 'restart') ;
+
+safe_symlink("../../plugins/18check_badcountries", "root//etc/e-smith/templates/var/service/qpsmtpd/config/peers/0/18check_badcountries");
+
diff --git a/root/etc/e-smith/db/configuration/defaults/geoip/status b/root/etc/e-smith/db/configuration/defaults/geoip/status
new file mode 100644
index 0000000..86981e6
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/geoip/status
@@ -0,0 +1 @@
+enabled
diff --git a/root/etc/e-smith/db/configuration/defaults/geoip/type b/root/etc/e-smith/db/configuration/defaults/geoip/type
new file mode 100644
index 0000000..24e1098
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/geoip/type
@@ -0,0 +1 @@
+service
diff --git a/root/etc/e-smith/db/configuration/defaults/qpsmtpd/BadCountries b/root/etc/e-smith/db/configuration/defaults/qpsmtpd/BadCountries
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/qpsmtpd/BadCountries
@@ -0,0 +1 @@
+
diff --git a/root/etc/e-smith/db/configuration/defaults/qpsmtpd/GeoIP b/root/etc/e-smith/db/configuration/defaults/qpsmtpd/GeoIP
new file mode 100644
index 0000000..86981e6
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/qpsmtpd/GeoIP
@@ -0,0 +1 @@
+enabled
diff --git a/root/etc/e-smith/events/actions/smeserver-geopip-download-action b/root/etc/e-smith/events/actions/smeserver-geopip-download-action
new file mode 100644
index 0000000..5dd2591
--- /dev/null
+++ b/root/etc/e-smith/events/actions/smeserver-geopip-download-action
@@ -0,0 +1,2 @@
+#!/bin/bash
+/usr/bin/geoipupdate
diff --git a/root/etc/e-smith/templates/etc/GeoIP.conf/10default b/root/etc/e-smith/templates/etc/GeoIP.conf/10default
new file mode 100644
index 0000000..c7f981a
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/GeoIP.conf/10default
@@ -0,0 +1,12 @@
+# The following AccountID and LicenseKey are required placeholders.
+# For geoipupdate versions earlier than 2.5.0, use UserId here instead of AccountID.
+#AccountID 0
+AccountID { $geoip{AccountID} ||'0' }
+LicenseKey { $geoip{LicenseKey} ||'000000000000'}
+
+# Include one or more of the following edition IDs:
+# * GeoLite2-City - GeoLite 2 City
+# * GeoLite2-Country - GeoLite2 Country
+# For geoipupdate versions earlier than 2.5.0, use ProductIds here instead of EditionIDs.
+EditionIDs GeoLite2-City GeoLite2-Country
+#ProductIds GeoLite2-City GeoLite2-Country
diff --git a/root/etc/e-smith/templates/etc/GeoIP.conf/20databasestore b/root/etc/e-smith/templates/etc/GeoIP.conf/20databasestore
new file mode 100644
index 0000000..1b34a47
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/GeoIP.conf/20databasestore
@@ -0,0 +1,9 @@
+# The remaining settings are OPTIONAL.
+
+# The directory to store the database files. Defaults to /usr/share/GeoIP
+# DatabaseDirectory /usr/share/GeoIP
+{
+ my $DatabaseDirectory = $geoip{DatabaseDirectory} || '/usr/share/GeoIP';
+ $OUT ="DatabaseDirectory $DatabaseDirectory";
+
+}
diff --git a/root/etc/e-smith/templates/etc/GeoIP.conf/25server b/root/etc/e-smith/templates/etc/GeoIP.conf/25server
new file mode 100644
index 0000000..49d6e85
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/GeoIP.conf/25server
@@ -0,0 +1,7 @@
+# The server to use. Defaults to "updates.maxmind.com".
+# Host updates.maxmind.com
+{
+ my $GEOserver = $geoip{Host} || 'updates.maxmind.com';
+ $OUT ="Host $GEOserver";
+
+}
diff --git a/root/etc/e-smith/templates/etc/GeoIP.conf/30proxy b/root/etc/e-smith/templates/etc/GeoIP.conf/30proxy
new file mode 100644
index 0000000..03d79a0
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/GeoIP.conf/30proxy
@@ -0,0 +1,8 @@
+# The proxy host name or IP address. You may optionally specify a
+# port number, e.g., 127.0.0.1:8888. If no port number is specified, 1080
+# will be used.
+# Proxy 127.0.0.1:8888
+
+# The user name and password to use with your proxy server.
+# ProxyUserPassword username:password
+
diff --git a/root/etc/e-smith/templates/etc/GeoIP.conf/35PreserveFileTimes b/root/etc/e-smith/templates/etc/GeoIP.conf/35PreserveFileTimes
new file mode 100644
index 0000000..64c5c5d
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/GeoIP.conf/35PreserveFileTimes
@@ -0,0 +1,8 @@
+# Whether to preserve modification times of files downloaded from the server.
+# Defaults to "0".
+# PreserveFileTimes 0
+{
+ my $FT = $geoip{PreserveFileTimes} || '0';
+ $OUT ="PreserveFileTimes $FT";
+
+}
diff --git a/root/etc/e-smith/templates/etc/GeoIP.conf/40lock b/root/etc/e-smith/templates/etc/GeoIP.conf/40lock
new file mode 100644
index 0000000..975bfb4
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/GeoIP.conf/40lock
@@ -0,0 +1,6 @@
+# The lock file to use. This ensures only one geoipupdate process can run at a
+# time.
+# Note: Once created, this lockfile is not removed from the filesystem.
+# Defaults to ".geoipupdate.lock" under the DatabaseDirectory.
+# LockFile /usr/share/GeoIP/.geoipupdate.lock
+
diff --git a/root/etc/e-smith/templates/etc/crontab/91_Update_Geoip_db b/root/etc/e-smith/templates/etc/crontab/91_Update_Geoip_db
new file mode 100644
index 0000000..1561a6e
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/crontab/91_Update_Geoip_db
@@ -0,0 +1,3 @@
+# Updating the GeoIP database monthly on the 5th at 0:00h.
+0 0 10 * * root /usr/bin/geoipupdate
+
diff --git a/root/etc/e-smith/templates/var/service/qpsmtpd/config/badcountries/10badcountries b/root/etc/e-smith/templates/var/service/qpsmtpd/config/badcountries/10badcountries
new file mode 100644
index 0000000..4e47db1
--- /dev/null
+++ b/root/etc/e-smith/templates/var/service/qpsmtpd/config/badcountries/10badcountries
@@ -0,0 +1,7 @@
+{
+ my @badcountries = split /[,:]/, ${qpsmtpd}{BadCountries} || '';
+
+ return "# No BadCountries are defined" unless (scalar @badcountries);
+
+ return join "\n", @badcountries;
+}
diff --git a/root/etc/e-smith/templates/var/service/qpsmtpd/config/peers/0/18check_badcountries b/root/etc/e-smith/templates/var/service/qpsmtpd/config/peers/0/18check_badcountries
new file mode 120000
index 0000000..740d1b9
--- /dev/null
+++ b/root/etc/e-smith/templates/var/service/qpsmtpd/config/peers/0/18check_badcountries
@@ -0,0 +1 @@
+../../plugins/18check_badcountries
\ No newline at end of file
diff --git a/root/etc/e-smith/templates/var/service/qpsmtpd/config/plugins/18check_badcountries b/root/etc/e-smith/templates/var/service/qpsmtpd/config/plugins/18check_badcountries
new file mode 100644
index 0000000..cd91d08
--- /dev/null
+++ b/root/etc/e-smith/templates/var/service/qpsmtpd/config/plugins/18check_badcountries
@@ -0,0 +1,6 @@
+{
+ return "# geoip disabled" unless (${qpsmtpd}{GeoIP} eq "enabled");
+
+ "check_badcountries";
+}
+
diff --git a/root/usr/bin/geocity.pl b/root/usr/bin/geocity.pl
new file mode 100755
index 0000000..03bd18b
--- /dev/null
+++ b/root/usr/bin/geocity.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/perl
+
+use GeoIP2::Database::Reader;
+
+# GeoLite2-City.mmdb
+if ( $ARGV[0] eq '-h' || $ARGV[0] eq '-help' || $ARGV[0] eq '' ) {
+ help();
+ exit;
+}
+
+my $reader = GeoIP2::Database::Reader->new(
+ file => '/usr/share/GeoIP/GeoLite2-City.mmdb',
+ locales => [ 'en', 'de', ]
+);
+
+my $city = $reader->city( ip => $ARGV[0] );
+
+my $city_rec = $city->city();
+print "City: ", $city_rec->name(), "\n";
+
+my $country = $city->country();
+print "Country: ", $country->iso_code(), "\n";
+
+sub help {
+ print "Usage: ./geoipcity.pl 1.2.3.4\n";
+}
diff --git a/root/usr/bin/geocountry.pl b/root/usr/bin/geocountry.pl
new file mode 100755
index 0000000..1206fc3
--- /dev/null
+++ b/root/usr/bin/geocountry.pl
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use GeoIP2::Database::Reader;
+
+if ( $ARGV[0] eq '-h' || $ARGV[0] eq '-help' || $ARGV[0] eq '' ) {
+ help();
+ exit;
+}
+
+my $reader = GeoIP2::Database::Reader->new(
+ file => '/usr/share/GeoIP/GeoLite2-Country.mmdb',
+ locales => [ 'en', 'de', ]
+);
+
+my $country = $reader->country( ip => $ARGV[0] );
+my $country_rec = $country->country();
+
+print "Country ", $country_rec->iso_code(), "\n";
+
+sub help {
+ print "Usage: ./geoipcountry.pl 1.2.3.4\n";
+}
diff --git a/root/usr/bin/geoiplook b/root/usr/bin/geoiplook
new file mode 100644
index 0000000..3a6a1c1
--- /dev/null
+++ b/root/usr/bin/geoiplook
@@ -0,0 +1,6 @@
+#!/bin/bash
+for var in "$@"
+do
+/usr/bin/mmdblookup --file /usr/share/GeoIP/GeoLite2-Country.mmdb --ip $1 country iso_code |cut -d\" -f2| tr -d '\n'
+echo ""
+done
diff --git a/root/usr/share/qpsmtpd/plugins/check_badcountries b/root/usr/share/qpsmtpd/plugins/check_badcountries
new file mode 100644
index 0000000..2dc2685
--- /dev/null
+++ b/root/usr/share/qpsmtpd/plugins/check_badcountries
@@ -0,0 +1,595 @@
+#!perl -w
+
+=head1 NAME
+
+geoip - provide geographic information about mail senders.
+
+=head1 SYNOPSIS
+
+Use MaxMind's GeoIP databases and the GeoIP2 or Geo::IP perl modules to report
+geographic information about incoming connections.
+
+=head1 DESCRIPTION
+
+Save geographic information about the sender in the following connection notes:
+
+ geoip_country - 2 char country code
+ geoip_country_name - english name of country
+ geoip_continent - 2 char continent code
+ geoip_city - english name of city
+ geoip_distance - distance in kilometers
+ geoip_asn - network number
+
+And adds entries like this to your logs:
+
+ (connect) ident::geoip: NA, US, United States, 1319 km
+ (connect) ident::geoip: AS, IN, India, 13862 km
+ (connect) ident::geoip: fail: no results
+ (connect) ident::geoip: NA, CA, Canada, 2464 km
+ (connect) ident::geoip: NA, US, United States, 2318 km
+ (connect) ident::geoip: AS, PK, Pakistan, 12578 km
+ (connect) ident::geoip: AS, TJ, Tajikistan, 11965 km
+ (connect) ident::geoip: EU, AT, Austria, 8745 km
+ (connect) ident::geoip: AS, IR, Iran, Islamic Republic of, 12180 km
+ (connect) ident::geoip: EU, BY, Belarus, 9030 km
+ (connect) ident::geoip: AS, CN, China, 11254 km
+ (connect) ident::geoip: NA, PA, Panama, 3163 km
+
+Calculating the distance has three prerequsites:
+
+ 1. The MaxMind city database (free or subscription)
+ 2. The Math::Complex perl module
+ 3. The IP address of this mail server (see CONFIG)
+
+Other plugins can utilize the geographic notes to alter the
+connection, reject, greylist, etc.
+
+=head1 CONFIG
+
+The following options can be appended in this plugins config/plugins entry.
+
+(With the free v2 'Lite' databases you can only access Countries and Cities)
+
+=head2 distance
+
+Enables geodesic distance calculation. Will calculate the distance "as the
+crow flies" from the remote mail server. Accepts a single argument, the IP
+address to calculate the distance from. This will typically be the public
+IP of your mail server.
+
+ ident/geoip [ distance 192.0.1.5 ]
+
+Default: none. (no distance calculations)
+
+=head2 too_far
+
+Assign negative karma to connections further than this many km.
+
+Default: none
+
+=head2 db_dir
+
+The path to the GeoIP database directory.
+
+ ident/geoip [ db_dir /etc/GeoIP ]
+
+Default: /usr/local/share/GeoIP
+
+=head2 add_headers
+
+Add message headers with GeoIP data
+
+ ident/geoip [ add_headers (true|false) ]
+
+Default: true
+
+=head1 LIMITATIONS
+
+The distance calculations are more concerned with being fast than accurate.
+The MaxMind location data is collected from whois and is of limited accuracy.
+MaxMind offers more accurate data for a fee.
+
+For distance calculations, the earth is considered a perfect sphere. In
+reality, it is not. Accuracy should be within 1%.
+
+This plugin does not update the GeoIP databases. You may want to.
+
+=head1 CHANGES
+
+2019-10 - JP Pialasse - reintroduce block country functionality with geoip v1
+
+2019-01 - John Crisp - modify to work correctly with mailstats
+
+2019-01 - JP Pialasse - make it compatible with old v1 + improve log level
+
+2018-06 - John Crisp - modify to work with SME server
+
+2014-06 - Matt Simerson - added GeoIP2 support
+
+2012-06 - Matt Simerson - added GeoIP City support, continent, distance
+
+2012-05 - Matt Simerson - added geoip_country_name note, added tests
+
+=head1 SEE ALSO
+
+MaxMind: http://www.maxmind.com/
+
+Databases: http://geolite.maxmind.com/download/geoip/database
+
+It may become worth adding support for Geo::IPfree, which uses another
+data source: http://software77.net/geo-ip/
+
+=head1 ACKNOWLEDGEMENTS
+
+Based on qpsmtpd ident/geoip plugin
+Originally by Doug Kruhm
+
+MaxMind - the packager and distributor of the free GeoIP data
+
+Stevan Bajic, the DSPAM author, who suggested SNARE, which describes using
+geodesic distance to determine spam probability. The research paper on SNARE
+can be found here:
+http://smartech.gatech.edu/bitstream/handle/1853/25135/GT-CSE-08-02.pdf
+
+=cut
+
+use strict;
+use warnings;
+
+use Qpsmtpd::Constants;
+
+#use GeoIP2; # eval'ed in register()
+#use Geo::IP; # eval loaded if GeoIP2 doesn't
+#use Math::Trig; # eval'ed in set_distance_gc
+
+sub register {
+ my ($self, $qp, @args) = @_;
+
+ $self->log(LOGERROR, "Bad arguments") if @args % 2;
+ $self->{_args} = {@args};
+ $self->{_args}{db_dir} ||= '/usr/share/GeoIP';
+
+ $self->load_geoip() or return;
+ my $enabled = $self->{_args}{add_headers};
+ $enabled = 'true' if ! defined $enabled;
+ return if $enabled =~ /false/i;
+ $self->register_hook( data_post => 'add_headers' );
+}
+
+sub load_geoip {
+ my ( $self ) = @_;
+ $self->load_geoip2() and return 1;
+ $self->load_geoip1() and return 1; #only search v1 if v2 not available
+ return 0;
+}
+
+sub load_geoip1 {
+ my $self = shift;
+
+ eval 'use Geo::IP';
+ if ($@) {
+ warn "could not load Geo::IP";
+ $self->log(LOGERROR, "could not load Geo::IP");
+ return;
+ }
+
+ $self->open_geoip_db();
+
+# Note that opening the GeoIP DB only in register has caused problems before:
+# https://github.com/smtpd/qpsmtpd/commit/29ea9516806e9a8ca6519fcf987dbd684793ebdd#plugins/ident/geoip
+# Opening the DB anew for every connection is horribly inefficient.
+# Instead, attempt to reopen upon connect if the DB connection fails.
+ $self->init_my_country_code();
+
+ $self->register_hook('connect', 'geoip_lookup');
+ return 1;
+}
+
+sub load_geoip2 {
+ my $self = shift;
+
+ eval 'use GeoIP2::Database::Reader';
+ if ($@) {
+ $self->log(LOGERROR, "could not load GeoIP2");
+ return;
+ }
+
+# warn "Using GeoIP2."
+# . " ASN data is not currently available using the GeoIP2 module!\n";
+
+ eval {
+ $self->{_geoip2_city} = GeoIP2::Database::Reader->new(
+ file => $self->{_args}{db_dir} . '/GeoLite2-City.mmdb',
+ );
+ };
+ if ($@) {
+ $self->log(LOGERROR, "unable to load GeoLite2-City.mmdb");
+ }
+
+ eval {
+ $self->{_geoip2_country} = GeoIP2::Database::Reader->new(
+ file => $self->{_args}{db_dir} . '/GeoLite2-Country.mmdb',
+ );
+ };
+ if ($@) {
+ $self->log(LOGERROR, "unable to load GeoLite2-Country.mmdb");
+ }
+
+ if ($self->{_geoip2_city} || $self->{_geoip2_country}) {
+ $self->register_hook('connect', 'geoip2_lookup');
+ return 1;
+ }
+
+ return;
+}
+
+sub add_headers {
+ my ( $self, $txn ) = @_;
+ for my $h (qw( Country Continent City ASN )) {
+ my $note = lc "geoip_$h";
+ next if ! $self->connection->notes($note);
+ $txn->header->delete("X-GeoIP-$h");
+ $txn->header->add( "X-GeoIP-$h", $self->connection->notes($note), 0 );
+ }
+ return DECLINED;
+}
+
+sub geoip2_lookup {
+ my $self = shift;
+
+ my $ip = $self->qp->connection->remote_ip;
+ return DECLINED if $self->is_localhost($ip);
+
+ if ($self->{_geoip2_city}) {
+ my $city_rec = $self->{_geoip2_city}->city(ip => $ip);
+ if ($city_rec) {
+ $self->qp->connection->notes('geoip_country', $city_rec->country->iso_code());
+ $self->qp->connection->notes('geoip_country_name', $city_rec->country->name());
+ $self->qp->connection->notes('geoip_continent', $city_rec->continent->code());
+ $self->qp->connection->notes('geoip_city', $city_rec->city->name());
+ $self->qp->connection->notes('geoip_asn', $city_rec->traits->autonomous_system_number());
+ #my $city = $self->qp->connection->notes('geoip_city');
+ #warn ("At City: $city");
+ # return DECLINED;
+ }
+ }
+
+ if ($self->{_geoip2_country}) {
+ my $country_rec = $self->{_geoip2_country}->country(ip => $ip);
+ if ($country_rec) {
+ $self->qp->connection->notes('geoip_country', $country_rec->country->iso_code());
+ $self->qp->connection->notes('geoip_country_name', $country_rec->country->name());
+ $self->qp->connection->notes('geoip_continent', $country_rec->continent->code());
+ #my $country = $country_rec->country->iso_code();
+ #warn ("At country: $country");
+ };
+ }
+
+ # City Information in case we want to play later
+ my $city = $self->qp->connection->notes('geoip_city') // '';
+ #warn ("At City (check): $city");
+ $self->log(LOGNOTICE, "GeoIP RemoteIP: $ip");
+
+ if ($city eq '') {
+ $self->log(LOGNOTICE, "GeoIP City: NA");
+ }
+ else {
+ $self->log(LOGNOTICE, "GeoIP City: $city");
+ }
+
+ my $country = $self->qp->connection->notes('geoip_country');
+ # Returns DECLINED if there are no countries found above
+ return DECLINED unless $country;
+ $self->log(LOGNOTICE, "GeoIP Country: $country");
+
+ if ( $self->qp->config("badcountries") ) {
+ my @badcountries = $self->qp->config("badcountries");
+ for (@badcountries) {
+ my ($pattern, $response) = split /\s+/, $_, 2;
+ #my $whitelisthost = $connection->notes('whitelisthost');
+ my $whitelisthost = $self->qp->connection->notes('whitelisthost');
+ if ($whitelisthost) {
+ $self->log(LOGNOTICE, "Country $country Pattern $pattern Whitehost $whitelisthost RemoteIP $ip");
+ $self->log(LOGNOTICE, "Geoip whitelisthost found $whitelisthost");
+ return OK;
+ }
+ else {
+ return (DENY, "Country is on Blocked List") if ($country eq $pattern);
+ }
+ }
+ }
+ return DECLINED;
+}
+
+
+sub geoip_lookup {
+ my $self = shift;
+
+ my $ip = $self->qp->connection->remote_ip;
+ return DECLINED if $self->is_localhost($ip);
+
+ # reopen the DB if Geo::IP failed due to DB update
+ $self->open_geoip_db();
+
+ my $c_code = $self->set_country_code() or do {
+ $self->log(LOGINFO, "skip, no results");
+ return DECLINED;
+ };
+
+ $self->set_asn();
+
+ my $c_name = $self->set_country_name();
+ my ($city, $continent_code, $distance) = '';
+
+ if ($self->{_my_country_code}) {
+ $continent_code = $self->set_continent($c_code);
+ $city = $self->set_city_gc();
+ $distance = $self->set_distance_gc();
+ }
+
+ my @msg_parts;
+ if ($continent_code && $continent_code ne '--') {
+ push @msg_parts, $continent_code;
+ };
+ push @msg_parts, $c_code if $c_code;
+
+ #push @msg_parts, $c_name if $c_name;
+ push @msg_parts, $city if $city;
+ if ($distance) {
+ push @msg_parts, "\t$distance km";
+ if ($self->{_args}{too_far} && $distance > $self->{_args}{too_far}) {
+ $self->adjust_karma(-1);
+ }
+ }
+ $self->log(LOGINFO, join(", ", @msg_parts));
+
+ my $country = $self->qp->connection->notes('geoip_country');
+ # Returns DECLINED if there are no countries found above
+ return DECLINED unless $country;
+ $self->log(LOGNOTICE, "GeoIP Country: $country");
+
+ if ( $self->qp->config("badcountries") ) {
+ my @badcountries = $self->qp->config("badcountries");
+ for (@badcountries) {
+ my ($pattern, $response) = split /\s+/, $_, 2;
+ #my $whitelisthost = $connection->notes('whitelisthost');
+ my $whitelisthost = $self->qp->connection->notes('whitelisthost');
+ if ($whitelisthost) {
+ $self->log(LOGNOTICE, "Country $country Pattern $pattern Whitehost $whitelisthost RemoteIP $ip");
+ $self->log(LOGNOTICE, "Geoip whitelisthost found $whitelisthost");
+ return OK;
+ }
+ else {
+ return (DENY, "Country is on Blocked List") if ($country eq $pattern);
+ }
+ }
+ }
+
+ return DECLINED;
+}
+
+sub open_geoip_db {
+ my $self = shift;
+
+ # this might detect if the DB connection failed. If not, this is where
+ # to add more code to do it.
+ return if (defined $self->{_geoip_city} || defined $self->{_geoip});
+
+ # The methods for using GeoIP work differently for the City vs Country DB
+ # save the handles in different locations
+ my $db_dir = $self->{_args}{db_dir};
+ foreach my $db (qw/ GeoIPCity GeoLiteCity /) {
+ next if !-f "$db_dir/$db.dat";
+ $self->log(LOGDEBUG, "using db $db");
+ $self->{_geoip_city} = Geo::IP->open("$db_dir/$db.dat");
+ last if $self->{_geoip_city};
+ }
+ warn "Missing GeoIP City data!\n" if ! $self->{_geoip_city};
+
+ if (-f "$db_dir/GeoIPASNum.dat") {
+ $self->log(LOGDEBUG, "using GeoIPASNum");
+ $self->{GeoIPASNum} = Geo::IP->open("$db_dir/GeoIPASNum.dat");
+ }
+ warn "Missing GeoIP ASN data!\n" if ! $self->{GeoIPASNum};
+
+ if (-f "$db_dir/GeoIPASNumv6.dat") {
+ $self->log(LOGDEBUG, "using GeoIPASNumv6");
+ $self->{GeoIPASNumv6} = Geo::IP->open("$db_dir/GeoIPASNumv6.dat");
+ warn "Missing GeoIP ASN IPV6 data!\n" if ! $self->{GeoIPASNum};
+ }
+
+ # can't think of a good reason to load country if city data is present
+ if (!$self->{_geoip_city}) {
+ $self->log(LOGDEBUG, "using default db");
+ eval { $self->{_geoip} = Geo::IP->new(); }; # loads default Country DB
+ if (!$self->{_geoip}) {
+ my $err = $@ || 'Unknown error';
+ warn "Missing GeoIP Country data:$err\n";
+ }
+ }
+}
+
+sub init_my_country_code {
+ my $self = shift;
+ my $ip = $self->{_args}{distance} or return;
+ $self->{_my_country_code} = $self->get_country_code($ip);
+}
+
+sub set_country_code {
+ my $self = shift;
+ my $ip = $self->qp->connection->remote_ip;
+ my $code = $self->get_country_code($ip) or return;
+ $self->qp->connection->notes('geoip_country', $code);
+ return $code;
+}
+
+sub get_country_code {
+ my $self = shift;
+ my $ip = shift || $self->qp->connection->remote_ip;
+ if ($self->{_geoip_city}) {
+ return $self->get_country_code_gc($ip);
+ }
+ if ($self->{_geoip}) {
+ return $self->{_geoip}->country_code_by_addr($ip);
+ }
+ return undef;
+}
+
+sub get_country_code_gc {
+ my $self = shift;
+ my $ip = shift || $self->qp->connection->remote_ip;
+ $self->{_geoip_record} = $self->{_geoip_city}->record_by_addr($ip)
+ or return;
+ return $self->{_geoip_record}->country_code;
+}
+
+sub set_country_name {
+ my $self = shift;
+ my $ip = $self->qp->connection->remote_ip;
+ my $name = $self->get_country_name($ip) or return;
+ $self->qp->connection->notes('geoip_country_name', $name);
+ return $name;
+}
+
+sub get_country_name {
+ my $self = shift;
+ my $ip = shift || $self->qp->connection->remote_ip;
+ if ($self->{_geoip_city}) {
+ return $self->get_country_name_gc($ip);
+ }
+ if ($self->{_geoip}) {
+ return $self->{_geoip}->country_name_by_addr($ip);
+ }
+ return undef;
+}
+
+sub get_country_name_gc {
+ my $self = shift;
+ return if !$self->{_geoip_record};
+ return $self->{_geoip_record}->country_name();
+}
+
+sub set_continent {
+ my ($self, $country_code) = @_;
+ return if !$country_code;
+ my $continent = $self->get_continent($country_code) or return;
+ $self->qp->connection->notes('geoip_continent', $continent);
+ return $continent;
+}
+
+sub get_continent {
+ my ($self, $country_code) = @_;
+ return if !$country_code;
+ if ($self->{_geoip_city}) {
+ return $self->get_continent_gc();
+ }
+ if ($self->{_geoip}) {
+ return $self->{_geoip}->continent_code_by_country_code($country_code);
+ }
+ return undef;
+}
+
+sub get_continent_gc {
+ my $self = shift;
+ return if !$self->{_geoip_record};
+ return $self->{_geoip_record}->continent_code();
+}
+
+sub set_asn {
+ my ($self, $ip) = @_;
+ $ip ||= $self->qp->connection->remote_ip;
+
+ if ($self->is_ipv6($ip)) {
+ return $self->set_asn_ipv6($ip);
+ }
+ return if ! $self->{GeoIPASNum};
+ return if ! $self->{GeoIPASNum}->can('name_by_addr');# prior Geo-IP 1.39 should use org_by_addr
+
+ my $asn = $self->{GeoIPASNum}->name_by_addr($ip) or return;
+ if ('AS' eq substr($asn, 0, 2)) {
+ $asn = substr($asn, 2);
+ }
+ $self->qp->connection->notes('geoip_asn', $asn);
+ return $asn;
+}
+
+sub set_asn_ipv6 {
+ my ($self, $ip) = @_;
+ $ip ||= $self->qp->connection->remote_ip;
+
+ return if ! $self->{GeoIPASNumv6};
+
+ my $asn = $self->{GeoIPASNumv6}->name_by_addr_v6($ip) or return;
+ $self->qp->connection->notes('geoip_asn', $asn);
+ return $asn;
+}
+
+sub set_city_gc {
+ my $self = shift;
+ return if !$self->{_geoip_record};
+ my $city = $self->{_geoip_record}->city() or return;
+ $self->qp->connection->notes('geoip_city', $city);
+ return $city;
+}
+
+sub set_distance_gc {
+ my $self = shift;
+ return if !$self->{_geoip_record};
+
+ my ($self_lat, $self_lon) = $self->get_my_lat_lon() or return;
+ my ($sender_lat, $sender_lon) = $self->get_sender_lat_lon() or return;
+
+ eval 'use Math::Trig qw(great_circle_distance deg2rad)';
+ if ($@) {
+ $self->log(LOGERROR,
+ "can't calculate distance, Math::Trig not installed");
+ return;
+ }
+
+ # Notice the 90 - latitude: phi zero is at the North Pole.
+ sub NESW { deg2rad($_[0]), deg2rad(90 - $_[1]) }
+ my @me = NESW($self_lon, $self_lat);
+ my @sender = NESW($sender_lon, $sender_lat);
+ my $km = great_circle_distance(@me, @sender, 6378);
+ $km = sprintf("%.0f", $km);
+
+ $self->qp->connection->notes('geoip_distance', $km);
+
+ #$self->log( LOGINFO, "distance $km km");
+ return $km;
+}
+
+sub get_my_lat_lon {
+ my $self = shift;
+ return if !$self->{_geoip_city};
+
+ if ($self->{_latitude} && $self->{_longitude}) {
+ return $self->{_latitude}, $self->{_longitude}; # cached
+ }
+
+ my $ip = $self->{_args}{distance} or return;
+ my $record = $self->{_geoip_city}->record_by_addr($ip) or do {
+ $self->log(LOGERROR, "no record for my Geo::IP location");
+ return;
+ };
+
+ $self->{_latitude} = $record->latitude();
+ $self->{_longitude} = $record->longitude();
+
+ if (!$self->{_latitude} || !$self->{_longitude}) {
+ $self->log(LOGNOTICE, "could not get my lat/lon");
+ }
+ return $self->{_latitude}, $self->{_longitude};
+}
+
+sub get_sender_lat_lon {
+ my $self = shift;
+
+ my $lat = $self->{_geoip_record}->latitude();
+ my $lon = $self->{_geoip_record}->longitude();
+ if (!$lat || !$lon) {
+ $self->log(LOGNOTICE, "could not get sender lat/lon");
+ return;
+ }
+ return $lat, $lon;
+}
diff --git a/smeserver-geoip.spec b/smeserver-geoip.spec
new file mode 100644
index 0000000..0e0d439
--- /dev/null
+++ b/smeserver-geoip.spec
@@ -0,0 +1,198 @@
+%define name smeserver-geoip
+%define version 1.2
+%define release 19
+
+Summary: SME Server geoip plugin
+Name: %{name}
+Version: %{version}
+Release: %{release}%{?dist}
+License: GPL
+Group: Email
+Source: %{name}-%{version}.tar.xz
+Packager: Doug Kruhm
+BuildRoot: /var/tmp/%{name}-%{version}-%{release}-buildroot
+BuildArchitectures: noarch
+Requires: perl-GeoIP2
+Requires: libmaxminddb >= 1.1.1
+Requires: libmaxminddb-devel >= 1.1.1
+Requires: geoipupdate
+Requires: geolite2-country
+Requires: geolite2-city
+# for legacy
+Requires: perl-Geo-IP
+Requires: GeoIP >= 1.6.5
+#
+Provides: smeserver-geoip2 = %{version}-%{release}
+PRovides: smeserver-geoip-legacy = %{version}-%{release}
+BuildRequires: e-smith-devtools >= 1.13.1-03
+
+%description
+The GEOIP plugin lets us know which country our mail server is receiving mail from. If we're receiving too much spam from a particular country, this will help track it down and then use that info to reject connections from that country. This ends up taking the load off our servers.
+
+CHANGE THE CONFIG DB: config setprop qpsmtpd BadCountries (i.e. config setprop qpsmtpd BadCountries BR)
+SIGNAL EVENT: signal-event email-update
+
+#----------------------------------------------------
+#%package -n smeserver-geoip-legacy
+#Summary: SME Server geoip plugin v1
+#Group: Email
+#Requires: perl-Geo-IP
+#Requires: GeoIP >= 1.6.5
+#Requires: smeserver-geoip = %{version}-%{release}
+#BuildRequires: e-smith-devtools >= 1.13.1-03
+#%description -n smeserver-geoip-legacy
+#Package to use the legacy plugin. DB are not updated since 2018
+#The GEOIP plugin lets us know which country our mail server is receiving mail from. If we're receiving too much spam from a particular country, this will help track it down and then use that info to reject connections from that country. This ends up taking the load off our servers.
+#----------------------------------------------------
+
+%changelog
+* Sat Sep 07 2024 cvs2git.sh aka Brian Read 1.2-19.sme
+- Roll up patches and move to git repo [SME: 12338]
+
+* Sat Sep 07 2024 BogusDateBot
+- Eliminated rpmbuild "bogus date" warnings due to inconsistent weekday,
+ by assuming the date is correct and changing the weekday.
+ Thu Jun 16 2007 --> Thu Jun 14 2007 or Sat Jun 16 2007 or Thu Jun 21 2007 or ....
+
+* Thu Mar 18 2021 Brian Read 1.2-18.sme
+- Add expand template for the qpsmtpd peers [SME: 11023]
+
+* Sun Mar 14 2021 Jean-Philippe Pialasse 1.2-17.sme
+- merge legacy with main as we have few packages still using legacy [SME: 11023]
+ those are php*-pecl-geoip proftpd bind-libs* bind-utils.
+
+* Sat Mar 13 2021 Jean-Philipe Pialasse 1.2-16.sme
+- rebuild for SME10 [SME: 11023]
+ make geoip2 default
+ create geoip-legacy package with old geoip1 stuffs
+ smeserver-geoip(-legacy)-update events
+
+* Tue Oct 06 2020 Brian Read 1.2-15.sme
+- Import to SME10 tree [SME: 11023]
+
+* Wed Jan 22 2020 John Crisp 1.2-14.sme
+- Change template from EditionID to ProductID
+- fix 20databasestore has a trailing tilde
+
+* Tue Jan 21 2020 Jean-Philipe Pialasse 1.2-13.sme
+- add support for mmdblookup [SME: 10740]
+- add support for download with AccountID and LicenseKey [SME: 10859]
+
+* Sun Oct 20 2019 Jean-Philipe Pialasse 1.2-10.sme
+- fix country not logged if no badcountries defined [SME: 10815]
+
+* Fri Oct 18 2019 Jean-Philipe Pialasse 1.2-9.sme
+- revert blocking country for geoipv1 qpsmtpd plugin [SME: 10820]
+
+* Thu Jan 24 2019 Jean-Philipe Pialasse 1.2-8.sme
+- make smeserver-geoip2 requires smeserver-geoip [SME: 10691]
+ this will avoid having both packages sharing few files.
+
+* Tue Jan 15 2019 John Crisp 1.2-7.sme
+- Update check_badcountries to work with mailstats
+
+* Sat Jan 05 2019 Jean-Philipe Pialasse 1.2-6.sme
+- remove crontab fragment for v1 [SME: 10691]
+
+* Sat Jan 05 2019 Jean-Philipe Pialasse 1.2-5.sme
+- fix wrong loglevel LOGINFO instead of LOGNOTICE needed [SME: 10679]
+- try first db v2 and back on v1 if not available [SME: 10691]
+- workaround FATAL PLUGIN ERROR when method name_by_addr not existing priori geo-ip 1.39 [SME: 10691]
+
+* Fri Jan 04 2019 Jean-Philipe Pialasse 1.2-4.sme
+- split smeserver-geoip into smeserver-geoip and smeserver-geoip2 for compatibility [SME: 10691]
+- TODO: workaround to find for plugin compatibility
+
+* Mon Oct 29 2018 John Crisp 1.2-3.sme
+- Fix Use of uninitialized value $city in string eq
+- Fix createlinks geoip-update action
+
+* Sat Jun 23 2018 John Crisp 1.2-2.sme
+- Fix typo in createlinks
+
+* Tue Jun 12 2018 John Crisp 1.2-1.sme
+- Update to latest v2 DBs [SME: 9033]
+- add geocity.pl and geocountry.pl test files
+
+* Wed Feb 15 2017 Jean-Philipe Pialasse 1.1.2-7.sme
+- update requirement for GeoIP 1.6.5 [SME: 9714]
+
+* Thu Dec 22 2016 John Crisp 1.1.2-6
+- Use newer versions of GeoIP databases [SME: 9714]
+
+* Wed Sep 23 2015 John Crisp 1.1.2-5
+- Add ability to whitelist an IP [SME:8981]
+
+* Tue Sep 22 2015 stephane de Labrusse 1.1.2-4
+- the event geoip-update download the geoip database
+- added smeserver-geoip-1.1.2.geoip-update-event.patch
+
+* Sun Jun 14 2015 stephane de Labrusse 1.1.2-2
+- added createlinks
+- added actions and crontab to download geoip database
+
+* Mon Sep 03 2012 Doug Kruhm 1.0.5
+- defining $country to reduce noise if not found [SME: 5011]
+- fixed misspelling in response if country is blocked [SME: 7058]
+- cleaning up versioning to MAJOR-MINOR-PATCH
+
+* Fri May 01 2009 Doug Kruhm 1.0.0-04
+- added response to connecting server if blocked [SME: 5011]
+
+* Fri May 01 2009 Doug Kruhm 1.0.0-03
+- added line to reduce log noise [SME: 5011]
+
+* Fri May 01 2009 Doug Kruhm 1.0.0-02
+- db defaults migrated from SPEC file to rpm files [SME: 5193]
+- removed %post and %postun macros [SME: 5194]
+- added Requires perl-Geo-IP [SME: 1866]
+- added e-smith-devtools as a requirement [SME: 1866]
+
+* Sat Jun 16 2007 Doug Kruhm
+ Thu Jun 16 2007 --> Thu Jun 14 2007 or Sat Jun 16 2007 or Thu Jun 21 2007 or ....
+- 1.0.0-01
+- Original version
+
+
+%prep
+%setup
+rm -rf root/var/
+# commented for legacy
+#mkdir -p root/etc/e-smith/events/smeserver-geoip-legacy
+
+%build
+perl createlinks
+
+%install
+rm -rf $RPM_BUILD_ROOT
+(cd root ; find . -depth -print | cpio -dump $RPM_BUILD_ROOT)
+rm -f %{name}-%{version}-filelist
+/sbin/e-smith/genfilelist $RPM_BUILD_ROOT \
+ --file /usr/bin/geoiplook 'attr(0755,root,root)' \
+> %{name}-%{version}-filelist
+
+cat %{name}-%{version}-filelist \
+|grep -v 'etc/e-smith/events/smeserver-geoip-legacy' \
+> %{name}-%{version}-filelist-base
+#cat %{name}-%{version}-filelist \
+#|grep 'etc/e-smith/events/smeserver-geoip-legacy' \
+#> %{name}-%{version}-filelist-legacy
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+
+%postun
+
+%files -f %{name}-%{version}-filelist-base
+%defattr(-,root,root)
+
+
+
+#%files -n smeserver-geoip-legacy -f %{name}-%{version}-filelist-legacy
+#%defattr(-,root,root,-)
+
+
+