Dovecot 2.0 support
authorJamie Cameron <jcameron@webmin.com>
Sat, 26 Mar 2011 18:40:51 +0000 (11:40 -0700)
committerJamie Cameron <jcameron@webmin.com>
Sat, 26 Mar 2011 18:40:51 +0000 (11:40 -0700)
dovecot/CHANGELOG
dovecot/backup_config.pl [new file with mode: 0755]
dovecot/config-Amazon-Linux [new file with mode: 0644]
dovecot/dovecot-lib.pl
dovecot/edit_login.cgi
dovecot/edit_ssl.cgi
dovecot/save_login.cgi
dovecot/save_mail.cgi
dovecot/save_net.cgi
dovecot/save_ssl.cgi

index 1598a3b..1edb843 100644 (file)
@@ -17,3 +17,5 @@ The locking methods for mailboxes and index files can be configured on the Mail
 Added fields for setting the index and control files locations.
 ---- Changes since 1.430 ----
 Added fields to the SSL page for an optional CA certificate file and private key password.
+---- Changes since 1.530 ----
+Added support for Dovecot 2.0, which renames many configuration directives and splits up the config using include files.
diff --git a/dovecot/backup_config.pl b/dovecot/backup_config.pl
new file mode 100755 (executable)
index 0000000..520e334
--- /dev/null
@@ -0,0 +1,43 @@
+
+do 'dovecot-lib.pl';
+
+# backup_config_files()
+# Returns files and directories that can be backed up
+sub backup_config_files
+{
+return ( $config{'dovecot_config'} );
+}
+
+# pre_backup(&files)
+# Called before the files are actually read
+sub pre_backup
+{
+return undef;
+}
+
+# post_backup(&files)
+# Called after the files are actually read
+sub post_backup
+{
+return undef;
+}
+
+# pre_restore(&files)
+# Called before the files are restored from a backup
+sub pre_restore
+{
+return undef;
+}
+
+# post_restore(&files)
+# Called after the files are restored from a backup
+sub post_restore
+{
+if (&is_dovecot_running()) {
+       return &apply_configuration();
+       }
+return undef;
+}
+
+1;
+
diff --git a/dovecot/config-Amazon-Linux b/dovecot/config-Amazon-Linux
new file mode 100644 (file)
index 0000000..658fb10
--- /dev/null
@@ -0,0 +1,4 @@
+dovecot=/usr/sbin/dovecot
+dovecot_config=/etc/dovecot/dovecot.conf
+init_script=dovecot
+pid_file=/var/run/dovecot/master.pid
index 2850802..81fc860 100755 (executable)
@@ -14,64 +14,90 @@ use WebminCore;
 sub get_config
 {
 if (!scalar(@get_config_cache)) {
-       @get_config_cache = );
-       local $lnum = 0;
-       local ($section, @sections);
-       open(CONF, $config{'dovecot_config'});
-       while(<CONF>) {
-               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' => $2,
-                                    'value' => $3,
-                                    'enabled' => !$1,
-                                    'section' => 1,
-                                    'members' => [ ],
-                                    'indent' => scalar(@sections),
-                                    'line' => $lnum,
-                                    'eline' => $lnum };
-                       if ($oldsection) {
-                               $section->{'sectionname'} =
-                                       $oldsection->{'name'};
-                               $section->{'sectionvalue'} =
-                                       $oldsection->{'value'};
-                               }
-                       push(@get_config_cache, $section);
+       @get_config_cache = &read_config_file($config{'dovecot_config'});
+       }
+return \@get_config_cache;
+}
+
+# read_config_file(filename)
+# Convert a file into a list od directives
+sub read_config_file
+{
+local ($file) = @_;
+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
                        }
