Difference between revisions of "Telephone scripts"

From Hackerspace ACKspace
Jump to: navigation, search
(Created page with "== lookup.php == This script returns some information about the caller, preferrably the name. It uses a local MySQL database and fetches info from some sites.")
 
(added number lookup script)
Line 1: Line 1:
== lookup.php ==
+
== Number lookup ==
 +
We make use of the mod_cidlookup for ease of use, but not all features are functional in our implementation.
 +
Every request is done via HTTP requests to a php script that will check if the number:
 +
* is an extension (and returns the name for that)
 +
* is stored in the local mySQL database, and return that
 +
* can be found online by using reversed number lookup websites for landlines
 +
* can be found online by using the national telecommunications authority database (opta.nl) for cell phones returning the associated cell provider
 +
* can be categorized by a more coarse lookup, like continent, country, region, town or number block owner, stored in a local array (~500 entries)
 +
 
 +
The setting for mod_cidlookup is:
 +
<param name="url" value="http://webserviceprovider/lookup.php?number=${caller_id_number}"/>
 +
 
 +
=== lookup.php ===
 
This script returns some information about the caller, preferrably the name.
 
This script returns some information about the caller, preferrably the name.
 
It uses a local MySQL database and fetches info from some sites.
 
It uses a local MySQL database and fetches info from some sites.
 +
 +
<pre>
 +
<?php
 +
/*
 +
* Copyright (c) 2012, ACKspace foundation
 +
* All rights reserved.
 +
*
 +
* Redistribution and use in source and binary forms, with or without
 +
* modification, are permitted provided that the following conditions are met:
 +
*
 +
* 1. Redistributions of source code must retain the above copyright notice, this
 +
*    list of conditions and the following disclaimer.
 +
* 2. Redistributions in binary form must reproduce the above copyright notice,
 +
*    this list of conditions and the following disclaimer in the documentation
 +
*    and/or other materials provided with the distribution.
 +
*
 +
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 +
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 +
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 +
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 +
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 +
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 +
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 +
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 +
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 +
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 +
*
 +
* The views and conclusions contained in the software and documentation are those
 +
* of the authors and should not be interpreted as representing official policies,
 +
* either expressed or implied, of the FreeBSD Project.
 +
*/
 +
 +
define( "COUNTRY", "31" );
 +
define( "REGION", "45" );
 +
define( "PLUS", "00" );
 +
define( "MYSQL_DB", "freeswitch_cidlookup" );
 +
 +
// Number dial plan, ~500 entries, shortened for wiki
 +
$arrNumbers[1]['info'] = "(continent) Verenigde Staten";
 +
$arrNumbers[1][4][4][1]['info'] = "Bermuda";
 +
$arrNumbers[3]['info'] = "(continent) Europa";
 +
$arrNumbers[3][1]['info'] = "(land) Nederland";
 +
$arrNumbers[3][1][1][4]['info'] = "Testnetnummer van KPN Telecom";
 +
$arrNumbers[3][1][4]['info'] = "(provincies) Oostelijk Noord-Brabant, Limburg";
 +
$arrNumbers[3][1][4][5]['info'] = "(regio) Heerlen";
 +
$arrNumbers[3][1][6]['info'] = "Mobiele nummers en Semafoondiensten";
 +
$arrNumbers[3][1][6][1]['info'] = "Mobiele telefoon";
 +
$arrNumbers[3][1][6][1][0]['info'] = "(GSM) KPN";
 +
$arrNumbers[3][1][8][5]['info'] = "(type) Plaatsonafhankelijk/VoIP";
 +
$arrNumbers[3][1][8][5][8][7]['info'] = "(VoIP) XS4ALL";
 +
 +
// Normalize the number
 +
$arrNumberInfo = normalizeNumber( getVar( "number", true ) );
 +
 +
// Extension? try and get from dialplan
 +
if ( $arrNumberInfo['type'] == "extension" )
 +
{
 +
    echo getExtension( $arrNumberInfo['local'] );
 +
    exit;
 +
}
 +
 +
if ( !function_exists( "mysql_connect" ))
 +
{
 +
    echo "ERROR";
 +
    exit;
 +
}
 +
 +
