/* At this time, billing never succeeds if you don't have a database. */ static switch_bool_t bill_event(double billamount, const char *billaccount, switch_channel_t *channel) { char *sql = NULL, *dsql = NULL; switch_status_t status = SWITCH_FALSE; if (globals.custom_sql_save) { if (switch_string_var_check_const(globals.custom_sql_save) || switch_string_has_escaped_data(globals.custom_sql_save)) { switch_channel_set_variable_printf(channel, "nibble_bill", "%f", billamount, SWITCH_FALSE); sql = switch_channel_expand_variables(channel, globals.custom_sql_save); if (sql != globals.custom_sql_save) dsql = sql; } else { sql = globals.custom_sql_save; } } else { sql = dsql = switch_mprintf("UPDATE %s SET %s=%s-%f WHERE %s='%s'", globals.db_table, globals.db_column_cash, globals.db_column_cash, billamount, globals.db_column_account, billaccount); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Doing update query\n[%s]\n", sql); status = nibblebill_execute_sql_callback(sql, nibblebill_callback, NULL); switch_safe_free(dsql); return status; }
static void tech_attach(private_t *tech_pvt, modem_t *modem) { tech_pvt->modem = modem; switch_set_string(modem->uuid_str, switch_core_session_get_uuid(tech_pvt->session)); switch_channel_set_variable_printf(tech_pvt->channel, "modem_slot", "%d", modem->slot); switch_channel_set_variable(tech_pvt->channel, "modem_devlink", modem->devlink); switch_channel_set_variable(tech_pvt->channel, "modem_digits", modem->digits); switch_channel_export_variable(tech_pvt->channel, "rtp_autoflush_during_bridge", "false", SWITCH_EXPORT_VARS_VARIABLE); }
/* At this time, billing never succeeds if you don't have a database. */ static switch_status_t bill_event(double billamount, const char *billaccount, switch_channel_t *channel) { char *sql = NULL, *dsql = NULL; switch_odbc_statement_handle_t stmt = NULL; switch_status_t status = SWITCH_STATUS_FALSE; if (!switch_odbc_available()) { return status; } if (globals.custom_sql_save) { if (switch_string_var_check_const(globals.custom_sql_save) || switch_string_has_escaped_data(globals.custom_sql_save)) { switch_channel_set_variable_printf(channel, "nibble_bill", "%f", billamount, SWITCH_FALSE); sql = switch_channel_expand_variables(channel, globals.custom_sql_save); if (sql != globals.custom_sql_save) dsql = sql; } else { sql = globals.custom_sql_save; } } else { sql = dsql = switch_mprintf("UPDATE %s SET %s=%s-%f WHERE %s='%s'", globals.db_table, globals.db_column_cash, globals.db_column_cash, billamount, globals.db_column_account, billaccount); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Doing update query\n[%s]\n", sql); if (switch_odbc_handle_exec(globals.master_odbc, sql, &stmt, NULL) != SWITCH_ODBC_SUCCESS) { char *err_str; err_str = switch_odbc_handle_get_error(globals.master_odbc, stmt); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERR: [%s]\n[%s]\n", sql, switch_str_nil(err_str)); switch_safe_free(err_str); } else { status = SWITCH_STATUS_SUCCESS; } if (stmt) { switch_odbc_statement_handle_free(&stmt); } switch_safe_free(dsql); return status; }
static switch_status_t process_hangup(switch_core_session_t *session) { const char* billaccount; switch_channel_t *channel = NULL; channel = switch_core_session_get_channel(session); /* Resume any paused billings, just in case */ /* nibblebill_resume(session); */ /* Now go handle like normal billing */ do_billing(session); billaccount = switch_channel_get_variable(channel, "nibble_account"); if (billaccount) { switch_channel_set_variable_printf(channel, "nibble_current_balance", "%f", get_balance(billaccount, channel)); } return SWITCH_STATUS_SUCCESS; }
SWITCH_DECLARE(switch_status_t) switch_limit_incr(const char *backend, switch_core_session_t *session, const char *realm, const char *resource, const int max, const int interval) { switch_limit_interface_t *limit = NULL; switch_channel_t *channel = NULL; int status = SWITCH_STATUS_SUCCESS; if (session) { channel = switch_core_session_get_channel(session); } /* locate impl, call appropriate func */ if (!(limit = get_backend(backend))) { switch_limit_log(session, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend); switch_goto_status(SWITCH_STATUS_GENERR, end); } switch_limit_log(session, SWITCH_LOG_INFO, "incr called: %s_%s max:%d, interval:%d\n", realm, resource, max, interval); if ((status = limit->incr(session, realm, resource, max, interval)) == SWITCH_STATUS_SUCCESS) { if (session) { /* race condition? what if another leg is doing the same thing? */ const char *existing = switch_channel_get_variable(channel, LIMIT_BACKEND_VARIABLE); if (existing) { if (!strstr(existing, backend)) { switch_channel_set_variable_printf(channel, LIMIT_BACKEND_VARIABLE, "%s,%s", existing, backend); } } else { switch_channel_set_variable(channel, LIMIT_BACKEND_VARIABLE, backend); switch_core_event_hook_add_state_change(session, limit_state_handler); } } } release_backend(limit); end: return status; }
/* 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) { /* FS vars we will use */ switch_channel_t *channel; switch_caller_profile_t *profile; /* Local vars */ nibble_data_t *nibble_data; switch_time_t ts = switch_micro_time_now(); double billamount; char date[80] = ""; char *uuid; switch_size_t retsize; switch_time_exp_t tm; const char *billrate; const char *billincrement; const char *billaccount; double nobal_amt = globals.nobal_amt; double lowbal_amt = globals.lowbal_amt; double balance; if (!session) { /* Why are we here? */ return SWITCH_STATUS_SUCCESS; } uuid = switch_core_session_get_uuid(session); /* Get channel var */ if (!(channel = switch_core_session_get_channel(session))) { return SWITCH_STATUS_SUCCESS; } /* Variables kept in FS but relevant only to this module */ billrate = switch_channel_get_variable(channel, "nibble_rate"); billincrement = switch_channel_get_variable(channel, "nibble_increment"); billaccount = switch_channel_get_variable(channel, "nibble_account"); if (!zstr(switch_channel_get_variable(channel, "nobal_amt"))) { nobal_amt = atof(switch_channel_get_variable(channel, "nobal_amt")); } if (!zstr(switch_channel_get_variable(channel, "lowbal_amt"))) { lowbal_amt = atof(switch_channel_get_variable(channel, "lowbal_amt")); } /* Return if there's no billing information on this session */ if (!billrate || !billaccount) { return SWITCH_STATUS_SUCCESS; } switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Attempting to bill at $%s per minute to account %s\n", billrate, billaccount); /* Get caller profile info from channel */ profile = switch_channel_get_caller_profile(channel); if (!profile || !profile->times) { /* No caller profile (why would this happen?) */ return SWITCH_STATUS_SUCCESS; } if (profile->times->answered < 1) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Not billing %s - call is not in answered state\n", billaccount); /* See if this person has enough money left to continue the call */ balance = get_balance(billaccount, channel); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Comparing %f to hangup balance of %f\n", balance, nobal_amt); if (balance <= nobal_amt) { /* Not enough money - reroute call to nobal location */ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Balance of %f fell below allowed amount of %f! (Account %s)\n", balance, nobal_amt, billaccount); transfer_call(session, globals.nobal_action); } return SWITCH_STATUS_SUCCESS; } /* Lock this session's data for this module while we tinker with it */ if (globals.mutex) { switch_mutex_lock(globals.mutex); } /* Get our nibble data var. This will be NULL if it's our first call here for this session */ nibble_data = (nibble_data_t *) switch_channel_get_private(channel, "_nibble_data_"); /* Are we in paused mode? If so, we don't do anything here - go back! */ if (nibble_data && (nibble_data->pausets > 0)) { if (globals.mutex) { switch_mutex_unlock(globals.mutex); } switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Received heartbeat, but we're paused - ignoring\n"); return SWITCH_STATUS_SUCCESS; } /* Have we done any billing on this channel yet? If no, set up vars for doing so */ if (!nibble_data) { nibble_data = switch_core_session_alloc(session, sizeof(*nibble_data)); memset(nibble_data, 0, sizeof(*nibble_data)); /* Setup new billing data (based on call answer time, in case this module started late with active calls) */ nibble_data->lastts = profile->times->answered; /* Set the initial answer time to match when the call was really answered */ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Beginning new billing on %s\n", uuid); } switch_time_exp_lt(&tm, nibble_data->lastts); switch_strftime_nocheck(date, &retsize, sizeof(date), "%Y-%m-%d %T", &tm); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%d seconds passed since last bill time of %s\n", (int) ((ts - nibble_data->lastts) / 1000000), date); if ((ts - nibble_data->lastts) >= 0) { /* If billincrement is set we bill by it and not by time elapsed */ if (!(switch_strlen_zero(billincrement))) { switch_time_t chargedunits = (ts - nibble_data->lastts) / 1000000 <= atol(billincrement) ? atol(billincrement) * 1000000 : (switch_time_t)(ceil((ts - nibble_data->lastts) / (atol(billincrement) * 1000000.0))) * atol(billincrement) * 1000000; billamount = (atof(billrate) / 1000000 / 60) * chargedunits - nibble_data->bill_adjustments; /* Account for the prepaid amount */ nibble_data->lastts += chargedunits; } else { /* Convert billrate into microseconds and multiply by # of microseconds that have passed since last *successful* bill */ billamount = (atof(billrate) / 1000000 / 60) * ((ts - nibble_data->lastts)) - nibble_data->bill_adjustments; /* Update the last time we billed */ nibble_data->lastts = ts; } switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Billing $%f to %s (Call: %s / %f so far)\n", billamount, billaccount, uuid, nibble_data->total); /* DO ODBC BILLING HERE and reset counters if it's successful! */ if (bill_event(billamount, billaccount, channel) == SWITCH_STATUS_SUCCESS) { /* Increment total cost */ nibble_data->total += billamount; /* Reset manual billing adjustments from pausing */ nibble_data->bill_adjustments = 0; /* Update channel variable with current billing */ switch_channel_set_variable_printf(channel, "nibble_total_billed", "%f", nibble_data->total); } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Failed to log to database!\n"); } } else { if (switch_strlen_zero(billincrement)) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Just tried to bill %s negative minutes! That should be impossible.\n", uuid); } /* Save this location */ if (channel) { switch_channel_set_private(channel, "_nibble_data_", nibble_data); /* don't verify balance and transfer to nobal if we're done with call */ if (switch_channel_get_state(channel) != CS_REPORTING && switch_channel_get_state(channel) != CS_HANGUP) { balance = get_balance(billaccount, channel); /* See if we've achieved low balance */ if (!nibble_data->lowbal_action_executed && balance <= lowbal_amt) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Balance of %f fell below low balance amount of %f! (Account %s)\n", balance, lowbal_amt, billaccount); if (exec_app(session, globals.lowbal_action) != SWITCH_STATUS_SUCCESS) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Low balance action didn't execute\n"); else nibble_data->lowbal_action_executed = 1; } /* See if this person has enough money left to continue the call */ if (balance <= nobal_amt) { /* Not enough money - reroute call to nobal location */ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Balance of %f fell below allowed amount of %f! (Account %s)\n", balance, 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 */ if (globals.mutex) { switch_mutex_unlock(globals.mutex); } /* Go check if this call is allowed to continue */ return SWITCH_STATUS_SUCCESS; }
/** * Start recording call * @param session the session to record * @param record the record component */ static int start_call_record(switch_core_session_t *session, struct rayo_component *component) { struct record_component *record_component = RECORD_COMPONENT(component); switch_channel_t *channel = switch_core_session_get_channel(session); int max_duration_sec = 0; switch_channel_set_variable(channel, "RECORD_HANGUP_ON_ERROR", "false"); switch_channel_set_variable(channel, "RECORD_TOGGLE_ON_REPEAT", ""); switch_channel_set_variable(channel, "RECORD_CHECK_BRIDGE", ""); switch_channel_set_variable(channel, "RECORD_MIN_SEC", "0"); switch_channel_set_variable(channel, "RECORD_STEREO", ""); switch_channel_set_variable(channel, "RECORD_READ_ONLY", ""); switch_channel_set_variable(channel, "RECORD_WRITE_ONLY", ""); switch_channel_set_variable(channel, "RECORD_APPEND", ""); switch_channel_set_variable(channel, "RECORD_WRITE_OVER", "true"); switch_channel_set_variable(channel, "RECORD_ANSWER_REQ", ""); switch_channel_set_variable(channel, "RECORD_SILENCE_THRESHOLD", "200"); if (record_component->initial_timeout > 0) { switch_channel_set_variable_printf(channel, "RECORD_INITIAL_TIMEOUT_MS", "%i", record_component->initial_timeout); } else { switch_channel_set_variable(channel, "RECORD_INITIAL_TIMEOUT_MS", ""); } if (record_component->final_timeout > 0) { switch_channel_set_variable_printf(channel, "RECORD_FINAL_TIMEOUT_MS", "%i", record_component->final_timeout); } else { switch_channel_set_variable(channel, "RECORD_FINAL_TIMEOUT_MS", ""); } /* allow dialplan override for these variables */ //switch_channel_set_variable(channel, "RECORD_PRE_BUFFER_FRAMES", ""); //switch_channel_set_variable(channel, "record_sample_rate", ""); //switch_channel_set_variable(channel, "enable_file_write_buffering", ""); /* max duration attribute is in milliseconds- convert to seconds */ if (record_component->max_duration > 0) { max_duration_sec = ceil((double)(record_component->max_duration - record_component->duration_ms) / 1000.0); } if (!strcmp(record_component->direction, "duplex")) { if (!record_component->mix) { /* STEREO */ switch_channel_set_variable(channel, "RECORD_STEREO", "true"); } /* else MONO (default) */ } else if (!strcmp(record_component->direction, "send")) { /* record audio sent from the caller */ switch_channel_set_variable(channel, "RECORD_READ_ONLY", "true"); } else if (!strcmp(record_component->direction, "recv")) { /* record audio received by the caller */ switch_channel_set_variable(channel, "RECORD_WRITE_ONLY", "true"); }; if (record_component->start_beep) { switch_ivr_displace_session(session, RECORD_BEEP, 0, ""); record_component->start_time = switch_micro_time_now(); } if (switch_ivr_record_session(session, (char *)RAYO_ID(component), max_duration_sec, NULL) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Recording started: file = %s\n", RAYO_ID(component)); return 1; } return 0; }
SWITCH_DECLARE(void) switch_core_session_hangup_state(switch_core_session_t *session, switch_bool_t force) { switch_call_cause_t cause = switch_channel_get_cause(session->channel); switch_call_cause_t cause_q850 = switch_channel_get_cause_q850(session->channel); int proceed = 1; int global_proceed = 1; int do_extra_handlers = 1; int silly = 0; int index = 0; switch_channel_state_t state = switch_channel_get_state(session->channel), midstate = state; const switch_endpoint_interface_t *endpoint_interface; const switch_state_handler_table_t *driver_state_handler = NULL; const switch_state_handler_table_t *application_state_handler = NULL; const char *hook_var; int use_session = 0; if (!force) { if (!switch_channel_test_flag(session->channel, CF_EARLY_HANGUP) && !switch_test_flag((&runtime), SCF_EARLY_HANGUP)) { return; } if (switch_thread_self() != session->thread_id) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "%s thread mismatch skipping state handler.\n", switch_channel_get_name(session->channel)); return; } } if (switch_test_flag(session, SSF_HANGUP)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "%s handler already called, skipping state handler.\n", switch_channel_get_name(session->channel)); return; } endpoint_interface = session->endpoint_interface; switch_assert(endpoint_interface != NULL); driver_state_handler = endpoint_interface->state_handler; switch_assert(driver_state_handler != NULL); switch_channel_set_hangup_time(session->channel); switch_core_media_bug_remove_all(session); switch_channel_stop_broadcast(session->channel); switch_channel_set_variable(session->channel, "hangup_cause", switch_channel_cause2str(cause)); switch_channel_set_variable_printf(session->channel, "hangup_cause_q850", "%d", cause_q850); //switch_channel_presence(session->channel, "unknown", switch_channel_cause2str(cause), NULL); switch_channel_set_timestamps(session->channel); STATE_MACRO(hangup, "HANGUP"); switch_core_media_set_stats(session); if ((hook_var = switch_channel_get_variable(session->channel, SWITCH_API_HANGUP_HOOK_VARIABLE))) { if (switch_true(switch_channel_get_variable(session->channel, SWITCH_SESSION_IN_HANGUP_HOOK_VARIABLE))) { use_session = 1; } api_hook(session, hook_var, use_session); } switch_channel_set_callstate(session->channel, CCS_HANGUP); switch_set_flag(session, SSF_HANGUP); }