-               elsif (/^\s*(#?)\s*}\s*$/ && $section) {
-                       # End of a section
-                       $section->{'eline'} = $lnum;
+               $section = { 'name' => $2,
+                            'value' => $3,
+                            'enabled' => !$1,
+                            'section' => 1,
+                            'members' => [ ],
+                            'indent' => scalar(@sections),
+                            'line' => $lnum,
+                            'eline' => $lnum,
+                            'file' => $file, };
+               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;
+               $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' => $2,
+                              'value' => $3,
+                              'enabled' => !$1,
+                              'line' => $lnum,
+                              'file' => $file, };
+               if ($section) {
+                       $dir->{'sectionname'} = $section->{'name'};
+                       $dir->{'sectionvalue'} = $section->{'value'};
+                       push(@{$section->{'members'}}, $dir);
                        $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' => $2,
-                                      'value' => $3,
-                                      'enabled' => !$1,
-                                      'line' => $lnum };
-                       if ($section) {
-                               $dir->{'sectionname'} = $section->{'name'};
-                               $dir->{'sectionvalue'} = $section->{'value'};
-                               push(@{$section->{'members'}}, $dir);
-                               $section->{'eline'} = $lnum;
-                               }
-                       push(@get_config_cache, $dir);
+               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));
                        }
-               $lnum++;
                }
-       close(CONF);
+       $lnum++;
        }
-return \@get_config_cache;
+return @rv;
 }
 
 # find(name, &config, [disabled-mode], [sectionname], [sectionvalue])
