#	$RCSfile: maintainer.pl,v $
#	$Revision: 1.6 $
#	$Author: marten $
#	$Date: 1994/12/29 15:04:08 $

# maintainer.pl - implements all functions associated with the maintainer
# attribute, as defined in ripe-??

# There is a few global variables it will use for authorisation:
# 
# $FROM - contains From: field from mail header
# $PASSWD - contains password found in message

# Also, the last maintainer object is kept globally for simplicity.
# Otherwise we keep looking the stupid thing up again and again and again.....

# maintainer - this is the main routine. It will receive two objects,
# the new and old object, and starts processing from there.
#
# Here's the scoop:
# 
# OldObject is empty: addition
# NewObject is empty: delete
# None empty:         normal update

require "notify.pl";
require "entype.pl";

sub Maintainer {

    local(*OldObject, *NewObject, $delete) = @_;
    local($status) = 0;
    local($returnstatus) = 0;

    # No maintainers mentioned, just return OK.

    return 1 if !$NewObject{"mb"} && !$OldObject{"mb"};

    # Case 1: OldObject is empty, ie this is a new object.
    # - just return if NewObject contains no maintainers
    # - use maintainers in NewObject to verify if it has maintainers

    if (!&entype(*OldObject) || !$OldObject{"mb"}) {
	return if !$NewObject{"mb"};

	$MatchingMaintainer = "";
	foreach (split(/\s+/, $NewObject{"mb"})) {
	    $status = &VrfyMaint($_, *NewObject);
	    $MatchingMaintainer = $_ if $status;
	    last if $status;
	}

	if (!$status) {

	    # All maintainers will receive a copy of the object with
	    # a message (specified in config) that someone tried to
	    # update one of their objects, but failed authorisation.

	    &ForwardToMaint(*OldObject, *NewObject);

	    return 0;
	}

	# Notification that an object has been added/changed/deleted.
	# For all maintainers, send them a notification if they have
	# a mnt-nfy attribute specified in their object. Otherwise
	# no notification is done to the maintainers. Routine itself
	# is clever enough to figure out is this is a delete/add/update.

	&NotifyMaintainers(*OldObject, *NewObject);

	return 1;
    }

    # Case 2: Normal case, NewObject replaces OldObject or is a delete

    if ($OldObject{"mb"}) {

	$MatchingMaintainer = "";
	foreach (split(/\s+/, $OldObject{"mb"})) {
	    $status = &VrfyMaint($_, *OldObject);
	    $MatchingMaintainer = $_ if $status;
	    last if $status;

	}

	if (!$status) {

	    # Auth failed. Bounce to originator and forward to maintainer
	    # Same as above.

	    &ForwardToMaint(*OldObject, *NewObject);

	    return 0;
	}

	# Notify maintainers if requested (as above) only if not uknown
	# maintainer is added.

	&NotifyMaintainers(*OldObject, *NewObject);

	return 1;
    }
}

sub GetMaintainer {
    local($maintainername, $source) = @_;
    local(*i) = 'curdb';
    local(%nothing) = ();
    local($normalized) = $maintainername;
    local($dbfile);
    $normalized =~ tr/A-Z/a-z/;

    local(@keys) = ($normalized);

    return 1 if ($CurMaintainer{"mt"} eq $maintainername);

    if ($TYPE{$source} eq "SPLIT") {
	$dbfile = "$DBFILE{$source}.mt";
    } else {
	$dbfile = $DBFILE{$source};
    }
    if (&dbopen(i, *nothing, 0, $dbfile)) {
	local(@matches) = &dbmatch(*i, *keys, 1);
	if ($#matches > 0) {
	    &syslog("ERRLOG","GetMaintainer - found more than one match for ".
		    "maintainer: $maintainername\n");
	    # Set error, should not happen!!!!!
	    &dbclose(*i);
	    return 0;
	}
	if (!$matches[0]) {
	    &syslog(
		    "ERRLOG",
		    "GetMaintainer() no match found for maintainer $maintainer"
		    );
	    &dbclose(*i);
	    return 0;
	}
	%CurMaintainer = &enread(i, $matches[0]);
	$ExistMaintainer{$CurMaintainer{"mt"}} = 1;
	&dbclose(*i);
	return 1;
    } else {
	&syslog("ERRLOG",
		"GetMaintainer - failed to open $DBFILE{$source}.mt\n"
		);
	return 0;
    }
}

sub VrfyMaint {
    local($maintainer, *object) = @_;
    local($status);
    local($source) = $object{"so"};

    if (&GetMaintainer($maintainer, $source)) {
	foreach (split(/\n/, $CurMaintainer{"at"})) {
	    $status = 0;
	    if ($_ eq "NONE") {
		return 1;
	    }
	    if ($_ =~ /^MAIL-FROM/) {
		$status = &CheckMailFrom($_);
		return 1 if $status;
		next;
	    }
	    if ($_ =~ /^CRYPT-PW/) {
		$status = &CheckPassword($_);
		return 1 if $status;
		next;
	    }
	}
	return $status;
    } else {
	return 0;
    }
}
    

