Rewrote lookups into framework
56
README
Normal file
@ -0,0 +1,56 @@
|
||||
Gemeinschaft - CallerID addon
|
||||
|
||||
(c)2009 Markus Birth <markus@birth-online.de>
|
||||
|
||||
|
||||
1. INSTALLATION
|
||||
|
||||
- copy the directories dialplan-scripts and inc to /opt/gemeinschaft
|
||||
- open the file /etc/asterisk/e-internal.ael
|
||||
- find the context to-internal-users-self (around line 478)
|
||||
- in there, find the to_user: section
|
||||
- before the Dial() command (around line 731), add the following line:
|
||||
|
||||
AGI(/opt/gemeinschaft/dialplan-scripts/in-get-callerid.agi,${CALLERID(num)});
|
||||
|
||||
so that it looks like this:
|
||||
|
||||
AGI(/opt/gemeinschaft/dialplan-scripts/in-get-callerid.agi,${CALLERID(num)});
|
||||
Dial(SIP/${EXTEN}${pgrpdialstr},${dialtimeout});
|
||||
|
||||
- somewhat further down find the to_queue: section
|
||||
- before the Queue() command (around line 912), add the line from above so that it looks like this:
|
||||
|
||||
AGI(/opt/gemeinschaft/dialplan-scripts/in-get-callerid.agi,${CALLERID(num)});
|
||||
Set(queue_entertime=${EPOCH});
|
||||
Queue(${EXTEN},${ring_instead_of_moh},,,${queuetimeout});
|
||||
Set(queue_waittime=$[${EPOCH}-${queue_entertime}]);
|
||||
|
||||
|
||||
|
||||
2. CONFIGURATION
|
||||
|
||||
- open the file inc/CallerID/CallerID.class.php
|
||||
- find the line that reads:
|
||||
|
||||
protected static $dataProvider = 'CSVLookup,CountryCodes';
|
||||
|
||||
- set your preferred order of lookup (from left to right, separated by commas)
|
||||
- the first match will be used
|
||||
- ignore the $countrycode and $areacode as they will be overwritten by your
|
||||
Gemeinschaft canonization settings (make sure they are correct!)
|
||||
|
||||
- Available dataProviders:
|
||||
|
||||
CSVLookup
|
||||
- the csv-file for CSVLookup is inc/CallerID/CSVLookup/telefonbuch.csv
|
||||
- the numbers need to be formatted as your phone shows them (i.e. not in international format!)
|
||||
|
||||
CountryCodes
|
||||
- uses several official prefix lists to return country and sometimes the region of the caller
|
||||
- always returns a match, so dataProviders after this one will not be triggered
|
||||
|
||||
OnlineLookup
|
||||
- looks up the incoming number at different online directories
|
||||
- the definitions are in the file inc/CallerID/OnlineLookup/jfritz-definitions.xml
|
||||
- the XML file should be compatible to the JFritz version (http://jfritz.org/)
|
27
dialplan-scripts/in-get-callerid.agi
Executable file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/php -q
|
||||
<?php
|
||||
|
||||
define( 'GS_VALID', true ); /// this is a parent file
|
||||
require_once( dirName(__FILE__) .'/../inc/conf.php' );
|
||||
require_once( GS_DIR .'inc/agi-fns.php' );
|
||||
|
||||
ini_set('implicit_flush', 1);
|
||||
ob_implicit_flush(1);
|
||||
|
||||
$number = trim(@$argv[1]);
|
||||
if (empty($number)) die();
|
||||
|
||||
gs_agi_verbose( '### Number identification for ' . $number );
|
||||
|
||||
require_once( GS_DIR . 'inc/CallerID/CallerID.class.php' );
|
||||
CallerID::$countrycode = gs_get_conf('GS_CANONIZE_COUNTRY_CODE', '49');
|
||||
CallerID::$areacode = gs_get_conf('GS_CANONIZE_AREA_CODE', '30');
|
||||
|
||||
$info = CallerID::getCallerId($number);
|
||||
gs_agi_verbose( '### Number ID result: ' . $info );
|
||||
|
||||
if ($info !== false) {
|
||||
echo 'SET VARIABLE CALLERID(name) ' . gs_agi_str_esc($info) . "\n";
|
||||
}
|
||||
|
||||
?>
|
@ -1,43 +0,0 @@
|
||||
#!/usr/bin/php -q
|
||||
<?php
|
||||
|
||||
define( 'GS_VALID', true ); /// this is a parent file
|
||||
require_once( dirName(__FILE__) .'/../inc/conf.php' );
|
||||
require_once( GS_DIR .'inc/agi-fns.php' );
|
||||
|
||||
ini_set('implicit_flush', 1);
|
||||
ob_implicit_flush(1);
|
||||
|
||||
$number = trim(@$argv[1]);
|
||||
if (empty($number)) die();
|
||||
|
||||
gs_agi_verbose( '### Number identification for ' . $number );
|
||||
|
||||
require_once( GS_DIR . 'inc/CountryCodes/CountryCodes.class.php' );
|
||||
|
||||
$intlnum = $number;
|
||||
if (substr($intlnum, 0, 2) == '00') {
|
||||
// int'l number
|
||||
$intlnum = '+' . substr($intlnum, 2);
|
||||
} elseif ($number{0} == '0') {
|
||||
// local number
|
||||
$intlnum = '+' . GS_CANONIZE_COUNTRY_CODE . substr($intlnum, 1);
|
||||
} else {
|
||||
// plain number
|
||||
$intlnum = '+' . GS_CANONIZE_COUNTRY_CODE . GS_CANONIZE_AREA_CODE . $intlnum;
|
||||
}
|
||||
gs_agi_verbose( '### Number identification for ' . $intlnum );
|
||||
|
||||
$country = CountryCodes::lookupNum($intlnum);
|
||||
// will set:
|
||||
// $country['Calling Code'] = matched part of number, e.g. "+4930"
|
||||
// $country['Country'] = country , e.g. "Deutschland"
|
||||
// $country['CC'] = ISO 2-letter country code, e.g. "DE"
|
||||
// $country['District'] = district of country , e.g. "Berlin"
|
||||
// $country['flag'] = <unused>
|
||||
|
||||
// file_put_contents('/tmp/countrylookup.txt', print_r($country, true) );
|
||||
|
||||
echo 'SET VARIABLE CALLERID(name) ' . gs_agi_str_esc($country['CC'] . ', ' . $country['District']) . "\n";
|
||||
|
||||
?>
|
@ -1,89 +0,0 @@
|
||||
#!/usr/bin/php -q
|
||||
<?php
|
||||
|
||||
define( 'GS_VALID', true ); /// this is a parent file
|
||||
require_once( dirName(__FILE__) .'/../inc/conf.php' );
|
||||
require_once( GS_DIR .'inc/agi-fns.php' );
|
||||
|
||||
ini_set('implicit_flush', 1);
|
||||
ob_implicit_flush(1);
|
||||
|
||||
$number = trim(@$argv[1]);
|
||||
if (empty($number)) die();
|
||||
|
||||
gs_agi_verbose( '### Number identification for ' . $number );
|
||||
|
||||
$intlnum = $number;
|
||||
if (substr($intlnum, 0, 2) == '00') {
|
||||
// int'l number
|
||||
$intlnum = '+' . substr($intlnum, 2);
|
||||
} elseif ($number{0} == '0') {
|
||||
// local number
|
||||
$intlnum = '+' . GS_CANONIZE_COUNTRY_CODE . substr($intlnum, 1);
|
||||
} else {
|
||||
// plain number
|
||||
$intlnum = '+' . GS_CANONIZE_COUNTRY_CODE . GS_CANONIZE_AREA_CODE . $intlnum;
|
||||
}
|
||||
gs_agi_verbose( '### Canonized number is ' . $intlnum );
|
||||
|
||||
$config_file = GS_DIR . 'etc/jf-reverse-lookup.xml';
|
||||
$xml = new DOMDocument();
|
||||
if (!file_exists($config_file)) echo 'No config!';
|
||||
$xml->load($config_file);
|
||||
|
||||
$countries =& $xml->getElementsByTagName('country'); // first country entry
|
||||
|
||||
for ($i=0; $i<$countries->length; $i++) {
|
||||
$country =& $countries->item($i);
|
||||
$cc = $country->attributes->getNamedItem('code')->nodeValue;
|
||||
if ( $cc == substr($intlnum, 0, strlen($cc)) ) break;
|
||||
}
|
||||
|
||||
$websites =& $country->getElementsByTagName('website');
|
||||
|
||||
for ($i=0; $i<$websites->length; $i++) {
|
||||
$website =& $websites->item($i);
|
||||
|
||||
$wname = $website->attributes->getNamedItem('name')->nodeValue;
|
||||
$wurl = $website->attributes->getNamedItem('url')->nodeValue;
|
||||
$wpref = $website->attributes->getNamedItem('prefix')->nodeValue;
|
||||
$wentry = $website->getElementsByTagName('entry')->item(0);
|
||||
|
||||
$checknum = $wpref . substr($intlnum, strlen($cc)); // replace int'l prefix by local prefix
|
||||
|
||||
gs_agi_verbose('Searching ' . $wname . ' (' . ($i+1) . '/' . $websites->length . ')');
|
||||
|
||||
$url = str_replace('$NUMBER', $checknum, $wurl);
|
||||
|
||||
// Had to use wget b/c PHP's file_get_contents() and other tricks didn't succeed (returned wrong data!)
|
||||
exec('wget -q -O - "' . $url . '"', $data);
|
||||
$data = implode("\n", $data);
|
||||
|
||||
gs_agi_verbose('-> got ' . strlen($data) . ' Bytes');
|
||||
|
||||
$lastPos = 0;
|
||||
$details = array();
|
||||
foreach ($wentry->childNodes as $child) {
|
||||
if ($child->nodeType == XML_TEXT_NODE) continue;
|
||||
$ntitle = $child->nodeName;
|
||||
$pattern = $child->nodeValue;
|
||||
|
||||
$matchct = preg_match('/'.$pattern.'/', $data, &$matches, PREG_OFFSET_CAPTURE, $lastPos);
|
||||
if ($matchct == 0) {
|
||||
gs_agi_verbose('NO MATCH FOUND!');
|
||||
break;
|
||||
}
|
||||
|
||||
$details[$ntitle] = rawurldecode($matches[1][0]);
|
||||
$lastPos = $matches[0][1];
|
||||
}
|
||||
|
||||
if (count($details) > 0) break;
|
||||
}
|
||||
|
||||
// print_r($details);
|
||||
if (isset($details['name']) && !empty($details['name'])) {
|
||||
echo 'SET VARIABLE CALLERID(name) ' . gs_agi_str_esc($details['name']) . "\n";
|
||||
}
|
||||
|
||||
?>
|
@ -1,5 +1,2 @@
|
||||
// add this line: (country/city lookup)
|
||||
AGI(/opt/gemeinschaft/dialplan-scripts/in-get-callerid1.agi,${CALLERID(num)});
|
||||
|
||||
// or this line: (online service lookup)
|
||||
AGI(/opt/gemeinschaft/dialplan-scripts/in-get-callerid2.agi,${CALLERID(num)});
|
||||
// add this line:
|
||||
AGI(/opt/gemeinschaft/dialplan-scripts/in-get-callerid.agi,${CALLERID(num)});
|
||||
|
@ -6,6 +6,7 @@
|
||||
* @author Markus Birth, mbirth@webwriters.de
|
||||
**/
|
||||
|
||||
if (class_exists('CSV')) return;
|
||||
ini_set('auto_detect_line_endings', '1');
|
||||
|
||||
class CSV {
|
||||
@ -28,7 +29,7 @@ class CSV {
|
||||
protected $num_allowed = '0123456789.';
|
||||
|
||||
private static $lfw_staleAge = 5; // staleAge in seconds for locked filewrite
|
||||
private static $lfw_timeLimit = 300000; // time limit in µs to try to gain lock
|
||||
private static $lfw_timeLimit = 300000; // time limit in <EFBFBD>s to try to gain lock
|
||||
|
||||
function __construct() {
|
||||
}
|
||||
@ -148,8 +149,8 @@ class CSV {
|
||||
foreach ($this->data as $i=>$d) {
|
||||
if (count($d)>$maxct) {
|
||||
$maxct = count($d);
|
||||
$maxrec = $i;
|
||||
}
|
||||
$maxrec = $i;
|
||||
}
|
||||
}
|
||||
$heads = array();
|
||||
foreach ($this->data[$maxrec] as $h=>$f) {
|
||||
@ -158,7 +159,7 @@ class CSV {
|
||||
foreach ($this->data as $i=>$r) {
|
||||
$r2 = array();
|
||||
foreach ($heads as $h) {
|
||||
$r2[] = $r[$h];
|
||||
$r2[] = $r[$h];
|
||||
}
|
||||
$this->data[$i] = $r2;
|
||||
}
|
||||
@ -263,9 +264,9 @@ class CSV {
|
||||
if (is_string($keycol)) {
|
||||
if ($this->useheaders === false) {
|
||||
$keycol = array_search($keycol, $this->data[0]);
|
||||
if (!$keycol) return false;
|
||||
if (!$keycol) return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
$sortme = array();
|
||||
foreach ($this->data as $i=>$d) {
|
44
inc/CallerID/CSVLookup/ResolverBridge.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
require_once(dirname(__FILE__) . '/../CallerIDResolver.interface.php');
|
||||
require_once(dirname(__FILE__) . '/CSV.class.php');
|
||||
|
||||
class CallerID_CSVLookup implements CallerIDResolver {
|
||||
const CSV_FILE = 'telefonbuch.csv';
|
||||
|
||||
protected static function localizeNumber($number) {
|
||||
// convert +XX to 00XX or omit if local
|
||||
if ($number{0} == '+') {
|
||||
if (substr($number, 1, strlen(CallerID::$countrycode)) == CallerID::$countrycode) {
|
||||
$number = '0' . substr($number, 1+strlen(CallerID::$countrycode));
|
||||
} else {
|
||||
$number = '00' . substr($number, 1);
|
||||
}
|
||||
}
|
||||
return $number;
|
||||
}
|
||||
|
||||
public static function lookupNum($number) {
|
||||
$csv = new CSV();
|
||||
$csv->setDelimiter(';');
|
||||
$csv->load(dirname(__FILE__) . '/' . self::CSV_FILE, true);
|
||||
|
||||
$data = $csv->getTable();
|
||||
|
||||
$csv->close();
|
||||
|
||||
$number = self::localizeNumber($number);
|
||||
|
||||
foreach ($data as $entry) {
|
||||
// CSV format: Name;Number
|
||||
if ($entry[1] == $number) {
|
||||
return $entry[0];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
1
inc/CallerID/CSVLookup/telefonbuch.csv
Normal file
@ -0,0 +1 @@
|
||||
Mustermann, Max;03012345678
|
|
41
inc/CallerID/CallerID.class.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
class CallerID {
|
||||
// could easily check folders automatically to find dataProviders
|
||||
protected static $dataProvider = 'CSVLookup,CountryCodes';
|
||||
public static $countrycode = '49';
|
||||
public static $areacode = '30';
|
||||
|
||||
protected static function canonizeNumber($number) {
|
||||
if ($number{0} == '+') return $number; // already canonized
|
||||
if (substr($number, 0, 2) == '00') {
|
||||
// int'l number
|
||||
$number = '+' . substr($number, 2);
|
||||
} elseif ($number{0} == '0') {
|
||||
// local number
|
||||
$number = '+' . self::$countrycode . substr($number, 1);
|
||||
} else {
|
||||
// plain number
|
||||
$number = '+' . self::$countrycode . self::$areacode . $number;
|
||||
}
|
||||
return $number;
|
||||
}
|
||||
|
||||
public static function getCallerId($number) {
|
||||
$number = self::canonizeNumber($number);
|
||||
$provider = explode(',', self::$dataProvider);
|
||||
|
||||
foreach ($provider as $p) {
|
||||
include_once(dirname(__FILE__) . '/' . $p . '/ResolverBridge.php');
|
||||
$classname = __CLASS__ . '_' . $p;
|
||||
if (!class_exists($classname)) continue;
|
||||
|
||||
$info = call_user_func(array($classname, 'lookupNum'), $number);
|
||||
if ($info !== false) return $info;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
9
inc/CallerID/CallerIDResolver.interface.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
interface CallerIDResolver {
|
||||
public static function lookupNum($number);
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
@ -1,4 +1,4 @@
|
||||
"Calling Code";"District"
|
||||
"Calling Code";"District";"CC";"Country"
|
||||
1;"Paris"
|
||||
2;"NW-Frankreich"
|
||||
3;"NO-Frankreich"
|
||||
@ -6,4 +6,4 @@
|
||||
5;"SW-Frankreich"
|
||||
6;"Mobilfunk"
|
||||
8;"Freephone"
|
||||
9;"ortsunabhängig"
|
||||
9;"ortsunabhängig"
|
Can't render this file because it has a wrong number of fields in line 2.
|
Can't render this file because it has a wrong number of fields in line 2.
|
407
inc/CallerID/CountryCodes/CSV.class.php
Normal file
@ -0,0 +1,407 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CSV class for easy use of CSV files as tables
|
||||
*
|
||||
* @author Markus Birth, mbirth@webwriters.de
|
||||
**/
|
||||
|
||||
if (class_exists('CSV')) return;
|
||||
ini_set('auto_detect_line_endings', '1');
|
||||
|
||||
class CSV {
|
||||
|
||||
/** CSV formatting variables */
|
||||
protected $csv_delim = ',';
|
||||
protected $csv_stren = '"';
|
||||
protected $csv_decim = '.';
|
||||
protected $csv_escap = '\\';
|
||||
|
||||
/** table-filename, read-only- and headers-flag */
|
||||
protected $file, $readonly;
|
||||
protected $useheaders = false;
|
||||
protected $autoconvert = false;
|
||||
|
||||
/** table contents */
|
||||
protected $data, $headers;
|
||||
|
||||
/** holds allowed characters for numerical values */
|
||||
protected $num_allowed = '0123456789.';
|
||||
|
||||
private static $lfw_staleAge = 5; // staleAge in seconds for locked filewrite
|
||||
private static $lfw_timeLimit = 300000; // time limit in <20>s to try to gain lock
|
||||
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
function setDecimal($nd) {
|
||||
if (strlen($nd) != 1) return false;
|
||||
$this->csv_decim = $nd;
|
||||
$this->num_allowed = '0123456789' . $nd;
|
||||
return true;
|
||||
}
|
||||
|
||||
function setStringEnclosure($enc) {
|
||||
if (strlen($enc) != 1) return false;
|
||||
$this->csv_stren = $enc;
|
||||
return true;
|
||||
}
|
||||
|
||||
function setDelimiter($deli) {
|
||||
if (strlen($deli) != 1) return false;
|
||||
$this->csv_delim = $deli;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a new table; empty or from given array via <code>$init</code>
|
||||
*
|
||||
* @public
|
||||
* @param $table name of new table
|
||||
* @param $init array with initial table (if omitted or not an array, an empty table is created)
|
||||
* @return <code>true</code> if successful; <code>false</code> if failed
|
||||
*/
|
||||
function create($file, $init = false) {
|
||||
if (isset($this->file) || isset($this->data)) return false;
|
||||
$this->file = $file;
|
||||
$this->readonly = false;
|
||||
$this->data = array();
|
||||
if ($init !== false) {
|
||||
if (!is_array($init)) $init = array($init);
|
||||
foreach ($init as $tmp) {
|
||||
if (!is_array($tmp)) $tmp = array($tmp);
|
||||
array_push($this->data, $tmp);
|
||||
}
|
||||
} else {
|
||||
$this->data = array();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* loads a table from CSV file
|
||||
*
|
||||
* @public
|
||||
* @param $table name of table to load (without '.csv'!)
|
||||
* @param $readonly if true, loads table for read-only (defaults to false)
|
||||
* @param $delim CSV delimiter (defaults to ',')
|
||||
* @param $stren CSV string encloser (defaults to '"')
|
||||
* @param $decim Decimal separator (defaults to '.')
|
||||
* @return <code>true</code> if successful; <code>false</code> if failed
|
||||
*/
|
||||
function load($file, $readonly = false) {
|
||||
if (isset($this->file) || isset($this->data) || !file_exists($file)) return false;
|
||||
$this->data = array();
|
||||
$this->file = $file;
|
||||
$this->readonly = $readonly;
|
||||
$f = fopen($this->file, 'rt');
|
||||
if ($f === false) return false;
|
||||
$safety = -1;
|
||||
while (($temp = fgetcsv($f, filesize($this->file), $this->csv_delim, $this->csv_stren)) !== false) {
|
||||
$temp = preg_replace('/\\\([^\\\])/', '\\1', $temp);
|
||||
$temp = str_replace('\\\\', '\\', $temp);
|
||||
$this->data[] = $temp;
|
||||
if (ftell($f) == $safety) break; // loop
|
||||
$safety = ftell($f);
|
||||
}
|
||||
fclose($f);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets whether to treat all fields as string or as specific types
|
||||
*
|
||||
* @public
|
||||
* @param $acflag new boolean value
|
||||
*/
|
||||
function setAutoConvert($acflag) {
|
||||
if (!is_bool($acflag)) return false;
|
||||
$this->autoconvert = $acflag;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets whether to use headers in first data row for description of fields
|
||||
* or use 0..n as field descriptors. Doesn't affect the $this->data object.
|
||||
*
|
||||
* @public
|
||||
* @param $hflag new boolean value
|
||||
*/
|
||||
function setUseHeaders($hflag) {
|
||||
if (!is_bool($hflag)) return false;
|
||||
if ($this->useheaders === $hflag) return true;
|
||||
if ($hflag) {
|
||||
// Apply headings from first row to all entries
|
||||
// and remove first row
|
||||
$heads = array_shift($this->data);
|
||||
foreach ($this->data as $i=>$r) {
|
||||
$r2 = array();
|
||||
foreach ($r as $j=>$d) {
|
||||
$r2[$heads[$j]] = $d;
|
||||
}
|
||||
$this->data[$i] = $r2;
|
||||
}
|
||||
} else {
|
||||
// Extract headings from longest data record
|
||||
// and prepend header line to data array
|
||||
$maxrec = 0;
|
||||
$maxct = 0;
|
||||
foreach ($this->data as $i=>$d) {
|
||||
if (count($d)>$maxct) {
|
||||
$maxct = count($d);
|
||||
$maxrec = $i;
|
||||
}
|
||||
}
|
||||
$heads = array();
|
||||
foreach ($this->data[$maxrec] as $h=>$f) {
|
||||
$heads[] = $h;
|
||||
}
|
||||
foreach ($this->data as $i=>$r) {
|
||||
$r2 = array();
|
||||
foreach ($heads as $h) {
|
||||
$r2[] = $r[$h];
|
||||
}
|
||||
$this->data[$i] = $r2;
|
||||
}
|
||||
array_unshift($this->data, $heads);
|
||||
}
|
||||
$this->useheaders = $hflag;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* saves currently open table to CSV file
|
||||
* (locked filewrite code from http://de2.php.net/manual/en/function.flock.php#46085)
|
||||
*
|
||||
* @public
|
||||
* @return <code>true</code> if successful; <code>false</code> if failed
|
||||
*/
|
||||
function save() {
|
||||
if ($this->readonly === true || !isset($this->file) || !isset($this->data)) return false;
|
||||
$headerstate = $this->useheaders;
|
||||
if ($this->useheaders === true) {
|
||||
// prepend headers if used
|
||||
$this->setUseHeaders(false);
|
||||
}
|
||||
|
||||
// BEGIN: Gain lock
|
||||
ignore_user_abort(true);
|
||||
$lockdir = $this->file . '.lock';
|
||||
if (is_dir($lockdir)) {
|
||||
if ((time() - filemtime($lockdir)) > self::$lfw_staleAge) {
|
||||
rmdir($lockdir);
|
||||
}
|
||||
}
|
||||
$locked = @mkdir($lockdir);
|
||||
if ($locked === false) {
|
||||
$timestart = microtime(true);
|
||||
do {
|
||||
if ((microtime(true) - $timestart) > self::$lfw_timeLimit) {
|
||||
ignore_user_abort(false);
|
||||
$this->setUseHeaders($headerstate);
|
||||
return false;
|
||||
}
|
||||
$locked = @mkdir($lockdir);
|
||||
} while ($locked === false);
|
||||
}
|
||||
// END: Gain lock
|
||||
|
||||
$f = fopen($this->file, 'wt');
|
||||
foreach ($this->data as $line) {
|
||||
if (count($line)>0 && strlen(implode('', $line))>0) {
|
||||
$temp = array();
|
||||
foreach ($line as $tmp) {
|
||||
if (!is_numeric($tmp) && !is_bool($tmp)) {
|
||||
if (strlen($this->csv_stren)>0) $tmp = strtr($tmp, array('\\'=>'\\\\', $this->csv_stren=>'\\'.$this->csv_stren));
|
||||
$temp[] = $this->csv_stren . $tmp . $this->csv_stren;
|
||||
} elseif (is_numeric($tmp)) {
|
||||
if (is_float($tmp)) {
|
||||
$temp[] = strtr((string)$tmp, ".", $this->csv_decim);
|
||||
} else {
|
||||
$temp[] = $tmp;
|
||||
}
|
||||
} elseif (is_bool($tmp)) {
|
||||
if ($tmp==true) {
|
||||
$temp[] = 'true';
|
||||
} else {
|
||||
$temp[] = 'false';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!fwrite($f, implode($this->csv_delim, $temp) . "\n")) {
|
||||
$this->setUseHeaders($headerstate); // restore previous state
|
||||
ignore_user_abort(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose($f);
|
||||
rmdir($lockdir);
|
||||
ignore_user_abort(false);
|
||||
$this->setUseHeaders($headerstate); // restore previous state
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* closes currently open table
|
||||
*
|
||||
* @public
|
||||
* @param $save if true, writes table to CSV file before close (defaults to true)
|
||||
* @return <code>true</code> if successful; <code>false</code> if failed
|
||||
*/
|
||||
function close($save = true) {
|
||||
if (!isset($this->file) || !isset($this->data)) return false;
|
||||
if ($save===true && !$this->readonly) {
|
||||
if (!$this->save()) return false;
|
||||
}
|
||||
unset($this->file);
|
||||
unset($this->readonly);
|
||||
unset($this->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
function sortRows($keycol, $rev = false) {
|
||||
if (is_string($keycol)) {
|
||||
if ($this->useheaders === false) {
|
||||
$keycol = array_search($keycol, $this->data[0]);
|
||||
if (!$keycol) return false;
|
||||
}
|
||||
|
||||
}
|
||||
$sortme = array();
|
||||
foreach ($this->data as $i=>$d) {
|
||||
$sortme[$i] = $d[$keycol];
|
||||
}
|
||||
asort($sortme);
|
||||
$newdata = array();
|
||||
if ($this->useheaders === false) {
|
||||
$newdata[0] = $this->data[0];
|
||||
}
|
||||
foreach ($sortme as $i=>$d) {
|
||||
if ($i==0 && $this->useheaders === false) continue;
|
||||
$newdata[] = $this->data[$i];
|
||||
}
|
||||
if ($rev === true) $newdata = array_reverse($newdata);
|
||||
$this->data = $newdata;
|
||||
return true;
|
||||
}
|
||||
|
||||
function getTable() {
|
||||
if (!isset($this->data)) return false;
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
function setTable($tbl) {
|
||||
if (!is_array($tbl) || !is_array(reset($tbl))) return false;
|
||||
$this->data = $tbl;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns an array with all elements of a complete row
|
||||
*
|
||||
* @public
|
||||
* @param $rid id of the row
|
||||
* @return array with elements if successful; <code>false</code> if failed
|
||||
*/
|
||||
function getRow($rid) {
|
||||
if (!isset($this->data[$rid])) return false;
|
||||
if (!$this->autoconvert) return $this->data[$rid];
|
||||
|
||||
$result = array();
|
||||
foreach ($this->data[$rid] as $i=>$d) {
|
||||
$result[$i] = $this->guessType($d);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a single cell value
|
||||
*
|
||||
* @public
|
||||
* @param $rid id of the row
|
||||
* @param $cid id of the column
|
||||
* @return cell contents if successful; <code>false</code> if failed
|
||||
*/
|
||||
function getRowCol($rid, $cid) {
|
||||
if (!isset($this->data[$rid][$cid])) return false;
|
||||
if (!$this->autoconvert) return $this->data[$rid][$cid];
|
||||
|
||||
return $this->guessType($this->data[$rid][$cid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses type of a value by its content
|
||||
*
|
||||
* @public
|
||||
* @param $val String to guess type
|
||||
* @return String, (int) or (float)
|
||||
*/
|
||||
function guessType($val) {
|
||||
$commas = 0;
|
||||
for ($i=0;$i<strlen($val);$i++) {
|
||||
$c = $val{$i};
|
||||
if (strpos($this->num_allowed, $c) === false) return $val;
|
||||
if ($c == $this->csv_decim) $commas++;
|
||||
}
|
||||
$val = strtr($val, $this->csv_decim, '.');
|
||||
if ($commas>1) return $val;
|
||||
if ($commas==1) return floatval($val);
|
||||
return intval($val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses type for the whole database
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
function guessTypeAll() {
|
||||
foreach ($this->data as $i=>$r) {
|
||||
foreach ($r as $j=>$d) {
|
||||
$this->data[$i][$j] = $this->guessType($d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* writes a value to a single cell
|
||||
*
|
||||
* @public
|
||||
* @param $rid id of the row
|
||||
* @param $cid id of the column
|
||||
* @param $val new value to write to cell
|
||||
* @return <code>true</code> if successful; <code>false</code> if failed
|
||||
*/
|
||||
function setRowCol($rid, $cid, $val) {
|
||||
$this->data[$rid][$cid] = $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* writes an array to a row
|
||||
*
|
||||
* @public
|
||||
* @param $rid id of the row
|
||||
* @param $line array with column contents to write to line
|
||||
* @return <code>true</code> if successful; <code>false</code> if failed
|
||||
*/
|
||||
function setRow($rid, $line) {
|
||||
if (!is_array($line)) return false;
|
||||
$this->data[$rid] = $line;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes a row
|
||||
*
|
||||
* @public
|
||||
* @param $rid id of the row
|
||||
* @return <code>true</code> if successful; <code>false</code> if failed
|
||||
*/
|
||||
function delRow($rid) {
|
||||
if (!isset($this->data[$rid])) return false;
|
||||
unset($this->data[$rid]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -81,4 +81,13 @@ class CountryCodes {
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($argc)) {
|
||||
echo 'TEST MODE' . PHP_EOL;
|
||||
do {
|
||||
echo 'Enter number, e.g. +4930123456 (0 = exit): ';
|
||||
$inp = trim(fgets(STDIN));
|
||||
print_r(CountryCodes::lookupNum($inp));
|
||||
} while ($inp != '0');
|
||||
}
|
||||
|
||||
?>
|
29
inc/CallerID/CountryCodes/ResolverBridge.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
require_once(dirname(__FILE__) . '/../CallerIDResolver.interface.php');
|
||||
require_once(dirname(__FILE__) . '/CountryCodes.class.php');
|
||||
|
||||
class CallerID_CountryCodes implements CallerIDResolver {
|
||||
|
||||
public static function lookupNum($number) {
|
||||
$info = CountryCodes::lookupNum($number);
|
||||
|
||||
/*
|
||||
* $info = array(
|
||||
* 'Calling Code' => '+493322',
|
||||
* 'Country' => 'Deutschland',
|
||||
* 'CC' => 'DE',
|
||||
* 'District' => 'Falkensee',
|
||||
* 'flag' => '',
|
||||
* );
|
||||
*/
|
||||
|
||||
$result = $info['CC'];
|
||||
if (!empty($info['District'])) $result .= ', ' . $info['District'];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
Can't render this file because it contains an unexpected character in line 3 and column 16.
|
92
inc/CallerID/OnlineLookup/OnlineLookup.class.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
class OnlineLookup {
|
||||
public static $xml = 'jfritz-definitions.xml';
|
||||
public static $countrycode = '49';
|
||||
public static $areacode = '30';
|
||||
|
||||
protected static function canonizeNumber($number) {
|
||||
if ($number{0} == '+') return $number; // already canonized
|
||||
if (substr($number, 0, 2) == '00') {
|
||||
// int'l number
|
||||
$number = '+' . substr($number, 2);
|
||||
} elseif ($number{0} == '0') {
|
||||
// local number
|
||||
$number = '+' . self::$countrycode . substr($number, 1);
|
||||
} else {
|
||||
// plain number
|
||||
$number = '+' . self::$countrycode . self::$areacode . $number;
|
||||
}
|
||||
return $number;
|
||||
}
|
||||
|
||||
protected static function getDefinitionsForCountry($number) {
|
||||
$configfile = dirname(__FILE__) . '/' . self::$xml;
|
||||
$xml = new DOMDocument();
|
||||
if (!file_exists($configfile)) return false;
|
||||
$xml->load($configfile);
|
||||
|
||||
$countries =& $xml->getElementsByTagName('country'); // first country entry
|
||||
|
||||
for ($i=0; $i<$countries->length; $i++) {
|
||||
$country =& $countries->item($i);
|
||||
$cc = $country->attributes->getNamedItem('code')->nodeValue;
|
||||
if ( $cc == substr($number, 0, strlen($cc)) ) return $country;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getNumberInfo($number, $maxhosts = 99) {
|
||||
$number = self::canonizeNumber($number);
|
||||
$country = self::getDefinitionsForCountry($number);
|
||||
if ($country === false) return false;
|
||||
|
||||
$cc = $country->attributes->getNamedItem('code')->nodeValue;
|
||||
|
||||
$websites =& $country->getElementsByTagName('website');
|
||||
|
||||
for ($i=0; $i<$websites->length; $i++) {
|
||||
$website =& $websites->item($i);
|
||||
|
||||
$wname = $website->attributes->getNamedItem('name')->nodeValue;
|
||||
$wurl = $website->attributes->getNamedItem('url')->nodeValue;
|
||||
$wpref = $website->attributes->getNamedItem('prefix')->nodeValue;
|
||||
$wentry = $website->getElementsByTagName('entry')->item(0);
|
||||
|
||||
$checknum = $wpref . substr($number, strlen($cc)); // replace int'l prefix by local prefix
|
||||
|
||||
$url = str_replace('$NUMBER', $checknum, $wurl);
|
||||
|
||||
// Had to use wget b/c PHP's file_get_contents() and other tricks didn't succeed (returned wrong data!)
|
||||
exec('wget -q -O - "' . $url . '"', $data);
|
||||
$data = implode("\n", $data);
|
||||
|
||||
$lastPos = 0;
|
||||
$details = array();
|
||||
foreach ($wentry->childNodes as $child) {
|
||||
if ($child->nodeType == XML_TEXT_NODE) continue;
|
||||
$ntitle = $child->nodeName;
|
||||
$pattern = $child->nodeValue;
|
||||
$pattern = str_replace('/', '\\/', $pattern);
|
||||
|
||||
$matchct = preg_match('/'.$pattern.'/', $data, &$matches, PREG_OFFSET_CAPTURE, $lastPos);
|
||||
if ($matchct == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$details[$ntitle] = html_entity_decode( rawurldecode( trim($matches[1][0]) ) );
|
||||
$lastPos = $matches[0][1];
|
||||
}
|
||||
|
||||
if (count($details) > 0 || --$maxhosts <= 0) break;
|
||||
}
|
||||
|
||||
// print_r($details);
|
||||
if (isset($details['name']) && !empty($details['name'])) {
|
||||
return $details;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
18
inc/CallerID/OnlineLookup/ResolverBridge.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
require_once(dirname(__FILE__) . '/../CallerIDResolver.interface.php');
|
||||
require_once(dirname(__FILE__) . '/OnlineLookup.class.php');
|
||||
|
||||
class CallerID_OnlineLookup implements CallerIDResolver {
|
||||
|
||||
public static function lookupNum($number) {
|
||||
OnlineLookup::$countrycode = CallerID::$countrycode;
|
||||
OnlineLookup::$areacode = CallerID::$areacode;
|
||||
|
||||
$info = OnlineLookup::getNumberInfo($number);
|
||||
if ($info === false) return false;
|
||||
return $info['name'];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Before Width: | Height: | Size: 643 B |
Before Width: | Height: | Size: 408 B |
Before Width: | Height: | Size: 604 B |
Before Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 643 B |
Before Width: | Height: | Size: 600 B |
Before Width: | Height: | Size: 497 B |
Before Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 428 B |
Before Width: | Height: | Size: 506 B |
Before Width: | Height: | Size: 647 B |
Before Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 673 B |
Before Width: | Height: | Size: 524 B |
Before Width: | Height: | Size: 663 B |
Before Width: | Height: | Size: 589 B |
Before Width: | Height: | Size: 593 B |
Before Width: | Height: | Size: 585 B |
Before Width: | Height: | Size: 504 B |
Before Width: | Height: | Size: 449 B |
Before Width: | Height: | Size: 497 B |
Before Width: | Height: | Size: 462 B |
Before Width: | Height: | Size: 457 B |
Before Width: | Height: | Size: 675 B |
Before Width: | Height: | Size: 486 B |
Before Width: | Height: | Size: 611 B |
Before Width: | Height: | Size: 639 B |
Before Width: | Height: | Size: 500 B |
Before Width: | Height: | Size: 593 B |
Before Width: | Height: | Size: 526 B |
Before Width: | Height: | Size: 631 B |
Before Width: | Height: | Size: 512 B |
Before Width: | Height: | Size: 443 B |
Before Width: | Height: | Size: 514 B |
Before Width: | Height: | Size: 600 B |
Before Width: | Height: | Size: 628 B |
Before Width: | Height: | Size: 398 B |
Before Width: | Height: | Size: 625 B |
Before Width: | Height: | Size: 528 B |
Before Width: | Height: | Size: 614 B |
Before Width: | Height: | Size: 521 B |
Before Width: | Height: | Size: 367 B |
Before Width: | Height: | Size: 453 B |
Before Width: | Height: | Size: 586 B |
Before Width: | Height: | Size: 450 B |
Before Width: | Height: | Size: 525 B |
Before Width: | Height: | Size: 472 B |
Before Width: | Height: | Size: 483 B |
Before Width: | Height: | Size: 477 B |
Before Width: | Height: | Size: 439 B |
Before Width: | Height: | Size: 563 B |
Before Width: | Height: | Size: 529 B |
Before Width: | Height: | Size: 608 B |
Before Width: | Height: | Size: 428 B |
Before Width: | Height: | Size: 476 B |
Before Width: | Height: | Size: 545 B |
Before Width: | Height: | Size: 572 B |
Before Width: | Height: | Size: 495 B |
Before Width: | Height: | Size: 620 B |
Before Width: | Height: | Size: 508 B |
Before Width: | Height: | Size: 582 B |
Before Width: | Height: | Size: 500 B |
Before Width: | Height: | Size: 429 B |
Before Width: | Height: | Size: 465 B |
Before Width: | Height: | Size: 508 B |
Before Width: | Height: | Size: 496 B |
Before Width: | Height: | Size: 653 B |
Before Width: | Height: | Size: 469 B |
Before Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 479 B |
Before Width: | Height: | Size: 532 B |
Before Width: | Height: | Size: 489 B |
Before Width: | Height: | Size: 610 B |
Before Width: | Height: | Size: 648 B |