Esempio n. 1
0
    function normalize($save="",$table="") {
        if (!$table) $table = $this->CDRS->table;

        if ($this->CDRS->CSCODE && $CarrierInfo = $this->CDRS->CDRTool['normalize']['CS_CODES'][$this->CDRS->CSCODE]) {
            // We found a carrier so we set the BillingId
            $this->BillingId          = $CarrierInfo[BillingPartyId];
        }

        if ($save) {

            if (!$this->id) {
                return 0;
            }

            $query  ="";
            $query1 ="";
            $query2 ="";

            if ($this->CDRS->normalizedField) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s='1' ",addslashes($this->CDRS->normalizedField));
                $mongo_field = array_search($this->CDRS->normalizedField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = 1;
            }

            if ($this->CDRS->BillingPartyIdField && $this->BillingPartyId) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->BillingPartyIdField),addslashes($this->BillingPartyId));
                $mongo_field = array_search($this->CDRS->BillingPartyIdField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->BillingPartyId;
            }

            if (strlen($this->durationNormalized) && $this->durationNormalized != $this->duration) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s ='%s' ",addslashes($this->CDRS->durationField),addslashes($this->durationNormalized));
                $this->duration=$this->durationNormalized;
                $mongo_field = array_search($this->CDRS->durationField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = intval($this->durationNormalized);
            } else {
                $mongo_field = array_search($this->CDRS->durationField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = intval($this->duration);
            }

            if ($this->CDRS->DestinationIdField) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->DestinationIdField),addslashes($this->DestinationId));
                $mongo_field = array_search($this->CDRS->DestinationIdField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->DestinationId;
            }

            if ($this->CDRS->ResellerIdField) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->ResellerIdField),addslashes($this->ResellerId));
                $mongo_field = array_search($this->CDRS->ResellerIdField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->ResellerId;
            }

            if ($this->usernameNormalized && $this->usernameNormalized!=$this->username) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->usernameField),addslashes($this->usernameNormalized));
                $mongo_field = array_search($this->CDRS->usernameField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->usernameNormalized;
            }

            if ($this->aNumberNormalized && $this->aNumberNormalized!=$this->aNumber) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->aNumberField),addslashes($this->aNumberNormalized));
                $this->aNumber=$this->aNumberNormalized;
                $mongo_field = array_search($this->CDRS->aNumberField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->aNumberNormalized;
            }

            if ($this->CDRS->applicationField && $this->applicationNormalized) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->applicationField), addslashes($this->applicationNormalized));
                $this->application=$this->applicationNormalized;
                $mongo_field = array_search($this->CDRS->applicationField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->applicationNormalized;
            }

            if ($this->CDRS->flowField && $this->flow) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->flowField),addslashes($this->flow));
                $mongo_field = array_search($this->CDRS->flowField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->flow;
            }

            if ($this->domainNormalized && $this->domainNormalized != $this->domain) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->domainField),addslashes($this->domainNormalized));
                $this->domainNumber=$this->domainNormalized;
                $this->domain=$this->domainNormalized;
                $mongo_field = array_search($this->CDRS->domainField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->domainNormalized;
            }

            if ($this->cNumberNormalized && $this->cNumberNormalized!=$this->cNumber) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->cNumberField),addslashes($this->cNumberNormalized));
                $this->cNumber=$this->cNumberNormalized;
                $mongo_field = array_search($this->CDRS->cNumberField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->cNumberNormalized;
            }

            if ($this->CDRS->BillingIdField && $this->BillingId) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->BillingIdField),addslashes($this->BillingId));
                $mongo_field = array_search($this->CDRS->BillingIdField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->BillingId;
            }

            if ($this->CDRS->RemoteAddressField && $this->RemoteAddressNormalized && $this->RemoteAddressNormalized!= $this->RemoteAddress) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->RemoteAddressField),addslashes($this->RemoteAddressNormalized));
                $mongo_field = array_search($this->CDRS->RemoteAddressField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->RemoteAddressNormalized;
            }

            if ($this->CDRS->CanonicalURIField && $this->CanonicalURINormalized && $this->CanonicalURINormalized!= $this->CanonicalURI) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->CanonicalURIField),addslashes($this->CanonicalURINormalized));
                $mongo_field = array_search($this->CDRS->CanonicalURIField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->CanonicalURINormalized;
            }

            if ($this->stopTimeNormalized) {
                if ($updatedFields) $query .= ", ";
                $updatedFields++;
                $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->stopTimeField),addslashes($this->stopTimeNormalized));
                $mongo_field = array_search($this->CDRS->stopTimeField, $this->CDRS->CDRFields);
                $this->mongo_cdr[$mongo_field] = $this->stopTimeNormalized;
            }

            if ($this->CDRS->ratingEnabled && ($this->duration || $this->application == 'message')) {

                if ($this->DestinationId) {
                    $Rate    = new Rate($this->CDRS->rating_settings, $this->CDRS->cdrtool);

                    if ($this->application == 'message') {
                        $RateDictionary=array(
                                              'callId'          => $this->callId,
                                              'timestamp'       => $this->timestamp,
                                              'duration'        => $this->duration,
                                              'DestinationId'   => $this->DestinationId,
                                              'BillingPartyId'  => $this->BillingPartyId,
                                              'ResellerId'      => $this->ResellerId,
                                              'domain'          => $this->domain,
                                              'gateway'         => $this->gateway,
                                              'RatingTables'    => $this->CDRS->RatingTables,
                                              'aNumber'         => $this->aNumber,
                                              'cNumber'         => $this->cNumber
                                              );

                        $Rate->calculateMessage($RateDictionary);
                    } else {
                        $RateDictionary=array(
                                              'callId'          => $this->callId,
                                              'timestamp'       => $this->timestamp,
                                              'duration'        => $this->duration,
                                              'DestinationId'   => $this->DestinationId,
                                              'inputTraffic'    => $this->inputTraffic,
                                              'outputTraffic'   => $this->outputTraffic,
                                              'BillingPartyId'  => $this->BillingPartyId,
                                              'ResellerId'      => $this->ResellerId,
                                              'domain'          => $this->domain,
                                              'gateway'         => $this->gateway,
                                              'RatingTables'    => $this->CDRS->RatingTables,
                                              'aNumber'         => $this->aNumber,
                                              'cNumber'         => $this->cNumber,
                                              'ENUMtld'         => $this->ENUMtld,
                                              'application'     => $this->application
                                              );

                        $Rate->calculateAudio($RateDictionary);
                    }

                    $this->pricePrint   = $Rate->pricePrint;
                    $this->price        = $Rate->price;
                    $this->rateInfo     = $Rate->rateInfo;
                    $this->rateDuration = $Rate->duration;

                    if ($Rate->broken_rate) {
                        $this->broken_rate=true;
                    }
                } else {
                    $this->rateInfo='';
                    $this->pricePrint='';
                    $this->price='';
                }

                if ($this->CDRS->priceField) {
                    if ($updatedFields) $query .= ", ";
                    $updatedFields++;
                    $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->priceField),addslashes($this->pricePrint));
                    $mongo_field = array_search($this->CDRS->priceField, $this->CDRS->CDRFields);
                    $this->mongo_cdr[$mongo_field] = floatval($this->pricePrint);

                    if ($this->CDRS->rateField ) {
                        if ($updatedFields) $query .= ", ";
                        $updatedFields++;
                        $query.=sprintf(" %s = '%s' ",addslashes($this->CDRS->rateField),addslashes($this->rateInfo));
                        $mongo_field = array_search($this->CDRS->rateField, $this->CDRS->CDRFields);
                        $this->mongo_cdr[$mongo_field] = $this->rateInfo;
                    }
                }
            }

            $query1 = sprintf("update %s set %s where %s = '%s'",addslashes($table),$query,addslashes($this->idField),addslashes($this->id));
            dprint($query1);

            #TODO remove me, I am used to temporary sync mysql data with mongo data
            if ($this->CDRS->mongo_table) {
                $mongo_field = array_search($this->idField, $this->CDRS->CDRFields);
                try {
                    $this->CDRS->mongo_table->update(array($mongo_field => $this->id), $this->mongo_cdr, array("upsert" => true));
                } catch (MongoException $e) {
                    printf("Caught Mongo exception: %s", $e->getMessage());
                } catch (Exception $e) {
                    printf("Caught exception: %s", $e->getMessage());
                }
            }

            if ($updatedFields) {
                if ($this->CDRS->CDRdb1->query($query1)) {
                    if ($this->CDRS->CDRdb1->affected_rows()) {
                        if ( $this->isBillingPartyLocal() && $table == "radacct".date('Ym')) {
                                // cache usage only if current month

                                $_traffic=($this->inputTraffic+$this->outputTraffic)/2;
                                $_usage=array('calls'    => 1,
                                              'duration' => $this->duration,
                                              'cost'     => $this->price,
                                              'cost_today' => $this->price,
                                              'traffic'  => $_traffic
                                             );

                                $this->cacheQuotaUsage($_usage);
                        }

                    } else {

                        if (preg_match("/^(\w+)(\d{4})(\d{2})$/",$table,$m)) {
                            $previousTable=$m[1].date('Ym', mktime(0, 0, 0, $m[3]-1, "01", $m[2]));
                            $query2 = sprintf("update %s set %s where %s = '%s'",addslashes($previousTable),$query,addslashes($this->idField),addslashes($this->id));

                            if ($this->CDRS->CDRdb1->query($query2)) {
                                if ($this->CDRS->CDRdb1->affected_rows()) {
                                    if ( $this->isBillingPartyLocal() && $previousTable == "radacct".date('Ym')) {
                                            // cache usage only if current month

                                            $_traffic=($this->inputTraffic+$this->outputTraffic)/2;
                                            $_usage=array('calls'    => 1,
                                                          'duration' => $this->duration,
                                                          'cost'     => $this->price,
                                                          'cost_today' => $this->price,
                                                          'traffic'  => $_traffic
                                                          );
                                            $this->cacheQuotaUsage($_usage);
                                    }
                                }
                            } else {
                                $log=sprintf ("Database error: %s (%s)",$this->CDRS->CDRdb1->Error,$this->CDRS->CDRdb1->Errno);
                                syslog(LOG_NOTICE, $log);
                                print($log);
                                return 0;
                            }

                        }

                    }

                    return 1;
                } else {
                    $log=sprintf ("Database error for query %s: %s (%s)",$query1,$this->CDRS->CDRdb1->Error,$this->CDRS->CDRdb1->Errno);
                    syslog(LOG_NOTICE, $log);
                    print($log);
                    return 0;
                }
            }

        } else {
            if ($this->CDRS->BillingPartyIdField && $CarrierInfo['BillingPartyId']) {
                $this->domain = $CarrierInfo['BillingDomain'];
            }

            if ($this->usernameNormalized && $this->usernameNormalized!=$this->username) {
                $this->username=$this->usernameNormalized;
            }

            if ($this->aNumberNormalized && $this->aNumberNormalized!=$this->aNumber) {
                $this->aNumber=$this->aNumberNormalized;
            }

            if ($this->domainNormalized && $this->domainNormalized != $this->domain) {
                $this->domainNumber=$this->domainNormalized;
            }

            if ($this->cNumberNormalized && $this->cNumberNormalized!=$this->cNumber) {
                $this->cNumber=$this->cNumberNormalized;
            }

            if ($this->CDRS->RemoteAddressField && $this->RemoteAddressNormalized && $this->RemoteAddressNormalized!= $this->RemoteAddress) {
                $this->RemoteAddress=$this->RemoteAddressNormalized;
            }
        }
        return 1;
    }
