Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F7313170
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
185 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/rating_lib.phtml b/rating_lib.phtml
index 319e10b..489658b 100644
--- a/rating_lib.phtml
+++ b/rating_lib.phtml
@@ -1,4706 +1,4756 @@
<?
#apd_set_pprof_trace();
/*
Copyright (c) 2007 AG Projects
http://ag-projects.com
Author Adrian Georgescu
This library contains rating classes and functions
*/
class Rate {
var $priceDenominator = 10000; // allow sub cents
var $priceDecimalDigits = 4; // web display
var $minimumDurationCharged = 0; // round call for rating to minimum X seconds, is overwritten by settings per customer, 0 to disable
var $durationPeriodRated = 60; // how the prices are indicated in the billing_rates, default is per minute
var $trafficSizeRated = 1024; // in KBytes, default 1MByte
var $minimumDuration = 0; // minimum duration considered to apply rates for a call, if call is shorter the price is zero
var $ENUMtld = '';
var $ENUMdiscount = 0; // how much percentage to substract from the final price
var $price = 0;
var $spans = 0; // number of spans we looped through
function Rate($settings=array(),&$db) {
$this->settings = $settings;
if (!is_object($db)) {
$this->db = new DB_cdrtool;
} else {
$this->db = &$db;
}
$this->db->Halt_On_Error="no";
if ($this->settings['priceDenominator']) {
$this->priceDenominator=$this->settings['priceDenominator'];
}
if ($this->settings['priceDecimalDigits']) {
$this->priceDecimalDigits=$this->settings['priceDecimalDigits'];
}
if ($this->settings['minimumDurationCharged']) {
$this->minimumDurationCharged=$this->settings['minimumDurationCharged'];
}
if ($this->settings['durationPeriodRated']) {
$this->durationPeriodRated=$this->settings['durationPeriodRated'];
}
if ($this->settings['trafficSizeRated']) {
$this->trafficSizeRated=$this->settings['trafficSizeRated'];
}
if ($this->settings['minimumDuration']) {
// if call is shorter than this, it has zero cost
$this->minimumDuration=$this->settings['minimumDuration'];
}
}
function calculate(&$dictionary) {
/////////////////////////////////////////////////////
// required fields passed from a CDR structure
//
// Session start time
$this->callId = $dictionary['callId'];
$this->timestamp = $dictionary['timestamp'];
// Session usage, type and destination Id
$this->duration = $dictionary['duration'];
$this->traffic = 2 * ($dictionary['inputTraffic'] + $dictionary['outputTraffic']);
$this->applicationType = $dictionary['applicationType'];
$this->DestinationId = $dictionary['DestinationId'];
// Billable entities we try to best match the rating tables
// against
$this->BillingPartyId = $dictionary['BillingPartyId'];
$this->domain = $dictionary['domain'];
$this->gateway = $dictionary['gateway'];
if (!$this->gateway) $this->gateway='0.0.0.0';
$this->RatingTables = &$dictionary['RatingTables'];
- //
- /////////////////////////////////////////////////////////
-
/////////////////////////////////////////////////////////
// informational fields from CDR structure
$this->aNumber = $dictionary['aNumber'];
$this->cNumber = $dictionary['cNumber'];
$this->ENUMtld = $dictionary['ENUMtld'];
if ($this->minimumDuration && $this->duration < $this->minimumDuration) {
//syslog(LOG_NOTICE, "Duration less than minimum $this->minimumDuration");
$this->rateInfo .= " Duration < $this->minimumDuration s\n";
return false;
}
if ($this->ENUMtld && $this->ENUMtld != 'n/a' && $this->ENUMtld != 'none' && $this->RatingTables->ENUMtlds[$this->ENUMtld]) {
$this->ENUMdiscount = $this->RatingTables->ENUMtlds[$this->ENUMtld]['discount'];
if (!is_numeric($this->ENUMdiscount ) || $this->ENUMdiscount <0 || $this->ENUMdiscount >100) {
syslog(LOG_NOTICE, "Error: ENUM discount for tld $this->ENUMtld must be between 0 and 100");
}
}
if (!$this->duration) $this->duration = 0;
if (!$this->traffic) $this->traffic = 0;
if (!$this->applicationType) $this->applicationType='audio';
$trafficRate = 0;
$durationRate = 0;
$foundRates=array();
if (!$this->DestinationId) {
return false;
}
if (!$this->lookupProfiles()) {
return false;
}
$this->startTimeBilling = getLocalTime($this->billingTimezone,$this->timestamp);
list($dateText,$timeText) = explode(" ",trim($this->startTimeBilling));
$Bdate = explode("-",$dateText);
$Btime = explode(":",$timeText);
$this->timestampBilling = mktime($Btime[0], $Btime[1], $Btime[2], $Bdate[1], $Bdate[2], $Bdate[0]);
$this->startTimeBilling = Date("Y-m-d H:i:s",$this->timestampBilling);
$this->trafficKB=number_format($this->traffic/1024,0,"","");
if ($this->increment >= 1) {
// increase the billed duration to the next increment
$this->duration = $this->increment * ceil($this->duration / $this->increment);
}
if ($this->min_duration >= 1) {
$this->minimumDurationCharged = $this->min_duration;
}
if ($this->duration) {
unset($IntervalsForPricing);
$this->rateInfo .=
" Duration: $this->duration s\n".
" App: $this->applicationType\n".
" Destination: $this->DestinationId\n".
" Customer: $this->CustomerProfile\n";
if ($this->ENUMtld && $this->ENUMtld != 'none' && $this->ENUMtld != 'n/a') {
$this->rateInfo .=
" ENUM tld: $this->ENUMtld\n".
" ENUM discount: $this->ENUMdiscount%\n";
}
if ($this->increment > 1) {
$this->rateInfo .=
" Increment: $this->increment s\n";
}
if ($this->min_duration > 1) {
$this->rateInfo .=
" Min duration: $this->min_duration s\n";
}
$i=0;
$durationRatedTotal=0;
// get recursively a set of arrays with rates
// until we billed the whole duration
while ($durationRatedTotal < $this->duration) {
if ($i == "0") {
$dayofweek = date("w",$this->timestampBilling);
$hourofday = date("G",$this->timestampBilling);
$dayofyear = date("Y-m-d",$this->timestampBilling);
} else {
$dayofweek = date("w",$this->timestampBilling+$durationRatedTotal);
$hourofday = $foundRate['nextHourOfDay'];
$dayofyear = date("Y-m-d",$this->timestampBilling+$durationRatedTotal);
}
$foundRate = $this->lookupRate($dayofyear,$dayofweek,$hourofday,$durationRatedTotal);
$durationRatedTotal = $durationRatedTotal + $foundRate['duration'];
if (!$foundRate['rate']) {
return false;
}
$foundRates[] = $foundRate;
$i++;
if ($i > 10) {
// possible loop because of wrong coding make sure we can end this somehow
$body="Rating of call $this->callId (DestId=$this->DestinationId) has more than 10 spans. It could be a serious bug.\n";
mail($this->toEmail, "CDRTool rating problem", $body , $this->extraHeaders);
syslog(LOG_NOTICE, "Error: Rating of call $this->callId (DestId=$this->DestinationId) has more than 10 spans.");
break;
}
}
}
$j=0;
$span=0;
foreach ($foundRates as $thisRate) {
$spanPrice=0;
$span++;
if ($j > 0) {
$payConnect=0;
$durationForRating=$thisRate['duration'];
} else {
$payConnect=1;
if ($this->minimumDurationCharged && $this->duration < $this->minimumDurationCharged) {
$durationForRating=$this->minimumDurationCharged;
} else {
$durationForRating=$thisRate['duration'];
}
}
$connectCost = $thisRate['values']['connectCost'];
$durationRate = $thisRate['values']['durationRate'];
$trafficRate = $thisRate['values']['trafficRate'];
if ($span=="1") {
$connectCostSpan=$connectCost;
} else {
$connectCostSpan=0;
}
$connectCostPrint = number_format($connectCostSpan/$this->priceDenominator,$this->priceDecimalDigits);
$durationRatePrint = number_format($durationRate/$this->priceDenominator,$this->priceDecimalDigits);
$trafficRatePrint = number_format($trafficRate/$this->priceDenominator,$this->priceDecimalDigits);
if (!$connectCostSpan) $connectCostSpan=0;
if (!$durationRate) $durationRate=0;
if (!$trafficRate) $trafficRate=0;
if (!$this->inputTraffic) $this->inputTraffic=0;
if (!$this->outputTraffic) $this->outputTraffic=0;
if ($span>1) $this->rateInfo .= "--\n";
/*
durationRate*durationForRating/durationPeriodRated/priceDenominator+
trafficRate/priceDenominator/trafficSizeRated*(inputTraffic+outputTraffic)/8");
$durationRate*$durationForRating/$this->durationPeriodRated/$this->priceDenominator+
$trafficRate/$this->priceDenominator/$this->trafficSizeRated*($this->inputTraffic+$this->outputTraffic)/8");
*/
$spanPrice = $durationRate*$durationForRating/$this->durationPeriodRated/$this->priceDenominator+
$trafficRate/$this->priceDenominator/$this->trafficSizeRated*($this->inputTraffic+$this->outputTraffic)/8;
$this->price = $this->price+$spanPrice;
$spanPricePrint = number_format($spanPrice,$this->priceDecimalDigits);
$this->rateSyslog="";
if ($span=="1" && $thisRate[profile]) {
$this->rateInfo .=
" Connect fee: $connectCostPrint\n".
" StartTime: $this->startTimeBilling\n".
"--\n";
$this->rateSyslog .= "ConnectFee=$connectCostPrint ";
$this->price = $this->price+$connectCostSpan/$this->priceDenominator*$payConnect;
}
$this->rateInfo .=
" Span: $span\n".
" Duration: $durationForRating s\n";
$this->rateSyslog .= "Span=$span Duration=$durationForRating DestId=$this->DestinationId $thisRate[customer]";
if ($thisRate['profile']) {
$this->rateInfo .=
" ProfileId: $thisRate[profile] / $thisRate[day]\n".
" RateId: $thisRate[rate] / $thisRate[interval]h\n".
" Rate: $durationRatePrint / $this->durationPeriodRated s\n".
" Price: $spanPricePrint\n";
#" TrafficRate: $trafficRatePrint / $this->trafficSizeRated KBytes\n".
$this->rateSyslog .= " Profile=$thisRate[profile] Period=$thisRate[day] Rate=$thisRate[rate] Interval=$thisRate[interval] Cost=$durationRatePrint/$this->durationPeriodRated";
} else {
$this->rateInfo .=
" ProfileId: none\n".
" RateId: none\n";
$this->rateSyslog .= " Profile=none, Rate=none";
}
$this->rateSyslog .= " Price=".sprintf("%.4f",$spanPrice);
syslog(LOG_NOTICE, $this->rateSyslog);
$j++;
}
$this->rateInfo=trim($this->rateInfo);
if ($this->ENUMdiscount) {
$this->priceBeforeDiscount=sprintf("%.4f",$this->price);
$this->price = $this->price - $this->price*$this->ENUMdiscount/100;
$this->price=sprintf("%.4f",$this->price);
$this->rateInfo .=
"\n--\n".
" Total: $this->priceBeforeDiscount\n".
" Total after discount: $this->price\n";
}
$this->price=sprintf("%.4f",$this->price);
if ($this->price > 0) {
$this->pricePrint=number_format($this->price,$this->priceDecimalDigits);
} else if ($thisRate[profile]) {
if ($j) {
if ($this->DestinationId && !strlen($durationRate)) {
$this->brokenRates[$this->DestinationId]++;
}
}
$this->pricePrint="";
}
return true;
}
function lookupProfiles() {
unset($this->allProfiles);
/*
lookup the profile_name in billing_customers in the following order:
subscriber, domain, gateway (based on $dayofweek):
- profile1 matches days [1-5] (Work-day)
- profile2 matches days [6-0] (Week-end)
- week starts with 0 Sunday and ends with 6 Saturday
Alternatively look for profile1_alt and profile2_alt
If no rates are found for destination in the profileX,
than lookup rates in profileX_alt
*/
$query=sprintf("select * from billing_customers
where subscriber = '%s'
or domain = '%s'
or gateway = '%s'
or (subscriber = '' and domain = '' and gateway = '')
order by subscriber desc,
domain desc,
gateway desc
limit 1
",
addslashes($this->BillingPartyId),
addslashes($this->domain),
addslashes($this->gateway)
);
//syslog(LOG_NOTICE, $query);
if (!$this->db->query($query)) {
$log=sprintf ("Database error for query %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
print $log;
syslog(LOG_NOTICE, $log);
return false;
}
$log=sprintf("Found %d rows",$this->db->num_rows());
if ($this->db->num_rows()) {
$this->db->next_record();
if ($this->db->Record['profile_name1'] && $this->db->Record['profile_name2']) {
$this->allProfiles = array (
"profile1" => $this->db->Record['profile_name1'],
"profile2" => $this->db->Record['profile_name2'],
"profile1_alt" => $this->db->Record['profile_name1_alt'],
"profile2_alt" => $this->db->Record['profile_name2_alt'],
"timezone" => $this->db->Record['timezone'],
"increment" => $this->db->Record['increment'],
"min_duration" => $this->db->Record['min_duration'],
"country_code" => $this->db->Record['country_code']
);
if ($this->db->Record['subscriber']) {
$this->CustomerProfile = sprintf("subscriber=%s",$this->db->Record['subscriber']);
} else if ($this->db->Record['domain']) {
$this->CustomerProfile = sprintf("domain=%s",$this->db->Record['domain']);
} else if ($this->db->Record['gateway']) {
$this->CustomerProfile = sprintf("gateway=%s",$this->db->Record['gateway']);
} else {
$this->CustomerProfile = "default";
}
$this->increment = $this->db->Record['increment'];
$this->min_duration = $this->db->Record['min_duration'];
$this->billingTimezone = $this->db->Record['timezone'];
if ($this->min_duration > 1) $this->minimumDurationCharged = $this->min_duration;
return true;
} else {
return false;
}
} else {
return false;
}
}
function lookupRate($dayofyear,$dayofweek,$hourofday,$durationRatedAlready) {
/*
// Required information from CDR structure
$this->BillingPartyId # calling subscriber
$this->domain # multiple callers may belong to same domain
$this->gateway # multiple callers may belong to the same gateway
$this->cNumber # E164 destination prefixed with 00 (e.g. 0041 CH)
$this->DestinationId # longest matched DestinationId
// pertinent to the curent rating SPAN (a span = same profile like evening hours)
$hourofday # which hour of teh day started for peak/ofpeak rates
$dayofweek # which day of the week for matching profiles
$dayofyear # which day of the year for matching holidays
$durationRatedAlready= the full duration for which a profile is defined (e.g. 0800-1800)
// the call is called recursively until the $durationRatedAlready = $CDR->duration
// when a call spans multiple profiles. If we span multiple profiles we must call
// the function again to lookup the corect rates
Rating logic
------------
1. using the profile_name found, lookup the rate_name based
on $hourofday in billing_profiles
- the day may be split in maximum 4 periods
- each day starts with hour 0 and ends with hour 24
- rate_name1 defines the first interval after hour 0
- rate_name2 defines the first interval after rate_name1
- rate_name3 defines the first interval after rate_name2
- rate_name4 defines the first interval after rate_name3
When the hour matches an interval use the rate_nameX found
to lookup the rate in billing_rates
- if no record is found use the rate called 'default'
2. lookup in billing_rates the record having same name found above
and billing_rates.destination = $this->DestinationId
- return an array with all the values to
$this->calculate() function that called us
*/
// get work-day or weekend profile
if ($this->RatingTables->holidays[$dayofyear]) {
$this->profileName = $this->allProfiles[profile2];
$this->profileNameAlt = $this->allProfiles[profile2_alt];
$this->PeriodOfProfile = "weekend";
} else {
if ($dayofweek >=1 && $dayofweek <=5 ) {
$this->profileName = $this->allProfiles[profile1];
$this->profileNameAlt = $this->allProfiles[profile1_alt];
$this->PeriodOfProfile = "weekday";
} else {
$this->profileName = $this->allProfiles[profile2];
$this->profileNameAlt = $this->allProfiles[profile2_alt];
$this->PeriodOfProfile = "weekend";
}
}
// get rate for the time of the day
$timestampNextProfile = $this->timestampBilling + $durationRatedAlready;
$profileValues = $this->RatingTables->profiles[$this->profileName];
$profileValuesAlt = $this->RatingTables->profiles[$this->profileNameAlt];
if (is_array($profileValues)) {
if ($hourofday < $profileValues['hour1'] ) {
$this->rateName = $profileValues['rate_name1'];
$this->timeInterval = "0-".$profileValues['hour1'];
$foundProfile = $profileValues['hour1'];
$this->nextProfile = $profileValues['hour1'];
} else if ($hourofday < $profileValues['hour2']) {
$this->rateName = $profileValues['rate_name2'];
$this->timeInterval = $profileValues['hour1']."-".$profileValues['hour2'];
$foundProfile = $profileValues['hour2'];
$this->nextProfile = $profileValues['hour2'];
} else if ($hourofday < $profileValues['hour3']) {
$this->rateName = $profileValues['rate_name3'];
$this->timeInterval = $profileValues['hour2']."-".$profileValues['hour3'];
$foundProfile = $profileValues['hour3'];
$this->nextProfile = $profileValues['hour3'];
} else if ($hourofday < $profileValues['hour4']) {
$this->rateName = $profileValues['rate_name4'];
$this->timeInterval = $profileValues['hour3']."-".$profileValues['hour4'];
$foundProfile = $profileValues['hour4'];
$this->nextProfile = 0;
}
if ($this->rateName) {
$found_history=false;
//get historical rating if exists
if (is_array($this->RatingTables->ratesHistory[$this->rateName][$this->DestinationId][$this->applicationType])) {
$h=0;
foreach (($this->RatingTables->ratesHistory[$this->rateName][$this->DestinationId][$this->applicationType]) as $_idx) {
$h++;
if ($_idx['startDate'] <= $this->timestamp) {
if ($_idx['endDate'] > $this->timestamp) {
// found historical rate
$found_history=true;
$this->rateValues=$_idx;
break;
} else {
$_log=sprintf("Interval missmatch %s < %s",$_idx['endDate'],$this->timestamp);
continue;
}
} else {
$_log=sprintf("Interval missmatch %s > %s",$_idx['startDate'],$this->timestamp);
continue;
}
}
}
if (!$found_history) {
$this->rateValues=$this->lookupRateValues($this->rateName,$this->DestinationId,$this->applicationType);
}
} else {
}
if (!$this->rateValues && is_array($profileValuesAlt)) {
if ($hourofday < $profileValuesAlt['hour1'] ) {
$this->rateName = $profileValuesAlt['rate_name1'];
$this->timeInterval = "0-".$profileValuesAlt['hour1'];
$foundProfile = $profileValuesAlt['hour1'];
$this->nextProfile = $profileValuesAlt['hour1'];
} else if ($hourofday < $profileValuesAlt['hour2']) {
$this->rateName = $profileValuesAlt['rate_name2'];
$this->timeInterval = $profileValuesAlt['hour1']."-".$profileValuesAlt['hour2'];
$foundProfile = $profileValuesAlt['hour2'];
$this->nextProfile = $profileValuesAlt['hour2'];
} else if ($hourofday < $profileValuesAlt['hour3']) {
$this->rateName = $profileValuesAlt['rate_name3'];
$this->timeInterval = $profileValuesAlt['hour2']."-".$profileValuesAlt['hour3'];
$foundProfile = $profileValuesAlt['hour3'];
$this->nextProfile = $profileValuesAlt['hour3'];
} else if ($hourofday < $profileValuesAlt['hour4']) {
$this->rateName = $profileValuesAlt['rate_name4'];
$this->timeInterval = $profileValuesAlt['hour3']."-".$profileValuesAlt['hour4'];
$foundProfile = $profileValuesAlt['hour4'];
$this->nextProfile = 0;
}
if ($this->rateName) {
$found_history=false;
//get historical rating if exists
if (is_array($this->RatingTables->ratesHistory[$this->rateName][$this->DestinationId][$this->applicationType])) {
$h=0;
foreach (($this->RatingTables->ratesHistory[$this->rateName][$this->DestinationId][$this->applicationType]) as $_idx) {
$h++;
if ($_idx['startDate'] <= $this->timestamp) {
if ($_idx['endDate'] > $this->timestamp) {
// found historical rate
$found_history=true;
$this->rateValues=$_idx;
break;
} else {
$_log=sprintf("Interval missmatch %s < %s",$_idx['endDate'],$this->timestamp);
continue;
}
} else {
$_log=sprintf("Interval missmatch %s > %s",$_idx['startDate'],$this->timestamp);
continue;
}
}
}
if (!$found_history) {
$this->rateValues=$this->lookupRateValues($this->rateName,$this->DestinationId,$this->applicationType);
}
} else {
}
}
}
if (!$this->rateValues) {
$this->rateNotFound=true;
$log=sprintf("Error: Cannot find rates for callid=%s, billing party=%s, customer %s, gateway=%s, destination=%s, profile=%s, app=%s",
$this->callId,$this->BillingPartyId,$this->CustomerProfile,$this->gateway,$this->DestinationId,$this->profileName,$this->applicationType);
/*
$subject=$this->CDRS->CDRTool['provider']['service']." - CDRTool rating problem";
mail($this->toEmail, $subject, $log, $this->extraHeaders);
*/
syslog(LOG_NOTICE, $log);
return false;
}
if ($this->nextProfile == "24") $this->nextProfile = 0;
$DST = Date("I",$timestampNextProfile);
if (!$this->nextProfile) {
// check it we change daylight saving time tomorrow
// yes this cann happen and we must apply a different rate
$timestampNextProfile =$timestampNextProfile+24*3600;
$DSTNext = Date("I",$timestampNextProfile);
if ($DST != $DSTNext) {
if ($DSTNext==0) {
$timestampNextProfile=$timestampNextProfile+3600;
} else if ($DSTNext==1) {
$timestampNextProfile=$timestampNextProfile-3600;
}
}
}
$durationToRate=$this->duration-$durationRatedAlready;
$month = Date("m",$timestampNextProfile);
$day = Date("d",$timestampNextProfile);
$year = Date("Y",$timestampNextProfile);
$nextProfileTimestamp=mktime($this->nextProfile, 0, 0, $month,$day,$year);
$npdt=Date("Y-m-d H:i", $nextProfileTimestamp);
$timeTillNextProfile=$nextProfileTimestamp-$this->timestampBilling;
if ($durationToRate > $timeTillNextProfile) {
$diff=$durationToRate-$timeTillNextProfile;
$this->durationRated=$timeTillNextProfile;
} else {
$this->durationRated=$durationToRate;
}
$rate=array(
"customer" =>$this->CustomerProfile,
"application" =>$this->applicationType,
"profile" =>$this->profileName,
"day" =>$this->PeriodOfProfile,
"destinationId" =>$this->DestinationId,
"duration" =>$this->durationRated,
"rate" =>$this->rateName,
"values" =>$this->rateValues,
"interval" =>$this->timeInterval,
"nextHourOfDay" =>$this->nextProfile
);
return $rate;
}
function MaxSessionTime(&$dictionary) {
// Used for prepaid application
$this->MaxSessionTimeSpans=0;
$trafficRate = 0;
$durationRate = 0;
/////////////////////////////////////////////////////
// required fields passed from the CDR structure
//
$this->timestamp = time();
$this->callId = $dictionary['callId'];
$this->DestinationId = $dictionary['DestinationId'];
$this->BillingPartyId = $dictionary['BillingPartyId'];
$this->domain = $dictionary['domain'];
$this->duration = $dictionary['duration'];
$this->aNumber = $dictionary['aNumber'];
$this->cNumber = $dictionary['cNumber'];
$this->RatingTables = &$dictionary['RatingTables'];
$this->applicationType = $dictionary['Application'];
$Balance = $dictionary['Balance'];
if (!$this->applicationType) $this->applicationType='audio';
if (!$this->DestinationId) {
return false;
}
$this->lookupProfiles();
$this->startTimeBilling = getLocalTime($this->billingTimezone,$this->timestamp);
list($dateText,$timeText) = explode(" ",trim($this->startTimeBilling));
$Bdate = explode("-",$dateText);
$Btime = explode(":",$timeText);
$this->timestampBilling = mktime($Btime[0], $Btime[1], $Btime[2], $Bdate[1], $Bdate[2], $Bdate[0]);
$this->startTimeBilling = Date("Y-m-d H:i:s",$this->timestampBilling);
$i=0;
$durationRatedTotal=0;
while ($Balance > 0 ) {
$span++;
$this->MaxSessionTimeSpans++;
//syslog(LOG_NOTICE,"Getting rate $i");
if ($i == "0") {
$dayofweek = date("w",$this->timestampBilling);
$hourofday = date("G",$this->timestampBilling);
$dayofyear = date("Y-m-d",$this->timestampBilling);
} else {
$dayofweek = date("w",$this->timestampBilling+$durationRatedTotal);
$hourofday = $foundRate['nextHourOfDay'];
$dayofyear = date("Y-m-d",$this->timestampBilling+$durationRatedTotal);
}
$foundRate = $this->lookupRate($dayofyear,$dayofweek,$hourofday,$durationRatedTotal);
if ($this->rateNotFound) {
// break here to avoid loops
break;
}
$thisRate=$foundRate;
if ($j > 0) {
$payConnect=0;
$durationForRating = $thisRate['duration'];
} else {
$payConnect=1;
if ($this->minimumDurationCharged && $this->duration < $this->minimumDurationCharged) {
$durationForRating=$this->minimumDurationCharged;
} else {
$durationForRating=$thisRate['duration'];
}
}
$j++;
$connectCost = $thisRate['values']['connectCost'];
$durationRate = $thisRate['values']['durationRate'];
if ($span=="1") {
$connectCostSpan=$connectCost;
$setupBalanceRequired=$connectCost/$this->priceDenominator;
if ($connectCost && $Balance <= $setupBalanceRequired) {
syslog(LOG_NOTICE,"Balance too small: $Balance <= $setupBalanceRequired");
return false;
}
$Balance = $Balance-$setupBalanceRequired;
} else {
$connectCostSpan=0;
$setupBalanceRequired=0;
}
$connectCostPrint = number_format($connectCostSpan/$this->priceDenominator,$this->priceDecimalDigits);
$durationRatePrint = number_format($durationRate/$this->priceDenominator,$this->priceDecimalDigits);
$spanPrice = $this->price+$setupBalanceRequired*$payConnect+
$durationRate*$durationForRating/$this->durationPeriodRated/$this->priceDenominator;
if ($Balance > $spanPrice) {
$Balance = $Balance-$spanPrice;
$durationRatedTotal = $durationRatedTotal+ $foundRate['duration'];
} else {
$durationAllowedinThisSpan = $Balance /
$durationRate * $this->durationPeriodRated * $this->priceDenominator;
$rateOfThisSpan=$durationRate/$this->priceDenominator;
$durationRatedTotal=$durationRatedTotal + $durationAllowedinThisSpan;
$Balance=$Balance-$spanPrice;
return $durationRatedTotal;
}
if ($durationRatedTotal >= $this->duration) {
return sprintf("%f",$durationRatedTotal);
}
$i++;
if ($i>10) {
return sprintf("%f",$durationRatedTotal);
break;
}
}
return false;
}
function lookupRateValues($rateName,$DestinationId,$applicationType='audio') {
if ($this->settings['split_rating_table']) {
if ($rateName) {
$table="billing_rates_".$rateName;
} else {
$table="billing_rates_default";
}
} else {
$table="billing_rates";
}
// lookup rate from MySQL
$query=sprintf("select durationRate, trafficRate, connectCost from %s
where name = '%s' and destination = '%s' and application = '%s'",
$table,
$rateName,
$DestinationId,
$applicationType
);
if (!$this->db->query($query)) {
if ($this->db->Errno != 1146) {
$log=sprintf ("Database error for query %s: %s (%s)",$query,$this->db->Error,$this->db->Errnoc);
print $log;
syslog(LOG_NOTICE, $log);
return false;
}
// try the main table
// lookup rate from MySQL
$query=sprintf("select durationRate, trafficRate, connectCost from billing_rates
where name = '%s' and destination = '%s' and application = '%s'",
$rateName,
$DestinationId,
$applicationType
);
if (!$this->db->query($query)) {
$log=sprintf ("Database error for query %s: %s (%s)",$query,$this->db->Error,$this->db->Errnoc);
print $log;
syslog(LOG_NOTICE, $log);
return false;
}
}
$log=sprintf("Found %d rows",$this->db->num_rows());
if ($this->db->num_rows()) {
$this->db->next_record();
return array(
"durationRate" => $this->db->Record['durationRate'],
"trafficRate" => $this->db->Record['trafficRate'],
"connectCost" => $this->db->Record['connectCost']
);
} else {
return false;
}
}
}
class RatingTables {
var $delimiter=",";
var $filesToImport=array();
var $importFilesPatterns=array('rates_history',
'rates',
'profiles',
'destinations',
'customers'
);
var $mustReload = false;
function RatingTables () {
global $CDRTool;
global $RatingEngine;
$this->db = new DB_cdrtool;
$this->CDRTool = $CDRTool;
$this->db->Halt_On_Error="no";
if ($RatingEngine['csv_delimiter']) {
$this->delimiter=$RatingEngine['csv_delimiter'];
}
$this->tables=array(
"destinations"=>array("name"=>"Destinations",
"keys"=>array("id"),
"exceptions" =>array(),
"domainFilterColumn"=>"domain",
"fields"=>array(
"gateway"=>array("size"=>15,
"name"=>"Trusted peer"
),
"domain"=>array("size"=>15,
"name"=>"Domain"
),
"subscriber"=>array("size"=>15,
"name"=>"Subscriber"
),
"dest_id"=>array("size"=>20,
"name"=>"Destination Id"
),
"dest_name"=>array("size"=>20,
"name"=>"Description"
)
)
),
"billing_customers"=>array("name"=>"Customers",
"keys"=>array("id"),
"domainFilterColumn"=>"domain",
"exceptions" =>array('country_code'),
"fields"=>array("gateway"=>array("size"=>15,
"name"=>"Trusted peer"
),
"domain"=>array("size"=>15,
"name"=>"Domain"
),
"subscriber"=>array("size"=>25,
"name"=>"Subscriber"
),
"profile_name1"=>array("size"=>8,
"name"=>"WeekDay"
),
"profile_name1_alt"=>array("size"=>8,
"name"=>"Fallback"
),
"profile_name2"=>array("size"=>8,
"name"=>"WeekEnd"
),
"profile_name2_alt"=>array("size"=>8,
"name"=>"Fallback"
),
"timezone" =>array("size"=>16,
"name"=>"Timezone"
),
"increment" =>array("size"=>3,
"name"=>"Incr"
),
"min_duration" =>array("size"=>3,
"name"=>"Minim"
),
"country_code" =>array("size"=>3,
"name"=>"CC"
)
)
),
"billing_profiles"=>array("name"=>"Profiles",
"keys"=>array("id"),
"exceptions" =>array(),
"size"=>6,
"domainFilterColumn"=>"domain",
"fields"=>array(
"gateway"=>array("size"=>15,
"name"=>"Trusted peer"
),
"domain"=>array("size"=>15,
"name"=>"Domain"
),
"subscriber"=>array("size"=>25,
"name"=>"Subscriber"
),
"name"=>array("size"=>10,
"name"=>"Profile Id"
),
"rate_name1"=>array("size"=>10,
"name"=>"Rate Id1"
),
"hour1"=>array("size"=>3,
"name"=>"00-H1"
),
"rate_name2"=>array("size"=>10,
"name"=>"Rate Id2"
),
"hour2"=>array("size"=>3,
"name"=>"H1-H2"
),
"rate_name3"=>array("size"=>10,
"name"=>"Rate Id3"
),
"hour3"=>array("size"=>3,
"name"=>"H2-H3"
),
"rate_name4"=>array("size"=>10,
"name"=>"Rate Id4"
),
"hour4"=>array("size"=>3,
"name"=>"H3-24"
),
)
),
"billing_rates"=>array("name"=>"Rates",
"keys"=>array("id"),
"size"=>10,
"domainFilterColumn"=>"domain",
"exceptions" =>array('trafficRate'),
"fields"=>array(
"gateway"=>array("size"=>15,
"name"=>"Trusted peer"
),
"domain"=>array("size"=>15,
"name"=>"Domain"
),
"subscriber"=>array("size"=>25,
"name"=>"Subscriber"
),
"name"=>array("size"=>10,
"name"=>"Rate Id"
),
"destination"=>array("size"=>15,
"name"=>"Destination"
),
"durationRate"=>array("size"=>10,
"name"=>"Price"
),
"application"=>array("size"=>6,
"name"=>"App"
),
"connectCost"=>array("size"=>10,
"name"=>"Connect"
)
)
),
"billing_rates_history"=>array("name"=>"Rates history",
"keys"=>array("id"),
"size"=>10,
"domainFilterColumn"=>"domain",
"exceptions" =>array('trafficRate'),
"fields"=>array(
"gateway"=>array("size"=>15,
"name"=>"Trusted peer"
),
"domain"=>array("size"=>15,
"name"=>"Domain"
),
"subscriber"=>array("size"=>20,
"name"=>"Subscriber"
),
"name"=>array("size"=>10,
"name"=>"Rate Id"
),
"destination"=>array("size"=>12,
"name"=>"Destination"
),
"durationRate"=>array("size"=>8,
"name"=>"Price"
),
"application"=>array("size"=>6,
"name"=>"App"
),
"connectCost"=>array("size"=>8,
"name"=>"Connect"
),
"startDate"=>array("size"=>11,
"name"=>"Start Date"
),
"endDate"=>array("size"=>11,
"name"=>"End Date"
)
)
),
"billing_enum_tlds"=>array("name"=>"ENUM TLDs",
"keys"=>array("id"),
"exceptions" =>array(),
"size"=>6,
"domainFilterColumn"=>"domain",
"fields"=>array(
"gateway"=>array("size"=>15,
"name"=>"Trusted peer"
),
"domain"=>array("size"=>15,
"name"=>"Domain"
),
"subscriber"=>array("size"=>25,
"name"=>"Subscriber"
),
"enum_tld"=>array("size"=>35,
"name"=>"ENUM TLD"
),
"e164_regexp"=>array("size"=>35,
"name"=>"E164 regexp"
),
"discount"=>array("size"=>10,
"name"=>"Discount"
)
)
),
"prepaid"=>array("name"=>"Prepaid accounts",
"keys"=>array("id"),
"size"=>15,
"exceptions" =>array(),
"domainFilterColumn"=>"account",
"fields"=>array("account"=>array("size"=>35,
"name"=>"SIP prepaid account",
"readonly"=>0
),
"balance"=>array("size"=>10,
"name"=>"Balance"
),
"change_date"=>array("size"=>19,
"name"=>"Last change",
"readonly"=>1
),
"call_lock"=>array("size"=>1,
"name"=>"Lock",
"readonly"=>0
),
"call_in_progress"=>array("size"=>18,
"name"=>"Call in progress",
"readonly"=>1
),
"last_call_price"=>array("size"=>10,
"name"=>"Last price",
"readonly"=>1
),
"maxsessiontime"=>array("size"=>5,
"name"=>"Max",
"readonly"=>1
),
"destination"=>array("size"=>15,
"name"=>"Last destination",
"readonly"=>1
)
)
),
"prepaid_cards"=>array("name"=>"Prepaid cards",
"keys"=>array("id"),
"size"=>15,
"exceptions" =>array('service'),
"domainFilterColumn"=>"account",
"fields"=>array("batch"=>array("size"=>30,
"name"=>"Batch name"
),
"date_batch"=>array("size"=>11,
"name"=>"Batch date"
),
"number"=>array("size"=>20,
"name"=>"Card number"
),
"id"=>array("size"=>20,
"name"=>"Card id"
),
"value"=>array("size"=>6,
"name"=>"Card value"
),
"blocked"=>array("size"=>1,
"name"=>"Lock"
),
"date_active"=>array("size"=>18,
"name"=>"Activation date"
)
)
)
);
}
function ImportCSVFiles($dir=false) {
$results=0;
if (!$dir) $dir=$this->CDRTool['Path']."/csv";
$this->scanFilesForImport($dir);
foreach ($this->filesToImport as $file) {
$importFunction="Import".ucfirst($file['type']);
printf("Reading file %s\n",$file['path']);
$results = $results + $this->$importFunction($file['path']);
$this->logImport($dir,$file['name'],$file['watermark'],$results);
}
return $results;
}
function ImportRates($file) {
if (!$file || !is_readable($file) || !$fp = fopen($file, "r")) return false;
$i=0;
$inserted = 0;
$updated = 0;
$deleted = 0;
print "Importing Rates:\n";
while ($buffer = fgets($fp,1024)) {
$buffer=trim($buffer);
$p = explode($this->delimiter, $buffer);
$ops = trim($p[0]);
$gateway = trim($p[1]);
$domain = trim($p[2]);
$subscriber = trim($p[3]);
$profile = trim($p[4]);
$destination = trim($p[5]);
$durationRate = trim($p[6]);
//$trafficRate = trim($p[7]);
$application = trim($p[7]);
$connectCost = trim($p[8]);
if (!$application) $application='audio';
if ($ops=="1") {
$query=sprintf("insert into billing_rates
(
gateway,
domain,
subscriber,
name,
destination,
durationRate,
trafficRate,
application,
connectCost
) values (
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s'
)",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($profile),
addslashes($destination),
addslashes($durationRate),
addslashes($trafficRate),
addslashes($application),
addslashes($connectCost)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$inserted++;
} else {
$failed++;
}
} else if ($ops=="3") {
$query=sprintf("delete from billing_rates
where gateway = '%s'
and domain = '%s'
and subscriber = '%s'
and name = '%s'
and destination = '%s'
and application = '%s'",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($profile),
addslashes($destination),
addslashes($application)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$deleted++;
}
} else if ($ops=="2") {
$query=sprintf("select * from billing_rates
where name = '%s'
and destination = '%s'
and gateway = '%s'
and domain = '%s'
and subscriber = '%s'
and application = '%s'
",
addslashes($profile),
addslashes($destination),
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($application)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->num_rows()) {
$query=sprintf("update billing_rates set
durationRate = '%s',
trafficRate = '%s',
connectCost = '%s'
where name = '%s'
and destination = '%s'
and gateway = '%s'
and domain = '%s'
and subscriber = '%s'
and application = '%s'
",
addslashes($durationRate),
addslashes($trafficRate),
addslashes($connectCost),
addslashes($profile),
addslashes($destination),
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($application)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$updated++;
}
} else {
$query=sprintf("insert into billing_rates
(
gateway,
domain,
subscriber,
name,
destination,
durationRate,
trafficRate,
application,
connectCost
) values (
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s'
)",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($profile),
addslashes($destination),
addslashes($durationRate),
addslashes($trafficRate),
addslashes($application),
addslashes($connectCost)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$inserted++;
} else {
$failed++;
}
}
}
$this->showImportProgress($file);
$i++;
}
if ($i) print "Read $i records\n";
if ($inserted) print "Inserted $inserted records\n";
if ($updated) print "Updated $updated records\n";
if ($deleted) print "Delete $deleted records\n";
$results=$inserted+$updated+$deleted;
return $results;
}
function ImportRatesHistory($file) {
if (!$file || !is_readable($file) || !$fp = fopen($file, "r")) return false;
$this->mustReload=true;
$i=0;
$inserted = 0;
$updated = 0;
$deleted = 0;
print "Importing Rates history:\n";
while ($buffer = fgets($fp,1024)) {
$buffer=trim($buffer);
$p = explode($this->delimiter, $buffer);
$ops = trim($p[0]);
$gateway = trim($p[1]);
$domain = trim($p[2]);
$subscriber = trim($p[3]);
$profile = trim($p[4]);
$destination = trim($p[5]);
$durationRate = trim($p[6]);
$trafficRate = trim($p[7]);
$application = trim($p[8]);
$connectCost = trim($p[9]);
$startDate = trim($p[10]);
$endDate = trim($p[11]);
if ($ops=="1") {
$query=sprintf("insert into billing_rates_history
(
gateway,
domain,
subscriber,
name,
destination,
durationRate,
trafficRate,
application,
connectCost,
startDate,
endDate
) values (
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s'
)",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($profile),
addslashes($destination),
addslashes($durationRate),
addslashes($trafficRate),
addslashes($application),
addslashes($connectCost),
addslashes($startDate),
addslashes($endDate)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$inserted++;
} else {
$failed++;
}
} else if ($ops=="3") {
$query=sprintf("delete from billing_rates_history
where gateway = '%s'
and domain = '%s'
and subscriber = '%s'
and name = '%s'
and destination = '%s'
and startDate = '%s'
and endDate = '%s'",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($profile),
addslashes($destination),
addslashes($startDate),
addslashes($endDate)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$deleted++;
}
} else if ($ops=="2") {
$query=sprintf("select * from billing_rates_history
where name = '%s'
and destination = '%s'
and gateway = '%s'
and domain = '%s'
and subscriber = '%s'
and startDate = '%s'
and endDate = '%s'
",
addslashes($profile),
addslashes($destination),
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($startDate),
addslashes($endDate)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->num_rows()) {
$query=sprintf("update billing_rates_history set
durationRate = '%s',
trafficRate = '%s',
application = '%s',
connectCost = '%s'
where name = '%s'
and destination = '%s'
and gateway = '%s'
and domain = '%s'
and subscriber = '%s'
and startDate = '%s'
and endDate = '%s'
",
addslashes($durationRate),
addslashes($trafficRate),
addslashes($application),
addslashes($connectCost),
addslashes($profile),
addslashes($destination),
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($startDate),
addslashes($endDate)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$updated++;
}
} else {
$query=sprintf("insert into billing_rates_history
(
gateway,
domain,
subscriber,
name,
destination,
durationRate,
trafficRate,
application,
connectCost,
startDate,
endDate
) values (
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s'
)",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($profile),
addslashes($destination),
addslashes($durationRate),
addslashes($trafficRate),
addslashes($application),
addslashes($connectCost),
addslashes($startDate),
addslashes($endDate)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$inserted++;
} else {
$failed++;
}
}
}
$j++;
if ($j=="10000") {
flush();
$j=0;
}
$this->showImportProgress($file);
$i++;
}
if ($i) print "Read $i records\n";
if ($inserted) print "Inserted $inserted records\n";
if ($updated) print "Updated $updated records\n";
if ($deleted) print "Delete $deleted records\n";
$results=$inserted+$updated+$deleted;
return $results;
}
function ImportCustomers($file) {
if (!$file || !is_readable($file) || !$fp = fopen($file, "r")) return false;
$this->mustReload=true;
$i=0;
$inserted = 0;
$updated = 0;
$deleted = 0;
print "Importing Customers:\n";
while ($buffer = fgets($fp,1024)) {
$buffer=trim($buffer);
$p = explode($this->delimiter, $buffer);
$ops = trim($p[0]);
$gateway = trim($p[1]);
$domain = trim($p[2]);
$subscriber = trim($p[3]);
$profile_name1 = trim($p[4]);
$profile_name1_alt = trim($p[5]);
$profile_name2 = trim($p[6]);
$profile_name2_alt = trim($p[7]);
$timezone = trim($p[8]);
$increment = trim($p[9]);
$min_duration = trim($p[10]);
$country_code = trim($p[11]);
if ($ops=="1") {
$query=sprintf("insert into billing_customers
(
gateway,
domain,
subscriber,
profile_name1,
profile_name2,
timezone,
profile_name1_alt,
profile_name2_alt,
increment,
min_duration,
country_code
) values (
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s'
)",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($profile_name1),
addslashes($profile_name2),
addslashes($timezone),
addslashes($profile_name1_alt),
addslashes($profile_name2_alt),
addslashes($increment),
addslashes($min_duration),
addslashes($country_code)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$inserted++;
} else {
$failed++;
}
} else if ($ops=="3") {
$query=sprintf("delete from billing_customers
where gateway = '%s'
and domain = '%s'
and subscriber = '%s'
",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$deleted++;
}
} else if ($ops=="2") {
$query=sprintf("select * from billing_customers
where gateway = '%s'
and domain = '%s'
and subscriber = '%s'
",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->num_rows()) {
$query=sprintf("update billing_customers set
profile_name1 = '%s',
profile_name2 = '%s',
profile_name1_alt = '%s',
profile_name2_alt = '%s',
timezone = '%s',
increment = '%s',
min_duration = '%s',
country_code = '%s'
where gateway = '%s'
and domain = '%s'
and subscriber = '%s'\n",
addslashes($profile_name1),
addslashes($profile_name2),
addslashes($profile_name1_alt),
addslashes($profile_name2_alt),
addslashes($timezone),
addslashes($increment),
addslashes($min_duration),
addslashes($country_code),
addslashes($gateway),
addslashes($domain),
addslashes($subscriber)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows()) {
$updated++;
}
} else {
$query=sprintf("insert into billing_customers
(
gateway,
domain,
subscriber,
profile_name1,
profile_name2,
timezone,
profile_name1_alt,
profile_name2_alt,
increment,
min_duration,
country_code
) values (
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s'
)",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($profile_name1),
addslashes($profile_name2),
addslashes($timezone),
addslashes($profile_name1_alt),
addslashes($profile_name2_alt),
addslashes($increment),
addslashes($min_duration),
addslashes($country_code)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows()) {
$inserted++;
}
}
}
$this->showImportProgress($file);
$i++;
}
if ($i) print "Read $i records\n";
if ($inserted) print "Inserted $inserted records\n";
if ($updated) print "Updated $updated records\n";
if ($deleted) print "Delete $deleted records\n";
$results=$inserted+$updated+$deleted;
return $results;
}
function ImportDestinations($file) {
if (!$file || !is_readable($file) || !$fp = fopen($file, "r")) return false;
$this->mustReload=true;
$i=0;
$inserted = 0;
$updated = 0;
$deleted = 0;
print "Importing Destinations:\n";
while ($buffer = fgets($fp,1024)) {
$buffer=trim($buffer);
$p = explode($this->delimiter, $buffer);
$ops = trim($p[0]);
$gateway = trim($p[1]);
$domain = trim($p[2]);
$subscriber = trim($p[3]);
$dest_id = trim($p[4]);
$dest_name = trim($p[5]);
if ($ops=="1") {
$query=sprintf("insert into destinations
(
gateway,
domain,
subscriber,
dest_id,
dest_name
) values (
'%s',
'%s',
'%s',
'%s',
'%s'
)",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($dest_id),
addslashes($dest_name)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$inserted++;
} else {
$failed++;
}
} elseif ($ops=="3") {
$query=sprintf("delete from destinations
where gateway = '%s'
and domain = '%s'
and subscriber = '%s'
and dest_id = '%s'
",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($dest_id)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$deleted++;
}
} elseif ($ops=="2") {
$query=sprintf("select * from destinations
where gateway = '%s'
and domain = '%s'
and subscriber = '%s'
and dest_id = '%s'
",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($dest_id)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->num_rows()) {
$query=sprintf("update destinations set
dest_name = '%s'
where gateway = '%s'
and domain = '%s'
and subscriber = '%s'
and dest_id = '%s'
",
addslashes($dest_name),
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($dest_id)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows()) {
$updated++;
}
} else {
$query=sprintf("insert into destinations
(
gateway,
domain,
subscriber,
dest_id,
dest_name
) values (
'%s',
'%s',
'%s',
'%s',
'%s'
)",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($dest_id),
addslashes($dest_name)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$inserted++;
} else {
$failed++;
}
}
}
$this->showImportProgress($file);
$i++;
}
if ($i) print "Read $i records\n";
if ($inserted) print "Inserted $inserted records\n";
if ($updated) print "Updated $updated records\n";
if ($deleted) print "Delete $deleted records\n";
$results=$inserted+$updated+$deleted;
return $results;
}
function ImportProfiles($file) {
if (!$file || !is_readable($file) || !$fp = fopen($file, "r")) return false;
$this->mustReload=true;
$i=0;
$inserted = 0;
$updated = 0;
$deleted = 0;
print "Importing Profiles:\n";
while ($buffer = fgets($fp,1024)) {
$buffer=trim($buffer);
$p = explode($this->delimiter, $buffer);
$ops = trim($p[0]);
$gateway = trim($p[1]);
$domain = trim($p[2]);
$subscriber = trim($p[3]);
$profile = trim($p[4]);
$rate1 = trim($p[5]);
$hour1 = trim($p[6]);
$rate2 = trim($p[7]);
$hour2 = trim($p[8]);
$rate3 = trim($p[9]);
$hour3 = trim($p[10]);
$rate4 = trim($p[11]);
$hour4 = trim($p[12]);
if (!$hour1) $hour1=0;
if (!$hour2) $hour2=0;
if (!$hour3) $hour3=0;
if (!$hour4) $hour4=0;
if ($ops=="1") {
$query=sprintf("insert into billing_profiles
(
gateway,
domain,
subscriber,
name,
rate_name1,
hour1,
rate_name2,
hour2,
rate_name3,
hour3,
rate_name4,
hour4
) values (
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s'
)",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($profile),
addslashes($rate1),
addslashes($hour1),
addslashes($rate2),
addslashes($hour2),
addslashes($rate3),
addslashes($hour3),
addslashes($rate4),
addslashes($hour4)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$inserted++;
} else {
$failed++;
}
} else if ($ops=="3") {
$query=sprintf("delete from billing_profiles
where name = '%s'
and gateway = '%s'
and domain = '%s'
and subscriber = '%s'
",
addslashes($profile),
addslashes($gateway),
addslashes($domain),
addslashes($subscriber)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$deleted++;
}
} else if ($ops=="2") {
$query=sprintf("select * from billing_profiles
where name = '%s'
and gateway = '%s'
and domain = '%s'
and subscriber = '%s'\n",
addslashes($profile),
addslashes($gateway),
addslashes($domain),
addslashes($subscriber)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->num_rows()) {
$query=sprintf("update billing_profiles set
rate_name1 = '%s',
rate_name2 = '%s',
rate_name3 = '%s',
rate_name4 = '%s',
hour1 = '%s',
hour2 = '%s',
hour3 = '%s',
hour4 = '%s'
where name = '%s'
and gateway = '%s'
and domain = '%s'
and subscriber = '%s'\n",
addslashes($rate1),
addslashes($rate2),
addslashes($rate3),
addslashes($rate4),
addslashes($hour1),
addslashes($hour2),
addslashes($hour3),
addslashes($hour4),
addslashes($profile),
addslashes($gateway),
addslashes($domain),
addslashes($subscriber)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows()) {
$updated++;
}
} else {
$query=sprintf("insert into billing_profiles
(
gateway,
domain,
subscriber,
name,
rate_name1,
hour1,
rate_name2,
hour2,
rate_name3,
hour3,
rate_name4,
hour4
) values (
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s'
)",
addslashes($gateway),
addslashes($domain),
addslashes($subscriber),
addslashes($profile),
addslashes($rate1),
addslashes($hour1),
addslashes($rate2),
addslashes($hour2),
addslashes($rate3),
addslashes($hour3),
addslashes($rate4),
addslashes($hour4)
);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
if ($this->db->affected_rows() >0) {
$inserted++;
} else {
$failed++;
}
}
}
$this->showImportProgress($file);
$i++;
}
if ($i) print "Read $i records\n";
if ($inserted) print "Inserted $inserted records\n";
if ($updated) print "Updated $updated records\n";
if ($deleted) print "Delete $deleted records\n";
$results=$inserted+$updated+$deleted;
return $results;
}
function LoadRatingTables () {
$log=sprintf("Memory usage: %0.2fMB, memory limit: %sB",memory_get_usage()/1024/1024,ini_get('memory_limit'));
syslog(LOG_NOTICE, $log);
$loaded['profiles'] = $this->LoadProfilesTable();
$loaded['ratesHistory'] = $this->LoadRatesHistoryTable();
$loaded['holidays'] = $this->LoadHolidaysTable();
$loaded['enumTlds'] = $this->LoadENUMtldsTable();
foreach(array_keys($loaded) as $_load) {
syslog(LOG_NOTICE, "Loaded $loaded[$_load] $_load");
}
$log=sprintf("Memory usage: %0.2fMB, memory limit: %sB",memory_get_usage()/1024/1024,ini_get('memory_limit'));
syslog(LOG_NOTICE, $log);
return $loaded;
}
function LoadENUMtldsTable() {
$query="select * from billing_enum_tlds";
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
$i=0;
$rows=$this->db->num_rows();
while($this->db->next_record()) {
if ($this->db->Record['enum_tld']) {
$i++;
$_app=$this->db->Record['application'];
if (!$_app) $_app='audio';
$_ENUMtlds[$this->db->Record['enum_tld']]=
array(
"discount" => $this->db->Record['discount'],
"e164_regexp" => $this->db->Record['e164_regexp']
);
}
}
$this->ENUMtlds = $_ENUMtlds;
$this->ENUMtldsCount = $i;
return $i;
}
function LoadRatesHistoryTable() {
$query="select *,
UNIX_TIMESTAMP(startDate) as startDateTimestamp,
UNIX_TIMESTAMP(endDate) as endDateTimestamp
from billing_rates_history
order by name ASC,destination ASC,startDate DESC";
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
$i=0;
$rows=$this->db->num_rows();
while($this->db->next_record()) {
if ($this->db->Record['name'] && $this->db->Record['destination']) {
$i++;
$_app=$this->db->Record['application'];
if (!$_app) $_app='audio';
$_rates[$this->db->Record['name']][$this->db->Record['destination']][$_app][$this->db->Record['id']]=
array(
"durationRate" => $this->db->Record['durationRate'],
"trafficRate" => $this->db->Record['trafficRate'],
"connectCost" => $this->db->Record['connectCost'],
"startDate" => $this->db->Record['startDateTimestamp'],
"endDate" => $this->db->Record['endDateTimestamp']
);
}
}
$this->ratesHistory=$_rates;
$this->ratesHistoryCount=$i;
return $i;
}
function LoadProfilesTable() {
$query="select * from billing_profiles order by name";
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
$i=0;
while($this->db->next_record()) {
$i++;
if ($this->db->Record['name'] && $this->db->Record['hour1'] > 0 ) {
$_profiles[$this->db->Record['name']]=
array(
"rate_name1" => $this->db->Record['rate_name1'],
"hour1" => $this->db->Record['hour1'],
"rate_name2" => $this->db->Record['rate_name2'],
"hour2" => $this->db->Record['hour2'],
"rate_name3" => $this->db->Record['rate_name3'],
"hour3" => $this->db->Record['hour3'],
"rate_name4" => $this->db->Record['rate_name4'],
"hour4" => $this->db->Record['hour4'],
);
}
}
$this->profiles=$_profiles;
return $i;
}
function LoadHolidaysTable() {
$query="select * from billing_holidays order by day";
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
$i=0;
while($this->db->next_record()) {
if ($this->db->Record['day']) {
$i++;
$_holidays[$this->db->Record['day']]++;
}
}
$this->holidays=$_holidays;
return $i;
}
function checkRatingEngineConnection () {
global $RatingEngine;
if ($RatingEngine['socketIP'] && $RatingEngine['socketPort'] &&
$fp = fsockopen ($RatingEngine['socketIP'], $RatingEngine['socketPort'], $errno, $errstr, 2)) {
fclose($fp);
return true;
}
return false;
}
function showCustomers($filter) {
return true;
foreach (array_keys($this->customers) as $key) {
if (strlen($filter)) {
if (preg_match("/$filter/",$key)) {
$customers=$customers.$key."\n";
}
} else {
$customers=$customers.$key."\n";
}
}
return $customers;
}
function showProfiles() {
foreach (array_keys($this->profiles) as $key) {
$profiles=$profiles.$key."\n";
}
return $profiles;
}
function showENUMtlds() {
foreach (array_keys($this->ENUMtlds) as $key) {
$ENUMtlds=$ENUMtlds.$key."\n";
}
return $ENUMtlds;
}
function scanFilesForImport($dir) {
if (!$dir) $dir=$this->CDRTool['Path']."/csv";
if ($handle = opendir($dir)) {
while (false !== ($filename = readdir($handle))) {
if ($filename != "." && $filename != "..") {
foreach ($this->importFilesPatterns as $_pattern) {
if (strstr($filename,$_pattern) && preg_match("/\.csv$/",$filename)) {
$fullPath=$dir."/".$filename;
if ($content=file_get_contents($fullPath)) {
$watermark=$filename."-".md5($content);
if ($this->hasFileBeenImported($filename,$watermark)) break;
$this->filesToImport[]=array( 'name' => $filename,
'watermark' => $watermark,
'type' => $_pattern,
'path' => $fullPath
);
}
break;
}
}
}
}
}
}
function hasFileBeenImported($filename,$watermark) {
$query=sprintf("select * from log where url = '%s'\n",$watermark);
if ($this->db->query($query)) {
if ($this->db->num_rows()) {
$this->db->next_record();
$log=sprintf ("File %s has already been imported at %s.\n",$filename,$this->db->f('date'));
syslog(LOG_NOTICE, $log);
print $log;
return true;
} else {
return false;
}
} else {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
}
function logImport($dir,$filename,$watermark,$results=0) {
$query=sprintf("insert into log (date,login,ip,url,results,description,datasource)
values (NOW(),'ImportScript','localhost','%s','%s','Imported %s','%s')",
$watermark,$results,$filename,$dir
);
$log=sprintf ("Imported file %s, %d records have been affected\n",$filename,$results);
syslog(LOG_NOTICE, $log);
if (!$this->db->query($query)) {
printf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
return false;
}
}
function showImportProgress ($filename='unspecified',$increment=10000) {
$this->importIndex++;
if ($this->importIndex == $increment) {
printf ("Loaded %d records from %s\n",$this->importIndex,$filename);
flush();
$this->importIndex=0;
}
}
function splitRatingTable() {
$query="select distinct(name) from billing_rates order by name ASC";
if (!$this->db->query($query)) {
printf ("Database error for %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
return false;
}
while ($this->db->next_record()) {
$rate_names[]=$this->db->f('name');
}
foreach ($rate_names as $name) {
if (!$name) $name='default';
$table="billing_rates_".$name;
$query=sprintf("drop table if exists %s",$table);
if (!$this->db->query($query)) {
printf ("Database error for %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
return false;
}
if (!$this->db->query($query)) {
printf ("Database error for %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
return false;
}
$query=sprintf("create table %s select * from billing_rates where name = '%s'\n",$table,$name);
if (!$this->db->query($query)) {
printf ("Database error for %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
return false;
} else {
$query=sprintf("alter table %s add index rate_idx (name)",$table);
if (!$this->db->query($query)) {
printf ("Database error for %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
return false;
} else {
printf ("Added rate_idx index to table %s\n",$table);
}
$query=sprintf("alter table %s add index destination_idx (destination)",$table);
if (!$this->db->query($query)) {
printf ("Database error for %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
return false;
} else {
printf ("Added destination_idx index to table %s\n",$table);
}
$query=sprintf("select count(*) as c from %s",$table);
$this->db->query($query);
$this->db->next_record();
$records=$this->db->f('c');
printf ("Created table %s with %s records\n",$table,$records);
}
}
}
}
class SERQuota {
var $localDomains = array();
var $quotaGroup = 'quota'; // group set if subscriber was blocked by quota
function SERQuota(&$parent) {
global $DATASOURCES;
$this->AccountsDBClass = &$parent->AccountsDBClass;
$this->CDRdb = &$parent->CDRdb;
$this->table = &$parent->table;
$this->CDRTool = &$parent->CDRTool;
$this->cdr_source = &$parent->cdr_source;
if (!class_exists($this->AccountsDBClass)) {
print("Info: No database defined for SIP accounts $this->cdr_source.\n");
return false;
}
$this->AccountsDB = new $this->AccountsDBClass;
$this->enableThor = $parent->enableThor;
$parent->LoadDomains();
$this->localDomains = &$parent->localDomains;
$this->cdr_source = &$parent->cdr_source;
$this->BillingPartyIdField = &$parent->CDRFields['BillingPartyId'];
$this->parent = &$parent;
$this->db = new DB_cdrtool;
$this->db->Halt_On_Error="no";
$this->CDRS = &$parent;
$this->mc_key_accounts = $this->cdr_source.':accounts';
$this->mc_key_init = $this->cdr_source.':quotaCheckInit';
// load e-mail addresses for quota notifications
$query="select * from settings
where var_module = 'notifications'";
if ($this->db->query($query) && $this->db->num_rows()) {
while ($this->db->next_record()) {
$_bp =$this->db->f('billing_party');
$_name =$this->db->f('var_name');
$_value =$this->db->f('var_value');
if ($_bp && $_name && $_value) {
$this->notificationAddresses[$_bp][$_name]=$_value;
}
}
}
if ($DATASOURCES[$this->cdr_source]['soapEngineId']) {
require_once("provisioning/ngnpro_soap_library.phtml");
require_once("provisioning/ngnpro_engines.inc");
if (in_array($DATASOURCES[$this->cdr_source]['soapEngineId'],array_keys($soapEngines))) {
$this->SOAPurl = $soapEngines[$DATASOURCES[$this->cdr_source]['soapEngineId']]['url'];
$this->SOAPlogin = array(
"username" => $soapEngines[$DATASOURCES[$this->cdr_source]['soapEngineId']]['username'],
"password" => $soapEngines[$DATASOURCES[$this->cdr_source]['soapEngineId']]['password'],
"admin" => true
);
$this->SoapAuth=array('auth', $this->SOAPlogin , 'urn:AGProjects:NGNPro', 0, '');
$this->soapclient = new WebService_NGNPro_SipPort($this->SOAPurl);
$this->soapclient->setOpt('curl', CURLOPT_SSL_VERIFYPEER, 0);
$this->soapclient->setOpt('curl', CURLOPT_SSL_VERIFYHOST, 0);
$this->soapclient->setOpt('curl', CURLOPT_TIMEOUT, 2);
}
}
}
function LoadAccounts() {
$b=time();
if (!$this->AccountsDBClass) {
print("Info: No database defined for SIP accounts.\n");
return false;
}
if (!$this->LoadAccountsfromCache()) {
if ($this->enableThor) {
$query=sprintf("select * from sip_accounts");
if (!$this->AccountsDB->query($query)) {
$log=sprintf("Error: %s (%s)",$this->AccountsDB->Error,$this->AccountsDB->Errno);
syslog(LOG_NOTICE,$log);
return false;
}
$log=sprintf("Read %d accounts from Thor database\n",$this->AccountsDB->num_rows());
print $log;
syslog(LOG_NOTICE,$log);
while ($this->AccountsDB->next_record()) {
$i++;
$_account=$this->AccountsDB->f('username')."@".$this->AccountsDB->f('domain');
$_profile=json_decode(trim($this->AccountsDB->f('profile')));
$_accounts[$_account]=array('timezone' => $_profile->timezone,
'quota' => $_profile->quota,
'email' => $_profile->email,
'first_name' => $_profile->first_name,
'last_name' => $_profile->last_name
);
if (in_array('quota',$_profile->groups)) {
$_accounts[$account]['block_by_quota']= 1;
}
}
} else {
$query=sprintf("select * from subscriber");
if (!$this->AccountsDB->query($query)) {
$log=sprintf("Error: %s (%s)",$this->AccountsDB->Error,$this->AccountsDB->Errno);
syslog(LOG_NOTICE,$log);
return false;
}
$log=sprintf("Read %d accounts from SER database\n",$this->AccountsDB->num_rows());
print $log;
syslog(LOG_NOTICE,$log);
while ($this->AccountsDB->next_record()) {
if (strlen($this->AccountsDB->f('domain'))) {
$_account = $this->AccountsDB->f('username')."@".$this->AccountsDB->f('domain');
} else {
$_account = $this->AccountsDB->f('username');
}
$_accounts[$_account]=array('timezone' => $this->AccountsDB->f('timezone'),
'quota' => $this->AccountsDB->f('quota'),
'email' => $this->AccountsDB->f('email_address'),
'first_name' => $this->AccountsDB->f('first_name'),
'last_name' => $this->AccountsDB->f('last_name')
);
}
$query = sprintf("select * from grp where grp = '%s'",$this->quotaGroup);
if (!$this->AccountsDB->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->AccountsDB->Error,$this->AccountsDB->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
while($this->AccountsDB->next_record()) {
$account=$this->AccountsDB->f('username')."@".$this->AccountsDB->f('domain');
$_accounts[$account]['block_by_quota']= 1;
}
}
$this->Accounts = &$_accounts;
$this->SaveAccountsToCache();
}
$count=count($this->Accounts);
$e=time();
$d=$e-$b;
if ($d >0 ) syslog(LOG_NOTICE, "Loaded $count SIP accounts in $d seconds");
return $count;
}
function SaveAccountsToCache() {
$b=time();
unset($mc_value);
foreach (array_keys($this->Accounts) as $account) {
if (!$account) continue;
$mc_value.= sprintf("%s:quota=%s;email=%s;first_name=%s;last_name=%s;block_by_quota=%d\n",
$account,
$this->Accounts[$account]['quota'],
$this->Accounts[$account]['email'],
$this->Accounts[$account]['first_name'],
$this->Accounts[$account]['last_name'],
$this->Accounts[$account]['block_by_quota']
);
}
if (is_object($this->CDRS->mc)) {
$this->CDRS->mc->set($this->mc_key_accounts,$mc_value,MEMCACHE_COMPRESSED,3600);
$log=sprintf("Saved compressed %d accounts in memcache key %s (%.02f MB)\n",count($this->Accounts),$this->mc_key_accounts,strlen($mc_value)/1024/1024);
print $log;
syslog(LOG_NOTICE,$log);
$mc_stats=$this->CDRS->mc->getStats();
$mc_usage=$mc_stats['bytes']/$mc_stats['limit_maxbytes']*100;
$log=sprintf ("Memcache usage %.0f%s\n",$mc_usage,"%");
print $log;
syslog(LOG_NOTICE,$log);
}
$e=time();
$d=$e-$b;
if ($d >0 ) syslog(LOG_NOTICE, "Saved SIP accounts to memcache in $d seconds");
}
function LoadAccountsfromCache() {
if ($this->CDRS->mc && $mc_value = $this->CDRS->mc->get($this->mc_key_accounts)) {
$_accounts_tmp=explode("\n",$mc_value);
foreach ($_accounts_tmp as $_account_tmp) {
$_avps=explode(":",$_account_tmp);
$_properties = explode(";",$_avps[1]);
foreach ($_properties as $_property) {
$_els=explode("=",$_property);
$_accounts[$_avps[0]][$_els[0]]=$_els[1];
}
}
$log=sprintf("Load %d accounts from memcache key %s (%.02f MB)\n",count($_accounts),$this->mc_key_accounts,strlen($mc_value)/1024/1024);
//print $log;
syslog(LOG_NOTICE,$log);
$this->Accounts = &$_accounts;
return true;
}
return false;
}
function ShowAccountsWithQuota($treshhold='') {
if (!$this->LoadAccounts()) return false;
foreach (array_keys($this->Accounts) as $_subscriber) {
if ($this->Accounts[$_subscriber]['quota'] ) {
$usage = $this->CDRS->getMonthlyUsageFromCache($_subscriber);
if ($usage['cost'] > 0) {
if ($this->Accounts[$_subscriber]['block_by_quota']) {
$blockedStatus="blocked";
} else {
$blockedStatus='';;
}
$usageRatio=$usage['cost']*100/$this->Accounts[$_subscriber]['quota'];
if ($treshhold && $treshhold > $usageRatio) continue;
$usageStatus=sprintf("usage=%-10s",$usage['cost']);
printf ("%-35s quota=%-6s %s %.2f%s %s\n",$_subscriber,$this->Accounts[$_subscriber]['quota'],$usageStatus,$usageRatio,'%',$blockedStatus);
}
}
}
}
function Reset($reset_quota_for=array()) {
// deblock users blocked by quota
if (!$this->AccountsDBClass) {
print("Info: No database defined for SIP accounts.\n");
return false;
}
if ($this->enableThor) {
$query=sprintf("select * from sip_accounts");
if (!$this->AccountsDB->query($query)) {
$log=sprintf("Error: %s (%s)",$this->AccountsDB->Error,$this->AccountsDB->Errno);
syslog(LOG_NOTICE,$log);
return false;
}
while ($this->AccountsDB->next_record()) {
$i++;
$_account=$this->AccountsDB->f('username')."@".$this->AccountsDB->f('domain');
$_profile=json_decode(trim($this->AccountsDB->f('profile')));
if (in_array('quota',$_profile->groups)) {
$blockedAccounts[]=$this->AccountsDB->f('account');
}
}
} else {
$query=sprintf("select CONCAT(username,'@',domain) as account from grp where grp = '%s'",$this->quotaGroup);
if (count($reset_quota_for)) {
$k=0;
foreach ($reset_quota_for as $_account) {
if ($k) $usage_keys.= ", ";
$usage_keys.="'".$_account."'";
$k++;
}
$query.= "and CONCAT(username,'@',domain) in (".$usage_keys.")";
}
if (!$this->AccountsDB->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->AccountsDB->Error,$this->AccountsDB->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
while ($this->AccountsDB->next_record()) {
$blockedAccounts[]=$this->AccountsDB->f('account');
}
}
if (count($reset_quota_for)) {
$blockedAccounts=array_intersect($blockedAccounts,$reset_quota_for);
}
if (count($blockedAccounts) >0 ) {
$this->unBlockRemoteAccounts($blockedAccounts);
if (!$this->enableThor) {
$query=sprintf("delete from grp where grp = '%s'",$this->quotaGroup);
if (count($reset_quota_for)) {
$k=0;
foreach ($reset_quota_for as $_account) {
if ($k) $usage_keys.= ", ";
$usage_keys.="'".$_account."'";
$k++;
}
$query.= "and CONCAT(username,'@',domain) in (".$usage_keys.")";
}
if (!$this->AccountsDB->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->AccountsDB->Error,$this->AccountsDB->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
}
}
if (count($blockedAccounts)) {
$log=sprintf ("Reset %d users blocked by quota\n",count($blockedAccounts));
print $log;
syslog(LOG_NOTICE, $log);
}
}
function getMonthlyUsageFromDatabaseCache() {
$query=sprintf("select * from memcache where `key` like '%s%s'",
$this->CDRS->mc_key_mu_prefix,"%");
if (!$this->db->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
while($this->db->next_record()) {
$account = substr($this->db->f('key'),strlen($this->CDRS->mc_key_mu_prefix));
$_usage = $this->CDRS->parseMonthlyUsage($this->db->f('value'));
$this->Accounts[$account]['usage']=$_usage;
}
}
function initMonthlyUsageFromDatabase($month="",$reset_quota_for=array()) {
if (!$month) {
$this->startTime=Date("Y-m-01 00:00",time());
} else {
$this->startTime=$month."-01 00:00";
}
$this->LoadAccounts();
$j=0;
$usage_keys='';
if (count($reset_quota_for)) {
$k=0;
foreach ($reset_quota_for as $_account) {
if ($k) $usage_keys.= ", ";
$usage_keys.="'".$_account."'";
$k++;
}
$usage_keys="and ".$this->BillingPartyIdField. " in (".$usage_keys.")";
}
$query=sprintf("select %s,
count(*) as calls,
sum(AcctSessionTime) as duration,
sum(Price) as cost,
sum(AcctInputOctets + AcctOutputOctets)/2 as traffic
from %s
where AcctStartTime >= '%s'
and Normalized = '1'
%s
group by %s\n",
addslashes($this->BillingPartyIdField),
addslashes($this->table),
addslashes($this->startTime),
$usage_keys,
addslashes($this->BillingPartyIdField)
);
if (!$this->CDRdb->query($query)) {
if ($this->CDRdb->Errno != 1146) {
$log=sprintf ("Database error: %s (%s)",$this->CDRdb->Error,$this->CDRdb->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
}
$rows=$this->CDRdb->num_rows();
$log=sprintf ("%d callers generated traffic in %s\n",$rows,Date("Y-m",time()));
print $log;
syslog(LOG_NOTICE, $log);
$j=0;
while($this->CDRdb->next_record()) {
if (!$this->Accounts[$this->CDRdb->f($this->BillingPartyIdField)]) continue;
$this->Accounts[$this->CDRdb->f($this->BillingPartyIdField)]['usage']['calls'] = $this->CDRdb->f('calls');
$this->Accounts[$this->CDRdb->f($this->BillingPartyIdField)]['usage']['duration'] = $this->CDRdb->f('duration');
$this->Accounts[$this->CDRdb->f($this->BillingPartyIdField)]['usage']['cost'] = $this->CDRdb->f('cost');
$this->Accounts[$this->CDRdb->f($this->BillingPartyIdField)]['usage']['traffic'] = $this->CDRdb->f('traffic');
$j++;
}
}
function initMonthlyUsageFromCache () {
if (!$this->AccountsDBClass) {
print("Info: No database defined for SIP accounts.\n");
return false;
}
$this->cachedUsage=array();
if (is_object($this->CDRS->mc)) {
if ($this->enableThor) {
$query="select * from sip_accounts";
} else {
$query="select * from subscriber";
}
if (!$this->AccountsDB->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->AccountsDB->Error,$this->AccountsDB->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
while($this->AccountsDB->next_record()) {
$_account=$this->AccountsDB->f('username')."@".$this->AccountsDB->f('domain');;
$this->cachedUsage[$_account]['usage'] = $this->CDRS->getMonthlyUsageFromCache($_account);
}
}
return true;
}
function checkQuota($notify) {
global $UserQuota;
$this->cacheMonthlyUsage();
$this->LoadAccounts();
$toNotify=array();
if (!is_object($this->CDRS->mc)) {
$this->getMonthlyUsageFromDatabaseCache();
}
$_checks=0;
foreach (array_keys($this->Accounts) as $account) {
if (!$account['quota']) continue;
list($username,$domain)=explode("@",$account);
if (is_object($this->CDRS->mc)) $this->Accounts[$account]['usage']=$this->CDRS->getMonthlyUsageFromCache($account);
if ($reason=$this->checkLimit($account,$this->Accounts[$account]['usage'])) {
$exceeding_accounts++;
if (!$this->Accounts[$account]['block_by_quota']) {
if (!$seen_title) {
$line=sprintf ("%40s %6s %8s %8s %13s %s\n","User","Calls","Price","Minutes","Traffic","Reason");
print $line;
$email_body=$line;
$seen_title++;
}
$traffic = number_format($this->Accounts[$account]['usage']['traffic']/1024/1024,2);
$duration = number_format($this->Accounts[$account]['usage']['duration']/60,0,"","");
$line = sprintf ("%40s %6s %8s %8s %10s MB %s\n",$account,$this->Accounts[$account]['usage']['calls'],$this->Accounts[$account]['usage']['cost'],$duration,$traffic,$reason);
$email_body = $email_body.$line;
print $line;
$log=sprintf("Quota exceeded for %s: %s",$account, $reason);
syslog(LOG_NOTICE, $log);
$log_query=sprintf("insert into log
(date,login,ip,datasource,results,description)
values (NOW(),'quotacheck','localhost','QuotaCheck','1','%s')",
addslashes($log)
);
if (!$this->db->query($log_query)) {
$log=sprintf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
print $log;
syslog(LOG_NOTICE,$log);
}
if ($this->blockAccount($account)) {
if ($notify && $this->Accounts[$account]['email']) {
$toNotify[]=$account;
}
$blocked_now++;
$blockedAccountsNow=$blockedAccountsNow.$account."\n";
}
} else {
$blockedAccountsPrevious=$blockedAccountsPrevious.$account."\n";
$blocked_previous++;
}
}
$_checks++;
}
if ($exceeding_accounts) {
$line=sprintf("%6d accounts have exceeded their traffic limits\n",$exceeding_accounts);
print $line;
$email_body=$email_body.$line;
} else {
$log=sprintf("No quota has been exceeded\n");
syslog(LOG_NOTICE, $log);
}
if ($blocked_now) {
$line=sprintf("%6d accounts have been blocked now\n",$blocked_now);
$email_body=$email_body.$line;
if (is_object($this->CDRS->mc)) $this->CDRS->mc->delete($this->CDRS->mc_key_accounts);
}
if ($blockedAccountsNow) {
$line="Blocked accounts now:\n".$blockedAccountsNow;
print $line;
$email_body=$email_body.$line.$batch_block;
}
if ($blockedAccountsPrevious) {
$line="Blocked acccounts previously:\n".$blockedAccountsPrevious;
print $line;
$email_body=$email_body.$line.$batch_unblock;
}
// send notification to the provider
if ($this->CDRTool['provider']['toEmail'] && $blockedAccountsNow) {
$from = $this->CDRTool['provider']['fromEmail'];
$to = $this->CDRTool['provider']['toEmail'];
$bcc = $this->CDRTool['provider']['bccEmail'];
$service = $this->CDRTool['provider']['service'];
if (!$service) $service = "SIP";
if ($from) $extraHeaders="From: $from\r\nBCC: $from";
if ($bcc) $extraHeaders=$extraHeaders.",".$bcc;
print("Notify CDRTool provider at $to\n");
mail($to, "$service platform - CDRTool quota check", $email_body, $extraHeaders);
}
if ($notify && is_array($toNotify) && count($toNotify) >0) {
// send notification to accounts
foreach($toNotify as $rcpt) {
$this->notifyAccount($rcpt);
}
}
}
function checkLimit($account,$usage) {
global $UserQuota;
$maxcost = "";
$maxtraffic = "";
if ($this->Accounts[$account]['quota']) {
$maxcost = $this->Accounts[$account]['quota'];
} elseif ($UserQuota['default']['cost']) {
$maxcost = $UserQuota['default']['cost'];
}
if ($maxcost && ($usage['cost'] > $maxcost)) {
return "Cost $usage[cost] > $maxcost";
}
if ($this->Accounts[$account]['quota_traffic']) {
$maxtraffic = $this->Accounts[$account]['quota_traffic'];
} elseif ($UserQuota['default']['traffic']) {
$maxtraffic = $UserQuota['default']['traffic'];
}
if ($maxtraffic && ($usage['traffic'] > $maxtraffic*1024*1024)) {
$trafficMB=number_format($usage['traffic']/1024/1024,0,".","");
return "Traffic $trafficMB > $maxtraffic MB";
}
return false;
}
function notifyAccount($account) {
global $DATASOURCES;
list($username,$domain)=explode("@",$account);
if (!$DATASOURCES[$this->cdr_source][UserQuotaNotify]) {
return false;
}
$providerName=$this->notificationAddresses[$domain]['providerName'];
if (!$providerName) $providerName="your SIP service provider";
$body=sprintf("Dear __NAME__,\n\n".
"Your SIP account %s has been temporarily blocked\n".
"because your monthly quota has been exceeded.\n\n".
"To unblock your account you may contact %s.\n\n".
"N.B. This is an automatically generated message. Do not reply to it.\n",
$account,
$providerName);
$fromEmail = $this->CDRTool[provider][fromEmail];
$bccEmail = $this->CDRTool[provider][bccEmail];
$seen_bcc[$bccEmail]++;
if ($this->notificationAddresses[$domain]['fromEmail']) {
$fromEmail=$this->notificationAddresses[$domain]['fromEmail'];
}
if ($this->notificationAddresses[$domain]['quotaBody']) {
$body=$this->notificationAddresses[$domain]['quotaBody'];
}
if ($this->notificationAddresses[$domain]['quotaSubject']) {
$subject=$this->notificationAddresses[$domain]['quotaSubject'];
}
$fullname=$this->Accounts[$account]['first_name']." ".$this->Accounts[$account]['last_name'];
$body=preg_replace("/__NAME__/",$fullname,$body);
$body=preg_replace("/__ACCOUNT__/",$account,$body);
if (!$subject) {
$subject=sprintf("Monthly quota exceeded for SIP account %s",$account);
} else {
$subject=preg_replace("/__ACCOUNT__/",$account,$subject);
}
$toEmail=$this->Accounts[$account]['email'];
if (!$toEmail || !$fromEmail) {
return false;
}
$seen_bcc[$toEmail]++;
$extraHeaders="From: $fromEmail";
if ($this->notificationAddresses[$domain][bccEmail]) {
if ($bccEmail) $bccEmail.= ",";
$bccEmail.=$this->notificationAddresses[$domain][bccEmail];
}
if ($bccEmail) $extraHeaders = $extraHeaders."\r\nBCC: ".$bccEmail;
mail($toEmail,$subject,$body, $extraHeaders);
$log_msg=sprintf("Monthly quota exceeded for %s. Notified To:%s From:%s\n",$account, $toEmail,$fromEmail);
syslog(LOG_NOTICE, $log_msg);
print $log_msg;
}
function blockAccount($account) {
list($username,$domain)=explode("@",$account);
if (is_object($this->soapclient)) {
return $this->blockAccountRemote($account);
} else {
$log=sprintf ("Unblock account %s in sql",$account);
syslog(LOG_NOTICE, $log);
// update OpenSER database using SQL query
$query=sprintf("delete from grp
where username = '%s'
and domain = '%s'
and grp = '%s'",
addslashes($username),
addslashes($domain),
addslashes($this->quotaGroup)
);
if (!$this->AccountsDB->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->AccountsDB->Error,$this->AccountsDB->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
$query=sprintf("insert into grp
(username,domain,grp,last_modified)
values
('%s','%s','%s',NOW())",
addslashes($username),
addslashes($domain),
addslashes($this->quotaGroup)
);
if (!$this->AccountsDB->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->AccountsDB->Error,$this->AccountsDB->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
} else {
return true;
}
}
}
function blockAccountRemote($account) {
list($username,$domain)=explode("@",$account);
if (!$username || !$domain) {
$log=sprintf("Error: misssing username/domain in blockAccountRemote()");
syslog(LOG_NOTICE, $log);
return false;
}
$this->soapclient->addHeader($this->SoapAuth);
$result = $this->soapclient->addToGroup(array("username" => $username,"domain"=> $domain), "quota");
if (PEAR::isError($result)) {
$error_msg = $result->getMessage();
$error_fault = $result->getFault();
$error_code = $result->getCode();
$log=sprintf("Error from %s: %s (%s)",$this->SOAPurl,$error_fault->faultstring,$error_fault->faultcode);
syslog(LOG_NOTICE, $log);
if ($error_fault->detail->exception->errorcode != "1030") {
$from = $this->CDRTool['provider']['fromEmail'];
$to = $this->CDRTool['provider']['toEmail'];
$extraHeaders = "From: $from";
$email_body = "Remote SOAP request failure when calling blockAccountRemote(): \n\n".
$log;
mail($to, "CDRTool SOAP client failure", $email_body, $extraHeaders);
}
return false;
} else {
$log=sprintf ("Block account %s at %s",$account,$this->SOAPurl );
syslog(LOG_NOTICE, $log);
return true;
}
}
function unBlockRemoteAccounts($accounts) {
if (!is_object($this->soapclient)) {
return;
}
foreach ($accounts as $account) {
list($username,$domain)=explode("@",$account);
if (!$username || !$domain) return true;
$this->soapclient->addHeader($this->SoapAuth);
$result = $this->soapclient->removeFromGroup(array("username" => $username,"domain"=> $domain), "quota");
if (PEAR::isError($result)) {
$error_msg = $result->getMessage();
$error_fault = $result->getFault();
$error_code = $result->getCode();
if ($error_fault->detail->exception->errorcode &&
$error_fault->detail->exception->errorcode != "1030" &&
$error_fault->detail->exception->errorcode != "1031"
) {
$from = $this->CDRTool[provider][fromEmail];
$to = $this->CDRTool[provider][toEmail];
$extraHeaders="From: $from";
$email_body="SOAP request failure: \n\n".
$log=sprintf ("SOAP client error: %s %s\n",$error_fault->detail->exception->errorcode,$error_fault->detail->exception->errorstring);
syslog(LOG_NOTICE, $log);
mail($to, "CDRTool SOAP failure", $email_body, $extraHeaders);
}
} else {
$log=sprintf ("Unblock remote account %s at %s",$account,$this->SOAPurl);
syslog(LOG_NOTICE, $log);
}
}
}
function saveQuotaInitFlag() {
if (is_object($this->CDRS->mc)) {
if ($this->CDRS->mc->set($this->mc_key_init,'1',0,0)) {
return true;
} else {
return false;
}
} else {
$query=sprintf("delete from memcache where `key`= '%s'",$this->mc_key_init);
if (!$this->db->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
$query=sprintf("insert into memcache (`key`,`value`) values ('%s','1')",$this->mc_key_init);
if ($this->db->query($query) && $this->db->affected_rows()) {
return true;
} else {
return false;
}
}
}
function deleteQuotaInitFlag() {
if (is_object($this->CDRS->mc)) {
if (!$this->CDRS->mc->delete($this->mc_key_init)) {
return false;
}
}
$query=sprintf("delete from memcache where `key`= '%s'",$this->mc_key_init);
if (!$this->db->query($query)) return false;
return true;
}
function cacheMonthlyUsage() {
if (is_object($this->CDRS->mc)) {
if (!$this->CDRS->mc->get($this->mc_key_init)) {
$reset_quota_for = json_decode($this->CDRS->mc->get('reset_quota_for'));
$this->Reset($reset_quota_for);
$this->deleteMonthlyUsageFromCache($reset_quota_for);
$this->initMonthlyUsageFromDatabase('',$reset_quota_for);
$this->CDRS->cacheMonthlyUsage(&$this->Accounts);
if ($this->CDRS->status['cached_keys']['saved_keys']) {
$log=sprintf("Saved %d usage keys in cache\n",$this->CDRS->status['cached_keys']['saved_keys']);
print $log;
syslog(LOG_NOTICE, $log);
}
if ($this->CDRS->status['cached_keys']['failed_keys']) {
$log=sprintf("Error: failed to save %d usage keys\n",$this->CDRS->status['cached_keys']['failed_keys']);
print $log;
syslog(LOG_NOTICE, $log);
}
if ($this->saveQuotaInitFlag()) {
$this->CDRS->mc->delete('reset_quota_for');
return true;
} else {
$log=sprintf ("Error: failed to save key quotaCheckInit in memcache");
syslog(LOG_NOTICE, $log);
return false;
}
}
} else {
$query=sprintf("select * from memcache where `key` = '%s'",$this->mc_key_init);
if (!$this->db->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
if (!$this->db->num_rows()) {
$query=sprintf("select * from memcache where `key` = 'reset_quota_for'");
if (!$this->db->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
if ($this->db->num_rows()) {
$this->db->next_record();
$reset_quota_for = json_decode($this->db->f('reset_quota_for'));
}
$this->Reset($reset_quota_for);
$this->deleteMonthlyUsageFromCache($reset_quota_for);
$this->initMonthlyUsageFromDatabase('',$reset_quota_for);
if ($this->CDRS->status['cached_keys']['saved_keys']) {
$log=sprintf("Saved %d keys\n",$this->CDRS->status['cached_keys']['saved_keys']);
print $log;
syslog(LOG_NOTICE, $log);
}
if ($this->CDRS->status['cached_keys']['failed_keys']) {
$log=sprintf("Error: failed to save %d keys\n",$this->CDRS->status['cached_keys']['failed_keys']);
print $log;
syslog(LOG_NOTICE, $log);
}
if ($this->saveQuotaInitFlag()) {
$query=sprintf("delete from memcache where `key` = 'reset_quota_for'");
$this->db->query($query);
return true;
} else {
$log=sprintf ("Error: failed to save key quotaCheckInit in memcache");
syslog(LOG_NOTICE, $log);
return false;
}
}
}
}
function deleteMonthlyUsageFromCache ($reset_quota_for=array()) {
$deleted_keys=0;
if (!$this->AccountsDBClass) {
print("Info: No database defined for SIP accounts.\n");
return false;
}
if (is_object($this->CDRS->mc)) {
if (count($reset_quota_for)) {
foreach($reset_quota_for as $_account) {
$mc_key=$this->CDRS->mc_key_mu_prefix.$_account;
if ($this->CDRS->mc->delete($mc_key)) {
$deleted_keys++;
}
}
} else {
if ($this->enableThor) {
$query="select * from sip_accounts";
} else {
$query="select * from subscriber";
}
if (!$this->AccountsDB->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->AccountsDB->Error,$this->AccountsDB->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
while($this->AccountsDB->next_record()) {
$mc_key=$this->CDRS->mc_key_mu_prefix.$this->AccountsDB->f('username')."@".$this->AccountsDB->f('domain');
if ($this->CDRS->mc->delete($mc_key)) {
$deleted_keys++;
}
}
}
if ($deleted_keys) {
$log=sprintf("Deleted %d keys from cache\n",$deleted_keys);
print $log;
syslog(LOG_NOTICE, $log);
}
}
if (count($reset_quota_for)) {
$k=0;
foreach ($reset_quota_for as $_account) {
if ($k) $usage_keys.= ", ";
$usage_keys="'".$_account."'";
$k++;
}
$query=sprintf("delete from memcache where `key` in (%s)",$usage_keys);
} else {
$query=sprintf("delete from memcache where `key` like '%s%s'",$this->CDRS->mc_key_mu_prefix,"%");
}
if (!$this->db->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->db->Error,$this->Accountsdb->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return false;
}
if ($this->db->affected_rows()) {
$log=sprintf("Deleted %d mysql keys\n",$this->db->affected_rows());
print $log;
syslog(LOG_NOTICE, $log);
}
return true;
}
function compareUsage() {
$this->initMonthlyUsageFromCache();
$this->initMonthlyUsageFromDatabase();
foreach (array_keys($this->cachedUsage) as $_key) {
if ($this->Accounts[$_key]['usage']['calls'] && $this->Accounts[$_key]['usage']['calls'] != $this->cachedUsage[$_key]['usage']['calls']) {
$errorMargin=($this->cachedUsage[$_key]['usage']['calls']/$this->Accounts[$_key]['usage']['calls']-1)*100;
printf ("%50s Database=%10d Cached=%10d Error=%.4f%s\n",$_key,$this->Accounts[$_key]['usage']['calls'],$this->cachedUsage[$_key]['usage']['calls'],$errorMargin,"%");
$errors++;
}
}
if (!$errors) print "Cached usage is up to date with database usage.\n";
}
}
class RatingEngine {
// set in global.inc $RatingEngine['prepaid_lock'] = 0;
// to disable lock when doing prepaid calls
// While so one may perform multiple calls in parallel using the same
// prepaid balance it also means that a negative balance can be reached
var $prepaid_lock = 1;
var $method = '';
var $log_runtime = false;
function RatingEngine (&$CDRS) {
global $RatingEngine; // set in global.inc
$this->settings = $RatingEngine;
if ($this->settings['prepaid_lock'] == false || $this->settings['prepaid_lock'] == 0) {
$this->prepaid_lock=0;
}
if ($this->settings['log_runtime']) {
$this->log_runtime=true;
}
$this->CDRS = &$CDRS;
$this->db = new DB_CDRTool;
$this->table = "prepaid";
$this->sessionCounter = 0;
$this->beginStatisticsTime = time();
$this->lastMinuteSessionCounter = 0;
$this->lastMinuteStatisticsTime = time();
$this->lastHourSessionCounter = 0;
$this->lastHourStatisticsTime = time();
$this->lastDaySessionCounter = 0;
$this->lastDayStatisticsTime = time();
}
function loadPrepaidAccounts($account='') {
$query=sprintf("select * from %s",$this->table);
if ($account) $query.= sprintf(" where account = '%s' ",addslashes($account));
if (!$this->db->query($query)) {
$log=sprintf ("Database error: %s (%s)",$this->db->Error,$this->db->Errno);
print $log;
syslog(LOG_NOTICE,$log);
return 0;
}
if ($account) {
if ($this->db->num_rows()) {
$this->db->next_record();
$this->balance[$this->db->f('account')] = $this->db->f('balance');
$log=sprintf("Loaded prepaid account %s with balance %s",$this->db->f('account'),$this->db->f('balance'));
syslog(LOG_NOTICE, $log);
return 1;
} else {
$log=sprintf("Error: Prepaid account %s does not exist",$account);
syslog(LOG_NOTICE, $log);
return 0;
}
} else {
while($this->db->next_record()) {
$_balance[$this->db->f('account')] = $this->db->f('balance');
}
if (is_array($_balance)) {
$this->balance=$_balance;
} else {
$this->balance=array();
}
$log=sprintf("Loaded %d prepaid accounts",count($this->balance));
syslog(LOG_NOTICE, $log);
return 1;
}
}
function reloadRatingTables () {
$b=time();
if (is_object($this->CDRS->mc)) {
$this->CDRS->mc->delete('destinations');
$this->CDRS->mc->delete('ENUMtlds');
}
$i=$this->CDRS->LoadDestinations();
syslog(LOG_NOTICE, "Loaded $i destinations");
$i=$this->CDRS->LoadENUMtlds();
syslog(LOG_NOTICE, "Loaded $i ENUMtlds");
$this->CDRS->RatingTables->LoadRatingTables();
$this->loadPrepaidAccounts();
$e=time();
$d=$e-$b;
if ($d >0 ) syslog(LOG_NOTICE, "Loaded rating tables in $d seconds");
$this->db->query("update settings set var_value ='' where var_name = 'reloadRating'");
return 1;
}
function reloadCustomers ($customerFilter) {
return 1;
}
function reloadDomains () {
if (is_object($this->CDRS->mc)) $this->CDRS->mc->delete($this->CDRS->mc_key_domains);
return 1;
}
function reloadSipAccountsWithQuota () {
if (is_object($this->CDRS->mc)) {
if ($this->CDRS->mc->delete($this->CDRS->mc_key_accounts)) {
$log=sprintf("Deleted %s key from memcache",$this->CDRS->mc_key_accounts);
syslog(LOG_NOTICE, $log);
}
}
return 1;
}
function reloadPrepaidAccounts($account='') {
return $this->loadPrepaidAccounts($account);
}
function showPrepaidAccounts($filter) {
foreach (array_keys($this->balance) as $key) {
if (strlen($filter)) {
if (preg_match("/$filter/",$key)) {
$accounts=$accounts.sprintf("Account=%-35s Balance=%8.4f",$key,$this->balance[$key])."\n";
}
} else {
$accounts=$accounts.sprintf("Account=%-35s Balance=%8.4f",$key,$this->balance[$key])."\n";
}
}
return $accounts;
}
function getBalanceHistory($account,$limit=50) {
list($username,$domain)=explode("@",$account);
if (!$username || !$domain) return 0;
$query=sprintf("select * from prepaid_history where username = '%s' and domain = '%s' order by id desc",
addslashes($username),
addslashes($domain)
);
if ($limit) $query.= sprintf (" limit %d",$limit);
-
if (!$this->db->query($query)) {
$log=sprintf ("getBalanceHistory error: %s (%s)",$this->db->Error,$this->db->Errno);
syslog(LOG_NOTICE, $log);
return 0;
}
- unset($line);
- $line .= sprintf("%s\n",$this->db->num_rows());
-
while ($this->db->next_record()) {
- $line .= sprintf
- ("Action='%s'\tSource='%s'\tValue='%s'\tDate='%s'\tBalance='%s'\n",
- $this->db->f('action'),
- $this->db->f('number'),
- $this->db->f('value'),
- $this->db->f('date'),
- $this->db->f('balance')
- );
+ $history[]=array('account' =>$account,
+ 'action' =>$this->db->f('action'),
+ 'number' =>$this->db->f('number'),
+ 'value' =>$this->db->f('value'),
+ 'balance' =>$this->db->f('balance')
+ );
}
+ $line=json_encode($history);
return $line;
}
function DebitBalance($account,$balance) {
$els=explode(":",$account);
if (count($els) == 2) {
$account=$els[1];
}
if (!$account) {
syslog(LOG_NOTICE, "DebitBalance() error: missing account");
return 0;
}
if (!is_numeric($balance)) {
syslog(LOG_NOTICE, "DebitBalance() error: balance must be numeric");
return 0;
}
if ($this->balance[$account]) {
$query=sprintf("update %s
set balance = balance - '%s',
change_date = NOW(),
last_call_price = '%s',
call_in_progress = '0000-00-00 00:00:00',
call_lock = '0',
maxsessiontime = '0'
where account = '%s'",
$this->table,
$balance,
$balance,
addslashes($account)
);
if ($this->db->query($query)) {
$query=sprintf("select balance from %s where account = '%s'",
$this->table,
addslashes($account)
);
$this->db->query($query);
$this->db->next_record();
$this->balance[$account]=$this->db->f('balance');
return 1;
} else {
$log=sprintf ("DebitBalance() error: %s (%s)",$this->db->Error,$this->db->Errno);
syslog(LOG_NOTICE, $log);
return 0;
}
} else {
syslog(LOG_NOTICE, "DebitBalance() error: Account $account does not exist");
return "none";
}
}
function CreditBalance($account,$balance) {
if (!is_numeric($balance)) {
syslog(LOG_NOTICE, "CreditBalance() error: balance \"$balance\"is invalid");
return 0;
}
$els=explode(":",$account);
if (count($els) == 2) {
$account=$els[1];
}
if (!$account) {
syslog(LOG_NOTICE, "CreditBalance() error: missing account");
return 0;
}
list($prepaidUser,$prepaidDomain)=explode("@",$account);
if (strlen($this->balance[$account])) {
$query=sprintf("update %s
set balance = balance + '%s',
change_date = NOW()
where account = '%s'",
$this->table,
$balance,
addslashes($account)
);
$this->db->query($query);
if ($this->db->affected_rows()) {
$this->balance[$account]=$this->balance[$account]+$balance;
$log=sprintf ("Prepaid account $account credited with $balance");
syslog(LOG_NOTICE, $log);
// log to prepaid_history
$query=sprintf("insert into prepaid_history
(username,domain,action,number,value,balance,date)
values
('%s','%s','Set balance','manual update','%s','%s',NOW())",
addslashes($prepaidUser),
addslashes($prepaidDomain),
$balance,
$this->balance[$account]
);
if (!$this->db->query($query)) {
$log=sprintf("Error: %s (%s)",$this->db->Error,$this->db->Errno);
syslog(LOG_NOTICE, $log);
}
return 1;
} else {
$log=sprintf ("CreditBalance() error: failed to debit balance: %s (%s)",$this->db->Error,$this->db->Errno);
syslog(LOG_NOTICE, $log);
return 0;
}
} else {
$query=sprintf("insert into %s
(balance, account, change_date)
values ('%s','%s',NOW())",
$this->table,
$balance,
addslashes($account)
);
$this->db->query($query);
if ($this->db->affected_rows()) {
$this->balance[$account]=$this->balance[$account]+$balance;
$log=sprintf ("Added prepaid account $account with balance=$balance");
syslog(LOG_NOTICE, $log);
// log to prepaid_history
$query=sprintf("insert into prepaid_history
(username,domain,action,number,value,balance,date)
values
('%s','%s','Set balance','manual update','%s','%s',NOW())",
addslashes($prepaidUser),
addslashes($prepaidDomain),
$balance,
$balance
);
if (!$this->db->query($query)) {
$log=sprintf("Error: %s (%s)",$this->db->Error,$this->db->Errno);
syslog(LOG_NOTICE, $log);
}
return 1;
} else {
$log=sprintf ("CreditBalance() error: failed to credit balance: %s (%s)",$this->db->Error,$this->db->Errno);
syslog(LOG_NOTICE, $log);
return 0;
}
}
}
function DeleteBalance($account) {
$els=explode(":",$account);
if (count($els) == 2) {
$account=$els[1];
}
if (!$account) {
syslog(LOG_NOTICE, "DeleteBalance() error: missing account");
return 0;
}
$query=sprintf("delete from %s where account = '%s'",
$this->table,addslashes($account)
);
if (!$this->db->query($query)) {
$log=sprintf("DeleteBalance error for query %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
syslog(LOG_NOTICE, $log);
return 0;
}
unset($this->balance[$account]);
$log=sprintf ("Prepaid account %s has been deleted",$account);
syslog(LOG_NOTICE, $log);
return 1;
}
function DeleteBalanceHistory($account) {
$account=trim($account);
$els=explode(":",$account);
if (count($els) == 2) {
$account=$els[1];
}
if (!$account) {
syslog(LOG_NOTICE, "DeleteBalanceHistory() error: missing account");
return 0;
}
list($username,$domain)=explode('@',$account);
$query=sprintf("delete from prepaid_history where username = '%s' and domain = '%s'",
addslashes($username),
addslashes($domain)
);
if (!$this->db->query($query)) {
$log=sprintf("DeleteBalanceHistory error for query %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
syslog(LOG_NOTICE, $log);
return 0;
}
$log=sprintf ("History of prepaid account %s has been deleted",$account);
syslog(LOG_NOTICE, $log);
return 1;
}
+ function GetEntityProfiles($entity) {
+
+ if (!$entity) {
+ syslog(LOG_NOTICE, "GetEntityProfiles");
+ return 0;
+ }
+
+ $query=sprintf("select * from billing_customers where
+ subscriber = '%s' or domain = '%s' or gateway = '%s'",
+ addslashes($entity),
+ addslashes($entity),
+ addslashes($entity)
+ );
+
+ if (!$this->db->query($query)) {
+ $log=sprintf ("GetEntityProfiles error: %s (%s)",$this->db->Error,$this->db->Errno);
+ syslog(LOG_NOTICE, $log);
+ return 0;
+ }
+
+ if ($this->db->num_rows() ==1 ) {
+ $this->db->next_record();
+ $entity = array('entity' => $entity,
+ 'profileWeekday' => $this->db->f('profile_name1'),
+ 'profileWeekdayAlt' => $this->db->f('profile_name1_alt'),
+ 'profileWeekend' => $this->db->f('profile_name2'),
+ 'profileWeekendAlt' => $this->db->f('profile_name2_alt'),
+ 'timezone' => $this->db->f('timezone'),
+ 'increment' => $this->db->f('increment'),
+ 'minDuration' => $this->db->f('min_duration'),
+ 'countryCode' => $this->db->f('country_code')
+ );
+ }
+
+ $line=json_encode($entity);
+ return $line;
+
+ }
+
+ function SetEntityProfiles($entity,$profiles) {
+
+ if (!$entity) {
+ syslog(LOG_NOTICE, "SetEntityProfiles");
+ return 0;
+ }
+
+ }
+
function showHelp() {
$help=
"Version\n".
"Help\n".
"MaxSessionTime From=sip:123@example.com To=sip:0031650222333@example.com Duration=7200 Lock=1\n".
"ShowPrice From=sip:123@example.com To=sip:0031650222333@example.com DestinationId=31650 Duration=59\n".
"AddBalance From=123@example.com Value=10.00\n".
"GetBalance From=123@example.com\n".
"GetBalanceHistory From=123@example.com\n".
"DeleteBalance From=123@example.com\n".
"DeleteBalanceHistory From=123@example.com\n".
"DebitBalance From=sip:123@example.com To=sip:0031650222333@example.com Duration=59\n".
"ShowPrepaidAccounts Filter=123@example.com\n".
"ReloadPrepaidAccounts Account=abc@example.com\n".
+ "GetEntityProfiles Entity=abc@example.com\n".
"ReloadRatingTables\n".
"ReloadSipAccountsWithQuota\n".
"ReloadDomains\n".
"ShowAccounts\n".
"ShowProfiles\n".
"ShowENUMtlds\n".
"ShowMemory\n"
;
return $help;
}
function logRuntime() {
if (!$this->log_runtime) return;
$t=0;
$log='';
foreach (array_keys($this->runtime) as $_key) {
$stamp=$this->runtime[$_key];
if ($prev_stamp) {
$_exec_time=$stamp-$prev_stamp;
$log .= sprintf("%s=%1.7f ",$_key,$_exec_time);
}
$prev_stamp=$stamp;
$t++;
}
syslog(LOG_NOTICE, $log);
}
function processNetworkInput($tinput) {
// Read key=value pairs from input
// Strip any unnecessary spaces
$this->runtime=array();
$tinput=preg_replace("/\s+/"," ",$tinput);
$_els=explode(" ",trim($tinput));
$this->runtime['start']=microtime_float();
syslog(LOG_NOTICE, $tinput);
if (!$_els[0]) return 0;
// read fields from input
unset($NetFields);
unset($seenField);
$i=0;
while ($i < count($_els)) {
$i++;
$_dict = explode("=",$_els[$i]);
$_key = strtolower(trim($_dict[0]));
$_value = strtolower(trim($_dict[1]));
if ($_key && $seenField[$_key]) {
$log=sprintf ("Error: '$_key' attribute is present more than once in $tinput");
syslog(LOG_NOTICE, $log);
return 0;
} else {
if ($_key) {
$NetFields[$_key]=$_value;
$seenField[$_key]++;
}
}
}
$NetFields['action']=strtolower($_els[0]);
$this->method = $NetFields['action'];
// begin processing
if ($NetFields['action']=="maxsessiontime") {
if (!$NetFields['from']) {
$log=sprintf ("Error: Missing From parameter");
syslog(LOG_NOTICE, $log);
return 0;
}
if (!$NetFields['to']) {
$log=sprintf ("Error: Missing To parameter");
syslog(LOG_NOTICE, $log);
return 0;
}
if (!$NetFields['duration']) {
$NetFields['duration']=12*3600; // 12 hours
}
$this->sessionCounter++;
$this->lastMinuteSessionCounter++;
$this->lastHourSessionCounter++;
$this->lastDaySessionCounter++;
$_now=time();
$_runtime = time() - $this->beginStatisticsTime;
$_intervalMinute = $_now - $this->lastMinuteStatisticsTime;
if ( $_now > $this->lastMinuteStatisticsTime + 60 && $_intervalMinute > 0) {
$log=sprintf("Normalization done in %d s, memory usage: %0.2f MB, memory limit: %sB",$d,memory_get_usage()/1024/1024,ini_get('memory_limit'));
$_cpsTotal = $this->sessionCounter/$_runtime;
$_cpsMinute = $this->lastMinuteSessionCounter/$_intervalMinute;
$this->statistics = array (
'lastMinute' => array('calls' => $this->lastMinuteSessionCounter,
'interval' => $_intervalMinute,
'cps' => $_cpsMinute ),
'total' => array('calls' => $this->sessionCounter,
'interval' => $_runtime,
'cps' => $_cpsTotal)
);
$this->lastMinuteSessionCounter=0;
$this->lastMinuteStatisticsTime=time();
$log=sprintf ("Load last minute: %s calls @ %s cps, memory: %0.2f MB, uptime: %0.1f hours",
$this->statistics['lastMinute']['calls'],
sprintf("%.2f",$this->statistics['lastMinute']['cps']),
memory_get_usage()/1024/1024,
$_runtime/3600
);
syslog(LOG_NOTICE, $log);
$_intervalHour = $_now - $this->lastHourStatisticsTime;
if ( $_now > $this->lastHourStatisticsTime + 3600 && $_intervalHour > 0) {
$_cpsHour = $this->lastHourSessionCounter/$_intervalHour;
$this->statistics['lastHour'] = array('calls' => $this->lastHourSessionCounter,
'interval' => $_intervalHour,
'cps' => $_cpsHour );
$this->lastHourSessionCounter=0;
$this->lastHourStatisticsTime=time();
$log=sprintf ("Load last hour: %s calls @ %s cps",
$this->statistics['lastHour']['calls'],
sprintf("%.2f",$this->statistics['lastHour']['cps']));
syslog(LOG_NOTICE, $log);
$log=sprintf ("Load since start: %s sessions @ %s cps",
$this->statistics['total']['calls'],
sprintf("%.2f",$this->statistics['total']['cps']));
syslog(LOG_NOTICE, $log);
}
$_intervalDay = $_now - $this->lastDayStatisticsTime;
if ( $_now > $this->lastDayStatisticsTime + 3600*24 && $_intervalDay > 0) {
$_cpsDay = $this->lastDaySessionCounter/$_intervalDay;
$this->statistics['lastDay'] = array('calls' => $this->lastDaySessionCounter,
'interval' => $_intervalDay,
'cps' => $_cpsDay );
$this->lastDaySessionCounter=0;
$this->lastDayStatisticsTime=time();
$log=sprintf ("Load last day: %s calls @ %s cps",
$this->statistics['lastDay']['calls'],
sprintf("%.2f",$this->statistics['lastDay']['cps']));
syslog(LOG_NOTICE, $log);
}
}
$this->runtime['update_statistics']=microtime_float();
$CDRStructure=array (
$this->CDRS->CDRFields['callId'] => $NetFields['callid'],
$this->CDRS->CDRFields['aNumber'] => $NetFields['from'],
$this->CDRS->CDRFields['CanonicalURI'] => $NetFields['to'],
$this->CDRS->CDRFields['duration'] => $NetFields['duration'],
$this->CDRS->CDRFields['timestamp'] => time()
);
$CDR = new $this->CDRS->CDR_class(&$this->CDRS, &$CDRStructure);
$CDR->normalize();
$this->runtime['normalize_cdr']=microtime_float();
$Balance=$this->balance[$CDR->BillingPartyId];
$c=count($this->balance);
if (!$Balance) {
$this->logRuntime();
return "none";
}
if ($this->prepaid_lock) {
$query=sprintf("select * from %s where account = '%s'",
addslashes($this->table),
addslashes($CDR->BillingPartyId)
);
if (!$this->db->query($query)) {
$log=sprintf ("Database error for %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
syslog(LOG_NOTICE,$log);
$this->logRuntime();
return "none";
}
if ($this->db->num_rows()) {
$this->db->next_record();
if ($this->db->f('call_lock') == "1") {
$_cip=$this->db->f('call_in_progress');
$log = sprintf ("Account locked, call in progres since $_cip");
syslog(LOG_NOTICE, $log);
$this->logRuntime();
return "locked";
}
}
}
$this->runtime['check_lock']=microtime_float();
if (!strlen($this->balance[$CDR->BillingPartyId])) {
$log = sprintf ("Prepaid $CDR->BillingPartyId does not exist");
syslog(LOG_NOTICE, $log);
$this->logRuntime();
return "none";
}
if (!preg_match("/^0/",$CDR->CanonicalURINormalized)) {
$log = sprintf ("Call to %s, no limit imposed",$CDR->CanonicalURINormalized);
syslog(LOG_NOTICE, $log);
$this->logRuntime();
return "none";
} else {
if (!$CDR->DestinationId) {
$log = sprintf ("Error: cannot figure out the destination id for $CDR->CanonicalURI");
$this->logRuntime();
syslog(LOG_NOTICE, $log);
return "0";
}
}
if ($Balance) {
$maxduration=0;
// Build Rate dictionary containing normalized CDR fields plus customer Balance
$RateDictionary=array(
'duration' => $CDR->duration,
'callId' => $CDR->callId,
'Balance' => $Balance,
'timestamp' => $CDR->timestamp,
'DestinationId' => $CDR->DestinationId,
'domain' => $CDR->domain,
'BillingPartyId' => $CDR->BillingPartyId,
'ENUMtld' => $CDR->ENUMtld,
'RatingTables' => &$this->CDRS->RatingTables
);
$Rate = new Rate($this->settings, $this->db);
$this->runtime['instantiate_rate']=microtime_float();
$maxduration = round($Rate->MaxSessionTime($RateDictionary));
$this->runtime['calculate_maxduration']=microtime_float();
if ($maxduration<0) {
$log = sprintf ("Error: maxduration is negative ($maxduration)");
syslog(LOG_NOTICE, $log);
return 0;
}
if (!$Rate->billingTimezone) {
$log = sprintf ("Error: cannot figure out the billing timezone");
syslog(LOG_NOTICE, $log);
return 0;
}
if (!$Rate->startTimeBilling) {
$log = sprintf ("Error: cannot figure out the billing start time");
syslog(LOG_NOTICE, $log);
return 0;
}
$log=sprintf ("CallId=%s BillingParty=%s DestId=%s Balance=%s MaxSessionTime=%s Spans=%d",
$NetFields['callid'],
$CDR->BillingPartyId,
$CDR->DestinationId,
$RateDictionary['Balance'],
$maxduration,
$Rate->MaxSessionTimeSpans
);
syslog(LOG_NOTICE, $log);
if ($NetFields['lock'] && $maxduration > 0) {
// mark the account that is locked during call
$query=sprintf("update %s
set
call_in_progress = NOW(),
call_lock = '%s',
destination = '%s',
maxsessiontime = '%s'
where account = '%s'",
addslashes($this->table),
addslashes($this->prepaid_lock),
addslashes($CDR->destinationPrint),
addslashes($maxduration),
addslashes($CDR->BillingPartyId));
if (!$this->db->query($query)) {
$log=sprintf ("Database error for %s: %s (%s)",$query,$this->db->Error,$this->db->Errno);
syslog(LOG_NOTICE,$log);
return 0;
}
if (!$this->db->affected_rows()) {
$log=sprintf ("$CDR->BillingPartyId is already locked");
syslog(LOG_NOTICE, $log);
}
}
$this->runtime['update_prepaid']=microtime_float();
$this->logRuntime();
return $maxduration;
} else {
$this->logRuntime();
return 0;
}
} else if ($NetFields['action'] == "debitbalance") {
if (!$NetFields['from']) {
$log=sprintf ("Error: Missing From parameter");
syslog(LOG_NOTICE, $log);
return "Failed";
}
if (!$NetFields['to']) {
$log=sprintf ("Error: Missing To parameter");
syslog(LOG_NOTICE, $log);
return "Failed";
}
if (!strlen($NetFields['duration'])) {
$log=sprintf ("Error: Missing Duration parameter");
syslog(LOG_NOTICE, $log);
return "Failed";
}
$timestamp=time();
$CDRStructure=array (
$this->CDRS->CDRFields['callId'] => $NetFields['callid'],
$this->CDRS->CDRFields['aNumber'] => $NetFields['from'],
$this->CDRS->CDRFields['CanonicalURI'] => $NetFields['to'],
$this->CDRS->CDRFields['duration'] => $NetFields['duration'],
$this->CDRS->CDRFields['timestamp'] => time()
);
// Init CDR
$CDR = new $this->CDRS->CDR_class($this->CDRS, $CDRStructure);
$CDR->normalize();
$this->runtime['normalize_cdr']=microtime_float();
if (!is_array($this->balance) || !array_key_exists($CDR->BillingPartyId,$this->balance)) {
return "Not Prepaid";
}
// Build Rate dictionary containing normalized CDR fields plus customer Balance
$RateDictionary=array(
'callId' => $NetFields['callid'],
'timestamp' => $CDR->timestamp,
'duration' => $CDR->duration,
'DestinationId' => $CDR->DestinationId,
'domain' => $CDR->domain,
'traffic' => $CDR->traffic,
'BillingPartyId' => $CDR->BillingPartyId,
'RatingTables' => &$this->CDRS->RatingTables
);
$Rate = new Rate($this->settings, $this->db);
$this->runtime['instantiate_rate']=microtime_float();
$Rate->calculate($RateDictionary);
$this->runtime['calculate_rate']=microtime_float();
$result = $this->DebitBalance($CDR->BillingPartyId,$Rate->price);
$this->runtime['debit_balance']=microtime_float();
if ($CDR->duration) {
$oldBalance=$this->balance[$CDR->BillingPartyId];
$log=sprintf ("CallId=%s BillingParty=%s DestId=%s Duration=%s Price=%s Balance=%s",
$NetFields['callid'],
$CDR->BillingPartyId,
$CDR->DestinationId,
$CDR->duration,
$Rate->price,
$this->balance[$CDR->BillingPartyId]
);
syslog(LOG_NOTICE, $log);
}
if ($result) {
$RateReturn="Ok";
if (strlen($Rate->price)) {
$RateReturn.="\n".$Rate->price;
if ($Rate->rateInfo) {
$RateReturn.="\n".trim($Rate->rateInfo);
}
}
return $RateReturn;
} else {
return "Failed";
}
} else if ($NetFields['action'] == "addbalance") {
if (!$NetFields['from']) {
$log=sprintf ("Error: Missing From parameter");
syslog(LOG_NOTICE, $log);
return 0;
}
if (!is_numeric($NetFields['value'])) {
$log=sprintf ("Error: Missing Value parameter, it must be numeric");
syslog(LOG_NOTICE, $log);
return 0;
}
$log=sprintf ("Account : %s",$NetFields['from']);
syslog(LOG_NOTICE, $log);
$log=sprintf ("Balance : %s",$this->balance[$NetFields['from']]);
syslog(LOG_NOTICE, $log);
$result = $this->CreditBalance($NetFields['from'],$NetFields['value']);
if (intval($NetFields['value']) != 0) {
$log=sprintf ("New Balance : %s",$this->balance[$NetFields['from']]);
syslog(LOG_NOTICE, $log);
}
if ($result) {
return $result;
} else {
return 0;
}
} else if ($NetFields['action'] == "deletebalance") {
if (!$NetFields['from']) {
$log=sprintf ("Error: Missing From parameter");
syslog(LOG_NOTICE, $log);
return 0;
}
return $this->DeleteBalance($NetFields['from']);
} else if ($NetFields['action'] == "deletebalancehistory") {
if (!$NetFields['from']) {
$log=sprintf ("Error: Missing From parameter");
syslog(LOG_NOTICE, $log);
return 0;
}
return $this->DeleteBalanceHistory($NetFields['from']);
} else if ($NetFields['action'] == "showprice") {
if (!$NetFields['from']) {
$log=sprintf ("Error: Missing From parameter");
syslog(LOG_NOTICE, $log);
return 0;
}
if (!$NetFields['to']) {
$log=sprintf ("Error: Missing To parameter");
syslog(LOG_NOTICE, $log);
return 0;
}
if (!strlen($NetFields['duration'])) {
$log=sprintf ("Error: Missing Duration parameter");
syslog(LOG_NOTICE, $log);
return 0;
}
if ($NetFields['timestamp']) {
$timestamp=$NetFields['timestamp'];
} else {
$timestamp=time();
}
$application="audio";
if ($NetFields['application']) $application=$NetFields['application'];
$Rate = new Rate($this->settings, $this->db);
$RateDictionary=array(
'callId' => $NetFields['callid'],
'timestamp' => $timestamp,
'duration' => $NetFields['duration'],
'inputTraffic' => $NetFields['inputtraffic'],
'outputTraffic' => $NetFields['outputtraffic'],
'DestinationId' => $NetFields['destinationid'],
'BillingPartyId' => $NetFields['from'],
'domain' => $NetFields['domain'],
'gateway' => $NetFields['gateway'],
'aNumber' => $NetFields['from'],
'cNumber' => $NetFields['to'],
'ENUMtld' => $NetFields['enumtld'],
'applicationType' => $application,
'RatingTables' => &$this->CDRS->RatingTables,
);
$Rate->calculate($RateDictionary);
$this->runtime['calculate_rate']=microtime_float();
if (strlen($Rate->price)) {
$RateReturn=$Rate->price;
if ($Rate->rateInfo) {
$RateReturn.="\n".trim($Rate->rateInfo);
}
} else {
$RateReturn="0";
}
return $RateReturn;
} else if ($NetFields['action'] == "getbalance") {
if (!$NetFields['from']) {
$log=sprintf ("Error: Missing From parameter");
syslog(LOG_NOTICE, $log);
return 0;
}
if (!$this->balance[$NetFields['from']]) {
$balance=sprintf("%0.4f",0);
} else {
$balance=number_format($this->balance[$NetFields['from']],4,".","");
}
return $balance;
} else if ($NetFields['action'] == "getbalancehistory") {
if (!$NetFields['from']) {
$log=sprintf ("Error: Missing From parameter");
syslog(LOG_NOTICE, $log);
return 0;
}
$history=$this->getBalanceHistory($NetFields['from']);
return trim($history);
+ } else if ($NetFields['action'] == "getentityprofiles") {
+ if (!$NetFields['entity']) {
+ $log=sprintf ("Error: Missing Entity parameter");
+ syslog(LOG_NOTICE, $log);
+ return 0;
+ }
+
+ $entity=$this->GetEntityProfiles($NetFields['entity']);
+ return trim($entity);
} else if ($NetFields['action'] == "showprepaidaccounts") {
return trim($this->showPrepaidAccounts($NetFields['filter']));
} else if ($NetFields['action'] == "showprofiles") {
return trim($this->CDRS->RatingTables->showProfiles());
} else if ($NetFields['action'] == "showenumtlds") {
return trim($this->CDRS->RatingTables->showENUMtlds());
} else if ($NetFields['action'] == "version") {
$version_file=$this->CDRS->CDRTool['Path']."/version";
$version="CDRTool version ".trim(file_get_contents($version_file));
return $version;
} else if ($NetFields['action'] == "help") {
return $this->showHelp();
} else if ($NetFields['action'] == "reloadratingtables") {
return $this->reloadRatingTables();
} else if ($NetFields['action'] == "reloadsipaccountswithquota") {
return $this->reloadSipAccountsWithQuota();
} else if ($NetFields['action'] == "reloaddomains") {
return $this->CDRS->LoadDomains();
} else if ($NetFields['action'] == "reloadcustomers") {
if ($NetFields['customer'] && $NetFields['type']) {
$_customerFilter=array('customer'=>$NetFields['customer'],
'type'=>$NetFields['type']);
}
return $this->reloadCustomers($_customerFilter);
} else if ($NetFields['action'] == "reloadprepaidaccounts") {
return $this->reloadPrepaidAccounts($NetFields['account']);
} else if ($NetFields['action'] == "showmemory") {
$return = sprintf ("%s destinations\n%s profiles\n%s holidays\n%s prepaid accounts\n%s sessions\n%s sessions last minute\n%s sessions last hour\n%s sessions last day",
$this->CDRS->destinationsCount,
count($this->CDRS->RatingTables->profiles),
count($this->CDRS->RatingTables->holidays),
count($this->balance),
count($this->sessionCounter),
count($this->lastMinuteSessionCounter),
count($this->lastHourSessionCounter),
count($this->lastDaySessionCounter)
);
return $return;
} else {
$log=sprintf ("Error: Invalid request");
syslog(LOG_NOTICE, $log);
return 0;
}
}
}
function reloadRatingEngineTables () {
global $RatingEngine;
if ($RatingEngine['socketIP'] && $RatingEngine['socketPort'] &&
$fp = fsockopen ($RatingEngine['socketIP'], $RatingEngine['socketPort'], $errno, $errstr, 2)) {
fputs($fp, "ReloadRatingTables\n");
fclose($fp);
return true;
}
return false;
}
function reloadPrepaidAccounts($account='') {
global $RatingEngine;
if ($RatingEngine['socketIP'] && $RatingEngine['socketPort'] &&
$fp = fsockopen ($RatingEngine['socketIP'], $RatingEngine['socketPort'], $errno, $errstr, 2)) {
$account=trim($account);
$cmd="ReloadPrepaidAccounts";
if ($account) $cmd .= sprintf(" Account=%s",$account);
$cmd.="\n";
fputs($fp,$cmd);
fclose($fp);
return true;
}
return false;
}
function reloadSipAccountsWithQuota () {
global $RatingEngine;
if ($RatingEngine['socketIP'] && $RatingEngine['socketPort'] &&
$fp = fsockopen ($RatingEngine['socketIP'], $RatingEngine['socketPort'], $errno, $errstr, 2)) {
fputs($fp, "reloadSipAccountsWithQuota\n");
fclose($fp);
return true;
}
return false;
}
?>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Feb 1, 10:51 AM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3489237
Default Alt Text
(185 KB)
Attached To
Mode
rCDRT CDRTool
Attached
Detach File
Event Timeline
Log In to Comment