Howto add custom (agi) callerid lookup source

Hi,

I used to use a plain vanilla Asterisk server, but switched recently to freepbx. I have a custom script, which does CallerID lookup from LDAP. This is an AGI script.

I can add CallerID lookup Sources, but I can only choose from the following types:

Internal
ENUM
HTTP
MySQL
SugarCRM

I expected ‘Internal’ to be agi, but my scriptname does not popup in the config (grep scriptname /etc/asterisk/*)

Is this possible, and if so, how?

I am willing to share my little script, but I do not know how to contribute.

Thanks for any reply.

Marcel

PS. I’m using FreePBX 2.9.0.7 + Asterisk 1.6.2.9 on Debian squeeze

Marcel:

Caller ID Superfecta is a project that looks up names from CID for dozens of sources, including LDAP. Please consider sharing your work there as well.

https://github.com/tm1000/Caller-ID-Superfecta

Hi lgaetz,

Nice to know, but:

  1. Where can I get this module? It is not available under module manager…
  2. The perl-script is not very fancy (just ~90 lines long). How do I contribute?

Marcel

CID Superfecta is a 3rd party module not hosted by FreePBX. Download and install directions can be found here: http://pbxinaflash.com/forum/showthread.php?t=10977

The module was developed and tested for PBX in a flash and is known to work with other distributions, but I don’t know that it has (yet!) been tested on the FreePBX distribution, it may or may not be compatible on your system.

Also, in your first post, I missed the fact that this is a perl/agi script; Superfecta is all PHP. It still may be useful for the project tho. If you don’t have github login credentials, post it here. Place your script between code tags so the forum doesn’t mangle it.

Here’s the script. I’ve replaced my personal info for placeholders, but that’s pretty straightforward. It is not the prettiest way of programming, but it does have some comments :slight_smile:

#!/usr/bin/perl use strict; # be picky with the synatax use Net::LDAP;

use constant LDAPSERVER => “ldaps://ldapserver.domain.tld”; # ldap URI
use constant LDAPTIMEOUT=> 2; # timeout in seconds
use constant BASEDN => “dc=domain,dc=tld”;
use constant BINDDN => “uid=clid2name,ou=roles,dc=domain,dc=tld”;
use constant BINDPW => “averystrongsecret”;
use constant FILTER => ‘(|(mobile=%s)(telephoneNumber=%s)(homePhone=%s))’; # filter to search for
use constant AGIVAR => ‘callerid’; # the agi variable to use
use constant AGIDEF => ‘calleridname’; # the agi variable to use when nothing returned
use constant MAXNATNUM => 10; # maximum digits in national number (no country code)
use constant THISCOUNTRY=> 31; # country code
use constant LOCALPREFIX=> 0; # local prefix code (replaces country code)
use constant DEBUG => 0; # 0=no debug, 1=debug output to STDERR (asterisk console)

$|=1; # no buffer for the output
my %AGI; # to store the variables from asterisk
my $name; # to hold the resulting name

while () { # read all agi variables
chomp;
last unless length($);
if (/^agi
(\w+):\s+(.*)$/) {
$AGI{$1}=$2;
}
}
if (DEBUG) { # dump all variables
print STDERR “AGI Environment Dump:\n”;
foreach my $i (sort keys %AGI) {
print STDERR " – $i = $AGI{$i}\n";
}
}

sub normalizeclid { # To convert different types of CLI to type in LDAP
my ($res) = @;
my $x = THISCOUNTRY;
my $y = LOCALPREFIX;
(DEBUG) && print STDERR “NormalizeCLID: input is $res\n”;
if (length($res) > MAXNATNUM ) {
$res =~ s/^$x/$y/;
}
(DEBUG) && print STDERR “NormalizeCLID: output is $res\n”;
return($res);
}
sub checkresult { # process response of Asterisk
my ($res) = @
;
my $retval;
chomp $res;
if ($res=~ /^200/) {
$res =~ /result=(-?\d+)/;
if (!length($1)) {
print STDERR “FAIL ($res)\n”;
} else {
print STDERR “PASS ($1)\n”;
}
} else {
print STDERR “FAIL (uexpected result ‘$res’)\n”;
}
}
my $id = AGIVAR;
my $id2= AGIDEF;
if (!$AGI{$id}) { # check if callerid is available
$AGI{$id} = “UNKNOWN” ; # a default value (when missing/empty);
} else {

$AGI{$id}=&normalizeclid ($AGI{$id}); # replace ‘31’ prefix with '0’
my @filterargs;
my $filtervars = FILTER =~ tr/%/%/;
for (my $i = 0; $i < $filtervars; $i++) { $filterargs[$i] = $AGI{$id} }
my $ldap = Net::LDAP->new(LDAPSERVER, timeout => LDAPTIMEOUT) or die “$@”;
my $mesg = $ldap->bind(BINDDN, password => BINDPW) or die “$@”;
my $filter = sprintf(FILTER,@filterargs);
(DEBUG) && print STDERR “FILTER=$filter\n”;
$mesg = $ldap->search(base => BASEDN, filter => $filter, attrs => [‘cn’,‘o’] );

$mesg->code && die $mesg->error;

if ($mesg->count >0) {
my $result = $mesg->entry(0);
$name = $result->get_value(‘cn’) ." (".$result->get_value(‘o’).")";
} else {
($AGI{$id2} ) ? $name="(not in Directory)" : $name=“SIP:”.$AGI{$id2};
}
$mesg = $ldap->unbind;
}

print STDOUT “SET CALLERID “$name”<$AGI{$id}>\n”;
my $result = ;
&checkresult($result);

I added a link to this thread on the Superfecta dev site.

I assume you are running the FreePBX distro, if so and if you ever get Superfecta running, report back with your success or failure please. I don’t know of any testing that was done on that distribution.

No, I do not use the distro. I’m using FreePBX 2.9.0.7 + Asterisk 1.6.2.9 on Debian squeeze (see the initial post).

I tried Superfecta, but the ldap source says it is unable to bind. My script however, running from the same box, is able to connect. I tried with both ldap and ldaps, but to no avail.

But your script did show me how the HTTP method works. So I’ve adapted my script (mostly deletions, a few changes), and now it uses HTTP as the delivery method. The whole LDAP part is unchanged, but see for yourself:

#!/usr/bin/perl use strict; # be picky with the synatax use Net::LDAP; use CGI::Simple;

use constant LDAPSERVER => “ldaps://ldapserver.domain.tld”; # ldap URI
use constant LDAPTIMEOUT=> 2; # timeout in seconds
use constant BASEDN => “dc=domain,dc=tld”;
use constant BINDDN => “uid=clid2name,ou=roles,dc=domain,dc=tld”;
use constant BINDPW => “averystrongsecret”;
use constant FILTER => ‘(|(mobile=%s)(telephoneNumber=%s)(homePhone=%s))’; # filter to search for
use constant LOOKUP => ‘callerid’; # the variable to use
use constant MAXNATNUM => 10; # maximum digits in national number (no country code)
use constant THISCOUNTRY=> 31; # country code
use constant LOCALPREFIX=> 0; # local prefix code (replaces country code)
use constant DEBUG => 1; # 0=no debug, 1=debug output to STDERR (asterisk console)

$|=1; # no buffer for the output
my $name; # to hold the resulting name
my $q = new CGI::Simple; # get options from query

sub normalizeclid { # To convert different types of CLI to type in LDAP
my ($res) = @_;
my $x = THISCOUNTRY;
my $y = LOCALPREFIX;
(DEBUG) && print STDERR “NormalizeCLID: input is $res\n”;
if (length($res) > MAXNATNUM ) {
$res =~ s/^$x/$y/;
}
(DEBUG) && print STDERR “NormalizeCLID: output is $res\n”;
return($res);
}

my $id = $q->param(LOOKUP);
if (!$id) { # check if callerid is available
$id = “UNKNOWN” ; # a default value (when missing/empty);
} else {

$id=&normalizeclid ($id); # replace ‘31’ prefix with '0’
my @filterargs;
my $filtervars = FILTER =~ tr/%/%/;
for (my $i = 0; $i < $filtervars; $i++) { $filterargs[$i] = $id }
my $ldap = Net::LDAP->new(LDAPSERVER, timeout => LDAPTIMEOUT) or die “$@”;
my $mesg = $ldap->bind(BINDDN, password => BINDPW) or die “$@”;
my $filter = sprintf(FILTER,@filterargs);
(DEBUG) && print STDERR “FILTER=$filter\n”;
$mesg = $ldap->search(base => BASEDN, filter => $filter, attrs => [‘cn’,‘o’] );

$mesg->code && die $mesg->error;

if ($mesg->count >0) {
my $result = $mesg->entry(0);
$name = $result->get_value(‘cn’) ." (".$result->get_value(‘o’).")";
} else {
$name=“UNKNOWN”;
}
$mesg = $ldap->unbind;
}

printf ("%s%s", $q->header(), $name);

The whole AGI-interface handling is removed. It is also easy to supply all settings in the query (just use the q->param method a few more times), but for me this will do.

Hope this is helpfull for others. It works in my setup.

Marcel

Glad you got it working. If Superfecta generally works but LDAP doesn’t it could be that you are missing php-ldap.

It was missing, but then there is a nice message saying so. After adding php5-ldap, the bind fails.

I did not investigate further, as abovementioned solution works for me.

Thanks,

Marcel