static void nibblebill_pause(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_time_t ts = switch_micro_time_now(); nibble_data_t *nibble_data; if (!channel) { return; } /* 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_"); if (!nibble_data) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Can't pause - channel is not initialized for billing!\n"); return; } /* Set pause counter if not already set */ if (nibble_data->pausets == 0) nibble_data->pausets = ts; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Paused billing timestamp!\n"); /* Done checking - release lock */ if (globals.mutex) { switch_mutex_unlock(globals.mutex); } }
/** * Start call progress detection * * @param session the session to detect * @param name of the descriptor to use * @return SWITCH_STATUS_SUCCESS if successful */ switch_status_t callprogress_detector_start(switch_core_session_t *session, const char *name) { switch_channel_t *channel = switch_core_session_get_channel(session); tone_detector_t *detector = NULL; tone_descriptor_t *descriptor = NULL; switch_media_bug_t *bug = NULL; /* are we already running? */ bug = switch_channel_get_private(channel, TONE_PRIVATE); if (bug) { return SWITCH_STATUS_FALSE; } /* find the tone descriptor with the matching name and create the detector */ descriptor = switch_core_hash_find(spandsp_globals.tones, name); if (!descriptor) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "no tone descriptor defined with name '%s'. Update configuration. \n", name); return SWITCH_STATUS_FALSE; } tone_detector_create(session, &detector, descriptor); /* start listening for tones */ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Starting tone detection for '%s'\n", name); switch_core_media_bug_add(session, "spandsp_tone_detect", NULL, callprogress_detector_process_buffer, detector, 0 /* stop time */, SMBF_READ_REPLACE, &bug); if (!bug) { return SWITCH_STATUS_FALSE; } switch_channel_set_private(channel, TONE_PRIVATE, bug); return SWITCH_STATUS_SUCCESS; }
static switch_status_t spy_on_hangup(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); const char *data = switch_channel_get_private(channel, "_userspy_"); const char *uuid = switch_core_session_get_uuid(session); spy_t *spy = NULL, *p = NULL, *prev = NULL; switch_thread_rwlock_wrlock(globals.spy_hash_lock); spy = switch_core_hash_find(globals.spy_hash, data); for (p = spy; p; p = p->next) { if (p->uuid == uuid) { if (prev) { prev->next = p->next; } else { spy = p->next; } globals.spy_count--; break; } prev = p; } switch_core_hash_insert(globals.spy_hash, data, spy); switch_thread_rwlock_unlock(globals.spy_hash_lock); return SWITCH_STATUS_SUCCESS; }
static void nibblebill_reset(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_time_t ts = switch_micro_time_now(); nibble_data_t *nibble_data; if (!channel) { return; } /* 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_"); if (!nibble_data) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Can't reset - channel is not initialized for billing!\n"); return; } /* Lock this session's data for this module while we tinker with it */ if (globals.mutex) { switch_mutex_lock(globals.mutex); } /* Update the last time we billed */ nibble_data->lastts = ts; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Reset last billing timestamp marker to right now!\n"); /* Done checking - release lock */ if (globals.mutex) { switch_mutex_unlock(globals.mutex); } }
switch_bool_t sonar_ping_callback(switch_core_session_t *session, const char *app, const char *app_data){ switch_channel_t *channel = switch_core_session_get_channel(session); sonar_ping_helper_t *ph = switch_channel_get_private(channel, "__sonar_ping__"); int diff; if (!ph) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not locate private sonar helper data\n"); return SWITCH_TRUE; } if ( ph->end ) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sonar not yet reset. Likely a repeat detection.\n"); return SWITCH_TRUE; } ph->end = switch_time_now(); diff = ph->end - ph->start; ph->start = 0; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Sonar ping took %ld milliseconds\n", (long)diff / 1000); diff /= 1000; ph->sum += diff; ph->max = MAX(ph->max, diff); ph->min = MIN(ph->min, diff); ph->samples[ph->received++] = diff; return SWITCH_TRUE; }
static double nibblebill_check(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); nibble_data_t *nibble_data; double amount = 0; if (!channel) { return -99999; } /* 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_"); if (!nibble_data) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Can't check - channel is not initialized for billing!\n"); return -99999; } /* Lock this session's data for this module while we tinker with it */ if (globals.mutex) { switch_mutex_lock(globals.mutex); } amount = nibble_data->total; /* Done checking - release lock */ if (globals.mutex) { switch_mutex_unlock(globals.mutex); } return amount; }
static switch_status_t do_snap(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_media_bug_t *bug = switch_channel_get_private(channel, "snapshot"); char *file; switch_file_handle_t fh = { 0 }; switch_codec_implementation_t read_impl = { 0 }; switch_size_t bytes_read; int16_t pdata[4096] = { 0 }; if (bug) { switch_time_exp_t tm; switch_size_t retsize; char date[80] = ""; struct cap_cb *cb = (struct cap_cb *) switch_core_media_bug_get_user_data(bug); if (!cb) { return SWITCH_STATUS_FALSE; } switch_time_exp_lt(&tm, switch_time_make(switch_epoch_time_now(NULL), 0)); switch_strftime(date, &retsize, sizeof(date), "%Y_%m_%d_%H_%M_%S", &tm); file = switch_core_session_sprintf(session, "%s%s%s_%s.wav", SWITCH_GLOBAL_dirs.sounds_dir, SWITCH_PATH_SEPARATOR, cb->base, date); switch_core_session_get_read_impl(session, &read_impl); fh.channels = 0; fh.native_rate = read_impl.actual_samples_per_second; if (switch_core_file_open(&fh, file, 0, read_impl.actual_samples_per_second, SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error opening %s\n", file); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); return SWITCH_STATUS_FALSE; } switch_mutex_lock(cb->mutex); while ((bytes_read = switch_buffer_read(cb->buffer, pdata, sizeof(pdata)))) { switch_size_t samples = bytes_read / 2; if (switch_core_file_write(&fh, pdata, &samples) != SWITCH_STATUS_SUCCESS) { break; } } switch_mutex_unlock(cb->mutex); switch_core_file_close(&fh); switch_core_set_variable("file", file); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Wrote %s\n", file); return SWITCH_STATUS_SUCCESS; } switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s Bug is not attached.\n", switch_channel_get_name(channel)); return SWITCH_STATUS_FALSE; }
/** * Stop call progress detection * @param session the session to stop * @return SWITCH_STATUS_SUCCESS if successful */ switch_status_t callprogress_detector_stop(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_media_bug_t *bug = switch_channel_get_private(channel, TONE_PRIVATE); if (bug) { switch_core_media_bug_remove(session, &bug); switch_channel_set_private(channel, TONE_PRIVATE, NULL); } return SWITCH_STATUS_SUCCESS; }
switch_status_t stop_fsk_detect_session(switch_core_session_t *session) { switch_media_bug_t *bug; switch_channel_t *channel = switch_core_session_get_channel(session); if ((bug = switch_channel_get_private(channel, "fsk"))) { switch_channel_set_private(channel, "fsk", NULL); switch_core_media_bug_remove(session, &bug); return SWITCH_STATUS_SUCCESS; } return SWITCH_STATUS_FALSE; }
switch_status_t spandsp_stop_tdd_encode_session(switch_core_session_t *session) { switch_media_bug_t *bug; switch_channel_t *channel = switch_core_session_get_channel(session); if ((bug = switch_channel_get_private(channel, "tdd_encode"))) { switch_channel_set_private(channel, "tdd_encode", NULL); switch_core_media_bug_remove(session, &bug); return SWITCH_STATUS_SUCCESS; } return SWITCH_STATUS_FALSE; }
SWITCH_DECLARE_NONSTD(switch_status_t) hanguphook(switch_core_session_t *session_hungup) { switch_channel_t *channel = switch_core_session_get_channel(session_hungup); CoreSession *coresession = NULL; switch_channel_state_t state = switch_channel_get_state(channel); if ((coresession = (CoreSession *) switch_channel_get_private(channel, "CoreSession"))) { if (coresession->hook_state != state) { coresession->hook_state = state; coresession->check_hangup_hook(); } } return SWITCH_STATUS_SUCCESS; }
SWITCH_DECLARE_NONSTD(switch_status_t) dtmf_callback(switch_core_session_t *session_cb, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) { switch_channel_t *channel = switch_core_session_get_channel(session_cb); CoreSession *coresession = NULL; coresession = (CoreSession *) switch_channel_get_private(channel, "CoreSession"); if (!coresession) { return SWITCH_STATUS_FALSE; } return coresession->run_dtmf_callback(input, itype); }
static void nibblebill_resume(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_time_t ts = switch_micro_time_now(); nibble_data_t *nibble_data; const char *billrate; if (!channel) { return; } /* 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_"); if (!nibble_data) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Can't resume - channel is not initialized for billing (This is expected at hangup time)!\n"); return; } if (nibble_data->pausets == 0) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Can't resume - channel is not paused! (This is expected at hangup time)\n"); return; } /* Lock this session's data for this module while we tinker with it */ if (globals.mutex) { switch_mutex_lock(globals.mutex); } billrate = switch_channel_get_variable(channel, "nibble_rate"); /* Calculate how much was "lost" to billings during pause - we do this here because you never know when the billrate may change during a call */ nibble_data->bill_adjustments += (atof(billrate) / 1000000 / 60) * ((ts - nibble_data->pausets)); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Resumed billing! Subtracted %f from this billing cycle.\n", (atof(billrate) / 1000000 / 60) * ((ts - nibble_data->pausets))); nibble_data->pausets = 0; /* Done checking - release lock */ if (globals.mutex) { switch_mutex_unlock(globals.mutex); } }
static switch_status_t start_capture(switch_core_session_t *session, unsigned int seconds, switch_media_bug_flag_t flags, const char *base) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_media_bug_t *bug; switch_status_t status; switch_codec_implementation_t read_impl = { 0 }; struct cap_cb *cb; switch_size_t bytes; switch_bind_flag_t bind_flags = 0; if (switch_channel_get_private(channel, "snapshot")) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Already Running.\n"); return SWITCH_STATUS_FALSE; } if (seconds < 5) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Must be at least 5 seconds!\n"); return SWITCH_STATUS_FALSE; } switch_core_session_get_read_impl(session, &read_impl); if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_FALSE; } cb = switch_core_session_alloc(session, sizeof(*cb)); cb->base = switch_core_session_strdup(session, base); bytes = read_impl.samples_per_second * seconds * 2; switch_buffer_create_dynamic(&cb->buffer, bytes, bytes, bytes); switch_mutex_init(&cb->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); if ((status = switch_core_media_bug_add(session, "snapshot", NULL, capture_callback, cb, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) { return status; } bind_flags = SBF_DIAL_ALEG | SBF_EXEC_ALEG | SBF_EXEC_SAME; switch_ivr_bind_dtmf_meta_session(session, 7, bind_flags, "snapshot::snap"); switch_channel_set_private(channel, "snapshot", bug); return SWITCH_STATUS_SUCCESS; }
static switch_status_t spy_on_hangup(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); char *data = switch_channel_get_private(channel, "_userspy_"); switch_thread_rwlock_wrlock(globals.spy_hash_lock); if ((switch_core_hash_delete(globals.spy_hash, data) != SWITCH_STATUS_SUCCESS)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No such key in userspy: %s \n", data); } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Userspy deactivated on %s\n", data); globals.spy_count--; } switch_thread_rwlock_unlock(globals.spy_hash_lock); return SWITCH_STATUS_SUCCESS; }
static switch_status_t channel_on_destroy(switch_core_session_t *session) { switch_channel_t *channel = NULL; private_t *tech_pvt = NULL; void *pop; switch_event_t *vars; channel = switch_core_session_get_channel(session); switch_assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); if ((vars = (switch_event_t *) switch_channel_get_private(channel, "__loopback_vars__"))) { switch_channel_set_private(channel, "__loopback_vars__", NULL); switch_event_destroy(&vars); } if (tech_pvt) { switch_core_timer_destroy(&tech_pvt->timer); if (switch_core_codec_ready(&tech_pvt->read_codec)) { switch_core_codec_destroy(&tech_pvt->read_codec); } if (switch_core_codec_ready(&tech_pvt->write_codec)) { switch_core_codec_destroy(&tech_pvt->write_codec); } if (tech_pvt->write_frame) { switch_frame_free(&tech_pvt->write_frame); } while (switch_queue_trypop(tech_pvt->frame_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) { switch_frame_t *frame = (switch_frame_t *) pop; switch_frame_free(&frame); } } return SWITCH_STATUS_SUCCESS; }
SWITCH_DECLARE_NONSTD(switch_status_t) hanguphook(switch_core_session_t *session_hungup) { if (session_hungup) { switch_channel_t *channel = switch_core_session_get_channel(session_hungup); CoreSession *coresession = NULL; switch_channel_state_t state = switch_channel_get_state(channel); if ((coresession = (CoreSession *) switch_channel_get_private(channel, "CoreSession"))) { if (coresession->hook_state != state) { coresession->cause = switch_channel_get_cause(channel); coresession->hook_state = state; coresession->check_hangup_hook(); } } return SWITCH_STATUS_SUCCESS; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "hangup hook called with null session, something is horribly wrong\n"); return SWITCH_STATUS_FALSE; } }
/* NB. this starts the input thread after some initial setup for the call leg */ void conference_loop_output(conference_member_t *member) { switch_channel_t *channel; switch_frame_t write_frame = { 0 }; uint8_t *data = NULL; switch_timer_t timer = { 0 }; uint32_t interval; uint32_t samples; //uint32_t csamples; uint32_t tsamples; uint32_t flush_len; uint32_t low_count, bytes; call_list_t *call_list, *cp; switch_codec_implementation_t read_impl = { 0 }; int sanity; switch_status_t st; switch_core_session_get_read_impl(member->session, &read_impl); channel = switch_core_session_get_channel(member->session); interval = read_impl.microseconds_per_packet / 1000; samples = switch_samples_per_packet(member->conference->rate, interval); //csamples = samples; tsamples = member->orig_read_impl.samples_per_packet; low_count = 0; bytes = samples * 2 * member->conference->channels; call_list = NULL; cp = NULL; member->loop_loop = 0; switch_assert(member->conference != NULL); flush_len = switch_samples_per_packet(member->conference->rate, member->conference->interval) * 2 * member->conference->channels * (500 / member->conference->interval); if (switch_core_timer_init(&timer, member->conference->timer_name, interval, tsamples, NULL) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_ERROR, "Timer Setup Failed. Conference Cannot Start\n"); return; } switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Setup timer %s success interval: %u samples: %u\n", member->conference->timer_name, interval, tsamples); write_frame.data = data = switch_core_session_alloc(member->session, SWITCH_RECOMMENDED_BUFFER_SIZE); write_frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE; write_frame.codec = &member->write_codec; /* Start the input thread */ conference_loop_launch_input(member, switch_core_session_get_pool(member->session)); if ((call_list = switch_channel_get_private(channel, "_conference_autocall_list_"))) { const char *cid_name = switch_channel_get_variable(channel, "conference_auto_outcall_caller_id_name"); const char *cid_num = switch_channel_get_variable(channel, "conference_auto_outcall_caller_id_number"); const char *toval = switch_channel_get_variable(channel, "conference_auto_outcall_timeout"); const char *flags = switch_channel_get_variable(channel, "conference_utils_auto_outcall_flags"); const char *profile = switch_channel_get_variable(channel, "conference_auto_outcall_profile"); const char *ann = switch_channel_get_variable(channel, "conference_auto_outcall_announce"); const char *prefix = switch_channel_get_variable(channel, "conference_auto_outcall_prefix"); const char *maxwait = switch_channel_get_variable(channel, "conference_auto_outcall_maxwait"); const char *delimiter_val = switch_channel_get_variable(channel, "conference_auto_outcall_delimiter"); int to = 60; int wait_sec = 2; int loops = 0; if (ann && !switch_channel_test_app_flag_key("conference_silent", channel, CONF_SILENT_REQ)) { member->conference->special_announce = switch_core_strdup(member->conference->pool, ann); } switch_channel_set_private(channel, "_conference_autocall_list_", NULL); conference_utils_set_flag(member->conference, CFLAG_OUTCALL); if (toval) { to = atoi(toval); if (to < 10 || to > 500) { to = 60; } } for (cp = call_list; cp; cp = cp->next) { int argc; char *argv[512] = { 0 }; char *cpstr = strdup(cp->string); int x = 0; switch_assert(cpstr); if (!zstr(delimiter_val) && strlen(delimiter_val) == 1) { char delimiter = *delimiter_val; argc = switch_separate_string(cpstr, delimiter, argv, (sizeof(argv) / sizeof(argv[0]))); } else { argc = switch_separate_string(cpstr, ',', argv, (sizeof(argv) / sizeof(argv[0]))); } for (x = 0; x < argc; x++) { char *dial_str = switch_mprintf("%s%s", switch_str_nil(prefix), argv[x]); switch_assert(dial_str); conference_outcall_bg(member->conference, NULL, NULL, dial_str, to, switch_str_nil(flags), cid_name, cid_num, NULL, profile, &member->conference->cancel_cause, NULL); switch_safe_free(dial_str); } switch_safe_free(cpstr); } if (maxwait) { int tmp = atoi(maxwait); if (tmp > 0) { wait_sec = tmp; } } loops = wait_sec * 10; switch_channel_set_app_flag(channel, CF_APP_TAGGED); do { switch_ivr_sleep(member->session, 100, SWITCH_TRUE, NULL); } while(switch_channel_up(channel) && (member->conference->originating && --loops)); switch_channel_clear_app_flag(channel, CF_APP_TAGGED); if (!switch_channel_ready(channel)) { member->conference->cancel_cause = SWITCH_CAUSE_ORIGINATOR_CANCEL; goto end; } conference_member_play_file(member, "tone_stream://%(500,0,640)", 0, SWITCH_TRUE); } if (!conference_utils_test_flag(member->conference, CFLAG_ANSWERED)) { switch_channel_answer(channel); } sanity = 2000; while(!conference_utils_member_test_flag(member, MFLAG_ITHREAD) && sanity > 0) { switch_cond_next(); sanity--; } /* Fair WARNING, If you expect the caller to hear anything or for digit handling to be processed, */ /* you better not block this thread loop for more than the duration of member->conference->timer_name! */ while (!member->loop_loop && conference_utils_member_test_flag(member, MFLAG_RUNNING) && conference_utils_member_test_flag(member, MFLAG_ITHREAD) && switch_channel_ready(channel)) { switch_event_t *event; int use_timer = 0; switch_buffer_t *use_buffer = NULL; uint32_t mux_used = 0; //if (member->reset_media || switch_channel_test_flag(member->channel, CF_CONFERENCE_RESET_MEDIA)) { // switch_cond_next(); // continue; //} switch_mutex_lock(member->write_mutex); if (switch_channel_test_flag(member->channel, CF_CONFERENCE_ADV)) { if (member->conference->la) { conference_event_adv_la(member->conference, member, SWITCH_TRUE); } switch_channel_clear_flag(member->channel, CF_CONFERENCE_ADV); } if (switch_core_session_dequeue_event(member->session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { if (event->event_id == SWITCH_EVENT_MESSAGE) { char *from = switch_event_get_header(event, "from"); char *to = switch_event_get_header(event, "to"); char *body = switch_event_get_body(event); if (to && from && body) { if (strchr(to, '+') && strncmp(to, CONF_CHAT_PROTO, strlen(CONF_CHAT_PROTO))) { switch_event_del_header(event, "to"); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "to", "%s+%s@%s", CONF_CHAT_PROTO, member->conference->name, member->conference->domain); } else { switch_event_del_header(event, "to"); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "to", "%s", member->conference->name); } chat_send(event); } } switch_event_destroy(&event); } if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { /* test to see if outbound channel has answered */ if (switch_channel_test_flag(channel, CF_ANSWERED) && !conference_utils_test_flag(member->conference, CFLAG_ANSWERED)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Outbound conference channel answered, setting CFLAG_ANSWERED\n"); conference_utils_set_flag(member->conference, CFLAG_ANSWERED); } } else { if (conference_utils_test_flag(member->conference, CFLAG_ANSWERED) && !switch_channel_test_flag(channel, CF_ANSWERED)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "CLFAG_ANSWERED set, answering inbound channel\n"); switch_channel_answer(channel); } } use_buffer = NULL; mux_used = (uint32_t) switch_buffer_inuse(member->mux_buffer); use_timer = 1; if (mux_used) { if (mux_used < bytes) { if (++low_count >= 5) { /* partial frame sitting around this long is useless and builds delay */ conference_utils_member_set_flag_locked(member, MFLAG_FLUSH_BUFFER); } } else if (mux_used > flush_len) { /* getting behind, clear the buffer */ conference_utils_member_set_flag_locked(member, MFLAG_FLUSH_BUFFER); } } if (switch_channel_test_app_flag(channel, CF_APP_TAGGED)) { conference_utils_member_set_flag_locked(member, MFLAG_FLUSH_BUFFER); } else if (mux_used >= bytes) { /* Flush the output buffer and write all the data (presumably muxed) back to the channel */ switch_mutex_lock(member->audio_out_mutex); write_frame.data = data; use_buffer = member->mux_buffer; low_count = 0; if ((write_frame.datalen = (uint32_t) switch_buffer_read(use_buffer, write_frame.data, bytes))) { if (write_frame.datalen) { write_frame.samples = write_frame.datalen / 2 / member->conference->channels; if( !conference_utils_member_test_flag(member, MFLAG_CAN_HEAR)) { memset(write_frame.data, 255, write_frame.datalen); } else if (member->volume_out_level) { /* Check for output volume adjustments */ switch_change_sln_volume(write_frame.data, write_frame.samples * member->conference->channels, member->volume_out_level); } write_frame.timestamp = timer.samplecount; if (member->fnode) { conference_member_add_file_data(member, write_frame.data, write_frame.datalen); } conference_member_check_channels(&write_frame, member, SWITCH_FALSE); if (switch_core_session_write_frame(member->session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { switch_mutex_unlock(member->audio_out_mutex); break; } } } switch_mutex_unlock(member->audio_out_mutex); } if (conference_utils_member_test_flag(member, MFLAG_FLUSH_BUFFER)) { if (switch_buffer_inuse(member->mux_buffer)) { switch_mutex_lock(member->audio_out_mutex); switch_buffer_zero(member->mux_buffer); switch_mutex_unlock(member->audio_out_mutex); } conference_utils_member_clear_flag_locked(member, MFLAG_FLUSH_BUFFER); } switch_mutex_unlock(member->write_mutex); if (conference_utils_member_test_flag(member, MFLAG_INDICATE_MUTE)) { if (!zstr(member->conference->muted_sound)) { conference_member_play_file(member, member->conference->muted_sound, 0, SWITCH_TRUE); } else { char msg[512]; switch_snprintf(msg, sizeof(msg), "Muted"); conference_member_say(member, msg, 0); } conference_utils_member_clear_flag(member, MFLAG_INDICATE_MUTE); } if (conference_utils_member_test_flag(member, MFLAG_INDICATE_MUTE_DETECT)) { if (!zstr(member->conference->mute_detect_sound)) { conference_member_play_file(member, member->conference->mute_detect_sound, 0, SWITCH_TRUE); } else { char msg[512]; switch_snprintf(msg, sizeof(msg), "Currently Muted"); conference_member_say(member, msg, 0); } conference_utils_member_clear_flag(member, MFLAG_INDICATE_MUTE_DETECT); } if (conference_utils_member_test_flag(member, MFLAG_INDICATE_UNMUTE)) { if (!zstr(member->conference->unmuted_sound)) { conference_member_play_file(member, member->conference->unmuted_sound, 0, SWITCH_TRUE); } else { char msg[512]; switch_snprintf(msg, sizeof(msg), "Un-Muted"); conference_member_say(member, msg, 0); } conference_utils_member_clear_flag(member, MFLAG_INDICATE_UNMUTE); } if (switch_core_session_private_event_count(member->session)) { switch_channel_set_app_flag(channel, CF_APP_TAGGED); switch_ivr_parse_all_events(member->session); switch_channel_clear_app_flag(channel, CF_APP_TAGGED); conference_utils_member_set_flag_locked(member, MFLAG_FLUSH_BUFFER); switch_core_session_set_read_codec(member->session, &member->read_codec); } else { switch_ivr_parse_all_messages(member->session); } if (use_timer) { switch_core_timer_next(&timer); } else { switch_cond_next(); } } /* Rinse ... Repeat */ end: if (!member->loop_loop) { conference_utils_member_clear_flag_locked(member, MFLAG_RUNNING); /* Wait for the input thread to end */ if (member->input_thread) { switch_thread_join(&st, member->input_thread); member->input_thread = NULL; } } switch_core_timer_destroy(&timer); if (member->loop_loop) { return; } switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_INFO, "Channel leaving conference, cause: %s\n", switch_channel_cause2str(switch_channel_get_cause(channel))); /* if it's an outbound channel, store the release cause in the conference struct, we might need it */ if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { member->conference->bridge_hangup_cause = switch_channel_get_cause(channel); } }
/* 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; }
/* State methods they get called when the state changes to the specific state returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it. */ static switch_status_t channel_on_init(switch_core_session_t *session) { switch_channel_t *channel, *b_channel; private_t *tech_pvt = NULL, *b_tech_pvt = NULL; switch_core_session_t *b_session; char name[128]; switch_caller_profile_t *caller_profile; switch_event_t *vars = NULL; const char *var; tech_pvt = switch_core_session_get_private(session); switch_assert(tech_pvt != NULL); channel = switch_core_session_get_channel(session); switch_assert(channel != NULL); if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND) && !switch_test_flag(tech_pvt, TFLAG_BLEG)) { if (!(b_session = switch_core_session_request(loopback_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL))) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Failure.\n"); goto end; } if (switch_core_session_read_lock(b_session) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Failure.\n"); switch_core_session_destroy(&b_session); goto end; } switch_core_session_add_stream(b_session, NULL); b_channel = switch_core_session_get_channel(b_session); b_tech_pvt = (private_t *) switch_core_session_alloc(b_session, sizeof(*b_tech_pvt)); switch_snprintf(name, sizeof(name), "loopback/%s-b", tech_pvt->caller_profile->destination_number); switch_channel_set_name(b_channel, name); if (tech_init(b_tech_pvt, b_session, switch_core_session_get_read_codec(session)) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); switch_core_session_destroy(&b_session); goto end; } caller_profile = switch_caller_profile_clone(b_session, tech_pvt->caller_profile); caller_profile->source = switch_core_strdup(caller_profile->pool, modname); switch_channel_set_caller_profile(b_channel, caller_profile); b_tech_pvt->caller_profile = caller_profile; switch_channel_set_state(b_channel, CS_INIT); tech_pvt->other_session = b_session; tech_pvt->other_tech_pvt = b_tech_pvt; tech_pvt->other_channel = b_channel; //b_tech_pvt->other_session = session; //b_tech_pvt->other_tech_pvt = tech_pvt; //b_tech_pvt->other_channel = channel; b_tech_pvt->other_uuid = switch_core_session_strdup(b_session, switch_core_session_get_uuid(session)); switch_set_flag_locked(tech_pvt, TFLAG_LINKED); switch_set_flag_locked(b_tech_pvt, TFLAG_LINKED); switch_set_flag_locked(b_tech_pvt, TFLAG_BLEG); switch_channel_set_flag(channel, CF_ACCEPT_CNG); if ((vars = (switch_event_t *) switch_channel_get_private(channel, "__loopback_vars__"))) { switch_event_header_t *h; switch_channel_set_private(channel, "__loopback_vars__", NULL); for (h = vars->headers; h; h = h->next) { switch_channel_set_variable(tech_pvt->other_channel, h->name, h->value); } switch_event_destroy(&vars); } if ((var = switch_channel_get_variable(channel, "loopback_export"))) { int argc = 0; char *argv[128] = { 0 }; char *dup = switch_core_session_strdup(session, var); if ((argc = switch_split(dup, ',', argv))) { int i; for (i = 0; i < argc; i++) { if (!zstr(argv[i])) { const char *val = switch_channel_get_variable(channel, argv[i]); if(!zstr(val)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Transfer variable [%s]=[%s] %s -> %s\n", argv[i], val, switch_channel_get_name(channel), switch_channel_get_name(tech_pvt->other_channel)); switch_channel_set_variable(tech_pvt->other_channel, argv[i], val); } } } } } if (switch_test_flag(tech_pvt, TFLAG_APP)) { switch_set_flag(b_tech_pvt, TFLAG_APP); switch_clear_flag(tech_pvt, TFLAG_APP); } switch_channel_set_variable(channel, "other_loopback_leg_uuid", switch_channel_get_uuid(b_channel)); switch_channel_set_variable(b_channel, "other_loopback_leg_uuid", switch_channel_get_uuid(channel)); if (switch_core_session_thread_launch(b_session) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Error spawning thread\n"); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); goto end; } } else if ((tech_pvt->other_session = switch_core_session_locate(tech_pvt->other_uuid))) { tech_pvt->other_tech_pvt = switch_core_session_get_private(tech_pvt->other_session); tech_pvt->other_channel = switch_core_session_get_channel(tech_pvt->other_session); } if (!tech_pvt->other_session) { switch_clear_flag_locked(tech_pvt, TFLAG_LINKED); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); goto end; } switch_channel_set_variable(channel, "loopback_leg", switch_test_flag(tech_pvt, TFLAG_BLEG) ? "B" : "A"); switch_channel_set_state(channel, CS_ROUTING); end: return SWITCH_STATUS_SUCCESS; }
SWITCH_DECLARE(void *)CoreSession::getPrivate(char *var) { this_check(NULL); sanity_check(NULL); return switch_channel_get_private(channel, var); }