@@ -126,7 +152,6 @@ return wantarray ? @rv : $rv[0];
 sub save_directive
 {
 local ($conf, $name, $value, $sname, $svalue) = @_;
-local $lref = &read_file_lines($config{'dovecot_config'});
 local $dir = ref($name) ? $name : &find($name, $conf, 0, $sname, $svalue);
 local $newline = ref($name) ? "$name->{'name'} = $value" : "$name = $value";
 if ($sname) {
@@ -134,13 +159,15 @@ if ($sname) {
        }
 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($conf, $dir->{'line'}, -1);
+       &renumber($conf, $dir->{'line'}, $dir->{'file'}, -1);
        @$conf = grep { $_ ne $dir } @$conf;
        }
 elsif (!$dir && defined($value)) {
@@ -148,8 +175,9 @@ elsif (!$dir && defined($value)) {
        local $cmt = &find($name, $conf, 1, $sname, $svalue);
        if ($cmt) {
                # After comment
+               local $lref = &read_file_lines($cmt->{'file'});
                splice(@$lref, $cmt->{'line'}+1, 0, $newline);
-               &renumber($conf, $cmt->{'line'}+1, 1);
+               &renumber($conf, $cmt->{'line'}+1, $cmt->{'file'}, 1);
                push(@$conf, { 'name' => $name,
                               'value' => $value,
                               'line' => $cmt->{'line'}+1,
@@ -161,9 +189,10 @@ elsif (!$dir && defined($value)) {
                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;
                splice(@$lref, $line, 0, $newline);
-               &renumber($conf, $line, 1);
+               &renumber($conf, $line, $insect[$#insect]->{'file'}, 1);
                push(@$conf, { 'name' => $name,
                               'value' => $value,
                               'line' => $line,
@@ -171,7 +200,8 @@ elsif (!$dir && defined($value)) {
                               'sectionvalue' => $svalue });
                }
        else {
-               # Need to put at end
+               # Need to put at end of main config
+               local $lref = &read_file_lines($config{'dovecot_config'});
                push(@$lref, $newline);
                push(@$conf, { 'name' => $name,
                               'value' => $value,
@@ -187,7 +217,7 @@ elsif (!$dir && defined($value)) {
 sub save_section
 {
 local ($conf, $section) = @_;
-local $lref = &read_file_lines($config{'dovecot_config'});
+local $lref = &read_file_lines($section->{'file'});
 local $indent = "  " x $section->{'indent'};
 local @newlines;
 push(@newlines, $indent.$section->{'name'}." ".$section->{'value'}." {");
@@ -197,17 +227,20 @@ foreach my $m (@{$section->{'members'}}) {
 push(@newlines, $indent."}");
 local $oldlen = $section->{'eline'} - $section->{'line'} + 1;
 splice(@$lref, $section->{'line'}, $oldlen, @newlines);
-&renumber($conf, $section->{'eline'}, scalar(@newlines)-$oldlen);
+&renumber($conf, $section->{'eline'}, $section->{'file'},
+         scalar(@newlines)-$oldlen);
 $section->{'eline'} = $section->{'line'} + scalar(@newlines) - 1;
 }
 
-# renumber(&conf, line, offset)
+# renumber(&conf, line, file, offset)
 sub renumber
 {
-local ($conf, $line, $offset) = @_;
+local ($conf, $line, $file, $offset) = @_;
 foreach my $c (@$conf) {
-       $c->{'line'} += $offset if ($c->{'line'} >= $line);
-       $c->{'eline'} += $offset if ($c->{'eline'} >= $line);
+       if ($c->{'file'} eq $file) {
+               $c->{'line'} += $offset if ($c->{'line'} >= $line);
+               $c->{'eline'} += $offset if ($c->{'eline'} >= $line);
+               }
        }
 }
 
@@ -244,7 +277,7 @@ sub stop_dovecot
 {
 local $script = &get_initscript();
 if ($script) {
-       local $out = &backquote_logged("$script stop 2>&1");
+       local $out = &backquote_logged("$script stop 2>&1 </dev/null");
        return $? ? "<pre>$out</pre>" : undef;
        }
 else {
@@ -265,7 +298,7 @@ sub start_dovecot
 {
 local $script = &get_initscript();
 local $cmd = $script ? "$script start" : $config{'dovecot'};
-local $out = &backquote_logged("$cmd 2>&1");
+local $out = &backquote_logged("$cmd 2>&1 </dev/null");
 return $? ? "<pre>$out</pre>" : undef;
 }
 
@@ -323,5 +356,28 @@ 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);
+       }
+}
+
 1;
+r
 
index 3adeec6..d57ace8 100755 (executable)
@@ -20,7 +20,7 @@ print &ui_table_row($text{'login_realm'},
 
 # Authentication mechanisms (MD5, etc..)
 if (&find("auth_mechanisms", $conf, 2)) {
-       # Version 0.99 format
+       # Version 0.99 and 2.0 format
        @mechs = split(/\s+/, &find_value("auth_mechanisms", $conf));
        }
 else {
@@ -46,6 +46,12 @@ elsif ($usec = &find_section("userdb", $conf, undef, "auth", "default")) {
        $args = &find_value("args", $conf, undef, "userdb", $usec->{'value'});
        $userdb .= " $args" if ($args);
        }
+elsif (&find_value("driver", $conf, 2, "userdb")) {
+       # Version 2.0 format
+       $userdb = &find_value("driver", $conf, undef, "userdb");
+       $args = &find_value("args", $conf, undef, "userdb");
+       $userdb .= " ".$args if ($args);
+       }
 else {
        # Version 1.0 format
        $userdb = &find_value("userdb", $conf, undef, "auth", "default");
@@ -113,6 +119,12 @@ elsif ($psec = &find_section("passdb", $conf, undef, "auth", "default")) {
        $passdb .= " $args" if ($args);
        $alpha_opts = 1;
        }
+elsif (&find_value("driver", $conf, 2, "passdb")) {
+       # Version 2.0 format
+       $passdb = &find_value("driver", $conf, undef, "passdb");
+       $args = &find_value("args", $conf, undef, "passdb");
+       $passdb .= " ".$args if ($args);
+       }
 else {
        # Version 1.0 format
        $passdb = &find_value("passdb", $conf, undef, "auth", "default");
index 8c42ebf..c7573b8 100755 (executable)
@@ -9,12 +9,24 @@ print &ui_form_start("save_ssl.cgi", "post");
 print &ui_table_start($text{'ssl_header'}, "width=100%", 4);
 
 # SSL cert and key files
-$cert = &find_value("ssl_cert_file", $conf);
+if (&find_value("ssl_cert", $conf, 2)) {
+       $cert = &find_value("ssl_cert", $conf);
+       $cert =~ s/^<//;
+       }
+else {
+       $cert = &find_value("ssl_cert_file", $conf);
+       }
 print &ui_table_row($text{'ssl_cert'},
            &ui_opt_textbox("cert", $cert, 40, &getdef("ssl_cert_file")), 3,
            [ undef, "nowrap" ]);
 
-$key = &find_value("ssl_key_file", $conf);
+if (&find_value("ssl_key", $conf, 2)) {
+       $key = &find_value("ssl_key", $conf);
+       $key =~ s/^<//;
+       }
+else {
+       $key = &find_value("ssl_key_file", $conf);
+       }
 print &ui_table_row($text{'ssl_key'},
            &ui_opt_textbox("key", $key, 40, &getdef("ssl_key_file")), 3,
            [ undef, "nowrap" ]);
@@ -26,7 +38,13 @@ print &ui_table_row($text{'ssl_pass'},
            [ undef, "nowrap" ]);
 
 # SSL CA file
-$ca = &find_value("ssl_ca_file", $conf);
+if (&find_value("ssl_ca", $conf, 2)) {
+       $ca = &find_value("ssl_ca", $conf);
+       $ca =~ s/^<//;
+       }
+else {
+       $ca = &find_value("ssl_ca_file", $conf);
+       }
 print &ui_table_row($text{'ssl_ca'},
            &ui_opt_textbox("ca", $ca, 40,
                &getdef("ssl_ca_file", [ [ "", $text{'ssl_none'} ] ])), 3,
index 6f7892a..b6f7638 100755 (executable)
@@ -4,8 +4,8 @@
 require './dovecot-lib.pl';
 &ReadParse();
 &error_setup($text{'login_err'});
-&lock_file($config{'dovecot_config'});
 $conf = &get_config();
+&lock_dovecot_files($conf);
 
 # Allowed and default realm
 &save_directive($conf, "auth_realms",
@@ -67,6 +67,12 @@ elsif (&find("auth_userdb", $conf, 2)) {
        # Version 0.99 format
        &save_directive($conf, "auth_userdb", $userdb);
        }
+elsif (&find_value("driver", $conf, 2, "userdb")) {
+       # Version 2.0 format
+       $args = $userdb =~ s/\s+(\S.*)$// ? $1 : undef;
+       &save_directive($conf, "driver", $userdb, "userdb");
+       &save_directive($conf, "args", $args, "userdb");
+       }
 else {
        # Version 1.0 format
        &save_directive($conf, "userdb", $userdb, "auth", "default");
@@ -133,6 +139,12 @@ elsif (&find("auth_passdb", $conf, 2)) {
        # Version 0.99 format
        &save_directive($conf, "auth_passdb", $passdb);
        }
+elsif (&find_value("driver", $conf, 2, "passdb")) {
+       # Version 2.0 format
+       $args = $passdb =~ s/\s+(\S.*)$// ? $1 : undef;
+       &save_directive($conf, "driver", $passdb, "passdb");
+       &save_directive($conf, "args", $args, "passdb");
+       }
 else {
        # Version 1.0 format
        &save_directive($conf, "passdb", $passdb, "auth", "default");
@@ -177,7 +189,7 @@ if (&find("login_processes_count", $conf, 2)) {
        }
 
 &flush_file_lines();
-&unlock_file($config{'dovecot_config'});
+&unlock_dovecot_files($conf);
 &webmin_log("login");
 &redirect("");
 
index e124796..d0280ac 100755 (executable)
@@ -4,8 +4,8 @@
 require './dovecot-lib.pl';
 &ReadParse();
 &error_setup($text{'mail_err'});
-&lock_file($config{'dovecot_config'});
 $conf = &get_config();
+&lock_dovecot_files($conf);
 
 # Mail file location
 if ($in{'envmode'} == 4) {
@@ -100,7 +100,7 @@ foreach $l ("mbox_read_locks", "mbox_write_locks") {
        }
 
 &flush_file_lines();
-&unlock_file($config{'dovecot_config'});
+&unlock_dovecot_files($conf);
 &webmin_log("mail");
 &redirect("");
 
index 0df62bd..6bb7a9e 100755 (executable)
@@ -4,8 +4,8 @@
 require './dovecot-lib.pl';
 &ReadParse();
 &error_setup($text{'net_err'});
-&lock_file($config{'dovecot_config'});
 $conf = &get_config();
+&lock_dovecot_files($conf);
 
 &save_directive($conf, "protocols", join(" ", split(/\0/, $in{'protocols'})));
 $sslopt = &find("ssl_disable", $conf, 2) ? "ssl_disable" : "ssl";
@@ -31,7 +31,7 @@ foreach $l (@listens) {
        }
 
 &flush_file_lines();
-&unlock_file($config{'dovecot_config'});
+&unlock_dovecot_files($conf);
 &webmin_log("net");
 &redirect("");
 
index 1d324ac..44a4f73 100755 (executable)
@@ -4,18 +4,45 @@
 require './dovecot-lib.pl';
 &ReadParse();
 &error_setup($text{'ssl_err'});
-&lock_file($config{'dovecot_config'});
 $conf = &get_config();
+&lock_dovecot_files($conf);
 
 # Save SSL cert and key
-$in{'cert_def'} || -r $in{'cert'} || &error($text{'ssl_ecert'});
-$in{'key_def'} || -r $in{'key'} || &error($text{'ssl_ekey'});
-&save_directive($conf, "ssl_cert_file", $in{'cert_def'} ? undef : $in{'cert'});
-&save_directive($conf, "ssl_key_file", $in{'key_def'} ? undef : $in{'key'});
+$in{'cert_def'} || -r $in{'cert'} || $in{'cert'} =~ /^[<>\|]/ ||
+       &error($text{'ssl_ecert'});
+if (&find_value("ssl_cert", $conf, 2)) {
+       $in{'cert'} = "<".$in{'cert'} if ($in{'cert'} =~ /^\//);
+       &save_directive($conf, "ssl_cert",
+                       $in{'cert_def'} ? undef : $in{'cert'});
+       }
+else {
+       &save_directive($conf, "ssl_cert_file",
+                       $in{'cert_def'} ? undef : $in{'cert'});
+       }
+$in{'key_def'} || -r $in{'key'} || $in{'key'} =~ /^[<>\|]/ ||
+       &error($text{'ssl_ekey'});
+if (&find_value("ssl_key", $conf, 2)) {
+       $in{'key'} = "<".$in{'key'} if ($in{'key'} =~ /^\//);
+       &save_directive($conf, "ssl_key",
+                       $in{'key_def'} ? undef : $in{'key'});
+       }
+else {
+       &save_directive($conf, "ssl_key_file",
+                       $in{'key_def'} ? undef : $in{'key'});
+       }
 
 # Save SSL CA cert
-$in{'ca_def'} || -r $in{'ca'} || &error($text{'ssl_eca'});
-&save_directive($conf, "ssl_ca_file", $in{'ca_def'} ? undef : $in{'ca'});
+$in{'ca_def'} || -r $in{'ca'} || $in{'ca'} =~ /^[<>\|]/ ||
+       &error($text{'ssl_eca'});
+if (&find_value("ssl_ca", $conf, 2)) {
+       $in{'ca'} = "<".$in{'ca'} if ($in{'ca'} =~ /^\//);
+       &save_directive($conf, "ssl_ca",
+                       $in{'ca_def'} ? undef : $in{'ca'});
+       }
+else {
+       &save_directive($conf, "ssl_ca_file",
+                       $in{'ca_def'} ? undef : $in{'ca'});
+       }
 
 # Save SSL key password
 $in{'pass_def'} || $in{'pass'} =~ /\S/ || &error($text{'ssl_epass'});
@@ -30,7 +57,7 @@ $in{'regen_def'} || $in{'regen'} =~ /^\d+$/ || &error($text{'ssl_eregen'});
                $in{'plain'} ? $in{'plain'} : undef);
 
 &flush_file_lines();
-&unlock_file($config{'dovecot_config'});
+&unlock_dovecot_files($conf);
 &webmin_log("ssl");
 &redirect("");