Ok, if you use Asterisk (or a Do It Yourself type VoIP service), you sometimes do not receive the CNAM (think Caller ID with Name data) from your carrier. Well, since geeks like information and anything “free,” there’s a simple way to use the internet to do a reverse number lookup query. So, if you’re a technical type, keep reading… otherwise, non-geek posts will return very soon.

So, using a simple Perl script, you can check many resources and attempt to get the calling party’s name. There are tons of caller id lookup scripts out there, but most require the Asterisk Perl AGI module, a localized NPA/NXX database, or a local database query.

Since I don’t like installing the module, don’t want to install a local lookup table, and don’t wish to cache CID names in a database, I’ve modified publicly available scripts as follows (also, check up the write up on Team Forrest, with a little more detail):

Update: AnyWho changed their url and format, the new code is live on Team Forrest but the old code is still below.

#!/usr/bin/perl -w
use strict;
use LWP::UserAgent;
$|=1;
sub trim($);

my %AGI; my $tests = 0; my $fail = 0; my $pass = 0; my $result = ""; my $cidnum = ""; my $cidname = "";
my $npa = ""; my $nxx = ""; my $station = ""; my $name = "";

$cidnum = $ARGV[0];

while(<STDIN>) {
	chomp;
	last unless length($_);
	if (/^agi_(w+):s+(.*)$/) {
		$AGI{$1} = $2;
	}
}

my $AnyWho = '1' ;
my $Google = '1' ;
my $www411 = '1' ;

if(substr($cidnum,0,1) eq '1'){
$cidnum=substr($cidnum,1);
}

if(substr($cidnum,0,2) eq '+1'){
$cidnum=substr($cidnum,2);
}

if ($cidnum =~ /^(d{3})(d{3})(d{4})$/) {
	$npa = $1;
	$nxx = $2;
	$station = $3;
	}
elsif($cidnum =~/<(d{3})(d{3})(d{4})>/){
	$npa = $1;
	$nxx = $2;
	$station = $3;
	}
else {
	print qq(VERBOSE "ERROR: unable to parse caller id" 2n);
	exit(0);
}

if ($AnyWho > '0') {
	print qq(VERBOSE "STATUS: checking AnyWho for name lookup" 2n);
	if ($name = &anywho_lookup ($npa, $nxx, $station)) {
		$cidname = $name;
		print qq(SET VARIABLE CALLERID(name) "$cidname"n);
		print qq(VERBOSE "STATUS: AnyWho said name was $cidname " 2n);
		exit(0);
		}
	else {
		print qq(VERBOSE "STATUS: unable to find name with AnyWho" 2n);
		}
	}
else {
	print qq(VERBOSE "STATUS: AnyWho lookup disabled" 2n);
}

if ($Google > '0') {
	print qq(VERBOSE "STATUS: checking Google for name lookup" 2n);
	if ($name = &google_lookup ($npa, $nxx, $station)) {
		$cidname = $name;
		print qq(SET VARIABLE CALLERID(name) "$cidname"n);
		print qq(VERBOSE "STATUS: Google said name was $cidname " 2n);
		exit(0);
		}
	else {
		print qq(VERBOSE "STATUS: unable to find name with Google" 2n);
		}
	}
else {
	print qq(VERBOSE "STATUS: Google lookup disabled" 2n);
}

if ($www411 > '0') {
	print qq(VERBOSE "STATUS: checking www411 for name lookup" 2n);
	if ($name = &www411_lookup ($npa, $nxx, $station)) {
		$cidname = $name;
		print qq(SET VARIABLE CALLERID(name) "$cidname"n);
		print qq(VERBOSE "STATUS: www411 said name was $cidname " 2n);
		exit(0);
		}
	else {
		print qq(VERBOSE "STATUS: unable to find name with www411" 2n);
		}
	}
else {
	print qq(VERBOSE "STATUS: www411 lookup disabled" 2n);
}

print qq(SET VARIABLE CALLERID(name) "$cidnum"n);
print qq(VERBOSE "STATUS: Unknown name for $cidnum " 2n);
exit(0);

