# Functions for parsing the dovecot config file BEGIN { push(@INC, ".."); }; use WebminCore; &init_config(); @supported_auths = ( "anonymous", "plain", "digest-md5", "cram-md5", "apop" ); @mail_envs = ( undef, "maildir:~/Maildir", "mbox:~/mail/:INBOX=/var/mail/%u", "maildir:~/Maildir:mbox:~/mail/" ); # get_config_file() # Returns the full path to the first valid config file sub get_config_file { foreach my $f (split(/\s+/, $config{'dovecot_config'})) { return $f if (-r $f); } return undef; } # get_add_config_file() # Returns the full path to the first valid config file for new top-level # directives sub get_add_config_file { foreach my $f (split(/\s+/, $config{'add_config'})) { return $f if (-r $f); } return &get_config_file(); } # get_config() # Returns a list of dovecot config entries sub get_config { if (!@get_config_cache) { @get_config_cache = &read_config_file(&get_config_file()); } return \@get_config_cache; } # read_config_file(filename, [&include-parent-rv]) # Convert a file into a list od directives sub read_config_file { local ($file, $incrv) = @_; local $filedir = $file; $filedir =~ s/\/[^\/]+$//; local $lnum = 0; local ($section, @sections); open(CONF, "<".$file); local @lines = <CONF>; close(CONF); local $_; local @rv; local $section; foreach (@lines) { s/\r|\n//g; if (/^(\s*(#?)\s*)([a-z0-9\_]+)\s*(\S*)\s*\{\s*$/) { # Start of a section .. add this as a value too local $oldsection = $section; if ($section) { push(@sections, $section); # save old } $section = { 'name' => $3, 'value' => $4, 'enabled' => !$2, 'space' => $1, 'section' => 1, 'members' => [ ], 'indent' => scalar(@sections), 'line' => $lnum, 'eline' => $lnum, 'file' => $file, }; $section->{'space'} =~ s/#//; if ($oldsection) { $section->{'sectionname'} = $oldsection->{'name'}; $section->{'sectionvalue'} = $oldsection->{'value'}; } push(@rv, $section); } elsif (/^\s*(#?)\s*}\s*$/ && $section) { # End of a section $section->{'eline'} = $lnum; if (@sections) { $section = pop(@sections); } else { $section = undef; } } elsif (/^(\s*)(#?)([a-z0-9\_]+)\s+=\s*(.*)/) { # A directive inside a section local $dir = { 'name' => $3, 'value' => $4, 'enabled' => !$2, 'space' => $1, 'line' => $lnum, 'eline' => $lnum, 'file' => $file, }; if ($section) { $dir->{'sectionname'} = $section->{'name'}; $dir->{'sectionvalue'} = $section->{'value'}; push(@{$section->{'members'}}, $dir); $section->{'eline'} = $lnum; } $dir->{'value'} =~ s/\s+$//; # Fix up references to other variables my @w = split(/\s+/, $dir->{'value'}); my $changed; foreach my $w (@w) { if ($w =~ /^\$(\S+)/) { my $var = $1; my ($prev) = grep { $_->{'name'} eq $var } @rv; if (!$prev && $incrv) { ($prev) = grep { $_->{'name'} eq $var } @$incrv; } if ($prev) { $w = $prev->{'value'}; $changed = 1; } else { $w = undef; $changed = 1; } } } if ($changed) { @w = grep { defined($_) } @w; $dir->{'value'} = join(" ", @w); } push(@rv, $dir); } elsif (/^\s*!(include|include_try)\s+(\S+)/) { # Include file(s) local $glob = $2; if ($glob !~ /^\//) { $glob = $filedir."/".$glob; } foreach my $i (glob($glob)) { push(@rv, &read_config_file($i, \@rv)); } } $lnum++; } return @rv; } # find(name, &config, [disabled-mode], [sectionname], [sectionvalue], [first]) # Mode 0=enabled, 1=disabled, 2=both sub find { local ($name, $conf, $mode, $sname, $svalue, $first) = @_; local @rv = grep { !$_->{'section'} && $_->{'name'} eq $name && ($mode == 0 && $_->{'enabled'} || $mode == 1 && !$_->{'enabled'} || $mode == 2) } @$conf; if (defined($sname)) { # If a section was requested, limit to it @rv = grep { $_->{'sectionname'} eq $sname && $_->{'sectionvalue'} eq $svalue } @rv; } if (wantarray) { return @rv; } elsif ($first) { return $rv[0]; } else { return $rv[$#rv]; } } # find_value(name, &config, [disabled-mode], [sectionname], [sectionvalue]) # Mode 0=enabled, 1=disabled, 2=both sub find_value { local @rv = &find(@_); if (wantarray) { return map { $_->{'value'} } @rv; } elsif (!@rv) { return undef; } else { # Prefer the last one that isn't self-referential my @unself = grep { $_->{'value'} !~ /\$\Q$name\E/ } @rv; @rv = @unself if (@unself); return $rv[$#rv]->{'value'}; } } # find_section(name, &config, [disabled-mode], [sectionname], [sectionvalue]) # Returns a Dovecot config section object sub find_section { local ($name, $conf, $mode, $sname, $svalue) = @_; local @rv = grep { $_->{'section'} && $_->{'name'} eq $name && ($mode == 0 && $_->{'enabled'} || $mode == 1 && !$_->{'enabled'} || $mode == 2) } @$conf; if (defined($sname)) { # If a section was requested, limit to it @rv = grep { $_->{'sectionname'} eq $sname && $_->{'sectionvalue'} eq $svalue } @rv; } return wantarray ? @rv : $rv[0]; } # save_directive(&conf, name|&dir, value, [sectionname], [sectionvalue]) # Updates one directive in the config file sub save_directive { local ($conf, $name, $value, $sname, $svalue) = @_; local $dir; if (ref($name)) { # Old directive given $dir = $name; } else { # Find by name, by prefer those that aren't self-referential my @dirs = &find($name, $conf, 0, $sname, $svalue, 1); ($dir) = grep { $_->{'value'} !~ /\$\Q$name\E/ } @dirs; if (!$dir) { $dir = $dirs[0]; } } local $newline = ref($name) ? "$name->{'name'} = $value" : "$name = $value"; if ($dir) { $newline = $dir->{'space'}.$newline; } elsif ($sname) { $newline = " ".$newline; } if ($dir && defined($value)) { # Updating some directive local $lref = &read_file_lines($dir->{'file'}); $lref->[$dir->{'line'}] = $newline; $dir->{'value'} = $value; } elsif ($dir && !defined($value)) { # Deleting some directive local $lref = &read_file_lines($dir->{'file'}); splice(@$lref, $dir->{'line'}, 1); &renumber(\@get_config_cache, $dir->{'line'}, $dir->{'file'}, -1); my $idx = &indexof($dir, @$conf); if ($idx >= 0) { splice(@$conf, $idx, 1); } } elsif (!$dir && defined($value)) { # Adding some directive .. put it after the commented version, if any local $cmt = &find($name, $conf, 1, $sname, $svalue); if ($cmt) { # After commented version of same directive local $lref = &read_file_lines($cmt->{'file'}); $newline = $cmt->{'space'}.$newline; splice(@$lref, $cmt->{'line'}+1, 0, $newline); &renumber($conf, $cmt->{'line'}+1, $cmt->{'file'}, 1); push(@$conf, { 'name' => $name, 'value' => $value, 'enabled' => 1, 'file' => $cmt->{'file'}, 'line' => $cmt->{'line'}+1, 'eline' => $cmt->{'line'}+1, 'sectionname' => $sname, 'sectionvalue' => $svalue }); } elsif ($sname) { # Put at end of section local @insect = grep { $_->{'sectionname'} eq $sname && $_->{'sectionvalue'} eq $svalue } @$conf; @insect || &error("Failed to find section $sname $svalue !"); local $lref = &read_file_lines($insect[$#insect]->{'file'}); local $line = $insect[$#insect]->{'line'}+1; $newline = $insect[$#insect]->{'space'}.$newline; splice(@$lref, $line, 0, $newline); &renumber($conf, $line, $insect[$#insect]->{'file'}, 1); push(@$conf, { 'name' => $name, 'value' => $value, 'enabled' => 1, 'file' => $insect[$#insect]->{'file'}, 'line' => $line, 'eline' => $line, 'sectionname' => $sname, 'sectionvalue' => $svalue }); } else { # Need to put at end of main config local $file = &get_add_config_file(); local $lref = &read_file_lines($file); push(@$lref, $newline); push(@$conf, { 'name' => $name, 'value' => $value, 'enabled' => 1, 'file' => $file, 'line' => scalar(@$lref)-1, 'eline' => scalar(@$lref)-1, 'sectionname' => $sname, 'sectionvalue' => $svalue }); } } } # save_section(&conf, §ion) # Updates one section in the config file sub save_section { local ($conf, $section) = @_; local $lref = &read_file_lines($section->{'file'}); local $indent = " " x $section->{'indent'}; local @newlines; push(@newlines, $indent.$section->{'name'}." ".$section->{'value'}." {"); foreach my $m (@{$section->{'members'}}) { push(@newlines, $indent." ".$m->{'name'}." = ".$m->{'value'}); } push(@newlines, $indent."}"); local $oldlen = $section->{'eline'} - $section->{'line'} + 1; splice(@$lref, $section->{'line'}, $oldlen, @newlines); &renumber($conf, $section->{'eline'}, $section->{'file'}, scalar(@newlines)-$oldlen); $section->{'eline'} = $section->{'line'} + scalar(@newlines) - 1; my $i = 1; foreach my $m (@{$section->{'members'}}) { $m->{'line'} = $m->{'eline'} = $section->{'line'} + $i++; $m->{'space'} = " "; $m->{'file'} = $section->{'file'}; $m->{'sectionname'} = $section->{'name'}; $m->{'sectionvalue'} = $section->{'value'}; } } # create_section(&conf, §ion, [&parent], [&before]) # Adds a section to the config file sub create_section { local ($conf, $section, $parent, $before) = @_; local $indent = " " x $section->{'indent'}; local @newlines; push(@newlines, $indent.$section->{'name'}." ".$section->{'value'}." {"); foreach my $m (@{$section->{'members'}}) { push(@newlines, $indent." ".$m->{'name'}." = ".$m->{'value'}); } push(@newlines, $indent."}"); my $file; my $lref; if ($parent) { # Add to another config block $file = $parent->{'file'}; $lref = &read_file_lines($file); $section->{'line'} = $parent->{'eline'}; } else { # Add to the global config file $file = &get_config_file(); $lref = &read_file_lines($file); if ($before) { # Add before another block $section->{'line'} = $before->{'line'}; } else { # Add at the end $section->{'line'} = scalar(@$lref); } } splice(@$lref, $section->{'line'}, 0, @newlines); &renumber($conf, $section->{'eline'}, $section->{'file'}, scalar(@newlines)-$oldlen); $section->{'eline'} = $section->{'line'} + scalar(@newlines) - 1; $section->{'file'} = $file; my $i = 1; foreach my $m (@{$section->{'members'}}) { $m->{'line'} = $m->{'eline'} = $section->{'line'} + $i++; $m->{'space'} = " "; $m->{'indent'} = $section->{'indent'} + 1; $m->{'file'} = $section->{'file'}; $m->{'sectionname'} = $section->{'name'}; $m->{'sectionvalue'} = $section->{'value'}; } } # renumber(&conf, line, file, offset) sub renumber { my ($conf, $line, $file, $offset) = @_; foreach my $c (@$conf) { if ($c->{'file'} eq $file) { $c->{'line'} += $offset if ($c->{'line'} >= $line); $c->{'eline'} += $offset if ($c->{'eline'} >= $line); } } } # renumber_section_and_members(&conffull, file, line, offset, member_sname, member_svalue) sub renumber_section_and_members { my ($conffull, $file, $line, $offset, $member_sname, $member_svalue) = @_; my @section = &find_section($sname, $conffull); if ($member_sname || $member_svalue) { @section = grep { ($_->{'members'}->[0]->{'sectionname'} eq $member_sname || !defined($member_sname)) && ($_->{'members'}->[0]->{'sectionvalue'} eq $member_svalue || !defined($member_svalue)) && $_->{'members'}->[0]->{'file'} eq $file } @section; } &renumber(\@section, $line, $file, $offset); } # is_dovecot_running() # Returns the PID if the server process is active, undef if not sub is_dovecot_running { # Try the configured PID file first local $pid =&check_pid_file($config{'pid_file'}); return $pid if ($pid); # Look in the base dir local $base = &find_value("base_dir", &get_config(), 2); return &check_pid_file("$base/master.pid"); } # stop_dovecot() # Attempts to stop the dovecot server process, returning an error message or # undef if successful sub stop_dovecot { if ($config{'init_script'}) { &foreign_require("init"); my ($ok, $out) = &init::stop_action($config{'init_script'}); return $ok ? undef : "<pre>".&html_escape($out)."</pre>"; } else { local $pid = &is_dovecot_running(); if ($pid && kill('TERM', $pid)) { return undef; } else { return $text{'stop_erunning'}; } } } # start_dovecot() # Attempts to start the dovecot server process, returning an error message or # undef if successful sub start_dovecot { if ($config{'init_script'}) { &foreign_require("init"); my ($ok, $out) = &init::start_action($config{'init_script'}); return $ok ? undef : "<pre>".&html_escape($out)."</pre>"; } else { local $cmd = $config{'dovecot'}; local $temp = &transname(); &system_logged("$cmd >$temp 2>&1 </dev/null &"); sleep(1); local $out = &read_file_contents($temp); &unlink_file($temp); return &is_dovecot_running() ? undef : "<pre>$out</pre>"; } } # apply_configration([full-restart]) # Reload the Dovecot configuration, optionally with a full restart sub apply_configuration { local ($restart) = @_; local $pid = &is_dovecot_running(); if (!$pid) { return $text{'stop_erunning'}; } elsif ($restart) { # Fully shut down and re-start if ($config{'init_script'}) { &foreign_require("init"); my ($ok, $out) = &init::restart_action($config{'init_script'}); return $ok ? undef : "<pre>".&html_escape($out)."</pre>"; } else { &stop_dovecot(); local $err; for(my $i=0; $i<5; $i++) { $err = &start_dovecot(); last if (!$err); sleep(1); } return $err; } } else { # Send the HUP signal return &kill_logged('HUP', $pid) ? undef : $!; } } # getdef(name, [&mapping]) # Returns 'Default (value)' for some config sub getdef { local $def = &find_value($_[0], &get_config(), 1); if (defined($def)) { local $map; if ($_[1]) { ($map) = grep { $_->[0] eq $def } @{$_[1]}; } if (defined($map)) { return "$text{'default'} ($map->[1])"; } elsif ($def) { return "$text{'default'} ($def)"; } } return $text{'default'}; } # get_dovecot_version() # Returns the dovecot version number, or undef if not available sub get_dovecot_version { local $out = &backquote_command("$config{'dovecot'} --version 2>&1"); return $out =~ /([0-9\.]+)/ ? $1 : undef; } # version_atleast(ver) # Returns 1 if running at least some version or above sub version_atleast { local ($wantver) = @_; local $ver = &get_dovecot_version(); return 0 if (!$ver); return &compare_version_numbers($ver, $wantver) >= 0; } sub list_lock_methods { local ($forindex) = @_; return ( "dotlock", "fcntl", "flock", $forindex ? ( ) : ( "lockf" ) ); } # lock_dovecot_files([&conf]) # Lock all files in the Dovecot config sub lock_dovecot_files { local ($conf) = @_; $conf ||= &get_config(); foreach my $f (&unique(map { $_->{'file'} } @$conf)) { &lock_file($f); } } # unlock_dovecot_files([&conf]) # Release lock on all files sub unlock_dovecot_files { local ($conf) = @_; $conf ||= &get_config(); foreach my $f (reverse(&unique(map { $_->{'file'} } @$conf))) { &unlock_file($f); } } # get_supported_protocols() # Returns the list of usable protocols for the current Dovecot version sub get_supported_protocols { if (&get_dovecot_version() >= 2) { return ( "imap", "pop3", "lmtp" ); } else { return ( "imap", "pop3", "imaps", "pop3s" ); } } 1; r
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
images | Folder | 0755 |
|
|
lang | Folder | 0755 |
|
|
CHANGELOG | File | 1.49 KB | 0644 |
|
apply.cgi | File | 199 B | 0755 |
|
backup_config.pl | File | 735 B | 0755 |
|
bootup.cgi | File | 416 B | 0755 |
|
cgi_args.pl | File | 150 B | 0755 |
|
config | File | 135 B | 0644 |
|
config-AlmaLinux-7.0-ALL | File | 128 B | 0644 |
|
config-Amazon-Linux | File | 124 B | 0644 |
|
config-CentOS-Stream-Linux-8.0-ALL | File | 128 B | 0644 |
|
config-CloudLinux-8.0-ALL | File | 128 B | 0644 |
|
config-Oracle-Linux-8.0-ALL | File | 128 B | 0644 |
|
config-Rocky-Linux-7.0-ALL | File | 128 B | 0644 |
|
config-debian-linux | File | 124 B | 0644 |
|
config-freebsd-8.0-8.9 | File | 150 B | 0644 |
|
config-freebsd-9-ALL | File | 142 B | 0644 |
|
config-gentoo-linux | File | 104 B | 0644 |
|
config-mandrake-linux | File | 116 B | 0644 |
|
config-openSUSE-Linux-15.0-ALL | File | 124 B | 0644 |
|
config-openmamba-linux | File | 116 B | 0644 |
|
config-pardus-linux | File | 104 B | 0644 |
|
config-redhat-linux-11.0-23.0 | File | 142 B | 0644 |
|
config-redhat-linux-24.0-ALL | File | 146 B | 0644 |
|
config-suse-linux | File | 124 B | 0644 |
|
config-syno-linux | File | 162 B | 0644 |
|
config.info | File | 240 B | 0644 |
|
config.info.bg | File | 306 B | 0644 |
|
config.info.ca | File | 225 B | 0644 |
|
config.info.cs | File | 208 B | 0644 |
|
config.info.de | File | 187 B | 0644 |
|
config.info.es | File | 218 B | 0644 |
|
config.info.fr | File | 0 B | 0644 |
|
config.info.hr | File | 0 B | 0644 |
|
config.info.hu | File | 0 B | 0644 |
|
config.info.it | File | 185 B | 0644 |
|
config.info.nl | File | 186 B | 0644 |
|
config.info.no | File | 179 B | 0644 |
|
config.info.pl | File | 189 B | 0644 |
|
config.info.zh_TW | File | 192 B | 0644 |
|
dovecot-lib.pl | File | 14.9 KB | 0755 |
|
edit_login.cgi | File | 7.75 KB | 0755 |
|
edit_mail.cgi | File | 5.13 KB | 0755 |
|
edit_manual.cgi | File | 928 B | 0755 |
|
edit_net.cgi | File | 1.67 KB | 0755 |
|
edit_ssl.cgi | File | 2.1 KB | 0755 |
|
index.cgi | File | 1.96 KB | 0755 |
|
install_check.pl | File | 370 B | 0755 |
|
log_parser.pl | File | 424 B | 0755 |
|
module.info | File | 168 B | 0644 |
|
module.info.af | File | 0 B | 0644 |
|
module.info.af.auto | File | 103 B | 0644 |
|
module.info.ar | File | 0 B | 0644 |
|
module.info.ar.auto | File | 120 B | 0644 |
|
module.info.be | File | 0 B | 0644 |
|
module.info.be.auto | File | 125 B | 0644 |
|
module.info.bg | File | 0 B | 0644 |
|
module.info.bg.auto | File | 178 B | 0644 |
|
module.info.ca | File | 116 B | 0644 |
|
module.info.cs | File | 33 B | 0644 |
|
module.info.cs.auto | File | 80 B | 0644 |
|
module.info.da | File | 0 B | 0644 |
|
module.info.da.auto | File | 94 B | 0644 |
|
module.info.de | File | 102 B | 0644 |
|
module.info.el | File | 0 B | 0644 |
|
module.info.el.auto | File | 200 B | 0644 |
|
module.info.es | File | 39 B | 0644 |
|
module.info.es.auto | File | 82 B | 0644 |
|
module.info.eu | File | 0 B | 0644 |
|
module.info.eu.auto | File | 121 B | 0644 |
|
module.info.fa | File | 0 B | 0644 |
|
module.info.fa.auto | File | 135 B | 0644 |
|
module.info.fi | File | 0 B | 0644 |
|
module.info.fi.auto | File | 111 B | 0644 |
|
module.info.fr | File | 0 B | 0644 |
|
module.info.fr.auto | File | 122 B | 0644 |
|
module.info.he | File | 0 B | 0644 |
|
module.info.he.auto | File | 139 B | 0644 |
|
module.info.hr | File | 0 B | 0644 |
|
module.info.hr.auto | File | 127 B | 0644 |
|
module.info.hu | File | 36 B | 0644 |
|
module.info.hu.auto | File | 74 B | 0644 |
|
module.info.it | File | 33 B | 0644 |
|
module.info.it.auto | File | 73 B | 0644 |
|
module.info.ja | File | 0 B | 0644 |
|
module.info.ja.auto | File | 126 B | 0644 |
|
module.info.ko | File | 0 B | 0644 |
|
module.info.ko.auto | File | 119 B | 0644 |
|
module.info.lt | File | 0 B | 0644 |
|
module.info.lt.auto | File | 122 B | 0644 |
|
module.info.lv | File | 0 B | 0644 |
|
module.info.lv.auto | File | 108 B | 0644 |
|
module.info.ms | File | 113 B | 0644 |
|
module.info.mt | File | 0 B | 0644 |
|
module.info.mt.auto | File | 118 B | 0644 |
|
module.info.nl | File | 33 B | 0644 |
|
module.info.nl.auto | File | 69 B | 0644 |
|
module.info.no | File | 33 B | 0644 |
|
module.info.no.auto | File | 54 B | 0644 |
|
module.info.pl | File | 116 B | 0644 |
|
module.info.pt | File | 0 B | 0644 |
|
module.info.pt.auto | File | 119 B | 0644 |
|
module.info.pt_BR | File | 0 B | 0644 |
|
module.info.pt_BR.auto | File | 125 B | 0644 |
|
module.info.ro | File | 0 B | 0644 |
|
module.info.ro.auto | File | 117 B | 0644 |
|
module.info.ru | File | 41 B | 0644 |
|
module.info.ru.auto | File | 98 B | 0644 |
|
module.info.sk | File | 0 B | 0644 |
|
module.info.sk.auto | File | 108 B | 0644 |
|
module.info.sl | File | 0 B | 0644 |
|
module.info.sl.auto | File | 117 B | 0644 |
|
module.info.sv | File | 0 B | 0644 |
|
module.info.sv.auto | File | 105 B | 0644 |
|
module.info.th | File | 0 B | 0644 |
|
module.info.th.auto | File | 196 B | 0644 |
|
module.info.tr | File | 0 B | 0644 |
|
module.info.tr.auto | File | 109 B | 0644 |
|
module.info.uk | File | 0 B | 0644 |
|
module.info.uk.auto | File | 135 B | 0644 |
|
module.info.ur | File | 0 B | 0644 |
|
module.info.ur.auto | File | 131 B | 0644 |
|
module.info.vi | File | 0 B | 0644 |
|
module.info.vi.auto | File | 122 B | 0644 |
|
module.info.zh | File | 0 B | 0644 |
|
module.info.zh.auto | File | 99 B | 0644 |
|
module.info.zh_TW | File | 0 B | 0644 |
|
module.info.zh_TW.auto | File | 105 B | 0644 |
|
save_login.cgi | File | 5.91 KB | 0755 |
|
save_mail.cgi | File | 2.77 KB | 0755 |
|
save_manual.cgi | File | 520 B | 0755 |
|
save_net.cgi | File | 960 B | 0755 |
|
save_ssl.cgi | File | 1.89 KB | 0755 |
|
start.cgi | File | 194 B | 0755 |
|
stop.cgi | File | 190 B | 0755 |
|