Mod cidlookup

From FreeSWITCH Wiki

Revision as of 06:45, 22 April 2012 by Avi Marcus (Talk | contribs)
Jump to: navigation, search

Contents

mod_cidlookup (Caller ID Lookup API)

mod_cidlookup allows one to:

  • lookup number->name mapping in a local database
  • lookup number->name mapping from a URL
  • cache the results of the URL lookup in memcache
  • if all else fails, lookup city/state information

Providers that provide number->name lookups can be found here.

Other CallerID / CNAM Resources

Some CNAM providers offer you a way to perform the CNAM DIP via an e164 method, or a SIP Subscribe method.

For information on how to perform the CNAM dip via e164, or enum see this page on Enum / e164 VoIP CNAM Dips.

There is no obvious way to perform the CNAM DIP via the SIP Subscribe method, although that does not mean that it isn't possible.

WARNING!

I have discovered that on occasion retrieval of information from voipcnam.com takes some time to occur. If you perform the CID Lookup before you perform the <action application="answer"/> in your dialplan, this can cause the call to drop and/or behave in other unexpected ways.

In my dialplan, I prefer to perform the CID Lookup before I answer the call so I can filter not only on numbers, but also on the name. I discovered that if you do <action application="pre_answer"/> before performing the CID Lookup, the problem of the carrier giving up on placing the call amongst other problems, that the problems go away.

Requirements

mod_cidlookup will work with any of the modules missing (url, local database, memcache) -- though that part of the functionality will be disabled if one of them is missing.

memcache support requires the mod_memcache module to be loaded.

Installing

To use mod_cidlookup:

Tell FreeSWITCH to compile in this module by editing modules.conf in /usr/src/freeswitch/trunk and uncomment:

#applications/mod_cidlookup

Now go recompile FreeSWITCH...

make ; make install

Tell FreeSWITCH to actually use the cidlookup module when running by adding the module to modules.conf.xml in /usr/local/freeswitch/conf/autoload_configs:

<load module="mod_memcache"/>
<load module="mod_cidlookup"/>

Finally, edit the default config in the autoload_configs directory to hold your cidlookup configuration.

Now load up FreeSWITCH!

CLI

From the commandline issue:

cidlookup status

which would use the options from your config file:

+OK
 url: http://query.voipcnam.com/query.php?api_key=MYAPIKEY&number=${caller_id_number}
 cache: true
 cache-expire: 86400
 odbc-dsn: phone
 sql:  SELECT name||' ('||type||')' AS name    FROM phonebook p JOIN numbers n ON p.id = n.phonebook_id   WHERE n.number='${caller_id_number}'    LIMIT 1
 ODBC Compiled: true
cidlookup 18885551212

might give:

TEST QUERY-3XD0

If you have DEBUG logging turned on, you'll see it trying each step and whether the data is stored in memcache.

This verifies that the module is running and it can load it's configuration information.

Dialplan Application

The cidlookup Application sets the caller_id_name if the lookup succeeds. It sets the original caller id name to the variable original_caller_id_name The easiest way to use it is to put the following block towards the top of your dialplan/public.xml:


    <extension name="cid_number_cleanup" continue="true">
      <condition field="caller_id_number" expression="^(?:\+)(\d+)$">
        <action application="set" data="effective_caller_id_number=$1" inline="true"/>
      </condition>
    </extension>

    <extension name="cid_name_cleanup" continue="true">
      <condition field="caller_id_name" expression="^(?:\+)(\d+)$">
        <action application="set" data="effective_caller_id_name=$1" inline="true"/>
      </condition>
    </extension>

    <extension name="cid_lookup-country_code_1" continue="true">
      <condition field="${module_exists(mod_cidlookup)}" expression="true"/>
      <condition field="caller_id_name" expression="^(?:\+)(\d+)$|^$"/>
      <condition field="caller_id_number" expression="^(?:\+1|1)?([2-9]\d\d[2-9]\d{6})$">
        <action application="cidlookup" data="$1"/>
      </condition>
    </extension>


