Author: dschreiber Date: Tue May 26 00:50:41 2009 New Revision: 13432 Log: Added feature mod_nibblebill to check balance and transfer the caller to an alternate dialplan context & extension if they deplete their funds. Modified: freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill/mod_nibblebill.c Modified: freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill/mod_nibblebill.c ============================================================================== --- freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill/mod_nibblebill.c (original) +++ freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill/mod_nibblebill.c Tue May 26 00:50:41 2009 @@ -56,8 +56,8 @@ /* Defaults */ #ifdef SWITCH_HAVE_ODBC -/*static char SQL_LOOKUP[] = "SELECT %s FROM %s WHERE %s=\"%s\"";*/ -static char SQL_SAVE[] = "UPDATE %s SET %s=%s-%f WHERE %s=%s"; +static char SQL_LOOKUP[] = "SELECT %s FROM %s WHERE %s='%s'"; +static char SQL_SAVE[] = "UPDATE %s SET %s=%s-%f WHERE %s='%s'"; #endif typedef struct @@ -73,7 +73,7 @@ typedef struct nibblebill_results { - float funds; + float balance; float percall_max; /* Overrides global on a per-user level */ float lowbal_amt; /* ditto */ @@ -117,6 +117,8 @@ #endif } globals; +static void nibblebill_pause(switch_core_session_t *session); + /************************** * Setup FreeSWITCH Macros * **************************/ @@ -141,14 +143,14 @@ SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_nobal_action, globals.nobal_action); #ifdef SWITCH_HAVE_ODBC -/*static int nibblebill_callback(void *pArg, int argc, char **argv, char **columnNames) +static int nibblebill_callback(void *pArg, int argc, char **argv, char **columnNames) { nibblebill_results_t *cbt = (nibblebill_results_t *) pArg; - cbt->funds = atof(argv[0]); + cbt->balance = atof(argv[0]); return 0; -}*/ +} #endif static switch_status_t load_config(void) @@ -271,6 +273,37 @@ } +static void transfer_call(switch_core_session_t *session, char *destination) +{ + char *argv[4] = { 0 }; + + switch_separate_string(destination, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + const char *uuid; + switch_channel_t *channel = switch_core_session_get_channel(session); + /* Find the uuid of our B leg. If it exists, transfer it first */ + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE))) { + /* Make sure we are in the media path on B leg */ + switch_ivr_media(uuid, SMF_REBRIDGE); + + /* Get info on the B leg */ + switch_core_session_t *b_session; + if ((b_session = switch_core_session_locate(uuid))) { + /* Transfer the B leg */ + switch_ivr_session_transfer(b_session, argv[0], argv[1], argv[2]); + switch_core_session_rwunlock(b_session); + } + } + + /* Make sure we are in the media path on A leg */ + uuid = switch_core_session_get_uuid(session); + switch_ivr_media(uuid, SMF_REBRIDGE); + + /* Transfer the A leg */ + switch_ivr_session_transfer(session, argv[0], argv[1], argv[2]); +} + + /* At this time, billing never succeeds if you don't have a database. */ static switch_status_t bill_event(float billamount, const char *billaccount) { @@ -300,6 +333,36 @@ } +static float get_balance(const char *billaccount) +{ +#ifdef SWITCH_HAVE_ODBC + char sql[1024] = ""; + nibblebill_results_t pdata; + float balance = 0.00; + + memset(&pdata, 0, sizeof(pdata)); + snprintf(sql, 1024, SQL_LOOKUP, globals.db_column_cash, globals.db_table, globals.db_column_account, billaccount); + + if (switch_odbc_handle_callback_exec(globals.master_odbc, sql, nibblebill_callback, &pdata) != SWITCH_ODBC_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error running this query: [%s]\n", sql); +#endif + /* TODO: Return -1 for safety */ + + return -1.00; +#ifdef SWITCH_HAVE_ODBC + } else { + /* Successfully retrieved! */ + balance = pdata.balance; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Retrieved current balance for account %s (balance = %f)\n", billaccount, balance); + } + +#endif + return balance; +} + + + /* This is where we actually charge the guy This can be called anytime a call is in progress or at the end of a call before the session is destroyed */ static switch_status_t do_billing(switch_core_session_t *session) @@ -426,6 +489,20 @@ /* Save this location, but only if the channel/session are not hungup (otherwise, we're done) */ if (channel && switch_channel_get_state(channel) != CS_HANGUP) { switch_channel_set_private(channel, "_nibble_data_", nibble_data); + + /* See if this person has enough money left to continue the call */ + float balance; + balance = get_balance(billaccount); + if (balance < globals.nobal_amt) { + /* Not enough money - reroute call to nobal location */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Balance of %f fell below allowed amount of %f! (Account %s)\n", balance, globals.nobal_amt, billaccount); + + /* IMPORTANT: Billing must be paused before the transfer occurs! This prevents infinite loops, since the transfer will result */ + /* in nibblebill checking the call again in the routing process for an allowed balance! */ + /* If you intend to give the user the option to re-up their balance, you must clear & resume billing once the balance is updated! */ + nibblebill_pause(session); + transfer_call(session, globals.nobal_action); + } } /* Done changing - release lock */ @@ -648,6 +725,7 @@ } + #define APP_SYNTAX "pause | resume | reset | adjust <amount> | heartbeat <seconds> | check" SWITCH_STANDARD_APP(nibblebill_app_function) {