Page MenuHomePhabricator

No OneTemporary

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

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)

Event Timeline