Handle hostnames with upper-case letters
[webmin.git] / fetchmail / fetchmail-lib.pl
1 # fetchmail-lib.pl
2 # Functions for parsing fetchmail config files
3
4 BEGIN { push(@INC, ".."); };
5 use WebminCore;
6 &init_config();
7 %access = &get_module_acl();
8
9 if ($module_info{'usermin'}) {
10         if ($no_switch_user) {
11                 @remote_user_info = getpwnam($remote_user);
12                 }
13         else {
14                 &switch_to_remote_user();
15                 &create_user_config_dirs();
16                 }
17         $cron_cmd = "$user_module_config_directory/check.pl";
18         $cron_user = $remote_user;
19         $fetchmail_config = "$remote_user_info[7]/.fetchmailrc";
20         $can_cron = $config{'can_cron'};
21         $can_daemon = $config{'can_daemon'};
22         }
23 else {
24         $cron_cmd = "$module_config_directory/check.pl";
25         $cron_user = "root";
26         $fetchmail_config = $config{'config_file'};
27         $can_cron = $access{'cron'};
28         $can_daemon = $access{'daemon'};
29         }
30
31 # parse_config_file(file, [&global])
32 # Parses a fetchmail config file into a list of hashes, each representing
33 # one mail server to poll
34 sub parse_config_file
35 {
36 local $lnum = 0;
37 local ($line, @rv, @toks);
38
39 # Tokenize the file
40 open(FILE, $_[0]);
41 while($line = <FILE>) {
42         $line =~ s/\r|\n//g;
43         $line =~ s/^\s*#.*$//;
44         while($line =~ /^[\s:;,]*"([^"]*)"(.*)$/ ||
45               $line =~ /^[\s:;,]*'([^"]*)'(.*)$/ ||
46               $line =~ /^[\s:;,]*([^\s:;,]+)(.*)$/) {
47                 push(@toks, [ $1, $lnum ]);
48                 $line = $2;
49                 }
50         $lnum++;
51         }
52 close(FILE);
53
54 # Split into poll sections
55 @toks = grep { $_->[0] !~ /^(and|with|has|wants|options|here)$/i } @toks;
56 local ($poll, $user, $i);
57 for($i=0; $i<@toks; $i++) {
58         local $t = $toks[$i];
59
60         # Server options
61         if ($t->[0] eq 'poll' || $t->[0] eq 'server' ||
62             $t->[0] eq 'skip' || $t->[0] eq 'defaults') {
63                 # Start of a new poll
64                 $poll = { 'line' => $t->[1],
65                           'file' => $_[0],
66                           'index' => scalar(@rv),
67                           'skip' => ($t->[0] eq 'skip'),
68                           'defaults' => ($t->[0] eq 'defaults') };
69                 $poll->{'poll'} = $toks[++$i]->[0] if (!$poll->{'defaults'});
70                 undef($user);
71                 push(@rv, $poll);
72                 }
73         elsif ($t->[0] eq 'proto' || $t->[0] eq 'protocol') {
74                 $poll->{'proto'} = $toks[++$i]->[0];
75                 }
76         elsif ($t->[0] eq 'via') {
77                 $poll->{'via'} = $toks[++$i]->[0];
78                 }
79         elsif ($t->[0] eq 'port') {
80                 $poll->{'port'} = $toks[++$i]->[0];
81                 }
82         elsif ($t->[0] eq 'timeout') {
83                 $poll->{'timeout'} = $toks[++$i]->[0];
84                 }
85         elsif ($t->[0] eq 'interface') {
86                 $poll->{'interface'} = $toks[++$i]->[0];
87                 }
88         elsif ($t->[0] eq 'monitor') {
89                 $poll->{'monitor'} = $toks[++$i]->[0];
90                 }
91         elsif ($t->[0] eq 'auth' || $t->[0] eq 'authenticate') {
92                 $poll->{'auth'} = $toks[++$i]->[0];
93                 }
94
95         # User options
96         elsif ($t->[0] eq 'user' || $t->[0] eq 'username') {
97                 $user = { 'user' => $toks[++$i]->[0] };
98                 push(@{$poll->{'users'}}, $user);
99                 }
100         elsif ($t->[0] eq 'pass' || $t->[0] eq 'password') {
101                 $user->{'pass'} = $toks[++$i]->[0];
102                 }
103         elsif ($t->[0] eq 'is' || $t->[0] eq 'to') {
104                 $i++;
105                 while($i < @toks &&
106                       $toks[$i]->[1] == $t->[1]) {
107                         push(@{$user->{'is'}}, $toks[$i]->[0]);
108                         $i++;
109                         }
110                 $i--;
111                 }
112         elsif ($t->[0] eq 'folder') {
113                 $user->{'folder'} = $toks[++$i]->[0];
114                 }
115         elsif ($t->[0] eq 'keep') { $user->{'keep'} = 1; }
116         elsif ($t->[0] eq 'nokeep') { $user->{'keep'} = 0; }
117         elsif ($t->[0] eq 'no' && $toks[$i+1]->[0] eq 'keep') {
118                 $user->{'keep'} = 0;
119                 $i++;
120                 }
121         elsif ($t->[0] eq 'fetchall') { $user->{'fetchall'} = 1; }
122         elsif ($t->[0] eq 'nofetchall') { $user->{'fetchall'} = 0; }
123         elsif ($t->[0] eq 'no' && $toks[$i+1]->[0] eq 'fetchall') {
124                 $user->{'fetchall'} = 0;
125                 $i++;
126                 }
127         elsif ($t->[0] eq 'ssl') { $user->{'ssl'} = 1; }
128         elsif ($t->[0] eq 'nossl') { $user->{'ssl'} = 0; }
129         elsif ($t->[0] eq 'no' && $toks[$i+1]->[0] eq 'ssl') {
130                 $user->{'ssl'} = 0;
131                 $i++;
132                 }
133         elsif ($t->[0] eq 'preconnect') {
134                 $user->{'preconnect'} = $toks[++$i]->[0];
135                 }
136         elsif ($t->[0] eq 'postconnect') {
137                 $user->{'postconnect'} = $toks[++$i]->[0];
138                 }
139
140         else {
141                 # Found an unknown option!
142                 if ($user) {
143                         push(@{$user->{'unknown'}}, $t->[0]);
144                         }
145                 elsif ($poll) {
146                         push(@{$poll->{'unknown'}}, $t->[0]);
147                         }
148                 }
149
150         if ($poll) {
151                 if ($i<@toks) {
152                         $poll->{'eline'} = $toks[$i]->[1];
153                         }
154                 else {
155                         $poll->{'eline'} = $toks[$#toks]->[1];
156                         }
157                 }
158         }
159
160 return @rv;
161 }
162
163 # create_poll(&poll, file)
164 # Add a new poll section to a fetchmail config file
165 sub create_poll
166 {
167 local $lref = &read_file_lines($_[1]);
168 if ($_[0]->{'defaults'}) {
169         # Put a new defaults section at the top
170         splice(@$lref, 0, 0, &poll_lines($_[0]));
171         }
172 else {
173         push(@$lref, &poll_lines($_[0]));
174         }
175 &flush_file_lines();
176 }
177
178 # delete_poll(&poll, file)
179 # Delete a poll section from a fetchmail config file
180 sub delete_poll
181 {
182 local $lref = &read_file_lines($_[1]);
183 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1);
184 &flush_file_lines();
185 }
186
187 # modify_poll(&poll, file)
188 # Modify a poll section in a fetchmail config file
189 sub modify_poll
190 {
191 local $lref = &read_file_lines($_[1]);
192 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1,
193        &poll_lines($_[0]));
194 &flush_file_lines();
195 }
196
197 sub poll_lines
198 {
199 local @rv;
200 local $name = $_[0]->{'poll'};
201 $name = "\"$name\"" if ($name =~ /[\s:;,]/);
202 if ($_[0]->{'skip'}) {
203         push(@rv, "skip $name");
204         }
205 elsif ($_[0]->{'defaults'}) {
206         push(@rv, "defaults $name");
207         }
208 else {
209         push(@rv, "poll $name");
210         }
211 push(@rv, "\tproto $_[0]->{'proto'}") if ($_[0]->{'proto'});
212 push(@rv, "\tauth $_[0]->{'auth'}") if ($_[0]->{'auth'});
213 push(@rv, "\tvia $_[0]->{'via'}") if ($_[0]->{'via'});
214 push(@rv, "\tport $_[0]->{'port'}") if ($_[0]->{'port'});
215 push(@rv, "\ttimeout $_[0]->{'timeout'}") if ($_[0]->{'timeout'});
216 push(@rv, "\tinterface \"$_[0]->{'interface'}\"") if ($_[0]->{'interface'});
217 push(@rv, "\tmonitor $_[0]->{'monitor'}") if ($_[0]->{'monitor'});
218 push(@rv, "\t".join(" ", map { /^\S+$/ ? $_ : "\"$_\"" }
219                              @{$_[0]->{'unknown'}})) if (@{$_[0]->{'unknown'}});
220
221 foreach $u (@{$_[0]->{'users'}}) {
222         push(@rv, "\tuser \"$u->{'user'}\"");
223         push(@rv, "\tpass \"$u->{'pass'}\"") if ($u->{'pass'});
224         push(@rv, "\tis ".join(" ", @{$u->{'is'}})) if (@{$u->{'is'}});
225         push(@rv, "\tfolder $u->{'folder'}") if ($u->{'folder'});
226         push(@rv, "\tkeep") if ($u->{'keep'} eq '1');
227         push(@rv, "\tnokeep") if ($u->{'keep'} eq '0');
228         push(@rv, "\tfetchall") if ($u->{'fetchall'} eq '1');
229         push(@rv, "\tno fetchall") if ($u->{'fetchall'} eq '0');
230         push(@rv, "\tssl") if ($u->{'ssl'} eq '1');
231         push(@rv, "\tno ssl") if ($u->{'ssl'} eq '0');
232         push(@rv, "\tpreconnect \"$u->{'preconnect'}\"")
233                 if ($u->{'preconnect'});
234         push(@rv, "\tpostconnect \"$u->{'postconnect'}\"")
235                 if ($u->{'postconnect'});
236         push(@rv, "\t".join(" ", map { /^\S+$/ ? $_ : "\"$_\"" }
237                              @{$u->{'unknown'}})) if (@{$u->{'unknown'}});
238         }
239
240 return @rv;
241 }
242
243 # can_edit_user(user)
244 sub can_edit_user
245 {
246 local %umap;
247 map { $umap{$_}++; } split(/\s+/, $access{'users'});
248 if ($access{'mode'} == 1 && !$umap{$_[0]} ||
249     $access{'mode'} == 2 && $umap{$_[0]}) { return 0; }
250 elsif ($access{'mode'} == 3) {
251         return $remote_user eq $_[0];
252         }
253 else {
254         return 1;
255         }
256 }
257
258 # get_fetchmail_version([&out])
259 sub get_fetchmail_version
260 {
261 local $out = &backquote_command("$config{'fetchmail_path'} -V 2>&1 </dev/null");
262 ${$_[0]} = $out if ($_[0]);
263 return $out =~ /fetchmail\s+release\s+(\S+)/ ? $1 : undef;
264 }
265
266 # show_polls(&polls, file, user)
267 sub show_polls
268 {
269 if (@{$_[0]}) {
270         print &ui_columns_start([ $text{'index_poll'},
271                                   $text{'index_active'},
272                                   $text{'index_proto'},
273                                   $text{'index_users'} ], 100);
274         foreach $p (@{$_[0]}) {
275                 local @cols;
276                 push(@cols, "<a href='edit_poll.cgi?file=$_[1]&".
277                             "idx=$p->{'index'}&user=$_[2]'>".
278                             &html_escape($p->{'poll'})."</a>");
279                 push(@cols, $p->{'skip'} ?
280                     "<font color=#ff0000>$text{'no'}</font>" : $text{'yes'});
281                 push(@cols, $p->{'proto'} ? &html_escape(uc($p->{'proto'}))
282                                           : $text{'default'});
283                 local $ulist;
284                 foreach $u (@{$p->{'users'}}) {
285                         $ulist .= sprintf "%s -> %s<br>\n",
286                                 &html_escape($u->{'user'}),
287                                 &html_escape(@{$u->{'is'}} ?
288                                    join(" ", @{$u->{'is'}}) : $_[2]);
289                         }
290                 push(@cols, $ulist);
291                 print &ui_columns_row(\@cols);
292                 }
293         print &ui_columns_end();
294         }
295 local @links = (
296   "<a href='edit_poll.cgi?new=1&file=$_[1]&user=$_[2]'>$text{'index_add'}</a>",
297   "<a href='edit_global.cgi?file=$_[1]&user=$_[2]'>$text{'index_global'}</a>"
298         );
299 if (@{$_[0]}) {
300         push(@links, "<a href='check.cgi?file=$_[1]&user=$_[2]'>$text{'index_run'}</a>");
301         }
302 print &ui_links_row(\@links);
303 }
304
305
306 1;
307