Handle hostnames with upper-case letters
[webmin.git] / sendmail / aliases-lib.pl
1 # aliases-lib.pl
2 # Alias file functions
3
4 # aliases_file(&config)
5 # Returns the alias filenames
6 sub aliases_file
7 {
8 if ($config{'alias_file'}) { return [ $config{'alias_file'} ]; }
9 else {
10         local(@afiles, $o);
11         foreach $o (&find_type("O", $_[0])) {
12                 if ($o->{'value'} =~ /^\s*AliasFile=(.*)$/) {
13                         push(@afiles, split(/,/, $1));
14                         }
15                 }
16         map { s/dbm:// } @afiles;
17         return \@afiles;
18         }
19 }
20
21 # list_aliases(files)
22 # Returns an array of data structures, each containing information about
23 # one sendmail alias
24 sub list_aliases
25 {
26 local $jfiles = join(",", @{$_[0]});
27 local $c = $list_aliases_cache{$jfiles};
28 if (!defined($c)) {
29         $c = $list_aliases_cache{$jfiles} = [ ];
30         local $file;
31         local @skip = split(/\s+/, $config{'alias_skip'});
32         foreach $file (@{$_[0]}) {
33                 local $lalias;
34                 local $lnum = 0;
35                 local $cmt;
36                 &open_readfile(AFILE, $file);
37                 while(<AFILE>) {
38                         s/\r|\n//g;     # remove newlines
39                         if (/^\s*#+\s*(.*)/ && &is_table_comment($_, 1)) {
40                                 # A comment line
41                                 $cmt = &is_table_comment($_, 1);
42                                 }
43                         elsif (/^(#*)\s*([^:$ \t]+)\s*:\s*(.*)$/) {
44                                 local(%alias, @values, $v);
45                                 $alias{'line'} = $cmt ? $lnum-1 : $lnum;
46                                 $alias{'eline'} = $lnum;
47                                 $alias{'file'} = $file;
48                                 $alias{'files'} = $_[0];
49                                 $alias{'enabled'} = $1 ? 0 : 1;
50                                 $alias{'name'} = $2;
51                                 $alias{'cmt'} = $cmt;
52                                 $v = $3;
53                                 $alias{'value'} = $v;
54                                 while($v =~ /^\s*,?\s*()"([^"]+)"(.*)$/ ||
55                                       $v =~ /^\s*,?\s*(\|)"([^"]+)"(.*)$/ ||
56                                       $v =~ /^\s*,?\s*()([^,\s]+)(.*)$/) {
57                                         push(@values, $1.$2); $v = $3;
58                                         }
59                                 $alias{'values'} = \@values;
60                                 $alias{'num'} = scalar(@$c);
61                                 if (&indexof($alias{'name'}, @skip) < 0) {
62                                         push(@$c, \%alias);
63                                         $lalias = \%alias;
64                                         }
65                                 $cmt = undef;
66                                 }
67                         elsif (/^(#*)\s+(\S.*)$/ && $lalias &&
68                                ($1 && !$lalias->{'enabled'} ||
69                                 !$1 && $lalias->{'enabled'})) {
70                                 # continuation of last alias
71                                 $lalias->{'eline'} = $lnum;
72                                 local $v = $2;
73                                 $lalias->{'value'} .= $v;
74                                 while($v =~ /^\s*,?\s*()"([^"]+)"(.*)$/ ||
75                                       $v =~ /^\s*,?\s*(\|)"([^"]+)"(.*)$/ ||
76                                       $v =~ /^\s*,?\s*()([^,\s]+)(.*)$/) {
77                                         push(@{$lalias->{'values'}}, $1.$2); $v = $3;
78                                         }
79                                 $cmt = undef;
80                                 }
81                         else {
82                                 # Some other line
83                                 $lalias = undef;
84                                 $cmt = undef;
85                                 }
86                         $lnum++;
87                         }
88                 close(AFILE);
89                 }
90         }
91 return @$c;
92 }
93
94 # alias_form([alias], [no-comment])
95 # Display a form for editing or creating an alias. Each alias can map to
96 # 1 or more programs, files, lists or users
97 sub alias_form
98 {
99 local ($a, $nocmt, $afile) = @_;
100 local (@values, $v, $type, $val, @typenames);
101 if ($a) { @values = @{$a->{'values'}}; }
102 @typenames = map { $text{"aform_type$_"} } (0 .. 6);
103 $typenames[0] = "&lt;$typenames[0]&gt;";
104
105 # Start of form and table
106 print &ui_form_start("save_alias.cgi", "post");
107 if ($a) {
108         print &ui_hidden("num", $a->{'num'}),"\n";
109         }
110 else {
111         print &ui_hidden("new", 1),"\n";
112         }
113 print &ui_table_start($a ? $text{'aform_edit'}
114                          : $text{'aform_create'}, undef, 2);
115
116 # Description
117 if (!$nocmt) {
118         print &ui_table_row(&hlink($text{'aform_cmt'},"alias_cmt"),
119                 &ui_textbox("cmt", $a ? $a->{'cmt'} : undef, 50));
120         }
121
122
123 # Alias name
124 print &ui_table_row(&hlink($text{'aform_name'},"alias_name"),
125                     &ui_textbox("name", $a ? $a->{'name'} : "", 20));
126
127 # Enabled flag
128 print &ui_table_row(&hlink($text{'aform_enabled'}, "alias_enabled"),
129                     &ui_yesno_radio("enabled", !$a || $a->{'enabled'} ? 1 : 0));
130
131 # Alias file, if more than one possible
132 if ($afile && @$afile > 1) {
133         print &ui_table_row(&hlink($text{'aform_file'}, "alias_file"),
134                 &ui_select("afile", undef, $afile));
135         }
136
137 # Destinations
138 local @typeopts;
139 for($j=0; $j<@typenames; $j++) {
140         if (!$j || $access{"aedit_$j"}) {
141                 push(@typeopts, [ $j, $typenames[$j] ]);
142                 }
143         }
144 for($i=0; $i<=@values; $i++) {
145         ($type, $val) = $values[$i] ? &alias_type($values[$i]) : (0, "");
146
147         local $typesel = &ui_select("type_$i", $type, \@typeopts);
148         local $valtxt = &ui_textbox("val_$i", $val, 30);
149         local $edlnk;
150         if ($type == 2 && $a) {
151                 $edlnk = "<a href='edit_afile.cgi?file=$val&num=$a->{'num'}'>".
152                          "$text{'aform_afile'}</a>\n";
153                 }
154         elsif ($type == 5 && $a) {
155                 $edlnk = "<a href='edit_rfile.cgi?file=$val&num=$a->{'num'}'>".
156                          "$text{'aform_afile'}</a>\n";
157                 }
158         elsif ($type == 6 && $a) {
159                 $edlnk = "<a href='edit_ffile.cgi?file=$val&num=$a->{'num'}'>".
160                          "$text{'aform_afile'}</a>\n";
161                 }
162         print &ui_table_row(&hlink($text{'aform_val'},"alias_to"),
163                               $typesel."\n".$valtxt."\n".$edlnk);
164         }
165
166 # Table and form end
167 print &ui_table_end();
168 if ($a) {
169         print &ui_form_end([ [ "save", $text{'save'} ],
170                              [ "delete", $text{'delete'} ] ]);
171         }
172 else {
173         print &ui_form_end([ [ "create", $text{'create'} ] ]);
174         }
175 }
176
177 # create_alias(&details, &files, [norebuild])
178 # Create a new alias
179 sub create_alias
180 {
181 &list_aliases($_[1]);   # force cache init
182
183 # Update the config file
184 local(%aliases);
185 $_[0]->{'file'} ||= $_[1]->[0];
186 local $lref = &read_file_lines($_[0]->{'file'});
187 $_[0]->{'line'} = scalar(@$lref);
188 push(@$lref, &make_table_comment($_[0]->{'cmt'}, 1));
189 local $str = ($_[0]->{'enabled'} ? "" : "# ") . $_[0]->{'name'} . ": " .
190              join(',', map { /\s/ ? "\"$_\"" : $_ } @{$_[0]->{'values'}});
191 push(@$lref, $str);
192 $_[0]->{'eline'} = scalar(@$lref)-1;
193 &flush_file_lines($_[0]->{'file'});
194 if (!$_[2]) {
195         if (!&rebuild_map_cmd($_[0]->{'file'})) {
196                 &system_logged("newaliases >/dev/null 2>&1");
197                 }
198         }
199
200 # Add to the cache
201 local $jfiles = join(",", @{$_[1]});
202 local $c = $list_aliases_cache{$jfiles};
203 $_[0]->{'num'} = scalar(@$c);
204 push(@$c, $_[0]);
205 }
206
207 # delete_alias(&details, [norebuild])
208 # Deletes one mail alias
209 sub delete_alias
210 {
211 # Remove from the file
212 local $lref = &read_file_lines($_[0]->{'file'});
213 local $len = $_[0]->{'eline'} - $_[0]->{'line'} + 1;
214 splice(@$lref, $_[0]->{'line'}, $len);
215 &flush_file_lines($_[0]->{'file'});
216 if (!$_[1]) {
217         if (!&rebuild_map_cmd($_[0]->{'file'})) {
218                 &system_logged("newaliases >/dev/null 2>&1");
219                 }
220         }
221
222 # Remove from the cache
223 local $jfiles = join(",", @{$_[0]->{'files'}});
224 local $c = $list_aliases_cache{$jfiles};
225 local $idx = &indexof($_[0], @$c);
226 splice(@$c, $idx, 1) if ($idx != -1);
227 &renumber_list($c, $_[0], -$len);
228 }
229
230 # modify_alias(&old, &details, [norebuild])
231 # Update some existing alias
232 sub modify_alias
233 {
234 # Add to the file
235 local @newlines;
236 push(@newlines, &make_table_comment($_[1]->{'cmt'}, 1));
237 local $str = ($_[1]->{'enabled'} ? "" : "# ") . $_[1]->{'name'} . ": " .
238              join(',', map { /\s/ ? "\"$_\"" : $_ } @{$_[1]->{'values'}});
239 push(@newlines, $str);
240 local $lref = &read_file_lines($_[0]->{'file'});
241 local $len = $_[0]->{'eline'} - $_[0]->{'line'} + 1;
242 splice(@$lref, $_[0]->{'line'}, $len, @newlines);
243 &flush_file_lines($_[0]->{'file'});
244 if (!$_[2]) {
245         if (!&rebuild_map_cmd($_[0]->{'file'})) {
246                 &system_logged("newaliases >/dev/null 2>&1");
247                 }
248         }
249
250 local $jfiles = join(",", @{$_[0]->{'files'}});
251 local $c = $list_aliases_cache{$jfiles};
252 local $idx = &indexof($_[0], @$c);
253 $_[1]->{'file'} = $_[0]->{'file'};
254 $_[1]->{'line'} = $_[0]->{'line'};
255 $_[1]->{'eline'} = $_[1]->{'line'}+scalar(@newlines)-1;
256 $c->[$idx] = $_[1] if ($idx != -1);
257 &renumber_list($c, $_[0], scalar(@newlines) - $len);
258 }
259
260 # alias_type(string)
261 # Return the type and destination of some alias string
262 sub alias_type
263 {
264 local @rv;
265 if ($_[0] =~ /^\|$module_config_directory\/autoreply.pl\s+(\S+)/) {
266         @rv = (5, $1);
267         }
268 elsif ($_[0] =~ /^\|$module_config_directory\/filter.pl\s+(\S+)/) {
269         @rv = (6, $1);
270         }
271 elsif ($_[0] =~ /^\|(.*)$/) {
272         @rv = (4, $1);
273         }
274 elsif ($_[0] =~ /^(\/.*)$/) {
275         @rv = (3, $1);
276         }
277 elsif ($_[0] =~ /^:include:(.*)$/) {
278         @rv = (2, $1);
279         }
280 else {
281         @rv = (1, $_[0]);
282         }
283 return wantarray ? @rv : $rv[0];
284 }
285
286 # lock_alias_files(&files)
287 sub lock_alias_files
288 {
289 foreach $f (@{$_[0]}) {
290         &lock_file($f);
291         }
292 }
293
294 # unlock_alias_files(&files)
295 sub unlock_alias_files
296 {
297 foreach $f (@{$_[0]}) {
298         &unlock_file($f);
299         }
300 }
301
302 # can_edit_alias(&alias)
303 sub can_edit_alias
304 {
305 local ($a) = @_;
306 foreach my $v (@{$a->{'values'}}) {
307         $access{"aedit_".&alias_type($v)} || return 0;
308         }
309 if ($access{'amode'} == 2) {
310         $a->{'name'} =~ /$access{'aliases'}/ || return 0;
311         }
312 elsif ($access{'amode'} == 3) {
313         $a->{'name'} eq $remote_user || return 0;
314         }
315 return 1;
316 }
317
318 1;
319