WARNING: This will cause a lookup if the call comes from "Anonymous" too, but an sql lookup of "blank" (it strips alpha characters it seems) which will return unrelated results. Is there some more elegant way to do the anti-action?


The cidlookup API can also be called from the dialplan using the ${api(args)} method. For example:

<action application="set" data="cid_name=${cidlookup(${caller_id_number})}"/>

A failed lookup will result in a value of "-ERR" -- check that prior to setting effective_caller_id_name.

API

The API can be called via ESL or used directly from fs_cli.

cidlookup status|number [skipurl] [skipcitystate]

Configuration

Parameters:

url
 URL of the service that takes a number and returns as it's only data the name.
 There are several providers that do this or you can implement this yourself.
 Sample config is using the service from voipcnam.

whitepages-apikey

 Whitepages.com offers a free reverse lookup API for low volume use.  If you
 intend to run high volume, they offer a commercial service.  Refer to 
 [developer.whitepages.com] for an API key.
cache
 true|false: use memcache to cache the number->name lookup
cache-expire
 seconds to keep the lookup in the cache
odbc-dsn
 database:user:password
 ODBC Database connection string
sql
 The SQL to execute.  The only parameter supported is ${caller_id_number} which is 
 translated to the number passed to cidlookup.  The query should return a single
 value which is the name.
citystate-sql
 The SQL to execute when falling back to city/state query.  The only parameter supplied is
 ${caller_id_number} which is the number passed to cidlookup.  The query should return a 
 single value which is the name.  This is ONLY used for NANPA numbers.  They must be 11 
 digits and start with 1.  

Sample configuration file:

<configuration name="cidlookup.conf" description="cidlookup Configuration">
  <settings>
    <param name="url" value="http://query.voipcnam.com/query.php?api_key=MYAPIKEY&number=${caller_id_number}"/>
    <param name="cache" value="true"/>
    <param name="cache-expire" value="86400"/>

    <param name="odbc-dsn" value="phone:phone:phone"/>
    <param name="sql" value="
SELECT name||' ('||type||')' AS name
  FROM phonebook p JOIN numbers n ON p.id = n.phonebook_id
  WHERE n.number='${caller_id_number}'
  LIMIT 1
    "/>
    <param name="citystate-sql" value="
    SELECT ratecenter||' '||state as name
      FROM npa_nxx_company_ocn
      WHERE npa = ${caller_id_number:1:3} AND nxx = ${caller_id_number:4:3}
      LIMIT 1
      "/>
  </settings>
</configuration>

Sample schema for PostgreSQL:

phone=> \d phonebook
                           Table "fs.phonebook"
 Column |  Type   |                       Modifiers
--------+---------+--------------------------------------------------------
 id     | integer | not null default nextval('phonebook_id_seq'::regclass)
 name   | text    | not null
 notes  | text    | not null default ''::text
Indexes:
    "phonebook_pkey" PRIMARY KEY, btree (id)

phone=> \d p_numbers
                              Table "fs.p_numbers"
    Column    |  Type   |                       Modifiers                       
--------------+---------+--------------------------------------------------------
 id           | integer | not null default nextval('p_numbers_id_seq'::regclass)
 phonebook_id | integer |
 number       | text    | not null default ''::text
 type         | text    | not null default 'h'::text
Indexes:
    "p_numbers_pkey" PRIMARY KEY, btree (id)
    "i_numbers" btree (number)
Foreign-key constraints:
    "numbers_phonebook_id_fkey" FOREIGN KEY (phonebook_id) REFERENCES phonebook(id) ON DELETE CASCADE

Table creation query for the above schema:

create table phonebook(id serial PRIMARY KEY, name text , notes text default 'h'); create table p_numbers(id serial PRIMARY KEY, phonebook_id integer, number text default , type text default 'h'); create unique index number on p_numbers (number); alter table p_numbers add constraint numbers_phonebook_id_fkey FOREIGN KEY (phonebook_id) REFERENCES phonebook(id) ON DELETE CASCADE;