Esempio n. 2
0
    function processNetworkInput($tinput) {

        // Read key=value pairs from input
        // Strip any unnecessary spaces
        $this->runtime=array();

        $tinput=preg_replace("/\s+/"," ",$tinput);

        if ($tinput == "/" and strlen($this->last_input)) {
            $tinput = $this->last_input;
        } else {
            $this->last_input = $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]));
            if ($_key == 'callid') {
                $_value = trim($_dict[1]);
            } else {
                $_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 $log;
            }

            if (!$NetFields['to']) {
                $log=sprintf ("error: missing To parameter");
                syslog(LOG_NOTICE, $log);
                return $log;
            }

            if (!$NetFields['gateway']) {
                $log=sprintf ("error: missing gateway parameter");
                syslog(LOG_NOTICE, $log);
                return $log;
            }

            if (!$NetFields['callid']) {
                $log=sprintf ("error: missing Call Id parameter");
                syslog(LOG_NOTICE, $log);
                return $log;
            }

            if (!$NetFields['duration'] && $this->settings['MaxSessionTime']) {
                $NetFields['duration']=$this->settings['MaxSessionTime'];
            }

            $app_prefix = preg_replace('/[.].*$/', '', $NetFields['application']);
            if (strlen($app_prefix)) {
                if ($app_prefix == 'audio' || $app_prefix == 'sms' ) {
                    $application=$NetFields['application'];
                } else {
                    $log=sprintf ("error: unsupported application %s",$NetFields['application']);
                    syslog(LOG_NOTICE, $log);
                    return $log;
                }
            } else {
                $application='audio';
            }

            list($username_t,$domain_t)=explode("@",$NetFields['from']);

            $CDRStructure=array (
                              $this->CDRS->CDRFields['callId']         => $NetFields['callid'],
                              $this->CDRS->CDRFields['aNumber']        => $NetFields['from'],
                              $this->CDRS->CDRFields['CanonicalURI']   => $NetFields['to'],
                              $this->CDRS->CDRFields['gateway']        => $NetFields['gateway'],
                              $this->CDRS->CDRFields['duration']       => floor($NetFields['duration']),
                              $this->CDRS->CDRFields['timestamp']      => time(),
                              $this->CDRS->CDRFields['domain']         => $domain_t,
                              $this->CDRS->CDRFields['application']    => $application,
                              'skip_fix_prepaid_duration'              => true
                              );

            $CDR = new $this->CDRS->CDR_class($this->CDRS, $CDRStructure);
            $CDR->normalize();

            $this->runtime['normalize_cdr']=microtime_float();

            $query=sprintf("select * from %s where account = '%s'",addslashes($this->prepaid_table),addslashes($CDR->BillingPartyId));

            if (!$this->db->query($query)) {
                $log=sprintf ("Database error for query '%s': %s (%s), link_id =%s, query_id =%s",$query,$this->db->Error,$this->db->Errno,$this->db->Link_ID,$this->db->Query_ID);
                syslog(LOG_NOTICE,$log);
                $this->logRuntime();
                $ret=sprintf("error: database error for query '%s': %s (%s)",$query,$this->db->Error,$this->db->Errno)."\n"."type=prepaid";
                return $ret;
            }

            if (!$this->db->num_rows()) {
                $log=sprintf ("MaxSessionTime=unlimited Type=postpaid CallId=%s BillingParty=%s",$NetFields['callid'],$CDR->BillingPartyId);
                syslog(LOG_NOTICE, $log);
                $ret="none"."\n"."type=postpaid";
                return $ret;
            }

            $this->db->next_record();
            $current_balance     = $this->db->f('balance');
            $old_session_counter = $this->db->f('session_counter');
            $max_sessions        = $this->db->f('max_sessions');
            if (strlen($this->db->f('active_sessions'))) {
                // load active sessions
                $active_sessions = json_decode($this->db->f('active_sessions'),true);

                if (count($active_sessions)) {
                    // purge stale sessions

                    $active_sessions_new=array();

                    $expired=0;

                    foreach (array_keys($active_sessions) as $_session) {

                        $expired_since=time() - $active_sessions[$_session]['timestamp'] - $active_sessions[$_session]['MaxSessionTime'];
                        if ($expired_since > 120) {
                            // this session has passed its maxsessiontime plus its reasonable setup time of 2 minutes,
                            // it could be stale
                            // because the call control module did not call debitbalance, so we purge it

                            $log = sprintf ("Session %s for %s has expired since %d seconds",
                            $_session,
                            $active_sessions[$_session]['BillingPartyId'],
                            $expired_since);
                            syslog(LOG_NOTICE, $log);
                            $expired++;
                        } else {
                            $active_sessions_new[$_session]=$active_sessions[$_session];
                        }
                    }

                    if ($expired) {
                        $active_sessions=$active_sessions_new;
                    }
                }

            } else {
                $active_sessions=array();
            }


            if (!$current_balance) {
                $log=sprintf ("No balance found");
                syslog(LOG_NOTICE,$log);
                $this->logRuntime();
                $ret="0"."\n"."type=prepaid";
                return $ret;

            }

            if (preg_match("/^0[0-9]{1,}@/",$CDR->CanonicalURINormalized)) {
                if (!$CDR->DestinationId) {
                    $log = sprintf ("error: cannot figure out the destination id for %s",$CDR->CanonicalURI);
                    $this->logRuntime();
                    syslog(LOG_NOTICE, $log);
                    $ret=$log."\n"."type=prepaid";
                    return $ret;
                }
            } else {
                $log=sprintf ("MaxSessionTime=unlimited Type=prepaid CallId=%s BillingParty=%s DestId=None",$NetFields['callid'],$CDR->BillingPartyId);
                syslog(LOG_NOTICE, $log);
                $this->logRuntime();
                $ret="none"."\n"."type=prepaid";
                return $ret;
            }

            $session_counter=count($active_sessions);

            if ($max_sessions && $session_counter >= $max_sessions) {
                $log = sprintf ("Locked: maximum number of concurrent calls %s reached, $max_sessions allowed");
                syslog(LOG_NOTICE, $log);
                $ret="Locked"."\n"."type=prepaid";
                return $ret;
            }

            $maxduration=0;
            // Build Rate dictionary containing normalized CDR fields plus customer Balance

            if (count($active_sessions)) {
                // set  $this->remaining_balance and $this->parallel_calls for ongoing calls:
                if (!$this->getActivePrepaidSessions($active_sessions, $current_balance, $CDR->BillingPartyId, array($CDR->callId))) {
                    $ret="0"."\n"."type=prepaid";
                    return $ret;
                }

                $this->runtime['get_parallel_calls']=microtime_float();

                // add this new call to the list of parallel calls
                $RateDictionary=array(
                                      'duration'        => $CDR->duration,
                                      'callId'          => $CDR->callId,
                                      'Balance'         => $this->remaining_balance,
                                      'timestamp'       => $CDR->timestamp,
                                      'DestinationId'   => $CDR->DestinationId,
                                      'region'          => $CDR->region,
                                      'domain'          => $CDR->domain,
                                      'gateway'         => $CDR->gateway,
                                      'BillingPartyId'  => $CDR->BillingPartyId,
                                      'ENUMtld'         => $CDR->ENUMtld,
                                      'RatingTables'    => $this->CDRS->RatingTables,
                                      'application'     => $application
                                      );

                $Rate = new Rate($this->settings, $this->db);

                $_maxduration = round($Rate->MaxSessionTime($RateDictionary));

                $log = sprintf ("Maximum duration for new session %s of %s to destination %s having balance=%s is %s",
                $CDR->callId,
                $CDR->BillingPartyId,
                $CDR->DestinationId,
                $this->remaining_balance,
                $_maxduration);
                syslog(LOG_NOTICE, $log);

                if ($_maxduration > 0) {
                    $this->parallel_calls[$CDR->callId]=array('remainingBalancePerSecond' => $this->remaining_balance/$_maxduration);
                } else {
                    $log = sprintf ("Maximum duration for new session %s of %s <=0",$CDR->callId,$CDR->BillingPartyId);
                    syslog(LOG_NOTICE, $log);
                    $ret="0"."\n"."type=prepaid";
                    return $ret;
                }

                $this->parallel_calls[$CDR->callId]=array('remainingBalancePerSecond' => $this->remaining_balance/$_maxduration);

                $maxduration=$this->getAggregatedMaxSessiontime($this->parallel_calls, $this->remaining_balance, $CDR->BillingPartyId);

            } else {
                $RateDictionary=array(
                                      'duration'        => $CDR->duration,
                                      'callId'          => $CDR->callId,
                                      'Balance'         => $current_balance,
                                      'timestamp'       => $CDR->timestamp,
                                      'DestinationId'   => $CDR->DestinationId,
                                      'region'          => $CDR->region,
                                      'domain'          => $CDR->domain,
                                      'gateway'         => $CDR->gateway,
                                      'BillingPartyId'  => $CDR->BillingPartyId,
                                      'ENUMtld'         => $CDR->ENUMtld,
                                      'RatingTables'    => $this->CDRS->RatingTables,
                                      'application'     => $application
                                      );

                $Rate = new Rate($this->settings, $this->db);

                $this->runtime['instantiate_rate']=microtime_float();
                $maxduration = round($Rate->MaxSessionTime($RateDictionary));
            }

            // add new active session
            $active_sessions[$CDR->callId]= array('timestamp'       => $CDR->timestamp,
                                                  'duration'        => $CDR->duration,
                                                  'BillingPartyId'  => $CDR->BillingPartyId,
                                                  'MaxSessionTime'  => $maxduration,
                                                  'domain'          => $CDR->domain,
                                                  'gateway'         => $CDR->gateway,
                                                  'Destination'     => $CDR->destinationPrint,
                                                  'DestinationId'   => $CDR->DestinationId,
                                                  'region'          => $CDR->region,
                                                  'connectCost'     => $Rate->connectCost
                                                  );

            if ($CDR->ENUMtld) {
                $active_sessions[$CDR->callId]['ENUMtld']=$CDR->ENUMtld;
            }

            $this->runtime['calculate_maxduration']=microtime_float();

            if ($maxduration < 0) {
                $log = sprintf ("error: maxduration %s is negative",$maxduration);
                syslog(LOG_NOTICE, $log);
                $ret=$log."\n"."type=prepaid";
                return $ret;
            }

            if ($Rate->min_duration && $maxduration < $Rate->min_duration) {
                $log = sprintf ("Notice: maxduration of %s is less then min_duration (%s)",$maxduration,$Rate->min_duration);
                syslog(LOG_NOTICE, $log);
                $ret="0"."\n"."type=prepaid";
                return $ret;
            }

            if (!$Rate->billingTimezone) {
                $log = sprintf ("error: cannot figure out the billing timezone")."\n"."type=prepaid";
                syslog(LOG_NOTICE, $log);
                $ret=$log."\n"."type=prepaid";
                return $ret;
            }

            if (!$Rate->startTimeBilling) {
                $log = sprintf ("error: cannot figure out the billing start time")."\n"."type=prepaid";
                syslog(LOG_NOTICE, $log);
                $ret=$log."\n"."type=prepaid";
                return $ret;
            }

            $log=sprintf("MaxSessionTime=%s Type=prepaid CallId=%s BillingParty=%s DestId=%s Balance=%s Spans=%d Counter=%d->%d",
            $maxduration,
            $NetFields['callid'],
            $CDR->BillingPartyId,
            $CDR->DestinationId,
            $RateDictionary['Balance'],
            $Rate->MaxSessionTimeSpans,
            $old_session_counter,
            count($active_sessions)
            );

            syslog(LOG_NOTICE, $log);

            if ($maxduration > 0) {
                $query=sprintf("update %s
                set
                active_sessions = '%s',
                session_counter  = '%s'
                where account  = '%s'",
                addslashes($this->prepaid_table),
                addslashes(json_encode($active_sessions)),
                count($active_sessions),
                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);
                    $log=sprintf ("error: database error %s (%s)",$this->db->Error,$this->db->Errno);
                    return $log;
                }
            }

            $this->runtime['update_prepaid']=microtime_float();

            $this->logRuntime();

            $ret=$maxduration."\n"."type=prepaid";
            return $ret;

        } else if ($NetFields['action'] == "dumpprepaidsessions") {
            if (!$NetFields['account']) {
                $log=sprintf ("error: missing account parameter");
                syslog(LOG_NOTICE, $log);
                return $log;
            }

            $query=sprintf("select * from %s where account = '%s'",
            addslashes($this->prepaid_table),
            addslashes($NetFields['account'])
            );

            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 0;
            }

            if (!$this->db->num_rows()) {
                $log=sprintf ("DebitBalanceAudio() error: account $account does not exist");
                syslog(LOG_NOTICE, $log);
                $this->logRuntime();
                return 0;
            }

            $this->db->next_record();

            return var_export(json_decode($this->db->f('active_sessions'),true), true);

        } else if ($NetFields['action'] == "debitbalance") {

            if (!$NetFields['from']) {
                $log=sprintf ("error: missing From parameter");
                syslog(LOG_NOTICE, $log);
                return $log;
            }

            if (!$NetFields['to']) {
                $log=sprintf ("error: missing To parameter");
                syslog(LOG_NOTICE, $log);
                return $log;
            }

            $app_prefix = preg_replace('/[.].*$/', '', $NetFields['application']);
            if (!strlen($app_prefix) || (strlen($app_prefix) && $app_prefix == 'audio')) {
                if (!strlen($NetFields['duration'])) {
                    $log=sprintf ("error: missing Duration parameter");
                    syslog(LOG_NOTICE, $log);
                    return $log;
                }
            }

            if (strlen($app_prefix)) {
                if ($app_prefix == 'audio' || $app_prefix == 'sms' ) {
                    $application=$NetFields['application'];
                } else {
                    $log=sprintf ("error: unsupported application %s",$NetFields['application']);
                    syslog(LOG_NOTICE, $log);
                    return $log;
                }
            } else {
                $application='audio';
                $app_prefix='audio';
            }

            if (!$NetFields['gateway']) {
                $log=sprintf ("error: missing gateway parameter");
                syslog(LOG_NOTICE, $log);
                return $log;
            }

            if (!$NetFields['callid']) {
                $log=sprintf ("error: missing Call Id parameter");
                syslog(LOG_NOTICE, $log);
                return $log;
            }

            if ($NetFields['force']) {
                $force=true;
            } else {
                $force=false;
            }

            $timestamp=time();

            list($username_t,$domain_t)=explode("@",$NetFields['from']);

            $CDRStructure=array (
                              $this->CDRS->CDRFields['callId']         => $NetFields['callid'],
                              $this->CDRS->CDRFields['aNumber']        => $NetFields['from'],
                              $this->CDRS->CDRFields['CanonicalURI']   => $NetFields['to'],
                              $this->CDRS->CDRFields['gateway']        => $NetFields['gateway'],
                              $this->CDRS->CDRFields['ENUMtld']        => $NetFields['enumtld'],
                              $this->CDRS->CDRFields['duration']       => floor($NetFields['duration']),
                              $this->CDRS->CDRFields['timestamp']      => time(),
                              $this->CDRS->CDRFields['domain']         => $domain_t,
                              $this->CDRS->CDRFields['application']    => $application,
                              'skip_fix_prepaid_duration'              => true
                              );


            // Init CDR
            $CDR = new $this->CDRS->CDR_class($this->CDRS, $CDRStructure);
            $CDR->normalize();

            $this->runtime['normalize_cdr']=microtime_float();

            // Build Rate dictionary containing normalized CDR fields plus customer Balance
            $RateDictionary=array(
                                  'callId'          => $NetFields['callid'],
                                  'timestamp'       => $CDR->timestamp,
                                  'duration'        => $CDR->duration,
                                  'DestinationId'   => $CDR->DestinationId,
                                  'region'          => $CDR->region,
                                  'domain'          => $CDR->domain,
                                  'gateway'         => $CDR->gateway,
                                  'BillingPartyId'  => $CDR->BillingPartyId,
                                  'ENUMtld'         => $CDR->ENUMtld,
                                  'RatingTables'    => $this->CDRS->RatingTables,
                                  'application'     => $application
                                  );


            $Rate = new Rate($this->settings, $this->db);

            $this->runtime['instantiate_rate']=microtime_float();

            if ($app_prefix == 'audio') {
                if ($Rate->calculateAudio($RateDictionary)) {

                    $this->runtime['calculate_rate']=microtime_float();

                    $this->sessionDoesNotExist=false;

                    $result = $this->DebitBalanceAudio($CDR->BillingPartyId,$Rate->price,$NetFields['callid'],$CDR->duration,$force);

                    if ($this->sessionDoesNotExist) {
                        return "Failed";
                    }

                    $this->runtime['debit_balance']=microtime_float();

                    $log = sprintf ("DebitBalance=%s Duration=%s CallId=%s BillingParty=%s DestId=%s MaxSessionTime=%d Counter=%d->%d",
                    $Rate->price,
                    $CDR->duration,
                    $NetFields['callid'],
                    $CDR->BillingPartyId,
                    $CDR->DestinationId,
                    $result,
                    $this->old_session_count,
                    $this->new_session_count
                    );

                    syslog(LOG_NOTICE, $log);

                    $RateReturn = "Ok";
                    $RateReturn.= sprintf("\nMaxSessionTime=%d",$result);

                    if (strlen($Rate->price)) {
                        $RateReturn.="\n".$Rate->price;
                        if ($Rate->rateInfo) {
                            $RateReturn.="\n".trim($Rate->rateInfo);
                        }
                    }

                    return $RateReturn;
                } else {
                    syslog(LOG_NOTICE, 'Failed to calculate rate in DebitBalance()');
                    return "Failed\n";
                }
            } else if ($app_prefix == 'sms') {
                // return Ok, No credit, Error
                if ($Rate->calculateMessage($RateDictionary)) {

                    if ($this->DebitBalanceMessage($CDR->BillingPartyId,$CDR->destinationPrint,$Rate->price,$NetFields['callid'])) {

                        $log = sprintf ("Price=%s CallId=%s BillingParty=%s DestId=%s Application=%s",
                        $Rate->price,
                        $NetFields['callid'],
                        $CDR->BillingPartyId,
                        $CDR->DestinationId,
                        $application
                        );

                        syslog(LOG_NOTICE, $log);

                        $RateReturn = "Ok";

                        if (strlen($Rate->price)) {
                            $RateReturn.="\n".$Rate->price;
                            if ($Rate->rateInfo) {
                                $RateReturn.="\n".trim($Rate->rateInfo);
                            }
                        }

                        return $RateReturn;
                    } else {
                        return "Failed";
                    }

                } else {
                    return "Failed";
                }

            } else {
                return false;
            }


        } 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;
            }

            return $this->CreditBalance($NetFields['from'],$NetFields['value']);

        } 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();
            }

            if (!$NetFields['gateway']) {
                $log=sprintf ("error: missing gateway parameter");
                syslog(LOG_NOTICE, $log);
                return $log;
            }

            $app_prefix = preg_replace('/[.].*$/', '', $NetFields['application']);
            if (strlen($app_prefix)) {
                if ($app_prefix == 'audio' || $app_prefix == 'sms' ) {
                    $application=$NetFields['application'];
                } else {
                    $log=sprintf ("error: unsupported application %s",$NetFields['application']);
                    syslog(LOG_NOTICE, $log);
                    return $log;
                }
            } else {
                $application='audio';
            }

            list($username_t,$domain_t)=explode("@",$NetFields['from']);

            $CDRStructure=array (
                              $this->CDRS->CDRFields['callId']         => $NetFields['callid'],
                              $this->CDRS->CDRFields['aNumber']        => $NetFields['from'],
                              $this->CDRS->CDRFields['CanonicalURI']   => $NetFields['to'],
                              $this->CDRS->CDRFields['gateway']        => $NetFields['gateway'],
                              $this->CDRS->CDRFields['ENUMtld']        => $NetFields['enumtld'],
                              $this->CDRS->CDRFields['duration']       => floor($NetFields['duration']),
                              $this->CDRS->CDRFields['timestamp']      => time(),
                              $this->CDRS->CDRFields['domain']         => $domain_t,
                              $this->CDRS->CDRFields['application']    => $application,
                              'skip_fix_prepaid_duration'              => true
                              );

            $CDR = new $this->CDRS->CDR_class($this->CDRS, $CDRStructure);
            $CDR->normalize();

            $Rate    = new Rate($this->settings, $this->db);

            $RateDictionary=array(
                                  'callId'          => $CDR->callId,
                                  'timestamp'       => $CDR->timestamp,
                                  'duration'        => $CDR->duration,
                                  'DestinationId'   => $CDR->DestinationId,
                                  'region'          => $CDR->region,
                                  'domain'          => $CDR->domain,
                                  'gateway'         => $CDR->gateway,
                                  'BillingPartyId'  => $CDR->BillingPartyId,
                                  'ENUMtld'         => $CDR->ENUMtld,
                                  'RatingTables'    => $this->CDRS->RatingTables,
                                  'application'     => $application
                                  );

            $Rate->calculateAudio($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;
            }

            $query=sprintf("select * from %s where account = '%s'",
            addslashes($this->prepaid_table),
            addslashes($NetFields['from'])
            );

            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 0;
            }

            if ($this->db->num_rows()) {
                $this->db->next_record();
                return number_format($this->db->f('balance'),4,".","");
            } else {
                return sprintf("%0.4f",0);
            }

        } 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'] == "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'] == "keepalive") {
            return $this->keepAlive();
        } else if ($NetFields['action'] == "reloadquota") {
            if (!$NetFields['account']) {
                $log=sprintf ("Error: Missing Account parameter");
                syslog(LOG_NOTICE, $log);
                return 0;
            }

            return $this->reloadQuota($NetFields['account']);
        } 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 {
            $log=sprintf ("Error: Invalid request");
                syslog(LOG_NOTICE, $log);
            return 0;
        }
    }