#!/usr/bin/env perl

use strict;
use warnings;
use Carp;
use Getopt::Long;
use Pod::Usage;
use Data::Dumper;

use Net::SSL::ExpireDate;
use Regexp::Common qw(net);
use YAML;
use URI::Escape;
# use Smart::Comments;

MAIN: {
    my %opt;
    Getopt::Long::Configure("bundling");
    GetOptions(\%opt,
               'duration|d=s',
               'file|f=s',
               'link|l',
               'help|h|?') or pod2usage(-verbose=>1);
    pod2usage(-verbose=>1) if exists $opt{'help'};
    pod2usage("missing option: --file") unless exists $opt{'file'};

    ### %opt
    open my $file, '<', $opt{'file'}
        or croak "$opt{'file'}: $!";
    chomp (my @testee = <$file>);
    close $file;
    ### @testee

    my $duration = exists $opt{'duration'} ? $opt{'duration'} : undef;
    ### $duration

    my $output = {
        title => 'Certificate Expire Date',
        entry => [],
    };

    for my $t (@testee) {
        next if $t =~ /^#/;
        next if $t =~ /^$/;
        ### testee: $t
        my $ed = Net::SSL::ExpireDate->new( build_arg($t) );
        if ($ed->is_expired($duration)) {
            ### expired: $ed->expire_date->iso8601
            push @{$output->{entry}}, {
                title => $t . ($duration ? " (expire within $duration)" : ''),
                date  => $ed->expire_date->iso8601,
            };
            ${ $output->{entry} }[-1]->{link} = mk_link($ed, $duration) if $opt{'link'};
        }
    }

    print YAML::Dump $output;

    exit 0;
}

sub build_arg {
    my ($v) = @_;
    if ($v =~ m{^(file)://(.+)}) {
        return $1 => $2;
    } elsif ($v =~ m{^(https)://([^/]+)}) {
        return $1 => $2;
    } elsif ($v =~ m{^$RE{net}{domain}{-nospace}{-keep}$}) {
        return 'https' => $1;
    } elsif (-r $v) {
        return 'file' => $v;
    } else {
        croak "$v: assume file. but cannot read.";
    }
}

sub mk_link {
    my ($ed, $duration) = @_;
    $ed->type . '://' . $ed->target . ($duration ? '#' . uri_escape($duration) : '');
}

__END__

=head1 NAME

B<check-cert-expire.pl> - check and list expired certificates

=head1 SYNOPSIS

B<check-cert-expire.pl> [ B<--help> ] [ B<--duration> DURATION ] B<--file> DATAFILE

  $ cat <<EOF > server-list.txt
  https://rt.cpan.org
  https://www.google.com
  EOF
  
  $ check-cert-expire.pl --duration '3 months' --file server-list.txt
  $ check-cert-expire.pl -d         '3 months' -f     server-list.txt
  ---
  entry:
    - date: 2015-04-14T05:12:17
      title: https://rt.cpan.org
  title: Certificate Expire Date

=head1 DESCRIPTION

Examine expire date of certificate and output name and expire date if expired.
Output format is YAML.

Examinee certificate is both OK via network (HTTPS) and local file.

=head1 OPTIONS

=over 4

=item B<--file> DATAFILE

=item B<-f> DATAFILE

DATAFILE is name of plain text file contains testee list.

Acceptable list format is the following.

  FORMAT        EXAMPLE
  ===============================
  https://FQDN  https://rt.cpan.org
  file://PATH   file:///etc/ssl/cert.pem
  FQDN          rt.cpan.org
  PATH          /etc/ssl/cert.pem

=item B<--duration> DURATION

=item B<-d> DURATION

Specify the furtur point to check expiration.
If omitted, check against just now.

DURATION accepts human readable text. See also L<Time::Duration::Parse|Time::Duration::Parse>.

  3 days
  4 months
  10 years
  4 months and 3days

=item B<--link>

=item B<-l>

Add dummy link attribute.

=back

=head1 SEE ALSO

L<Net::SSL::ExpireDate|Net::SSL::ExpireDate>,
L<Time::Duration::Parse|Time::Duration::Parse>

=head1 AUTHOR

HIROSE, Masaaki E<lt>hirose31@gmail.comE<gt>

=cut

# for Emacsen
# Local Variables:
# mode: cperl
# cperl-indent-level: 4
# indent-tabs-mode: nil
# End:

# vi: set ts=4 sw=4 sts=0 :