// Fetch the number from the database
 +
if ( $strName = dbLookup( $arrNumberInfo ))
 +
{
 +
    echo $strName;
 +
    exit;
 +
}
 +
 +
// Nothing in the database?
 +
// national number starting with 0[1-578]?
 +
// Fetch number from website (check last get timestamp to prevent DoS
 +
// put result in DB
 +
if ( preg_match( "/^0[1-578].*/", $arrNumberInfo['national'] ) && $strName = fetchWebsiteResult( $arrNumberInfo['national'] ))
 +
{
 +
    echo $strName;
 +
 +
    // Add the name to the DB
 +
    $arrNumberInfo['name'] = $strName;
 +
    dbInsert( $arrNumberInfo );
 +
    exit;
 +
}
 +
else if ( preg_match( "/^0[6].*/", $arrNumberInfo['national'] ) && $strName = fetchOptaResult( $arrNumberInfo['national'] ))
 +
{
 +
    // Number porting
 +
    echo $strName;
 +
 +
    // Add the name to the DB, so we don't have to look it up anymore
 +
    $arrNumberInfo['name'] = $strName;
 +
    dbInsert( $arrNumberInfo );
 +
    exit;
 +
}
 +
 +
// Nothing on the website? Try and find the number in the array
 +
if ( isset( $arrNumberInfo['international'] ))
 +
{
 +
    $strName = _getInfo( str_split( $arrNumberInfo['international'] ));
 +
    echo $strName;
 +
    exit;
 +
}
 +
 +
/////////////////////////////////////////////////////////////////////
 +
function fetchWebsiteResult( $_strNumber )
 +
{
 +
 +
    if ( $strName = fetch_delefoondetective_nl( $_strNumber ))
 +
        return $strName;
 +
 +
    if ( $strName = fetch_gevonden_cc( $_strNumber ))
 +
        return $strName;
 +
 +
    if ( $strName = fetch_zoekenbel_nl( $_strNumber ))
 +
        return $strName;
 +
 +
    if ( $strName = fetch_nummerzoeker_com( $_strNumber ))
 +
        return $strName;
 +
 +
    if ( $strName = fetch_nummerid_com( $_strNumber ))
 +
        return $strName;
 +
 +
    if ( $strName = fetch_gebeld_nl( $_strNumber ))
 +
        return $strName;
 +
 +
    return false;
 +
}
 +
 +
function fetchOptaResult( $_strNumber )
 +
{
 +
    $strPage = file_get_contents( "http://www.opta.nl/nl/nummers/nummers-zoeken/resultaat/?query=".$_strNumber."&page=1&portering=1" );
 +
 +
    if ( !preg_match( "/<strong>Huidige aanbieder<\/strong>.*?<p>(.*?)<\/p>/si", $strPage, $matches ))
 +
        return false;
 +
 +
    return "(GSM) ".$matches[1];
 +
}
 +
 +
function fetch_delefoondetective_nl( $_strNumber )
 +
{
 +
    $strPage = file_get_contents( "http://www.telefoondetective.nl/telefoonnummer/".$_strNumber."/" );
 +
 +
    if ( !preg_match( "/<div\sid=\"name\"><h\d>(.*?)<\/h\d><\/div>/i", $strPage, $matches ))
 +
        return false;
 +
 +
    return $matches[1];
 +
}
 +
 +
function fetch_gevonden_cc( $_strNumber )
 +
{
 +
    return false;
 +
}
 +
 +
function fetch_zoekenbel_nl( $_strNumber )
 +
{
 +
    return false;
 +
}
 +
 +
function fetch_nummerzoeker_com( $_strNumber )
 +
{
 +
    return false;
 +
}
 +
 +
 +
function fetch_nummerid_com( $_strNumber )
 +
{
 +
    return false;
 +
}
 +
 +
function fetch_gebeld_nl( $_strNumber )
 +
{
 +
    return false;
 +
}
 +
 +
