Handle hostnames with upper-case letters
[webmin.git] / qmailadmin / qmail-lib.pl
1 # qmail-lib.pl
2 # Common functions for parsing qmail config files
3
4 BEGIN { push(@INC, ".."); };
5 use WebminCore;
6 &init_config();
7 do 'boxes-lib.pl';
8
9 $qmail_alias_dir = "$config{'qmail_dir'}/alias";
10 $qmail_bin_dir = "$config{'qmail_dir'}/bin";
11 $qmail_control_dir = "$config{'qmail_dir'}/control";
12 $qmail_routes_file = "$qmail_control_dir/smtproutes";
13 $qmail_virts_file = "$qmail_control_dir/virtualdomains";
14 $qmail_start_cmd = "$config{'qmail_dir'}/rc";
15 $qmail_mess_dir = "$config{'qmail_dir'}/queue/mess";
16 $qmail_info_dir = "$config{'qmail_dir'}/queue/info";
17 $qmail_local_dir = "$config{'qmail_dir'}/queue/local";
18 $qmail_remote_dir = "$config{'qmail_dir'}/queue/remote";
19 $qmail_users_dir = "$config{'qmail_dir'}/users";
20 $qmail_assigns_file = "$config{'qmail_dir'}/users/assign";
21
22 $config{'perpage'} ||= 20;      # a value of 0 can cause problems
23
24 # list_aliases()
25 # Returns a list of qmail alias file names
26 sub list_aliases
27 {
28 local @rv;
29 opendir(DIR, $qmail_alias_dir);
30 foreach $f (readdir(DIR)) {
31         next if ($f !~ /^\.qmail-(\S+)$/);
32         push(@rv, $1);
33         }
34 closedir(DIR);
35 return @rv;
36 }
37
38 # get_alias(name)
39 sub get_alias
40 {
41 local $alias = { 'name' => $_[0],
42                  'file' => "$qmail_alias_dir/.qmail-$_[0]",
43                  'values' => [ ] };
44 open(ALIAS, $alias->{'file'});
45 while(<ALIAS>) {
46         s/\r|\n//g;
47         s/#.*$//g;
48         if (/\S/) {
49                 push(@{$alias->{'values'}}, $_);
50                 }
51         }
52 close(ALIAS);
53 return $alias;
54 }
55
56 # alias_form([alias])
57 # Display a form for editing or creating an alias. Each alias can map to
58 # 1 or more programs, files, lists or users
59 sub alias_form
60 {
61 local($a, @values, $v, $type, $val, @typenames);
62 $a = $_[0];
63 if ($a) { @values = @{$a->{'values'}}; }
64 @typenames = map { $text{"aform_type$_"} } (0 .. 6);
65 $typenames[0] = "&lt;$typenames[0]&gt;";
66
67 print "<form method=post action=save_alias.cgi>\n";
68 if ($a) { print "<input type=hidden name=old value='$a->{'name'}'>\n"; }
69 else { print "<input type=hidden name=new value=1>\n"; }
70 print "<table border>\n";
71 print "<tr $tb> <td><b>",$a ? $text{'aform_edit'}
72                             : $text{'aform_create'},"</b></td> </tr>\n";
73 print "<tr $cb> <td><table>\n";
74
75 print "<tr> <td><b>$text{'aform_name'}</b></td>\n";
76 local $n = $a ? $a->{'name'} : '';
77 $n =~ s/:/./g;
78 local @virts = grep { $_->{'domain'} && !$_->{'user'} && $_->{'prepend'} }
79                     &list_virts();
80 if ($n =~ /^(\S+)-(\S+)$/) {
81         local ($pn, $bn) = ($1, $2);
82         foreach $v (@virts) {
83                 if ($v->{'prepend'} eq $pn) {
84                         $virt = $v;
85                         $n = $bn;
86                         last;
87                         }
88                 }
89         }
90 if (@virts) {
91         printf "<td><input name=name size=20 value=\"%s\">\@", $n;
92         print "<select name=virt>\n";
93         printf "<option value='' %s>%s\n",
94                 $virt ? "" : "checked", $text{'aform_novirt'};
95         foreach $v (@virts) {
96                 printf "<option value='%s' %s>%s\n",
97                         $v->{'prepend'}, $virt eq $v ? "selected" : "",
98                         $v->{'domain'};
99                 }
100         print "</select></td> </tr>\n";
101         }
102 else {
103         printf "<td><input name=name size=20 value=\"%s\"></td> </tr>\n", $n;
104         }
105
106 for($i=0; $i<=@values; $i++) {
107         ($type, $val) = $values[$i] ? &alias_type($values[$i]) : (0, "");
108         print "<tr> <td valign=top><b>$text{'aform_val'}</b></td>\n";
109         print "<td><select name=type_$i>\n";
110         for($j=0; $j<@typenames; $j++) {
111                 printf "<option value=$j %s>$typenames[$j]\n",
112                         $type == $j ? "selected" : "";
113                 }
114         print "</select>\n";
115         print "<input name=val_$i size=30 value=\"$val\">\n";
116         if ($type == 5 && $a) {
117                 print "<a href='edit_rfile.cgi?file=$val&name=$a->{'name'}'>",
118                       "$text{'aform_afile'}</a>\n";
119                 }
120         elsif ($type == 6 && $a) {
121                 print "<a href='edit_ffile.cgi?file=$val&name=$a->{'name'}'>",
122                       "$text{'aform_afile'}</a>\n";
123                 }
124         print "</td> </tr>\n";
125         }
126 print "<tr> <td colspan=2 align=right>\n";
127 if ($a) {
128         print "<input type=submit value=$text{'save'}>\n";
129         print "<input type=submit name=delete value=$text{'delete'}>\n";
130         }
131 else { print "<input type=submit value=$text{'create'}>\n"; }
132 print "</td> </tr>\n";
133 print "</table></td></tr></table></form>\n";
134 }
135
136 # alias_type(string)
137 # Return the type and destination of some alias string
138 sub alias_type
139 {
140 local @rv;
141 if ($_[0] =~ /^\&(.*)/) {
142         @rv = (1, $1);
143         }
144 elsif ($_[0] =~ /^\|$module_config_directory\/autoreply.pl\s+(\S+)/) {
145         @rv = (5, $1);
146         }
147 elsif ($_[0] =~ /^\|$module_config_directory\/filter.pl\s+(\S+)/) {
148         @rv = (6, $1);
149         }
150 elsif ($_[0] =~ /^\|(.*)$/) {
151         @rv = (4, $1);
152         }
153 elsif ($_[0] =~ /^(\/.*)\/$/) {
154         @rv = (2, $1);
155         }
156 elsif ($_[0] =~ /^(\/.*)$/) {
157         @rv = (3, $1);
158         }
159 else {
160         @rv = (1, $_[0]);
161         }
162 return wantarray ? @rv : $rv[0];
163 }
164
165 # create_alias(&alias)
166 # Creates a new qmail alias file
167 sub create_alias
168 {
169 local $f = "$qmail_alias_dir/.qmail-$_[0]->{'name'}";
170 &open_lock_tempfile(FILE, ">$f");
171 foreach $v (@{$_[0]->{'values'}}) {
172         &print_tempfile(FILE, $v,"\n");
173         }
174 &close_tempfile(FILE);
175 &set_ownership_permissions(undef, undef, 0644, $f);
176 }
177
178 # modify_alias(&old, &alias)
179 # Modifies an existing qmail alias
180 sub modify_alias(&old, &alias)
181 {
182 if ($_[0]->{'name'} ne $_[1]->{'name'}) {
183         &lock_file($_[0]->{'file'});
184         unlink($_[0]->{'file'});
185         &unlock_file($_[0]->{'file'});
186         }
187 &create_alias($_[1]);
188 }
189
190 # delete_alias(&alias)
191 # Deletes an existing qmail alias file
192 sub delete_alias
193 {
194 &lock_file($_[0]->{'file'});
195 unlink("$_[0]->{'file'}");
196 &unlock_file($_[0]->{'file'});
197 }
198
199 # get_control_file(file)
200 # Returns the value from a qmail single-line control file
201 sub get_control_file
202 {
203 open(FILE, "$qmail_control_dir/$_[0]") || return undef;
204 local $line = <FILE>;
205 close(FILE);
206 $line =~ s/\r|\n//g;
207 return $line;
208 }
209
210 # set_control_file(file, value)
211 # Sets the value in a qmail single-line control file
212 sub set_control_file
213 {
214 &lock_file("$qmail_control_dir/$_[0]");
215 if (defined($_[1])) {
216         &open_tempfile(FILE, ">$qmail_control_dir/$_[0]");
217         &print_tempfile(FILE, $_[1],"\n");
218         &close_tempfile(FILE);
219         }
220 else {
221         unlink("$qmail_control_dir/$_[0]");
222         }
223 &unlock_file("$qmail_control_dir/$_[0]");
224 }
225
226 # list_control_file()
227 # Returns the contents of a multi-line control file
228 sub list_control_file
229 {
230 local @lines;
231 open(FILE, "$qmail_control_dir/$_[0]") || return undef;
232 while(<FILE>) {
233         s/\r|\n//g;
234         s/#.*$//g;
235         push(@lines, $_) if (/\S/);
236         }
237 close(FILE);
238 return \@lines;
239
240 }
241
242 # save_control_file(file, &lines)
243 # Saves the contents of a multi-line control file
244 sub save_control_file
245 {
246 &lock_file("$qmail_control_dir/$_[0]");
247 if (defined($_[1])) {
248         &open_tempfile(FILE, ">$qmail_control_dir/$_[0]");
249         foreach $l (@{$_[1]}) {
250                 &print_tempfile(FILE, $l,"\n");
251                 }
252         &close_tempfile(FILE);
253         }
254 else {
255         unlink("$qmail_control_dir/$_[0]");
256         }
257 &unlock_file("$qmail_control_dir/$_[0]");
258 }
259
260 # list_routes()
261 # Returns a list of all SMTP routes
262 sub list_routes
263 {
264 if (!scalar(@list_routes_cache)) {
265         local $lnum = 0;
266         local @rv;
267         open(ROUTES, $qmail_routes_file);
268         while(<ROUTES>) {
269                 s/\r|\n//g;
270                 s/#.*$//;
271                 if (/^(\S*):(\S*):(\d+)/) {
272                         push(@rv, { 'from' => $1,
273                                     'to' => $2,
274                                     'port' => $3,
275                                     'idx' => scalar(@rv),
276                                     'line' => $lnum });
277                         }
278                 elsif (/^(\S*):(\S*)/) {
279                         push(@rv, { 'from' => $1,
280                                     'to' => $2,
281                                     'idx' => scalar(@rv),
282                                     'line' => $lnum });
283                         }
284                 $lnum++;
285                 }
286         close(ROUTES);
287         @list_routes_cache = @rv;
288         }
289 return @list_routes_cache;
290 }
291
292 # create_route(&route)
293 sub create_route
294 {
295 &list_routes(); # force cache init
296 &lock_file($qmail_routes_file);
297 local $lref = &read_file_lines($qmail_routes_file);
298 push(@$lref, $_[0]->{'from'}.':'.$_[0]->{'to'}.
299              ($_[0]->{'port'} ? ':'.$_[0]->{'port'} : ''));
300 &flush_file_lines();
301 &unlock_file($qmail_routes_file);
302
303 # Update in memory cache
304 $_[0]->{'line'} = @$lref-1;
305 $_[0]->{'idx'} = scalar(@list_routes_cache);
306 push(@list_routes_cache, $_[0]);
307 }
308
309 # modify_route(&old, &route)
310 sub modify_route
311 {
312 &lock_file($qmail_routes_file);
313 local $lref = &read_file_lines($qmail_routes_file);
314 splice(@$lref, $_[0]->{'line'}, 1,
315        $_[1]->{'from'}.':'.$_[1]->{'to'}.
316        ($_[1]->{'port'} ? ':'.$_[1]->{'port'} : ''));
317 &flush_file_lines();
318 &unlock_file($qmail_routes_file);
319
320 # Update in memory cache
321 if ($_[0] ne $_[1]) {
322         $_[1]->{'line'} = $_[0]->{'line'};
323         $_[1]->{'idx'} = $_[0]->{'idx'};
324         $list_routes_cache[$_[1]->{'idx'}] = $_[1];
325         }
326 }
327
328 # delete_route(&route)
329 sub delete_route
330 {
331 &lock_file($qmail_routes_file);
332 local $lref = &read_file_lines($qmail_routes_file);
333 splice(@$lref, $_[0]->{'line'}, 1);
334 &flush_file_lines();
335 &unlock_file($qmail_routes_file);
336
337 # delete from cache
338 splice(@list_routes_cache, $_[0]->{'idx'}, 1);
339 foreach my $v (@list_routes_cache) {
340         $v->{'line'}-- if ($v->{'line'} > $_[0]->{'line'});
341         $v->{'idx'}-- if ($v->{'idx'} > $_[0]->{'idx'});
342         }
343 }
344
345 # route_form([&route])
346 sub route_form
347 {
348 print "<form method=post action=save_route.cgi>\n";
349 if ($_[0]) { print "<input type=hidden name=idx value='$_[0]->{'idx'}'>\n"; }
350 else { print "<input type=hidden name=new value=1>\n"; }
351
352 print "<table border>\n";
353 print "<tr $tb> <td><b>",$a ? $text{'rform_edit'}
354                             : $text{'rform_create'},"</b></td> </tr>\n";
355 print "<tr $cb> <td><table>\n";
356 print "<tr> <td><b>$text{'rform_from'}</b></td>\n";
357 printf "<td><input name=from size=30 value='%s'></td> </tr>\n",
358         $_[0] ? $_[0]->{'from'} : "";
359
360 print "<tr> <td><b>$text{'rform_to'}</b></td>\n";
361 printf "<td><input type=radio name=to_def value=1 %s> %s\n",
362         $_[0] && $_[0]->{'to'} ? "" : "checked", $text{'routes_direct'};
363 printf "<input type=radio name=to_def value=0 %s>\n",
364         $_[0] && $_[0]->{'to'} ? "checked" : "";
365 printf "<input name=to size=30 value='%s'></td>\n",
366         $_[0] ? $_[0]->{'to'} : "";
367
368 print "<td><b>$text{'rform_port'}</b></td>\n";
369 printf "<td><input type=radio name=port_def value=1 %s> %s\n",
370         $_[0] && $_[0]->{'port'} ? "" : "checked", $text{'default'};
371 printf "<input type=radio name=port_def value=0 %s>\n",
372         $_[0] && $_[0]->{'port'} ? "checked" : "";
373 printf "<input name=port size=4 value='%s'></td> </tr>\n",
374         $_[0] ? $_[0]->{'port'} : "";
375
376 print "<tr> <td colspan=4 align=right>\n";
377 if ($_[0]) {
378         print "<input type=submit value=$text{'save'}>\n";
379         print "<input type=submit name=delete value=$text{'delete'}>\n";
380         }
381 else { print "<input type=submit value=$text{'create'}>\n"; }
382 print "</td> </tr>\n";
383 print "</table></td></tr></table></form>\n";
384 }
385
386 # list_virts()
387 # Returns a list of all virtualdomains file entries
388 sub list_virts
389 {
390 if (!scalar(@list_virts_cache)) {
391         local $lnum = 0;
392         local @rv;
393         open(VIRTS, $qmail_virts_file);
394         while(<VIRTS>) {
395                 s/\r|\n//g;
396                 s/#.*$//;
397                 if (/^(\S+)\@(\S+):(\S*)/) {
398                         push(@rv, { 'user' => $1,
399                                     'domain' => $2,
400                                     'from' => "$1\@$2",
401                                     'prepend' => $3,
402                                     'line' => $lnum,
403                                     'idx' => scalar(@rv) } );
404                         }
405                 elsif (/^(\S*):(\S*)/) {
406                         push(@rv, { 'domain' => $1,
407                                     'from' => $1,
408                                     'prepend' => $2,
409                                     'line' => $lnum,
410                                     'idx' => scalar(@rv) } );
411                         }
412                 $lnum++;
413                 }
414         close(VIRTS);
415         @list_virts_cache = @rv;
416         }
417 return @list_virts_cache;
418 }
419
420 # create_virt(&virt)
421 sub create_virt
422 {
423 &list_virts();  # force cache init
424 &lock_file($qmail_virts_file);
425 local $lref = &read_file_lines($qmail_virts_file);
426 push(@$lref, join(":", $_[0]->{'user'} ? "$_[0]->{'user'}\@$_[0]->{'domain'}"
427                                        : $_[0]->{'domain'},
428                        $_[0]->{'prepend'}));
429 &flush_file_lines();
430 &unlock_file($qmail_virts_file);
431
432 # Update in memory cache
433 $_[0]->{'line'} = @$lref-1;
434 $_[0]->{'idx'} = scalar(@list_virts_cache);
435 push(@list_virts_cache, $_[0]);
436 }
437
438 # delete_virt(&virt)
439 sub delete_virt
440 {
441 &lock_file($qmail_virts_file);
442 local $lref = &read_file_lines($qmail_virts_file);
443 splice(@$lref, $_[0]->{'line'}, 1);
444 &flush_file_lines();
445 &unlock_file($qmail_virts_file);
446
447 # delete from cache
448 splice(@list_virts_cache, $_[0]->{'idx'}, 1);
449 foreach my $v (@list_virts_cache) {
450         $v->{'line'}-- if ($v->{'line'} > $_[0]->{'line'});
451         $v->{'idx'}-- if ($v->{'idx'} > $_[0]->{'idx'});
452         }
453 }
454
455 # modify_virt(&old, &virt)
456 sub modify_virt
457 {
458 &lock_file($qmail_virts_file);
459 local $lref = &read_file_lines($qmail_virts_file);
460 splice(@$lref, $_[0]->{'line'}, 1,
461              join(":", $_[1]->{'user'} ? "$_[1]->{'user'}\@$_[1]->{'domain'}"
462                                        : $_[1]->{'domain'},
463                        $_[1]->{'prepend'}));
464 &flush_file_lines();
465 &unlock_file($qmail_virts_file);
466
467 # Update in memory cache
468 if ($_[0] ne $_[1]) {
469         $_[1]->{'line'} = $_[0]->{'line'};
470         $_[1]->{'idx'} = $_[0]->{'idx'};
471         $list_virts_cache[$_[1]->{'idx'}] = $_[1];
472         }
473 }
474
475 # virt_form(&virt)
476 sub virt_form
477 {
478 print "<form method=post action=save_virt.cgi>\n";
479 if ($_[0]) { print "<input type=hidden name=idx value='$_[0]->{'idx'}'>\n"; }
480 else { print "<input type=hidden name=new value=1>\n"; }
481
482 print "<table border>\n";
483 print "<tr $tb> <td><b>",$a ? $text{'vform_edit'}
484                             : $text{'vform_create'},"</b></td> </tr>\n";
485 print "<tr $cb> <td><table>\n";
486
487 print "<tr> <td valign=top><b>$text{'vform_from'}</b></td> <td>\n";
488 printf "<input type=radio name=from_mode value=0 %s> %s<br>\n",
489         $_[0] && !$_[0]->{'from'} ? "checked" : "", $text{'vform_all'};
490 printf "<input type=radio name=from_mode value=1 %s> %s\n",
491         !$_[0] || $_[0]->{'domain'} && !$_[0]->{'user'} ? "checked" : "",
492         $text{'vform_domain'};
493 printf "<input name=domain size=20 value='%s'><br>\n",
494         $_[0] && $_[0]->{'domain'} && !$_[0]->{'user'} ? $_[0]->{'domain'} : "";
495 printf "<input type=radio name=from_mode value=2 %s> %s\n",
496         $_[0] && $_[0]->{'user'} ? "checked" : "",
497         $text{'vform_user'};
498 printf "<input name=user size=15 value='%s'>@",
499         $_[0] && $_[0]->{'user'} ? $_[0]->{'user'} : "";
500 printf "<input name=domain2 size=20 value='%s'></td> </tr>",
501         $_[0] && $_[0]->{'user'} ? $_[0]->{'domain'} : "";
502
503 print "<tr> <td valign=top><b>$text{'vform_to'}</b></td> <td>\n";
504 printf "<input type=radio name=prepend_mode value=0 %s> %s<br>\n",
505         $_[0] && !$_[0]->{'prepend'} ? "checked" : "", $text{'vform_none'};
506 if (!$_[0]) {
507         printf "<input type=radio name=prepend_mode value=2 %s> %s<br>\n",
508                 "checked", $text{'vform_auto'};
509         }
510 printf "<input type=radio name=prepend_mode value=1 %s> %s\n",
511         $_[0] && $_[0]->{'prepend'} ? "checked" : "", $text{'vform_prepend'};
512 printf "<input name=prepend size=15 value='%s'></td> </tr>\n",
513         $_[0] && $_[0]->{'prepend'} ? $_[0]->{'prepend'} : "";
514
515 print "<tr> <td colspan=2 align=right>\n";
516 if ($_[0]) {
517         print "<input type=submit value=$text{'save'}>\n";
518         print "<input type=submit name=delete value=$text{'delete'}>\n";
519         }
520 else { print "<input type=submit value=$text{'create'}>\n"; }
521 print "</td> </tr>\n";
522 print "</table></td></tr></table></form>\n";
523 }
524
525 # list_queue()
526 # Returns an array of structures for entries in the mail queue
527 sub list_queue
528 {
529 local (@rv, %qmap);
530 @rv = ( );
531 opendir(DIR, $qmail_mess_dir);
532 foreach $m (readdir(DIR)) {
533         next if ($m !~ /^\d+$/);
534         opendir(DIR2, "$qmail_mess_dir/$m");
535         foreach $m2 (readdir(DIR2)) {
536                 $qmap{$m2} = "$qmail_mess_dir/$m/$m2"
537                         if ($m2 =~ /^\d+$/);
538                 }
539         closedir(DIR2);
540         }
541 closedir(DIR);
542 open(QUEUE, "$qmail_bin_dir/qmail-qread |");
543 while(<QUEUE>) {
544         if (/^(\d+)\s+(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\S+)\s+#(\d+)\s+(\d+)\s+(.*)/) {
545                 local $q = { 'from' => $10,
546                              'id' => $8,
547                              'file' => $qmap{$8},
548                              'date' => "$1 $2 $3 $4:$5:$6" };
549                 $_ = <QUEUE>;
550                 if (/^\s*(\S+)\s+(.*)/) {
551                         $q->{'source'} = $1;
552                         $q->{'to'} = $2;
553                         push(@rv, $q);
554                         }
555                 }
556         }
557 close(QUEUE);
558 return @rv;
559 }
560
561 # wrap_lines(text, width)
562 # Given a multi-line string, return an array of lines wrapped to
563 # the given width
564 sub wrap_lines
565 {
566 local @rv;
567 local $w = $_[1];
568 foreach $rest (split(/\n/, $_[0])) {
569         if ($rest =~ /\S/) {
570                 while($rest =~ /^(.{1,$w}\S*)\s*([\0-\377]*)$/) {
571                         push(@rv, $1);
572                         $rest = $2;
573                         }
574                 }
575         else {
576                 # Empty line .. keep as it is
577                 push(@rv, $rest);
578                 }
579         }
580 return @rv;
581 }
582
583 # link_urls(text)
584 sub link_urls
585 {
586 local $r = $_[0];
587 $r =~ s/((http|ftp|https|mailto):[^><"'\s]+[^><"'\s\.])/<a href="$1">$1<\/a>/g;
588 return $r;
589 }
590
591 # list_assigns()
592 # Returns a list of qmail user assignments
593 sub list_assigns
594 {
595 if (!scalar(@list_assigns_cache)) {
596         local @rv;
597         local $lnum = 0;
598         open(ASSIGNS, $qmail_assigns_file);
599         while(<ASSIGNS>) {
600                 s/\r|\n//g;
601                 last if ($_ eq '.');
602                 local @line = split(/:/, $_, 8);
603                 if ($line[0] =~ /^([\+=])(\S*)/) {
604                         local $asn = { 'address' => $2,
605                                        'mode' => $1,
606                                        'user' => $line[1],
607                                        'uid' => $line[2],
608                                        'gid' => $line[3],
609                                        'home' => $line[4],
610                                        'dash' => $line[5],
611                                        'preext' => $line[6],
612                                        'idx' => scalar(@rv),
613                                        'line' => $lnum };
614                         push(@rv, $asn);
615                         }
616                 $lnum++;
617                 }
618         close(ASSIGNS);
619         @list_assigns_cache = @rv;
620         }
621 return @list_assigns_cache;
622 }
623
624 # create_assign(&assign)
625 sub create_assign
626 {
627 &list_assigns();        # force cache init
628 &lock_file($qmail_assigns_file);
629 local $lref = &read_file_lines($qmail_assigns_file);
630 local $dot;
631 for($i=0; $i<@$lref; $i++) {
632         if ($lref->[$i] eq '.') {
633                 $dot++;
634                 last;
635                 }
636         }
637 splice(@$lref, $i, 0, join(":", "$_[0]->{'mode'}$_[0]->{'address'}",
638                                 $_[0]->{'user'}, $_[0]->{'uid'}, $_[0]->{'gid'},
639                                 $_[0]->{'home'}, $_[0]->{'dash'},
640                                 $_[0]->{'preext'}, ''));
641 push(@$lref, ".") if (!$dot);
642 &flush_file_lines();
643 &unlock_file($qmail_assigns_file);
644
645 # Update in memory cache
646 $_[0]->{'line'} = @$lref-1;
647 $_[0]->{'idx'} = scalar(@list_assigns_cache);
648 push(@list_assigns_cache, $_[0]);
649 }
650
651 # modify_assign(&old, &assign)
652 sub modify_assign
653 {
654 &lock_file($qmail_assigns_file);
655 local $lref = &read_file_lines($qmail_assigns_file);
656 $lref->[$_[0]->{'line'}] = join(":", "$_[1]->{'mode'}$_[1]->{'address'}",
657                                 $_[1]->{'user'}, $_[1]->{'uid'}, $_[1]->{'gid'},
658                                 $_[1]->{'home'}, $_[1]->{'dash'},
659                                 $_[1]->{'preext'}, '');
660 &flush_file_lines();
661 &unlock_file($qmail_assigns_file);
662
663 # Update in memory cache
664 if ($_[0] ne $_[1]) {
665         $_[1]->{'line'} = $_[0]->{'line'};
666         $_[1]->{'idx'} = $_[0]->{'idx'};
667         $list_assigns_cache[$_[1]->{'idx'}] = $_[1];
668         }
669 }
670
671 # delete_assign(&assign)
672 sub delete_assign
673 {
674 &lock_file($qmail_assigns_file);
675 local $lref = &read_file_lines($qmail_assigns_file);
676 splice(@$lref, $_[0]->{'line'}, 1);
677 &flush_file_lines();
678 &unlock_file($qmail_assigns_file);
679
680 # delete from cache
681 splice(@list_assigns_cache, $_[0]->{'idx'}, 1);
682 foreach my $v (@list_assigns_cache) {
683         $v->{'line'}-- if ($v->{'line'} > $_[0]->{'line'});
684         $v->{'idx'}-- if ($v->{'idx'} > $_[0]->{'idx'});
685         }
686 }
687
688 # assign_form([&assign])
689 sub assign_form
690 {
691 print "<form method=post action=save_assign.cgi>\n";
692 if ($_[0]) { print "<input type=hidden name=idx value='$_[0]->{'idx'}'>\n"; }
693 else { print "<input type=hidden name=new value=1>\n"; }
694
695 print "<table border>\n";
696 print "<tr $tb> <td><b>",$a ? $text{'sform_edit'}
697                             : $text{'sform_create'},"</b></td> </tr>\n";
698 print "<tr $cb> <td><table>\n";
699
700 print "<tr> <td valign=top><b>$text{'sform_address'}</b></td> <td colspan=3>\n";
701 printf "<input type=radio name=mode value='=' %s> %s\n",
702         !$_[0] || $_[0]->{'mode'} eq '=' ? 'checked' : '', $text{'sform_mode0'};
703 printf "<input name=address0 size=20 value='%s'><br>\n",
704         $_[0] && $_[0]->{'mode'} eq '=' ? $_[0]->{'address'} : '';
705 printf "<input type=radio name=mode value='+' %s> %s\n",
706         $_[0] && $_[0]->{'mode'} eq '+' ? 'checked' : '', $text{'sform_mode1'};
707 printf "<input name=address1 size=20 value='%s'></td> </tr>\n",
708         $_[0] && $_[0]->{'mode'} eq '+' ? $_[0]->{'address'} : '';
709
710 print "<tr> <td><b>$text{'sform_user'}</b></td>\n";
711 print "<td>",&unix_user_input("user", $_[0] ? $_[0]->{'user'} : ''),"</td>\n";
712
713 print "<td><b>$text{'sform_home'}</b></td>\n";
714 printf "<td><input name=home size=25 value='%s'> %s</td> </tr>\n",
715         $_[0] ? $_[0]->{'home'} : '', &file_chooser_button("home", 1);
716
717 print "<tr> <td><b>$text{'sform_uid'}</b></td>\n";
718 printf "<td><input name=uid size=6 value='%s'></td>\n",
719         $_[0] ? $_[0]->{'uid'} : '';
720
721 print "<td><b>$text{'sform_gid'}</b></td>\n";
722 printf "<td><input name=gid size=6 value='%s'></td> </tr>\n",
723         $_[0] ? $_[0]->{'gid'} : '';
724
725 print "<tr> <td colspan=4 align=right>\n";
726 if ($_[0]) {
727         print "<input type=submit value='$text{'save'}'>\n";
728         print "<input type=submit name=delete value='$text{'delete'}'>\n";
729         }
730 else { print "<input type=submit value='$text{'create'}'>\n"; }
731 print "</td> </tr>\n";
732 print "</table></td></tr></table></form>\n";
733 }
734
735 # user_mail_dir(username, ...)
736 # Returns the full path to a user's mail file or directory
737 sub user_mail_dir
738 {
739 if ($config{'mail_system'} == 1) {
740         if (@_ > 1) {
741                 return "$_[7]/$config{'mail_dir_qmail'}/";
742                 }
743         else {
744                 local @u = getpwnam($_[0]);
745                 return "$u[7]/$config{'mail_dir_qmail'}/";
746                 }
747         }
748 else {
749         return &user_mail_file(@_);
750         }
751 }
752
753 # restart_qmail()
754 # Tells qmail to reload its configuration files by sending a HUP signal
755 sub restart_qmail
756 {
757 if ($config{'apply_cmd'}) {
758         &system_logged("($config{'apply_cmd'}) >/dev/null 2>&1 </dev/null");
759         }
760 else {
761         &kill_byname_logged("qmail-send", HUP);
762         }
763 }
764
765 # stop_qmail()
766 # Attempts to stop qmail, and returns an error message if it fails
767 sub stop_qmail
768 {
769 if ($config{'stop_cmd'}) {
770         &system_logged("( $config{'stop_cmd'} ) >/dev/null 2>&1 </dev/null &");
771         return undef;
772         }
773 else {
774         if (&kill_byname_logged("qmail-send", TERM)) {
775                 return undef;
776                 }
777         else {
778                 return $text{'stop_err'};
779                 }
780         }
781 }
782
783 # start_qmail()
784 # Attempts to start qmail, and returns an error message if it fails
785 sub start_qmail
786 {
787 if ($config{'start_cmd'}) {
788         &system_logged("( $config{'start_cmd'} ) >/dev/null 2>&1 </dev/null &");
789         }
790 else {
791         &system_logged("$qmail_start_cmd >/dev/null 2>&1 </dev/null &");
792         }
793 return undef;
794 }
795
796 # Returns the PID of qmail-send if running
797 sub is_qmail_running
798 {
799 local ($pid) = &find_byname("^\\S*qmail-send");
800 return kill(0, $pid) ? $pid : undef;
801 }
802
803 1;
804