use strict;
use warnings;
+use Data::Dumper;
use PVE::Tools;
use PVE::INotify;
+use PMG::Utils;
+use PMG::Config;
+use PMG::ClusterConfig;
+
my $inotify_file_id = 'fetchmailrc';
my $config_filename = '/etc/pmg/fetchmailrc';
+my $config_link_filename = '/etc/fetchmailrc';
+
+my $fetchmail_default_id = 'fetchmail_default';
+my $fetchmail_default_filename = '/etc/default/fetchmail';
+
+sub read_fetchmail_default {
+ my ($filename, $fh) = @_;
+
+ if (defined($fh)) {
+ while (defined(my $line = <$fh>)) {
+ if ($line =~ m/^START_DAEMON=yes\s*$/) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+sub write_fetchmail_default {
+ my ($filename, $fh, $enable) = @_;
+
+ open (my $orgfh, "<", $filename);
+
+ my $wrote_start_daemon = 0;
+
+ my $write_start_daemon_line = sub {
+
+ return if $wrote_start_daemon; # only once
+ $wrote_start_daemon = 1;
+
+ if ($enable) {
+ print $fh "START_DAEMON=yes\n";
+ } else {
+ print $fh "START_DAEMON=no\n";
+ }
+ };
+
+ if (defined($orgfh)) {
+ while (defined(my $line = <$orgfh>)) {
+ if ($line =~ m/^#?START_DAEMON=.*$/) {
+ $write_start_daemon_line->();
+ } else {
+ print $fh $line;
+ }
+ }
+ } else {
+ $write_start_daemon_line->();
+ }
+}
+
+PVE::INotify::register_file(
+ $fetchmail_default_id, $fetchmail_default_filename,
+ \&read_fetchmail_default,
+ \&write_fetchmail_default,
+ undef,
+ always_call_parser => 1);
+
+my $set_fetchmail_defaults = sub {
+ my ($item) = @_;
+
+ $item->{protocol} //= 'pop3';
+ $item->{interval} //= 1;
+ $item->{enable} //= 0;
+
+ if (!$item->{port}) {
+ if ($item->{protocol} eq 'pop3') {
+ if ($item->{ssl}) {
+ $item->{port} = 995;
+ } else {
+ $item->{port} = 110;
+ }
+ } elsif ($item->{protocol} eq 'imap') {
+ if ($item->{ssl}) {
+ $item->{port} = 993;
+ } else {
+ $item->{port} = 143;
+ }
+ } else {
+ die "unknown fetchmail protocol '$item->{protocol}'\n";
+ }
+ }
+
+ return $item;
+};
sub read_fetchmail_conf {
my ($filename, $fh) = @_;
if ($fh) {
+ # scan for proxmox marker - skip non proxmox lines
while (defined(my $line = <$fh>)) {
+ last if $line =~ m/^\#\s+proxmox\s+settings.*$/;
+ }
+ # now parse the rest
+
+ my $data = '';
+ my $linenr = 0;
+
+ my $get_next_token = sub {
+
+ do {
+ while ($data =~ /\G("([^"]*)"|\S+|)(?:\s|$)/g) {
+ my ($token, $string) = ($1, $2);
+ if ($1 ne '') {
+ $string =~ s/\\x([0-9A-Fa-f]{2})/chr(hex($1))/eg
+ if defined($string);
+ return wantarray ? ($token, $string) : $token;
+ }
+ }
+ $data = <$fh>;
+ $linenr = $fh->input_line_number();
+ } while (defined($data));
+
+ return undef; # EOF
+ };
+
+ my $get_token_argument = sub {
+ my ($token, $string) = $get_next_token->();
+ die "line $linenr: missing token arguemnt\n" if !$token;
+ return $string // $token;
+ };
+
+ my $finalize_item = sub {
+ my ($item) = @_;
+ $cfg->{$item->{id}} = $item;
+ };
+ my $item;
+ while (my ($token, $string) = $get_next_token->()) {
+ last if !defined($token);
+ if ($token eq 'poll' || $token eq 'skip') {
+ $finalize_item->($item) if defined($item);
+ my $id = $get_token_argument->();
+ $item = { id => $id };
+ $item->{enable} = $token eq 'poll' ? 1 : 0;
+ next;
+ }
+ die "line $linenr: unexpected token '$token'\n"
+ if !defined($item);
+
+ if ($token eq 'user') {
+ $item->{user} = $get_token_argument->();
+ } elsif ($token eq 'via') {
+ $item->{server} = $get_token_argument->();
+ } elsif ($token eq 'pass') {
+ $item->{pass} = $get_token_argument->();
+ } elsif ($token eq 'to') {
+ $item->{target} = $get_token_argument->();
+ } elsif ($token eq 'protocol') {
+ $item->{protocol} = $get_token_argument->();
+ } elsif ($token eq 'port') {
+ $item->{port} = $get_token_argument->();
+ } elsif ($token eq 'interval') {
+ $item->{interval} = $get_token_argument->();
+ } elsif ($token eq 'ssl' || $token eq 'keep' ||
+ $token eq 'dropdelivered') {
+ $item->{$token} = 1;
+ } else {
+ die "line $linenr: unexpected token '$token' inside entry '$item->{id}'\n";
+ }
}
+ $finalize_item->($item) if defined($item);
}
return $cfg;
}
sub write_fetchmail_conf {
- my ($filename, $fh, $cfg) = @_;
+ my ($filename, $fh, $fmcfg) = @_;
+
+ my $data = {};
+
+ # Note: we correctly quote data here to make fetchmailrc.tt simpler
+
+ my $entry_count = 0;
+
+ foreach my $id (keys %$fmcfg) {
+ my $org = $fmcfg->{$id};
+ my $item = { id => $id };
+ $entry_count++;
+ foreach my $k (keys %$org) {
+ my $v = $org->{$k};
+ $v =~ s/([^A-Za-z0-9\:\@\-\._~])/sprintf "\\x%02x",ord($1)/eg;
+ $item->{$k} = $v;
+ }
+ $set_fetchmail_defaults->($item);
+ my $options = [ 'dropdelivered' ];
+ push @$options, 'ssl' if $item->{ssl};
+ push @$options, 'keep' if $item->{keep};
+ $item->{options} = join(' ', @$options);
+ $data->{$id} = $item;
+ }
my $raw = '';
- my $gid = getgrnam('www-data');
- chown(0, $gid, $fh);
- chmod(0640, $fh);
+ my $pmgcfg = PMG::Config->new();
+ my $vars = $pmgcfg->get_template_vars();
+ $vars->{fetchmail_users} = $data;
+
+ my $tt = PMG::Config::get_template_toolkit();
+ $tt->process('fetchmailrc.tt', $vars, \$raw) ||
+ die $tt->error() . "\n";
+
+ my (undef, undef, $uid, $gid) = getpwnam('fetchmail');
+ chown($uid, $gid, $fh) if defined($uid) && defined($gid);
+ chmod(0600, $fh);
PVE::Tools::safe_print($filename, $fh, $raw);
+
+ update_fetchmail_default($entry_count);
+}
+
+sub update_fetchmail_default {
+ my ($enable) = @_;
+
+ my $cinfo = PMG::ClusterConfig->new();
+
+ my $is_enabled = PVE::INotify::read_file('fetchmail_default');
+ my $role = $cinfo->{local}->{type} // '-';
+ if (($role eq '-') || ($role eq 'master')) {
+ if (!!$enable != !!$is_enabled) {
+ PVE::INotify::write_file('fetchmail_default', $enable);
+ PMG::Utils::service_cmd('fetchmail', 'restart');
+ }
+ if (! -e $config_link_filename) {
+ symlink ($config_filename, $config_link_filename);
+ }
+ } else {
+ if ($is_enabled) {
+ PVE::INotify::write_file('fetchmail_default', 0);
+ }
+ if (-e $config_link_filename) {
+ unlink $config_link_filename;
+ }
+ }
}
PVE::INotify::register_file(
undef,
always_call_parser => 1);
-
-
1;