#!/usr/bin/perl -w

# cidcall - caller ID report

# Created by John L. Chmielewski on Fri Sep 14, 2001
#
# Copyright (c) 2001-2020 by
#   John L. Chmielewski <jlc@users.sourceforge.net>
#   Todd Andrews <tandrews@users.sourceforge.net>
#   Aron Green

use strict;
use warnings;
use Pod::Usage;
use File::Basename;
use Getopt::Long qw(:config no_ignore_case_always);
use File::Glob ':bsd_glob';
use Time::Piece;

my ($help, $man, $version);
my ($blk, $cid, $end, $hup, $msg, $not, $out, $pid, $put, $wid, $mwi, $rid, $allTypes);
my ($label, $date, $lineid, $name, $number);
my ($stime, $etime, $mtype, $mesg, $exception, $extra, $iflag);
my ($cidlog, $listyears);
my ($stripOne, @columns);
my ($lineCount_col, $label_col, $name_col, $nmbr_col, $lineid_col,  $date_col, $stime_col, $etime_col, $exception_col, $mesgType_col, $mesg_col) = (0..10);
my $debug = 1;
my $verbose = 1;
my $lineCount;
my $lineCountModulo = 50;
my ($humanReadable, $delimited) = (1, 2);
my $outputFormat = $humanReadable;
my $delimiter = ",";
my $cl_lineid;
my $yearlog = 0;
my $thisyear = 0;
my $t = localtime;
my $currentyear = $t->year;
my @fields = "DATE, TIME, LINE, NMBR, MESG, FNMBR, NTYPE, CTRY, LOCA, CARI, NAME, MTYPE,";

my $prog = basename($0);
my $VERSION = "(NCID) 1.18";

format STDOUT =
@<<< @<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<<< @<<<<<<< @<<<<<<<
$label, $name,             $number,      $lineid,    $date,      $stime,  $extra
.

select(STDERR); $| = 1; # enable autoflush, otherwise output to STDERR
select(STDOUT); $| = 1; # may appear before output to STDOUT

Getopt::Long::Configure ("bundling");
my ($result) = GetOptions(
    "help|h"        => \$help,
    "man|m"         => \$man,
    "debug|D"       => \$debug,
    "verbose|v=i"   => \$verbose,
    "version|V"     => \$version,
    "format|f=i"    => \$outputFormat,
    "lineid|i=s"    => \$cl_lineid,
    "list-years|l"  => \$listyears,
    "delimiter|d=s" => \$delimiter,
    "strip-one|1"   => \$stripOne,
    "all-types|a"   => \$allTypes,
    "yearlog|y=i"   => \$yearlog,
    "thisyear|t"    => \$thisyear,
    "BLK|B"         => \$blk,
    "CID|C"         => \$cid,
    "END|E"         => \$end,
    "HUP|H"         => \$hup,
    "MSG|M"         => \$msg,
    "MWI"           => \$mwi,
    "NOT|N"         => \$not,
    "OUT|O"         => \$out,
    "PID|P"         => \$pid,
    "PUT|p"         => \$put,
    "RID|R"         => \$rid,
    "WID|W"         => \$wid
 ) || pod2usage(2);
die "$prog $VERSION\n" if $version;
pod2usage(-verbose => 1, -exitval => 0) if $help;
pod2usage(-verbose => 2, -exitval => 0) if $man;

if ($listyears) {
    my @files;
    @files = glob("~/NCID/log/cidcall*.log");
    foreach my $file (@files) {
        $file = basename($file);
        print "$file\n";
    }
} else {
    ($cidlog = shift) || ($cidlog = "/var/log/cidcall.log");

    $cidlog = glob("~/NCID/log/cidcall-$yearlog.log") if $yearlog;

    $cidlog = glob("~/NCID/log/cidcall-$currentyear.log") if $thisyear;

    if ( ($outputFormat < 0) || ($outputFormat > 2) ) { die "Format option must be in the range of 0-2.";}

    $delimiter = pack ('H2', '09') if $delimiter eq "t";

    if ($outputFormat == $delimited) {
        #create column headings
        &initColumns;
        $columns[$lineCount_col]= "Line Count";
        $columns[$label_col]    = "Call Type";
        $columns[$name_col]     = "Name";
        $columns[$nmbr_col]     = "Number";
        $columns[$lineid_col]   = "Line ID";
        $columns[$date_col]     = "Date";
        $columns[$stime_col]    = "Start";
        $columns[$etime_col]    = "End";
        $columns[$exception_col]= "Exception";
        $columns[$mesgType_col] = "Message Type";
        $columns[$mesg_col]     = "Message";
        &doDelimited;
    }

    open(CIDLOG, $cidlog) || die "Could not open $cidlog\n";

    $lineCount = 0;
    logMsg(3,"\n\nProcessing log file '$cidlog'\n\n");

    if ($allTypes) {$blk = $cid = $end = $hup = $msg = $mwi = $not = $out = $pid = $put = $rid = $wid = 1;}

    while (<CIDLOG>) {
        $lineCount ++;
        if ($lineCount % $lineCountModulo == 0) { logMsg(7,"\n==> '$cidlog' continues <==\n\n"); }
        logMsg(5,sprintf("[%6d] %s", $lineCount, $_));
        if (! /DATE|MSG:/) {next;}
        if (!$blk && !$cid && !$end && !$hup && !$msg && !$mwi && !$not && !$out && !$pid && !$put && !$rid && !$wid) {
            if (!$outputFormat) { print; next;}
            if (/BLK:|CID:|HUP:|MWI:|OUT:|PID:|PUT:|RID:|WID:/) {&parseLine;}
        }
        else {
            if ($blk) { if (/BLK:/) {&parseLine;} }
            if ($cid) { if (/CID:/) {&parseLine;} }
            if ($end) { if (/END:/) {&parseLine;} }
            if ($hup) { if (/HUP:/) {&parseLine;} }
            if ($msg) { if (/MSG:/) {&parseLine;} }
            if ($msg) { if (/MWI:/) {&parseLine;} }
            if ($not) { if (/NOT:/) {&parseLine;} }
            if ($out) { if (/OUT:/) {&parseLine;} }
            if ($pid) { if (/PID:/) {&parseLine;} }
            if ($put) { if (/PUT:/) {&parseLine;} }
            if ($rid) { if (/RID:/) {&parseLine;} }
            if ($wid) { if (/WID:/) {&parseLine;} }
        }
    }
}

sub parseLine {
    my $newline = $_;
    $newline =~ s/\*([[A-Z]{4,})\*/|$1|/g;
    $newline =~ s/\*+$/|/;
    if (/MSG:|NOT:/) {$newline =~ s/(\*\*\|)/|||/};
    if (/FNMBR/) {
        ($number) = $newline =~ /\|FNMBR\|([\s\w:?!@#$%&*()_{}[\]=:;'<>,.?+-]+)\|/;
    } elsif (/NMBR/) {
        ($number) = $newline =~ /\|NMBR\|([\s\w:?!@#$%&*()_{}[\]=:;'<>,.?+-]+)\|/;
    }
    if (/MSG:|NOT:/) {
      $etime = "";
      if (($iflag = $newline =~ /\s+\|\|\|/) != 1) {
        chop;
        $date = $stime = $number = $name = $mtype = "";
        ($label, $mesg) = /(\w+:)\s+(.*)$/;
      } else {
      ($mtype) = $newline =~ /\|MTYPE\|(\w+)\|/;
      $extra = $mtype;
      ($label)  = $newline =~ /^(\w+:)/;
      ($date)   = $newline =~ /\|DATE\|(\d+)/;
      ($stime)  = $newline =~ /\|TIME\|(\d+)/;
      ($name)   = $newline =~ /\|NAME\|([\s\w:?!@#$%&*()_{}[\]=:;'<>,.?+-]+)\|/;
      ($lineid) = $newline =~ /\|LINE\|([\s\w:?!@#$%&*()_{}[\]=:;'<>,.?+-]+)\|/;
      ($mesg)   = $newline =~ /\s+(.*)\|\|\|/;
      $date  =~ s/(\d\d)(\d\d)(\d\d\d\d)*/$1\/$2\/$3/;
      $stime =~ s/(\d\d)(\d\d)/$1:$2/;
      }
    } else {
      $iflag = 1;
      $mesg = $extra = $mtype = $etime = "";
      ($label)  = $newline =~ /^(\w+:)/;
      ($date)   = $newline =~ /\|DATE\|(\d+)/;
      ($stime)  = $newline =~ /\|TIME\|(\d+)/;
      ($name)   = $newline =~ /\|NAME\|([\s\w:?!@#$%&*()_{}[\]=:;'<>,.?+-]+)\|/;
      ($lineid) = $newline =~ /\|LINE\|([\s\w:?!@#$%&*()_{}[\]=:;'<>,.?+-]+)\|/;
      $date  =~ s/(\d\d)(\d\d)(\d\d\d\d)*/$1\/$2\/$3/;
      $stime =~ s/(\d\d)(\d\d)/$1:$2/;
      if (/END:/) {
        ($stime, $etime) =
         /.*SCALL.\d+\/\d+\/\d+ (\d\d:\d\d:\d\d).*ECALL.\d+\/\d+\/\d+ (\d\d:\d\d:\d\d).*$/;
        $extra = $etime;
      }
    }

    if ($cl_lineid) {
       if ($cl_lineid ne $lineid) {return;}
    }

    $exception="";

    if ($outputFormat == $delimited) {
       &initColumns;
       $columns[$lineCount_col]= $lineCount;
       $columns[$label_col]    = $label;
       $columns[$name_col]     = $name;
       ($columns[$nmbr_col], $columns[$exception_col]) = (normalizeNumber($number));
       $columns[$lineid_col]   = $lineid;
       $columns[$date_col]     = $date;
       $columns[$stime_col]    = $stime;
       $columns[$etime_col]    = $etime;
       $columns[$mesgType_col] = $mtype;
       $columns[$mesg_col]     = $mesg;
       &doDelimited;

    } elsif (!$outputFormat) {
      print;
    } else {
      write if $iflag;
      if ($mesg && $outputFormat == 1) {
        print "$label " if !$iflag;
        print "$mesg\n";
      }
    }
}

sub doDelimited {

   if ($delimiter eq ",") {
      # put quotes around columns containing the delimiter
      # Acme Manufacturing, Inc. => "Acme Manufacturing, Inc."
      foreach my $i (0 .. $#columns) {
          if (index($columns[$i],$delimiter) > -1 ) { $columns[$i] = "\"" . $columns[$i] . "\""; }
      }
    }

    print $columns[$lineCount_col], $delimiter,
          $columns[$label_col]    , $delimiter,
          $columns[$name_col]     , $delimiter,
          $columns[$nmbr_col]     , $delimiter,
          $columns[$lineid_col]   , $delimiter,
          $columns[$date_col]     , $delimiter,
          $columns[$stime_col]    , $delimiter,
          $columns[$etime_col]    , $delimiter,
          $columns[$exception_col], $delimiter,
          $columns[$mesgType_col] , $delimiter,
          $columns[$mesg_col]     ,
          "\n";

}

sub initColumns {
    @columns=();
    $columns[$lineCount_col]="";
    $columns[$label_col]    ="";
    $columns[$name_col]     ="";
    $columns[$nmbr_col]     ="";
    $columns[$lineid_col]   ="";
    $columns[$date_col]     ="";
    $columns[$stime_col]    ="";
    $columns[$etime_col]    ="";
    $columns[$exception_col]="";
    $columns[$mesgType_col] ="";
    $columns[$mesg_col]     ="";
}

sub normalizeNumber {
    my $n = shift;
    my $orig_n = $n;
    my $exception = "";
    if (substr($n,0,1) eq "*") { $n=substr($n,1); }
    if (substr($n,-1) eq "*") { $n=substr($n,0,length($n)-1);}
    if ( (substr($n,0,1) eq "1" ) && (length($n) == 11) && $stripOne ) {
       $n = substr($n,1);
    }
    if ($n ne $orig_n) { $exception = "Original number: $orig_n"; }
    return ($n, $exception);
}

sub logMsg {
    my($level, $message) = @_;

    if (!defined $message) {print "Oops, unexpected exit\n"; exit 1}

    # write to STDOUT
    print $message if $debug && $verbose >= $level;

    # write to logfile
    #print LOGFILE $message if $fileopen && $verbose >= $level;
}

=head1 NAME

cidcall - view calls, hangups, messages and end of calls in the NCID call file

=head1 SYNOPSIS

 cidcall [--help       | -h]
         [--list-years | -l]
         [--man        | -m]
         [--format     | -f <0>]
         [--version    | -V]

 cidcall [--all-types  | -a]
         [--format     | -f <1-2>]
         [--delimiter  | -d <text>]
         [--strip-one  | -1]
         [--verbose    | -v <1-9>]
         [--thisyear   | -t]
         [--yearlog    | -y <4 digit year>]
         [--BLK        | -B]
         [--CID        | -C]
         [--END        | -E]
         [--HUP        | -H]
         [--MSG        | -M]
         [--MWI]
         [--NOT        | -N]
         [--OUT        | -O]
         [--PID        | -P]
         [--PUT        | -p]
         [--RID        | -R]
         [--WID        | -W]
         [cidlog]

=head1 DESCRIPTION

The cidcall tool displays the cidcall.log file in one of
three different formats: raw, human readable and delimited.

The default is to display BLK, CID, HUP, MWI, OUT, PID, PUT, RID and WID lines in
a human readable format.

=head2 Options

=over 7

=item -h, --help

Displays the help message and exits.

=item -m, --man

Displays the manual page and exits.

=item -D, --debug

Debug mode. Always enabled, reserved for future use.

=item -v, --verbose <1-9>

Output information while processing is occurring.  Set
the level to a higher number for more information.  Levels range from
1 to 9, but not all levels are used.

Default: verbose = 1

=item -V, --version

Displays the version and exits.

=item -f <0-2>, --format <0-2>

Determines the output format used.

Output format 0 displays the call log as-is.

Output format 1 displays the call log in human readable text.

Output format 2 displays the call log with field delimiters for easy
parsing by another program.
Uses options -d|--delimiter and -1|--strip-one.

The default output format is 1 (human readable).

=item -d <text>, --delimiter <text>

Used when output format is 2 (delimited). Fields will be delimited by
<text>.

For pipe-delimited output, surround the pipe symbol with single or double
quotes: '|' or "|".

For tab-delimited output, specify only the letter "t".

For comma-delimited output, fields containing an embedded comma will
automatically be surrounded by double-quotes.

Default delimiter is a comma (",").

=item -l, --list-years

Lists all the yearly call logs.

=item -1, --strip-one

Used when output format is 2 (delimited). If a number is exactly
11 digits and it begins with "1", strip the "1" before outputting
it. This is to facilitate consistent sorting of the output for
10 digit numbers.

If the leading "1" is stripped, the "Exception" column will so indicate.

=item -i, --lineid <text>

Output only those lines where the lineid matches <text>.

=item -a, --all-types

Equivalent to typing --BLK, --CID, --END, --HUP, --MSG, --MWI, --NOT,
--OUT, --PID, --PUT, --RID and --WID on the command line.

=item -B, --BLK

Displays BLK lines (blocked calls) in the call file.

=item -C, --CID

Displays CID lines (incoming calls) in the call file.

=item -E, --END

Displays END lines (gateway end of call) in the call file.

=item -H, --HUP

Displays HUP lines (terminated calls) in the call file.

=item -M, --MSG

Displays MSG lines (messages) in the call file.

=item --MWI

Displays MWI lines (voicemail message waiting) in the call file.

Note: There is no short option for --MWI.

=item -N, --NOT

Displays NOT lines (smartphone note (message)) in the call file.

=item -O, --OUT

Displays OUT lines (outgoing calls) in the call file.

=item -P, --PID

Displays PID lines (smartphone Caller ID) in the call file.

=item -p, --PUT

Displays PID lines (smartphone outgoing calls) in the call file.

=item -R, --RID

Displays RID lines (ringback calls) in the call file.

=item -W, --WID

Displays WID lines ("call waiting" calls) in the call file.

=item -t, --thisyear

Obtains data from $HOME/NCID/log/cidcall-<thisyear>.log instead of the
default.  This overrides a call log given on the command line.

=item -y, --yearlog <4 digit year>

Obtains data from $HOME/NCID/log/cidcall-<year>.log instead of the
default.  This overrides a call log given on the command line.

=back

=head2 Arguments

=over 7

=item cidlog

The NCID call file.

Default: /var/log/cidcall.log

=back

=head1 EXAMPLES

=over 2

=item Output as tab-delimited, changing 11-digit numbers beginning with "1" to be 10-digits:

cidcall -f 2 -d t -1

=item Output as pipe-delimited, changing 11-digit numbers beginning with "1" to be 10-digits, then sorting numerically on the phone number column:

cidcall -f 2 -d '|' -1 | sort -t '|' -k4,4 -n

=back

=head1 FILES

 /var/log/cidcall.log
 $HOME/NCID/log/cidcall-<year>.log

=head1 SEE ALSO

ncidd.conf.5

=cut
