#! /usr/local/bin/perl
# -*- indent-tabs-mode: nil; -*-
# vim:ft=perl:et:sw=4
# $Id$

# Sympa - SYsteme de Multi-Postage Automatique
#
# Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel
# Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
# 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites
# Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 GIP RENATER
# Copyright 2017, 2021 The Sympa Community. See the
# AUTHORS.md file at the top-level directory of this distribution and at
# <https://github.com/sympa-community/sympa.git>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

use lib split(/:/, $ENV{SYMPALIB} || ''), '/usr/local/libexec/sympa';
use strict;
use warnings;
use English qw(-no_match_vars);
use Getopt::Long;
use Pod::Usage;

use Conf;
use Sympa::Constants;
use Sympa::Crash;    # Show traceback.
use Sympa::Log;

# If this program was invoked by the other, throw standard output away so
# that parent's output (e.g. via CGI) won't be stained.
unless (-t) {
    open STDOUT, '>&STDERR';
}

my %options;
unless (GetOptions(\%options, 'domain=s', 'help|h', 'version|v')) {
    pod2usage(-exitval => 1, -output => \*STDERR);
}
if ($options{'help'}) {
    pod2usage(0);
} elsif ($options{'version'}) {
    printf "Sympa %s\n", Sympa::Constants::VERSION;
    exit 0;
}

my $log = Sympa::Log->instance;
$log->{log_to_stderr} = 'all' if -t;

my $robot_id = $options{'domain'};

# Load configuration
unless (Conf::load()) {
    $log->syslog('err', 'The configuration file %s contains error',
        Conf::get_sympa_conf());
    exit 1;
}

$log->openlog(
    $Conf::Conf{'syslog'},
    $Conf::Conf{'log_socket_type'},
    database_backend => undef
);

# setting log_level using conf unless it is set by calling option
$log->{level} = $Conf::Conf{'log_level'};

my ($aliases_file, $aliases_program, $aliases_db_type);
if ($robot_id) {
    unless (Conf::valid_robot($robot_id)) {
        $log->syslog('err', 'Robot %s does not exist', $robot_id);
        exit 1;
    }
    $aliases_file    = Conf::get_robot_conf($robot_id, 'sendmail_aliases');
    $aliases_program = Conf::get_robot_conf($robot_id, 'aliases_program');
    $aliases_db_type = Conf::get_robot_conf($robot_id, 'aliases_db_type');
} else {
    $aliases_file    = $Conf::Conf{'sendmail_aliases'};
    $aliases_program = $Conf::Conf{'aliases_program'};
    $aliases_db_type = $Conf::Conf{'aliases_db_type'};
}
if ($aliases_file eq 'none') {
    exit 0;    # do nothing
} elsif (!-e $aliases_file) {
    $log->syslog('err', 'The aliases file %s does not exist', $aliases_file);
    exit 1;
}
unless ($aliases_db_type =~ /\A\w+\z/) {
    $log->syslog('err', 'Invalid aliases_db_type "%s"', $aliases_db_type);
    exit 1;
}

if ($aliases_program =~ m{\A/}) {
    $log->syslog('debug2', 'Executing "%s %s"',
        $aliases_program, $aliases_file);

    exec $aliases_program, $aliases_file;
} elsif ($aliases_program eq 'makemap') {
    $log->syslog('debug2', 'Executing "%s %s %s < %s"',
        q{/usr/sbin/makemap}, $aliases_db_type, $aliases_file, $aliases_file);

    unless (open STDIN, '<', $aliases_file) {
        $log->syslog('err', 'Canot open %s', $aliases_file);
        exit 1;
    }
    exec q{/usr/sbin/makemap}, $aliases_db_type, $aliases_file;
} elsif ($aliases_program eq 'newaliases') {
    $log->syslog(
        'debug2',
        'Executing "%s"',
        q{/usr/bin/newaliases }
    );

    # Some newaliases utilities e.g. with Postfix cannot take arguments.
    # OTOH if it may take arg, exec() must take separate one to avoid shell
    # metacharacters.
    if (q{}) {
        exec q{/usr/bin/newaliases}, q{};
    } else {
        exec q{/usr/bin/newaliases};
    }
} elsif ($aliases_program eq 'postalias') {
    $log->syslog('debug2', 'Executing "%s %s:%s"',
        q{/usr/local/sbin/postalias}, $aliases_db_type, $aliases_file);

    exec q{/usr/local/sbin/postalias}, "$aliases_db_type:$aliases_file";
} elsif ($aliases_program eq 'postmap') {
    $log->syslog('debug2', 'Executing "%s %s:%s"',
        q{/usr/local/sbin/postmap}, $aliases_db_type, $aliases_file);

    exec q{/usr/local/sbin/postmap}, "$aliases_db_type:$aliases_file";
} elsif ($aliases_program eq 'none') {
    $log->syslog('debug2', 'Skipping execution of aliases_program');
    exit 0;
} else {
    $log->syslog('err', 'Invalid aliases_program "%s"', $aliases_program);
    exit 1;
}

my $errno = $ERRNO;
$log->syslog('err', 'Cannot execute aliases_program "%s": %m',
    $aliases_program);
exit($errno || 1);

__END__

=encoding utf-8

=head1 NAME

sympa_newaliases, sympa_newaliases.pl - Alias database maintenance

=head1 SYNOPSIS

  sympa_newaliases.pl --domain=dom.ain

=head1 DESCRIPTION

F<sympa_newaliases.pl> is a program to maintain alias database.

It is typically invoked from
L<Sympa::Aliases::Template> module via sympa_newaliases-wrapper,
then updates alias database.

=head1 OPTIONS

F<sympa_newaliases.pl> may run with following options.

=over

=item C<--domain=>I<domain>

Name of virtual robot on which aliases will be updated.

=item C<-h>, C<--help>

Print this help message.

=back

=head1 CONFIGURATION PARAMETERS

Following site configuration parameters in F</usr/local/etc/sympa/sympa.conf> will be referred.
They may be overridden by robot.conf of each virtual robot.

=over

=item sendmail_aliases

Source text of alias database.

Default value is F<$SENDMAIL_ALIASES>.

=item aliases_program

System command to update alias database.
Possible values are:

=over

=item C<makemap>

Sendmail makemap utility.

=item C<newaliases>

L<newaliases(1)> or compatible utility.

=item C<postalias>

Postfix L<postalias(1)> utility.

=item C<postmap>

Postfix L<postmap(1)> utility.

=item Full path

Full path to executable file.
File will be invoked with the value of C<sendmail_aliases> as an argument.

=back

Default value is C<newaliases>.

=item aliases_db_type

Type of alias database.
This is meaningful when value of C<aliases_program> parameter is
C<makemap>, C<postalias> or C<postmap>.

Possible values will be vary by system commands.
For example, C<postalias> and C<postmap> can support any of
C<btree>, C<cdb>, C<dbm>, C<hash> and C<sdbm>.

Default value is C<hash>.

=back

=head1 RETURN VALUE

Returns with exit code 0.
If invoked system command failed, returns with its exit code.
On other failures, returns with 1.

=head1 FILES

=over

=item F</usr/local/etc/sympa/sympa.conf>

Sympa site configuration.

=item F<$LIBEXECDIR/sympa_newaliases-wrapper>

Set UID wrapper for sympa_newaliases.pl.

=back

=head1 HISTORY

sympa_newaliases.pl appeared on Sympa 6.1.18.
It was initially written by
IKEDA Soji <ikeda@conversion.co.jp>.

=head1 SEE ALSO

L<Sympa::Aliases::Template>.

=cut
