|     |   | 
| (5 intermediate revisions by the same user not shown) | 
| Line 1: | Line 1: | 
| − | We make use of the mod_cidlookup for ease of use, but not all features are functional in our implementation. | + | {{Project | 
|  | + | |Featured=No | 
|  | + | |State=Active | 
|  | + | |Members=Xopr | 
|  | + | |Description=Caller ID lookup | 
|  | + | |GitHub=cidlookup | 
|  | + | }} | 
|  | + | <onlyinclude>This script uses mod_cidlookup combined with a custom php page. It tests the number against a SQLite database, several reversed number lookup websites, the national telecommunications authority database and a coarse array of areas of the world, which are called in order of granularity. The script can be called both on outgoing and incoming calls | 
|  | + |   | 
|  | + | </onlyinclude>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: |  | 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 an extension (and returns the name for that) | 
| − | * is stored in the local mySQL database, and return that | + | * is stored in the local SQLite database, and return that | 
|  | * can be found online by using reversed number lookup websites for landlines |  | * 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 found online by using the national telecommunications authority database (acm.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) |  | * 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: |  | The setting for mod_cidlookup is: | 
| − |   <param name="url" value="http://webserviceprovider/lookup.php?number=${caller_id_number}"/> | + |   <param name="url" value="https://domain.tld/cid.php?key=<your_desired_key>&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.
 |  | 
| − |   |  | 
| − | <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 numberis 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">
 | + | === old implementation === | 
| − |     <!-- if the name starts with + followed by digits, strip the
 | + | The "inline" old script can be found here: https://ackspace.nl/w/index.php?title=Telephone_system:Number_lookup&oldid=2652 | 
| − |          + 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 === |  | === 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. |  | 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. | 
|  | + | [[Category:Telephony]][[Category:Telephone snippet]][[Category:FreeSWITCH]] |