#! /usr/bin/perl
# -*- indent-tabs-mode: nil; -*-
# vim:ft=perl:et:sw=4
# $Id: p12topem.pl.in 12612 2016-01-01 01:48:29Z sikeda $

# 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 GIP RENATER
#
# 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 '/usr/local/libdata/perl5/site_perl';
use strict;
use warnings;
use Getopt::Long;

use Conf;
use Sympa::Constants;
use Sympa::DatabaseManager;
use Sympa::List;

my $sympa_conf_file = Sympa::Constants::CONFIG;

## Load sympa config
unless (Conf::load($sympa_conf_file)) {
    die 'config_error';
}

## Probe Db if defined
if ($Conf::Conf{'db_name'} and $Conf::Conf{'db_type'}) {
    unless (Sympa::DatabaseManager::probe_db()) {
        die('Database %s defined in sympa.conf has not the right structure or is unreachable. If you don\'t use any database, comment db_xxx parameters in sympa.conf',
            $Conf::Conf{'db_name'}
        );
    }
}

my $openssl;
foreach my $path (
    split(/:/, $ENV{PATH} || ''),
    qw(/usr/local/bin /usr/local/ssl/bin /opt/local/bin /usr/sfw/bin /usr/bin)
    ) {
    my $file = $path . '/openssl';
    if (-x $file) {
        $openssl = $file;
        last;
    }
}

my $home_sympa = $Conf::Conf{'home'};
my $outpass    = $Conf::Conf{'key_passwd'};
my $etc_dir    = $Conf::Conf{'etc'};

## Check option
my %options;
GetOptions(\%main::options, 'pkcs12=s', 'listname=s', 'robot=s', 'help|h');

$main::options{'foreground'} = 1;
my $listname = $main::options{'listname'};
my $robot    = $main::options{'robot'};
my $p12input = $main::options{'pkcs12'};

my ($cert, $privatekey, $inpass, $key);

if (   ($main::options{'help'} ne '')
    || !(-r $main::options{'pkcs12'})
    || (($main::options{'listname'} ne '') && ($main::options{'robot'} ne ''))
    ) {
    print_usage();
} else {

    if ($listname) {
        my $self = Sympa::List->new($listname, $robot);
        $cert       = $self->{'dir'} . '/cert.pem';
        $privatekey = $self->{'dir'} . '/private_key';
        unless (-d $self->{'dir'}) {
            printf
                "unknown list $listname (directory $self->{'dir'} not found)\n";
            die;
        }
    } elsif ($robot) {
        if (-d $Conf::Conf{'etc'} . '/' . $robot) {
            $cert       = $Conf::Conf{'etc'} . '/' . $robot . '/cert.pem';
            $privatekey = $Conf::Conf{'etc'} . '/' . $robot . '/private_key';
        } else {
            $cert       = $Conf::Conf{'etc'} . '/cert.pem';
            $privatekey = $Conf::Conf{'etc'} . '/private_key';
        }
    }

    if (-r "$cert") {
        printf "$cert certificate already exists\n";
        die;
    }
    if (-r "$privatekey") {
        printf "$privatekey already exists\n";
        die;
    }

    unless ($openssl) {
        printf
            "You must set PATH environment variable to use openssl command line utility.\n";
        die;
    }

    my $pipeout;

    system 'stty', '-echo';
    printf "password to access to $p12input :";
    chop($inpass = <STDIN>);
    print "\n";
    system 'stty', 'echo';
    open $pipeout, '|-', $openssl, 'pkcs12',
        '-in'  => $p12input,
        '-out' => $cert,
        '-nokeys', '-clcerts', '-passin' => 'stdin';
    print $pipeout "$inpass\n";
    close $pipeout;

    unless ($outpass) {
        system 'stty', '-echo';
        printf "sympa password to protect list private_key $key:";
        chop($outpass = <STDIN>);
        print "\n";
        system 'stty', 'echo';
    }
    open $pipeout, '|-', $openssl, 'pkcs12',
        '-in'  => $p12input,
        '-out' => $privatekey,
        '-nocerts',
        '-passin' => 'stdin',
        '-des3', '-passout' => 'stdin';
    print $pipeout "$inpass\n$outpass\n";
    close $pipeout;

    printf "$privatekey and  $cert created.\n";
    exit;
}

sub print_usage {
    printf "

Usage p12topem.pl --pkcs12 <pkcs#12_cert_file> --listname <listname> or
      p12topem.pl --pkcs12 <pkcs#12_cert_file> --robot <robot>

This script is intended to convert a PKCS#12 certificates in PEM format
using Openssl. This is useful because most PKI providerd deliver certificates
using a web interface so the certificate is stored in your browser.

When exporting a certificate from a browser (Netscape, IE, Mozilla etc)
the result is stored using PKCS#12 format.Sympa requires a pair of PEM
certificate and private key. You must then convert your pkcs#12 into PEM. 

For a list certificate, the file will be installed in
$home_sympa/<listname>/cert.pem and $home_sympa/<listname>/private_key

For Sympa itself a certificate will be installed in 
$Conf::Conf{'etc'}/<robot>/cert.pem and  $Conf::Conf{'etc'}/<robot>/private_key or
$Conf::Conf{'etc'}/cert.pem and Conf{'etc'}/private_key


You are then prompted for inpassword (the password used to encrypt the
pkc#12 file).\n";
    unless ($outpass) {
        printf
            "Because you did not configure Sympa's password \"key_passwd\"  in
sympa.conf you will also be prompted for the password used by sympa to access
to the list private key)\n";
    }
}
