package Mail::Toaster::Setup::Vpopmail;
use strict;
use warnings;
our $VERSION = '5.50';
use Carp;
use English '-no_match_vars';
use Params::Validate ':all';
use lib 'lib';
use parent 'Mail::Toaster::Base';
sub install {
my $self = shift;
my %p = validate( @_, { $self->get_std_opts } );
return $p{test_ok} if defined $p{test_ok}; # for testing
if ( !$self->conf->{'install_vpopmail'} ) {
$self->audit( "vpopmail: installing, skipping (disabled)" );
return;
}
my $version = $self->conf->{install_vpopmail} || '5.4.33';
if ( $OSNAME eq "freebsd" ) {
# always install the port version, so subsequent ports will
# find it registered in the ports db.
$self->install_freebsd_port;
}
if ( $version ne 'port' ) {
$self->install_from_source( %p );
};
return $self->post_install;
};
sub install_freebsd_port {
my $self = shift;
my %p = validate( @_, { $self->get_std_opts },);
my $conf = $self->conf;
my $version = $conf->{'install_vpopmail'};
return $p{test_ok} if defined $p{test_ok}; # for testing only
my @defs = 'LOGLEVEL="p"';
my $learn = $conf->{vpopmail_learn_passwords} ? 'SET' : 'UNSET';
my $ip_alias = $conf->{vpopmail_ip_alias_domains} ? 'SET' : 'UNSET';
my $qmail_ext= $conf->{vpopmail_qmail_ext} ? 'SET' : 'UNSET';
my $single_dom=$conf->{vpopmail_disable_many_domains} ? 'SET' : 'UNSET';
my $maildrop = $conf->{vpopmail_maildrop} ? 'SET' : 'UNSET';
my $mysql = $conf->{'vpopmail_mysql'} ? 'SET' : 'UNSET';
my $roaming = $conf->{vpopmail_roaming_users} ? 'SET' : 'UNSET';
my $mysql_rep= my $mysql_lim = my $sql_log = my $valias = 'UNSET';
my $auth_log = $conf->{vpopmail_auth_logging} ? 'SET' : 'UNSET';
if ( $roaming eq 'SET' && $conf->{vpopmail_relay_clear_minutes} ) {
push @defs, 'RELAYCLEAR='.$conf->{vpopmail_relay_clear_minutes};
};
if ( $mysql eq 'SET' ) {
$self->error( "vpopmail_mysql is enabled but install_mysql is not. Please correct your settings" ) if ! $conf->{install_mysql};
$mysql_rep = 'SET' if $conf->{vpopmail_mysql_replication};
$mysql_lim = 'SET' if $conf->{vpopmail_mysql_limits};
$valias = 'SET' if $conf->{vpopmail_valias};
$sql_log = 'SET' if $conf->{vpopmail_mysql_logging};
};
$self->freebsd->install_port( 'vpopmail',
flags => join( ',', @defs ),
options => "# installed by Mail::Toaster
# Options for vpopmail-5.4.32_3
_OPTIONS_READ=vpopmail-5.4.32_3
_FILE_COMPLETE_OPTIONS_LIST=AUTH_LOG CLEAR_PASSWD DOCS DOMAIN_QUOTAS FILE_LOCKING FILE_SYNC FPIC IP_ALIAS LDAP LDAP_SASL LEARN_PASSWORDS MAILDROP MD5_PASSWORDS MYSQL MYSQL_LIMITS MYSQL_REPLICATION ONCHANGE_SCRIPT ORACLE PASSWD PGSQL QMAIL_EXT ROAMING SEEKABLE SINGLE_DOMAIN SMTP_AUTH_PATCH SPAMASSASSIN SPAMFOLDER SQL_LOG SQL_LOG_TRIM SUID_VCHKPW SYBASE USERS_BIG_DIR VALIAS
OPTIONS_FILE_$auth_log+=AUTH_LOG
OPTIONS_FILE_SET+=CLEAR_PASSWD
OPTIONS_FILE_SET+=DOCS
OPTIONS_FILE_UNSET+=DOMAIN_QUOTAS
OPTIONS_FILE_SET+=FILE_LOCKING
OPTIONS_FILE_UNSET+=FILE_SYNC
OPTIONS_FILE_SET+=FPIC
OPTIONS_FILE_$ip_alias+=IP_ALIAS
OPTIONS_FILE_UNSET+=LDAP
OPTIONS_FILE_UNSET+=LDAP_SASL
OPTIONS_FILE_$learn+=LEARN_PASSWORDS
OPTIONS_FILE_$maildrop+=MAILDROP
OPTIONS_FILE_SET+=MD5_PASSWORDS
OPTIONS_FILE_$mysql+=MYSQL
OPTIONS_FILE_$mysql_lim+=MYSQL_LIMITS
OPTIONS_FILE_$mysql_rep+=MYSQL_REPLICATION
OPTIONS_FILE_UNSET+=ONCHANGE_SCRIPT
OPTIONS_FILE_UNSET+=ORACLE
OPTIONS_FILE_UNSET+=PASSWD
OPTIONS_FILE_UNSET+=PGSQL
OPTIONS_FILE_$qmail_ext+=QMAIL_EXT
OPTIONS_FILE_$roaming+=ROAMING
OPTIONS_FILE_SET+=SEEKABLE
OPTIONS_FILE_$single_dom+=SINGLE_DOMAIN
OPTIONS_FILE_UNSET+=SMTP_AUTH_PATCH
OPTIONS_FILE_UNSET+=SPAMASSASSIN
OPTIONS_FILE_UNSET+=SPAMFOLDER
OPTIONS_FILE_$sql_log+=SQL_LOG
OPTIONS_FILE_UNSET+=SQL_LOG_TRIM
OPTIONS_FILE_UNSET+=SUID_VCHKPW
OPTIONS_FILE_UNSET+=SYBASE
OPTIONS_FILE_SET+=USERS_BIG_DIR
OPTIONS_FILE_$valias+=VALIAS
",
) or return;
my $vpopdir = $self->get_vpop_dir;
my $docroot = $self->conf->{'toaster_http_docs'};
# add a symlink so docs are web browsable
if ( -d $docroot && ! -e "$docroot/vpopmail" ) {
if ( -d "$vpopdir/doc/man_html" ) {
symlink "$vpopdir/doc/man_html", "$docroot/vpopmail";
}
}
}
sub install_from_source {
my $self = shift;
my %p = validate( @_, { $self->get_std_opts },);
my $conf = $self->conf;
my $version = $conf->{'install_vpopmail'} || "5.4.33";
my $package = "vpopmail-$version";
my $vpopdir = $self->get_vpop_dir;
$self->create_user(); # add the vpopmail user/group
my $uid = getpwnam( $conf->{'vpopmail_user'} || "vpopmail" );
my $gid = getgrnam( $conf->{'vpopmail_group'} || "vchkpw" );
my $installed = $self->installed_version();
if ( $installed && $installed eq $version ) {
$self->util->yes_or_no(
"Do you want to reinstall vpopmail with the same version?",
timeout => 60,
) or do {
$self->post_install();
return 1;
};
}
my $conf_args;
foreach ( qw/ rebuild_tcpserver_file ip_alias_domains valias mysql_logging
qmail_ext learn_passwords mysql / ) {
my $mt_setting = 'vpopmail_' . $_;
my $conf_arg = "--enable-$_";
$conf_arg =~ s/_/-/g;
my $r = $conf->{$mt_setting} ? 'yes' : 'no';
$conf_args .= " $conf_arg=$r";
print "$conf_arg=$r\n";
};
if ( ! $self->is_newer( min => "5.3.30", cur => $version ) ) {
if ( defined $conf->{'vpopmail_default_quota'} ) {
$conf_args .=
" --enable-defaultquota=".$conf->{'vpopmail_default_quota'};
print "default quota: ".$conf->{'vpopmail_default_quota'}."\n";
}
else {
$conf_args .= " --enable-defaultquota=100000000S,10000C";
print "default quota: 100000000S,10000C\n";
}
}
$conf_args .= $self->roaming_users();
if ( $OSNAME eq "darwin" && !-d "/usr/local/mysql"
&& -d "/opt/local/include/mysql" ) {
$conf_args .= " --enable-incdir=/opt/local/include/mysql";
$conf_args .= " --enable-libdir=/opt/local/lib/mysql";
}
my $tcprules = $self->util->find_bin( "tcprules", verbose=>0 );
$conf_args .= " --enable-tcprules-prog=$tcprules";
my $src = $conf->{'toaster_src_dir'} || "/usr/local/src";
$self->util->cwd_source_dir( "$src/mail" );
my $tarball = "$package.tar.gz";
# save having to download it again
if ( -e "/usr/ports/distfiles/vpopmail-$version.tar.gz" ) {
copy(
"/usr/ports/distfiles/vpopmail-$version.tar.gz",
"/usr/local/src/mail/"
);
}
$self->util->sources_get(
'package' => $package,
site => "http://" . $conf->{'toaster_sf_mirror'},
path => "/vpopmail",
);
if ( -d $package ) {
$self->util->source_warning(
package => $package,
src => "$src/mail",
) or return;
}
$self->util->extract_archive( $tarball ) or die;
if ( $conf->{vpopmail_mysql} ) {
$conf_args .= $self->mysql_options();
};
$conf_args .= $self->logging();
$conf_args .= $self->default_domain($version);
$conf_args .= $self->etc_passwd();
# in case someone updates their toaster and not their config file
if ( defined $conf->{'vpopmail_qmail_ext'}
&& $conf->{'vpopmail_qmail_ext'} ) {
$conf_args .= " --enable-qmail-ext=y";
print "qmail extensions: yes\n";
}
if ( defined $conf->{'vpopmail_maildrop'} ) {
$conf_args .= " --enable-maildrop=y";
};
print "fixup for longer passwords\n";
system "sed -i -Ee '/^pw_clear_passwd char(/s/16/128/' vmysql.h";
system "sed -i -Ee '/^pw_passwd char(/s/40/128/' vmysql.h";
chdir($package);
print "running configure with $conf_args\n\n";
$self->util->syscmd( "./configure $conf_args", verbose => 0 );
$self->util->syscmd( "make", verbose => 0 );
$self->util->syscmd( "make install-strip", verbose => 0 );
if ( -e "vlimits.h" ) {
# this was for a bug in vpopmail 5.4.?(1-2) installer
$self->util->syscmd( "cp vlimits.h $vpopdir/include/", verbose => 0);
}
return 1;
}
sub default_domain {
my $self = shift;
my $version = shift;
my $default_domain;
if ( defined $self->conf->{'vpopmail_default_domain'} ) {
$default_domain = $self->conf->{'vpopmail_default_domain'};
}
else {
$self->util->yes_or_no( "Do you want to use a default domain? " ) or do {
print "default domain: NONE SELECTED.\n";
return '';
};
$default_domain = $self->util->ask("your default domain");
};
if ( ! $default_domain ) {
print "default domain: NONE SELECTED.\n";
return '';
};
if ( $self->is_newer( min => "5.3.22", cur => $version ) ) {
my $vpopetc = $self->get_vpop_etc;
$self->util->file_write( "$vpopetc/defaultdomain",
lines => [ $default_domain ],
verbose => 0,
);
$self->util->chown( "$vpopetc/defaultdomain",
uid => $self->conf->{'vpopmail_user'} || "vpopmail",
gid => $self->conf->{'vpopmail_group'} || "vchkpw",
);
return '';
}
print "default domain: $default_domain\n";
return " --enable-default-domain=$default_domain";
};
sub vpopmail_etc {
my $self = shift;
my %p = validate( @_, { $self->get_std_opts } );
my $vetc = $self->get_vpop_etc;
mkpath( $vetc, oct('0775') ) if ! -d $vetc;
if ( -d $vetc ) {
print "$vetc already exists.\n";
}
else {
print "creating $vetc\n";
mkdir( $vetc, oct('0775') ) or carp "failed to create $vetc: $!\n";
}
$self->setup->tcp_smtp( etc_dir => $vetc );
$self->setup->tcp_smtp_cdb( etc_dir => $vetc );
}
sub etc_passwd {
my $self = shift;
unless ( defined $self->conf->{'vpopmail_etc_passwd'} ) {
print "\t\t CAUTION!! CAUTION!!
The system user account feature is NOT compatible with qmail-smtpd-chkusr.
If you selected that option in the qmail build, you should not answer
yes here. If you are unsure, select (n).\n";
if ( $self->util->yes_or_no( "Do system users (/etc/passwd) get mail? (n) ")) {
print "system password accounts: yes\n";
return " --enable-passwd";
}
}
if ( $self->conf->{'vpopmail_etc_passwd'} ) {
print "system password accounts: yes\n";
return " --enable-passwd";
}
print "system password accounts: no\n";
};
sub get_vpop_etc {
my $self = shift;
my $base = $self->get_vpop_dir;
return "$base/etc";
};
sub get_vpop_dir {
my $self = shift;
return $self->{conf}{vpopmail_home_dir} || '/usr/local/vpopmail';
};
sub installed_version {
my $self = shift;
my $vpopdir = $self->get_vpop_dir;
return if ! -x "$vpopdir/bin/vpasswd";
my $installed = `$vpopdir/bin/vpasswd -v | head -1 | cut -f2 -d" "`;
chop $installed;
$self->alert( "vpopmail version $installed installed." );
return $installed;
}
sub logging {
my $self = shift;
my $conf = $self->conf;
if ( defined $conf->{vpopmail_logging} && $conf->{vpopmail_logging} ) {
if ( $conf->{'vpopmail_logging_verbose'} ) {
print "logging: verbose with failed passwords\n";
return " --enable-logging=v";
}
print "logging: everything\n";
return " --enable-logging=y";
}
if ( ! $self->util->yes_or_no( "Do you want logging enabled? (y) ")) {
return " --enable-logging=p";
};
if ( $self->util->yes_or_no( "Do you want verbose logging? (y) ")) {
print "logging: verbose\n";
return " --enable-logging=v";
}
print "logging: verbose with failed passwords\n";
return " --enable-logging=p";
};
sub post_install {
my $self = shift;
$self->vpopmail_etc;
$self->mysql_privs;
$self->util->install_module( "vpopmail" ) if $self->{conf}{install_ezmlm_cgi};
print "vpopmail: complete.\n";
return 1;
};
sub roaming_users {
my $self = shift;
my $roaming = $self->conf->{'vpopmail_roaming_users'};
if ( defined $roaming && !$roaming ) {
print "roaming users: no\n";
return " --enable-roaming-users=n";
}
# default to enabled
if ( !defined $self->conf->{'vpopmail_roaming_users'} ) {
print "roaming users: value not set?!\n";
}
print "roaming users: yes\n";
my $min = $self->conf->{'vpopmail_relay_clear_minutes'};
if ( $min && $min ne 180 ) {
print "roaming user minutes: $min\n";
return " --enable-roaming-users=y" .
" --enable-relay-clear-minutes=$min";
};
return " --enable-roaming-users=y";
};
sub test {
my $self = shift;
my %p = validate( @_, { $self->get_std_opts },);
return $p{test_ok} if defined $p{test_ok};
print "do vpopmail directories exist...\n";
my $vpdir = $self->conf->{'vpopmail_home_dir'};
foreach ( "", "bin", "domains", "etc/", "include", "lib" ) {
$self->setup->test->pretty(" $vpdir/$_", -d "$vpdir/$_" );
}
print "checking vpopmail binaries...\n";
foreach (
qw/
clearopensmtp vaddaliasdomain vadddomain
valias vadduser vchkpw
vchangepw vconvert vdeldomain
vdelivermail vdeloldusers vdeluser
vdominfo vipmap vkill
vmkpasswd vmoddomlimits vmoduser
vpasswd vpopbull vqmaillocal
vsetuserquota vuserinfo /
)
{
$self->setup->test->pretty(" $_", -x "$vpdir/bin/$_" );
}
print "do vpopmail libs exist...\n";
foreach ("$vpdir/lib/libvpopmail.a") {
$self->setup->test->pretty(" $_", -e $_ );
}
print "do vpopmail includes exist...\n";
foreach (qw/ config.h vauth.h vlimits.h vpopmail.h vpopmail_config.h /) {
$self->setup->test->pretty(" include/$_", -e "$vpdir/include/$_" );
}
print "checking vpopmail etc files...\n";
my @vpetc = qw/ inc_deps lib_deps tcp.smtp tcp.smtp.cdb vlimits.default /;
push @vpetc, 'vpopmail.mysql' if $self->conf->{'vpopmail_mysql'};
foreach ( @vpetc ) {
$self->setup->test->pretty(" $_", (-e "$vpdir/etc/$_" && -s "$vpdir/etc/$_" ));
}
}
sub create_user {
my $self = shift;
my %p = validate( @_, { $self->get_std_opts } );
my $vpopdir = $self->get_vpop_dir;
my $vpuser = $self->conf->{vpopmail_user} || 'vpopmail';
my $vpgroup = $self->conf->{vpopmail_group} || 'vchkpw';
my $uid = getpwnam($vpuser);
my $gid = getgrnam($vpgroup);
if ( !$uid || !$gid ) {
$self->group_add( $vpgroup, "89" );
$self->user_add( $vpuser, 89, 89, homedir => $vpopdir );
}
$uid = getpwnam($vpuser);
$gid = getgrnam($vpgroup);
return $self->error( "failed to add vpopmail user or group!")
if ( !$uid || !$gid );
return 1;
}
sub mysql_options {
my $self = shift;
my $mysql_repl = $self->conf->{vpopmail_mysql_replication};
my $my_write = $self->conf->{vpopmail_mysql_repl_master} || 'localhost';
my $db = $self->conf->{vpopmail_mysql_database} || 'vpopmail';
my $opts;
if ( $self->conf->{vpopmail_mysql_limits} ) {
print "mysql qmailadmin limits: yes\n";
$opts .= " --enable-mysql-limits=y";
}
if ( $mysql_repl ) {
$opts .= " --enable-mysql-replication=y";
print "mysql replication: yes\n";
print " replication master: $my_write\n";
}
if ( $self->conf->{vpopmail_disable_many_domains} ) {
$opts .= " --disable-many-domains";
}
return $opts;
}
sub mysql_privs {
my $self = shift;
my %p = validate( @_, { $self->get_std_opts },);
if ( !$self->conf->{'vpopmail_mysql'} ) {
print "vpopmail mysql_privs: mysql support not selected\n";
return;
}
my $mysql_repl = $self->conf->{vpopmail_mysql_replication};
my $my_write = $self->conf->{vpopmail_mysql_repl_master} || 'localhost';
my $my_write_port = $self->conf->{vpopmail_mysql_repl_master_port} || 3306;
my $my_read = $self->conf->{vpopmail_mysql_repl_slave} || 'localhost';
my $my_read_port = $self->conf->{vpopmail_mysql_repl_slave_port} || 3306;
my $db = $self->conf->{vpopmail_mysql_database} || 'vpopmail';
my $user = $self->conf->{'vpopmail_mysql_user'} || $self->conf->{vpopmail_mysql_repl_user};
my $pass = $self->conf->{'vpopmail_mysql_pass'} || $self->conf->{vpopmail_mysql_repl_pass};
my $vpopdir = $self->get_vpop_dir;
my @lines = "$my_read|0|$user|$pass|$db";
if ($mysql_repl) {
push @lines, "$my_write|$my_write_port|$user|$pass|$db";
}
else {
push @lines, "$my_read|$my_read_port|$user|$pass|$db";
}
$self->util->file_write( "$vpopdir/etc/vpopmail.mysql",
lines => \@lines,
verbose => 1,
);
my $dot = $self->mysql->parse_dot_file( ".my.cnf", "[mysql]", 0 )
|| { user => $user, pass => $pass, host => $my_write, db => $db };
my ( $dbh, $dsn, $drh ) = $self->mysql->connect( $dot, 1 );
if ( !$dbh ) {
$dot = { user => 'root', pass => '', host => $my_write };
( $dbh, $dsn, $drh ) = $self->mysql->connect( $dot, 1 );
};
if ( !$dbh ) {
print <<"EOMYSQLGRANT";
WARNING: I couldn't connect to your database server! If this is a new install,
you will need to connect to your database server and run this command manually:
mysql -u root -h $my_write -p
CREATE DATABASE $db;
GRANT ALL PRIVILEGES ON $db.* TO $user\@'$my_write' IDENTIFIED BY '$pass';
use $db;
CREATE TABLE relay ( ip_addr char(18) NOT NULL default '',
timestamp char(12) default NULL, name char(64) default NULL,
PRIMARY KEY (ip_addr)) PACK_KEYS=1;
ALTER TABLE vpopmail MODIFY pw_clear_passwd VARCHAR(128);
ALTER TABLE vpopmail MODIFY pw_passwd VARCHAR(128);
quit;
If this is an upgrade and you already use MySQL authentication,
then you can safely ignore this warning.
EOMYSQLGRANT
return;
}
my $query = "use $db";
my $sth = $self->mysql->query( $dbh, $query, 1 );
if ( !$sth->errstr ) {
$self->audit( "vpopmail: database setup, ok (exists)" );
$sth->finish;
return 1;
}
print "vpopmail: no vpopmail database, creating it now...\n";
$query = "CREATE DATABASE $db";
$sth = $self->mysql->query( $dbh, $query );
print "vpopmail: granting privileges to $user\n";
$query =
"GRANT ALL PRIVILEGES ON $db.* TO $user\@'$my_write' IDENTIFIED BY '$pass'";
$sth = $self->mysql->query( $dbh, $query );
print "vpopmail: creating the relay table.\n";
$query = "CREATE TABLE $db.relay ( ip_addr char(18) NOT NULL default '', timestamp char(12) default NULL, name char(64) default NULL, PRIMARY KEY (ip_addr)) PACK_KEYS=1";
$sth = $self->mysql->query( $dbh, $query );
$self->audit( "vpopmail: databases created, ok" );
$sth = $self->mysql->query( $dbh, "ALTER TABLE vpopmail MODIFY pw_clear_passwd VARCHAR(128)" );
$sth = $self->mysql->query( $dbh, "ALTER TABLE vpopmail MODIFY pw_passwd VARCHAR(128)" );
$sth->finish;
return 1;
}
1;