function dbLookup( $_arrNumberInfo )
 +
{
 +
    if (!$db = mysql_connect( NULL, "username", "password" ))
 +
        return false;
 +
 +
    // TODO: close db
 +
    if (!mysql_select_db( MYSQL_DB, $db ))
 +
        return false;
 +
 +
    // Prevent SQL injection on variables
 +
    $country = mysql_real_escape_string( $_arrNumberInfo['country'] );
 +
    $international = mysql_real_escape_string( $_arrNumberInfo['international'] );
 +
 +
    // Full number partial listing (experimental)
 +
    $query = "SELECT name FROM telephone_names WHERE country_code=".$country." AND INSTR( '".$international."', number ) = 1 ORDER BY LENGTH(number), sortorder LIMIT 1";
 +
 +
    if (!$result = mysql_query( $query, $db ))
 +
        return false;
 +
 +
    $row = mysql_fetch_row( $result );
 +
    mysql_close( $db );
 +
    return $row[0];
 +
}
 +
 +
 +
function dbInsert( $_arrNumberInfo )
 +
{
 +
    if (!$db = mysql_connect( NULL, "username", "password" ))
 +
        return false;
 +
 +
    // TODO: close db
 +
    if (!mysql_select_db( "freeswitch_cidlookup", $db ))
 +
        return false;
 +
 +
    // Prevent SQL injection on variables
 +
    $country = mysql_real_escape_string( $_arrNumberInfo['country'] );
 +
    $international = mysql_real_escape_string( $_arrNumberInfo['international'] );
 +
    $name = mysql_real_escape_string( $_arrNumberInfo['name'] );
 +
 +
    $query = "INSERT INTO telephone_names (country_code,number,name) VALUES (".$country.",'".$international."','".$name."')";
 +
 +
    if (!$result = mysql_query( $query, $db ))
 +
    {
 +
        echo mysql_error( $db );
 +
        return false;
 +
    }
 +
 +
    mysql_close( $db );
 +
 +
    return true;
 +
}
 +
 +
 +
function getExtension( $_strExtension )
 +
{
 +
    return false;
 +
    //return "Ext. ".$_strExtension;
 +
}
 +
 +
function normalizeNumber( $_strNumber )
 +
{
 +
    $arrInfo = array();
 +
 +
    if ( preg_match( "/^([19]\d+)/", $_strNumber, $matches ))
 +
    {
 +
        $arrInfo['local'] = $matches[1];
 +
        $arrInfo['type'] = 'extension';
 +
        return $arrInfo;
 +
    }
 +
 +
    $_strNumber = preg_replace( "/^([2345678])/", COUNTRY.REGION.'$1', $_strNumber );
 +
 +
    // Add country on national dials
 +
    $_strNumber = preg_replace( "/^(0)([^0].*)/", COUNTRY.'$2', $_strNumber );
 +
 +
    // Replace + and 00 international symbols before parsing (include space for URI conversion
 +
    $_strNumber = preg_replace( "/^( |\+|00)/", '', $_strNumber );
 +
 +
    $arrInfo['country'] = intval( substr( $_strNumber, 0, 2 ));
 +
    $arrInfo['international'] = $_strNumber;
 +
    $arrInfo['type'] = 'international';
 +
 +
    // National number?
 +
    if ( $arrInfo['country'] == intval( COUNTRY ))
 +
    {
 +
        // only works with countries of 2 digits; replaces it with a 0
 +
        $arrInfo['national'] = "0".substr( $_strNumber, 2 );
 +
        $arrInfo['type'] = 'national';
 +
    }
 +
 +
    return $arrInfo;
 +
};
 +
 +
function GetInfo( $_strNumber )
 +
{
 +
    global $arrNumbers;
 +
 +
    $_strNumber = preg_replace( "/^([2345678])/", COUNTRY.REGION.'$1', $_strNumber );
 +
 +
    // Add country on national dials
 +
    $_strNumber = preg_replace( "/^(0)([^0].*)/", COUNTRY.'$2', $_strNumber );
 +
 +
    // Replace + and 00 international symbols before parsing
 +
    $_strNumber = preg_replace( "/^(\+|00)/", '', $_strNumber );
 +
 +
    return _getInfo( str_split( $_strNumber ));
 +
};
 +
 +
