use Cyrus::IMAP::Admin;
use FileHandle;
use File::Basename;
use Fcntl ':flock';
my $DEBUG = 0;
my $VERBOSE = 1;
my $mta_cfg = 1;
my $TRANSPORT_FILE = '/etc/postfix/transport';
my $VIRTUAL_FILE = '/etc/postfix/virtual';
my $user = '';
my $pass = '';
my $quota = '';
my $partition = 'default';
while ($_ = shift) {
if (/^-h/) {
usage();
exit;
} elsif (/^-u(ser)?$/) {
$user = shift;
} elsif (/^-p(ass)?$/) {
$pass = shift;
} elsif (/^-part(ition)?/) {
$partition = shift;
} elsif (/^-q(uota)?/) {
$quota = shift;
if ($quota =~ /^(\d+)m/i) {
$quota *= 1024;
}
} elsif (/^-no_mta_cfg/) {
$mta_cfg = 0;
} else {
print "unknown param [$_]\n\n";
usage();
exit;
}
}
if (!$user) {
$user = query('Enter new Cyrus user');
}
if (!$pass) {
$pass = query('Enter password for new Cyrus user', 1);
}
if ($DEBUG) {
print "USERNAME: $user, PASSWORD: $pass\n";
print "PARTITION = [$partition]\n";
print "QUOTA = [$quota]\n";
print "MTA_CFG = [$mta_cfg]\n";
}
$unix_user = (getpwnam($user)) ? 1 : 0;
$VERBOSE && print "$user is".($unix_user ? '' : ' NOT')." a unix user\n";
$client = Cyrus::IMAP::Admin->new('localhost');
defined($client) || die "failed to get Admin obj.";
while (1) {
$cp = getCyrusPassword();
$ret = $client->authenticate(
User => 'cyrus',
Password => $cp,
);
if ($ret) { last; }
print "Cyrus password incorrect.\n";
}
$mbox = "user.$user";
$VERBOSE && print "adding CYRUS user [$mbox]\n";
if (!$DEBUG) {
if (!$client->create($mbox, $partition)) {
die "failed to create mailbox [$mbox]";
}
if (!$client->setacl($mbox, cyrus => 'all')) {
die "failed to give cyrus user permissions for [$mbox]";
}
}
if ($quota && !$DEBUG) {
$VERBOSE && print "setting quota for [$mbox] to $quota\n";
if (!$client->setquota($mbox, STORAGE => $quota)) {
print "failed to set quota for [$mbox]\n";
}
}
setSaslPassword($user, $pass);
if ($unix_user && $mta_cfg) {
addTransportLine($user);
addVirtualLine($user);
$VERBOSE && print "generating new postfix maps\n";
myExec("postmap $TRANSPORT_FILE");
myExec("postmap $VIRTUAL_FILE");
}
exit;
sub usage
{
print <<_EOF_;
usage: $0 -u NEWUSER -p NEWPASSWORD [options]
options:
-q[uota] <quota> quota for mailbox in KB, or MB, if suffixed with 'm'
-part[ition] <part> partition on which to create mailbox. none = 'default'
-no_mta_cfg do not perform MTA-specific steps (Postfix)
If "-u USER" or "-p PASS" is not specified on command line, script
will prompt for it/them.
_EOF_
}
sub query
{
my $prompt = shift;
my $echo_off = shift;
print "${prompt}? ";
my $ans;
do {
if ($echo_off) { system("stty -echo"); }
chomp($ans = <STDIN>);
if ($echo_off) { system("stty echo"); }
if (!$ans) { print "you must enter something here\n"; }
} while(!length($ans));
$ans;
}
sub setSaslPassword
{
my ($user, $pass) = @_;
my $fh = FileHandle->new;
open($fh, "|saslpasswd2 -p $user") || die "popen to saslpasswd2 failed";
print $fh "$pass\n";
close($fh);
}
sub getCyrusPassword
{
print "Please Enter Cyrus administration password: ";
system('stty -echo');
my $p;
chomp($p = <STDIN>);
system('stty echo');
print "\n";
$p;
}
sub addTransportLine
{
my $user = shift;
my $line = "${user}\@y42.org\tcyrus:";
editFile($TRANSPORT_FILE, 'add-cyrus-user', $line);
}
sub addVirtualLine
{
my $user = shift;
my $line = "${user}\t${user}\@y42.org";
editFile($VIRTUAL_FILE, 'Cyrus IMAP users', $line);
}
sub editFile
{
my ($file, $tag, $line) = @_;
my (@contents);
$VERBOSE && print "editing [$file]\n";
my $dn = dirname($file);
my $bn = basename($file);
my $fh = FileHandle->new("+<$file");
defined($fh) || die "open $file for reading";
lockFile($fh);
my $state = 'looking';
while(<$fh>) {
if ($state eq 'found') {
if (/^\s* push(@contents, $_);
next;
}
push(@contents, "$line\n");
push(@contents, $_);
$state = 'copyrest';
} elsif ($state eq 'looking') {
push(@contents, $_);
if (/$tag/) {
$state = 'found';
}
} else {
push(@contents, $_);
}
}
if ($state eq 'looking') {
die "tag [$tag] not found in file [$file]";
}
if ($DEBUG) {
unlockFile($fh);
return;
}
seek($fh, 0, 0) || die "seek";
truncate($fh, 0) || die "truncate";
foreach (@contents) {
print $fh $_;
}
unlockFile($fh);
$fh->close;
}
sub myRename
{
my ($old, $new) = @_;
($VERBOSE > 1) && print "rename: $old --> $new\n";
$DEBUG && return;
if (!rename($old, $new)) {
die "rename of $old to $new failed";
}
}
sub myExec
{
my $cmd = shift;
($VERBOSE > 1) && print "CMD: $cmd\n";
$DEBUG && return;
return !system($cmd);
}
sub unlockFile
{
my $fh = shift;
$DEBUG && print "unlocking file\n";
flock($fh, LOCK_UN) || die "flock lock_un ($!)";
}
sub lockFile
{
my $fh = shift;
$DEBUG && print "locking file\n";
flock($fh, LOCK_EX) || die "flock lock_ex ($!)";
}