/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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)&&(billsecmin_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; }