function _getInfo( $_arrNumber )
 +
{
 +
    global $arrNumbers;
 +
 +
    $arrInfo = array();
 +
 +
    $arrNumberInfo = $arrNumbers;
 +
    $nIndent = 0;
 +
    $strDigits = "";
 +
    $strDetailedInfo = "";
 +
    foreach ( $_arrNumber as $digit )
 +
    {
 +
        if ( isset( $arrNumberInfo[$digit] ) )
 +
        {
 +
            $strDigits .= $digit;
 +
            $arrNumberInfo = $arrNumberInfo[$digit];
 +
            if ( isset( $arrNumberInfo['info'] ) )
 +
            {
 +
                $arrInfo[] = str_repeat( "-", $nIndent ) . $strDigits . " " . $arrNumberInfo['info'];
 +
                $strDigits = "";
 +
                $strDetailedInfo = $arrNumberInfo['info'];
 +
            }
 +
            $nIndent++;
 +
        } else {
 +
            break;
 +
        }
 +
    }
 +
 +
    return $strDetailedInfo;
 +
}
 +
 +
////////////////////////////////////////////////////////////////////////////////
 +
// Helpers
 +
////////////////////////////////////////////////////////////////////////////////
 +
function getVar( $_strVarName, $_bAllowGet = false )
 +
{
 +
    // If _POST var is set, return _POST var,
 +
    // else, if _GET var is set and is allowed, return _GET var
 +
    // else, requested var not found: return NULL
 +
    if ( isset( $_POST[ $_strVarName ] ) )
 +
        return $_POST[ $_strVarName ];
 +
    else if ( isset( $_GET[ $_strVarName ] ) && ($_bAllowGet == true) )
 +
        return $_GET[ $_strVarName ];
 +
    else
 +
        return NULL;
 +
}
 +
?>
 +
</pre>
 +
 +
=== dialing out ===
 +
This default dialplan snippet does a caller id lookup, and updates the callee name which will be visible on the local extension
 +
 +
<pre>
 +
<extension name="National_numbers">
 +
    <condition field="destination_number" expression="^0([1-578]\d{8})$">
 +
        <action application="set" data="effective_caller_id_number=${outbound_caller_id}"/>
 +
        <action application="export" data="callee_id_name=${cidlookup(0031$1)}" />
 +
        <action application="bridge" data="sofia/gateway/myLandLineProvider/31$1"/>
 +
    </condition>
 +
</extension>
 +
</pre>
 +
 +
=== incoming calls ===
 +
This public dialplan snippet somewhat at the top sets the number (if any) first, checks if it has an international prefix, does a lookup for incoming calls and will set the name accordingly.
 +
 +
The second part will strip any leading + sign
 +
 +
<pre>
 +
<extension name="fix_cidnam" continue="true">
 +
    <!--make sure the module is loaded, or else loading it will kill our call!-->
 +
    <!-- Simple case: name=number or name is empty -->
 +
    <!-- and number is a 10digit (excluding optional leading 1), in nanpa nxx-nxx-xxxx form -->
 +
    <!-- will skipurl lookup if not a 10digit # (don't lookup INTL), and instead just query the SQL -->
 +
    <condition field="${module_exists(mod_cidlookup)}" expression="true"/>
 +
    <condition field="caller_id_name" expression="^${caller_id_number}$|^$"/>
 +
    <condition field="caller_id_number" expression="^(\+|00)(\d+)$">
 +
        <action application="cidlookup" data="00$2"/>
 +
        <anti-action application="cidlookup" data="${caller_id_number}"/>
 +
    </condition>
 +
</extension>
 +
 +
<extension name="fix_cidnam_plus" continue="true">
 +
    <!-- if the name starts with + followed by digits, strip the
 +
        + and then pass the number -->
 +
    <condition field="caller_id_name" expression="^\+(1[2-9]\d\d[2-9]\d{6})$">
 +
        <action application="cidlookup" data="$1"/>
 +
    </condition>
 +
</extension>
 +
</pre>
 +
 +
=== todo ===
 +
items stored in the database will not be updated anymore. The only way to refresh the number's information is to remove the entry manually which will cause a new lookup the next time that number is requested.