sub CheckMailFrom {
    local($line) = @_;

    $line =~ m/MAIL-FROM\s+(.*)/;
    local($regex) = $1;

    return 1 if $FROM =~ /$regex/;
    &syslog("AUDITLOG", "Auth email failure \"$FROM\" !~ \"$regex\"");
    return 0;

}

sub CheckPassword {
    local($line) = @_;

    $line = m/CRYPT-PW\s+(\S+)/;
    local($cpwd) = $1;

    return 1 if crypt($PASSWORD, $cpwd) eq $cpwd;
    &syslog("AUDITLOG", "Auth passwd failure \"$PASSWORD\" !~ \"$cpwd\"");
    return 0;
}

# This routine will handle the mnt-nfy function in the maintainer object
# It will extract the email addresses from mnt-nfy attribute, and from
# there on, treat them as normal notifiers.

sub NotifyMaintainers {
    local(*Oldobject, *NewObject) = @_;
    local(@notif) = ();
    local($line);
    local($del) = 1 if !&entype(*NewObject);
    local($m) = 0;
    local($source);

    if ($OldObject{"mb"}) {
	foreach $line (split(/\n/, $OldObject{"mb"})) {
	    @notif = (@notif, split(/\s+/, $line));
	    $source = $OldObject{"so"};
	}
    } else {
	foreach $line (split(/\n/, $NewObject{"mb"})) {
	    @notif = (@notif, split(/\s+/, $line));
	    $source = $NewObject{"so"};
	}
    }
    foreach $m (0..$#notif) {
	&GetMaintainer($notif[$m], $source);
	if ($CurMaintainer{"mn"}) {
	    local(@mns);
	    foreach (split(/\n/, $CurMaintainer{"mn"})) {
		@mns = (@mns, $_);
	    }
	    splice(@notif, $m, 1, @mns);
	} else {
	    splice(@notif, $m, 1);
	    $m--;
	}
    }

    &DoAddNotify(*notif, *OldObject, *NewObject, $del);
}
    
# This routine will forward objects to a all maintainers if authorisation
# failed. The message configured in config should make clear to the 
# maintainer that the update actually failed, and they should take action
# as the proper maintainers.
# Notification of succeeded updates for maintainers is done through
# the normal notify mechanism only if maintainer object contains a
# mnt-nfy attribute.

sub ForwardToMaint {
    local(*OldObject, *NewObject) = @_;
    local($line);
    local(@maintainers) = ();
    local($oldtype) = &entype(*OldObject);
    local($newtype) = &entype(*NewObject);
    local($source);
    local($email);

    if ($OldObject{"mb"}) {
	foreach $line (split(/\n/, $OldObject{"mb"})) {
	    @maintainers = (@maintainers, split(/\s+/, $line));
	    $source = $OldObject{"so"};
	}
    } else {
	foreach $line (split(/\n/, $NewObject{"mb"})) {
	    @maintainers = (@maintainers, split(/\s+/, $line));
	    $source = $NewObject{"so"};
	}
    }

    foreach (@maintainers) {
	# The idea was to not send a notificatio to the matching
	# maintainer. Let's forget about that one for now.

	# next if $_ eq $MatchingMaintainer;

	if (!&GetMaintainer($_, $source)) {
	    &syslog("ERRLOG",
		    "Failed to get maintainer $_ in fwd msg generation");
	    return;
	}

	foreach $email (split(/\n/, $CurMaintainer{"dt"})) {

	    if (!$forward{$email}) {
		$forward{$email} = &ForwardTmpFile($email);
		open(FORWARD, ">$forward{$email}") ||
		    &syslog("ERRLOG",
			    "Cannot create maintainer fwd file $forward{$email}");
		&WriteForwardHeader(FORWARD, $email);
	    } else {
		open(FORWARD, ">>$forward{$email}") ||
		    &syslog("ERRLOG",
			    "Cannot open maintainer fwd file $forward{$email}");
	    }
	    select(FORWARD);
	    if (!$newtype) {	# Deletion
		print "---\n";
		print "DELETION REQUESTED FOR:\n\n";
		print "\n" if &enwrite(*OldObject,1,0,1);
	    } elsif (!$oldtype) {	# Addition
		print "---\n";
		print "ADDITION REQUESTED FOR:\n\n";
		print "\n" if &enwrite(*NewObject,1,0,1);
	    } else {		# Update
		print "---\n";
		print "UPDATE REQUESTED FOR:\n\n";
		print "\n" if &enwrite(*NewObject,1,0,1);
	    }
	    close(FORWARD);
	}
    }
    select(STDOUT);
}

sub ForwardTmpFile {
    local($email) = @_;
    return "$TMPDIR/$email.$$";
}

sub WriteForwardHeader {
    local($filehandle, $email) = @_;

    select($filehandle);

    print $FWHEADER;
    $email = $DEFMAIL if $TESTMODE;
    print "To: $email\n";
    print "\n";

    eval "print \"$FWTEXT\n\";";
    select(STDOUT);
}

sub SendForwardMails {
    local($a);

    foreach $a (keys %forward) {
	system("$MAILCMD < $forward{$a}");
	unlink($forward{$a});
    }
}
1;
