--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore Fri Feb 21 11:56:39 2014 +0100
@@ -0,0 +1,4 @@
+syntax: glob
+.ok.*
+*.[0-9].gz
+x
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgtags Fri Feb 21 11:56:39 2014 +0100
@@ -0,0 +1,1 @@
+6a6c18cddf46998e8a1acc933ff4afbacdb177b8 hhsp-dovecot-0.1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.perltidyrc Fri Feb 21 11:56:39 2014 +0100
@@ -0,0 +1,2 @@
+-ce
+-noll
--- a/Common.pm Tue Jul 31 10:46:37 2007 +0000
+++ b/Common.pm Fri Feb 21 11:56:39 2014 +0100
@@ -1,4 +1,5 @@
package Common;
+
# $Id$
# $URL$
use strict;
@@ -9,64 +10,84 @@
GLOBAL => { DEFAULT => undef },
# * common *
- add => { ARGS => "!", ALIAS => [qw/new create/] },
- list => { ARGS => "!", ALIAS => "ls" },
- modify => { ARGS => "!", ALIAS => "change" },
- delete => { ARGS => "!", ALIAS => "remove" },
+ add => { ARGS => "!", ALIAS => [qw/new create/] },
+ list => { ARGS => "!", ALIAS => "ls" },
+ modify => { ARGS => "!", ALIAS => "change" },
+ delete => { ARGS => "!", ALIAS => "remove" },
- ldap_base => { ARGS => "=s", DEFAULT => ldapBase(qw(/etc/openldap/ldap.conf /etc/ldap/ldap.conf)) },
- ldap_server => { ARGS => "=s", DEFAULT => "localhost" },
- ldap_bind_dn => { ARGS => "=s", DEFAULT => "cn=admin", ALIAS => "ldap_admin" },
- ldap_password =>{ ARGS => "=s" },
+ ldap_base => {
+ ARGS => "=s",
+ DEFAULT => ldapBase(qw(/etc/openldap/ldap.conf /etc/ldap/ldap.conf))
+ },
+ ldap_server => { ARGS => "=s", DEFAULT => "localhost" },
+ ldap_bind_dn =>
+ { ARGS => "=s", DEFAULT => "cn=admin", ALIAS => "ldap_admin" },
+ ldap_password => { ARGS => "=s" },
- help => { ARGS => "!" },
- debug => { ARGS => "!" },
+ help => { ARGS => "!" },
+ debug => { ARGS => "!" },
- description => { ARGS => "=s" },
-
+ description => { ARGS => "=s" },
# * account *
default_domain => { ARGS => "=s", DEFAULT => "" },
- imap_server => { ARGS => "=s", DEFAULT => "localhost" },
- imap_admin => { ARGS => "=s", DEFAULT => $ENV{USER} },
- imap_password =>{ ARGS => "=s" },
- imap_quota => { ARGS => "=i", DEFAULT => 300, ALIAS => "quota" },
+ imap_server => { ARGS => "=s", DEFAULT => "localhost" },
+ imap_port => { ARGS => "=s", DEFAULT => "143" },
+ imap_admin => { ARGS => "=s", DEFAULT => $ENV{USER} },
+ imap_password => { ARGS => "=s" },
+ imap_quota =>
+ { ARGS => "=i", DEFAULT => 300 * 1024 * 1024, ALIAS => "quota" },
+ imap_aclgroups => { ARGS => "=s", ALIAS => "aclgroups" },
+
+ # dovecots mail_location (%1, %u & %d supported)
+ imap_mail_location =>
+ { ARGS => "=s", DEFAULT => '/var/vmail/users/%d/%1/%u' },
+
+ mbox => { ARGS => "!", DEFAULT => 1 },
+ password => { ARGS => "=s" },
- mbox => { ARGS => "!", DEFAULT => 1 },
- password => { ARGS => "=s" },
-# internal => { ARGS => "!", DEFAULT => ":", ALIAS => "restricted" },
+ # internal => { ARGS => "!", DEFAULT => ":", ALIAS => "restricted" },
- other => { ARGS => ":s" },
- group => { ARGS => ":s" },
- forward => { ARGS => ":s" },
- fullname => { ARGS => "=s", ALIAS => "realname" },
- address => { ARGS => "=s", ALIAS => "primary" },
+ other => { ARGS => ":s" },
+ group => { ARGS => ":s" },
+ forward => { ARGS => ":s" },
+ fullname => { ARGS => "=s", ALIAS => "realname" },
+ address => { ARGS => "=s", ALIAS => "primary" },
+
+ # * acl *
+ acl_admin => { ARGS => "=s" },
+ acl_password => { ARGS => "=s" },
+ folder => { ARGS => ":s@" },
+ acl => { ARGS => "=s" },
+ recursive => { ARGS => "!", DEFAULT => 0 },
# * alias * group *
- members => { ARGS => ":s" },
+ members => { ARGS => ":s" },
# * shared *
#access => { ARGS => ":s" },
# * group *
- gid_min => { ARGS => "=i", DEFAULT => 60000 },
- gid_max => { ARGS => "=i", DEFAULT => 60100 },
+ gid_min => { ARGS => "=i", DEFAULT => 60000 },
+ gid_max => { ARGS => "=i", DEFAULT => 60100 },
# * ldap intern *
- ldap_ou_aliases => { ARGS => "=s", DEFAULT => "ou=MailAliases" },
- ldap_ou_accounts => { ARGS => "=s", DEFAULT => "ou=MailAccounts" },
- ldap_ou_groups => { ARGS => "=s", DEFAULT => "ou=Groups" },
+ ldap_ou_aliases => { ARGS => "=s", DEFAULT => "ou=MailAliases" },
+ ldap_ou_accounts => { ARGS => "=s", DEFAULT => "ou=MailAccounts" },
+ ldap_ou_groups => { ARGS => "=s", DEFAULT => "ou=Groups" },
+
+ ldap_oc_alias => { ARGS => "=s", DEFAULT => "XXXmailAlias" },
+ ldap_oc_recipient => { ARGS => "=s", DEFAULT => "XXXmailRecipient" },
+ ldap_oc_accessgroup => { ARGS => "=s", DEFAULT => "XXXmailAccessGroup" },
- ldap_oc_alias => { ARGS => "=s", DEFAULT => "XXXmailAlias" },
- ldap_oc_recipient => { ARGS => "=s", DEFAULT => "XXXmailRecipient" },
- ldap_oc_accessgroup => { ARGS => "=s", DEFAULT => "XXXmailAccessGroup" },
-
- ldap_at_address => { ARGS => "=s", DEFAULT => "XXXmailAddress" },
- ldap_at_group => { ARGS => "=s", DEFAULT => "XXXmailGroup" },
+ ldap_at_address => { ARGS => "=s", DEFAULT => "XXXmailAddress" },
+ ldap_at_group => { ARGS => "=s", DEFAULT => "XXXmailGroup" },
+ ldap_at_quota => { ARGS => "=s", DEFAULT => "XXXmailQuota" },
+ ldap_at_aclgroups => { ARGS => "=s", DEFAULT => "XXXmailACLGroups" },
ldap_at_forwardingaddress =>
- { ARGS => "=s", DEFAULT => "XXXmailForwardingAddress" },
- ldap_at_primaryaddress =>
- { ARGS => "=s", DEFAULT => "XXXmailPrimaryAddress" },
+ { ARGS => "=s", DEFAULT => "XXXmailForwardingAddress" },
+ ldap_at_primaryaddress =>
+ { ARGS => "=s", DEFAULT => "XXXmailPrimaryAddress" },
);
--- a/Makefile Tue Jul 31 10:46:37 2007 +0000
+++ b/Makefile Fri Feb 21 11:56:39 2014 +0100
@@ -75,4 +75,7 @@
rubber ${RUBBER_FLAGS} $<
%.gz: %.pod
- iconv -f utf8 -t iso8859-15 $< | pod2man --name $(basename $<) --section $(subst .,,$(suffix $@)) | gzip >$@
+ pod2man --utf8 --name $(basename $<) --section $(subst .,,$(suffix $@)) $< | gzip >$@
+
+tidy:
+ perltidy -b $(SCRIPTS) $(PM)
--- a/account.pm Tue Jul 31 10:46:37 2007 +0000
+++ b/account.pm Fri Feb 21 11:56:39 2014 +0100
@@ -1,23 +1,24 @@
package account;
+
# © Heiko Schlittermann
# $Id$
# $URL$
use strict;
use warnings;
+use File::Path qw(remove_tree);
use Net::LDAP;
-use Net::LDAP::Constant qw(LDAP_ALREADY_EXISTS LDAP_NO_SUCH_OBJECT LDAP_TYPE_OR_VALUE_EXISTS);
+use Net::LDAP::Constant
+ qw(LDAP_ALREADY_EXISTS LDAP_NO_SUCH_OBJECT LDAP_TYPE_OR_VALUE_EXISTS);
use Net::LDAP::Entry;
-use Cyrus::IMAP::Admin;
+use Mail::IMAPTalk;
use Text::Wrap;
use password;
-
my $Cf;
-my ($ldap, $ubase, $abase);
-my ($imap);
-END { $imap and $imap = undef; };
-
+my ( $ldap, $ubase, $abase );
+my ( $imap, $imap_password );
+END { $imap and $imap = undef; }
sub _add();
sub _list();
@@ -25,6 +26,7 @@
sub _mkpw($);
sub uniq(@);
sub verbose(@);
+sub _mbox($);
sub OU_ACCOUNTS();
sub OU_ALIASES();
@@ -33,73 +35,82 @@
sub AT_ADDRESS();
sub AT_GROUP();
sub AT_FORWARDINGADDRESS();
+sub AT_QUOTA();
+sub AT_ACLGROUPS();
sub import(@) {
$Cf = shift;
require constant;
- import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts;
- import constant OU_ALIASES => $Cf->ldap_ou_aliases;
- import constant OC_RECIPIENT => $Cf->ldap_oc_recipient;
- import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress;
- import constant AT_ADDRESS => $Cf->ldap_at_address;
- import constant AT_GROUP => $Cf->ldap_at_group;
+ import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts;
+ import constant OU_ALIASES => $Cf->ldap_ou_aliases;
+ import constant OC_RECIPIENT => $Cf->ldap_oc_recipient;
+ import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress;
+ import constant AT_ADDRESS => $Cf->ldap_at_address;
+ import constant AT_GROUP => $Cf->ldap_at_group;
import constant AT_FORWARDINGADDRESS => $Cf->ldap_at_forwardingaddress;
+ import constant AT_QUOTA => $Cf->ldap_at_quota;
+ import constant AT_ACLGROUPS => $Cf->ldap_at_aclgroups;
$ubase = OU_ACCOUNTS . "," . $Cf->ldap_base;
$abase = OU_ALIASES . "," . $Cf->ldap_base;
}
sub run($) {
+
# Eigentlich brauchen wir für alles imap und ldap
$ldap = new Net::LDAP $Cf->ldap_server or die;
- my $r = $ldap->bind($Cf->ldap_bind_dn,
- password => $Cf->ldap_password || $ENV{LDAP_PASS} || password::ask("LDAP (". $Cf->ldap_bind_dn .") password: "));
+ my $r = $ldap->bind( $Cf->ldap_bind_dn,
+ password => $Cf->ldap_password
+ || $ENV{LDAP_PASS}
+ || password::ask( "LDAP (" . $Cf->ldap_bind_dn . ") password: " ) );
die $r->error, "\n" if $r->code;
- $imap = new Cyrus::IMAP::Admin or die $@;
- $imap->authenticate(-server => $Cf->imap_server, -user => $Cf->imap_admin,
- -password => $Cf->imap_password || $ENV{IMAP_PASS} || password::ask("IMAP (". $Cf->imap_admin .") password: "))
- or die $@;
-
+ $imap =
+ Mail::IMAPTalk->new( Server => $Cf->imap_server, Port => $Cf->imap_port )
+ or die "Can't connect to IMAP Server '", $Cf->imap_server, "', Port '",
+ $Cf->imap_port, "': ", $@;
+ $imap_password =
+ $Cf->imap_password
+ || $ENV{IMAP_PASS}
+ || password::ask( "IMAP (" . $Cf->imap_admin . ") password: " );
- if ($Cf->list) { _list() }
- elsif ($Cf->add) { _add() }
- elsif ($Cf->delete) { _delete() }
- elsif ($Cf->modify) { _modify() }
- else { die "Need action (--add|--modify|--list|--delete)\n" };
+ if ( $Cf->list ) { _list() }
+ elsif ( $Cf->add ) { _add() }
+ elsif ( $Cf->delete ) { _delete() }
+ elsif ( $Cf->modify ) { _modify() }
+ else { die "Need action (--add|--modify|--list|--delete)\n" }
}
sub _add() {
-# Beim Hinzufügen tragen wir nur das unbedingt notwendige
-# ein. Wenn es schon eine mailPrimaryAddress gibt oder eine
-# mail, machen wir gar nichts.
-# Ansonsten:
-# uid wird hinzugefügt
-# cn, sn bleiben unangetastet
-# Wenn die mailbox-Option gesetzt ist, wird die
-# IMAP-Mailbox angelegt.
+ # Beim Hinzufügen tragen wir nur das unbedingt notwendige
+ # ein. Wenn es schon eine mailPrimaryAddress gibt oder eine
+ # mail, machen wir gar nichts.
+ # Ansonsten:
+ # uid wird hinzugefügt
+ # cn, sn bleiben unangetastet
+ # Wenn die mailbox-Option gesetzt ist, wird die
+ # IMAP-Mailbox angelegt.
die "Need user name for creation\n" if not @ARGV;
my $user = shift @ARGV;
- my $mailPrimaryAddress = $Cf->primary || $user; # evtl. mit !
- my $mailAddress = [$user, split /,/, $Cf->other || ""]; # ditto
+ my $mailPrimaryAddress = $Cf->primary || $user; # evtl. mit !
+ my $mailAddress = [ $user, split /,/, $Cf->other || "" ]; # ditto
- $user =~ s/!$//; # jetzt können wir ! nicht mehr brauchn
- my $mbox = "user/$user";
+ $user =~ s/!$//; # jetzt können wir ! nicht mehr brauchn
my $cn = $Cf->fullname || $user;
- my $sn = (reverse split " ", $cn)[0];
- my $mailGroup = [split /,/, $Cf->group || ""];
- my $mailForwardingAddress = [split /,/, $Cf->forward || ""];
- my $pw = _mkpw($Cf->password || "{pwgen}");
+ my $sn = ( reverse split " ", $cn )[0];
+ my $mailGroup = [ split /,/, $Cf->group || "" ];
+ my $mailForwardingAddress = [ split /,/, $Cf->forward || "" ];
+ my $pw = _mkpw( $Cf->password || "{pwgen}" );
+ my $mbox = _mbox($user);
- if ($mailPrimaryAddress !~ /@/) {
- $mailPrimaryAddress .= "@" . $Cf->default_domain;
+ if ( $mailPrimaryAddress !~ /@/ ) {
+ $mailPrimaryAddress .= "@" . $Cf->default_domain;
}
-
my $dn = "uid=$user,$ubase";
my $r;
@@ -365,55 +376,99 @@
sub _list() {
my $filter;
@ARGV = ("*") unless @ARGV;
- $filter = "(|" . join("", map { "(uid=$_)" } @ARGV) . ")";
+ $filter = "(|" . join( "", map { "(uid=$_)" } @ARGV ) . ")";
my $r = $ldap->search(
- filter => $filter,
- base => $ubase,
- #attrs => [qw/uid cn mail userPassword/, (AT_PRIMARYADDRESS)]
+ filter => $filter,
+ base => $ubase,
+
+ #attrs => [qw/uid cn mail userPassword/, (AT_PRIMARYADDRESS)]
);
die $r->error if $r->code;
#if (-t STDOUT) { open(LESS, "|less -F -X") and select LESS; }
+ while ( my $e = $r->shift_entry ) {
+ my $uid = $e->get_value("uid");
+ my $cn = join( ", ", $e->get_value("cn") );
+ my $mr = $e->get_value(AT_PRIMARYADDRESS) || ""; # ??
+ my $ml = join( ", ", $e->get_value(AT_ADDRESS) ) || ""; # ??
+ my $mg = join( ", ", $e->get_value(AT_GROUP) ) || ""; # ??
+ my $forw = join( ", ", $e->get_value(AT_FORWARDINGADDRESS) ) || "";
+ my $ag = $e->get_value(AT_ACLGROUPS);
+ $ag = '$' . join ',$', split /,/, $ag if $ag;
- while (my $e = $r->shift_entry) {
- my $uid = $e->get_value("uid");
- my $cn = join(", ", $e->get_value("cn"));
- my $mr = $e->get_value(AT_PRIMARYADDRESS) || ""; # ??
- my $ml = join(", ", $e->get_value(AT_ADDRESS)) || ""; # ??
- my $mg = join(", ", $e->get_value(AT_GROUP)) || ""; # ??
- my $forw = join (", ", $e->get_value(AT_FORWARDINGADDRESS)) || "";
- my $mbox = "user/$uid";
+ print "$uid: $cn <$mr>";
+
+ #if (($e->get_value("iusRestrictedMail")||"") eq "TRUE") {
+ #print " INTERNAL";
+ #}
- print "$uid: $cn <$mr>";
+ # das imap protokoll sieht keine zustandsänderung von 'authenticated'
+ # zu 'not authenticated' vor - daher müssen wir für jeden nutzer eine
+ # eigene verbindung aufbauen
+ $imap = Mail::IMAPTalk->new(
+ Server => $Cf->imap_server,
+ Port => $Cf->imap_port
+ )
+ or die "Can't connect to IMAP Server '", $Cf->imap_server,
+ "', Port '", $Cf->imap_port, "': ", $@;
+ $imap->login( "$uid*" . $Cf->imap_admin, $imap_password ) or die $@;
- #if (($e->get_value("iusRestrictedMail")||"") eq "TRUE") {
- #print " INTERNAL";
- #}
+ my %q;
+ if ( $imap->capability->{quota} ) {
+
+ # prepare patterns for shared folders - we want to ignore them in
+ # quota calculations (TODO: what happens if a user has/attempts to
+ # create a folder with the name of a namespace? he could avoid
+ # quota limits that way?)
+ my $ns = $imap->namespace() or die $@;
+ my @p = map qr{^\Q$_->[0]\E}, ( @{ $ns->[1] }, @{ $ns->[2] } );
+
+ my $folders = $imap->list( '', '*' ) or die $@;
+
+ for my $f ( @{$folders} ) {
- MBOX: {
- if (!$imap->list($mbox)) {
- print ", no mbox";
- last MBOX;
- }
- print ", mbox";
- my %q = $imap->listquota($mbox);
- my ($used, $max) = map { int($_ / 1024) } @{$q{STORAGE}};
+ # single folder sieht wie folgt aus: [[flag1, flag2, ...], separator, foldername]
+ #next if '\\Noselect' ~~ $f->[0];
+ # ignore shared folders
+ map { next if ( $f->[2] . $f->[1] ) =~ $_ } @p;
+ my $q = $imap->getquotaroot( $f->[2] )
+ or $@ eq
+ q{IMAP Command : 'getquotaroot' failed. Response was : no - Not showing other users' quota.}
+ or die $@;
+ delete $q->{quotaroot};
+ %q = ( %q, %{$q} );
+
+ }
+
+ }
+
+ $imap->logout or die $@;
- if (!$max) {
- print ", no quota";
- last MBOX;
- }
- print ", quota ($used/$max): " . int(100 * $used/$max) . "%";
- }
- print "\n";
+ # da wir uns anmelden konnten haben wir auch eine 'mbox'
+ print ", mbox";
+ my $has_quota;
+ for my $qr ( keys %q ) {
+ my @q = @{ $q{$qr} };
+ my $elem = '';
+ $elem = shift @q while defined $elem and $elem ne 'STORAGE';
+ my ( $used, $max ) = map { int( $_ / 1024 ) } @q[ 0 .. 1 ];
+ $max ||= 1;
+ print ", quota '$qr': $used/${max}MB "
+ . int( 100 * $used / $max ) . "%";
+ $has_quota = 1;
+ }
+ print ", no quota" unless $has_quota;
+ print "\n";
- print "\tPassword: ", $> == 0 ? $e->get_value("userPassword") : "*", "\n";
-
- print wrap("\t", "\t\t", "Other Adresses: $ml\n") if $ml;
- print wrap("\t", "\t\t", "Mail Groups: $mg\n") if $mg;
- print wrap("\t", "\t\t", "Forwardings: $forw\n") if $forw;
+ print "\tPassword: ", $> == 0 ? $e->get_value("userPassword") : "*",
+ "\n";
+
+ print wrap( "\t", "\t\t", "Other Adresses: $ml\n" ) if $ml;
+ print wrap( "\t", "\t\t", "Mail Groups: $mg\n" ) if $mg;
+ print wrap( "\t", "\t\t", "Forwardings: $forw\n" ) if $forw;
+ print wrap( "\t", "\t\t", "ACL Groups: $ag\n" ) if $ag;
}
}
@@ -428,19 +483,44 @@
return keys %x;
}
-{ my @pw;
-sub _mkpw($) {
- my $in = $_[0];
+{
+ my @pw;
+
+ sub _mkpw($) {
+ my $in = $_[0];
+
+ return $in unless $in and $in eq "{pwgen}";
- return $in unless $in and $in eq "{pwgen}";
+ if ( !@pw ) {
+ chomp( @pw = `pwgen 8 10 2>/dev/null` );
+ die "pwgen: $!" if $?;
+ }
+ return shift @pw;
+
+ }
+}
+
+sub _mbox($) {
- if (!@pw) {
- chomp(@pw = `pwgen 8 10 2>/dev/null|| mkpasswd`);
- die "pwgen/mkpasswd: $!" if $?;
- }
- return shift @pw;
-
-} }
+ my ($user) = @_;
+
+ my ( $localpart, $domain, $escapes );
+
+ # assuming usernames of the form localpart@domain
+ $user =~ /(.+)@(.+)$/;
+ ( $localpart, $domain ) = ( $1, $2 );
+
+ die "Invalid username '$user'"
+ unless $escapes->{'%u'} = $localpart
+ and $escapes->{'%1'} = substr $localpart, 0, 1
+ and $escapes->{'%d'} = $domain;
+ my $mbox = $Cf->imap_mail_location;
+ $mbox =~ s/$_/$escapes->{$_}/ for keys %{$escapes};
+
+ return $mbox;
+
+}
1;
+
# vim:sts=4 sw=4 aw ai sm nohlsearch:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/account.pm.rej Fri Feb 21 11:56:39 2014 +0100
@@ -0,0 +1,530 @@
+--- account.pm Mon Mar 02 13:51:24 2009 +0000
++++ account.pm Thu Dec 15 16:08:16 2011 +0100
+@@ -118,257 +129,342 @@
+
+ verbose("\t$dn...");
+
+- $r = $ldap->search(base => $ubase, filter => "(uid=$user)");
++ $r = $ldap->search( base => $ubase, filter => "(uid=$user)" );
+ die $r->error if $r->code;
+ die "Multiple entries not expected" if $r->count > 1;
+
+ my $e;
+- if ($r->count) {
+- $e = $r->shift_entry;
++ if ( $r->count ) {
++ $e = $r->shift_entry;
+ } else {
+- $e = new Net::LDAP::Entry;
+- $e->dn($dn);
++ $e = new Net::LDAP::Entry;
++ $e->dn($dn);
+ }
+
+- if ($e->exists("mail") || $e->exists(AT_PRIMARYADDRESS) || $e->exists("userPassword")) {
+- verbose "exists\n";
++ if ( $e->exists("mail")
++ || $e->exists(AT_PRIMARYADDRESS)
++ || $e->exists("userPassword") )
++ {
++ verbose "exists\n";
+ } else {
+- FORCE:
++ FORCE:
+
+- # Bevor wir ans Werk gehen, noch ein paar Tests (mailPrimaryAddress und mail darf
+- # noch nicht vergeben sein)
+- foreach my $a ($mailPrimaryAddress, @$mailAddress) {
+- $a =~ s/!$// and next; # wenn ein ! am Ende steht, dann ist es so gewollt und wird
+- # nicht geprüft
+- $r = $ldap->search(filter => "(mail=$a)", base => $ubase);
+- die $r->error if $r->code;
+- die "$a ist schon vergeben\n" if $r->count;
+- }
++ # Bevor wir ans Werk gehen, noch ein paar Tests (mailPrimaryAddress und mail darf
++ # noch nicht vergeben sein)
++ foreach my $a ( $mailPrimaryAddress, @$mailAddress ) {
++ $a =~ s/!$//
++ and
++ next; # wenn ein ! am Ende steht, dann ist es so gewollt und wird
++ # nicht geprüft
++ $r = $ldap->search( filter => "(mail=$a)", base => $ubase );
++ die $r->error if $r->code;
++ die "$a ist schon vergeben\n" if $r->count;
++ }
+
+- $e->replace(objectClass => [uniq $e->get("objectClass"), qw/uidObject person/, OC_RECIPIENT]);
+- $e->replace(uid => [uniq $e->get("uid"), $user]);
++ $e->replace(
++ objectClass => [
++ uniq $e->get("objectClass"),
++ qw/uidObject person/,
++ OC_RECIPIENT
++ ]
++ );
++ $e->replace( uid => [ uniq $e->get("uid"), $user ] );
+
+- $e->add((AT_ADDRESS) => $mailAddress);
+- $e->add((AT_PRIMARYADDRESS) => $mailPrimaryAddress);
+- $e->add(userPassword => $pw);
+- $e->add((AT_GROUP) => $mailGroup) if @$mailGroup;
+- $e->add((AT_FORWARDINGADDRESS) => $mailForwardingAddress) if @$mailForwardingAddress;
++ $e->add( (AT_ADDRESS) => $mailAddress );
++ $e->add( (AT_PRIMARYADDRESS) => $mailPrimaryAddress );
++ $e->add( userPassword => "{plain}$pw" );
++ $e->add( (AT_GROUP) => $mailGroup ) if @$mailGroup;
++ $e->add( (AT_FORWARDINGADDRESS) => $mailForwardingAddress )
++ if @$mailForwardingAddress;
++ $e->add( (AT_QUOTA) => $Cf->imap_quota );
++ $e->add( (AT_ACLGROUPS) => $Cf->imap_aclgroups ) if $Cf->imap_aclgroups;
+
+- # $e->add(iusRestrictedMail => $Cf->internal) if $Cf->internal ne ":";
++ # $e->add(iusRestrictedMail => $Cf->internal) if $Cf->internal ne ":";
+
+- $e->exists("sn") or $e->add(sn => $sn);
+- $e->exists("cn") or $e->add(cn => $cn);
++ $e->exists("sn") or $e->add( sn => $sn );
++ $e->exists("cn") or $e->add( cn => $cn );
+
++ $r = $e->update($ldap);
++ die $r->error if $r->code;
+
+- $r = $e->update($ldap);
+- die $r->error if $r->code;
+-
+- verbose("ok");
+- verbose(" Password: $pw") if not $Cf->password or $Cf->password eq "{pwgen}";
++ verbose('ok');
++ verbose(" Password: $pw")
++ if not $Cf->password
++ or $Cf->password eq "{pwgen}";
+ }
+
+- if($Cf->mbox) {
+- verbose("\n\t$mbox...");
++ if ( $Cf->mbox ) {
+
+- if ($imap->list($mbox)) { verbose("exists") }
+- else {
+- $imap->create($mbox) and verbose("ok") or die $@;
+- $imap->setacl($mbox, $Cf->imap_admin => "lrswipcda") or die $@;
+- $imap->setquota($mbox, STORAGE => 1024 * $Cf->imap_quota) or die $@;
+- }
++ verbose("\n\t$mbox...");
++
++ if ( -d $mbox ) {
++
++ verbose('exists')
++
++ } elsif ( ( $imap->login( $user, $pw ) or die $@ )
++ and $imap->capability->{acl} )
++ {
++
++ # wenn wir acl verwenden,
++ # * dann triggert 'list' acl file (und damit maildir) erzeugung
++ # bei dovecot
++ # * müssen wir dem master nutzer ausdrücklich rechte gewähren
++ # (sofern wir das nicht eleganter über globale acl regeln können)
++ # (lra: sicht-, les- und administrierbar)
++ my $f = $imap->list( '', '*' ) or die $@;
++
++ #$imap->setacl( $f->[0]->[2], $Cf->imap_admin, 'lra' ) or die $@;
++ verbose('ok');
++
++ } else {
++
++ verbose('will be created automatically on first email delivery');
++
++ }
++
+ }
+
+-
+ verbose("\n");
+ }
+
+ sub _modify() {
+-# Auch hier gehen wir davon aus, daß die dn direkt aus dem User-Namen folgt:
+-# dn: uid=USER,...
++
++ # Auch hier gehen wir davon aus, daß die dn direkt aus dem User-Namen folgt:
++ # dn: uid=USER,...
+ my (@users) = @ARGV or die "Need username(s)\n";
+ my @dns;
+
+- my $r = $ldap->search(base => $ubase,
+- filter => "(|" . join("", map { "(uid=$_)" } @ARGV) . ")");
++ my $r = $ldap->search(
++ base => $ubase,
++ filter => "(|" . join( "", map { "(uid=$_)" } @ARGV ) . ")"
++ );
+ die $r->error if $r->code;
+ die "No entries found.\n" if $r->count == 0;
+
+- while (my $e = $r->shift_entry) {
+- my $r;
++ while ( my $e = $r->shift_entry ) {
++ my $r;
+
+- my $user = $e->get_value("uid");
+- my $dn = $e->dn;
+- my $mbox = "user/$user";
++ my $user = $e->get_value("uid");
++ my $dn = $e->dn;
+
+- my $modified = 0;
+- verbose "$user:";
++ my $modified = 0;
++ verbose "$user:";
+
+- verbose "\n\t$dn...";
++ verbose "\n\t$dn...";
+
+- # Fix: iusMailOptions wurde erst später eingeführt, bei Bedarf also hinzufügen
+- #if (!grep /^iusMailOptions$/, @{$e->get("objectClass")}) {
+- #$e->add(objectClass => "iusMailOptions");
+- #}
++ # Fix: iusMailOptions wurde erst später eingeführt, bei Bedarf also hinzufügen
++ #if (!grep /^iusMailOptions$/, @{$e->get("objectClass")}) {
++ #$e->add(objectClass => "iusMailOptions");
++ #}
+
+- if (my $cn = $Cf->fullname) {
+- # Aus dem Fullnamen leiten wir cn und sn ab.
+- my $sn = (reverse split " ", $cn)[0];
++ if ( my $cn = $Cf->fullname ) {
+
+- if ($cn =~ s/^\+//) {
+- $e->replace(
+- cn => [uniq $e->get("cn"), $cn],
+- sn => [uniq $e->get("sn"), $sn]);
+- } elsif ($cn =~ s/^-//) {
+- $e->delete(cn => [$cn], sn => [$sn]);
+- } else { $e->replace(cn => $cn, sn => $sn); }
+- $modified++;
+- }
++ # Aus dem Fullnamen leiten wir cn und sn ab.
++ my $sn = ( reverse split " ", $cn )[0];
+
+- if (defined $Cf->other) {
+- my @o = split /,/, $Cf->other;
+- grep { /^[+-]/ } @o or $e->delete(AT_ADDRESS);
++ if ( $cn =~ s/^\+// ) {
++ $e->replace(
++ cn => [ uniq $e->get("cn"), $cn ],
++ sn => [ uniq $e->get("sn"), $sn ]
++ );
++ } elsif ( $cn =~ s/^-// ) {
++ $e->delete( cn => [$cn], sn => [$sn] );
++ } else {
++ $e->replace( cn => $cn, sn => $sn );
++ }
++ $modified++;
++ }
+
+- foreach my $a (split /,/, $Cf->other) {
+- if ($a =~ s/^-//) {
+- $e->delete((AT_ADDRESS) => [$a])
+- } else {
+- $a =~ s/^\+//;
++ if ( defined $Cf->other ) {
++ my @o = split /,/, $Cf->other;
++ grep { /^[+-]/ } @o or $e->delete(AT_ADDRESS);
+
+- # Darf noch nicht woanders sein
+- $r = $ldap->search(base => $ubase, filter => "(mail=$a)");
+- die $r->error if $r->code;
+- die "$a ist schon vergeben\n" if $r->count;
++ foreach my $a ( split /,/, $Cf->other ) {
++ if ( $a =~ s/^-// ) {
++ $e->delete( (AT_ADDRESS) => [$a] );
++ } else {
++ $a =~ s/^\+//;
+
+- $e->add((AT_ADDRESS) => [$a])
+- }
+- }
+- $modified++;
+- }
++ # Darf noch nicht woanders sein
++ $r = $ldap->search( base => $ubase, filter => "(mail=$a)" );
++ die $r->error if $r->code;
++ die "$a ist schon vergeben\n" if $r->count;
+
+- if (defined $Cf->group) {
+- my @g = split /,/, $Cf->group;
+- grep { /^[+-]/ } @g or $e->delete(AT_GROUP)
+- if $e->get_value(AT_GROUP);
++ $e->add( (AT_ADDRESS) => [$a] );
++ }
++ }
++ $modified++;
++ }
+
+- foreach my $g (@g) {
+- if ($g =~ s/^-//) {
+- $e->delete((AT_GROUP) => [$g])
+- } else {
+- $g =~ s/^\+//;
+- $e->add((AT_GROUP) => [$g])
+- }
+- }
+- $modified++;
+- }
++ if ( defined $Cf->group ) {
++ my @g = split /,/, $Cf->group;
++ grep { /^[+-]/ } @g
++ or $e->delete(AT_GROUP)
++ if $e->get_value(AT_GROUP);
+
+- if (defined $Cf->forward) {
+- my @f = split /,/, $Cf->forward;
+- grep { /^[+-]/ } @f or $e->delete(AT_FORWARDINGADDRESS)
+- if $e->get_value(AT_FORWARDINGADDRESS);
++ foreach my $g (@g) {
++ if ( $g =~ s/^-// ) {
++ $e->delete( (AT_GROUP) => [$g] );
++ } else {
++ $g =~ s/^\+//;
++ $e->add( (AT_GROUP) => [$g] );
++ }
++ }
++ $modified++;
++ }
+
+- foreach my $f (@f) {
+- if ($f =~ s/^-//) {
+- $e->delete((AT_FORWARDINGADDRESS) => [$f]);
+- } else {
+- $f =~ s/^\+//;
+- $e->add((AT_FORWARDINGADDRESS) => [$f]);
+- }
+- }
+- $modified++;
+- }
++ if ( defined $Cf->forward ) {
++ my @f = split /,/, $Cf->forward;
++ grep { /^[+-]/ } @f
++ or $e->delete(AT_FORWARDINGADDRESS)
++ if $e->get_value(AT_FORWARDINGADDRESS);
+
+- if (my $a = $Cf->primary) {
+- $r = $ldap->search(base => $ubase,
+- # filter => "(|(mailPrimaryAddress=$a)(mail=$a))");
+- filter => "(mail=$a)");
+- die $r->error if $r->code;
+- die "$a ist schon vergeben\n" if $r->count;
+-
+- $e->replace((AT_PRIMARYADDRESS) => $Cf->primary);
+- $modified++;
+- }
++ foreach my $f (@f) {
++ if ( $f =~ s/^-// ) {
++ $e->delete( (AT_FORWARDINGADDRESS) => [$f] );
++ } else {
++ $f =~ s/^\+//;
++ $e->add( (AT_FORWARDINGADDRESS) => [$f] );
++ }
++ }
++ $modified++;
++ }
+
+- if (my $pw = _mkpw($Cf->password)) {
+- $e->replace(userPassword => $pw);
+- $modified++;
+- }
++ if ( defined $Cf->quota ) {
++ $e->replace( (AT_QUOTA) => $Cf->quota );
++ $modified++;
++ }
+
+- #if ($Cf->internal ne ":") {
+- #$e->replace(iusRestrictedMail => $Cf->internal ? "TRUE" : "FALSE");
+- #$modified++;
+- #}
++ if ( defined $Cf->aclgroups ) {
+
+- $e->dump if $Cf->debug;
++ my $ag = $Cf->aclgroups;
++ my $lag = $e->get_value(AT_ACLGROUPS);
++ # groups should be supplied with leading '$' for consistency with
++ # dovecots imap acl, but should not be saved in ldap with it!
++ $ag =~ s/(^|,[+-]?)\K\$//g;
+
+- if ($modified) {
+- $r = $e->update($ldap);
+- die $r->error.$r->code if $r->code;
+- }
++ if ( $ag =~ /(^|,\s*)[+-]/ ) {
++ my %x;
++ @x{ split /,/, $lag } = ();
++ for ( split /,/, $ag ) {
++ if (s/^-//) {
++ delete $x{$_};
++ } else {
++ s/^\+//;
++ $x{$_} = undef;
++ }
++ }
+
+- # FIXME: Wenn keine Mailbox existiert, gibt es hier ein Problem
+- if (defined $Cf->imap_quota) {
+- $imap->setquota($mbox, STORAGE => $Cf->imap_quota * 1024)
+- or die $@;
+- }
++ $ag = join ',', sort keys %x;
+
+- verbose "ok\n";
++ }
+
+- print "\n";
++ if ($ag) {
++ $e->replace( (AT_ACLGROUPS) => $ag );
++ } else {
++ $e->delete( AT_ACLGROUPS ) if $lag;
++ }
++ $modified++;
++ }
++
++ if ( my $a = $Cf->primary ) {
++ $r = $ldap->search(
++ base => $ubase,
++
++ # filter => "(|(mailPrimaryAddress=$a)(mail=$a))");
++ filter => "(mail=$a)"
++ );
++ die $r->error if $r->code;
++ die "$a ist schon vergeben\n" if $r->count;
++
++ $e->replace( (AT_PRIMARYADDRESS) => $Cf->primary );
++ $modified++;
++ }
++
++ if ( my $pw = _mkpw( $Cf->password ) ) {
++ $e->replace( userPassword => $pw );
++ $modified++;
++ }
++
++ #if ($Cf->internal ne ":") {
++ #$e->replace(iusRestrictedMail => $Cf->internal ? "TRUE" : "FALSE");
++ #$modified++;
++ #}
++
++ $e->dump if $Cf->debug;
++
++ if ($modified) {
++ $r = $e->update($ldap);
++ die $r->error . $r->code if $r->code;
++ }
++
++ verbose "ok\n";
++
++ print "\n";
+ }
+ }
+
+ sub _delete() {
+-# Wir gehen davon aus, daß es einen dn uid=USER,ou=.... gibt, den wir löschen können.
+-# Wir löschen den kompletten Container. Es kann natürlich sein, daß er noch jemand anders gehört.
+-# Dann ist das Pech. Um es besser zu haben, müßten wir für alles unsere eigenen
+-# Objektklassen haben...
+
+- if (!@ARGV) {
+- print "User: ";
+- chomp($_ = <>);
+- @ARGV = ($_);
++ # Wir gehen davon aus, daß es einen dn uid=USER,ou=.... gibt, den wir löschen können.
++ # Wir löschen den kompletten Container. Es kann natürlich sein, daß er noch jemand anders gehört.
++ # Dann ist das Pech. Um es besser zu haben, müßten wir für alles unsere eigenen
++ # Objektklassen haben...
++
++ if ( !@ARGV ) {
++ print "User: ";
++ chomp( $_ = <> );
++ @ARGV = ($_);
+ }
+
++ foreach (@ARGV) {
++ my $user = $_;
++ my $dn = "uid=$user,$ubase";
+
+- foreach (@ARGV) {
+- my $user = $_;
+- my $dn = "uid=$user,$ubase";
+- my $mbox = "user/$user";
++ verbose("$user:\n");
+
+- verbose("$user:\n");
++ # Nachsehen, ob es noch aliase gibt, in denen dieser Nutzer steht:
++ my $r = $ldap->search(
++ base => $abase,
++ filter => "(" . AT_FORWARDINGADDRESS . "=$_)",
++ attrs => [ "mail", AT_FORWARDINGADDRESS ]
++ );
++ while ( my $e = $r->shift_entry ) {
++ verbose("\tdeleting $user from alias "
++ . $e->get_value("mail")
++ . "..." );
++ $e->delete( (AT_FORWARDINGADDRESS) => [$user] );
+
+- # Nachsehen, ob es noch aliase gibt, in denen dieser Nutzer steht:
+- my $r = $ldap->search(base => $abase,
+- filter => "(".AT_FORWARDINGADDRESS."=$_)",
+- attrs => ["mail", AT_FORWARDINGADDRESS]);
+- while (my $e = $r->shift_entry) {
+- verbose("\tdeleting $user from alias ".$e->get_value("mail")."...");
+- $e->delete((AT_FORWARDINGADDRESS) => [$user]);
++ my $r = $e->update($ldap);
++ if ( $r->code == 0 ) { verbose("ok\n") }
++ else { die $r->error }
++ }
+
+- my $r = $e->update($ldap);
+- if ($r->code == 0) { verbose("ok\n") }
+- else { die $r->error }
+- }
++ verbose("\tdeleting $dn...");
++ $r = $ldap->delete($dn);
+
+- verbose("\tdeleting $dn...");
+- $r = $ldap->delete($dn);
++ if ( $r->code == LDAP_NO_SUCH_OBJECT ) {
++ verbose("doesn't exist");
++ } elsif ( $r->code == 0 ) {
++ verbose("ok");
++ } else {
++ die $r->error;
++ }
++ verbose("\n");
+
+- if ($r->code == LDAP_NO_SUCH_OBJECT) {
+- verbose("doesn't exist");
+- } elsif ($r->code == 0) {
+- verbose("ok");
+- } else {
+- die $r->error;
+- }
+- verbose("\n");
+-
+- if ($Cf->mbox) {
+- verbose("\tdeleting mbox $mbox...");
+- $imap->delete($mbox) and verbose("ok")
+- or verbose($imap->error);
+- }
++ if ( $Cf->mbox ) {
++ my $m = _mbox($user);
++ if ( not( defined $m and $m ) ) {
++ verbose("can't determine mbox location - not deleting it");
++ } else {
++ verbose("\tdeleting $m...");
++ verbose( ( remove_tree $m) ? 'ok' : " Can't remove '$m': $!" );
++ }
++ }
+
+- verbose("\n");
++ print
++ "Don't forget to remove acl entries for this user if any exist!\n";
++ verbose("\n");
+
+ }
+ }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/acl.pm Fri Feb 21 11:56:39 2014 +0100
@@ -0,0 +1,461 @@
+package acl;
+
+# © Heiko Schlittermann
+# $Id$
+# $URL$
+
+use strict;
+use warnings;
+require 5.10.0;
+use File::Path qw(remove_tree);
+use Net::LDAP;
+use Net::LDAP::Constant
+ qw(LDAP_ALREADY_EXISTS LDAP_NO_SUCH_OBJECT LDAP_TYPE_OR_VALUE_EXISTS);
+use Net::LDAP::Entry;
+use Mail::IMAPTalk;
+use Text::Wrap;
+use password;
+use Term::ReadKey;
+
+my $Cf;
+my ( $ldap, $ubase, $abase );
+my ( $imap, $acl_password, $nspat );
+END { $imap and $imap = undef; }
+
+sub _list();
+sub _mkpw($);
+
+sub list_by_user($@);
+sub list_by_folder($);
+sub list_groups(@);
+sub uniq(@);
+sub verbose(@);
+sub prompt($$);
+sub imap_list($$);
+sub imap_rlist($$$);
+sub acl_folders($);
+
+sub OU_ACCOUNTS();
+sub OU_ALIASES();
+sub AT_PRIMARYADDRESS();
+sub OC_RECIPIENT();
+sub AT_ADDRESS();
+sub AT_GROUP();
+sub AT_FORWARDINGADDRESS();
+sub AT_QUOTA();
+sub AT_ACLGROUPS();
+
+sub import(@) {
+ $Cf = shift;
+
+ require constant;
+ import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts;
+ import constant OU_ALIASES => $Cf->ldap_ou_aliases;
+ import constant OC_RECIPIENT => $Cf->ldap_oc_recipient;
+ import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress;
+ import constant AT_ADDRESS => $Cf->ldap_at_address;
+ import constant AT_GROUP => $Cf->ldap_at_group;
+ import constant AT_FORWARDINGADDRESS => $Cf->ldap_at_forwardingaddress;
+ import constant AT_ACLGROUPS => $Cf->ldap_at_aclgroups;
+
+ $ubase = OU_ACCOUNTS . "," . $Cf->ldap_base;
+ $abase = OU_ALIASES . "," . $Cf->ldap_base;
+}
+
+sub run($) {
+
+ # Eigentlich brauchen wir für alles imap und ldap
+ $ldap = new Net::LDAP $Cf->ldap_server or die;
+ my $r = $ldap->bind( $Cf->ldap_bind_dn,
+ password => $Cf->ldap_password
+ || $ENV{LDAP_PASS}
+ || password::ask( "LDAP (" . $Cf->ldap_bind_dn . ") password: " ) );
+ die $r->error, "\n" if $r->code;
+
+ $acl_password =
+ $Cf->acl_password
+ || $ENV{IMAP_PASS}
+ || password::ask( "IMAP (" . $Cf->acl_admin . ") password: " );
+
+ $imap = Mail::IMAPTalk->new(
+ Server => $Cf->imap_server,
+ Port => $Cf->imap_port
+ )
+ or die "Can't connect to IMAP Server '", $Cf->imap_server,
+ "', Port '", $Cf->imap_port, "': ", $@;
+ $imap->login( $Cf->acl_admin, $acl_password ) or die $@;
+ die "IMAP Server does not advertise acl support"
+ unless $imap->capability->{acl};
+
+ $imap->set_tracing(1) if $ENV{TRACE};
+
+ # requires an imap connection
+ my $ns = $imap->namespace() or die "No public namespaces available: $@";
+ $nspat = [];
+ for ( @{ $ns->[2] } ) {
+ ( my $n = $_->[0] ) =~ s/$_->[1]$//;
+ push @{$nspat}, [ qr/\Q$n\E($_->[1]|$)/, $_->[1] ];
+ }
+
+ if ( $Cf->add ) { _modify() }
+ elsif ( $Cf->delete ) { $Cf->acl('delete'); _modify() }
+ elsif ( $Cf->list ) { _list() }
+ elsif ( $Cf->modify ) { _modify() }
+ else { die "Need action (--add|--delete|--list|--modify)\n" }
+
+}
+
+sub _modify() {
+
+ # Auch hier gehen wir davon aus, daß die dn direkt aus dem User-Namen folgt:
+ # dn: uid=USER,...
+ my @users;
+ @ARGV or die "Need user(s)\n";
+ $Cf->folder ~~ [] and die "Need folders(s)\n";
+ $Cf->acl or die "Need acl\n";
+ $Cf->recursive
+ and $Cf->create
+ and die "Use either --recursive or --create but not both\n";
+
+ my $r = $ldap->search(
+ base => $ubase,
+ filter => "(|" . join( "", map { "(uid=$_)" } @ARGV ) . ")"
+ );
+ die $r->error if $r->code;
+ unless ( $r->count ) {
+ prompt( 'No matching user found in ldap. Continue? (y/N)', "n\n" ) =~
+ /y/i
+ or exit 0;
+ @users = @ARGV;
+ }
+
+ while ( my $e = ( $r->shift_entry or shift @users ) ) {
+
+ my ( $user, $dn );
+
+ if ( ref $e eq 'Net::LDAP::Entry' ) {
+ $user = $e->get_value("uid");
+ $dn = $e->dn;
+ } else {
+ $user = $e;
+ $dn = '[dn not available]';
+ }
+
+ my $modified = 0;
+ verbose "$user:\n";
+ verbose "\t$dn...\n";
+
+ for my $folder ( @{ $Cf->folder } ) {
+
+ $imap->create($folder)
+ or die "Can't create folder '$folder': $@"
+ if $Cf->create;
+
+ my @folders = @{ acl_folders($folder) } or die "Got empty folderlist - does '$folder' exist? (use --create if you want me to create it)";
+ for my $f ( @folders ) {
+
+ if ( $Cf->acl eq 'delete' ) {
+ $imap->deleteacl( $f, $user ) or die "Can't delete acl: $@";
+ verbose "\t$f: none\n";
+ } else {
+ $imap->setacl( $f, $user, $Cf->acl )
+ or die "Can't set acl: $@";
+ verbose "\t$f: " . $Cf->acl . "\n";
+ }
+
+ }
+
+ }
+
+ # Fix: iusMailOptions wurde erst später eingeführt, bei Bedarf also hinzufügen
+ #if (!grep /^iusMailOptions$/, @{$e->get("objectClass")}) {
+ #$e->add(objectClass => "iusMailOptions");
+ #}
+
+ #if ($Cf->internal ne ":") {
+ #$e->replace(iusRestrictedMail => $Cf->internal ? "TRUE" : "FALSE");
+ #$modified++;
+ #}
+
+ verbose "ok\n";
+ print "\n";
+
+ }
+
+}
+
+sub _list() {
+
+ #@ARGV = ("*") unless @ARGV;
+
+ die "option acl_admin required\n" unless $Cf->acl_admin;
+
+ if ( $Cf->aclgroups ) {
+
+ warn "--folder option ignored when listing groups"
+ unless $Cf->folder ~~ [];
+ list_groups(@ARGV);
+
+ } elsif (@ARGV) {
+
+ # my $uid = $ARGV[0];
+ # # searching by more than use user may be too expensive
+ # die "Searching by more than one user not supported" unless @ARGV == 1 or $uid =~ /\*/;
+ #list_by_user($_) for @ARGV;
+
+ warn "--folder option ignored when listing by user"
+ unless $Cf->folder ~~ [];
+ list_by_user( $imap, @ARGV );
+
+ } elsif ( not $Cf->folder ~~ [] ) {
+
+ list_by_folder($_) for @{ $Cf->folder };
+
+ } else {
+
+ die
+ "Need either user or --folder. If you really want to search all users then supply the pattern '*'.";
+
+ }
+
+}
+
+sub list_groups(@) {
+
+ @_ = ('*') unless @_;
+ my @ag = split ',', $Cf->imap_aclgroups;
+ my $ag_all = 1 if '*' ~~ @ag;
+ my $ag_att = AT_ACLGROUPS;
+ my $filter =
+ "(&($ag_att=*)" . "(|" . join( "", map { "(uid=$_)" } @_ ) . "))";
+ my $r = $ldap->search(
+ attrs => [ 'uid', AT_ACLGROUPS ],
+ filter => $filter,
+ base => $ubase,
+ );
+ die $r->error if $r->code;
+
+ unless ( $r->count ) {
+ print("No aclgroups found in ldap\n");
+ exit 0;
+ }
+
+ my $users;
+ while ( my $e = ( $r->shift_entry ) ) {
+ my $uid = $e->get_value('uid');
+ my @ag_cur = split ',', $e->get_value($ag_att);
+ for (@ag_cur) {
+ $users->{$_} =
+ defined $users->{$_}
+ ? [ @{ $users->{$_} }, $uid ]
+ : [$uid]
+ if $ag_all or $_ ~~ @ag;
+ }
+ }
+
+ print "$_:\n\t", join( "\n\t", @{ $users->{$_} } ), "\n\n"
+ for keys %{$users};
+
+}
+
+sub list_by_user($@) {
+
+ my $imap = shift;
+ my $filter = "(|" . join( "", map { "(uid=$_)" } @_ ) . ")";
+
+ #my $filter = "(uid=$uid)";
+ my $r = $ldap->search(
+ filter => $filter,
+ base => $ubase,
+ );
+ die $r->error if $r->code;
+ my @users;
+ unless ( $r->count ) {
+ verbose("No matching users found in ldap.\n");
+ @users = @_;
+ }
+
+ while ( my $e = ( $r->shift_entry or shift @users ) ) {
+
+ my ( $uid, $cn, $mr );
+ if ( ref $e eq 'Net::LDAP::Entry' ) {
+ $uid = $e->get_value("uid");
+ $cn = join( ", ", $e->get_value("cn") );
+ $mr = $e->get_value(AT_PRIMARYADDRESS) || ""; # ??
+ } else {
+ $uid = $e;
+ $cn = '[cn not available]';
+ $mr = '[address not available]';
+ }
+
+ print "$uid: $cn <$mr>\n";
+
+ #if (($e->get_value("iusRestrictedMail")||"") eq "TRUE") {
+ #print " INTERNAL";
+ #}
+
+ die "IMAP Server does not advertise acl support"
+ unless $imap->capability->{acl};
+
+ # namespace() result looks like this
+ # [
+ # [ # list of private namespace(s)
+ # [
+ # prefix,
+ # name
+ # ],
+ # ...
+ # ],
+ # [ # list of namespace(s) for mailboxes shared by other users
+ # [
+ # prefix,
+ # name
+ # ],
+ # ...
+ # [ # list of namespace(s) for 'public' shared mailboxes
+ # [
+ # prefix,
+ # name
+ # ],
+ # ...
+ # ]
+ my $hasacl;
+ my $ns = $imap->namespace() or die "No public namespaces available: $@";
+
+ # uns interessieren nur 'public' namespaces
+ for my $n ( @{ $ns->[2] } ) {
+
+ my $folders = imap_rlist( '', $n->[0], $n->[1] );
+ for my $f ( @{$folders} ) {
+
+ #next if '\\Noselect' ~~ $f->[0];
+ my $perms = $imap->getacl($f) or die "Can't getacl '$f': $@";
+ my ( $u, $p );
+ while ( $u = shift @{$perms} and $p = shift @{$perms} ) {
+ next unless $u eq $uid;
+ $hasacl = 1;
+ print "\t$f: $u [$p]\n";
+ }
+
+ }
+
+ }
+
+ print "\tno acl found on listable folders in shared namespaces\n"
+ unless $hasacl;
+ print "\n";
+
+ }
+
+}
+
+sub list_by_folder($) {
+
+ my ($folder) = @_;
+
+ for my $f ( @{ acl_folders($folder) } ) {
+
+ my $hasacl;
+ print "$f\n";
+
+ my $perms = $imap->getacl($f) or die $@;
+ my ( $u, $p );
+ while ( $u = shift @{$perms} and $p = shift @{$perms} ) {
+
+ # '#user' will be listed when we have a global acl for 'user'
+ my $gl = $u =~ /^\$?#/ ? ' [global]' : '';
+ my $gr = $u =~ /^#?\$/ ? ' [group]' : '';
+ $hasacl = 1;
+ print "\t$u [$p]$gr$gl\n";
+ }
+
+ print "\tno acl found\n" unless $hasacl;
+ print "\n";
+
+ }
+
+}
+
+sub verbose(@) {
+ printf STDERR @_;
+}
+
+sub uniq(@) {
+ my %x;
+ @x{@_} = ();
+ return keys %x;
+}
+
+{
+ my @pw;
+
+ sub _mkpw($) {
+ my $in = $_[0];
+
+ return $in unless $in and $in eq "{pwgen}";
+
+ if ( !@pw ) {
+ chomp( @pw = `pwgen 8 10 2>/dev/null` );
+ die "pwgen: $!" if $?;
+ }
+ return shift @pw;
+
+ }
+}
+
+sub imap_list($$) {
+
+ my ( $ref, $folder ) = @_;
+
+ my $list = $imap->list( $ref, $folder )
+ or die "Can't list('$ref', '$folder'): $@";
+
+ # single folder sieht wie folgt aus: [[flag1, flag2, ...], separator, foldername]
+ ref $list and return [ map $_->[2], @{$list} ];
+
+ # assuming empty result list otherwise
+ return [];
+
+}
+
+sub imap_rlist($$$) {
+
+ my ( $ref, $folder, $sep ) = @_;
+ $folder =~ s/$sep+$//;
+
+ my $list = imap_list( $ref, $folder );
+ push @{$list}, @{ imap_list( $ref, "$folder$sep*" ) } if $Cf->recursive;
+ return $list;
+}
+
+sub acl_folders($) {
+
+ my ($f) = @_;
+ my $folders;
+
+ for my $np ( @{$nspat} ) {
+
+ # don't modify $f!
+ ( my $ft = $f ) =~ s/$np->[1]$//;
+ return imap_rlist( '', $f, $np->[1] ) if ( $ft =~ /$np->[0]/ );
+ }
+
+ die "Foldername '$f' must begin with the name of a shared namespace\n";
+
+}
+
+sub prompt($$) {
+ my ( $prompt, $default ) = @_;
+ print $prompt, substr( $default, 0, 1 ), "\b";
+ ReadMode 4;
+ my $r = ReadKey(0);
+ ReadMode 0;
+ if ( $r eq "\n" ) { $r = $default }
+ else { $r .= substr( $default, 1 ) }
+ print $r;
+ return $r;
+}
+
+1;
+
+# vim:sts=4 sw=4 aw ai sm nohlsearch:
--- a/alias.pm Tue Jul 31 10:46:37 2007 +0000
+++ b/alias.pm Fri Feb 21 11:56:39 2014 +0100
@@ -1,4 +1,5 @@
package alias;
+
# © Heiko Schlittermann
# $Id$
# $URL$
@@ -7,17 +8,17 @@
use warnings;
use Net::LDAP;
use Net::LDAP::Constant qw(
- LDAP_ALREADY_EXISTS
- LDAP_NO_SUCH_OBJECT
- LDAP_NO_SUCH_ATTRIBUTE
- LDAP_TYPE_OR_VALUE_EXISTS);
+ LDAP_ALREADY_EXISTS
+ LDAP_NO_SUCH_OBJECT
+ LDAP_NO_SUCH_ATTRIBUTE
+ LDAP_TYPE_OR_VALUE_EXISTS);
use Net::LDAP::Entry;
use Text::Wrap;
use password;
my $Cf;
-my ($ldap, $abase, $ubase);
+my ( $ldap, $abase, $ubase );
sub _add();
sub _list();
@@ -37,142 +38,151 @@
$Cf = shift;
require constant;
- import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts;
- import constant OU_ALIASES => $Cf->ldap_ou_aliases;
- import constant OC_ALIAS => $Cf->ldap_oc_alias;
+ import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts;
+ import constant OU_ALIASES => $Cf->ldap_ou_aliases;
+ import constant OC_ALIAS => $Cf->ldap_oc_alias;
import constant AT_FORWARDINGADDRESS => $Cf->ldap_at_forwardingaddress;
- import constant AT_GROUP => $Cf->ldap_at_group;
- import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress;
+ import constant AT_GROUP => $Cf->ldap_at_group;
+ import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress;
$abase = OU_ALIASES . "," . $Cf->ldap_base;
$ubase = OU_ACCOUNTS . "," . $Cf->ldap_base;
}
sub run($) {
+
# Eigentlich brauchen wir für alles ldap
$ldap = new Net::LDAP $Cf->ldap_server or die;
- my $r = $ldap->bind($Cf->ldap_bind_dn,
- password => $Cf->ldap_password || $ENV{LDAP_PASS} || password::ask("LDAP (". $Cf->ldap_bind_dn .") password: "));
+ my $r = $ldap->bind( $Cf->ldap_bind_dn,
+ password => $Cf->ldap_password
+ || $ENV{LDAP_PASS}
+ || password::ask( "LDAP (" . $Cf->ldap_bind_dn . ") password: " ) );
die $r->error, "\n" if $r->code;
-
- if ($Cf->list) { _list() }
- elsif ($Cf->add) { _add() }
- elsif ($Cf->delete) { _delete() }
- elsif ($Cf->modify) { _modify() }
- else { die "Need action (--add|--modify|--list|--delete)\n" };
+ if ( $Cf->list ) { _list() }
+ elsif ( $Cf->add ) { _add() }
+ elsif ( $Cf->delete ) { _delete() }
+ elsif ( $Cf->modify ) { _modify() }
+ else { die "Need action (--add|--modify|--list|--delete)\n" }
}
sub _add() {
-# Wenn's den Alias schon gibt, wird er nicht mehr
-# angelegt
+
+ # Wenn's den Alias schon gibt, wird er nicht mehr
+ # angelegt
die "Need alias name for creation\n" if not @ARGV;
die "Need members\n" if not defined $Cf->members;
- my $alias = shift @ARGV;
+ my $alias = shift @ARGV;
my @members = split /,/, $Cf->members;
- my $dn = "mail=$alias,$abase";
+ my $dn = "mail=$alias,$abase";
my $r;
verbose("$alias:\n");
verbose("\t$dn...");
- $r = $ldap->search(base => $abase, filter => "(mail=$alias)");
+ $r = $ldap->search( base => $abase, filter => "(mail=$alias)" );
die $r->error if $r->code;
die "Multiple entries not expected" if $r->count > 1;
-
- $r = $ldap->add($dn, attrs => [
- objectClass => OC_ALIAS,
- mail => $alias,
- (AT_FORWARDINGADDRESS) => \@members
- ]);
- if ($r->code == LDAP_ALREADY_EXISTS) { verbose "exists" }
- elsif ($r->code) { die $r->error }
- else { verbose "ok" }
+
+ $r = $ldap->add(
+ $dn,
+ attrs => [
+ objectClass => OC_ALIAS,
+ mail => $alias,
+ (AT_FORWARDINGADDRESS) => \@members
+ ]
+ );
+ if ( $r->code == LDAP_ALREADY_EXISTS ) { verbose "exists" }
+ elsif ( $r->code ) { die $r->error }
+ else { verbose "ok" }
verbose("\n");
}
sub _modify() {
-# Auch hier gehen wir davon aus, daß die dn direkt aus dem Alias-Namen folgt:
-# dn: cn=USER,...
-# Jetzt behandeln wir lediglich die Modifikation auf Basis eines
-# alias-Namens!
+
+ # Auch hier gehen wir davon aus, daß die dn direkt aus dem Alias-Namen folgt:
+ # dn: cn=USER,...
+ # Jetzt behandeln wir lediglich die Modifikation auf Basis eines
+ # alias-Namens!
my (@users) = @ARGV or die "Need alias names(s)\n";
my @members = split /,/, $Cf->members;
my @add = grep { s/^\+// } @_ = @members;
- my @del = grep { s/^-// } @_ = @members;
+ my @del = grep { s/^-// } @_ = @members;
my @set = grep { !/^[\+-]/ } @members;
-
foreach my $alias (@ARGV) {
- my $dn = "mail=$alias,$abase";
- verbose "$alias:";
+ my $dn = "mail=$alias,$abase";
+ verbose "$alias:";
- my $r = $ldap->search(base => $abase, filter => "(mail=$alias)");
- die $r->error if $r->code;
+ my $r = $ldap->search( base => $abase, filter => "(mail=$alias)" );
+ die $r->error if $r->code;
- if ($r->count == 0) {
- verbose " not found\n";
- next;
- }
+ if ( $r->count == 0 ) {
+ verbose " not found\n";
+ next;
+ }
- while (my $e = $r->shift_entry) {
+ while ( my $e = $r->shift_entry ) {
- verbose "\n\t" . $e->dn . " ";
+ verbose "\n\t" . $e->dn . " ";
- if (@set) {
- $e->replace((AT_FORWARDINGADDRESS) => \@set);
- } else {
- @add and $e->replace((AT_FORWARDINGADDRESS) => [uniq $e->get(AT_FORWARDINGADDRESS), @add]);
- @del and $e->delete((AT_FORWARDINGADDRESS) => \@del);
- }
+ if (@set) {
+ $e->replace( (AT_FORWARDINGADDRESS) => \@set );
+ } else {
+ @add
+ and $e->replace( (AT_FORWARDINGADDRESS) =>
+ [ uniq $e->get(AT_FORWARDINGADDRESS), @add ] );
+ @del and $e->delete( (AT_FORWARDINGADDRESS) => \@del );
+ }
- $e->dump if $Cf->debug;
+ $e->dump if $Cf->debug;
- my $r = $e->update($ldap);
- if ($r->code == LDAP_NO_SUCH_ATTRIBUTE) {
- verbose "no member";
- } elsif ($r->code) {
- die $r->error . "/" . $r->code;
- } else {
- verbose "ok";
- }
- }
+ my $r = $e->update($ldap);
+ if ( $r->code == LDAP_NO_SUCH_ATTRIBUTE ) {
+ verbose "no member";
+ } elsif ( $r->code ) {
+ die $r->error . "/" . $r->code;
+ } else {
+ verbose "ok";
+ }
+ }
- print "\n";
+ print "\n";
}
}
sub _delete() {
-# Wir gehen davon aus, daß es einen dn mail=ALIAS,ou=MailAliases,...
-# gibt und löschen diesen gnadenlos.
+
+ # Wir gehen davon aus, daß es einen dn mail=ALIAS,ou=MailAliases,...
+ # gibt und löschen diesen gnadenlos.
- if (!@ARGV) {
- print "User: ";
- chomp($_ = <>);
- @ARGV = ($_);
+ if ( !@ARGV ) {
+ print "User: ";
+ chomp( $_ = <> );
+ @ARGV = ($_);
}
foreach (@ARGV) {
- my $dn = "mail=$_,$abase";
+ my $dn = "mail=$_,$abase";
- verbose("$_:\n");
- verbose("\tdeleting $dn...");
- my $r = $ldap->delete($dn);
+ verbose("$_:\n");
+ verbose("\tdeleting $dn...");
+ my $r = $ldap->delete($dn);
- if ($r->code == LDAP_NO_SUCH_OBJECT) {
- verbose("doesn't exist");
- } elsif ($r->code == 0) {
- verbose("ok");
- } else {
- die $r->error;
- }
-
- verbose("\n");
+ if ( $r->code == LDAP_NO_SUCH_OBJECT ) {
+ verbose("doesn't exist");
+ } elsif ( $r->code == 0 ) {
+ verbose("ok");
+ } else {
+ die $r->error;
+ }
+
+ verbose("\n");
}
}
@@ -180,45 +190,45 @@
sub _list() {
my $filter;
@ARGV = ("*") unless @ARGV;
- $filter = "(|" . join("", map { "(mail=$_)" } @ARGV) . ")";
+ $filter = "(|" . join( "", map { "(mail=$_)" } @ARGV ) . ")";
my $r = $ldap->search(
- filter => $filter,
- base => $abase,
- attrs => [qw/mail/, AT_FORWARDINGADDRESS],
+ filter => $filter,
+ base => $abase,
+ attrs => [ qw/mail/, AT_FORWARDINGADDRESS ],
);
die $r->error if $r->code;
$Text::Wrap::columns = columns() || 80;
- while (my $e = $r->shift_entry) {
- my $mail = $e->get("mail");
+ while ( my $e = $r->shift_entry ) {
+ my $mail = $e->get("mail");
- print wrap("", "\t", $e->get_value("mail")
- . ": "
- . join(", ", $e->get(AT_FORWARDINGADDRESS))
- . "\n");
-
+ print wrap( "", "\t",
+ $e->get_value("mail") . ": "
+ . join( ", ", $e->get(AT_FORWARDINGADDRESS) )
+ . "\n" );
+
}
- $filter = "(|" . join("", map { "(".AT_GROUP."=$_)" } @ARGV) . ")";
+ $filter = "(|" . join( "", map { "(" . AT_GROUP . "=$_)" } @ARGV ) . ")";
$r = $ldap->search(
- filter => $filter,
- base => $ubase,
- attrs => [AT_GROUP, AT_PRIMARYADDRESS]
+ filter => $filter,
+ base => $ubase,
+ attrs => [ AT_GROUP, AT_PRIMARYADDRESS ]
);
die $r->error if $r->code;
my %group;
- while (my $e = $r->shift_entry) {
- my $mail = $e->get_value(AT_PRIMARYADDRESS);
- foreach my $g ($e->get_value(AT_GROUP)) {
- push @{$group{$g}}, $mail;
- }
+ while ( my $e = $r->shift_entry ) {
+ my $mail = $e->get_value(AT_PRIMARYADDRESS);
+ foreach my $g ( $e->get_value(AT_GROUP) ) {
+ push @{ $group{$g} }, $mail;
+ }
}
- foreach my $g (keys %group) {
- print wrap("", "\t", "$g⇒ " . join(", ", @{$group{$g}}) . "\n");
+ foreach my $g ( keys %group ) {
+ print wrap( "", "\t", "$g⇒ " . join( ", ", @{ $group{$g} } ) . "\n" );
}
}
@@ -238,4 +248,5 @@
}
1;
+
# vim:sts=4 sw=4 aw ai sm:
--- a/group.pm Tue Jul 31 10:46:37 2007 +0000
+++ b/group.pm Fri Feb 21 11:56:39 2014 +0100
@@ -1,4 +1,5 @@
package group;
+
# © Heiko Schlittermann
# $Id$
# $URL$
@@ -6,18 +7,18 @@
use strict;
use warnings;
use Net::LDAP;
-use Net::LDAP::Constant qw(LDAP_ALREADY_EXISTS LDAP_NO_SUCH_OBJECT LDAP_TYPE_OR_VALUE_EXISTS);
+use Net::LDAP::Constant
+ qw(LDAP_ALREADY_EXISTS LDAP_NO_SUCH_OBJECT LDAP_TYPE_OR_VALUE_EXISTS);
use Net::LDAP::Entry;
-use Cyrus::IMAP::Admin;
+
+#use Cyrus::IMAP::Admin;
use Text::Wrap;
use password;
-
my $Cf;
-my ($ldap, $ubase, $abase, $gbase);
+my ( $ldap, $ubase, $abase, $gbase );
my ($imap);
-END { $imap and $imap = undef; };
-
+END { $imap and $imap = undef; }
sub _add();
sub _list();
@@ -40,16 +41,16 @@
$Cf = shift;
require constant;
- import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts;
- import constant OU_ALIASES => $Cf->ldap_ou_aliases;
- import constant OU_GROUPS => $Cf->ldap_ou_groups;
- import constant OC_RECIPIENT => $Cf->ldap_oc_recipient;
- import constant OC_ACCESSGROUP => $Cf->ldap_oc_accessgroup;
- import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress;
- import constant AT_ADDRESS => $Cf->ldap_at_address;
- import constant AT_GROUP => $Cf->ldap_at_group;
+ import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts;
+ import constant OU_ALIASES => $Cf->ldap_ou_aliases;
+ import constant OU_GROUPS => $Cf->ldap_ou_groups;
+ import constant OC_RECIPIENT => $Cf->ldap_oc_recipient;
+ import constant OC_ACCESSGROUP => $Cf->ldap_oc_accessgroup;
+ import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress;
+ import constant AT_ADDRESS => $Cf->ldap_at_address;
+ import constant AT_GROUP => $Cf->ldap_at_group;
import constant AT_FORWARDINGADDRESS => $Cf->ldap_at_forwardingaddress;
- import constant AT_MEMBERUID => "memberUid";
+ import constant AT_MEMBERUID => "memberUid";
$gbase = OU_GROUPS . "," . $Cf->ldap_base;
$ubase = OU_ACCOUNTS . "," . $Cf->ldap_base;
@@ -57,41 +58,46 @@
}
sub run($) {
+
# Eigentlich brauchen wir für alles imap und ldap
$ldap = new Net::LDAP $Cf->ldap_server or die;
- my $r = $ldap->bind($Cf->ldap_bind_dn,
- password => $Cf->ldap_password || $ENV{LDAP_PASS} || password::ask("LDAP (". $Cf->ldap_bind_dn .") password: "));
+ my $r = $ldap->bind( $Cf->ldap_bind_dn,
+ password => $Cf->ldap_password
+ || $ENV{LDAP_PASS}
+ || password::ask( "LDAP (" . $Cf->ldap_bind_dn . ") password: " ) );
die $r->error, "\n" if $r->code;
$imap = new Cyrus::IMAP::Admin or die $@;
- $imap->authenticate(-server => $Cf->imap_server, -user => $Cf->imap_admin,
- -password => $Cf->imap_password || $ENV{IMAP_PASS} || password::ask("IMAP (". $Cf->imap_admin .") password: "))
- or die $@;
-
+ $imap->authenticate(
+ -server => $Cf->imap_server,
+ -user => $Cf->imap_admin,
+ -password => $Cf->imap_password
+ || $ENV{IMAP_PASS}
+ || password::ask( "IMAP (" . $Cf->imap_admin . ") password: " )
+ ) or die $@;
- if ($Cf->list) { _list() }
- elsif ($Cf->add) { _add() }
- elsif ($Cf->delete) { _delete() }
- elsif ($Cf->modify) { _modify() }
- else { die "Need action (--add|--modify|--list|--delete)\n" };
+ if ( $Cf->list ) { _list() }
+ elsif ( $Cf->add ) { _add() }
+ elsif ( $Cf->delete ) { _delete() }
+ elsif ( $Cf->modify ) { _modify() }
+ else { die "Need action (--add|--modify|--list|--delete)\n" }
}
sub _add() {
-# Beim Hinzufügen tragen wir nur das unbedingt notwendige
-# ein. Wenn es schon eine mailPrimaryAddress gibt oder eine
-# mail, machen wir gar nichts.
-# Ansonsten:
-# uid wird hinzugefügt
-# cn, sn bleiben unangetastet
-# Wenn die mailbox-Option gesetzt ist, wird die
-# IMAP-Mailbox angelegt.
+ # Beim Hinzufügen tragen wir nur das unbedingt notwendige
+ # ein. Wenn es schon eine mailPrimaryAddress gibt oder eine
+ # mail, machen wir gar nichts.
+ # Ansonsten:
+ # uid wird hinzugefügt
+ # cn, sn bleiben unangetastet
+ # Wenn die mailbox-Option gesetzt ist, wird die
+ # IMAP-Mailbox angelegt.
die "Need group name for creation\n" if not @ARGV;
my $group = shift @ARGV;
- my @members = split /,/, $Cf->members||"";
-
+ my @members = split /,/, $Cf->members || "";
my $dn = "cn=$group,$gbase";
my $r;
@@ -100,41 +106,50 @@
verbose("\t$dn...");
- $r = $ldap->search(base => $gbase, filter => "(cn=$group)");
+ $r = $ldap->search( base => $gbase, filter => "(cn=$group)" );
die $r->error if $r->code;
die "entries not expected" if $r->count > 1;
my $e;
- if ($r->count) {
- $e = $r->shift_entry;
+ if ( $r->count ) {
+ $e = $r->shift_entry;
} else {
- $e = new Net::LDAP::Entry;
- # Jetzt eine neue ID finden
- foreach ($Cf->gid_min .. $Cf->gid_max) {
- # ist einfach eine lineare Suche, im Augenblick weiß ich nichts
- # clevereres
- my $r = $ldap->search(base => $gbase,
- filter => "(gidNumber=$_)",
- attrs => []);
- if ($r->count == 0) {
- $e->add(gidNumber => $_);
- last;
- }
- }
- $e->dn($dn);
- $e->add(cn => $group);
+ $e = new Net::LDAP::Entry;
+
+ # Jetzt eine neue ID finden
+ foreach ( $Cf->gid_min .. $Cf->gid_max ) {
+
+ # ist einfach eine lineare Suche, im Augenblick weiß ich nichts
+ # clevereres
+ my $r = $ldap->search(
+ base => $gbase,
+ filter => "(gidNumber=$_)",
+ attrs => []
+ );
+ if ( $r->count == 0 ) {
+ $e->add( gidNumber => $_ );
+ last;
+ }
+ }
+ $e->dn($dn);
+ $e->add( cn => $group );
}
- grep /^CYRUS MAIL ACCESS GROUP/, $e->get("description") or $e->add(description => "CYRUS MAIL ACCESS GROUP");
+ grep /^CYRUS MAIL ACCESS GROUP/, $e->get("description")
+ or $e->add( description => "CYRUS MAIL ACCESS GROUP" );
- if (defined $Cf->description) {
- my @d = map { s/^(CYRUS MAIL ACCESS GROUP).*/"$1: ".$Cf->description/eg; $_ } $e->get("description");
- $e->replace(description => \@d);
+ if ( defined $Cf->description ) {
+ my @d =
+ map { s/^(CYRUS MAIL ACCESS GROUP).*/"$1: ".$Cf->description/eg; $_ }
+ $e->get("description");
+ $e->replace( description => \@d );
}
- $e->replace(objectClass => [uniq $e->get("objectClass"), OC_ACCESSGROUP, "posixGroup"]);
- $e->replace((AT_MEMBERUID) => [uniq $e->get(AT_MEMBERUID), @members]) if @members;
+ $e->replace( objectClass =>
+ [ uniq $e->get("objectClass"), OC_ACCESSGROUP, "posixGroup" ] );
+ $e->replace( (AT_MEMBERUID) => [ uniq $e->get(AT_MEMBERUID), @members ] )
+ if @members;
$r = $e->update($ldap);
die $r->error if $r->code;
@@ -144,122 +159,137 @@
}
sub _modify() {
-# Auch hier gehen wir davon aus, daß die dn direkt aus dem User-Namen folgt:
-# dn: uid=USER,...
+
+ # Auch hier gehen wir davon aus, daß die dn direkt aus dem User-Namen folgt:
+ # dn: uid=USER,...
my (@groups) = @ARGV or die "Need groupname(s)\n";
- my $r = $ldap->search(base => $gbase,
- filter => $_ = "(&(objectClass=".OC_ACCESSGROUP.")(|" . join("", map { "(cn=$_)" } @ARGV) . "))");
+ my $r = $ldap->search(
+ base => $gbase,
+ filter => $_ =
+ "(&(objectClass="
+ . OC_ACCESSGROUP . ")(|"
+ . join( "", map { "(cn=$_)" } @ARGV ) . "))"
+ );
die $r->error if $r->code;
die "No entries found.\n" if $r->count == 0;
- while (my $e = $r->shift_entry) {
- my $r;
+ while ( my $e = $r->shift_entry ) {
+ my $r;
- my $group = $e->get_value("cn");
- my $dn = $e->dn;
+ my $group = $e->get_value("cn");
+ my $dn = $e->dn;
- my $modified = 0;
- verbose "$group:";
+ my $modified = 0;
+ verbose "$group:";
- verbose "\n\t$dn...";
+ verbose "\n\t$dn...";
- if (defined $Cf->members) {
- my @m = split /,/, $Cf->members;
- grep { /^[+-]/ } @m or $e->delete(AT_MEMBERUID)
- if $e->get_value(AT_MEMBERUID);
+ if ( defined $Cf->members ) {
+ my @m = split /,/, $Cf->members;
+ grep { /^[+-]/ } @m
+ or $e->delete(AT_MEMBERUID)
+ if $e->get_value(AT_MEMBERUID);
- foreach my $m (@m) {
- if ($m =~ s/^-//) {
- $e->delete((AT_MEMBERUID) => [$m])
- } else {
- $m =~ s/^\+//;
- $e->add((AT_MEMBERUID) => [$m])
- }
- }
- $modified++;
- }
+ foreach my $m (@m) {
+ if ( $m =~ s/^-// ) {
+ $e->delete( (AT_MEMBERUID) => [$m] );
+ } else {
+ $m =~ s/^\+//;
+ $e->add( (AT_MEMBERUID) => [$m] );
+ }
+ }
+ $modified++;
+ }
- if (defined $Cf->description) {
- my @d = map { s/^(CYRUS MAIL ACCESS GROUP).*/"$1: ".$Cf->description/eg; $_ } $e->get("description");
- $e->replace(description => \@d);
- $modified++;
- }
-
- $e->dump if $Cf->debug;
+ if ( defined $Cf->description ) {
+ my @d = map {
+ s/^(CYRUS MAIL ACCESS GROUP).*/"$1: ".$Cf->description/eg;
+ $_
+ } $e->get("description");
+ $e->replace( description => \@d );
+ $modified++;
+ }
- if ($modified) {
- $r = $e->update($ldap);
- die $r->error.$r->code if $r->code;
- }
+ $e->dump if $Cf->debug;
- verbose "ok\n";
+ if ($modified) {
+ $r = $e->update($ldap);
+ die $r->error . $r->code if $r->code;
+ }
- print "\n";
+ verbose "ok\n";
+
+ print "\n";
}
}
sub _delete() {
-# Wir gehen davon aus, daß es einen dn uid=USER,ou=.... gibt, den wir löschen können.
-# Wir löschen den kompletten Container. Es kann natürlich sein, daß er noch jemand anders gehört.
-# Dann ist das Pech. Um es besser zu haben, müßten wir für alles unsere eigenen
-# Objektklassen haben...
- if (!@ARGV) {
- print "Group: ";
- chomp($_ = <>);
- @ARGV = ($_);
+ # Wir gehen davon aus, daß es einen dn uid=USER,ou=.... gibt, den wir löschen können.
+ # Wir löschen den kompletten Container. Es kann natürlich sein, daß er noch jemand anders gehört.
+ # Dann ist das Pech. Um es besser zu haben, müßten wir für alles unsere eigenen
+ # Objektklassen haben...
+
+ if ( !@ARGV ) {
+ print "Group: ";
+ chomp( $_ = <> );
+ @ARGV = ($_);
}
#my $filter = "(&((cn=%s)(objectClass=".OC_ACCESSGROUP.")))";
- my $r = $ldap->search(base => $gbase,
- filter => "(&(objectClass=".OC_ACCESSGROUP.")(|" . join("", map { "(cn=$_)" } @ARGV) . "))",
- attrs => [AT_MEMBERUID, "cn"]);
+ my $r = $ldap->search(
+ base => $gbase,
+ filter => "(&(objectClass="
+ . OC_ACCESSGROUP . ")(|"
+ . join( "", map { "(cn=$_)" } @ARGV ) . "))",
+ attrs => [ AT_MEMBERUID, "cn" ]
+ );
- if ($r->count == 0) {
- verbose "No objects found\n";
- return;
+ if ( $r->count == 0 ) {
+ verbose "No objects found\n";
+ return;
}
- while (my $e = $r->shift_entry) {
- my $dn = $e->dn;
- verbose $dn;
- my $r = $ldap->delete($dn);
+ while ( my $e = $r->shift_entry ) {
+ my $dn = $e->dn;
+ verbose $dn;
+ my $r = $ldap->delete($dn);
- if ($r->code == LDAP_NO_SUCH_OBJECT) {
- verbose("doesn't exist");
- } elsif ($r->code == 0) {
- verbose(" ok");
- } else {
- die $r->error;
- }
- verbose("\n");
+ if ( $r->code == LDAP_NO_SUCH_OBJECT ) {
+ verbose("doesn't exist");
+ } elsif ( $r->code == 0 ) {
+ verbose(" ok");
+ } else {
+ die $r->error;
+ }
+ verbose("\n");
}
}
sub _list() {
my $filter;
@ARGV = ("*") unless @ARGV;
+
#$filter = "(|" . join("", map { "(uid=$_)" } @ARGV) . ")";
- $filter = "(objectClass=".OC_ACCESSGROUP.")";
+ $filter = "(objectClass=" . OC_ACCESSGROUP . ")";
my $r = $ldap->search(
- filter => $filter,
- base => $gbase,
- attrs => [AT_MEMBERUID, qw/cn description/],
+ filter => $filter,
+ base => $gbase,
+ attrs => [ AT_MEMBERUID, qw/cn description/ ],
);
die $r->error if $r->code;
+ while ( my $e = $r->shift_entry ) {
+ my $cn = $e->get_value("cn");
+ my $descr = $e->get_value("description");
+ my @uids = $e->get_value(AT_MEMBERUID);
- while (my $e = $r->shift_entry) {
- my $cn = $e->get_value("cn");
- my $descr = $e->get_value("description");
- my @uids = $e->get_value(AT_MEMBERUID);
-
- print "$cn: ($descr)\n";
- print "\t", join "\n\t", @uids;
- print "\n";
+ print "$cn: ($descr)\n";
+ print "\t", join "\n\t", @uids;
+ print "\n";
}
}
@@ -273,19 +303,23 @@
return keys %x;
}
-{ my @pw;
-sub _mkpw($) {
- my $in = $_[0];
+{
+ my @pw;
- return $in unless $in and $in eq "{pwgen}";
+ sub _mkpw($) {
+ my $in = $_[0];
+
+ return $in unless $in and $in eq "{pwgen}";
- if (!@pw) {
- chomp(@pw = `pwgen 8 10 2>/dev/null|| mkpasswd`);
- die "pwgen/mkpasswd: $!" if $?;
+ if ( !@pw ) {
+ chomp( @pw = `pwgen 8 10 2>/dev/null|| mkpasswd` );
+ die "pwgen/mkpasswd: $!" if $?;
+ }
+ return shift @pw;
+
}
- return shift @pw;
-
-} }
+}
1;
+
# vim:sts=4 sw=4 aw ai sm nohlsearch:
--- a/imap.pm Tue Jul 31 10:46:37 2007 +0000
+++ b/imap.pm Fri Feb 21 11:56:39 2014 +0100
@@ -6,12 +6,13 @@
verbose(" imap:");
my $imap = connectImap();
- $imap->setacl($mbox, $Cf->imap_admin => "lrswipcda");
- if ($imap->list($mbox)) {
- verbose("(exists)");
+ $imap->setacl( $mbox, $Cf->imap_admin => "lrswipcda" );
+ if ( $imap->list($mbox) ) {
+ verbose("(exists)");
} else {
- $imap->create($mbox) or die ":$@: $mbox\n";
- $imap->setquota($mbox, STORAGE => 1024 * $Cf->imap_quota) or die ":$@: $mbox\n";
+ $imap->create($mbox) or die ":$@: $mbox\n";
+ $imap->setquota( $mbox, STORAGE => 1024 * $Cf->imap_quota )
+ or die ":$@: $mbox\n";
}
verbose("ok");
@@ -23,21 +24,23 @@
verbose(" imap:");
my $imap = connectImap();
- $imap->setacl($mbox, $Cf->imap_admin, "rc");
+ $imap->setacl( $mbox, $Cf->imap_admin, "rc" );
- if (not $imap->exists($mbox)) {
- verbose("does not exist");
+ if ( not $imap->exists($mbox) ) {
+ verbose("does not exist");
} else {
- $imap->delete($mbox) or die "$@";
+ $imap->delete($mbox) or die "$@";
}
verbose("ok");
}
-
sub connectImap() {
- my $imap = new Cyrus::IMAP::Admin($Cf->imap_server) or die "$@";
- $imap->authenticate(-user => $Cf->imap_admin,
- -password => $ENV{IMAP_PASS} || askPass("IMAP (" . $Cf->imap_admin .") password: "));
+ my $imap = new Cyrus::IMAP::Admin( $Cf->imap_server ) or die "$@";
+ $imap->authenticate(
+ -user => $Cf->imap_admin,
+ -password => $ENV{IMAP_PASS}
+ || askPass( "IMAP (" . $Cf->imap_admin . ") password: " )
+ );
return $imap;
}
--- a/ldapBase.pm Tue Jul 31 10:46:37 2007 +0000
+++ b/ldapBase.pm Fri Feb 21 11:56:39 2014 +0100
@@ -1,4 +1,5 @@
package ldapBase;
+
# © Heiko Schlittermann
# $Id$
# $URL$
@@ -6,16 +7,15 @@
use strict;
use warnings;
use Exporter();
-our @ISA = qw/Exporter/;
+our @ISA = qw/Exporter/;
our @EXPORT = qw/&ldapBase/;
-
-sub ldapBase(@) {
- no warnings 'once';
- local @ARGV = grep { -f } @_;
+sub ldapBase(@) {
+ no warnings 'once';
+ local @ARGV = grep { -f } @_;
die "Can't find ldap.conf (searched @_)\n" if !@ARGV;
- my $r = (reverse grep { /^\s*BASE\s+(.*?)\s*$/ and $_ = $1 } <>)[0];
+ my $r = ( reverse grep { /^\s*BASE\s+(.*?)\s*$/ and $_ = $1 } <> )[0];
return $r;
-};
+}
# vim:sts=4 sw=4 aw ai sm:
--- a/ma Tue Jul 31 10:46:37 2007 +0000
+++ b/ma Fri Feb 21 11:56:39 2014 +0100
@@ -5,10 +5,10 @@
# $Id$
#
use constant USAGE => <<'#';
-Usage: !ME! account|alias|group --add|--list|--modify|--delete [options] [user|alias|shared mbox]
+Usage: !ME! account|alias|group|acl --add|--list|--modify|--delete [options] [user|alias|shared mbox]
* common options *
--ldap_server=s LDAP-Server [!$Cf->ldap_server!]
- --ldap_base=s LDAP-Basis [!$Cf->ldap_base!]
+ --ldap_base=s LDAP-Base [!$Cf->ldap_base!]
--ldap_admin=s LDAP BIND DN [!$Cf->ldap_admin!]
--ldap_password=s [!$Cf->ldap_password!]
@@ -19,7 +19,8 @@
* account options *
--default_domain Default Domain [!$Cf->default_domain!]
--[no]mbox Create MBox [!$Cf->mbox!]
- --imap_quota=i Mail Quota (MB) [!$Cf->imap_quota!]
+ --imap_quota=i Mail Quota [!$Cf->imap_quota!]
+ (Bytes)
--address=s Primary Mail [!$Cf->address!]
--other:s Alternative Mail addresses
(comma sep.) [!$Cf->other!]
@@ -28,17 +29,28 @@
--forward:s Forwarding [!$Cf->forward!]
--fullname=s Real Name [!$Cf->fullname!]
- --password=s Passwort [!$Cf->password!]
+ --password=s Password [!$Cf->password!]
+
+ * acl options *
+ --acl_admin=s ACL Admin [!$Cf->acl_admin!]
+ --acl_password=s Pasword [!$Cf->acl_admin!]
+ --folder:s@ Folder(s) [!join ',', @{$Cf->folder}!]
+ --acl=s ACL list [!$Cf->acl!]
+ --[no]recursive Rekursive [!$Cf->recursive!]
* alias options *
--members=s List of Members [!$Cf->members!]
* shared mailbox options *
+ [ currently not supported ]
+
* group options *
--members=s List of Members [!$Cf->members!]
--description=s Descripton [!$Cf->description!]
+ [ currently not supported ]
+
Passwords for LDAP and IMAP can be read from environment LDAP_PASS resp. IMAP_PASS.
Options can be read from config file named in $MA_CONF [!$ENV{MA_CONF}!].
@@ -52,13 +64,12 @@
use warnings;
use IO::File;
-use Cyrus::IMAP::Admin;
use AppConfig qw(:expand);
use File::Basename;
use FindBin;
use Carp;
-use lib ("$FindBin::RealBin/..", "$FindBin::RealBin/../lib/ma");
+use lib ( "$FindBin::RealBin/..", "$FindBin::RealBin/../lib/ma" );
use Common;
use ldapBase;
@@ -68,57 +79,61 @@
sub help();
my $Module = shift if @ARGV && $ARGV[0] !~ /^-/;
- $Module ||= "UNKNOWN";
-
+$Module ||= "UNKNOWN";
-$SIG{__DIE__} = sub { die "\n".ME.": ", @_ };
-
+$SIG{__DIE__} = sub { die "\n" . ME . ": ", @_ };
MAIN: {
$Cf = new AppConfig Common::CONFIG or die;
- if (exists $ENV{MA_CONF} and -f $ENV{MA_CONF}) {
- my $f = $ENV{MA_CONF};
- die ": $f is group/world readable/writeable\n" if 077 & (stat _)[2];
- $Cf->file($f) or die;
+ if ( exists $ENV{MA_CONF} and -f $ENV{MA_CONF} ) {
+ my $f = $ENV{MA_CONF};
+ die ": $f is group/world readable/writeable\n" if 077 & ( stat _ )[2];
+ $Cf->file($f) or die;
}
- $Cf->getopt(\@ARGV) or die "Bad Usage. Try --help.\n";
+ $Cf->getopt( \@ARGV ) or die "Bad Usage. Try --help.\n";
die "Need ldap base.\n" if not $Cf->ldap_base;
- if ($Cf->ldap_admin !~ /\Q$Cf->ldap_base/) {
- $Cf->ldap_admin($Cf->ldap_admin . "," . $Cf->ldap_base);
+ if ( $Cf->ldap_admin !~ /\Q$Cf->ldap_base/ ) {
+ $Cf->ldap_admin( $Cf->ldap_admin . "," . $Cf->ldap_base );
}
- if ($Cf->help) {
- if (-t STDOUT and -x "/usr/bin/less") { open(X, "|less -FX") }
- else { open(X, ">&STDOUT"); }
- print X help();
- exit 0;
+ if ( $Cf->help ) {
+ if ( -t STDOUT and -x "/usr/bin/less" ) { open( X, "|less -FX" ) }
+ else { open( X, ">&STDOUT" ); }
+ print X help();
+ exit 0;
}
- @_ = grep { $_ =~ /^\Q$Module\E/ } qw/account alias shared group/;
+ @_ = grep { $_ =~ /^\Q$Module\E/ } qw/account acl alias shared group/;
die "Need module. Try --help\n" if @_ == 0;
die "Module ambigous. (@_)\n" if @_ > 1;
- if ($_[0] eq 'account') {
- require account;
- account::import($Cf);
- account::run();
- } elsif ($_[0] eq 'alias') {
- require alias;
- alias::import($Cf);
- alias::run();
- } elsif ($_[0] eq 'shared') {
- require shared;
- shared::import($Cf);
- shared::run();
- } elsif ($_[0] eq 'group') {
- require group;
- group::import($Cf);
- group::run();
+ if ( $_[0] eq 'account' ) {
+ require account;
+ account::import($Cf);
+ account::run();
+ } elsif ( $_[0] eq 'acl' ) {
+ require acl;
+ acl::import($Cf);
+ acl::run();
+ } elsif ( $_[0] eq 'alias' ) {
+ require alias;
+ alias::import($Cf);
+ alias::run();
+ } elsif ( $_[0] eq 'shared' ) {
+ die "Command '$_[0]' is currently not supported\n";
+ require shared;
+ shared::import($Cf);
+ shared::run();
+ } elsif ( $_[0] eq 'group' ) {
+ die "Command '$_[0]' is currently not supported\n";
+ require group;
+ group::import($Cf);
+ group::run();
} else {
- die "Shit";
+ die "Shit";
}
}
@@ -128,7 +143,7 @@
}
sub help() {
- ($_ = USAGE) =~ s/!(.*?)!/(eval $1) || ""/eg;
+ ( $_ = USAGE ) =~ s/!(.*?)!/(eval $1) || ""/eg;
return $_;
}
--- a/ma.8.pod Tue Jul 31 10:46:37 2007 +0000
+++ b/ma.8.pod Fri Feb 21 11:56:39 2014 +0100
@@ -1,3 +1,5 @@
+=encoding utf8
+
=head1 NAME
ma -- mailadmin tool
@@ -51,9 +53,9 @@
=head1 OPTIONS
-=over 4
+=head2 OPTIONS für beide Sub-Kommandos
-=head2 OPTIONS für beide Sub-Kommandos
+=over 4
=item B<--add>|B<--modify>|B<--delete>|B<--list>
@@ -142,6 +144,7 @@
Es kann eine sprechende Bezeichnung für die Gruppe angegeben werden. Dieser
Bezeichnung wird immer(!) "CYRUS MAIL ACCESS GROUP" vorangestellt.
+=back
=head1 EXAMPLES
@@ -190,6 +193,6 @@
=back
-=head1 AUTHOR
+=head1 AUTHORS
-Heiko Schlittermann <hs@schlittermann.de>
+Heiko Schlittermann <hs@schlittermann.de>, Matthias Förste <foerste@schlittermann.de>
--- a/ma.conf.ex Tue Jul 31 10:46:37 2007 +0000
+++ b/ma.conf.ex Fri Feb 21 11:56:39 2014 +0100
@@ -18,7 +18,10 @@
at_forwardingaddress = XXXMailForwardingAddress
at_primaryaddress = XXXMailPrimaryAddress
at_group = XXXMailGroup
+at_quota = XXXMailQuotaBytes
[imap]
admin = cyrus
password = SECRET
+server = localhost
+port = 143
--- a/password.pm Tue Jul 31 10:46:37 2007 +0000
+++ b/password.pm Fri Feb 21 11:56:39 2014 +0100
@@ -5,9 +5,9 @@
return undef if not -t;
print $_[0];
- system(stty => "-echo");
+ system( stty => "-echo" );
my $ans = <STDIN>;
- system(stty => "echo");
+ system( stty => "echo" );
print "\n";
chomp $ans;
--- a/shared.pm Tue Jul 31 10:46:37 2007 +0000
+++ b/shared.pm Fri Feb 21 11:56:39 2014 +0100
@@ -1,4 +1,5 @@
package shared;
+
# © Heiko Schlittermann
# $Id$
# $URL$
@@ -6,18 +7,18 @@
use strict;
use warnings;
use Net::LDAP;
-use Net::LDAP::Constant qw(LDAP_ALREADY_EXISTS LDAP_NO_SUCH_OBJECT LDAP_TYPE_OR_VALUE_EXISTS);
+use Net::LDAP::Constant
+ qw(LDAP_ALREADY_EXISTS LDAP_NO_SUCH_OBJECT LDAP_TYPE_OR_VALUE_EXISTS);
use Net::LDAP::Entry;
-use Cyrus::IMAP::Admin;
+
+#use Cyrus::IMAP::Admin;
use Text::Wrap;
use password;
-
my $Cf;
-my ($ldap, $ubase, $abase);
+my ( $ldap, $ubase, $abase );
my ($imap);
-END { $imap and $imap = undef; };
-
+END { $imap and $imap = undef; }
sub _add();
sub _list();
@@ -37,12 +38,12 @@
$Cf = shift;
require constant;
- import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts;
- import constant OU_ALIASES => $Cf->ldap_ou_aliases;
- import constant OC_RECIPIENT => $Cf->ldap_oc_recipient;
- import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress;
- import constant AT_ADDRESS => $Cf->ldap_at_address;
- import constant AT_GROUP => $Cf->ldap_at_group;
+ import constant OU_ACCOUNTS => $Cf->ldap_ou_accounts;
+ import constant OU_ALIASES => $Cf->ldap_ou_aliases;
+ import constant OC_RECIPIENT => $Cf->ldap_oc_recipient;
+ import constant AT_PRIMARYADDRESS => $Cf->ldap_at_primaryaddress;
+ import constant AT_ADDRESS => $Cf->ldap_at_address;
+ import constant AT_GROUP => $Cf->ldap_at_group;
import constant AT_FORWARDINGADDRESS => $Cf->ldap_at_forwardingaddress;
$ubase = OU_ACCOUNTS . "," . $Cf->ldap_base;
@@ -50,188 +51,205 @@
}
sub run($) {
+
# Eigentlich brauchen wir für alles imap und ldap
$ldap = new Net::LDAP $Cf->ldap_server or die;
- my $r = $ldap->bind($Cf->ldap_bind_dn,
- password => $Cf->ldap_password || $ENV{LDAP_PASS} || password::ask("LDAP (". $Cf->ldap_bind_dn .") password: "));
+ my $r = $ldap->bind( $Cf->ldap_bind_dn,
+ password => $Cf->ldap_password
+ || $ENV{LDAP_PASS}
+ || password::ask( "LDAP (" . $Cf->ldap_bind_dn . ") password: " ) );
die $r->error, "\n" if $r->code;
$imap = new Cyrus::IMAP::Admin or die $@;
- $imap->authenticate(-server => $Cf->imap_server, -user => $Cf->imap_admin,
- -password => $Cf->imap_password || $ENV{IMAP_PASS} || password::ask("IMAP (". $Cf->imap_admin .") password: "))
- or die $@;
-
+ $imap->authenticate(
+ -server => $Cf->imap_server,
+ -user => $Cf->imap_admin,
+ -password => $Cf->imap_password
+ || $ENV{IMAP_PASS}
+ || password::ask( "IMAP (" . $Cf->imap_admin . ") password: " )
+ ) or die $@;
- if ($Cf->list) { _list() }
- elsif ($Cf->add) { _add() }
- elsif ($Cf->delete) { _delete() }
- elsif ($Cf->modify) { _modify() }
- else { die "Need action (--add|--modify|--list|--delete)\n" };
+ if ( $Cf->list ) { _list() }
+ elsif ( $Cf->add ) { _add() }
+ elsif ( $Cf->delete ) { _delete() }
+ elsif ( $Cf->modify ) { _modify() }
+ else { die "Need action (--add|--modify|--list|--delete)\n" }
}
sub _add() {
-# Beim Hinzufügen tragen wir nur das unbedingt notwendige
-# ein.
-# Die IMAP-Mailbox wird angelegt.
+ # Beim Hinzufügen tragen wir nur das unbedingt notwendige
+ # ein.
+ # Die IMAP-Mailbox wird angelegt.
die "Need mailbox name for creation\n" if not @ARGV;
my $mbox = shift @ARGV;
verbose("shared mbox:\n");
- if($Cf->mbox) {
- verbose("\n\t$mbox...");
+ if ( $Cf->mbox ) {
+ verbose("\n\t$mbox...");
- if ($imap->list($mbox)) { verbose("exists") }
- else {
- $imap->create($mbox) and verbose("ok") or die $@;
- $imap->setacl($mbox, $Cf->imap_admin => "lrswipcda") or die $@;
- $imap->setquota($mbox, STORAGE => 1024 * $Cf->imap_quota) or die $@;
- }
+ if ( $imap->list($mbox) ) { verbose("exists") }
+ else {
+ $imap->create($mbox) and verbose("ok") or die $@;
+ $imap->setacl( $mbox, $Cf->imap_admin => "lrswipcda" ) or die $@;
+ $imap->setquota( $mbox, STORAGE => 1024 * $Cf->imap_quota )
+ or die $@;
+ }
}
-
verbose("\n");
}
sub _modify() {
-# Auch hier gehen wir davon aus, daß die dn direkt aus dem User-Namen folgt:
-# dn: uid=USER,...
+
+ # Auch hier gehen wir davon aus, daß die dn direkt aus dem User-Namen folgt:
+ # dn: uid=USER,...
my (@users) = @ARGV or die "Need username(s)\n";
my @dns;
- my $r = $ldap->search(base => $ubase,
- filter => "(|" . join("", map { "(uid=$_)" } @ARGV) . ")");
+ my $r = $ldap->search(
+ base => $ubase,
+ filter => "(|" . join( "", map { "(uid=$_)" } @ARGV ) . ")"
+ );
die $r->error if $r->code;
die "No entries found.\n" if $r->count == 0;
- while (my $e = $r->shift_entry) {
- my $r;
+ while ( my $e = $r->shift_entry ) {
+ my $r;
- my $user = $e->get_value("uid");
- my $dn = $e->dn;
- my $mbox = "user/$user";
+ my $user = $e->get_value("uid");
+ my $dn = $e->dn;
+ my $mbox = "user/$user";
- my $modified = 0;
- verbose "$user:";
+ my $modified = 0;
+ verbose "$user:";
- verbose "\n\t$dn...";
+ verbose "\n\t$dn...";
- # Fix: iusMailOptions wurde erst später eingeführt, bei Bedarf also hinzufügen
- #if (!grep /^iusMailOptions$/, @{$e->get("objectClass")}) {
- #$e->add(objectClass => "iusMailOptions");
- #}
+ # Fix: iusMailOptions wurde erst später eingeführt, bei Bedarf also hinzufügen
+ #if (!grep /^iusMailOptions$/, @{$e->get("objectClass")}) {
+ #$e->add(objectClass => "iusMailOptions");
+ #}
- if (my $cn = $Cf->fullname) {
- # Aus dem Fullnamen leiten wir cn und sn ab.
- my $sn = (reverse split " ", $cn)[0];
+ if ( my $cn = $Cf->fullname ) {
+
+ # Aus dem Fullnamen leiten wir cn und sn ab.
+ my $sn = ( reverse split " ", $cn )[0];
- if ($cn =~ s/^\+//) {
- $e->replace(
- cn => [uniq $e->get("cn"), $cn],
- sn => [uniq $e->get("sn"), $sn]);
- } elsif ($cn =~ s/^-//) {
- $e->delete(cn => [$cn], sn => [$sn]);
- } else { $e->replace(cn => $cn, sn => $sn); }
- $modified++;
- }
+ if ( $cn =~ s/^\+// ) {
+ $e->replace(
+ cn => [ uniq $e->get("cn"), $cn ],
+ sn => [ uniq $e->get("sn"), $sn ]
+ );
+ } elsif ( $cn =~ s/^-// ) {
+ $e->delete( cn => [$cn], sn => [$sn] );
+ } else {
+ $e->replace( cn => $cn, sn => $sn );
+ }
+ $modified++;
+ }
- if (defined $Cf->other) {
- my @o = split /,/, $Cf->other;
- grep { /^[+-]/ } @o or $e->delete(AT_ADDRESS);
+ if ( defined $Cf->other ) {
+ my @o = split /,/, $Cf->other;
+ grep { /^[+-]/ } @o or $e->delete(AT_ADDRESS);
- foreach my $a (split /,/, $Cf->other) {
- if ($a =~ s/^-//) {
- $e->delete((AT_ADDRESS) => [$a])
- } else {
- $a =~ s/^\+//;
+ foreach my $a ( split /,/, $Cf->other ) {
+ if ( $a =~ s/^-// ) {
+ $e->delete( (AT_ADDRESS) => [$a] );
+ } else {
+ $a =~ s/^\+//;
- # Darf noch nicht woanders sein
- $r = $ldap->search(base => $ubase, filter => "(mail=$a)");
- die $r->error if $r->code;
- die "$a ist schon vergeben\n" if $r->count;
+ # Darf noch nicht woanders sein
+ $r = $ldap->search( base => $ubase, filter => "(mail=$a)" );
+ die $r->error if $r->code;
+ die "$a ist schon vergeben\n" if $r->count;
- $e->add((AT_ADDRESS) => [$a])
- }
- }
- $modified++;
- }
+ $e->add( (AT_ADDRESS) => [$a] );
+ }
+ }
+ $modified++;
+ }
- if (defined $Cf->group) {
- my @g = split /,/, $Cf->group;
- grep { /^[+-]/ } @g or $e->delete(AT_GROUP)
- if $e->get_value(AT_GROUP);
+ if ( defined $Cf->group ) {
+ my @g = split /,/, $Cf->group;
+ grep { /^[+-]/ } @g
+ or $e->delete(AT_GROUP)
+ if $e->get_value(AT_GROUP);
- foreach my $g (@g) {
- if ($g =~ s/^-//) {
- $e->delete((AT_GROUP) => [$g])
- } else {
- $g =~ s/^\+//;
- $e->add((AT_GROUP) => [$g])
- }
- }
- $modified++;
- }
+ foreach my $g (@g) {
+ if ( $g =~ s/^-// ) {
+ $e->delete( (AT_GROUP) => [$g] );
+ } else {
+ $g =~ s/^\+//;
+ $e->add( (AT_GROUP) => [$g] );
+ }
+ }
+ $modified++;
+ }
- if (my $a = $Cf->primary) {
- $r = $ldap->search(base => $ubase,
- # filter => "(|(mailPrimaryAddress=$a)(mail=$a))");
- filter => "(mail=$a)");
- die $r->error if $r->code;
- die "$a ist schon vergeben\n" if $r->count;
-
- $e->replace((AT_PRIMARYADDRESS) => $Cf->primary);
- $modified++;
- }
+ if ( my $a = $Cf->primary ) {
+ $r = $ldap->search(
+ base => $ubase,
+
+ # filter => "(|(mailPrimaryAddress=$a)(mail=$a))");
+ filter => "(mail=$a)"
+ );
+ die $r->error if $r->code;
+ die "$a ist schon vergeben\n" if $r->count;
- if (my $pw = _mkpw($Cf->password)) {
- $e->replace(userPassword => $pw);
- $modified++;
- }
+ $e->replace( (AT_PRIMARYADDRESS) => $Cf->primary );
+ $modified++;
+ }
- #if ($Cf->internal ne ":") {
- #$e->replace(iusRestrictedMail => $Cf->internal ? "TRUE" : "FALSE");
- #$modified++;
- #}
+ if ( my $pw = _mkpw( $Cf->password ) ) {
+ $e->replace( userPassword => $pw );
+ $modified++;
+ }
- $e->dump if $Cf->debug;
+ #if ($Cf->internal ne ":") {
+ #$e->replace(iusRestrictedMail => $Cf->internal ? "TRUE" : "FALSE");
+ #$modified++;
+ #}
- if ($modified) {
- $r = $e->update($ldap);
- die $r->error.$r->code if $r->code;
- }
+ $e->dump if $Cf->debug;
+
+ if ($modified) {
+ $r = $e->update($ldap);
+ die $r->error . $r->code if $r->code;
+ }
- # FIXME: Wenn keine Mailbox existiert, gibt es hier ein Problem
- if (defined $Cf->imap_quota) {
- $imap->setquota($mbox, STORAGE => $Cf->imap_quota * 1024)
- or die $@;
- }
+ # FIXME: Wenn keine Mailbox existiert, gibt es hier ein Problem
+ if ( defined $Cf->imap_quota ) {
+ $imap->setquota( $mbox, STORAGE => $Cf->imap_quota * 1024 )
+ or die $@;
+ }
- verbose "ok\n";
+ verbose "ok\n";
- print "\n";
+ print "\n";
}
}
sub _delete() {
- if (!@ARGV) {
- print "Mailbox: ";
- chomp($_ = <>);
- @ARGV = ($_);
+ if ( !@ARGV ) {
+ print "Mailbox: ";
+ chomp( $_ = <> );
+ @ARGV = ($_);
}
foreach my $mbox (@ARGV) {
- if ($Cf->mbox) {
- verbose("\tdeleting mbox $mbox...");
- $imap->delete($mbox) and verbose("ok")
- or verbose($imap->error);
- }
+ if ( $Cf->mbox ) {
+ verbose("\tdeleting mbox $mbox...");
+ $imap->delete($mbox) and verbose("ok")
+ or verbose( $imap->error );
+ }
- verbose("\n");
+ verbose("\n");
}
}
@@ -240,32 +258,33 @@
@ARGV = ("*") unless @ARGV;
foreach (@ARGV) {
- my @mboxes = $imap->list($_);
+ my @mboxes = $imap->list($_);
- foreach (@mboxes) {
- my ($mbox, $attr, $sep) = @$_;
- next if $mbox =~ /^user$sep/;
+ foreach (@mboxes) {
+ my ( $mbox, $attr, $sep ) = @$_;
+ next if $mbox =~ /^user$sep/;
- print "$mbox: shared mailbox";
+ print "$mbox: shared mailbox";
- # Quota
- my %q = $imap->listquota($mbox);
- my ($used, $max) = map { int($_ / 1024) } @{$q{STORAGE}};
+ # Quota
+ my %q = $imap->listquota($mbox);
+ my ( $used, $max ) = map { int( $_ / 1024 ) } @{ $q{STORAGE} };
- if (!$max) {
- print ", no quota";
- } else {
- print ", quota ($used/$max): " . int(100 * $used/$max) . "%";
- }
- print "\n";
+ if ( !$max ) {
+ print ", no quota";
+ } else {
+ print ", quota ($used/$max): "
+ . int( 100 * $used / $max ) . "%";
+ }
+ print "\n";
- # ACL
- my %acl = $imap->listacl($mbox);
- foreach (sort keys %acl) {
- print "\t$_: $acl{$_}\n";
- }
- }
-
+ # ACL
+ my %acl = $imap->listacl($mbox);
+ foreach ( sort keys %acl ) {
+ print "\t$_: $acl{$_}\n";
+ }
+ }
+
}
}
@@ -279,19 +298,23 @@
return keys %x;
}
-{ my @pw;
-sub _mkpw($) {
- my $in = $_[0];
+{
+ my @pw;
- return $in unless $in and $in eq "{pwgen}";
+ sub _mkpw($) {
+ my $in = $_[0];
+
+ return $in unless $in and $in eq "{pwgen}";
- if (!@pw) {
- chomp(@pw = `pwgen 8 10 2>/dev/null|| mkpasswd`);
- die "pwgen/mkpasswd: $!" if $?;
+ if ( !@pw ) {
+ chomp( @pw = `pwgen 8 10 2>/dev/null|| mkpasswd` );
+ die "pwgen/mkpasswd: $!" if $?;
+ }
+ return shift @pw;
+
}
- return shift @pw;
-
-} }
+}
1;
+
# vim:sts=4 sw=4 aw ai sm nohlsearch: