diff options
author | Barak A. Pearlmutter <bap@debian.org> | 2010-04-20 15:18:13 -0400 |
---|---|---|
committer | Barak A. Pearlmutter <bap@debian.org> | 2010-04-20 15:18:13 -0400 |
commit | 0960d4900c9bc749cd72e3d928e8cfbe081712ea (patch) | |
tree | a9e6d9f90ba35dd7f1fdb68a96f08808380bfbbe /utils/bbdb-cid.pl |
Import bbdb_2.36.orig.tar.gz
[dgit import orig bbdb_2.36.orig.tar.gz]
Diffstat (limited to 'utils/bbdb-cid.pl')
-rwxr-xr-x | utils/bbdb-cid.pl | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/utils/bbdb-cid.pl b/utils/bbdb-cid.pl new file mode 100755 index 0000000..a422b67 --- /dev/null +++ b/utils/bbdb-cid.pl @@ -0,0 +1,516 @@ +#!/usr/bin/perl -w +# +# Caller-ID-logger, by jwz (19-Jan-97) +# Modified: 24-Apr-97 +# +# Opens the modem and waits for it to print caller-ID data. When it does, +# it logs it to a file, parses it, and pops up a window using "xmessage". +# If the number is present in your .bbdb file, it shows the name (or company) +# associated with it. +# +# Todo: +# My caller ID service (in San Francisco) only ever sends numbers, not names, +# so I've never seen a "name" line come in; I assume that it would send both +# a name and a number, so it would be nice to present both (with error +# checking against BBDB) but the code as currently structured only handles +# one-line-per-call. It should realize that consecutive lines with the same +# timestamp are the same call. +# +# Modems other than ZyXELs have different caller-ID formats, and this doesn't +# deal with those. + +############################################################################## +# +# Some variables you might want to set... + + +# Set this to the device that your modem is attached to. +# +$modem_device = "/dev/ttyd1"; + +# This is your .bbdb file. (Set it to null if you don't want to do BBDB +# lookups at all, but why would you want to go and do a thing like that?) +# +$bbdb_file = "$ENV{HOME}/.bbdb"; + +# A shell command to use to cause emacs to pop up the BBDB buffer +# (bbdb-srv.pl is a good choice, so it defaults to the value of the +# shell environment variable $NS_MSG_DISPLAY_HOOK.) +# +$bbdb_cmd = $ENV{NS_MSG_DISPLAY_HOOK}; + +# If you want the $bbdb_cmd to be run on a different host, set it here. +# +$bbdb_host = "gimp"; + +# If you want each call to be logged to a file as well, name it here. +# +$logfile = "/usr/spool/fax/log/cid-log"; + +# For verbosity... +$debug = 0; + +# How to pop up a dialog box. +# +$xmessage_cmd = "xmessage"; +@xmessage_args = ("-display", ":0", + "-name", "Caller ID", + # roughly centered on my screen; YMMV. + "-geometry", "+400+400", + "-xrm", "*Font: -*-new cent*-bold-r-normal-*-240-*", + "-xrm", "*Foreground: black", + "-xrm", "*Background: lightgreen", + # no buttons on the window: dismiss it by clicking in it. + "-button", "", + "-xrm", "*form.Translations: #override <BtnDown>: exit(0)", + "-xrm", "*Command.Font: -*-new cent*-bold-r-normal-*-120-*", + "-xrm", "*Command.horizDistance: 130" + ); + +# Uh, let's turn off the screensaver before popping up the window. +# +$pre_dialog_cmd = "xscreensaver-command -deactivate"; + + +# commands (and their expected responses) used to initialize the modem. +# +@modem_init = ( "AT", "OK", # ping + "ATZ", "OK", # reset + "ATE0", "OK", # don't echo commands + "ATM0", "OK", # turn off speaker + "ATN0", "OK", # turn off ringer + "ATS40.2=1", "OK", # turn on caller ID + ); + + +# for diagnostics: if the modem ever asynchronously prints something that +# doesn't match this, we issue a warning. +# +$expected_responses = "^CALLER NUMBER" . "|" . + "^REASON FOR NO CALLER " . "|" . + "^RING" . "|" . + "^TIME: [-0-9: ]+\$"; + + +############################################################################## +# +# Talking to the serial port... +# +# + +if ( $ debug ) { + use diagnostics; +} + + +sub open_modem { + use IPC::Open2; + + # Close the terminal streams before forking `cu', because otherwise + # it fucks around with the stty settings. + # + open(SAVEIN, "<&STDIN") || die("can't dup stdin"); + open(SAVEOUT, ">&STDOUT") || die("can't dup stdout"); + open(SAVEERR, ">&STDERR") || die("can't dup stderr"); + close(STDIN); + close(STDOUT); + close(STDERR); + + my $cu_pid = open2( \*MODEM_IN, \*MODEM_OUT, + "cu -l$modem_device -s2400 2>&1"); + + # Now that cu has been launched, we can restore them. + # + open(STDIN, "<&SAVEIN") || die("can't restore stdin"); + open(STDOUT, ">&SAVEOUT") || die("can't restore stdout"); + open(STDERR, ">&SAVEERR") || die("can't restore stderr"); + close(SAVEIN); + close(SAVEOUT); + close(SAVEERR); + + # The following doesn't seem to work and I don't know why... + # + # Set up a signal handler to try and kill off the cu process + # when we exit, instead of waiting ~30 seconds for it to notice + # that the pipe is gone... + # +# $SIG{INT} = sub { my $signame = shift; +# if ( $debug) { +# print STDERR "sending $signame to $cu_pid\n"; +# } +# print MODEM_OUT "\r\n~.\r\n"; +# close MODEM_OUT; +# close MODEM_IN; +# kill ($signame, $cu_pid); +# exit (1); +# }; + + $_ = <MODEM_IN>; + chop; + if ( !m/^Connected/ ) { + print STDERR "$0: cu printed `$_' instead of `Connected'\n"; + } +} + +sub read_line { + $_ = <MODEM_IN>; + $_ || die("got eof on modem"); + s/[\r\n]+$//; + if ( $_ eq "" ) { + $_ = <MODEM_IN>; + $_ || die("got eof on modem"); + s/[\r\n]+$//; + } + return $_; +} + +sub command { + my ( $command, $expected_response) = @_; + + if ( $debug ) { + print STDERR "sending: $command\n"; + } + + print MODEM_OUT "$command\r\n"; + my $line = read_line(); + + if ( $line eq $command ) { + if ( $debug ) { + print STDERR " got echo: reading next line too...\n"; + } + $line = read_line(); + } + + if ( $line ne $expected_response ) { + print STDERR " got: $line ; expected: $expected_response\n"; + } elsif ( $debug ) { + print STDERR " got: $line\n"; + } +} + +sub init_modem { + open_modem; + + my $len = $#modem_init + 1; + my $i; + for ($i = 0; $i < $len; $i += 2) { + command($modem_init[$i], $modem_init[$i+1]); + } +} + +sub handle_async_line { + local ( $_ ) = @_; + + if (!m/$expected_responses/) { + print STDERR "modem turd: $_\n"; + + } elsif (m/CALLER/) { + if ( $debug ) { + print STDERR "caller: $_\n"; + } + handle_cid_line($_); + + } elsif ( $debug ) { + if ( $_ eq '' ) { + print STDERR "ignored: blank line\n"; + } else { + print STDERR "ignored: $_\n"; + } + } +} + + +############################################################################## +# +# Parsing BBDB and CID data... +# + +sub find_bbdb_record { + my ( $area, $exchange, $suffix ) = @_; + + if ( ! $bbdb_file ) { + return undef; + } + + # strip off leading 0's, to match the way it's stored in .bbdb. + $area =~ s/^0+(.)/$1/; + $exchange =~ s/^0+(.)/$1/; + $suffix =~ s/^0+(.)/$1/; + + my $bbdb_rec = undef; + my $pat = "\\[\"[^\"]+\" $area $exchange $suffix (nil|[0-9]+)\\]"; + + open(BBDB, "<$bbdb_file") || die("error opening $bbdb_file: $!\n"); + + while (<BBDB>) { + if ( m/$pat/ ) { + $bbdb_rec = $_; + last; + } + } + close(BBDB); + return $bbdb_rec; +} + + +# note: global (kludge!) +$pretty_number = 0; + +sub make_message_string { + my ( $number, $date, $fn, $ln, $co, $error ) = @_; + my $msg; + + my $line_prefix = " "; + my $line_suffix = " "; + + # First print the date (reformatted.) + # + $_ = $date; + my ( $dotw, $mon, $day, $hr, $min, $sec, $year ) = + m/^([^ ]+) +([^ ]+) +([^ ]+) +([^:]+):([^:]+):([^:]+) +([^ ]+) *$/; + $year =~ s/^..(..)/$1/; + $day =~ s/^0//; + $hr =~ s/^0//; + if ($hr < 12) { + $ampm = "am"; + } else { + $ampm = "pm"; + if ($hr > 12) { $hr -= 12 }; + } + $date = "$hr:$min$ampm, $day-$mon-$year ($dotw)"; + $msg = $line_prefix . $date . $line_suffix; + + # Next print the caller name, company, or error message. + # + if ( $error ) { + $msg .= "\n" . $line_prefix . $error . $line_suffix; + } elsif ( $co && !$fn && !$ln ) { + $msg .= "\n" . $line_prefix . $co . $line_suffix; + } elsif ( $fn || $ln ) { + $msg .= "\n" . $line_prefix . "$fn $ln" . $line_suffix; + } + + # Next print the phone number (formatted nicely.) + # + if ( $number ) { + my $area = 0; + my $exchange = 0; + my $suffix = 0; + $_ = $number; + ( $area, $exchange, $suffix ) = + m/^([0-9][0-9][0-9])([0-9][0-9][0-9])([0-9][0-9][0-9][0-9]+)/; + + # note: global (kludge!) + $pretty_number = "($area) $exchange-$suffix"; + $msg .= "\n" . $line_prefix . $pretty_number . $line_suffix; + } + + return $msg; +} + +use POSIX; +sub reaper { + $SIG{CHLD} = \&reaper; # loathe sysV + my $signame = shift; + if ( $debug >= 2 ) { + printf STDERR " (got SIG$signame...)\n"; + } + my $child; + while ( ( $child = waitpid(-1,WNOHANG) ), + $child > 0 ) { + if ( $debug >= 2 ) { + printf STDERR " (pid $child exited with $?)\n"; + } + } +} + +sub fork_and_exec { + my @cmd_list = @_; + + $SIG{CHLD} = \&reaper; + + if ( $debug >= 2 ) { + $_ = $cmd_list[0]; + s/ .*//; + print STDERR "forking for " . $_ . " at " . (localtime) . ".\n"; + } + + my $pid; + if ($pid = fork()) { + # parent + } elsif (!defined $pid) { + print STDERR "$0: fork failed: $!\n"; + } else { + # child + + if ( $debug ) { + $_ = $cmd_list[0]; + s/ .*//; + print STDERR "exec'ing " . $_ . " at " . (localtime) . + " in pid $$.\n"; + } + close(STDIN); + close(STDOUT); + close(STDERR); + exec @cmd_list; + } +} + + +sub fork_and_exec_for_bbdb { + my @cmd_list = @_; + my $number = shift @cmd_list; + + $SIG{CHLD} = \&reaper; + + if ( $debug >= 2 ) { + $_ = $cmd_list[0]; + s/ .*//; + print STDERR "forking for " . $_ . " at " . (localtime) . ".\n"; + } + + my $pid; + if ($pid = fork()) { + # parent + } elsif (!defined $pid) { + print STDERR "$0: fork failed: $!\n"; + exit (1); + } else { + # child + + if ( $debug ) { + $_ = $cmd_list[0]; + s/ .*//; + print STDERR "exec'ing " . $_ . " at " . (localtime) . + " in pid $$.\n"; + } + if ( system @cmd_list ) { + my $cmd = "gnudoit -q '(bbdb-srv-add-phone \"$pretty_number\")'"; + if ( $bbdb_host ) { + $cmd =~ s/([()\"])/\\$1/g; + $cmd = "rsh $bbdb_host $cmd"; + } + exec $cmd; + } + exit (0); + } +} + + +sub pop_up_dialog { + my ( $msg, $buttonp, $number ) = @_; + + fork_and_exec $pre_dialog_cmd; + + if ( ! $buttonp ) { + fork_and_exec $xmessage_cmd, @xmessage_args, "\n$msg\n\n"; + } else { + my @args = ( @xmessage_args, "-button", "Add To BBDB" ); + fork_and_exec_for_bbdb $number, $xmessage_cmd, @args, "\n$msg\n\n"; + } +} + +sub pop_up_bbdb_buffer { + my ( $caller ) = @_; + if ( $bbdb_cmd ) { + my $cmd = $bbdb_cmd; + if ( $bbdb_host ) { + $cmd = "rsh $bbdb_host $cmd"; + } + $caller =~ s/\\/\\\\/g; + $caller =~ s/\"/\\\\\"/g; + `echo "Path:\nFrom: \\\"$caller\\\" <>" | $cmd >&- 2>&- &`; + } +} + + +sub handle_cid_line { + my($line) = @_; + + my $date = localtime; + + # Log the call... + # + if ( $logfile ) { + if (open(LOG, ">>$logfile")) { + print LOG "$date\t$line\r\n"; + close LOG; + } else { + print STDERR "error opening $logfile: $!\n"; + } + } + + # Pull the phone number out of the message... + # + my $number = ""; + my $error = ""; + + $_ = $line; + if ( m/^CALLER NUMBER/ ) { + ( $number ) = m/^[^:]+: *(.*) *$/; + } else { + $error = $line; + } + + my $caller = undef; + + my $fn = undef; + my $ln = undef; + my $co = undef; + my $buttonp = 0; + + if ( !$number || $number eq "" ) { + $error =~ tr#A-Z#a-z#; + $error =~ s/^REASON FOR NO CALLER (NUMBER|NAME)/Caller unknown/i; + + } else { + $_ = $number; + + my $area = 0; + my $exchange = 0; + my $suffix = 0; + ( $area, $exchange, $suffix ) = + m/^([0-9][0-9][0-9])([0-9][0-9][0-9])([0-9][0-9][0-9][0-9]+)/; + + my $bbdb_rec = find_bbdb_record($area, $exchange, $suffix); + + if ( $bbdb_rec ) { + my $junk = 0; + $_ = $bbdb_rec; + # This will lose if names or aliases have double-quotes in them. + # No doubt there's some hairier regexp magic that handles that... + ( $fn, $ln ) = m/^[\[]\"([^\"]*)\" *\"([^\"]*)\"/; + ( $junk, $junk, $junk, $co ) = + m/^[[](nil|\"[^\"]*\") *(nil|\"[^\"]*\") (nil|[(][^)]*[)]) \"([^\"]*)\"/; + + if ( $fn || $ln ) { + $caller = "$fn $ln"; + } + } else { + $buttonp = 1; + } + } + + my $msg = make_message_string($number, $date, $fn, $ln, $co, $error); + pop_up_dialog($msg, $buttonp, $pretty_number); + + if ( $caller ) { + pop_up_bbdb_buffer($caller); + } +} + + +############################################################################## +# +# hey ho. let's go. +# +sub main { + init_modem(); + while (1) { + handle_async_line(read_line()); + } + exit (1); +} + +main(); + |