rendered paste body/*
* Asterisk -- A telephony toolkit for Linux.
*
* MCC - Termination Edition application
*
* Copyright (C) 2005, Mindaugas Kezys, Upnet
*
* $Id: app_mcc2.c,v 1.4.5
*
* TODO:
*
* check credit right before updating it and kill all other user's calls if new credit <=0 (how to do this?)
*
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*
*/
/*
HANGUP_CAUSE Error Codes:
NO_DB_ACCESS
DB_FATAL_ERROR
NO_USER_FOUND
LOW_CREDIT
BAD_DESTINATION
CANT_FREEZE_CREDIT
BLOCKED
*/
// ast_log(LOG_WARNING,"mes cia'\n");
#include <asterisk/lock.h>
#include <stdio.h>
#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/pbx.h>
#include <asterisk/module.h>
#include <asterisk/app.h>
#include <asterisk/say.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <math.h>
#include <asterisk/config.h>
#include <libpq-fe.h>
#include <asterisk/translate.h>
#include <asterisk/features.h>
#include <asterisk/musiconhold.h>
#include <asterisk/callerid.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <asterisk/options.h>
#define DATE_FORMAT "%Y-%m-%d"
#define TIME_FORMAT "%T"
static char *tdesc = "MCC2 - Calling Application for Authorization and Accounting";
static char *app = "mcc2";
static char *config = "mcc2.conf";
static char *synopsis = "MCC2 - Authorization, Calling and Accounting";
static char *descrip =
" Prepaid/postpaid calling application\n"
" Finds user (checks the credit) and calls destination (if it's enough)\n"
" Updates DB and allows simultaneus calls.\n";
static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbsock = NULL, *pgdbport = NULL, *min_frozen = NULL, *frozen_time = NULL, *pround_sec = NULL;
static int hostname_alloc = 0, dbname_alloc = 0, dbuser_alloc = 0, password_alloc = 0, dbsock_alloc = 0, dbport_alloc = 0, min_frozen_alloc = 0, frozen_time_alloc = 0, pround_sec_alloc = 0;
static int connected = 0; /* Initially not connected to the DB */
PGconn *conn;
ast_mutex_t postgreslock;
STANDARD_LOCAL_USER;
LOCAL_USER_DECL;
struct calldata {
char accountcode[30];
char user_id[20];
char tariff_id[20];
char credit[20];
char credit_frozen[20];
char connection_fee[20];
char rate_connection_fee[20];
char min_time[20];
char increment[20];
char hangup_cause[20];
char destination[30];
char prefix[30];
char tech[100];
char rate[20];
char enable_cid_prefix[20];
char disable_cid_prefix[20];
char enable_cid_to_provider[20];
int credit_to_freeze;
int timeout;
char date[20];
char time[20];
char source[30];
char cid[20];
char postpaid[20];
char blocked[20];
};
int SHOW_NOTICE;
int SHOW_ERROR;
int SHOW_WARNING;
int ROUND_TIME;
int ROUND_TO;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SHOW_NOTICE = atoi(pshow_notice);
/* Checks if we are connected to the db - if not then it tries to reconnect */
static int db_check_connected(void)
{
int i;
PGresult *result;
if (connected) {
/* seems that we are connected - but let us check that */
for (i=0; i<3; i++) {
/* PQstatus is not working as expected - so we are trying a query before PQstatus - hope that helps */
ast_mutex_lock(&postgreslock);
result = PQexec(conn, "SELECT 1");
ast_mutex_unlock(&postgreslock);
PQclear(result);
if (PQstatus(conn) == CONNECTION_BAD) {
/* seems that we are having troubles with the connection - trying to reset the connection */
/* and set connected to false */
connected = 0;
if (SHOW_ERROR) ast_log(LOG_ERROR, "app_mcc2: connection to database server '%s' is broken - I will try to reconnect (up to) 3 times\n", pghostname);
PQreset(conn);
/* retry it up to 3 times */
continue;
} else {
/* Everything is fine - so go on */
connected = 1;
break;
}
}
} else {
/* We are not connected - so make the initial connection */
conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
if (PQstatus(conn) != CONNECTION_BAD) {
ast_log(LOG_DEBUG, "app_mcc2: Succesfully connected to database '%s'.\n", pgdbname);
connected = 1;
} else {
if (SHOW_ERROR) ast_log(LOG_ERROR, "app_mcc2: FATAL - Couldn't connect to database server '%s'. \n", pghostname);
connected = 0;
}
}
return connected;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int mcc2_ivr_authorize(struct ast_channel *chan, struct calldata *cd, int aaskext, void *data)
{
int res=0;
int got_account_code=0;
char sqlcmd[2048] = "";
PGresult *result;
int total_frozen_credit;
char cust_rate[10] = "";
struct tm tm;
struct timeval tv;
time_t t;
//------------------
strcpy(cd->rate,"0");
strcpy(cd->credit_frozen,"0");
strcpy(cd->connection_fee,"0");
strcpy(cd->min_time,"0");
strcpy(cd->increment,"0");
strcpy(cd->rate_connection_fee,"0");
strcpy(cd->hangup_cause,"");
strcpy(cd->cid,"");
strcpy(cd->enable_cid_prefix,"");
strcpy(cd->disable_cid_prefix,"");
strcpy(cd->enable_cid_to_provider,"t");
//date-time
gettimeofday(&tv, NULL);
t=tv.tv_sec;
localtime_r(&t, &tm);
strftime(cd->date, 128, DATE_FORMAT, &tm);
strftime(cd->time, 128, TIME_FORMAT, &tm);
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Date: %s, time: %s\n", cd->date, cd->time);
//ast_log(LOG_WARNING,"1\n");
//source
strcpy(cd->source,chan->cdr->src);
//strncpy(cd->source,chan->cdr->src, strlen(chan->cdr->src));
//ast_log(LOG_WARNING,"2\n");
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Source: %s\n", cd->source);
//destination
//ast_log(LOG_WARNING,"AUTHORIZE - data; '%s', aaskext; '%i'\n", data, aaskext);
strcpy(cd->destination,data);
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Destination: %s\n", cd->destination);
//accountcode
strcpy(cd->accountcode,chan->accountcode);
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Accountcode: %s\n", cd->accountcode);
//check if we are connected to db
if (!db_check_connected()){
strcpy(cd->hangup_cause,"NO_DB_ACCESS");
strcpy(cd->user_id,"0");
res = -1;
}
//check if accountcode not empty
if ((!res)&&(!strlen(cd->accountcode))) {
if (SHOW_WARNING) ast_log(LOG_WARNING,"app_mcc2: ERROR: Empty accountcode!\n");
//send mail to me with same message, play some excuse audio to user
//res = ast_streamfile(chan, "prepaid-dest-unreachable", chan->language); if (!res) res = ast_waitstream(chan, "");
res = -1;
}
//-------- Looking for accountcode in 'users' table ----------------
if (!res){
sprintf(sqlcmd,"SELECT id, tariff_id, credit, credit_frozen, postpaid, blocked, connection_fee, min_time, increment FROM users WHERE users.id = '%s'", cd->accountcode);
ast_mutex_lock(&postgreslock);
result = PQexec(conn, sqlcmd);
ast_mutex_unlock(&postgreslock);
if (PQresultStatus(result) != PGRES_TUPLES_OK) {
if (SHOW_ERROR) {
ast_log(LOG_ERROR,"app_mcc2: FATAL ERROR 4: %s\n", PQresultErrorMessage(result));
ast_log(LOG_ERROR,"app_mcc2: FATAL ERROR 4: is your accountcode a number???\n");
}
//send mail to me with same message, play some excuse audio to user
//res = ast_streamfile(chan, "prepaid-no-aaa", chan->language);
//if (!res) res = ast_waitstream(chan, "");
res = -1;
} else {
if(PQntuples(result)<=0) {
if (SHOW_WARNING) ast_log(LOG_WARNING,"app_mcc2: WARNING: User with accountcode '%s' not found in database\n", cd->accountcode);
//send mail to me with same message, play some excuse audio to user
//res = ast_streamfile(chan, "prepaid-dest-unreachable", chan->language); if (!res) res = ast_waitstream(chan, "");
res = -1;
}
}
//retrieving user data
if (!res){
strcpy(cd->user_id, PQgetvalue(result,0,0));
strcpy(cd->tariff_id, PQgetvalue(result,0,1));
strcpy(cd->credit, PQgetvalue(result,0,2));
strcpy(cd->credit_frozen, PQgetvalue(result,0,3));
strcpy(cd->postpaid, PQgetvalue(result,0,4));
strcpy(cd->blocked, PQgetvalue(result,0,5));
strcpy(cd->connection_fee, PQgetvalue(result,0,6));
strcpy(cd->min_time, PQgetvalue(result,0,7));
strcpy(cd->increment, PQgetvalue(result,0,8));
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: User's data retrieved by accountcode: user_id: %s, tariff_id: %s, credit: %s, credit_frozen: %s, postpaid: %s, connection_fee: %s, min_time: %s, increment: %s\n", cd->user_id, cd->tariff_id, cd->credit, cd->credit_frozen, cd->postpaid, cd->connection_fee, cd->min_time, cd->increment);
got_account_code = 1;
}
PQclear(result);
}
//---------------------- no account code, looking at cid --------------------------
//+++++++++++++++++++++++++++++++++++++++++
//----Post 1.2----
// if (chan->cid.cid_num) strncpy(cd->cid,chan->cid.cid_num, strlen(chan->cid.cid_num));
if (chan->cid.cid_num) strcpy(cd->cid,chan->cid.cid_num);
//----Prior 1.2------
//if (cd->source) strcpy(cd->cid,cd->source);
//+++++++++++++++++++++++++++++++++++++++++
if (!got_account_code){
//search one more time
res=0;
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"No account code, searching user by CID: %s\n", cd->cid);
//---Taking user's data---
sprintf(sqlcmd,"SELECT users.id, users.tariff_id, users.credit, users.credit_frozen, users.postpaid, users.blocked, users.connection_fee, cids.enable_to_provider, users.min_time, users.increment FROM users INNER JOIN cids ON users.id=cids.user_id WHERE cids.cid='%s'", cd->cid);
ast_mutex_lock(&postgreslock);
result = PQexec(conn, sqlcmd);
ast_mutex_unlock(&postgreslock);
if (PQresultStatus(result) != PGRES_TUPLES_OK) {
if (SHOW_ERROR) ast_log(LOG_ERROR,"app_mcc2: FATAL ERROR 4: %s\n", PQresultErrorMessage(result));
//send mail to me with same message, play some excuse audio to user
//res = ast_streamfile(chan, "prepaid-no-aaa", chan->language);
//if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"DB_FATAL_ERROR");
strcpy(cd->user_id,"0");
res = -1;
} else {
if(PQntuples(result)<=0) {
if (SHOW_WARNING) ast_log(LOG_WARNING,"app_mcc2: WARNING: User with cid %s not found in database\n", cd->cid);
//send mail to me with same message, play some excuse audio to user
res = ast_streamfile(chan, "prepaid-auth-fail", chan->language);
if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"NO_USER_FOUND");
strcpy(cd->user_id,"0");
res = -1;
}
}
//retrieving user data
if (!res){
strcpy(cd->user_id, PQgetvalue(result,0,0));
strcpy(cd->tariff_id, PQgetvalue(result,0,1));
strcpy(cd->credit, PQgetvalue(result,0,2));
strcpy(cd->credit_frozen, PQgetvalue(result,0,3));
strcpy(cd->postpaid, PQgetvalue(result,0,4));
strcpy(cd->blocked, PQgetvalue(result,0,5));
strcpy(cd->connection_fee, PQgetvalue(result,0,6));
strcpy(cd->enable_cid_to_provider, PQgetvalue(result,0,7));
strcpy(cd->min_time, PQgetvalue(result,0,8));
strcpy(cd->increment, PQgetvalue(result,0,9));
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: User's data retrieved by cid: user_id: %s, tariff_id: %s, credit: %s, credit_frozen: %s, postpaid: %s, connection_fee: %s, enable_cid_to_provider: %s, min_time: %s, increment: %s\n", cd->user_id, cd->tariff_id, cd->credit, cd->credit_frozen, cd->postpaid, cd->connection_fee, cd->enable_cid_to_provider, cd->min_time, cd->increment);
}
PQclear(result);
}
//-------------------------------- end checking cid -------------------------------
//-------------------------------- process for prepaid users ----------------------
if (!strcmp("f", cd->postpaid)) {
//if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Processing credit for prepaid users\n");
//check credit
if ((!res)&&(atof(cd->credit) <= 0)) {
if (SHOW_WARNING) ast_log(LOG_WARNING,"app_mcc2: Low credit for user with account: %s\n", cd->accountcode);
res = ast_streamfile(chan, "prepaid-credit-low", chan->language);
if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"LOW_CREDIT");
res = -1;
}
//check frozen credit
if ((!res) && (atof(cd->credit)-atoi(cd->credit_frozen) < atoi(min_frozen)) && (atoi(cd->credit_frozen)) ){
if (SHOW_WARNING) ast_log(LOG_WARNING,"app_mcc2: Too low credit for more simultaneus calls\n");
res = ast_streamfile(chan, "prepaid-credit-low", chan->language);
if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"LOW_CREDIT");
res = -1;
}
}
//-------------------------------- end processing for prepaid users ---------------
//------------------------------------ BLOCKING users -----------------------------
if (!strcmp("t", cd->blocked)) {
if (SHOW_WARNING) ast_log(LOG_WARNING,"app_mcc2: This user is BLOCKED!!!\n");
res = ast_streamfile(chan, "prepaid-blocked", chan->language);
if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"BLOCKED");
res = -1;
}
//---------------------------------- stop blocking users --------------------------
//-------------------------------- asking for extension if needed -----------------
if ((!res)&&(aaskext)) {
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Asking for extension.\n");
res = ast_app_getdata(chan, "prepaid-enter-dest", cd->destination, sizeof(cd->destination)-2,0);
if (res < 0) res = -1;
ast_log(LOG_DEBUG,"app_mcc2: destination's length: %i, res = %i\n",strlen(cd->destination), res);
// if (strlen(cd->destination)<5) {
// strcpy(cd->hangup_cause,"BAD_DESTINATION");
// res = -1;
// }
}
//-------------------------------- end asking for extension -----------------------
//looking for most compatible destination and take tech/rate
if (!res){
sprintf(sqlcmd,"SELECT destinations.prefix, providers.tech, rates.rate, providers.enable_cid_prefix, providers.disable_cid_prefix, rates.connection_fee FROM destinations, providers, rates WHERE destinations.prefix=SUBSTRING('%s', 0, LENGTH(destinations.prefix)+1) AND destinations.provider_id = providers.id AND destinations.prefix = rates.prefix AND rates.tariff_id = '%s' ORDER BY LENGTH(destinations.prefix) DESC LIMIT 1", cd->destination, cd->tariff_id);
ast_mutex_lock(&postgreslock);
result = PQexec(conn, sqlcmd);
ast_mutex_unlock(&postgreslock);
if (PQresultStatus(result) != PGRES_TUPLES_OK) {
if (SHOW_ERROR) ast_log(LOG_ERROR,"app_mcc2: FATAL ERROR 4: %s\n", PQresultErrorMessage(result));
//send mail to me with same message, play some excuse audio to user
res = ast_streamfile(chan, "prepaid-no-aaa", chan->language);
if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"DB_FATAL_ERROR");
strcpy(cd->user_id,"0");
res = -1;
} else {
if(PQntuples(result)<=0) {
if (SHOW_WARNING) ast_log(LOG_WARNING,"app_mcc2: ERROR: Could not find prefix for destination %s\n", cd->destination);
//send mail to me with same message!!! BIG PRIORITY, play some excuse audio to user
res = ast_streamfile(chan, "prepaid-dest-unreachable", chan->language);
if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"BAD_DESTINATION");
res = -1;
}
}
//retrieving destination data
if (!res){
strcpy(cd->prefix, PQgetvalue(result,0,0));
strcpy(cd->tech, PQgetvalue(result,0,1));
strcpy(cd->rate, PQgetvalue(result,0,2));
strcpy(cd->enable_cid_prefix, PQgetvalue(result,0,3));
strcpy(cd->disable_cid_prefix,PQgetvalue(result,0,4));
strcpy(cd->rate_connection_fee,PQgetvalue(result,0,5));
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Destination's data retrieved: prefix: %s, tech: %s, rate: %s, enable_cid_prefix: %s, disable_cid_prefix: %s, rate_connection_fee: %s\n", cd->prefix, cd->tech, cd->rate, cd->enable_cid_prefix, cd->disable_cid_prefix, cd->rate_connection_fee);
}
PQclear(result);
}
//-------------------------------- modify CALLERID NUMBER ----------------------
char buf[100] = "";
if (!strcmp(cd->enable_cid_to_provider,"t"))
snprintf(buf, sizeof(buf), "%s%s", cd->enable_cid_prefix, cd->cid);
else
snprintf(buf, sizeof(buf), "%s%s", cd->disable_cid_prefix, cd->cid);
pbx_builtin_setvar_helper(chan, "CALLERID(num)", buf);
//-------------------------------------------------------------------------
// timeout for postpaid users (2h)
cd->timeout = 72000;
//-------------------------------- checking for custom rates ----------------------
if (!res){
sprintf(sqlcmd,"SELECT rate FROM cust_rates WHERE prefix='%s' AND user_id = '%s'", cd->prefix, cd->user_id);
ast_mutex_lock(&postgreslock);
result = PQexec(conn, sqlcmd);
ast_mutex_unlock(&postgreslock);
if (PQresultStatus(result) != PGRES_TUPLES_OK) {
if (SHOW_ERROR) ast_log(LOG_ERROR,"app_mcc2: FATAL ERROR 4: %s\n", PQresultErrorMessage(result));
//send mail to me with same message, play some excuse audio to user
res = ast_streamfile(chan, "prepaid-no-aaa", chan->language);
if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"DB_FATAL_ERROR");
strcpy(cd->user_id,"0");
res = -1;
} else {
if(PQntuples(result)<=0) { //cust rate not found
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Custom rate for prefix: '%s' not found.\n", cd->prefix);
} else { //cust rate found
strcpy(cust_rate, PQgetvalue(result,0,0));
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Custom rate fot prefix: '%s' FOUND! Custom rate: %s\n", cd->prefix, cust_rate);
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Primary rate: %s changed to custom rate: %s\n", cd->rate, cust_rate);
strcpy(cd->rate, cust_rate);
}
}
PQclear(result);
}
//-------------------------------- process for prepaid users ----------------------
if (!strcmp("f", cd->postpaid)) {
//if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Processing credit for prepaid users\n");
//freezing credit
if (!res) {
cd->credit_to_freeze = ceilf( atof(cd->rate)* atoi(frozen_time) );
cd->timeout = floorf(atoi(cd->credit) / atof(cd->rate) *60);
if (cd->credit_to_freeze > (atof(cd->credit) - atoi(cd->credit_frozen))) {
cd->credit_to_freeze = (atof(cd->credit) - atoi(cd->credit_frozen));
}
if (cd->timeout <0 ) cd->timeout = 72000;
if (cd->timeout == 0 ) res = -1;
if (!res){
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Frozen credit time: %s minutes, credit to freeze: %i ct(EUR), timeout: %i s\n", frozen_time, cd->credit_to_freeze, cd->timeout);
//update users.credit_frozen
total_frozen_credit = atoi(cd->credit_frozen) + cd->credit_to_freeze;
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Total frozen credit: %i\n", total_frozen_credit);
sprintf(sqlcmd,"UPDATE users SET credit_frozen='%i' WHERE users.id='%s'", total_frozen_credit ,cd->user_id);
ast_mutex_lock(&postgreslock);
result = PQexec(conn, sqlcmd);
ast_mutex_unlock(&postgreslock);
PQclear(result);
} else {
if (SHOW_WARNING) ast_log(LOG_WARNING,"app_mcc2: Too low credit for more simultaneus calls\n");
res = ast_streamfile(chan, "prepaid-credit-low", chan->language);
if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"LOW_CREDIT");
res = -1;
}
}
}
//-------------------------------- end processing for prepaid users ---------------
return res;
//
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int mcc2_pbx_dial(struct ast_channel *chan, void *data)
{
struct ast_app *app;
int ret;
app = pbx_findapp("Dial");
if (app) {
ret = pbx_exec(chan, app, data, 1);
} else {
if (SHOW_WARNING) ast_log(LOG_WARNING, "Could not find application (Dial)\n");
ret = -2;
}
//if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: HANGUP CAUSE: %s\n", ast_cdr_disp2str(chan->cdr->disposition));
return ret;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int mcc2_acct_stop(struct ast_channel *chan, struct ast_cdr *cdr, struct calldata *cd)
{
int res=0;
int billsec;
int round_sec;
int icredit;
float price;
char sqlcmd[2048] = "";
PGresult *result;
float new_credit = 0, new_frozen_credit = 0;
//hangup_cause
if (!strlen(cd->hangup_cause)) {strcpy(cd->hangup_cause,ast_cdr_disp2str(cdr->disposition));}
// if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Destination: %s\n", cd->destination);
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: HANGUP CAUSE: %s\n", ast_cdr_disp2str(cdr->disposition));
//billsec
ast_cdr_end(cdr);
if (!cdr->end.tv_sec && !cdr->end.tv_usec)
if (SHOW_WARNING) ast_log(LOG_WARNING, "app_mcc2: CDR on channel '%s' lacks end\n", cdr->channel);
if (!cdr->start.tv_sec && !cdr->start.tv_usec)
if (SHOW_WARNING) ast_log(LOG_WARNING, "app_mcc2: CDR on channel '%s' lacks start\n", cdr->channel);
cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
if (cdr->answer.tv_sec || cdr->answer.tv_usec) {
cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
} else
cdr->billsec = 0;
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Seconds to bill: %i\n", cdr->billsec);
//some bug fixing
// if (((atof(cd->rate)==0)||(cdr->billsec ==0))&&(!strcmp(cd->hangup_cause,"ANSWERED"))) {strcpy(cd->hangup_cause,"NO ANSWER");}
if ((cdr->billsec ==0)&&(!strcmp(cd->hangup_cause,"ANSWERED"))) {strcpy(cd->hangup_cause,"NO ANSWER");}
if (!strcmp(cd->hangup_cause,"ANSWERED")){
//round bill time (increment)
round_sec = atoi(cd->increment);//atoi(pround_sec);
if (!(cdr->billsec % round_sec)){
billsec = ceilf(cdr->billsec / round_sec)*round_sec;
} else {
billsec = (ceilf(cdr->billsec / round_sec)+1)*round_sec;
}
//if time less than xxx then time=xxx (min_time)
//if ((ROUND_TIME)&&(billsec<ROUND_TIME)) billsec=ROUND_TIME;
if ((cd->min_time)&&(billsec < atoi(cd->min_time))) billsec=atoi(cd->min_time);
//calculate price for the call
if (ROUND_TO == 1) {price = ceilf( (atof(cd->rate)* billsec)/6 )/10; } else
if (ROUND_TO == 2) {price = ceilf( (atof(cd->rate)* billsec)/0.6)/100; }else
{price = (atof(cd->rate)* billsec)/60 ;} // round to 0
//adding connection fee
price += atof(cd->connection_fee) + atof(cd->rate_connection_fee);
//ast_log(LOG_NOTICE,"ROUND_TO: %i, PRICE: %f\n", ROUND_TO, price);
} else {
billsec = 0;
price = 0;
}
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Rounded seconds to bill: %i\n", billsec);
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Price for the call: %f (connection fee: %s and rate_connectio_fee %s included)\n", price, cd->connection_fee, cd->rate_connection_fee);
//update table 'calls'
sprintf(sqlcmd,"INSERT INTO calls (user_id, date, time, src, dst, hangup_cause, billsec, price, rate, user_connection_fee, rate_connection_fee) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%i', '%f', '%s', '%s', '%s')", cd->user_id, cd->date, cd->time, cd->source, cd->destination, cd->hangup_cause, billsec, price, cd->rate, cd->connection_fee, cd->rate_connection_fee);
//if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: SQL command as follows: %s\n",sqlcmd);
ast_mutex_lock(&postgreslock);
result = PQexec(conn, sqlcmd);
ast_mutex_unlock(&postgreslock);
PQclear(result);
//msg to console about call status
ast_log(LOG_NOTICE,"User ID: %s, src: %s, dst: %s, hangup: %s, time: %i, rate: %s, price: %f\n",cd->user_id, cd->source, cd->destination, cd->hangup_cause, billsec, cd->rate, price);
//--------- if we have user and he is prepaid user, then ..... -----------------
if ((strcmp(cd->user_id,"0"))&&(!strcmp("f", cd->postpaid))){
//get credit, credit_frozen
//update credit, credit_frozen
sprintf(sqlcmd,"SELECT credit, credit_frozen FROM users WHERE users.id = '%s'", cd->user_id);
ast_mutex_lock(&postgreslock);
result = PQexec(conn, sqlcmd);
ast_mutex_unlock(&postgreslock);
if (PQresultStatus(result) != PGRES_TUPLES_OK) {
if (SHOW_ERROR) ast_log(LOG_ERROR,"app_mcc2: FATAL ERROR 4: %s\n", PQresultErrorMessage(result));
//send mail to me with same message, play some excuse audio to user
res = ast_streamfile(chan, "prepaid-no-aaa", chan->language);
if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"DB_FATAL_ERROR");
res = -1;
} else {
if(PQntuples(result)<=0) {
if (SHOW_ERROR) ast_log(LOG_ERROR,"app_mcc2: ERROR: User with accountcode %s not found in database\n", cd->accountcode);
//send mail to me with same message, play some excuse audio to user
res = ast_streamfile(chan, "prepaid-auth-fail", chan->language);
if (!res) res = ast_waitstream(chan, "");
strcpy(cd->hangup_cause,"NO_USER_FOUND");
res = -1;
}
}
//retrieving user data
if (!res){
strcpy(cd->credit, PQgetvalue(result,0,0));
strcpy(cd->credit_frozen, PQgetvalue(result,0,1));
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: User's data retrieved: credit: %s, credit_frozen: %s\n", cd->credit, cd->credit_frozen);
}
PQclear(result);
//calculate credit, credit_frozen
new_credit = atof(cd->credit) - price;
new_frozen_credit = atoi(cd->credit_frozen) - cd->credit_to_freeze;
if ((new_frozen_credit<0)||(new_frozen_credit>new_credit)) new_frozen_credit=0;
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: New credit: %f, new credit_frozen: %f\n", new_credit, new_frozen_credit);
//update db with new values
icredit = round(new_frozen_credit);
sprintf(sqlcmd,"UPDATE users SET credit='%f', credit_frozen='%i' WHERE users.id='%s'", new_credit, icredit ,cd->user_id);
ast_mutex_lock(&postgreslock);
result = PQexec(conn, sqlcmd);
ast_mutex_unlock(&postgreslock);
PQclear(result);
}
//------------------- end user operations --------------------
//no difference if we return error code
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
static int mcc2_exec(struct ast_channel *chan, void *data)
{
struct ast_cdr *cdr1; // Create local ast_cdr struct
int res = 0;
struct localuser *u;
struct calldata cd;
char dialstr[256] = "";
char dialstr2[256] = "";
char destination2[256] = "";
int askext = 0;
int nopickup = 0; //channell will not be picked up
int direct=0; //provaider's tech will be taken from mcc argument
char direct_tech[256] = ""; //tech for direct calling from mcc
char temp_tech[256] = ""; //temp tech for original tech formating
char cut[2] = ""; // how much to cut extension from provider's tech
int f,l,e; //variables for dialstring formating
//if (strncmp(data,"askext",strlen(data))==0) askext=1;
if (strstr(data,"askext")) askext=1;
//debug
//ast_log(LOG_WARNING,"data; '%s', askext; '%i'\n", data, askext);
if (strstr(data,"nopickup")) nopickup=1;
l=0;
f=0;
e=0;
//taking tech from mcc argument
if (strstr(data,"direct")){
strcpy(temp_tech, data);
l=strlen(temp_tech);
f=l-strlen(strstr((temp_tech),"direct|"));
e=f+7;
strcpy(direct_tech,"");
strcat(direct_tech,&temp_tech[e]);
ast_log(LOG_NOTICE,"Using tech from mcc: %s\n",direct_tech);
direct=1;
}
//removing garbage
if (strstr(data,"|")){
l=strlen(data);
f=l-strlen(strstr((data),"|"));
strcpy(data,(char *)strndup(data,f));
}
LOCAL_USER_ADD(u);
if ((chan->_state != AST_STATE_UP)&&(!nopickup)) {
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"Answering channel\n");
res = ast_answer(chan);
if (res) {
LOCAL_USER_REMOVE(u);
return -1;
}
}else{
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"NOT answering channel\n");
}
//authorizing
res = mcc2_ivr_authorize(chan, &cd, askext, data);
cdr1 = (struct ast_cdr*) malloc(sizeof(struct ast_cdr));
memcpy(cdr1,chan->cdr,sizeof(struct ast_cdr)); // Copy
//dialing
if (!res) {
ast_cdr_reset(chan->cdr,0);
// ast_cdr_reset(cdr1,0);
//ast_channel_setwhentohangup(chan,cd.timeout);
//if direct calling, use right tech
if (direct==1) {strcpy(cd.tech, direct_tech);}
// format new dialstring
if (strstr((cd.tech),"EXTEN")){
// get cutting param
if (strstr(cd.tech,"|")){
l = strlen(cd.tech);
f = l - strlen(strstr(cd.tech,"|")) + 1;
strcpy(cut,"");
strcat(cut,&cd.tech[f]);
//ast_log(LOG_NOTICE,"cut: %s\n", cut);
strcpy(cd.tech,(char *)strndup(cd.tech,f-1)); //trash the garbage
}
l=strlen(cd.tech);
f=l-strlen(strstr((cd.tech),"EXTEN"));
e=f+5;
strcpy(dialstr2,(char *)strndup(cd.tech,f));
//cut destination if necessary
if (cut){
strcat(destination2,&cd.destination[atoi(cut)]);
ast_log(LOG_NOTICE,"destination2: %s\n", destination2);
} else {
strcpy(destination2,cd.destination);
}
strcat(dialstr2,destination2);
strcat(dialstr2,&cd.tech[e]);
}else { //adding extension at the end of provider
strcpy(dialstr2,cd.tech);
if (direct!=1) strcat(dialstr2,cd.destination);
}
// //strcat(dialstr2,cd.destination);
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"Formating dialstring: length %i, first %i, last %i, string: %s\n", l,f,e,dialstr2);
// end format new dialstring
//adding timeout
strcpy(dialstr,dialstr2);
sprintf(dialstr2,"||S(%d)tT", cd.timeout);
strcat(dialstr,dialstr2);
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: Dialing '%s' with timeout of '%d'.\n", dialstr, cd.timeout);
res = mcc2_pbx_dial(chan, dialstr);
}
// if (SHOW_NOTICE) ast_log(LOG_NOTICE,"app_mcc2: HANGUP CAUSE: %s\n", ast_cdr_disp2str(chan->cdr->disposition));
//res =
mcc2_acct_stop(chan, chan->cdr, &cd);
// res = mcc2_acct_stop(chan, cdr1, &cd);
free(cdr1); // Free memory
LOCAL_USER_REMOVE(u);
if (SHOW_NOTICE) ast_log(LOG_NOTICE,"Application mcc2 started and ended succesfully, with end code: %i!\n", res);
// res=0;
return res;
}
int unload_module(void)
{
STANDARD_HANGUP_LOCALUSERS;
PQfinish(conn);
connected = 0;
if (pghostname && hostname_alloc) {
free(pghostname);
pghostname = NULL;
hostname_alloc = 0;
}
if (pgdbname && dbname_alloc) {
free(pgdbname);
pgdbname = NULL;
dbname_alloc = 0;
}
if (pgdbuser && dbuser_alloc) {
free(pgdbuser);
pgdbuser = NULL;
dbuser_alloc = 0;
}
if (pgdbsock && dbsock_alloc) {
free(pgdbsock);
pgdbsock = NULL;
dbsock_alloc = 0;
}
if (pgpassword && password_alloc) {
free(pgpassword);
pgpassword = NULL;
password_alloc = 0;
}
if (pgdbport && dbport_alloc) {
free(pgdbport);
pgdbport = NULL;
dbport_alloc = 0;
}
if (min_frozen && min_frozen_alloc) {
free(min_frozen);
min_frozen = NULL;
min_frozen_alloc = 0;
}
if (frozen_time && frozen_time_alloc) {
free(frozen_time);
frozen_time = NULL;
frozen_time_alloc = 0;
}
if (pround_sec && pround_sec_alloc) {
free(pround_sec);
pround_sec = NULL;
pround_sec_alloc = 0;
}
ast_unregister_application(app);
return 0;
}
int load_module(void)
{
int res;
struct ast_config *cfg;
struct ast_variable *var;
char *tmp;
//+++++ Post 1.2 +++++++++++
cfg = ast_config_load(config);
//++++++ Prior 1.2 +++++++++++
//cfg = ast_load(config);
//+++++++++++++++++++++++++
if (!cfg) {
if (SHOW_WARNING) ast_log(LOG_WARNING, "Unable to load config for mcc2 Application: %s\n", config);
return 0;
}
var = ast_variable_browse(cfg, "global");
if (!var) {
/* nothing configured */
return 0;
}
tmp = ast_variable_retrieve(cfg,"global","hostname");
if (tmp) {
pghostname = malloc(strlen(tmp) + 1);
if (pghostname != NULL) {
hostname_alloc = 1;
strcpy(pghostname, tmp);
} else {
if (SHOW_ERROR) ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
} else {
if (SHOW_WARNING) ast_log(LOG_WARNING,"mcc2 database server hostname not specified. Assuming localhost\n");
// pghostname = "localhost";
pghostname = malloc(strlen("localhost") + 1);
if (pghostname != NULL) {
hostname_alloc = 1;
strcpy(pghostname, "localhost");
} else {
if (SHOW_ERROR) ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
}
tmp = ast_variable_retrieve(cfg,"global","dbname");
if (tmp) {
pgdbname = malloc(strlen(tmp) + 1);
if (pgdbname != NULL) {
dbname_alloc = 1;
strcpy(pgdbname, tmp);
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
} else {
if (SHOW_WARNING) ast_log(LOG_WARNING,"mcc2 database not specified. Assuming asterisk\n");
// pgdbname = "asterisk";
pgdbname = malloc(strlen("asterisk") + 1);
if (pgdbname != NULL) {
dbname_alloc = 1;
strcpy(pgdbname, "asterisk");
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
}
tmp = ast_variable_retrieve(cfg,"global","user");
if (tmp) {
pgdbuser = malloc(strlen(tmp) + 1);
if (pgdbuser != NULL) {
dbuser_alloc = 1;
strcpy(pgdbuser, tmp);
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
} else {
if (SHOW_WARNING) ast_log(LOG_WARNING,"mcc2 database user not specified. Assuming postgres\n");
// pgdbuser = "postgres";
pgdbuser = malloc(strlen("postgres") + 1);
if (pgdbuser != NULL) {
dbuser_alloc = 1;
strcpy(pgdbuser, "postgres");
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
}
tmp = ast_variable_retrieve(cfg,"global","password");
if (tmp) {
pgpassword = malloc(strlen(tmp) + 1);
if (pgpassword != NULL) {
password_alloc = 1;
//strncpy(pgpassword, tmp, strlen(tmp));
strcpy(pgpassword, tmp);
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
} else {
if (SHOW_WARNING) ast_log(LOG_WARNING,"mcc2 database password not specified. Assuming blank\n");
// pgpassword = "";
pgpassword = malloc(strlen("") + 1);
if (pgpassword != NULL) {
password_alloc = 1;
//strncpy(pgpassword, tmp, strlen(tmp));
strcpy(pgpassword, "");
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
}
tmp = ast_variable_retrieve(cfg,"global","port");
if (tmp) {
pgdbport = malloc(strlen(tmp) + 1);
if (pgdbport != NULL) {
dbport_alloc = 1;
//strncpy(pgdbport, tmp, strlen(tmp));
strcpy(pgdbport,tmp);
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
} else {
if (SHOW_WARNING) ast_log(LOG_WARNING,"mcc2 database port not specified. Using 5432.\n");
// pgdbport = "5432";
pgdbport = malloc(strlen("5432") + 1);
if (pgdbport != NULL) {
dbport_alloc = 1;
//strncpy(pgdbport, tmp, strlen(tmp));
strcpy(pgdbport,"5432");
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
}
tmp = ast_variable_retrieve(cfg,"global","min_frozen");
if (tmp) {
min_frozen = malloc(strlen(tmp) + 1);
if (min_frozen != NULL) {
min_frozen_alloc = 1;
//strncpy(pgdbport, tmp, strlen(tmp));
strcpy(min_frozen,tmp);
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
} else {
if (SHOW_WARNING) ast_log(LOG_WARNING,"mcc2 minimum frozen credit not specified. Using 1000.\n");
min_frozen = malloc(strlen("1000") + 1);
if (min_frozen != NULL) {
min_frozen_alloc = 1;
//strncpy(pgdbport, tmp, strlen(tmp));
strcpy(min_frozen,"1000");
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
}
tmp = ast_variable_retrieve(cfg,"global","frozen_time");
if (tmp) {
frozen_time = malloc(strlen(tmp) + 1);
if (frozen_time != NULL) {
frozen_time_alloc = 1;
//strncpy(pgdbport, tmp, strlen(tmp));
strcpy(frozen_time,tmp);
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
} else {
if (SHOW_WARNING) ast_log(LOG_WARNING,"mcc2 frozen time not specified. Using 20 minutes.\n");
frozen_time = malloc(strlen("20") + 1);
if (frozen_time != NULL) {
frozen_time_alloc = 1;
//strncpy(pgdbport, tmp, strlen(tmp));
strcpy(frozen_time,"20");
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
}
tmp = ast_variable_retrieve(cfg,"global","round_sec");
if (tmp) {
pround_sec = malloc(strlen(tmp) + 1);
if (pround_sec != NULL) {
pround_sec_alloc = 1;
//strncpy(pgdbport, tmp, strlen(tmp));
strcpy(pround_sec,tmp);
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
} else {
if (SHOW_WARNING) ast_log(LOG_WARNING,"mcc2 round seconds not specified. Using 6 seconds.\n");
pround_sec = malloc(strlen("6") + 1);
if (pround_sec != NULL) {
pround_sec_alloc = 1;
//strncpy(pgdbport, tmp, strlen(tmp));
strcpy(pround_sec,"6");
} else {
ast_log(LOG_ERROR,"Out of memory error.\n");
return -1;
}
}
//-------------- activating prompts from config file -------------------
tmp = ast_variable_retrieve(cfg,"global","show_notice");
if (tmp) {
SHOW_NOTICE=atoi(tmp);
} else {
ast_log(LOG_WARNING,"mcc2 SHOW_NOTICE not specified in config file. Notices will be hidden.\n");
SHOW_NOTICE=0;
}
tmp = ast_variable_retrieve(cfg,"global","show_warning");
if (tmp) {
SHOW_WARNING=atoi(tmp);
} else {
ast_log(LOG_WARNING,"mcc2 SHOW_WARNING not specified in config file. Warnings will be shown.\n");
SHOW_WARNING=1;
}
tmp = ast_variable_retrieve(cfg,"global","show_error");
if (tmp) {
SHOW_ERROR=atoi(tmp);
} else {
ast_log(LOG_WARNING,"mcc2 SHOW_ERROR not specified in config file. Errors will be shown.\n");
SHOW_ERROR=1;
}
tmp = ast_variable_retrieve(cfg,"global","round_time");
if (tmp) {
ROUND_TIME=atoi(tmp);
} else {
ast_log(LOG_WARNING,"mcc2 ROUND_TIME not specified in config file. Default = 0.\n");
ROUND_TIME=0;
}
tmp = ast_variable_retrieve(cfg,"global","round_to");
if (tmp) {
ROUND_TO=atoi(tmp);
} else {
ast_log(LOG_WARNING,"mcc2 ROUND_TO not specified in config file. Default = 0.\n");
ROUND_TO=0;
}
//-------------------------------------------------------------------------
//+++++ Post 1.2 +++++++++++
ast_config_destroy(cfg);
//++++++ Prior 1.2 +++++++++++
//ast_destroy(cfg);
//+++++++++++++++++++++++++
ast_log(LOG_DEBUG,"app_mcc2: got hostname of %s\n",pghostname);
ast_log(LOG_DEBUG,"app_mcc2: got port of %s\n",pgdbport);
if (pgdbsock)
ast_log(LOG_DEBUG,"app_mcc2: got sock file of %s\n",pgdbsock);
ast_log(LOG_DEBUG,"app_mcc2: got user of %s\n",pgdbuser);
ast_log(LOG_DEBUG,"app_mcc2: got dbname of %s\n",pgdbname);
ast_log(LOG_DEBUG,"app_mcc2: got password of %s\n",pgpassword);
ast_log(LOG_DEBUG,"app_mcc2: got min_frozen of %s\n",min_frozen);
conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
if (PQstatus(conn) != CONNECTION_BAD) {
ast_log(LOG_DEBUG,"app_mcc2: Successfully connected to mcc2 database.\n");
connected = 1;
} else {
ast_log(LOG_ERROR, "app_mcc2: cannot connect to database server %s. Calls will not be logged\n", pghostname);
connected = 0;
}
ast_mutex_init(&postgreslock);
res = ast_register_application(app, mcc2_exec, synopsis, descrip);
if (res) {
ast_log(LOG_ERROR, "Unable to register mcc2 Application\n");
}
return res;
}
char *description(void)
{
return tdesc;
}
int usecount(void)
{
int res;
STANDARD_USECOUNT(res);
return res;
}
char *key()
{
return ASTERISK_GPL_KEY;
}