Caching

If memcache is enabled, the cache will be checked first before doing other lookups. However, only the results of successful URL lookups are cached. Lookups from the "City, State" fallback are not cached.


Falling back to "City State" in the absence of a name

Sometimes it would be nice to give the general location of the number based on telco records. In that case, a bit of setup needs to be done.

The table needs to be loaded into the same database as your cidlookup directory database. The CSV file is available at: npa nxx file

I loaded this data into PostgreSQL with the following ddl:

CREATE TABLE npa_nxx_company_ocn (
    npa smallint NOT NULL,
    nxx smallint NOT NULL,
    company_type text,
    ocn text,
    company_name text,
    lata integer,
    ratecenter text,
    state text
);
CREATE UNIQUE INDEX npanxx_idx ON npa_nxx_company_ocn USING btree (npa, nxx);

You can then load the data using psql with:

phone=> \copy npa_nxx_company_ocn from 'npa-nxx-companytype-ocn.csv' with csv
phone=> select count(*) from npa_nxx_company_ocn ;
 count
--------
 163900
(1 row)

The default config will work with the above data structure. The MySQL equivalent should be pretty simple.

Setting Outbound Name

Some phones (polycom and snom) support setting the callee's name once the call is complete. This allows one to see the name of the person being called as well as the number. Using cidlookup the name could come out of your corporate directory and if that doesn't work will be queried against a cnam lookup service. Assuming you've already normalized your number to e164 (without the leading +), just add the following to your dialplan prior to the bridge:

        <action application="export" data="callee_id_name=${cidlookup($1)}" />

Using PHP to do CID lookups

If you run also a webserver and have PHP on it you can query web directories for the caller id. In the following example I use http://tel.search.ch - a provider to lookup swiss phone numbers. They offer an api with XML output from which the name then will be parsed. For the api you have to get the key. In the case of http://tel.search.ch you can get the key from here: http://admin.tel.search.ch/api/getkey

Prerequisite is the Snoopy class. You can get it from: http://sourceforge.net/projectssnoopy/ . If you download and unpack it the class will be in the Snoopy-VERSION/ folder. Adjust this as required.

<?php                                                                                                  
          
// Get Snoopy Class here:
// http://sourceforge.net/projectssnoopy/                                                                                             
require_once('Snoopy-1.2.4/Snoopy.class.php');                                                         

$cid = $_GET['cid'];

// Check if the supplied number is an integer
if(!is_numeric($cid)) {
        // If it is not an integer, then quit
        exit;
}

// Build the query string. Only one result is needed so position and maxnum is set to 1. Check what your lookup service offers
$q = 'http://tel.search.ch/api/?key=YOURAPIKEY&pos=1&maxnum=1&was=' . $cid;

// Instantiate the class
$snoopy = new Snoopy;
// Make the query
$snoopy->fetch($q);

// Put the result into a variable
$result = $snoopy->results;
// As http://tel.search.ch provides an XML result, you have to filter out the number
$result = explode('<tel:name>', $result);
$result = explode('</tel:name>', $result[1]);
$result = $result[0];

if($result == '') {
        // If there is no result, then assign the cid as result. If you do more checks, then you may want to comment the following line out
         $result = $cid;
} else {
         $result = $result;
}

$result = utf8_encode($result);

echo $result;
                                                                                                       
?>

Save that script somewhere on your server and enhance the freeswitch/conf/autoload_configs/cidlookup.conf.xml with this:

         <param name="url" value="http://localhost/cid/index.php?cid=${caller_id_number}"/>

The above example assumes that you named the PHP script index.php and that it resides in the /cid/ folder from the server.

Note: Snoopy is a very powerful PHP class than can mimic a web browser in just about every way. If a lookup service does not provide an api you could make Snoopy submit a form and retrieve the result there. It's more complicated and requires some work from your side - an api is just a lot more comfortable.

Personal tools

Community
Support FreeSWITCH