]>
Commit | Line | Data |
---|---|---|
26be105b DM |
1 | package PMG::Fetchmail; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
cde18a76 | 5 | use Data::Dumper; |
26be105b DM |
6 | |
7 | use PVE::Tools; | |
8 | use PVE::INotify; | |
9 | ||
cde18a76 DM |
10 | use PMG::Config; |
11 | ||
26be105b DM |
12 | my $inotify_file_id = 'fetchmailrc'; |
13 | my $config_filename = '/etc/pmg/fetchmailrc'; | |
14 | ||
cde18a76 DM |
15 | my $set_fetchmail_defaults = sub { |
16 | my ($item) = @_; | |
17 | ||
18 | $item->{protocol} //= 'pop3'; | |
19 | $item->{interval} //= 1; | |
004cc4f3 | 20 | $item->{enable} //= 0; |
cde18a76 DM |
21 | |
22 | if (!$item->{port}) { | |
23 | if ($item->{protocol} eq 'pop3') { | |
24 | if ($item->{ssl}) { | |
25 | $item->{port} = 995; | |
26 | } else { | |
27 | $item->{port} = 110; | |
28 | } | |
29 | } elsif ($item->{protocol} eq 'imap') { | |
30 | if ($item->{ssl}) { | |
31 | $item->{port} = 993; | |
32 | } else { | |
33 | $item->{port} = 143; | |
34 | } | |
35 | } else { | |
36 | die "unknown fetchmail protocol '$item->{protocol}'\n"; | |
37 | } | |
38 | } | |
39 | ||
40 | return $item; | |
41 | }; | |
42 | ||
26be105b DM |
43 | sub read_fetchmail_conf { |
44 | my ($filename, $fh) = @_; | |
45 | ||
46 | my $cfg = {}; | |
47 | ||
48 | if ($fh) { | |
49 | ||
cde18a76 | 50 | # scan for proxmox marker - skip non proxmox lines |
26be105b | 51 | while (defined(my $line = <$fh>)) { |
cde18a76 | 52 | last if $line =~ m/^\#\s+proxmox\s+settings.*$/; |
26be105b | 53 | } |
cde18a76 DM |
54 | # now parse the rest |
55 | ||
56 | my $data = ''; | |
57 | my $linenr = 0; | |
58 | ||
59 | my $get_next_token = sub { | |
60 | ||
61 | do { | |
96845248 | 62 | while ($data =~ /\G("([^"]*)"|\S+|)(?:\s|$)/g) { |
5072afa7 | 63 | my ($token, $string) = ($1, $2); |
cde18a76 | 64 | if ($1 ne '') { |
719ea622 | 65 | $string =~ s/\\x([0-9A-Fa-f]{2})/chr(hex($1))/eg |
96845248 | 66 | if defined($string); |
5072afa7 | 67 | return wantarray ? ($token, $string) : $token; |
cde18a76 DM |
68 | } |
69 | } | |
70 | $data = <$fh>; | |
71 | $linenr = $fh->input_line_number(); | |
72 | } while (defined($data)); | |
73 | ||
74 | return undef; # EOF | |
75 | }; | |
76 | ||
77 | my $get_token_argument = sub { | |
78 | my ($token, $string) = $get_next_token->(); | |
79 | die "line $linenr: missing token arguemnt\n" if !$token; | |
80 | return $string // $token; | |
81 | }; | |
82 | ||
83 | my $finalize_item = sub { | |
84 | my ($item) = @_; | |
85 | $cfg->{$item->{id}} = $item; | |
86 | }; | |
87 | ||
88 | my $item; | |
89 | while (my ($token, $string) = $get_next_token->()) { | |
90 | last if !defined($token); | |
91 | if ($token eq 'poll' || $token eq 'skip') { | |
92 | $finalize_item->($item) if defined($item); | |
93 | my $id = $get_token_argument->(); | |
94 | $item = { id => $id }; | |
2d4e40c3 | 95 | $item->{enable} = $token eq 'poll' ? 1 : 0; |
cde18a76 DM |
96 | next; |
97 | } | |
98 | ||
99 | die "line $linenr: unexpected token '$token'\n" | |
100 | if !defined($item); | |
101 | ||
102 | if ($token eq 'user') { | |
103 | $item->{user} = $get_token_argument->(); | |
104 | } elsif ($token eq 'via') { | |
105 | $item->{server} = $get_token_argument->(); | |
106 | } elsif ($token eq 'pass') { | |
107 | $item->{pass} = $get_token_argument->(); | |
108 | } elsif ($token eq 'to') { | |
109 | $item->{target} = $get_token_argument->(); | |
110 | } elsif ($token eq 'protocol') { | |
111 | $item->{protocol} = $get_token_argument->(); | |
112 | } elsif ($token eq 'port') { | |
113 | $item->{port} = $get_token_argument->(); | |
114 | } elsif ($token eq 'interval') { | |
115 | $item->{interval} = $get_token_argument->(); | |
116 | } elsif ($token eq 'ssl' || $token eq 'keep' || | |
117 | $token eq 'dropdelivered') { | |
118 | $item->{$token} = 1; | |
119 | } else { | |
120 | die "line $linenr: unexpected token '$token' inside entry '$item->{id}'\n"; | |
121 | } | |
122 | } | |
123 | $finalize_item->($item) if defined($item); | |
26be105b DM |
124 | } |
125 | ||
126 | return $cfg; | |
127 | } | |
128 | ||
129 | sub write_fetchmail_conf { | |
cde18a76 DM |
130 | my ($filename, $fh, $fmcfg) = @_; |
131 | ||
5072afa7 DM |
132 | my $data = {}; |
133 | ||
134 | # Note: we correctly quote data here to make fetchmailrc.tt simpler | |
135 | ||
cde18a76 | 136 | foreach my $id (keys %$fmcfg) { |
5072afa7 DM |
137 | my $org = $fmcfg->{$id}; |
138 | my $item = { id => $id }; | |
139 | foreach my $k (keys %$org) { | |
140 | my $v = $org->{$k}; | |
96845248 | 141 | $v =~ s/([^A-Za-z0-9\:\@\-\._~])/sprintf "\\x%02x",ord($1)/eg; |
5072afa7 DM |
142 | $item->{$k} = $v; |
143 | } | |
cde18a76 DM |
144 | $set_fetchmail_defaults->($item); |
145 | my $options = [ 'dropdelivered' ]; | |
146 | push @$options, 'ssl' if $item->{ssl}; | |
147 | push @$options, 'keep' if $item->{keep}; | |
148 | $item->{options} = join(' ', @$options); | |
5072afa7 | 149 | $data->{$id} = $item; |
cde18a76 | 150 | } |
26be105b DM |
151 | |
152 | my $raw = ''; | |
153 | ||
cde18a76 DM |
154 | my $pmgcfg = PMG::Config->new(); |
155 | my $vars = $pmgcfg->get_template_vars(); | |
5072afa7 | 156 | $vars->{fetchmail_users} = $data; |
cde18a76 DM |
157 | |
158 | my $tt = PMG::Config::get_template_toolkit(); | |
159 | $tt->process('fetchmailrc.tt', $vars, \$raw) || | |
160 | die $tt->error() . "\n"; | |
161 | ||
162 | my (undef, undef, $uid, $gid) = getpwnam('fetchmail'); | |
163 | chown($uid, $gid, $fh) if defined($uid) && defined($gid); | |
164 | chmod(0600, $fh); | |
26be105b DM |
165 | |
166 | PVE::Tools::safe_print($filename, $fh, $raw); | |
167 | } | |
168 | ||
169 | PVE::INotify::register_file( | |
170 | $inotify_file_id, $config_filename, | |
171 | \&read_fetchmail_conf, | |
172 | \&write_fetchmail_conf, | |
173 | undef, | |
174 | always_call_parser => 1); | |
175 | ||
26be105b | 176 | 1; |