Revision as of 14:31, 23 June 2012

Number lookup

We make use of the mod_cidlookup for ease of use, but not all features are functional in our implementation. Every request is done via HTTP requests to a php script that will check if the number:

  • is an extension (and returns the name for that)
  • is stored in the local mySQL database, and return that
  • can be found online by using reversed number lookup websites for landlines
  • can be found online by using the national telecommunications authority database (opta.nl) for cell phones returning the associated cell provider
  • can be categorized by a more coarse lookup, like continent, country, region, town or number block owner, stored in a local array (~500 entries)

The setting for mod_cidlookup is:

<param name="url" value="http://webserviceprovider/lookup.php?number=${caller_id_number}"/>

lookup.php

This script returns some information about the caller, preferrably the name. It uses a local MySQL database and fetches info from some sites.

<?php
/*
 * Copyright (c) 2012, ACKspace foundation
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies, 
 * either expressed or implied, of the FreeBSD Project.
 */

define( "COUNTRY", "31" );
define( "REGION", "45" );
define( "PLUS", "00" );
define( "MYSQL_DB", "freeswitch_cidlookup" );

// Number dial plan, ~500 entries, shortened for wiki
$arrNumbers[1]['info'] = "(continent) Verenigde Staten";
$arrNumbers[1][4][4][1]['info'] = "Bermuda";
$arrNumbers[3]['info'] = "(continent) Europa";
$arrNumbers[3][1]['info'] = "(land) Nederland";
$arrNumbers[3][1][1][4]['info'] = "Testnetnummer van KPN Telecom";
$arrNumbers[3][1][4]['info'] = "(provincies) Oostelijk Noord-Brabant, Limburg";
$arrNumbers[3][1][4][5]['info'] = "(regio) Heerlen";
$arrNumbers[3][1][6]['info'] = "Mobiele nummers en Semafoondiensten";
$arrNumbers[3][1][6][1]['info'] = "Mobiele telefoon";
$arrNumbers[3][1][6][1][0]['info'] = "(GSM) KPN";
$arrNumbers[3][1][8][5]['info'] = "(type) Plaatsonafhankelijk/VoIP";
$arrNumbers[3][1][8][5][8][7]['info'] = "(VoIP) XS4ALL";

// Normalize the number
$arrNumberInfo = normalizeNumber( getVar( "number", true ) );

// Extension? try and get from dialplan
if ( $arrNumberInfo['type'] == "extension" )
{
    echo getExtension( $arrNumberInfo['local'] );
    exit;
}

if ( !function_exists( "mysql_connect" ))
{
    echo "ERROR";
    exit;
}

// Fetch the number from the database
if ( $strName = dbLookup( $arrNumberInfo ))
{
    echo $strName;
    exit;
}

// Nothing in the database?
// national number starting with 0[1-578]?
// Fetch number from website (check last get timestamp to prevent DoS
// put result in DB
if ( preg_match( "/^0[1-578].*/", $arrNumberInfo['national'] ) && $strName = fetchWebsiteResult( $arrNumberInfo['national'] ))
{
    echo $strName;

    // Add the name to the DB
    $arrNumberInfo['name'] = $strName;
    dbInsert( $arrNumberInfo );
    exit;
}
else if ( preg_match( "/^0[6].*/", $arrNumberInfo['national'] ) && $strName = fetchOptaResult( $arrNumberInfo['national'] ))
{
    // Number porting
    echo $strName;

    // Add the name to the DB, so we don't have to look it up anymore
    $arrNumberInfo['name'] = $strName;
    dbInsert( $arrNumberInfo );
    exit;
}

// Nothing on the website? Try and find the number in the array
if ( isset( $arrNumberInfo['international'] ))
{
    $strName = _getInfo( str_split( $arrNumberInfo['international'] ));
    echo $strName;
    exit;
}

/////////////////////////////////////////////////////////////////////
function fetchWebsiteResult( $_strNumber )
{

    if ( $strName = fetch_delefoondetective_nl( $_strNumber ))
        return $strName;

    if ( $strName = fetch_gevonden_cc( $_strNumber ))
        return $strName;

    if ( $strName = fetch_zoekenbel_nl( $_strNumber ))
        return $strName;

    if ( $strName = fetch_nummerzoeker_com( $_strNumber ))
        return $strName;

    if ( $strName = fetch_nummerid_com( $_strNumber ))
        return $strName;

    if ( $strName = fetch_gebeld_nl( $_strNumber ))
        return $strName;

    return false;
}

