2 # Common functions for parsing qmail config files
4 BEGIN { push(@INC, ".."); };
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";
22 $config{'perpage'} ||= 20; # a value of 0 can cause problems
25 # Returns a list of qmail alias file names
29 opendir(DIR, $qmail_alias_dir);
30 foreach $f (readdir(DIR)) {
31 next if ($f !~ /^\.qmail-(\S+)$/);
41 local $alias = { 'name' => $_[0],
42 'file' => "$qmail_alias_dir/.qmail-$_[0]",
44 open(ALIAS, $alias->{'file'});
49 push(@{$alias->{'values'}}, $_);
57 # Display a form for editing or creating an alias. Each alias can map to
58 # 1 or more programs, files, lists or users
61 local($a, @values, $v, $type, $val, @typenames);
63 if ($a) { @values = @{$a->{'values'}}; }
64 @typenames = map { $text{"aform_type$_"} } (0 .. 6);
65 $typenames[0] = "<$typenames[0]>";
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";
75 print "<tr> <td><b>$text{'aform_name'}</b></td>\n";
76 local $n = $a ? $a->{'name'} : '';
78 local @virts = grep { $_->{'domain'} && !$_->{'user'} && $_->{'prepend'} }
80 if ($n =~ /^(\S+)-(\S+)$/) {
81 local ($pn, $bn) = ($1, $2);
83 if ($v->{'prepend'} eq $pn) {
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'};
96 printf "<option value='%s' %s>%s\n",
97 $v->{'prepend'}, $virt eq $v ? "selected" : "",
100 print "</select></td> </tr>\n";
103 printf "<td><input name=name size=20 value=\"%s\"></td> </tr>\n", $n;
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" : "";
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";
120 elsif ($type == 6 && $a) {
121 print "<a href='edit_ffile.cgi?file=$val&name=$a->{'name'}'>",
122 "$text{'aform_afile'}</a>\n";
124 print "</td> </tr>\n";
126 print "<tr> <td colspan=2 align=right>\n";
128 print "<input type=submit value=$text{'save'}>\n";
129 print "<input type=submit name=delete value=$text{'delete'}>\n";
131 else { print "<input type=submit value=$text{'create'}>\n"; }
132 print "</td> </tr>\n";
133 print "</table></td></tr></table></form>\n";
137 # Return the type and destination of some alias string
141 if ($_[0] =~ /^\&(.*)/) {
144 elsif ($_[0] =~ /^\|$module_config_directory\/autoreply.pl\s+(\S+)/) {
147 elsif ($_[0] =~ /^\|$module_config_directory\/filter.pl\s+(\S+)/) {
150 elsif ($_[0] =~ /^\|(.*)$/) {
153 elsif ($_[0] =~ /^(\/.*)\/$/) {
156 elsif ($_[0] =~ /^(\/.*)$/) {
162 return wantarray ? @rv : $rv[0];
165 # create_alias(&alias)
166 # Creates a new qmail alias file
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");
174 &close_tempfile(FILE);
175 &set_ownership_permissions(undef, undef, 0644, $f);
178 # modify_alias(&old, &alias)
179 # Modifies an existing qmail alias
180 sub modify_alias(&old, &alias)
182 if ($_[0]->{'name'} ne $_[1]->{'name'}) {
183 &lock_file($_[0]->{'file'});
184 unlink($_[0]->{'file'});
185 &unlock_file($_[0]->{'file'});
187 &create_alias($_[1]);
190 # delete_alias(&alias)
191 # Deletes an existing qmail alias file
194 &lock_file($_[0]->{'file'});
195 unlink("$_[0]->{'file'}");
196 &unlock_file($_[0]->{'file'});
199 # get_control_file(file)
200 # Returns the value from a qmail single-line control file
203 open(FILE, "$qmail_control_dir/$_[0]") || return undef;
204 local $line = <FILE>;
210 # set_control_file(file, value)
211 # Sets the value in a qmail single-line control file
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);
221 unlink("$qmail_control_dir/$_[0]");
223 &unlock_file("$qmail_control_dir/$_[0]");
226 # list_control_file()
227 # Returns the contents of a multi-line control file
228 sub list_control_file
231 open(FILE, "$qmail_control_dir/$_[0]") || return undef;
235 push(@lines, $_) if (/\S/);
242 # save_control_file(file, &lines)
243 # Saves the contents of a multi-line control file
244 sub save_control_file
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");
252 &close_tempfile(FILE);
255 unlink("$qmail_control_dir/$_[0]");
257 &unlock_file("$qmail_control_dir/$_[0]");
261 # Returns a list of all SMTP routes
264 if (!scalar(@list_routes_cache)) {
267 open(ROUTES, $qmail_routes_file);
271 if (/^(\S*):(\S*):(\d+)/) {
272 push(@rv, { 'from' => $1,
275 'idx' => scalar(@rv),
278 elsif (/^(\S*):(\S*)/) {
279 push(@rv, { 'from' => $1,
281 'idx' => scalar(@rv),
287 @list_routes_cache = @rv;
289 return @list_routes_cache;
292 # create_route(&route)
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'} : ''));
301 &unlock_file($qmail_routes_file);
303 # Update in memory cache
304 $_[0]->{'line'} = @$lref-1;
305 $_[0]->{'idx'} = scalar(@list_routes_cache);
306 push(@list_routes_cache, $_[0]);
309 # modify_route(&old, &route)
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'} : ''));
318 &unlock_file($qmail_routes_file);
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];
328 # delete_route(&route)
331 &lock_file($qmail_routes_file);
332 local $lref = &read_file_lines($qmail_routes_file);
333 splice(@$lref, $_[0]->{'line'}, 1);
335 &unlock_file($qmail_routes_file);
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'});
345 # route_form([&route])
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"; }
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'} : "";
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'} : "";
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'} : "";
376 print "<tr> <td colspan=4 align=right>\n";
378 print "<input type=submit value=$text{'save'}>\n";
379 print "<input type=submit name=delete value=$text{'delete'}>\n";
381 else { print "<input type=submit value=$text{'create'}>\n"; }
382 print "</td> </tr>\n";
383 print "</table></td></tr></table></form>\n";
387 # Returns a list of all virtualdomains file entries
390 if (!scalar(@list_virts_cache)) {
393 open(VIRTS, $qmail_virts_file);
397 if (/^(\S+)\@(\S+):(\S*)/) {
398 push(@rv, { 'user' => $1,
403 'idx' => scalar(@rv) } );
405 elsif (/^(\S*):(\S*)/) {
406 push(@rv, { 'domain' => $1,
410 'idx' => scalar(@rv) } );
415 @list_virts_cache = @rv;
417 return @list_virts_cache;
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'}"
428 $_[0]->{'prepend'}));
430 &unlock_file($qmail_virts_file);
432 # Update in memory cache
433 $_[0]->{'line'} = @$lref-1;
434 $_[0]->{'idx'} = scalar(@list_virts_cache);
435 push(@list_virts_cache, $_[0]);
441 &lock_file($qmail_virts_file);
442 local $lref = &read_file_lines($qmail_virts_file);
443 splice(@$lref, $_[0]->{'line'}, 1);
445 &unlock_file($qmail_virts_file);
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'});
455 # modify_virt(&old, &virt)
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'}"
463 $_[1]->{'prepend'}));
465 &unlock_file($qmail_virts_file);
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];
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"; }
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";
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" : "",
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'} : "";
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'};
507 printf "<input type=radio name=prepend_mode value=2 %s> %s<br>\n",
508 "checked", $text{'vform_auto'};
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'} : "";
515 print "<tr> <td colspan=2 align=right>\n";
517 print "<input type=submit value=$text{'save'}>\n";
518 print "<input type=submit name=delete value=$text{'delete'}>\n";
520 else { print "<input type=submit value=$text{'create'}>\n"; }
521 print "</td> </tr>\n";
522 print "</table></td></tr></table></form>\n";
526 # Returns an array of structures for entries in the mail queue
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"
542 open(QUEUE, "$qmail_bin_dir/qmail-qread |");
544 if (/^(\d+)\s+(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\S+)\s+#(\d+)\s+(\d+)\s+(.*)/) {
545 local $q = { 'from' => $10,
548 'date' => "$1 $2 $3 $4:$5:$6" };
550 if (/^\s*(\S+)\s+(.*)/) {
561 # wrap_lines(text, width)
562 # Given a multi-line string, return an array of lines wrapped to
568 foreach $rest (split(/\n/, $_[0])) {
570 while($rest =~ /^(.{1,$w}\S*)\s*([\0-\377]*)$/) {
576 # Empty line .. keep as it is
587 $r =~ s/((http|ftp|https|mailto):[^><"'\s]+[^><"'\s\.])/<a href="$1">$1<\/a>/g;
592 # Returns a list of qmail user assignments
595 if (!scalar(@list_assigns_cache)) {
598 open(ASSIGNS, $qmail_assigns_file);
602 local @line = split(/:/, $_, 8);
603 if ($line[0] =~ /^([\+=])(\S*)/) {
604 local $asn = { 'address' => $2,
611 'preext' => $line[6],
612 'idx' => scalar(@rv),
619 @list_assigns_cache = @rv;
621 return @list_assigns_cache;
624 # create_assign(&assign)
627 &list_assigns(); # force cache init
628 &lock_file($qmail_assigns_file);
629 local $lref = &read_file_lines($qmail_assigns_file);
631 for($i=0; $i<@$lref; $i++) {
632 if ($lref->[$i] eq '.') {
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);
643 &unlock_file($qmail_assigns_file);
645 # Update in memory cache
646 $_[0]->{'line'} = @$lref-1;
647 $_[0]->{'idx'} = scalar(@list_assigns_cache);
648 push(@list_assigns_cache, $_[0]);
651 # modify_assign(&old, &assign)
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'}, '');
661 &unlock_file($qmail_assigns_file);
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];
671 # delete_assign(&assign)
674 &lock_file($qmail_assigns_file);
675 local $lref = &read_file_lines($qmail_assigns_file);
676 splice(@$lref, $_[0]->{'line'}, 1);
678 &unlock_file($qmail_assigns_file);
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'});
688 # assign_form([&assign])
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"; }
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";
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'} : '';
710 print "<tr> <td><b>$text{'sform_user'}</b></td>\n";
711 print "<td>",&unix_user_input("user", $_[0] ? $_[0]->{'user'} : ''),"</td>\n";
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);
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'} : '';
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'} : '';
725 print "<tr> <td colspan=4 align=right>\n";
727 print "<input type=submit value='$text{'save'}'>\n";
728 print "<input type=submit name=delete value='$text{'delete'}'>\n";
730 else { print "<input type=submit value='$text{'create'}'>\n"; }
731 print "</td> </tr>\n";
732 print "</table></td></tr></table></form>\n";
735 # user_mail_dir(username, ...)
736 # Returns the full path to a user's mail file or directory
739 if ($config{'mail_system'} == 1) {
741 return "$_[7]/$config{'mail_dir_qmail'}/";
744 local @u = getpwnam($_[0]);
745 return "$u[7]/$config{'mail_dir_qmail'}/";
749 return &user_mail_file(@_);
754 # Tells qmail to reload its configuration files by sending a HUP signal
757 if ($config{'apply_cmd'}) {
758 &system_logged("($config{'apply_cmd'}) >/dev/null 2>&1 </dev/null");
761 &kill_byname_logged("qmail-send", HUP);
766 # Attempts to stop qmail, and returns an error message if it fails
769 if ($config{'stop_cmd'}) {
770 &system_logged("( $config{'stop_cmd'} ) >/dev/null 2>&1 </dev/null &");
774 if (&kill_byname_logged("qmail-send", TERM)) {
778 return $text{'stop_err'};
784 # Attempts to start qmail, and returns an error message if it fails
787 if ($config{'start_cmd'}) {
788 &system_logged("( $config{'start_cmd'} ) >/dev/null 2>&1 </dev/null &");
791 &system_logged("$qmail_start_cmd >/dev/null 2>&1 </dev/null &");
796 # Returns the PID of qmail-send if running
799 local ($pid) = &find_byname("^\\S*qmail-send");
800 return kill(0, $pid) ? $pid : undef;