diff -r 000000000000 -r 2a5f2464f8c6 alias.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/alias.pm Fri Nov 04 06:29:26 2005 +0000 @@ -0,0 +1,215 @@ +package alias; +# © Heiko Schlittermann +# $Id$ +# $URL$ + +use strict; +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); +use Net::LDAP::Entry; +use Text::Wrap; + +use password; + +my $Cf; +my ($ldap, $abase); + +sub _add(); +sub _list(); +sub _delete(); +sub uniq(@); +sub verbose(@); +sub columns(); + +sub OU_ALIASES(); +sub OC_ALIAS(); +sub AT_FORWARDINGADDRESS(); + +sub import(@) { + $Cf = shift; + + require constant; + import constant OU_ALIASES => $Cf->ldap_ou_aliases; + import constant OC_ALIAS => $Cf->ldap_oc_alias; + import constant AT_FORWARDINGADDRESS => $Cf->ldap_at_forwardingaddress; + + $abase = OU_ALIASES . "," . $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: ")); + 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" }; + +} + +sub _add() { +# 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 @members = split /,/, $Cf->members; + my $dn = "mail=$alias,$abase"; + + my $r; + + verbose("$alias:\n"); + verbose("\t$dn..."); + + $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" } + + 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! + + 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 @set = grep { !/^[\+-]/ } @members; + + + foreach my $alias (@ARGV) { + my $dn = "mail=$alias,$abase"; + verbose "$alias:"; + + my $r = $ldap->search(base => $abase, filter => "(mail=$alias)"); + die $r->error if $r->code; + + if ($r->count == 0) { + verbose " not found\n"; + next; + } + + while (my $e = $r->shift_entry) { + + 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); + } + + $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"; + } + } + + print "\n"; + } +} + +sub _delete() { +# Wir gehen davon aus, daß es einen dn mail=ALIAS,ou=MailAliases,... +# gibt und löschen diesen gnadenlos. + + if (!@ARGV) { + print "User: "; + chomp($_ = <>); + @ARGV = ($_); + } + + foreach (@ARGV) { + my $dn = "mail=$_,$abase"; + + 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"); + + } +} + +sub _list() { + my $filter; + @ARGV = ("*") unless @ARGV; + $filter = "(|" . join("", map { "(mail=$_)" } @ARGV) . ")"; + + my $r = $ldap->search( + 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"); + + print wrap("", "\t", $e->get_value("mail") + . ": " + . join(", ", $e->get(AT_FORWARDINGADDRESS)) + . "\n"); + + } +} + +sub verbose(@) { + printf STDERR @_; +} + +sub uniq(@) { + my %x; + @x{@_} = (); + return keys %x; +} + +sub columns() { + `stty -a` =~ /columns\s+(\d+)/; + $1; +} + +1; +# vim:sts=4 sw=4 aw ai sm: