Merge pull request #654 from TV4Fun/emailonly

Add 'emailonly' client to send status emails without needing a DDNS service
This commit is contained in:
Richard Hansen 2024-05-25 15:13:36 -04:00 committed by GitHub
commit d1068bede1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 127 additions and 2 deletions

View file

@ -41,6 +41,8 @@ repository history](https://github.com/ddclient/ddclient/commits/master).
* The second and subsequent lines in a multi-line log message are now prefixed
with a `|` character.
[#676](https://github.com/ddclient/ddclient/pull/676)
* `emailonly`: New `protocol` option that simply emails you when your IP
address changes. [#654](https://github.com/ddclient/ddclient/pull/654)
### Bug fixes

View file

@ -65,6 +65,7 @@ handwritten_tests = \
t/builtinfw_query.pl \
t/get_ip_from_if.pl \
t/geturl_connectivity.pl \
t/interval_expired.pl \
t/is-and-extract-ipv4.pl \
t/is-and-extract-ipv6.pl \
t/is-and-extract-ipv6-global.pl \

View file

@ -391,3 +391,9 @@ ssl=yes # use ssl-support. Works with
# password=ddns_password
# redirect=2
# example.com
##
## Email Only
##
# protocol=emailonly
# host.example.com

View file

@ -104,7 +104,7 @@ my $programd = $0;
$programd =~ s%^.*/%%;
my $program = $programd;
$program =~ s/d$//;
my $now = time;
our $now = time;
my $hostname = hostname();
# subst_var(subst, default) returns subst unless it looks like @foo@ in which case it returns
@ -140,7 +140,8 @@ $ENV{'PATH'} = (exists($ENV{PATH}) ? "$ENV{PATH}:" : "") . "/sbin:/usr/sbin:/bin
our %globals;
our %config;
my ($result, %cache);
our %cache;
my $result;
my $saved_cache;
my %saved_opt;
my $daemon;
@ -1066,6 +1067,18 @@ my %services = (
%{$variables{'service-common-defaults'}},
},
},
'emailonly' => {
'updateable' => undef,
'update' => \&nic_emailonly_update,
'examples' => \&nic_emailonly_examples,
'variables' => {
%{$variables{'service-common-defaults'}},
'login' => undef,
'password' => undef,
# Change default to never re-notify if IP address has not changed.
'max-interval' => setv(T_DELAY, 0, 0, 'inf', 0),
},
},
);
# Delete undefined variables to make it easier to cancel previously defined variables.
for my $svc (values(%services)) {
@ -2465,6 +2478,8 @@ sub interval {
$value = $1 * 60*60;
} elsif ($value =~ /^(\d+)(days|d)/i) {
$value = $1 * 60*60*24;
} elsif ($value =~ qr/^(?:inf(?:init[ye])?|indefinite(?:ly)?|never|forever|always)$/i) {
$value = 'inf';
} elsif ($value !~ /^\d+$/) {
$value = undef;
}
@ -2473,6 +2488,7 @@ sub interval {
sub interval_expired {
my ($host, $time, $interval) = @_;
return 0 if ($config{$host}{$interval} // 0) == 'inf';
return 1 if !exists $cache{$host};
return 1 if !exists $cache{$host}{$time} || !$cache{$host}{$time};
return 1 if !exists $config{$host}{$interval} || !$config{$host}{$interval};
@ -8076,6 +8092,55 @@ sub nic_infomaniak_update {
}
}
######################################################################
## nic_emailonly_update
##
## Written by Joel Croteau
##
## Do not update Dynamic DNS, only send status emails. Use if you do
## not have a DDNS host, but still want to get emails when your IP
## address changes. Note that you must set the "mail" config option
## and configure sendmail for this to have an effect. At least one
## host must be specified; the host names are mentioned in the email.
######################################################################
sub nic_emailonly_update {
debug("\nnic_emailonly_update -------------------");
# Note: This is logged after $config{$_}{'max-interval'] even if the IP address hasn't changed,
# so it is best to avoid phrasing like, "IP address has changed."
logmsg(email => 1, join("\n", 'Host IP addresses:', map({
my $ipv4 = delete($config{$_}{'wantipv4'});
my $ipv6 = delete($config{$_}{'wantipv6'});
$config{$_}{'status-ipv4'} = 'good' if $ipv4;
$config{$_}{'status-ipv6'} = 'good' if $ipv6;
$config{$_}{'ipv4'} = $ipv4 if $ipv4;
$config{$_}{'ipv6'} = $ipv6 if $ipv6;
$config{$_}{'mtime'} = $now;
sprintf('%30s %s', $_, join(' ', grep(defined($_), $ipv4, $ipv6)));
} @_)));
}
######################################################################
## nic_emailonly_examples
######################################################################
sub nic_emailonly_examples {
return <<"EoEXAMPLE";
o 'emailonly'
The 'emailonly' protocol is a dummy protocol that will send status emails but
not actually issue any dynamic DNS updates. You can use this if you don\'t
have a DDNS host, but still want to get emails when your IP address changes.
For this to have an effect, you must set the 'mail' config option, have
sendmail properly configured on your machine, and specify at least one dummy
hostname.
Example ${program}.conf file entries:
## single host update
mail=me\@example.com
protocol=emailonly
host.example.com
EoEXAMPLE
}
# Execute main() if this file is run as a script or run via PAR (https://metacpan.org/pod/PAR),
# otherwise do nothing. This "modulino" pattern makes it possible to import this file as a module
# and test its functions directly; there's no need for test-only command-line arguments or stdout

51
t/interval_expired.pl Normal file
View file

@ -0,0 +1,51 @@
use Test::More;
SKIP: { eval { require Test::Warnings; } or skip($@, 1); }
eval { require 'ddclient'; } or BAIL_OUT($@);
my $h = 't/interval_expired.pl';
my $default_now = 1000000000;
my @test_cases = (
{
interval => 'inf',
want => 0,
},
{
now => 'inf',
interval => 'inf',
want => 0,
},
{
cache => '-inf',
interval => 'inf',
want => 0,
},
{
cache => undef, # Falsy cache value.
interval => 'inf',
want => 0,
},
{
now => 0,
cache => 0, # Different kind of falsy cache value.
interval => 'inf',
want => 0,
},
);
for my $tc (@test_cases) {
$tc->{now} //= $default_now;
# For convenience, $tc->{cache} is an offset from $tc->{now}, not an absolute time..
my $cachetime = $tc->{now} + $tc->{cache} if defined($tc->{cache});
$ddclient::config{$h} = {'interval' => $tc->{interval}};
%ddclient::config if 0; # suppress spurious warning "Name used only once: possible typo"
$ddclient::cache{$h} = {'cached-time' => $cachetime} if defined($cachetime);
%ddclient::cache if 0; # suppress spurious warning "Name used only once: possible typo"
$ddclient::now = $tc->{now};
$ddclient::now if 0; # suppress spurious warning "Name used only once: possible typo"
my $desc = "now=$tc->{now}, cache=${\($cachetime // 'undef')}, interval=$tc->{interval}";
is(ddclient::interval_expired($h, 'cached-time', 'interval'), $tc->{want}, $desc);
}
done_testing();