function fetchOptaResult( $_strNumber )
{
    $strPage = file_get_contents( "http://www.opta.nl/nl/nummers/nummers-zoeken/resultaat/?query=".$_strNumber."&page=1&portering=1" );

    if ( !preg_match( "/<strong>Huidige aanbieder<\/strong>.*?<p>(.*?)<\/p>/si", $strPage, $matches ))
        return false;

    return "(GSM) ".$matches[1];
}

function fetch_delefoondetective_nl( $_strNumber )
{
    $strPage = file_get_contents( "http://www.telefoondetective.nl/telefoonnummer/".$_strNumber."/" );

    if ( !preg_match( "/<div\sid=\"name\"><h\d>(.*?)<\/h\d><\/div>/i", $strPage, $matches ))
        return false;

    return $matches[1];
}

function fetch_gevonden_cc( $_strNumber )
{
    return false;
}

function fetch_zoekenbel_nl( $_strNumber )
{
    return false;
}

function fetch_nummerzoeker_com( $_strNumber )
{
    return false;
}


function fetch_nummerid_com( $_strNumber )
{
    return false;
}

function fetch_gebeld_nl( $_strNumber )
{
    return false;
}

function dbLookup( $_arrNumberInfo )
{
    if (!$db = mysql_connect( NULL, "username", "password" ))
        return false;

    // TODO: close db
    if (!mysql_select_db( MYSQL_DB, $db ))
        return false;

    // Prevent SQL injection on variables
    $country = mysql_real_escape_string( $_arrNumberInfo['country'] );
    $international = mysql_real_escape_string( $_arrNumberInfo['international'] );

    // Full number partial listing (experimental)
    $query = "SELECT name FROM telephone_names WHERE country_code=".$country." AND INSTR( '".$international."', number ) = 1 ORDER BY LENGTH(number), sortorder LIMIT 1";

    if (!$result = mysql_query( $query, $db ))
        return false;

    $row = mysql_fetch_row( $result );
    mysql_close( $db );
    return $row[0];
}


function dbInsert( $_arrNumberInfo )
{
    if (!$db = mysql_connect( NULL, "username", "password" ))
        return false;

    // TODO: close db
    if (!mysql_select_db( "freeswitch_cidlookup", $db ))
        return false;

    // Prevent SQL injection on variables
    $country = mysql_real_escape_string( $_arrNumberInfo['country'] );
    $international = mysql_real_escape_string( $_arrNumberInfo['international'] );
    $name = mysql_real_escape_string( $_arrNumberInfo['name'] );

    $query = "INSERT INTO telephone_names (country_code,number,name) VALUES (".$country.",'".$international."','".$name."')";

    if (!$result = mysql_query( $query, $db ))
    {
        echo mysql_error( $db );
        return false;
    }

    mysql_close( $db );

    return true;
}


function getExtension( $_strExtension )
{
    return false;
    //return "Ext. ".$_strExtension;
}

function normalizeNumber( $_strNumber )
{
    $arrInfo = array();

    if ( preg_match( "/^([19]\d+)/", $_strNumber, $matches ))
    {
        $arrInfo['local'] = $matches[1];
        $arrInfo['type'] = 'extension';
        return $arrInfo;
    }

    $_strNumber = preg_replace( "/^([2345678])/", COUNTRY.REGION.'$1', $_strNumber );

    // Add country on national dials
    $_strNumber = preg_replace( "/^(0)([^0].*)/", COUNTRY.'$2', $_strNumber );

    // Replace + and 00 international symbols before parsing (include space for URI conversion
    $_strNumber = preg_replace( "/^( |\+|00)/", '', $_strNumber );

    $arrInfo['country'] = intval( substr( $_strNumber, 0, 2 ));
    $arrInfo['international'] = $_strNumber;
    $arrInfo['type'] = 'international';

    // National number?
    if ( $arrInfo['country'] == intval( COUNTRY ))
    {
        // only works with countries of 2 digits; replaces it with a 0
        $arrInfo['national'] = "0".substr( $_strNumber, 2 );
        $arrInfo['type'] = 'national';
    }

    return $arrInfo;
};