sub anywho_lookup {
	my ($npa, $nxx, $station) = @_;
	my $ua = LWP::UserAgent->new( timeout => 45);
	my $URL = 'http://www.anywho.com/qry/wp_rl';
	$URL .= '?npa=' . $npa . '&telephone=' . $nxx . $station;
	$ua->agent('AsteriskAGIQuery/1');
	my $req = new HTTP::Request GET => $URL;
	my $res = $ua->request($req);
	if ($res->is_success()) {
		if ($res->content =~ /<!-- listing -->(.*)<!-- /listing -->/s) {
			my $listing = $1;
			if ($listing =~ /<B>(.*)</B>/) {
				my $clidname = $1;
				return $clidname;
			}
		}
	}
	return "";
}

sub google_lookup {
  my ($npa, $nxx, $station) = @_;
  my $ua = LWP::UserAgent->new( timeout => 45);
  my $URL = 'http://www.google.com/search?rls=en&q=phonebook:' .  $npa . $nxx . $station . '&ie=UTF-8&oe=UTF-8';
  $ua->agent('AsteriskAGIQuery/1');
  my $req = new HTTP::Request GET => $URL;
  my $res = $ua->request($req);
  if ($res->is_success()) {
    if ($res->content =~ /<font size=-2><br></font><font size=-1>(.+)<font color=green>/) {
      my $temp = $1;
      my $clidname = "";
      if ( $temp =~ /(.+)<font color=green>/o ) {
        $clidname = substr($1, 0, -3);
      } else {
        $clidname = substr($temp, 0, -3);
      }
      if ($clidname =~ /<a href(.+)//) {
        $clidname = $1 ;
        if ($clidname =~ />(.+)</) {
          $clidname = $1 ;
        }
      }
      return $clidname;
    }
  }
  return "";
}

sub www411_lookup {
  my ($npa, $nxx, $station) = @_;
  my $ua = LWP::UserAgent->new( timeout => 45);
  my $URL = 'http://www.411.com/search/Reverse_Phone?phone=' .  $npa . $nxx . $station;
  $ua->agent('AsteriskAGIQuery/1');
  my $req = new HTTP::Request GET => $URL;
  my $res = $ua->request($req);
  if ($res->is_success()) {
    if ($res->content =~ /Location: <strong>(.*)</strong>/s) {
      my $temp = $1;
      my $clidname = "";
      $temp =~ s/&/&/g;
      $temp =~ s/%20/ /g;
						$clidname = $temp;
		    return $clidname;
     }
  }
  return "";
}

Basically, what the script does is lookup the cidname from the CID number. I pass the script an argument (the caller id number) and it checks AnyWho, Google, and 411.com (in order). I chose 411 last and just use it to replace an NPA/NXX table. Basically, if AnyWho and Google fail, I’m assuming it’s a cell phone or unlisted number and will assume 411 will only return a location. If all fails, the script sets the callerid name to be the callerid number.

I’ve integrated it into asterisk by first creating a lookup context:

[cidname-lookup]
exten => s,1,NoOp(looking up callerid name)
exten => s,n,GotoIf($["foo${CALLERID(NAME)}" = "foo" ]?getname)
exten => s,n,GotoIf($["${CALLERID(NAME)}" = "${CALLERID(NUM)}" ]?getname)
exten => s,n,NoOp(caller id name exists as ${CALLERID(NAME)})
exten => s,n,Return
exten => s,n(getname),AGI(calleridname.pl,${CALLERID(NUM)})
exten => s,n,NoOp(Caller ID Name is now ${CALLERID(NAME)})
exten => s,n,Return

Basically, if the callerid name is present (and not the phonenumber), run the script. You can then call the script from anywhere in your Asterisk dialplan using a gosub routine. Such as:

exten => s,n,Gosub(cidname-lookup,s,1)
exten => s,n,dial(${FREDPHONE},30,mt)

Are there other ways to do this? Absolutely. And some even better 🙂 This way works for me… until I see a better way of doing it.