function GetInfo( $_strNumber )
{
    global $arrNumbers;

    $_strNumber = preg_replace( "/^([2345678])/", COUNTRY.REGION.'$1', $_strNumber );

    // Add country on national dials
    $_strNumber = preg_replace( "/^(0)([^0].*)/", COUNTRY.'$2', $_strNumber );

    // Replace + and 00 international symbols before parsing
    $_strNumber = preg_replace( "/^(\+|00)/", '', $_strNumber );

    return _getInfo( str_split( $_strNumber ));
};

function _getInfo( $_arrNumber )
{
    global $arrNumbers;

    $arrInfo = array();

    $arrNumberInfo = $arrNumbers;
    $nIndent = 0;
    $strDigits = "";
    $strDetailedInfo = "";
    foreach ( $_arrNumber as $digit )
    {
        if ( isset( $arrNumberInfo[$digit] ) )
        {
            $strDigits .= $digit;
            $arrNumberInfo = $arrNumberInfo[$digit];
            if ( isset( $arrNumberInfo['info'] ) )
            {
                $arrInfo[] = str_repeat( "-", $nIndent ) . $strDigits . " " . $arrNumberInfo['info'];
                $strDigits = "";
                $strDetailedInfo = $arrNumberInfo['info'];
            }
            $nIndent++;
        } else {
            break;
        }
    }

    return $strDetailedInfo;
}

////////////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////////////
function getVar( $_strVarName, $_bAllowGet = false )
{
    // If _POST var is set, return _POST var,
    // else, if _GET var is set and is allowed, return _GET var
    // else, requested var not found: return NULL
    if ( isset( $_POST[ $_strVarName ] ) )
        return $_POST[ $_strVarName ];
    else if ( isset( $_GET[ $_strVarName ] ) && ($_bAllowGet == true) )
        return $_GET[ $_strVarName ];
    else
        return NULL;
}
?>

dialing out

This default dialplan snippet does a caller id lookup, and updates the callee name which will be visible on the local extension

<extension name="National_numbers">
    <condition field="destination_number" expression="^0([1-578]\d{8})$">
        <action application="set" data="effective_caller_id_number=${outbound_caller_id}"/>
        <action application="export" data="callee_id_name=${cidlookup(0031$1)}" />
        <action application="bridge" data="sofia/gateway/myLandLineProvider/31$1"/>
    </condition>
</extension>

incoming calls

This public dialplan snippet somewhat at the top sets the number (if any) first, checks if it has an international prefix, does a lookup for incoming calls and will set the name accordingly.

The second part will strip any leading + sign

<extension name="fix_cidnam" continue="true">
    <!--make sure the module is loaded, or else loading it will kill our call!-->
    <!-- Simple case: name=number or name is empty -->
    <!-- and number is a 10digit (excluding optional leading 1), in nanpa nxx-nxx-xxxx form -->
    <!-- will skipurl lookup if not a 10digit # (don't lookup INTL), and instead just query the SQL -->
    <condition field="${module_exists(mod_cidlookup)}" expression="true"/>
    <condition field="caller_id_name" expression="^${caller_id_number}$|^$"/>
    <condition field="caller_id_number" expression="^(\+|00)(\d+)$">
        <action application="cidlookup" data="00$2"/>
        <anti-action application="cidlookup" data="${caller_id_number}"/>
    </condition>
</extension>

<extension name="fix_cidnam_plus" continue="true">
    <!-- if the name starts with + followed by digits, strip the
         + and then pass the number -->
    <condition field="caller_id_name" expression="^\+(1[2-9]\d\d[2-9]\d{6})$">
        <action application="cidlookup" data="$1"/>
    </condition>
</extension>

todo

items stored in the database will not be updated anymore. The only way to refresh the number's information is to remove the entry manually which will cause a new lookup the next time that number is requested.