From cbfe83cc8f27e3c9de5aba6467b91e33efcd7af4 Mon Sep 17 00:00:00 2001 From: Michael S Collins Date: Wed, 28 Sep 2011 16:51:11 -0700 Subject: [PATCH 001/429] Fix eavesdrop so that *0 works as well as 88 as the access code --- conf/dialplan/default.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/dialplan/default.xml b/conf/dialplan/default.xml index 020507912c..6d46eb069c 100644 --- a/conf/dialplan/default.xml +++ b/conf/dialplan/default.xml @@ -181,7 +181,7 @@ - + From 256a6264d48d9a63a421c127e7b55a082fb6f813 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 29 Sep 2011 08:28:12 -0500 Subject: [PATCH 002/429] prevent sql injection by using sqlite formatter on various code that generates sql stmts with switch_snprintf --- .../applications/mod_commands/mod_commands.c | 6 +-- .../mod_voicemail/mod_voicemail.c | 12 ++--- src/mod/endpoints/mod_sofia/sofia_reg.c | 50 +++++++++---------- src/switch_core_sqldb.c | 6 +-- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 62fbc0bf59..efc4603d52 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -4078,11 +4078,11 @@ SWITCH_STANDARD_API(show_function) holder.print_title = 0; if ((cmdname = strchr(command, ' ')) && strcasecmp(cmdname, "as")) { *cmdname++ = '\0'; - switch_snprintf(sql, sizeof(sql) - 1, - "select name, syntax, description, ikey from interfaces where hostname='%s' and type = 'api' and name = '%s' order by name", + switch_snprintfv(sql, sizeof(sql), + "select name, syntax, description, ikey from interfaces where hostname='%s' and type = 'api' and name = '%q' order by name", hostname, cmdname); } else { - switch_snprintf(sql, sizeof(sql) - 1, "select name, syntax, description, ikey from interfaces where hostname='%s' and type = 'api' order by name", hostname); + switch_snprintfv(sql, sizeof(sql), "select name, syntax, description, ikey from interfaces where hostname='%q' and type = 'api' order by name", hostname); } } else if (!strcasecmp(command, "nat_map")) { switch_snprintf(sql, sizeof(sql) - 1, diff --git a/src/mod/applications/mod_voicemail/mod_voicemail.c b/src/mod/applications/mod_voicemail/mod_voicemail.c index 3b12e7c7a1..16d3afa7e1 100644 --- a/src/mod/applications/mod_voicemail/mod_voicemail.c +++ b/src/mod/applications/mod_voicemail/mod_voicemail.c @@ -2016,10 +2016,10 @@ static void voicemail_check_main(switch_core_session_t *session, vm_profile_t *p "username='%s' and domain='%s' and flags='save'", (long) switch_epoch_time_now(NULL), myid, domain_name); vm_execute_sql(profile, sql, profile->mutex); - switch_snprintf(sql, sizeof(sql), "select file_path from voicemail_msgs where username='%s' and domain='%s' and flags='delete'", myid, + switch_snprintfv(sql, sizeof(sql), "select file_path from voicemail_msgs where username='%q' and domain='%q' and flags='delete'", myid, domain_name); vm_execute_sql_callback(profile, profile->mutex, sql, unlink_callback, NULL); - switch_snprintf(sql, sizeof(sql), "delete from voicemail_msgs where username='%s' and domain='%s' and flags='delete'", myid, domain_name); + switch_snprintfv(sql, sizeof(sql), "delete from voicemail_msgs where username='%q' and domain='%q' and flags='delete'", myid, domain_name); vm_execute_sql(profile, sql, profile->mutex); vm_check_state = VM_CHECK_FOLDER_SUMMARY; @@ -2305,7 +2305,7 @@ static void voicemail_check_main(switch_core_session_t *session, vm_profile_t *p } thepass = thehash = NULL; - switch_snprintf(sql, sizeof(sql), "select * from voicemail_prefs where username='%s' and domain='%s'", myid, domain_name); + switch_snprintfv(sql, sizeof(sql), "select * from voicemail_prefs where username='%q' and domain='%q'", myid, domain_name); vm_execute_sql_callback(profile, profile->mutex, sql, prefs_callback, &cbt); x_params = switch_xml_child(x_user, "variables"); @@ -3225,7 +3225,7 @@ static switch_status_t voicemail_leave_main(switch_core_session_t *session, vm_p goto end; } - switch_snprintf(sql, sizeof(sql), "select * from voicemail_prefs where username='%s' and domain='%s'", id, domain_name); + switch_snprintfv(sql, sizeof(sql), "select * from voicemail_prefs where username='%q' and domain='%q'", id, domain_name); vm_execute_sql_callback(profile, profile->mutex, sql, prefs_callback, &cbt); if (!vm_ext) { @@ -3327,7 +3327,7 @@ static switch_status_t voicemail_leave_main(switch_core_session_t *session, vm_p callback.buf = disk_usage; callback.len = sizeof(disk_usage); - switch_snprintf(sqlstmt, sizeof(sqlstmt), "select sum(message_len) from voicemail_msgs where username='%s' and domain='%s'", id, domain_name); + switch_snprintfv(sqlstmt, sizeof(sqlstmt), "select sum(message_len) from voicemail_msgs where username='%q' and domain='%q'", id, domain_name); vm_execute_sql_callback(profile, profile->mutex, sqlstmt, sql2str_callback, &callback); if (atoi(disk_usage) >= disk_quota) { @@ -3633,7 +3633,7 @@ SWITCH_STANDARD_API(prefs_api_function) } - switch_snprintf(sql, sizeof(sql), "select * from voicemail_prefs where username='%s' and domain='%s'", id, domain); + switch_snprintfv(sql, sizeof(sql), "select * from voicemail_prefs where username='%q' and domain='%q'", id, domain); vm_execute_sql_callback(profile, profile->mutex, sql, prefs_callback, &cbt); if (!strcasecmp(how, "greeting_path")) { diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index 253caf890c..a65c205b8f 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -676,10 +676,10 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_reg_del_callback, profile); if (now) { - switch_snprintf(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and expires <= %ld and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and expires <= %ld and hostname='%q'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_actually_execute_sql(profile, sql, NULL); @@ -687,11 +687,11 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) if (now) { - switch_snprintf(sql, sizeof(sql), "select call_id from sip_shared_appearance_dialogs where hostname='%s' " + switch_snprintfv(sql, sizeof(sql), "select call_id from sip_shared_appearance_dialogs where hostname='%q' " "and profile_name='%s' and expires <= %ld", mod_sofia_globals.hostname, profile->name, (long) now); sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_sla_dialog_del_callback, profile); - switch_snprintf(sql, sizeof(sql), "delete from sip_shared_appearance_dialogs where expires > 0 and hostname='%s' and expires <= %ld", + switch_snprintfv(sql, sizeof(sql), "delete from sip_shared_appearance_dialogs where expires > 0 and hostname='%q' and expires <= %ld", mod_sofia_globals.hostname, (long) now); @@ -700,19 +700,19 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) if (now) { - switch_snprintf(sql, sizeof(sql), "delete from sip_presence where expires > 0 and expires <= %ld and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "delete from sip_presence where expires > 0 and expires <= %ld and hostname='%q'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "delete from sip_presence where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_presence where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_actually_execute_sql(profile, sql, NULL); if (now) { - switch_snprintf(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and expires <= %ld and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and expires <= %ld and hostname='%q'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_actually_execute_sql(profile, sql, NULL); @@ -722,27 +722,27 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) "select call_id from sip_subscriptions where (expires = -1 or (expires > 0 and expires <= %ld)) and hostname='%s'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "select sub_to_user,sub_to_host,call_id from sip_subscriptions where expires >= -1 and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "select sub_to_user,sub_to_host,call_id from sip_subscriptions where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_sub_del_callback, profile); if (now) { - switch_snprintf(sql, sizeof(sql), "delete from sip_subscriptions where (expires = -1 or (expires > 0 and expires <= %ld)) and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "delete from sip_subscriptions where (expires = -1 or (expires > 0 and expires <= %ld)) and hostname='%q'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "delete from sip_subscriptions where expires >= -1 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_subscriptions where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_actually_execute_sql(profile, sql, NULL); if (now) { - switch_snprintf(sql, sizeof(sql), "delete from sip_dialogs where (expires = -1 or (expires > 0 and expires <= %ld)) and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "delete from sip_dialogs where (expires = -1 or (expires > 0 and expires <= %ld)) and hostname='%q'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "delete from sip_dialogs where expires >= -1 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_dialogs where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_actually_execute_sql(profile, sql, NULL); @@ -833,24 +833,24 @@ void sofia_reg_check_sync(sofia_profile_t *profile) sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_reg_del_callback, profile); - switch_snprintf(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_actually_execute_sql(profile, sql, NULL); - switch_snprintf(sql, sizeof(sql), "delete from sip_presence where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_presence where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_actually_execute_sql(profile, sql, NULL); - switch_snprintf(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_actually_execute_sql(profile, sql, NULL); - switch_snprintf(sql, sizeof(sql), "select sub_to_user,sub_to_host,call_id from sip_subscriptions where expires >= -1 and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "select sub_to_user,sub_to_host,call_id from sip_subscriptions where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_sub_del_callback, profile); - switch_snprintf(sql, sizeof(sql), "delete from sip_subscriptions where expires >= -1 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_subscriptions where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_actually_execute_sql(profile, sql, NULL); - switch_snprintf(sql, sizeof(sql), "delete from sip_dialogs where expires >= -1 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_dialogs where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_actually_execute_sql(profile, sql, NULL); switch_mutex_unlock(profile->ireg_mutex); @@ -871,10 +871,10 @@ char *sofia_reg_find_reg_url(sofia_profile_t *profile, const char *user, const c cbt.len = len; if (host) { - switch_snprintf(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%s' and (sip_host='%s' or presence_hosts like '%%%s%%')", + switch_snprintfv(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%q' and (sip_host='%q' or presence_hosts like '%%%q%%')", user, host, host); } else { - switch_snprintf(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%s'", user); + switch_snprintfv(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%q'", user); } @@ -900,10 +900,10 @@ switch_console_callback_match_t *sofia_reg_find_reg_url_multi(sofia_profile_t *p } if (host) { - switch_snprintf(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%s' and (sip_host='%s' or presence_hosts like '%%%s%%')", + switch_snprintfv(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%q' and (sip_host='%q' or presence_hosts like '%%%q%%')", user, host, host); } else { - switch_snprintf(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%s'", user); + switch_snprintfv(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%q'", user); } @@ -924,10 +924,10 @@ switch_console_callback_match_t *sofia_reg_find_reg_url_with_positive_expires_mu } if (host) { - switch_snprintf(sql, sizeof(sql), "select contact,expires from sip_registrations where sip_user='%s' and (sip_host='%s' or presence_hosts like '%%%s%%')", + switch_snprintfv(sql, sizeof(sql), "select contact,expires from sip_registrations where sip_user='%q' and (sip_host='%q' or presence_hosts like '%%%q%%')", user, host, host); } else { - switch_snprintf(sql, sizeof(sql), "select contact,expires from sip_registrations where sip_user='%s'", user); + switch_snprintfv(sql, sizeof(sql), "select contact,expires from sip_registrations where sip_user='%q'", user); } sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sofia_reg_find_reg_with_positive_expires_callback, &cbt); diff --git a/src/switch_core_sqldb.c b/src/switch_core_sqldb.c index f85f4b3d8c..90112aeb4e 100644 --- a/src/switch_core_sqldb.c +++ b/src/switch_core_sqldb.c @@ -733,7 +733,7 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_persistant_execute_trans(switch_ if ((result = switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 0)) != SWITCH_ODBC_SUCCESS) { char tmp[100]; - switch_snprintf(tmp, sizeof(tmp), "%s-%i", "Unable to Set AutoCommit Off", result); + switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result); errmsg = strdup(tmp); } } @@ -1144,7 +1144,7 @@ static char *parse_presence_data_cols(switch_event_t *event) SWITCH_STANDARD_STREAM(stream); for (i = 0; i < col_count; i++) { - switch_snprintf(col_name, sizeof(col_name), "variable_%s", cols[i]); + switch_snprintfv(col_name, sizeof(col_name), "variable_%q", cols[i]); stream.write_function(&stream, "%q='%q',", cols[i], switch_event_get_header_nil(event, col_name)); } @@ -1906,7 +1906,7 @@ switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_ const char *hostname = switch_core_get_switchname(); for (i = 0; tables[i]; i++) { - switch_snprintf(sql, sizeof(sql), "delete from %s where hostname='%s'", tables[i], hostname); + switch_snprintfv(sql, sizeof(sql), "delete from %q where hostname='%q'", tables[i], hostname); switch_cache_db_execute_sql(dbh, sql, NULL); } } From 71977499cad0ed3b7ef5ccccdc91ed3e28006f3f Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 29 Sep 2011 08:55:27 -0500 Subject: [PATCH 003/429] remove redundant data from output of fifo count --- src/mod/applications/mod_fifo/mod_fifo.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/mod/applications/mod_fifo/mod_fifo.c b/src/mod/applications/mod_fifo/mod_fifo.c index 827905590f..fe91155ee7 100644 --- a/src/mod/applications/mod_fifo/mod_fifo.c +++ b/src/mod/applications/mod_fifo/mod_fifo.c @@ -3677,7 +3677,6 @@ void node_dump(switch_stream_handle_t *stream) #define FIFO_API_SYNTAX "list|list_verbose|count|debug|status|importance []|reparse [del_all]" SWITCH_STANDARD_API(fifo_api_function) { - int len = 0; fifo_node_t *node; char *data = NULL; int argc = 0; @@ -3772,9 +3771,8 @@ SWITCH_STANDARD_API(fifo_api_function) for (hi = switch_hash_first(NULL, globals.fifo_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, &var, NULL, &val); node = (fifo_node_t *) val; - len = node_caller_count(node); switch_mutex_lock(node->update_mutex); - stream->write_function(stream, "%s:%d:%d:%d\n", (char *) var, node->consumer_count, node_caller_count(node), len); + stream->write_function(stream, "%s:%d:%d\n", (char *) var, node->consumer_count, node_caller_count(node)); switch_mutex_unlock(node->update_mutex); x++; } @@ -3783,9 +3781,8 @@ SWITCH_STANDARD_API(fifo_api_function) stream->write_function(stream, "none\n"); } } else if ((node = switch_core_hash_find(globals.fifo_hash, argv[1]))) { - len = node_caller_count(node); switch_mutex_lock(node->update_mutex); - stream->write_function(stream, "%s:%d:%d:%d\n", argv[1], node->consumer_count, node_caller_count(node), len); + stream->write_function(stream, "%s:%d:%d\n", argv[1], node->consumer_count, node_caller_count(node)); switch_mutex_unlock(node->update_mutex); } else { stream->write_function(stream, "none\n"); @@ -3795,7 +3792,6 @@ SWITCH_STANDARD_API(fifo_api_function) for (hi = switch_hash_first(NULL, globals.fifo_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, &var, NULL, &val); node = (fifo_node_t *) val; - len = node_caller_count(node); switch_mutex_lock(node->update_mutex); stream->write_function(stream, "%s:%d\n", (char *) var, node->has_outbound); switch_mutex_unlock(node->update_mutex); @@ -3806,7 +3802,6 @@ SWITCH_STANDARD_API(fifo_api_function) stream->write_function(stream, "none\n"); } } else if ((node = switch_core_hash_find(globals.fifo_hash, argv[1]))) { - len = node_caller_count(node); switch_mutex_lock(node->update_mutex); stream->write_function(stream, "%s:%d\n", argv[1], node->has_outbound); switch_mutex_unlock(node->update_mutex); From 4d6ee827e03400838352aed65f9acd12c1623ccb Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 29 Sep 2011 09:04:22 -0500 Subject: [PATCH 004/429] add _continue_ value for fifo orbit exten that just means exit back to the next dp instruction --- src/mod/applications/mod_fifo/mod_fifo.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mod/applications/mod_fifo/mod_fifo.c b/src/mod/applications/mod_fifo/mod_fifo.c index fe91155ee7..3d1504fa37 100644 --- a/src/mod/applications/mod_fifo/mod_fifo.c +++ b/src/mod/applications/mod_fifo/mod_fifo.c @@ -2534,7 +2534,10 @@ SWITCH_STANDARD_APP(fifo_function) if (orbit_ann) { switch_ivr_play_file(session, NULL, orbit_ann, NULL); } - switch_ivr_session_transfer(session, cd.orbit_exten, cd.orbit_dialplan, cd.orbit_context); + + if (strcmp(cd.orbit_exten, "_continue_")) { + switch_ivr_session_transfer(session, cd.orbit_exten, cd.orbit_dialplan, cd.orbit_context); + } } check_ocancel(session); From ed29a31462e7c1d2dab4cd00d6f71e233e947928 Mon Sep 17 00:00:00 2001 From: Brian West Date: Thu, 29 Sep 2011 18:00:21 -0500 Subject: [PATCH 005/429] missed one spot --- src/mod/endpoints/mod_sofia/sofia.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 640f1b2d0e..9601697ab4 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -695,6 +695,7 @@ void sofia_update_callee_id(switch_core_session_t *session, sofia_profile_t *pro if ((val = sofia_glue_get_unknown_header(sip, "X-FS-Display-Name"))) { name = (char *) val; + check_decode(name, session); fs++; } From e25a90a04bbc6da0e98545fa7eb7761ddcc69c0c Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 29 Sep 2011 13:22:01 -0500 Subject: [PATCH 006/429] always generate frames on sleep --- src/switch_ivr.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/switch_ivr.c b/src/switch_ivr.c index e0aa689e5c..a90576b8f8 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -138,7 +138,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, unsigned char *abuf = NULL; switch_codec_implementation_t imp = { 0 }; switch_codec_t codec = { 0 }; - int sval = 0; + int sval = -1; const char *var; /* @@ -192,6 +192,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, write_frame.codec = &codec; switch_zmalloc(abuf, SWITCH_RECOMMENDED_BUFFER_SIZE); + memset(abuf, 255, SWITCH_RECOMMENDED_BUFFER_SIZE); write_frame.data = abuf; write_frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE; write_frame.datalen = imp.decoded_bytes_per_packet; @@ -294,7 +295,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, } if (sval && write_frame.datalen) { - switch_generate_sln_silence((int16_t *) write_frame.data, write_frame.samples, sval); + if (sval > 0) { + switch_generate_sln_silence((int16_t *) write_frame.data, write_frame.samples, sval); + } switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0); } else { switch_core_session_write_frame(session, &cng_frame, SWITCH_IO_FLAG_NONE, 0); From 0a3e5d2f74cab5fe1eb761699584f08a3c3a3b15 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 29 Sep 2011 16:20:27 -0500 Subject: [PATCH 007/429] add ivr_menu_terminator variable you can set to none or the dtmf chars you want to terminate input --- src/switch_ivr_menu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/switch_ivr_menu.c b/src/switch_ivr_menu.c index c70db2399d..c171d1b02b 100644 --- a/src/switch_ivr_menu.c +++ b/src/switch_ivr_menu.c @@ -304,13 +304,18 @@ static switch_status_t play_and_collect(switch_core_session_t *session, switch_i switch_channel_t *channel; char *sound_expanded = sound; switch_size_t menu_buf_len = 0; + const char *terminator_str = "#"; if (!session || !menu || zstr(sound)) { return status; } if ((channel = switch_core_session_get_channel(session))) { + const char *tmp; sound_expanded = switch_channel_expand_variables(channel, sound); + if ((tmp = switch_channel_get_variable(channel, "ivr_menu_terminator")) && !zstr(tmp)) { + terminator_str = tmp; + } } memset(menu->buf, 0, menu->inlen + 1); @@ -343,7 +348,7 @@ static switch_status_t play_and_collect(switch_core_session_t *session, switch_i switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "waiting for %u/%u digits t/o %d\n", (uint32_t) (menu->inlen - strlen(menu->buf)), (uint32_t) need, menu->inter_timeout); status = switch_ivr_collect_digits_count(session, menu->ptr, menu->inlen - strlen(menu->buf), - need, "#", &terminator, menu_buf_len ? menu->inter_timeout : menu->timeout, + need, terminator_str, &terminator, menu_buf_len ? menu->inter_timeout : menu->timeout, menu->inter_timeout, menu->timeout); } @@ -365,7 +370,7 @@ static switch_status_t play_and_collect(switch_core_session_t *session, switch_i switch_ivr_phrase_macro(session, menu->confirm_macro, menu->buf, NULL, ap); if (menu->confirm_key && *buf == '\0') { - switch_ivr_collect_digits_count(session, buf, sizeof(buf), 1, "#", &terminator_key, menu->timeout, 0, 0); + switch_ivr_collect_digits_count(session, buf, sizeof(buf), 1, terminator_str, &terminator_key, menu->timeout, 0, 0); } if (menu->confirm_key && *buf != '\0') { From 3317f5d36da25d2fa87cec15d551ed5dd84e6ad3 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 30 Sep 2011 09:44:56 -0500 Subject: [PATCH 008/429] delay_echo was double the length in milliseconds from what it should be --- src/switch_ivr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_ivr.c b/src/switch_ivr.c index a90576b8f8..85ffa00b8a 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -2449,7 +2449,7 @@ SWITCH_DECLARE(void) switch_ivr_delay_echo(switch_core_session_t *session, uint3 interval = read_impl.microseconds_per_packet / 1000; //samples = switch_samples_per_packet(read_impl.samples_per_second, interval); - qlen = delay_ms / (interval); + qlen = delay_ms / (interval) / 2; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setting delay to %dms (%d frames)\n", delay_ms, qlen); jb = stfu_n_init(qlen, qlen, read_impl.samples_per_packet, read_impl.samples_per_second, 0); From f308f56c710e6342aabbd89eea6b69588b4b9b02 Mon Sep 17 00:00:00 2001 From: Rupa Schomaker Date: Fri, 30 Sep 2011 18:30:35 -0500 Subject: [PATCH 009/429] FS-3535 --resolve fix memory leak in mod_lcr --- src/mod/applications/mod_lcr/mod_lcr.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/mod/applications/mod_lcr/mod_lcr.c b/src/mod/applications/mod_lcr/mod_lcr.c index a12f9a0ed6..f3e18cc175 100644 --- a/src/mod/applications/mod_lcr/mod_lcr.c +++ b/src/mod/applications/mod_lcr/mod_lcr.c @@ -605,6 +605,7 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa char *key = NULL; int i = 0; int r = 0; + switch_bool_t lcr_skipped = SWITCH_TRUE; /* assume we'll throw it away, paranoid about leak */ switch_memory_pool_t *pool = cbt->pool; @@ -669,6 +670,7 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error inserting into dedup hash\n"); r = -1; goto end; } + lcr_skipped = SWITCH_FALSE; r = 0; goto end; } @@ -698,6 +700,7 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error inserting into dedup hash\n"); r = -1; goto end; } + lcr_skipped = SWITCH_FALSE; break; } } else { @@ -719,6 +722,7 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error inserting into dedup hash\n"); r = -1; goto end; } + lcr_skipped = SWITCH_FALSE; break; } else if (current->next == NULL) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "adding %s to end of list after %s\n", @@ -729,6 +733,7 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error inserting into dedup hash\n"); r = -1; goto end; } + lcr_skipped = SWITCH_FALSE; break; } } @@ -736,8 +741,13 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa end: - /* event is freed in lcr_destroy() switch_event_destroy(&additional->fields); */ - + /* lcr was not added to any lists, so destroy lcr object here */ + if (lcr_skipped == SWITCH_TRUE) { + /* ensure we didn't accidentally add additional to the list */ + switch_assert(additional->prev == NULL && current->next != additional); + lcr_destroy(additional); + } + return r; } From 994f9a8ca622aa301250413760997b7ba3c25218 Mon Sep 17 00:00:00 2001 From: Darren Schreiber Date: Fri, 30 Sep 2011 21:57:25 -0400 Subject: [PATCH 010/429] Fixed a memory leak, too short of connect times across data centers, a deadlock condition with the globals.bindings_rwlock not being released, a buffer overrun possibility or 4, and added the ability to send a body when injecting an event --- .../mod_erlang_event/handle_msg.c | 89 ++++++++++++++++--- .../mod_erlang_event/mod_erlang_event.c | 5 +- 2 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/mod/event_handlers/mod_erlang_event/handle_msg.c b/src/mod/event_handlers/mod_erlang_event/handle_msg.c index 87f91fc51b..2f45ea6203 100644 --- a/src/mod/event_handlers/mod_erlang_event/handle_msg.c +++ b/src/mod/event_handlers/mod_erlang_event/handle_msg.c @@ -26,6 +26,8 @@ * Anthony Minessale II * Andrew Thompson * Rob Charlton + * Darren Schreiber + * Mike Jerris * * * handle_msg.c -- handle messages received from erlang nodes @@ -590,13 +592,29 @@ static switch_status_t handle_msg_session_setevent(listener_t *listener, erlang_ static switch_status_t handle_msg_api(listener_t *listener, erlang_msg * msg, int arity, ei_x_buff * buf, ei_x_buff * rbuf) { char api_cmd[MAXATOMLEN]; - char arg[1024]; - if (arity < 3 || ei_decode_atom(buf->buff, &buf->index, api_cmd) || ei_decode_string(buf->buff, &buf->index, arg)) { - ei_x_encode_tuple_header(rbuf, 2); - ei_x_encode_atom(rbuf, "error"); - ei_x_encode_atom(rbuf, "badarg"); - return SWITCH_STATUS_SUCCESS; - } else { + int type; + int size; + char *arg; + switch_bool_t fail = SWITCH_FALSE; + + if (arity < 3) { + fail = SWITCH_TRUE; + } + + ei_get_type(buf->buff, &buf->index, &type, &size); + + if ((size > (sizeof(api_cmd) - 1)) || ei_decode_atom(buf->buff, &buf->index, api_cmd)) { + fail = SWITCH_TRUE; + } + + ei_get_type(buf->buff, &buf->index, &type, &size); + arg = malloc(size + 1); + + if (ei_decode_string(buf->buff, &buf->index, arg)) { + fail = SWITCH_TRUE; + } + + if (!fail) { struct api_command_struct acs = { 0 }; acs.listener = listener; acs.api_cmd = api_cmd; @@ -604,8 +622,17 @@ static switch_status_t handle_msg_api(listener_t *listener, erlang_msg * msg, in acs.bg = 0; acs.pid = msg->from; api_exec(NULL, (void *) &acs); + + switch_safe_free(arg); + /* don't reply */ return SWITCH_STATUS_FALSE; + } else { + ei_x_encode_tuple_header(rbuf, 2); + ei_x_encode_atom(rbuf, "error"); + ei_x_encode_atom(rbuf, "badarg"); + return SWITCH_STATUS_SUCCESS; + } } @@ -670,18 +697,38 @@ static switch_status_t handle_msg_sendevent(listener_t *listener, int arity, ei_ if ((strlen(esname) && switch_event_create_subclass(&event, etype, esname) == SWITCH_STATUS_SUCCESS) || switch_event_create(&event, etype) == SWITCH_STATUS_SUCCESS) { char key[1024]; - char value[1024]; + char *value; + int type; + int size; int i = 0; switch_bool_t fail = SWITCH_FALSE; while (!ei_decode_tuple_header(buf->buff, &buf->index, &arity) && arity == 2) { i++; - if (ei_decode_string(buf->buff, &buf->index, key) || ei_decode_string(buf->buff, &buf->index, value)) { + + ei_get_type(buf->buff, &buf->index, &type, &size); + + if ((size > (sizeof(key) - 1)) || ei_decode_string(buf->buff, &buf->index, key)) { fail = SWITCH_TRUE; break; } - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, key, value); + ei_get_type(buf->buff, &buf->index, &type, &size); + value = malloc(size + 1); + + if (ei_decode_string(buf->buff, &buf->index, value)) { + fail = SWITCH_TRUE; + break; + } + + if (!fail && !strcmp(key, "body")) { + switch_safe_free(event->body); + event->body = value; + } else if (!fail) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, key, value); + } + + /* Do not free malloc here! The above commands utilize the raw allocated memory and skip any copying/duplication. Faster. */ } if (headerlength != i || fail) { @@ -715,16 +762,32 @@ static switch_status_t handle_msg_sendmsg(listener_t *listener, int arity, ei_x_ if (switch_event_create(&event, SWITCH_EVENT_SEND_MESSAGE) == SWITCH_STATUS_SUCCESS) { char key[1024]; - char value[1024]; + char *value; + int type; + int size; int i = 0; switch_bool_t fail = SWITCH_FALSE; + while (!ei_decode_tuple_header(buf->buff, &buf->index, &arity) && arity == 2) { i++; - if (ei_decode_string(buf->buff, &buf->index, key) || ei_decode_string(buf->buff, &buf->index, value)) { + ei_get_type(buf->buff, &buf->index, &type, &size); + + if ((size > (sizeof(key) - 1)) || ei_decode_string(buf->buff, &buf->index, key)) { fail = SWITCH_TRUE; break; } - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, key, value); + + ei_get_type(buf->buff, &buf->index, &type, &size); + value = malloc(size + 1); + + if (ei_decode_string(buf->buff, &buf->index, value)) { + fail = SWITCH_TRUE; + break; + } + + if (!fail) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, key, value); + } } if (headerlength != i || fail) { diff --git a/src/mod/event_handlers/mod_erlang_event/mod_erlang_event.c b/src/mod/event_handlers/mod_erlang_event/mod_erlang_event.c index 92e30ecfb6..2ed139862c 100644 --- a/src/mod/event_handlers/mod_erlang_event/mod_erlang_event.c +++ b/src/mod/event_handlers/mod_erlang_event/mod_erlang_event.c @@ -389,6 +389,7 @@ static switch_xml_t erlang_fetch(const char *sectionstr, const char *tag_name, c if (!ptr->listener) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "NULL pointer binding!\n"); + switch_thread_rwlock_unlock(globals.bindings_rwlock); goto cleanup; /* our pointer is trash */ } @@ -820,7 +821,7 @@ static void listener_main_loop(listener_t *listener) /* do we need the mutex when reading? */ /*switch_mutex_lock(listener->sock_mutex); */ - status = ei_xreceive_msg_tmo(listener->sockfd, &msg, &buf, 100); + status = ei_xreceive_msg_tmo(listener->sockfd, &msg, &buf, 500); /*switch_mutex_unlock(listener->sock_mutex); */ switch (status) { @@ -1818,7 +1819,7 @@ SWITCH_MODULE_RUNTIME_FUNCTION(mod_erlang_event_runtime) #else errno = 0; #endif - if ((clientfd = ei_accept_tmo(&ec, (int) listen_list.sockfd, &conn, 100)) == ERL_ERROR) { + if ((clientfd = ei_accept_tmo(&ec, (int) listen_list.sockfd, &conn, 500)) == ERL_ERROR) { if (prefs.done) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Shutting Down\n"); } else if (erl_errno == ETIMEDOUT) { From 473aab18eb65004d3326f444cb336c57d0a81950 Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Sat, 1 Oct 2011 13:27:27 +0200 Subject: [PATCH 011/429] ftmod_misdn: Add -D_BSD_SOURCE to get u_int and friends. Needed for the mISDN/mISDNif.h header structs, linux/types.h only provides them for kernel code: In file included from src/ftmod/ftmod_misdn/ftmod_misdn.c:51: /usr/include/mISDN/mISDNif.h:296: error: expected specifier-qualifier-list before 'u_int' /usr/include/mISDN/mISDNif.h:306: error: expected specifier-qualifier-list before 'u_int' /usr/include/mISDN/mISDNif.h:339: error: expected ')' before 'nr' ... Signed-off-by: Stefan Knoblich --- libs/freetdm/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am index 3a88b5f521..7370ae2c8d 100644 --- a/libs/freetdm/Makefile.am +++ b/libs/freetdm/Makefile.am @@ -251,7 +251,7 @@ endif if HAVE_MISDN mod_LTLIBRARIES += ftmod_misdn.la ftmod_misdn_la_SOURCES = $(SRC)/ftmod/ftmod_misdn/ftmod_misdn.c -ftmod_misdn_la_CFLAGS = $(FTDM_CFLAGS) $(AM_CFLAGS) $(MISDN_CFLAGS) +ftmod_misdn_la_CFLAGS = $(FTDM_CFLAGS) $(AM_CFLAGS) $(MISDN_CFLAGS) -D_BSD_SOURCE ftmod_misdn_la_LDFLAGS = -shared -module -avoid-version ftmod_misdn_la_LIBADD = libfreetdm.la endif From 7f17ad7f92d61cc28c2f54b06d1126ead32ba6bc Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Sat, 1 Oct 2011 14:05:14 +0200 Subject: [PATCH 012/429] ftmod_misdn: Change last commit to set -D_GNU_SOURCE instead. This fixes all build errors. (Stand-alone build of FreeTDM worked fine, for some i-don't-really-want-to-know strange reason). Signed-off-by: Stefan Knoblich --- libs/freetdm/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am index 7370ae2c8d..d96c3f1963 100644 --- a/libs/freetdm/Makefile.am +++ b/libs/freetdm/Makefile.am @@ -251,7 +251,7 @@ endif if HAVE_MISDN mod_LTLIBRARIES += ftmod_misdn.la ftmod_misdn_la_SOURCES = $(SRC)/ftmod/ftmod_misdn/ftmod_misdn.c -ftmod_misdn_la_CFLAGS = $(FTDM_CFLAGS) $(AM_CFLAGS) $(MISDN_CFLAGS) -D_BSD_SOURCE +ftmod_misdn_la_CFLAGS = $(FTDM_CFLAGS) $(AM_CFLAGS) $(MISDN_CFLAGS) -D_GNU_SOURCE ftmod_misdn_la_LDFLAGS = -shared -module -avoid-version ftmod_misdn_la_LIBADD = libfreetdm.la endif From 1936c2b08650dc796c5c39665ada482f545c1c49 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 3 Oct 2011 10:45:17 -0500 Subject: [PATCH 013/429] FS-3493 --resolve --- .../mod_conference/mod_conference.c | 96 +++++++++++++++---- 1 file changed, 80 insertions(+), 16 deletions(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 53d2f3f34a..fd6229b909 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -257,11 +257,13 @@ typedef struct conference_obj { char *maxmember_sound; uint32_t announce_count; char *pin; + char *mpin; char *pin_sound; char *bad_pin_sound; char *profile_name; char *domain; char *caller_controls; + char *moderator_controls; uint32_t flags; member_flag_t mflags; switch_call_cause_t bridge_hangup_cause; @@ -283,10 +285,13 @@ typedef struct conference_obj { switch_speech_handle_t *sh; switch_byte_t *not_talking_buf; uint32_t not_talking_buf_len; + int pin_retries; int comfort_noise_level; int is_recording; int record_count; int video_running; + int ivr_dtmf_timeout; + int ivr_input_timeout; uint32_t eflags; uint32_t verbose_events; int end_count; @@ -710,14 +715,15 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe switch_event_fire(&event); } + channel = switch_core_session_get_channel(member->session); + switch_channel_set_variable_printf(channel, "conference_member_id", "%d", member->id); + switch_channel_set_variable_printf(channel, "conference_moderator", "%s", switch_test_flag(member, MFLAG_MOD) ? "true" : "false"); + switch_channel_set_variable(channel, CONFERENCE_UUID_VARIABLE, conference->uuid_str); + if (switch_test_flag(conference, CFLAG_WAIT_MOD) && switch_test_flag(member, MFLAG_MOD)) { switch_clear_flag(conference, CFLAG_WAIT_MOD); } - channel = switch_core_session_get_channel(member->session); - switch_channel_set_variable_printf(channel, "conference_member_id", "%d", member->id); - switch_channel_set_variable(channel, CONFERENCE_UUID_VARIABLE, conference->uuid_str); - if (conference->count > 1) { if (conference->moh_sound && !switch_test_flag(conference, CFLAG_WAIT_MOD)) { /* stop MoH if any */ @@ -787,12 +793,17 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe switch_channel_clear_app_flag_key("conf_silent", channel, CONF_SILENT_REQ); switch_channel_set_app_flag_key("conf_silent", channel, CONF_SILENT_DONE); - switch_ivr_dmachine_create(&member->dmachine, "mod_conference", NULL, 500, 0, NULL, NULL, NULL); + switch_ivr_dmachine_create(&member->dmachine, "mod_conference", NULL, + conference->ivr_dtmf_timeout, conference->ivr_input_timeout, NULL, NULL, NULL); controls = switch_channel_get_variable(channel, "conference_controls"); if (zstr(controls)) { - controls = conference->caller_controls; + if (!switch_test_flag(member, MFLAG_MOD) || !conference->moderator_controls) { + controls = conference->caller_controls; + } else { + controls = conference->moderator_controls; + } } if (zstr(controls)) { @@ -5772,6 +5783,13 @@ static int setup_media(conference_member_t *member, conference_obj_t *conference } +#define validate_pin(buf, pin, mpin) \ + pin_valid = (pin && strcmp(buf, pin) == 0); \ + if (!pin_valid && mpin && strcmp(buf, mpin) == 0) { \ + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Moderator PIN found!\n"); \ + pin_valid = 1; \ + mpin_matched = 1; \ + } /* Application interface function that is called from the dialplan to join the channel to a conference */ SWITCH_STANDARD_APP(conference_function) { @@ -5791,10 +5809,12 @@ SWITCH_STANDARD_APP(conference_function) member_flag_t mflags = 0; switch_core_session_message_t msg = { 0 }; uint8_t rl = 0, isbr = 0; - char *dpin = NULL; + char *dpin = ""; + char *mdpin = ""; conf_xml_cfg_t xml_cfg = { 0 }; switch_event_t *params = NULL; int locked = 0; + int mpin_matched = 0; uint32_t *mid; if (!switch_channel_test_app_flag_key("conf_silent", channel, CONF_SILENT_DONE) && @@ -6019,16 +6039,18 @@ SWITCH_STANDARD_APP(conference_function) } rl++; - if (!dpin && conference->pin) { + if (zstr(dpin) && conference->pin) { dpin = conference->pin; } + if (zstr(mdpin) && conference->mpin) { + mdpin = conference->mpin; + } - /* if this is not an outbound call, deal with conference pins */ - if (enforce_security && !zstr(dpin)) { + if (enforce_security && (!zstr(dpin) || !zstr(mdpin))) { char pin_buf[80] = ""; - int pin_retries = 3; /* XXX - this should be configurable - i'm too lazy to do it right now... */ + int pin_retries = conference->pin_retries; int pin_valid = 0; switch_status_t status = SWITCH_STATUS_SUCCESS; char *supplied_pin_value; @@ -6048,7 +6070,8 @@ SWITCH_STANDARD_APP(conference_function) while (*supplied_pin_value != 0 && *supplied_pin_value != ';') { pin_buf[i++] = *supplied_pin_value++; } - pin_valid = (strcmp(pin_buf, dpin) == 0); + + validate_pin(pin_buf, dpin, mdpin); memset(pin_buf, 0, sizeof(pin_buf)); } @@ -6061,6 +6084,7 @@ SWITCH_STANDARD_APP(conference_function) } while (!pin_valid && pin_retries && status == SWITCH_STATUS_SUCCESS) { + int maxpin = strlen(dpin) > strlen(mdpin) ? strlen(dpin) : strlen(mdpin); switch_status_t pstatus = SWITCH_STATUS_FALSE; /* be friendly */ @@ -6079,19 +6103,21 @@ SWITCH_STANDARD_APP(conference_function) } /* wait for them if neccessary */ - if (strlen(pin_buf) < strlen(dpin)) { + if (strlen(pin_buf) < maxpin) { char *buf = pin_buf + strlen(pin_buf); char term = '\0'; status = switch_ivr_collect_digits_count(session, buf, - sizeof(pin_buf) - strlen(pin_buf), strlen(dpin) - strlen(pin_buf), "#", &term, 10000, 0, 0); + sizeof(pin_buf) - strlen(pin_buf), maxpin - strlen(pin_buf), "#", &term, 10000, 0, 0); if (status == SWITCH_STATUS_TIMEOUT) { status = SWITCH_STATUS_SUCCESS; } } - pin_valid = (status == SWITCH_STATUS_SUCCESS && strcmp(pin_buf, dpin) == 0); + if (status == SWITCH_STATUS_SUCCESS) { + validate_pin(pin_buf, dpin, mdpin); + } if (!pin_valid) { /* zero the collected pin */ memset(pin_buf, 0, sizeof(pin_buf)); @@ -6192,7 +6218,11 @@ SWITCH_STANDARD_APP(conference_function) mflags = conference->mflags; set_mflags(flags_str, &mflags); - switch_set_flag_locked((&member), MFLAG_RUNNING | mflags); + mflags |= MFLAG_RUNNING; + if (mpin_matched) { + mflags |= MFLAG_MOD; + } + switch_set_flag_locked((&member), mflags); if (mflags & MFLAG_MINTWO) { conference->min = 2; @@ -6476,6 +6506,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c char *is_unlocked_sound = NULL; char *kicked_sound = NULL; char *pin = NULL; + char *mpin = NULL; char *pin_sound = NULL; char *bad_pin_sound = NULL; char *energy_level = NULL; @@ -6483,6 +6514,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c char *caller_id_name = NULL; char *caller_id_number = NULL; char *caller_controls = NULL; + char *moderator_controls = NULL; char *member_flags = NULL; char *conference_flags = NULL; char *perpetual_sound = NULL; @@ -6492,6 +6524,9 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c char *maxmember_sound = NULL; uint32_t rate = 8000, interval = 20; int comfort_noise_level = 0; + int pin_retries = 3; + int ivr_dtmf_timeout = 500; + int ivr_input_timeout = 0; char *suppress_events = NULL; char *verbose_events = NULL; char *auto_record = NULL; @@ -6628,6 +6663,13 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c kicked_sound = val; } else if (!strcasecmp(var, "pin") && !zstr(val)) { pin = val; + } else if (!strcasecmp(var, "moderator-pin") && !zstr(val)) { + mpin = val; + } else if (!strcasecmp(var, "pin-retries") && !zstr(val)) { + int tmp = atoi(val); + if (tmp >= 0) { + pin_retries = tmp; + } } else if (!strcasecmp(var, "pin-sound") && !zstr(val)) { pin_sound = val; } else if (!strcasecmp(var, "bad-pin-sound") && !zstr(val)) { @@ -6642,6 +6684,20 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c caller_id_number = val; } else if (!strcasecmp(var, "caller-controls") && !zstr(val)) { caller_controls = val; + } else if (!strcasecmp(var, "ivr-dtmf-timeout") && !zstr(val)) { + ivr_dtmf_timeout = atoi(val); + if (ivr_dtmf_timeout < 500) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "not very smart value for ivr-dtmf-timeout found (%d), defaulting to 500ms\n", ivr_dtmf_timeout); + ivr_dtmf_timeout = 500; + } + } else if (!strcasecmp(var, "ivr-input-timeout") && !zstr(val)) { + ivr_input_timeout = atoi(val); + if (ivr_input_timeout != 0 && ivr_input_timeout < 500) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "not very smart value for ivr-input-timeout found (%d), defaulting to 500ms\n", ivr_input_timeout); + ivr_input_timeout = 5000; + } + } else if (!strcasecmp(var, "moderator-controls") && !zstr(val)) { + moderator_controls = val; } else if (!strcasecmp(var, "comfort-noise") && !zstr(val)) { int tmp; tmp = atoi(val); @@ -6730,9 +6786,11 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c } conference->comfort_noise_level = comfort_noise_level; + conference->pin_retries = pin_retries; conference->caller_id_name = switch_core_strdup(conference->pool, caller_id_name); conference->caller_id_number = switch_core_strdup(conference->pool, caller_id_number); conference->caller_controls = switch_core_strdup(conference->pool, caller_controls); + conference->moderator_controls = switch_core_strdup(conference->pool, moderator_controls); conference->run_time = switch_epoch_time_now(NULL); @@ -6813,6 +6871,10 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c conference->pin = switch_core_strdup(conference->pool, pin); } + if (!zstr(mpin)) { + conference->mpin = switch_core_strdup(conference->pool, mpin); + } + if (!zstr(alone_sound)) { conference->alone_sound = switch_core_strdup(conference->pool, alone_sound); } @@ -6866,6 +6928,8 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c conference->rate = rate; conference->interval = interval; + conference->ivr_dtmf_timeout = ivr_dtmf_timeout; + conference->ivr_input_timeout = ivr_input_timeout; conference->eflags = 0xFFFFFFFF; if (!zstr(suppress_events)) { From 9e3f0b6df52df48f5cde15001803596218f85ad4 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 3 Oct 2011 12:22:09 -0500 Subject: [PATCH 014/429] FS-3493 --resolve --- conf/autoload_configs/conference.conf.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/conf/autoload_configs/conference.conf.xml b/conf/autoload_configs/conference.conf.xml index d4e5aa60ee..8da8acab08 100644 --- a/conf/autoload_configs/conference.conf.xml +++ b/conf/autoload_configs/conference.conf.xml @@ -45,6 +45,8 @@ + + @@ -87,6 +89,9 @@ + + + @@ -100,6 +105,12 @@ + + + + + + From d39b7c6b1a7e05e103bbe2314b20df743c34c999 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 3 Oct 2011 12:23:36 -0500 Subject: [PATCH 015/429] FS-3593 --resolve --- src/mod/xml_int/mod_xml_cdr/mod_xml_cdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/xml_int/mod_xml_cdr/mod_xml_cdr.c b/src/mod/xml_int/mod_xml_cdr/mod_xml_cdr.c index 279bcda2d5..7cfd3592bb 100644 --- a/src/mod/xml_int/mod_xml_cdr/mod_xml_cdr.c +++ b/src/mod/xml_int/mod_xml_cdr/mod_xml_cdr.c @@ -360,7 +360,7 @@ static switch_status_t my_on_reporting(switch_core_session_t *session) curl_easy_perform(curl_handle); curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes); switch_safe_free(destUrl); - if (httpRes == 200) { + if (httpRes >= 200 && httpRes <= 299) { goto success; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Got error [%ld] posting to web server [%s]\n", From efeaf2069eb1f9c3a415b7a92db60a7a7ec1c5cb Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 3 Oct 2011 18:53:17 -0500 Subject: [PATCH 016/429] FS-3594 --resolve --- src/mod/endpoints/mod_sofia/mod_sofia.h | 1 + src/mod/endpoints/mod_sofia/sofia.c | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index 74de4aab3e..49732723bd 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -625,6 +625,7 @@ struct sofia_profile { uint32_t event_timeout; int watchdog_enabled; switch_mutex_t *gw_mutex; + uint32_t queued_events; }; struct private_object { diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 9601697ab4..775722f6ef 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -1137,7 +1137,8 @@ void sofia_process_dispatch_event(sofia_dispatch_event_t **dep) sofia_dispatch_event_t *de = *dep; nua_handle_t *nh = de->nh; nua_t *nua = de->nua; - + sofia_profile_t *profile = de->profile; + *dep = NULL; our_sofia_event_callback(de->data->e_event, de->data->e_status, de->data->e_phrase, de->nua, de->profile, @@ -1145,6 +1146,10 @@ void sofia_process_dispatch_event(sofia_dispatch_event_t **dep) nua_destroy_event(de->event); su_free(nh->nh_home, de); + + switch_mutex_lock(profile->flag_mutex); + profile->queued_events--; + switch_mutex_unlock(profile->flag_mutex); nua_handle_unref(nh); nua_stack_unref(nua); @@ -1245,6 +1250,10 @@ void sofia_event_callback(nua_event_t event, { sofia_dispatch_event_t *de; + switch_mutex_lock(profile->flag_mutex); + profile->queued_events++; + switch_mutex_unlock(profile->flag_mutex); + de = su_alloc(nh->nh_home, sizeof(*de)); memset(de, 0, sizeof(*de)); nua_save_event(nua, de->event); @@ -1972,7 +1981,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void nua_shutdown(profile->nua); sanity = 10; - while (!sofia_test_pflag(profile, PFLAG_SHUTDOWN)) { + while (!sofia_test_pflag(profile, PFLAG_SHUTDOWN) || profile->queued_events > 0) { su_root_step(profile->s_root, 1000); if (!--sanity) { break; From b0b5fb16dcf86d5e0de9b9926eb8c69f3d0ea1bc Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 4 Oct 2011 14:14:40 -0500 Subject: [PATCH 017/429] fix missing null dmachine check --- src/switch_ivr_async.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index c36d5e648c..4a78322a60 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -78,11 +78,13 @@ struct switch_ivr_dmachine { SWITCH_DECLARE(switch_digit_action_target_t) switch_ivr_dmachine_get_target(switch_ivr_dmachine_t *dmachine) { + switch_assert(dmachine); return dmachine->target; } SWITCH_DECLARE(void) switch_ivr_dmachine_set_target(switch_ivr_dmachine_t *dmachine, switch_digit_action_target_t target) { + switch_assert(dmachine); dmachine->target = target; } @@ -90,6 +92,7 @@ SWITCH_DECLARE(void) switch_ivr_dmachine_set_target(switch_ivr_dmachine_t *dmach SWITCH_DECLARE(void) switch_ivr_dmachine_set_match_callback(switch_ivr_dmachine_t *dmachine, switch_ivr_dmachine_callback_t match_callback) { + switch_assert(dmachine); dmachine->match_callback = match_callback; } @@ -97,6 +100,7 @@ SWITCH_DECLARE(void) switch_ivr_dmachine_set_match_callback(switch_ivr_dmachine_ SWITCH_DECLARE(void) switch_ivr_dmachine_set_nonmatch_callback(switch_ivr_dmachine_t *dmachine, switch_ivr_dmachine_callback_t nonmatch_callback) { + switch_assert(dmachine); dmachine->nonmatch_callback = nonmatch_callback; } From 65836742e80a7db883e75963e12f50b998552fff Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 4 Oct 2011 14:37:48 -0500 Subject: [PATCH 018/429] fix missing null dmachine check --- src/switch_core_session.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/switch_core_session.c b/src/switch_core_session.c index 3bccaf5b0a..99bf524324 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -43,7 +43,9 @@ SWITCH_DECLARE(void) switch_core_session_set_dmachine(switch_core_session_t *ses int i = (int) target; if (i == 0 || i == 1) { - switch_ivr_dmachine_set_target(dmachine, target); + if (dmachine) { + switch_ivr_dmachine_set_target(dmachine, target); + } session->dmachine[i] = dmachine; } } From 5d77e789b515f58fd5ec45782daed45dd25c229f Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 4 Oct 2011 16:40:58 -0500 Subject: [PATCH 019/429] remove waste flags from both conference and member and explicitly always send audio from conferences to avoid random interop issues and general discomfort these flags are now depricated --- .../mod_conference/mod_conference.c | 53 +++++++------------ 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index fd6229b909..1f76f25833 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -132,7 +132,7 @@ typedef enum { MFLAG_ITHREAD = (1 << 4), MFLAG_NOCHANNEL = (1 << 5), MFLAG_INTREE = (1 << 6), - MFLAG_WASTE_BANDWIDTH = (1 << 7), + MFLAG_WASTE_FLAG = (1 << 7), MFLAG_FLUSH_BUFFER = (1 << 8), MFLAG_ENDCONF = (1 << 9), MFLAG_HAS_AUDIO = (1 << 10), @@ -159,7 +159,7 @@ typedef enum { CFLAG_BRIDGE_TO = (1 << 6), CFLAG_WAIT_MOD = (1 << 7), CFLAG_VID_FLOOR = (1 << 8), - CFLAG_WASTE_BANDWIDTH = (1 << 9), + CFLAG_WASTE_FLAG = (1 << 9), CFLAG_OUTCALL = (1 << 10), CFLAG_INHASH = (1 << 11), CFLAG_EXIT_SOUND = (1 << 12), @@ -1344,17 +1344,6 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v } } - if (switch_test_flag(conference, CFLAG_WASTE_BANDWIDTH) && !has_file_data) { - file_sample_len = bytes / 2; - - if (conference->comfort_noise_level) { - switch_generate_sln_silence((int16_t *) file_frame, file_sample_len, conference->comfort_noise_level); - } else { - memset(file_frame, 255, bytes); - } - has_file_data = 1; - } - if (ready || has_file_data) { /* Use more bits in the main_frame to preserve the exact sum of the audio samples. */ int main_frame[SWITCH_RECOMMENDED_BUFFER_SIZE / 2] = { 0 }; @@ -1427,8 +1416,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v continue; } - if (!switch_test_flag(omember, MFLAG_CAN_HEAR) && !switch_test_flag(omember, MFLAG_WASTE_BANDWIDTH) - && !switch_test_flag(conference, CFLAG_WASTE_BANDWIDTH)) { + if (!switch_test_flag(omember, MFLAG_CAN_HEAR)) { continue; } @@ -2817,22 +2805,21 @@ static void conference_loop_output(conference_member_t *member) switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); break; } - } else if (!switch_test_flag(member->conference, CFLAG_WASTE_BANDWIDTH)) { - if (switch_test_flag(member, MFLAG_WASTE_BANDWIDTH)) { - if (member->conference->comfort_noise_level) { - switch_generate_sln_silence(write_frame.data, samples, member->conference->comfort_noise_level); - } else { - memset(write_frame.data, 255, bytes); - } - - write_frame.datalen = bytes; - write_frame.samples = samples; - write_frame.timestamp = timer.samplecount; - - if (switch_core_session_write_frame(member->session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { - switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); - break; - } + } else { + + if (member->conference->comfort_noise_level) { + switch_generate_sln_silence(write_frame.data, samples, member->conference->comfort_noise_level); + } else { + memset(write_frame.data, 255, bytes); + } + + write_frame.datalen = bytes; + write_frame.samples = samples; + write_frame.timestamp = timer.samplecount; + + if (switch_core_session_write_frame(member->session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + break; } } @@ -5523,8 +5510,6 @@ static void set_mflags(const char *flags, member_flag_t *f) *f &= ~MFLAG_TALKING; } else if (!strcasecmp(argv[i], "deaf")) { *f &= ~MFLAG_CAN_HEAR; - } else if (!strcasecmp(argv[i], "waste")) { - *f |= MFLAG_WASTE_BANDWIDTH; } else if (!strcasecmp(argv[i], "mute-detect")) { *f |= MFLAG_MUTE_DETECT; } else if (!strcasecmp(argv[i], "dist-dtmf")) { @@ -5569,8 +5554,6 @@ static void set_cflags(const char *flags, uint32_t *f) *f |= CFLAG_WAIT_MOD; } else if (!strcasecmp(argv[i], "video-floor-only")) { *f |= CFLAG_VID_FLOOR; - } else if (!strcasecmp(argv[i], "waste-bandwidth")) { - *f |= CFLAG_WASTE_BANDWIDTH; } else if (!strcasecmp(argv[i], "video-bridge")) { *f |= CFLAG_VIDEO_BRIDGE; } From 8e2793b45ecf86b77b593b8b22130753e5d826c4 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 4 Oct 2011 18:02:34 -0500 Subject: [PATCH 020/429] another regression stemming from the series of commits to make stupid sonus happy --- src/switch_rtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 24e8c3346d..434c915d0b 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -3666,7 +3666,7 @@ static int rtp_common_write(switch_rtp_t *rtp_session, rtp_session->send_msg.header.ts = htonl(rtp_session->ts); - if ((rtp_session->ts > (rtp_session->last_write_ts + (rtp_session->samples_per_interval * 10))) + if ((rtp_session->ts != RTP_TS_RESET && rtp_session->ts > (rtp_session->last_write_ts + (rtp_session->samples_per_interval * 10))) || rtp_session->ts == rtp_session->samples_per_interval) { m++; } From f04fd38c3e695efc17ca79efb2337763113fc87e Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 4 Oct 2011 17:46:49 -0500 Subject: [PATCH 021/429] FS-3594 if this does not work you will need to give me access to your box --- src/mod/endpoints/mod_sofia/sofia.c | 2 +- src/mod/endpoints/mod_sofia/sofia_reg.c | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 775722f6ef..c2b344cdf7 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -808,7 +808,7 @@ static void our_sofia_event_callback(nua_event_t event, int check_destroy = 1; - if (sofia_private && sofia_private->de) { + if (sofia_private && sofia_private->is_call && sofia_private->de) { sofia_dispatch_event_t *qde = sofia_private->de; sofia_private->de = NULL; sofia_process_dispatch_event(&qde); diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index a65c205b8f..bccae9293f 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -68,6 +68,10 @@ static void sofia_reg_new_handle(sofia_gateway_t *gateway_ptr, int attach) static void sofia_reg_kill_reg(sofia_gateway_t *gateway_ptr) { + if (gateway_ptr->nh) { + nua_handle_bind(gateway_ptr->nh, NULL); + } + if (gateway_ptr->state != REG_STATE_REGED) { if (gateway_ptr->nh) { nua_handle_destroy(gateway_ptr->nh); @@ -76,19 +80,10 @@ static void sofia_reg_kill_reg(sofia_gateway_t *gateway_ptr) return; } - /* - if (!gateway_ptr->nh) { - sofia_reg_new_handle(gateway_ptr, SWITCH_FALSE); - } - */ - if (gateway_ptr->nh) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "UN-Registering %s\n", gateway_ptr->name); nua_unregister(gateway_ptr->nh, NUTAG_URL(gateway_ptr->register_url), NUTAG_REGISTRAR(gateway_ptr->register_proxy), TAG_END()); } - - - } void sofia_reg_fire_custom_gateway_state_event(sofia_gateway_t *gateway, int status, const char *phrase) @@ -114,6 +109,10 @@ void sofia_reg_unregister(sofia_profile_t *profile) switch_mutex_lock(mod_sofia_globals.hash_mutex); for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) { + if (gateway_ptr->nh) { + nua_handle_bind(gateway_ptr->nh, NULL); + } + if (gateway_ptr->sofia_private) { sofia_private_free(gateway_ptr->sofia_private); } From f4fa0c0efe023a6d7b5ead42f4f7a2b1a870d63b Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 4 Oct 2011 20:17:05 -0500 Subject: [PATCH 022/429] OMFG really? I had this right then i comitted it wrong.... --- src/switch_rtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 434c915d0b..c8a5777c3e 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -3666,7 +3666,7 @@ static int rtp_common_write(switch_rtp_t *rtp_session, rtp_session->send_msg.header.ts = htonl(rtp_session->ts); - if ((rtp_session->ts != RTP_TS_RESET && rtp_session->ts > (rtp_session->last_write_ts + (rtp_session->samples_per_interval * 10))) + if ((rtp_session->last_write_ts != RTP_TS_RESET && rtp_session->ts > (rtp_session->last_write_ts + (rtp_session->samples_per_interval * 10))) || rtp_session->ts == rtp_session->samples_per_interval) { m++; } From 216dce139c613aba2691c2579180c93174e509d8 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 5 Oct 2011 12:49:47 -0500 Subject: [PATCH 023/429] revert this it might annoy ppl with no transcoding --- src/switch_ivr.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 85ffa00b8a..19d3c102a1 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -138,7 +138,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, unsigned char *abuf = NULL; switch_codec_implementation_t imp = { 0 }; switch_codec_t codec = { 0 }; - int sval = -1; + int sval = 0; const char *var; /* @@ -192,7 +192,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, write_frame.codec = &codec; switch_zmalloc(abuf, SWITCH_RECOMMENDED_BUFFER_SIZE); - memset(abuf, 255, SWITCH_RECOMMENDED_BUFFER_SIZE); write_frame.data = abuf; write_frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE; write_frame.datalen = imp.decoded_bytes_per_packet; @@ -295,9 +294,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, } if (sval && write_frame.datalen) { - if (sval > 0) { - switch_generate_sln_silence((int16_t *) write_frame.data, write_frame.samples, sval); - } + switch_generate_sln_silence((int16_t *) write_frame.data, write_frame.samples, sval); switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0); } else { switch_core_session_write_frame(session, &cng_frame, SWITCH_IO_FLAG_NONE, 0); From 86efff6c1fcba926f3c2ee889da57031e180d9ab Mon Sep 17 00:00:00 2001 From: Brian West Date: Wed, 5 Oct 2011 20:39:08 -0500 Subject: [PATCH 024/429] missed a check_Decode --- src/mod/endpoints/mod_sofia/sofia.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index c2b344cdf7..f3ab8c48ef 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -8129,6 +8129,7 @@ void sofia_info_send_sipfrag(switch_core_session_t *aleg, switch_core_session_t if (zstr(acp->caller_id_name)) { snprintf(message, sizeof(message), "P-Asserted-Identity: \"%s\" <%s>", acp->caller_id_number, acp->caller_id_number); } else { + check_decode(acp->caller_id_name, aleg); snprintf(message, sizeof(message), "P-Asserted-Identity: \"%s\" <%s>", acp->caller_id_name, acp->caller_id_number); } From 7d6747c9ef4e8e98dbf0b9ee36c407770bec4eae Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 5 Oct 2011 13:54:47 -0500 Subject: [PATCH 025/429] update --- scripts/perl/tonegen/rtttl2tgml.pl | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/scripts/perl/tonegen/rtttl2tgml.pl b/scripts/perl/tonegen/rtttl2tgml.pl index 820e36cc4e..a8a3ed0632 100644 --- a/scripts/perl/tonegen/rtttl2tgml.pl +++ b/scripts/perl/tonegen/rtttl2tgml.pl @@ -18,8 +18,19 @@ our $NOTES = { my $file = shift or die "no file\n"; +my $rtttl; +my $cr = "\n"; -my $rtttl = `cat $file`; +if ($file eq "-nocr") { + $cr = ""; + $file = shift; +} + +if ($file eq "-") { + $rtttl = ; +} else { + $rtttl = `cat $file`; +} $rtttl =~ s/\n//g; @@ -53,7 +64,7 @@ foreach (@{$all}) { #print STDERR "$_->[0] $_->[1] $_->[2] $_->[3]\n"; - print "%($ms, 0, $NOTES->{$_->[1]}->[$_->[2]]);\n"; + print "%($ms,0,$NOTES->{$_->[1]}->[$_->[2]]);" . $cr; } From b2d2353ce915fabe0729218adfd266a6dad388ed Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 5 Oct 2011 13:55:16 -0500 Subject: [PATCH 026/429] FS-3596 --resolve --- src/mod/endpoints/mod_sofia/sofia_reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index bccae9293f..4978815f98 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -72,7 +72,7 @@ static void sofia_reg_kill_reg(sofia_gateway_t *gateway_ptr) nua_handle_bind(gateway_ptr->nh, NULL); } - if (gateway_ptr->state != REG_STATE_REGED) { + if (gateway_ptr->state != REG_STATE_REGED && gateway_ptr->state != REG_STATE_UNREGISTER) { if (gateway_ptr->nh) { nua_handle_destroy(gateway_ptr->nh); gateway_ptr->nh = NULL; From 624a292123025926b868dd2eb49645e824518e3d Mon Sep 17 00:00:00 2001 From: Joao Mesquita Date: Wed, 5 Oct 2011 19:02:02 -0300 Subject: [PATCH 027/429] New feature: param disallow-multiple-registration on user directory logs out all other users on other sessions with the same user:domain pair. --- src/mod/endpoints/mod_rtmp/rtmp.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/mod/endpoints/mod_rtmp/rtmp.c b/src/mod/endpoints/mod_rtmp/rtmp.c index 10a48b3748..cb104c3908 100644 --- a/src/mod/endpoints/mod_rtmp/rtmp.c +++ b/src/mod/endpoints/mod_rtmp/rtmp.c @@ -23,6 +23,7 @@ * Contributor(s): * * Mathieu Rene + * Joao Mesquita * * rtmp.c -- RTMP Protocol Handler * @@ -188,6 +189,7 @@ switch_status_t rtmp_check_auth(rtmp_session_t *rsession, const char *user, cons switch_xml_t xml = NULL, x_param, x_params; switch_bool_t allow_empty_password = SWITCH_FALSE; const char *passwd = NULL; + switch_bool_t disallow_multiple_registration = SWITCH_FALSE; switch_event_t *locate_params; switch_event_create(&locate_params, SWITCH_EVENT_GENERAL); @@ -211,6 +213,9 @@ switch_status_t rtmp_check_auth(rtmp_session_t *rsession, const char *user, cons if (!strcasecmp(var, "allow-empty-password")) { allow_empty_password = switch_true(val); } + if (!strcasecmp(var, "disallow-multiple-registration")) { + disallow_multiple_registration = switch_true(val); + } } } @@ -231,6 +236,28 @@ switch_status_t rtmp_check_auth(rtmp_session_t *rsession, const char *user, cons } else { switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_WARNING, "Authentication failed for %s@%s\n", user, domain); } + + if (disallow_multiple_registration) { + switch_hash_index_t *hi; + switch_thread_rwlock_rdlock(rsession->profile->session_rwlock); + for (hi = switch_hash_first(NULL, rsession->profile->session_hash); hi; hi = switch_hash_next(hi)) { + void *val; + const void *key; + switch_ssize_t keylen; + rtmp_session_t *item; + switch_hash_this(hi, &key, &keylen, &val); + + item = (rtmp_session_t *)val; + if (rtmp_session_check_user(item, user, domain) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_INFO, "Logging out %s@%s on RTMP sesssion [%s]\n", user, domain, item->uuid); + if (rtmp_session_logout(item, user, domain) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_ERROR, "Unable to logout %s@%s on RTMP sesssion [%s]\n", user, domain, item->uuid); + } + } + + } + switch_thread_rwlock_unlock(rsession->profile->session_rwlock); + } done: if (xml) { From 4893acdb167afc9ad444dbb60edca3cd2c8c82a6 Mon Sep 17 00:00:00 2001 From: Brian West Date: Wed, 5 Oct 2011 22:54:31 -0500 Subject: [PATCH 028/429] missed yet another one --- src/mod/endpoints/mod_sofia/mod_sofia.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 0ab0b152ff..7db01e88b7 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -1984,6 +1984,9 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi const char *ua = switch_channel_get_variable(tech_pvt->channel, "sip_user_agent"); switch_event_t *event; + check_decode(name, tech_pvt->session); + + if (zstr(number)) { number = tech_pvt->caller_profile->destination_number; } From 5a96ebdaa3d91ae89900ab553211512fa761a932 Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Thu, 6 Oct 2011 11:08:42 +0200 Subject: [PATCH 029/429] FreeTDM: Escape $(srcdir) in configure.ac variable Silencing a "configure:xxxx: srcdir: command not found" error message. Signed-off-by: Stefan Knoblich --- libs/freetdm/configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/freetdm/configure.ac b/libs/freetdm/configure.ac index a69618ba6f..f59cab95d6 100644 --- a/libs/freetdm/configure.ac +++ b/libs/freetdm/configure.ac @@ -54,7 +54,7 @@ fi AC_SUBST([confdir]) -DEFAULT_INCLUDES="-I. -I./src/include -I$(srcdir)" +DEFAULT_INCLUDES="-I. -I./src/include -I\$(srcdir)" AC_SUBST([DEFAULT_INCLUDES]) # Where to install the modules From 1f2bc29f603ce9a683fd72e93125d3c8f287d5a6 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Thu, 6 Oct 2011 09:33:57 -0500 Subject: [PATCH 030/429] FS-3595 --resolve --- conf/dialplan/default.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/dialplan/default.xml b/conf/dialplan/default.xml index 6d46eb069c..b18faad846 100644 --- a/conf/dialplan/default.xml +++ b/conf/dialplan/default.xml @@ -260,9 +260,9 @@ + - From 915b96ea8abcc1d3f50023d7068138f889707c3d Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 6 Oct 2011 09:47:34 -0500 Subject: [PATCH 031/429] update --- scripts/perl/tonegen/rtttl2tgml.pl | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/scripts/perl/tonegen/rtttl2tgml.pl b/scripts/perl/tonegen/rtttl2tgml.pl index a8a3ed0632..39d4ab715a 100644 --- a/scripts/perl/tonegen/rtttl2tgml.pl +++ b/scripts/perl/tonegen/rtttl2tgml.pl @@ -56,16 +56,11 @@ if ($r->has_errors()) { $all = $r->get_notes(); -$ms_per_beat = int (60000 / $r->get_bpm()); +$ms_per_beat = int (6000 / $r->get_bpm()); foreach (@{$all}) { - - my $ms = $ms_per_beat * (1 / $_->[0]) * 4; - - #print STDERR "$_->[0] $_->[1] $_->[2] $_->[3]\n"; - + my $ms = ($ms_per_beat * $_->[2]); print "%($ms,0,$NOTES->{$_->[1]}->[$_->[2]]);" . $cr; - } From d2710422ab1005f8800808bc0ef306ffa54ce011 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 6 Oct 2011 10:12:36 -0500 Subject: [PATCH 032/429] fix issue where clearing a single realm does not completely clear --- src/include/switch_ivr.h | 1 + .../applications/mod_dptools/mod_dptools.c | 1 + src/switch_ivr_async.c | 23 ++++++++++++++++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 901519f7dd..38a9249c79 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -896,6 +896,7 @@ SWITCH_DECLARE(switch_bool_t) switch_ivr_uuid_exists(const char *uuid); +SWITCH_DECLARE(const char *) switch_ivr_dmachine_get_name(switch_ivr_dmachine_t *dmachine); SWITCH_DECLARE(void) switch_ivr_dmachine_set_match_callback(switch_ivr_dmachine_t *dmachine, switch_ivr_dmachine_callback_t match_callback); SWITCH_DECLARE(void) switch_ivr_dmachine_set_nonmatch_callback(switch_ivr_dmachine_t *dmachine, switch_ivr_dmachine_callback_t nonmatch_callback); SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_create(switch_ivr_dmachine_t **dmachine_p, diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index c088d81111..d0a6337166 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -247,6 +247,7 @@ SWITCH_STANDARD_APP(clear_digit_action_function) if ((dmachine = switch_core_session_get_dmachine(session, target))) { if (zstr(realm) || !strcasecmp(realm, "all")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Digit parser %s: Clearing all realms\n", switch_ivr_dmachine_get_name(dmachine)); switch_core_session_set_dmachine(session, NULL, target); switch_ivr_dmachine_destroy(&dmachine); } else { diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 4a78322a60..1a0a341c9a 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -105,6 +105,11 @@ SWITCH_DECLARE(void) switch_ivr_dmachine_set_nonmatch_callback(switch_ivr_dmachi } +SWITCH_DECLARE(const char *) switch_ivr_dmachine_get_name(switch_ivr_dmachine_t *dmachine) +{ + return (const char *) dmachine->name; +} + SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_create(switch_ivr_dmachine_t **dmachine_p, const char *name, switch_memory_pool_t *pool, @@ -179,23 +184,35 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_set_realm(switch_ivr_dmachin dm_binding_head_t *headp = switch_core_hash_find(dmachine->binding_hash, realm); if (headp) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Digit parser %s: Setting realm to %s\n", dmachine->name, realm); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Digit parser %s: Setting realm to '%s'\n", dmachine->name, realm); dmachine->realm = headp; return SWITCH_STATUS_SUCCESS; } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Digit parser %s: Error Setting realm to %s\n", dmachine->name, realm); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Digit parser %s: Error Setting realm to '%s'\n", dmachine->name, realm); return SWITCH_STATUS_FALSE; } SWITCH_DECLARE(switch_status_t) switch_ivr_dmachine_clear_realm(switch_ivr_dmachine_t *dmachine, const char *realm) { + dm_binding_head_t *headp; + if (zstr(realm)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Digit parser %s: Error unknown realm: %s\n", dmachine->name, realm); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Digit parser %s: Error unknown realm: '%s'\n", dmachine->name, realm); return SWITCH_STATUS_FALSE; } + headp = switch_core_hash_find(dmachine->binding_hash, realm); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Digit parser %s: Clearing realm '%s'\n", dmachine->name, realm); + + if (headp == dmachine->realm) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Digit parser %s: '%s' was the active realm, no realm currently selected.\n", dmachine->name, realm); + dmachine->realm = NULL; + } + /* pool alloc'd just ditch it and it will give back the memory when we destroy ourselves */ switch_core_hash_delete(dmachine->binding_hash, realm); return SWITCH_STATUS_SUCCESS; From c51acfcc1b2da332ef5c68fd6baef62fc40a776a Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 6 Oct 2011 11:25:03 -0500 Subject: [PATCH 033/429] FS-3597 --resolve --- src/mod/applications/mod_http_cache/Makefile | 5 + .../mod_http_cache/http_cache.conf.xml | 8 + .../mod_http_cache/mod_http_cache.c | 1060 +++++++++++++++++ 3 files changed, 1073 insertions(+) create mode 100644 src/mod/applications/mod_http_cache/Makefile create mode 100644 src/mod/applications/mod_http_cache/http_cache.conf.xml create mode 100644 src/mod/applications/mod_http_cache/mod_http_cache.c diff --git a/src/mod/applications/mod_http_cache/Makefile b/src/mod/applications/mod_http_cache/Makefile new file mode 100644 index 0000000000..264f030dd4 --- /dev/null +++ b/src/mod/applications/mod_http_cache/Makefile @@ -0,0 +1,5 @@ +BASE=../../../.. +WANT_CURL=yes + +include $(BASE)/build/modmake.rules + diff --git a/src/mod/applications/mod_http_cache/http_cache.conf.xml b/src/mod/applications/mod_http_cache/http_cache.conf.xml new file mode 100644 index 0000000000..4150d6472e --- /dev/null +++ b/src/mod/applications/mod_http_cache/http_cache.conf.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/mod/applications/mod_http_cache/mod_http_cache.c b/src/mod/applications/mod_http_cache/mod_http_cache.c new file mode 100644 index 0000000000..23cb01a41d --- /dev/null +++ b/src/mod/applications/mod_http_cache/mod_http_cache.c @@ -0,0 +1,1060 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Chris Rienzo + * + * mod_http_cache.c -- HTTP GET with caching + * -- designed for downloading audio files from a webserver for playback + * + */ +#include +#include + +/* Defines module interface to FreeSWITCH */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_http_cache_shutdown); +SWITCH_MODULE_LOAD_FUNCTION(mod_http_cache_load); +SWITCH_MODULE_DEFINITION(mod_http_cache, mod_http_cache_load, mod_http_cache_shutdown, NULL); +SWITCH_STANDARD_API(http_cache_get); +SWITCH_STANDARD_API(http_cache_put); +SWITCH_STANDARD_API(http_cache_tryget); +SWITCH_STANDARD_API(http_cache_clear); + +#define DOWNLOAD_NEEDED "download" + +typedef struct url_cache url_cache_t; + +/** + * status if the cache entry + */ +enum cached_url_status { + /** downloading */ + CACHED_URL_RX_IN_PROGRESS, + /** marked for removal */ + CACHED_URL_REMOVE, + /** available */ + CACHED_URL_AVAILABLE +}; +typedef enum cached_url_status cached_url_status_t; + +/** + * Cached URL information + */ +struct cached_url { + /** The URL that was cached */ + char *url; + /** The path and name of the cached URL */ + char *filename; + /** The size of the cached URL, in bytes */ + size_t size; + /** URL use flag */ + int used; + /** Status of this entry */ + cached_url_status_t status; + /** Number of sessions waiting for this URL */ + int waiters; + /** time when downloaded */ + switch_time_t download_time; + /** nanoseconds until stale */ + switch_time_t max_age; +}; +typedef struct cached_url cached_url_t; + +static cached_url_t *cached_url_create(url_cache_t *cache, const char *url); +static void cached_url_destroy(cached_url_t *url, switch_memory_pool_t *pool); + +/** + * Data for write_file_callback() + */ +struct http_get_data { + /** File descriptor for the cached URL */ + int fd; + /** The cached URL data */ + cached_url_t *url; +}; +typedef struct http_get_data http_get_data_t; + +static switch_status_t http_get(cached_url_t *url, switch_core_session_t *session); +static size_t get_file_callback(void *ptr, size_t size, size_t nmemb, void *get); +static size_t get_header_callback(void *ptr, size_t size, size_t nmemb, void *url); +static void process_cache_control_header(cached_url_t *url, char *data); + +static switch_status_t http_put(switch_core_session_t *session, const char *url, const char *filename); + +/** + * Queue used for clock cache replacement algorithm. This + * queue has been simplified since replacement only happens + * once it is filled. + */ +struct simple_queue { + /** The queue data */ + void **data; + /** queue bounds */ + size_t max_size; + /** queue size */ + size_t size; + /** Current index */ + int pos; +}; +typedef struct simple_queue simple_queue_t; + + +/** + * The cache + */ +struct url_cache { + /** The maximum number of URLs to cache */ + int max_url; + /** The maximum size of this cache, in bytes */ + size_t max_size; + /** The default time to allow a cached URL to live, if none is specified */ + switch_time_t default_max_age; + /** The current size of this cache, in bytes */ + size_t size; + /** The location of the cache in the filesystem */ + char *location; + /** Cache mapped by URL */ + switch_hash_t *map; + /** Cached URLs queued for replacement */ + simple_queue_t queue; + /** Synchronizes access to cache */ + switch_mutex_t *mutex; + /** Memory pool */ + switch_memory_pool_t *pool; + /** Number of cache hits */ + int hits; + /** Number of cache misses */ + int misses; + /** Number of cache errors */ + int errors; +}; +static url_cache_t gcache; + +static char *url_cache_get(url_cache_t *cache, switch_core_session_t *session, const char *url, int download, switch_memory_pool_t *pool); +static switch_status_t url_cache_add(url_cache_t *cache, switch_core_session_t *session, cached_url_t *url); +static void url_cache_remove(url_cache_t *cache, switch_core_session_t *session, cached_url_t *url); +static void url_cache_remove_soft(url_cache_t *cache, switch_core_session_t *session, cached_url_t *url); +static switch_status_t url_cache_replace(url_cache_t *cache, switch_core_session_t *session); +static void url_cache_lock(url_cache_t *cache, switch_core_session_t *session); +static void url_cache_unlock(url_cache_t *cache, switch_core_session_t *session); +static void url_cache_clear(url_cache_t *cache, switch_core_session_t *session); + +/** + * Put a file to the URL + * @param session the (optional) session uploading the file + * @param url The URL + * @param filename The file to upload + * @return SWITCH_STATUS_SUCCESS if successful + */ +static switch_status_t http_put(switch_core_session_t *session, const char *url, const char *filename) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + CURL *curl_handle = NULL; + long httpRes = 0; + struct stat file_info = {0}; + FILE *file_to_put = NULL; + int fd; + + /* open file and get the file size */ + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "opening %s for upload to %s\n", filename, url); + fd = open(filename, O_RDONLY); + if (fd == -1) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "open() error: %s\n", strerror(errno)); + status = SWITCH_STATUS_FALSE; + goto done; + } + if (fstat(fd, &file_info) == -1) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "fstat() error: %s\n", strerror(errno)); + } + close(fd); + + /* libcurl requires FILE* */ + file_to_put = fopen(filename, "rb"); + if (!file_to_put) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "fopen() error: %s\n", strerror(errno)); + status = SWITCH_STATUS_FALSE; + goto done; + } + + curl_handle = curl_easy_init(); + if (!curl_handle) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "curl_easy_init() failure\n"); + status = SWITCH_STATUS_FALSE; + goto done; + } + curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1); + curl_easy_setopt(curl_handle, CURLOPT_PUT, 1); + curl_easy_setopt(curl_handle, CURLOPT_URL, url); + curl_easy_setopt(curl_handle, CURLOPT_READDATA, file_to_put); + curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); + curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 10); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-http-cache/1.0"); + curl_easy_perform(curl_handle); + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes); + curl_easy_cleanup(curl_handle); + + if (httpRes == 200 || httpRes == 201 || httpRes == 204) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s saved to %s\n", filename, url); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Received HTTP error %ld trying to save %s to %s\n", httpRes, filename, url); + status = SWITCH_STATUS_GENERR; + } + +done: + if (file_to_put) { + fclose(file_to_put); + } + + return status; +} + +/** + * Called by libcurl to write result of HTTP GET to a file + * @param ptr The data to write + * @param size The size of the data element to write + * @param nmemb The number of elements to write + * @param get Info about this current GET request + * @return bytes processed + */ +static size_t get_file_callback(void *ptr, size_t size, size_t nmemb, void *get) +{ + size_t realsize = (size * nmemb); + http_get_data_t *get_data = get; + ssize_t bytes_written = write(get_data->fd, ptr, realsize); + size_t result = 0; + if (bytes_written == -1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "write(): %s\n", strerror(errno)); + } else { + if (bytes_written != realsize) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "write(): short write!\n"); + } + get_data->url->size += bytes_written; + result = bytes_written; + } + + return result; +} +/** + * trim whitespace characters from string + * @param str the string to trim + * @return the trimmed string + */ +static char *trim(char *str) +{ + size_t len; + + if (zstr(str)) { + return str; + } + len = strlen(str); + + /* strip whitespace from front */ + for (int i = 0; i < len; i++) { + if (!isspace(str[i])) { + str = &str[i]; + len -= i; + break; + } + } + if (zstr(str)) { + return str; + } + + /* strip whitespace from end */ + for (int i = len - 1; i >= 0; i--) { + if (!isspace(str[i])) { + break; + } + str[i] = '\0'; + } + return str; +} + +#define MAX_AGE "max-age=" +/** + * cache-control: max-age=123456 + * Only support max-age. All other params are ignored. + */ +static void process_cache_control_header(cached_url_t *url, char *data) +{ + char *max_age_str; + int max_age; + + /* trim whitespace and check if empty */ + data = trim(data); + if (zstr(data)) { + return; + } + + /* find max-age param */ + max_age_str = strcasestr(data, MAX_AGE); + if (zstr(max_age_str)) { + return; + } + + /* find max-age value */ + max_age_str = max_age_str + (sizeof(MAX_AGE) - 1); + if (zstr(max_age_str)) { + return; + } + for (int i = 0; i < strlen(max_age_str); i++) { + if (!isdigit(max_age_str[i])) { + max_age_str[i] = '\0'; + break; + } + } + if (zstr(max_age_str)) { + return; + } + max_age = atoi(max_age_str); + + if (max_age < 0) { + return; + } + + url->max_age = switch_time_now() + (1000 * 1000 * max_age); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "setting max age to %u seconds from now\n", max_age); +} + +#define CACHE_CONTROL_HEADER "cache-control:" +#define CACHE_CONTROL_HEADER_LEN (sizeof(CACHE_CONTROL_HEADER) - 1) +/** + * Called by libcurl to process headers from HTTP GET response + * @param ptr the header data + * @param size The size of the data element to write + * @param nmemb The number of elements to write + * @param get get data + * @return bytes processed + */ +static size_t get_header_callback(void *ptr, size_t size, size_t nmemb, void *get) +{ + size_t realsize = (size * nmemb); + cached_url_t *url = get; + char *header = NULL; + + /* validate length... Apache and IIS won't send a header larger than 16 KB */ + if (realsize == 0 || realsize > 1024 * 16) { + return realsize; + } + + /* get the header, adding NULL terminator if there isn't one */ + switch_zmalloc(header, realsize + 1); + strncpy(header, (char *)ptr, realsize); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s", header); + + /* check which header this is and process it */ + if (!strncasecmp(CACHE_CONTROL_HEADER, header, CACHE_CONTROL_HEADER_LEN)) { + process_cache_control_header(url, header + CACHE_CONTROL_HEADER_LEN); + } + + switch_safe_free(header); + return realsize; +} + +/** + * Get exclusive access to the cache + * @param cache The cache + * @param session The session acquiring the cache + */ +static void url_cache_lock(url_cache_t *cache, switch_core_session_t *session) +{ + switch_mutex_lock(cache->mutex); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Locked cache\n"); +} + +/** + * Relinquish exclusive access to the cache + * @param cache The cache + * @param session The session relinquishing the cache + */ +static void url_cache_unlock(url_cache_t *cache, switch_core_session_t *session) +{ + switch_mutex_unlock(cache->mutex); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Unlocked cache\n"); +} + +/** + * Empties the cache + */ +static void url_cache_clear(url_cache_t *cache, switch_core_session_t *session) +{ + url_cache_lock(cache, session); + + // remove each cached URL from the hash and the queue + for (int i = 0; i < cache->queue.max_size; i++) { + cached_url_t *url = cache->queue.data[i]; + if (url) { + switch_core_hash_delete(cache->map, url->url); + cached_url_destroy(url, cache->pool); + cache->queue.data[i] = NULL; + } + } + cache->queue.pos = 0; + cache->queue.size = 0; + + // reset cache stats + cache->size = 0; + cache->hits = 0; + cache->misses = 0; + cache->errors = 0; + + url_cache_unlock(cache, session); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Emptied cache\n"); +} + +/** + * Get a URL from the cache, add it if it does not exist + * @param cache The cache + * @param session the (optional) session requesting the URL + * @param url The URL + * @param download If true, the file will be downloaded if it does not exist in the cache. + * @param pool The pool to use for allocating the filename + * @return The filename or NULL if there is an error + */ +static char *url_cache_get(url_cache_t *cache, switch_core_session_t *session, const char *url, int download, switch_memory_pool_t *pool) +{ + char *filename = NULL; + cached_url_t *u = NULL; + if (zstr(url)) { + return NULL; + } + + url_cache_lock(cache, session); + u = switch_core_hash_find(cache->map, url); + + /* check if expired */ + if (u && switch_time_now() >= (u->download_time + u->max_age)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Cached URL has expired.\n"); + url_cache_remove_soft(cache, session, u); /* will get permanently deleted upon replacement */ + u = NULL; + } + + /* make sure file hasn't been deleted */ + if (u && switch_file_exists(u->filename, pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Cached URL file is missing.\n"); + url_cache_remove_soft(cache, session, u); /* will get permanently deleted upon replacement */ + u = NULL; + } + + if (!u && download) { + /* URL is not cached, let's add it.*/ + /* Set up URL entry and add to map to prevent simultaneous downloads */ + cache->misses++; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Cache MISS: size = %ld (%ld MB), hit ratio = %d/%d\n", cache->queue.size, cache->size / 1000000, cache->hits, cache->hits + cache->misses); + u = cached_url_create(cache, url); + if (url_cache_add(cache, session, u) != SWITCH_STATUS_SUCCESS) { + /* This error should never happen */ + url_cache_unlock(cache, session); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Failed to add URL to cache!\n"); + cached_url_destroy(u, cache->pool); + return NULL; + } + + /* download the file */ + url_cache_unlock(cache, session); + if (http_get(u, session) == SWITCH_STATUS_SUCCESS) { + /* Got the file, let the waiters know it is available */ + url_cache_lock(cache, session); + u->status = CACHED_URL_AVAILABLE; + filename = switch_core_strdup(pool, u->filename); + cache->size += u->size; + } else { + /* Did not get the file, flag for replacement */ + url_cache_lock(cache, session); + url_cache_remove_soft(cache, session, u); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Failed to download URL %s\n", url); + cache->errors++; + } + } else if (!u) { + filename = DOWNLOAD_NEEDED; + } else { + /* Wait until file is downloaded */ + if (u->status == CACHED_URL_RX_IN_PROGRESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Waiting for URL %s to be available\n", url); + u->waiters++; + url_cache_unlock(cache, session); + while(u->status == CACHED_URL_RX_IN_PROGRESS) { + switch_sleep(10 * 1000); /* 10 ms */ + } + url_cache_lock(cache, session); + u->waiters--; + } + + /* grab filename if everything is OK */ + if (u->status == CACHED_URL_AVAILABLE) { + filename = switch_core_strdup(pool, u->filename); + cache->hits++; + u->used = 1; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Cache HIT: size = %ld (%ld MB), hit ratio = %d/%d\n", cache->queue.size, cache->size / 1000000, cache->hits, cache->hits + cache->misses); + } + } + url_cache_unlock(cache, session); + return filename; +} + +/** + * Add a URL to the cache. The caller must lock the cache. + * @param cache the cache + * @param session the (optional) session + * @param url the URL to add + * @return SWITCH_STATUS_SUCCESS if successful + */ +static switch_status_t url_cache_add(url_cache_t *cache, switch_core_session_t *session, cached_url_t *url) +{ + simple_queue_t *queue = &cache->queue; + if (queue->size >= queue->max_size && url_cache_replace(cache, session) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Adding %s(%s) to cache index %d\n", url->url, url->filename, queue->pos); + + queue->data[queue->pos] = url; + queue->pos = (queue->pos + 1) % queue->max_size; + queue->size++; + switch_core_hash_insert(cache->map, url->url, url); + return SWITCH_STATUS_SUCCESS; +} + +/** + * Select a URL for replacement and remove it from the cache. + * Currently implemented with the clock replacement algorithm. It's not + * great, but is better than least recently used and is simple to implement. + * + * @param cache the cache + * @param session the (optional) session + * @return SWITCH_STATUS_SUCCESS if successful + */ +static switch_status_t url_cache_replace(url_cache_t *cache, switch_core_session_t *session) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + int i = 0; + simple_queue_t *queue = &cache->queue; + + if (queue->size < queue->max_size || queue->size == 0) { + return SWITCH_STATUS_FALSE; + } + + for (i = 0; i < queue->max_size * 2; i++) { + cached_url_t *to_replace = (cached_url_t *)queue->data[queue->pos]; + + /* check for queue corruption */ + if (to_replace == NULL) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Unexpected empty URL at cache index %d\n", queue->pos); + status = SWITCH_STATUS_SUCCESS; + break; + } + + /* check if available for replacement */ + if (!to_replace->used && !to_replace->waiters) { + /* remove from cache and destroy it */ + url_cache_remove(cache, session, to_replace); + cached_url_destroy(to_replace, cache->pool); + status = SWITCH_STATUS_SUCCESS; + break; + } + + /* not available for replacement. Mark as not used and move to back of queue */ + if (to_replace->status == CACHED_URL_AVAILABLE) { + to_replace->used = 0; + } + queue->pos = (queue->pos + 1) % queue->max_size; + } + + return status; +} + +/** + * Remove a URL from the hash map and mark it as eligible for replacement from the queue. + */ +static void url_cache_remove_soft(url_cache_t *cache, switch_core_session_t *session, cached_url_t *url) +{ + switch_core_hash_delete(cache->map, url->url); + url->used = 0; + url->status = CACHED_URL_REMOVE; +} + +/** + * Remove a URL from the front or back of the cache + * @param cache the cache + * @param session the (optional) session + * @param url the URL to remove + */ +static void url_cache_remove(url_cache_t *cache, switch_core_session_t *session, cached_url_t *url) +{ + simple_queue_t *queue; + cached_url_t *to_remove; + + url_cache_remove_soft(cache, session, url); + + /* make sure cached URL matches the item in the queue and remove it */ + queue = &cache->queue; + to_remove = (cached_url_t *)queue->data[queue->pos]; + if (url == to_remove) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Removing %s(%s) from cache index %d\n", url->url, url->filename, queue->pos); + queue->data[queue->pos] = NULL; + queue->size--; + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "URL entry, %s, not in cache queue!!!\n", url->url); + } + + /* adjust cache statistics */ + cache->size -= url->size; +} + +/** + * Create a cached URL entry + * @param cache the cache + * @param url the URL to cache + * @return the cached URL + */ +static cached_url_t *cached_url_create(url_cache_t *cache, const char *url) +{ + switch_uuid_t uuid; + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + char *filename = NULL; + char uuid_dir[3] = { 0 }; + char *dirname = NULL; + cached_url_t *u = NULL; + const char *file_extension = ""; + + if (zstr(url)) { + return NULL; + } + + switch_zmalloc(u, sizeof(cached_url_t)); + + /* filename is constructed from UUID and is stored in cache dir (first 2 characters of UUID) */ + switch_uuid_get(&uuid); + switch_uuid_format(uuid_str, &uuid); + strncpy(uuid_dir, uuid_str, 2); + dirname = switch_mprintf("%s%s%s", cache->location, SWITCH_PATH_SEPARATOR, uuid_dir); + filename = &uuid_str[2]; + + /* create sub-directory if it doesn't exist */ + switch_dir_make_recursive(dirname, SWITCH_DEFAULT_DIR_PERMS, cache->pool); + + /* find extension on the end of URL */ + for(const char *ext = &url[strlen(url) - 1]; ext != url; ext--) { + if (*ext == '/' || *ext == '\\') { + break; + } + if (*ext == '.') { + /* found it */ + file_extension = ext; + break; + } + } + + /* intialize cached URL */ + u->filename = switch_mprintf("%s%s%s%s", dirname, SWITCH_PATH_SEPARATOR, filename, file_extension); + u->url = switch_safe_strdup(url); + u->size = 0; + u->used = 1; + u->status = CACHED_URL_RX_IN_PROGRESS; + u->waiters = 0; + u->download_time = switch_time_now(); + u->max_age = cache->default_max_age; + + switch_safe_free(dirname); + + return u; +} + +/** + * Destroy a cached URL (delete file and struct) + */ +static void cached_url_destroy(cached_url_t *url, switch_memory_pool_t *pool) +{ + if (!zstr(url->filename)) { + switch_file_remove(url->filename, pool); + } + switch_safe_free(url->filename); + switch_safe_free(url->url); + switch_safe_free(url); +} + +/** + * Fetch a file via HTTP + * @param url The cached URL entry + * @param session the (optional) session + * @return SWITCH_STATUS_SUCCESS if successful + */ +static switch_status_t http_get(cached_url_t *url, switch_core_session_t *session) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + CURL *curl_handle = NULL; + http_get_data_t get_data = {0}; + long httpRes = 0; + int start_time_ms = switch_time_now() / 1000; + + /* set up HTTP GET */ + get_data.fd = 0; + get_data.url = url; + + curl_handle = curl_easy_init(); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "opening %s for URL cache\n", get_data.url->filename); + if ((get_data.fd = open(get_data.url->filename, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)) > -1) { + curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 10); + curl_easy_setopt(curl_handle, CURLOPT_URL, get_data.url->url); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, get_file_callback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &get_data); + curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, get_header_callback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, (void *) url); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-http-cache/1.0"); + curl_easy_perform(curl_handle); + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes); + curl_easy_cleanup(curl_handle); + close(get_data.fd); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "open() error: %s\n", strerror(errno)); + return SWITCH_STATUS_GENERR; + } + + if (httpRes == 200) { + int duration_ms = (switch_time_now() / 1000) - start_time_ms; + if (duration_ms > 500) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "URL %s downloaded in %d ms\n", url->url, duration_ms); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "URL %s downloaded in %d ms\n", url->url, duration_ms); + } + } else { + url->size = 0; // nothing downloaded or download interrupted + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Received HTTP error %ld trying to fetch %s\n", httpRes, url->url); + return SWITCH_STATUS_GENERR; + } + + return status; +} + +/** + * Empties the entire cache + * @param cache the cache to empty + */ +static void setup_dir(url_cache_t *cache) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "setting up %s\n", cache->location); + switch_dir_make_recursive(cache->location, SWITCH_DEFAULT_DIR_PERMS, cache->pool); + + for (int i = 0x00; i <= 0xff; i++) { + switch_dir_t *dir = NULL; + char *dirname = switch_mprintf("%s%s%02x", cache->location, SWITCH_PATH_SEPARATOR, i); + if (switch_dir_open(&dir, dirname, cache->pool) == SWITCH_STATUS_SUCCESS) { + char filenamebuf[256] = { 0 }; + const char *filename = NULL; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "deleting cache files in %s...\n", dirname); + for(filename = switch_dir_next_file(dir, filenamebuf, sizeof(filenamebuf)); filename; + filename = switch_dir_next_file(dir, filenamebuf, sizeof(filenamebuf))) { + char *path = switch_mprintf("%s%s%s", dirname, SWITCH_PATH_SEPARATOR, filename); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "deleting: %s\n", path); + switch_file_remove(path, cache->pool); + switch_safe_free(path); + } + switch_dir_close(dir); + switch_safe_free(dirname); + } else { + switch_safe_free(dirname); + break; + } + } +} + +#define HTTP_GET_SYNTAX "" +/** + * Get a file from the cache, download if it isn't cached + */ +SWITCH_STANDARD_API(http_cache_get) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_memory_pool_t *lpool = NULL; + switch_memory_pool_t *pool = NULL; + char *filename; + + if (zstr(cmd) || strncmp("http://", cmd, strlen("http://"))) { + stream->write_function(stream, "USAGE: %s\n", HTTP_GET_SYNTAX); + return SWITCH_STATUS_SUCCESS; + } + + if (session) { + pool = switch_core_session_get_pool(session); + } else { + switch_core_new_memory_pool(&lpool); + pool = lpool; + } + + filename = url_cache_get(&gcache, session, cmd, 1, pool); + if (filename) { + stream->write_function(stream, "%s", filename); + + } else { + stream->write_function(stream, "-ERR\n"); + status = SWITCH_STATUS_FALSE; + } + + if (lpool) { + switch_core_destroy_memory_pool(&lpool); + } + + return status; +} + +#define HTTP_TRYGET_SYNTAX "" +/** + * Get a file from the cache, fail if download is needed + */ +SWITCH_STANDARD_API(http_cache_tryget) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_memory_pool_t *lpool = NULL; + switch_memory_pool_t *pool = NULL; + char *filename; + + if (zstr(cmd) || strncmp("http://", cmd, strlen("http://"))) { + stream->write_function(stream, "USAGE: %s\n", HTTP_GET_SYNTAX); + return SWITCH_STATUS_SUCCESS; + } + + if (session) { + pool = switch_core_session_get_pool(session); + } else { + switch_core_new_memory_pool(&lpool); + pool = lpool; + } + + filename = url_cache_get(&gcache, session, cmd, 0, pool); + if (filename) { + if (!strcmp(DOWNLOAD_NEEDED, filename)) { + stream->write_function(stream, "-ERR %s\n", DOWNLOAD_NEEDED); + } else { + stream->write_function(stream, "%s", filename); + } + } else { + stream->write_function(stream, "-ERR\n"); + status = SWITCH_STATUS_FALSE; + } + + if (lpool) { + switch_core_destroy_memory_pool(&lpool); + } + + return status; +} + +#define HTTP_PUT_SYNTAX " " +/** + * Put a file to the server + */ +SWITCH_STANDARD_API(http_cache_put) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + char *args = NULL; + char *argv[10] = { 0 }; + int argc = 0; + //switch_memory_pool_t *pool = NULL; + switch_memory_pool_t *lpool = NULL; + + if (zstr(cmd) || strncmp("http://", cmd, strlen("http://"))) { + stream->write_function(stream, "USAGE: %s\n", HTTP_PUT_SYNTAX); + status = SWITCH_STATUS_SUCCESS; + goto done; + } + + args = strdup(cmd); + argc = switch_separate_string(args, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + if (argc != 2) { + stream->write_function(stream, "USAGE: %s\n", HTTP_PUT_SYNTAX); + status = SWITCH_STATUS_SUCCESS; + goto done; + } + + /* + if (session) { + pool = switch_core_session_get_pool(session); + } else { + switch_core_new_memory_pool(&lpool); + pool = lpool; + } + */ + + status = http_put(session, argv[0], argv[1]); + if (status == SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "+OK\n"); + } else { + stream->write_function(stream, "-ERR\n"); + } + + if (lpool) { + switch_core_destroy_memory_pool(&lpool); + } + +done: + switch_safe_free(args); + return status; +} + +#define HTTP_CACHE_CLEAR_SYNTAX "" +/** + * Clears the cache + */ +SWITCH_STANDARD_API(http_cache_clear) +{ + if (!zstr(cmd)) { + stream->write_function(stream, "USAGE: %s\n", HTTP_CACHE_CLEAR_SYNTAX); + } else { + url_cache_clear(&gcache, session); + stream->write_function(stream, "+OK\n"); + } + return SWITCH_STATUS_SUCCESS; +} + +/** + * Configure the module + * @param cache to configure + * @return SWITCH_STATUS_SUCCESS if successful + */ +static switch_status_t do_config(url_cache_t *cache) +{ + char *cf = "http_cache.conf"; + switch_xml_t cfg, xml, param, settings; + switch_status_t status = SWITCH_STATUS_SUCCESS; + int max_urls; + int default_max_age_sec; + + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf); + return SWITCH_STATUS_TERM; + } + + /* set default config */ + max_urls = 4000; + default_max_age_sec = 86400; + cache->location = SWITCH_PREFIX_DIR "/http_cache"; + + /* get params */ + settings = switch_xml_child(cfg, "settings"); + if (settings) { + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + if (!strcasecmp(var, "max-urls")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Setting max-urls to %s\n", val); + max_urls = atoi(val); + } else if (!strcasecmp(var, "location")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Setting location to %s\n", val); + cache->location = switch_core_strdup(cache->pool, val); + } else if (!strcasecmp(var, "default-max-age")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Setting default-max-age to %s\n", val); + default_max_age_sec = atoi(val); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported param: %s\n", var); + } + } + } + + /* check config */ + if (max_urls <= 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "max-urls must be > 0\n"); + status = SWITCH_STATUS_TERM; + goto done; + } + if (zstr(cache->location)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "location must not be empty\n"); + status = SWITCH_STATUS_TERM; + goto done; + } + if (default_max_age_sec <= 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "default-max-age must be > 0\n"); + status = SWITCH_STATUS_TERM; + goto done; + } + + cache->max_url = max_urls; + cache->default_max_age = (default_max_age_sec * 1000 * 1000); /* convert from seconds to nanoseconds */ +done: + switch_xml_free(xml); + + return status; +} + +/** + * Called when FreeSWITCH loads the module + */ +SWITCH_MODULE_LOAD_FUNCTION(mod_http_cache_load) +{ + switch_api_interface_t *api; + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + SWITCH_ADD_API(api, "http_get", "HTTP GET", http_cache_get, HTTP_GET_SYNTAX); + SWITCH_ADD_API(api, "http_tryget", "HTTP GET from cache only", http_cache_tryget, HTTP_GET_SYNTAX); + SWITCH_ADD_API(api, "http_put", "HTTP PUT", http_cache_put, HTTP_PUT_SYNTAX); + SWITCH_ADD_API(api, "http_clear_cache", "Clear the cache", http_cache_clear, HTTP_CACHE_CLEAR_SYNTAX); + + memset(&gcache, 0, sizeof(url_cache_t)); + gcache.pool = pool; + + if (do_config(&gcache) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_TERM; + } + + switch_core_hash_init(&gcache.map, gcache.pool); + switch_mutex_init(&gcache.mutex, SWITCH_MUTEX_UNNESTED, gcache.pool); + + /* create the queue */ + gcache.queue.max_size = gcache.max_url; + gcache.queue.data = switch_core_alloc(gcache.pool, sizeof(void *) * gcache.queue.max_size); + gcache.queue.pos = 0; + gcache.queue.size = 0; + + setup_dir(&gcache); + + /* init CURL */ + curl_global_init(CURL_GLOBAL_ALL); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +/** + * Called when FreeSWITCH stops the module + */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_http_cache_shutdown) +{ + url_cache_clear(&gcache, NULL); + switch_core_hash_destroy(&gcache.map); + switch_mutex_destroy(gcache.mutex); + return SWITCH_STATUS_SUCCESS; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ From 439b81249473315eba0d1448dacf73e4741af4cb Mon Sep 17 00:00:00 2001 From: Christopher Rienzo Date: Thu, 6 Oct 2011 19:47:12 +0000 Subject: [PATCH 034/429] only check expiration on downloaded files --- .../mod_http_cache/mod_http_cache.c | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/mod/applications/mod_http_cache/mod_http_cache.c b/src/mod/applications/mod_http_cache/mod_http_cache.c index 23cb01a41d..e9a31a14d9 100644 --- a/src/mod/applications/mod_http_cache/mod_http_cache.c +++ b/src/mod/applications/mod_http_cache/mod_http_cache.c @@ -445,18 +445,20 @@ static char *url_cache_get(url_cache_t *cache, switch_core_session_t *session, c url_cache_lock(cache, session); u = switch_core_hash_find(cache->map, url); - /* check if expired */ - if (u && switch_time_now() >= (u->download_time + u->max_age)) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Cached URL has expired.\n"); - url_cache_remove_soft(cache, session, u); /* will get permanently deleted upon replacement */ - u = NULL; - } + if (u && u->status == CACHED_URL_AVAILABLE) { + /* check if expired */ + if (switch_time_now() >= (u->download_time + u->max_age)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Cached URL has expired.\n"); + url_cache_remove_soft(cache, session, u); /* will get permanently deleted upon replacement */ + u = NULL; + } - /* make sure file hasn't been deleted */ - if (u && switch_file_exists(u->filename, pool) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Cached URL file is missing.\n"); - url_cache_remove_soft(cache, session, u); /* will get permanently deleted upon replacement */ - u = NULL; + /* make sure file hasn't been deleted */ + if (switch_file_exists(u->filename, pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Cached URL file is missing.\n"); + url_cache_remove_soft(cache, session, u); /* will get permanently deleted upon replacement */ + u = NULL; + } } if (!u && download) { @@ -872,8 +874,6 @@ SWITCH_STANDARD_API(http_cache_put) char *args = NULL; char *argv[10] = { 0 }; int argc = 0; - //switch_memory_pool_t *pool = NULL; - switch_memory_pool_t *lpool = NULL; if (zstr(cmd) || strncmp("http://", cmd, strlen("http://"))) { stream->write_function(stream, "USAGE: %s\n", HTTP_PUT_SYNTAX); @@ -889,15 +889,6 @@ SWITCH_STANDARD_API(http_cache_put) goto done; } - /* - if (session) { - pool = switch_core_session_get_pool(session); - } else { - switch_core_new_memory_pool(&lpool); - pool = lpool; - } - */ - status = http_put(session, argv[0], argv[1]); if (status == SWITCH_STATUS_SUCCESS) { stream->write_function(stream, "+OK\n"); @@ -905,10 +896,6 @@ SWITCH_STANDARD_API(http_cache_put) stream->write_function(stream, "-ERR\n"); } - if (lpool) { - switch_core_destroy_memory_pool(&lpool); - } - done: switch_safe_free(args); return status; From 798bacb2b5cea83cde1adf64a042bbc4ce32b27a Mon Sep 17 00:00:00 2001 From: Christopher Rienzo Date: Thu, 6 Oct 2011 20:09:19 +0000 Subject: [PATCH 035/429] move mod_http_cache configuration to conf/autoload_configs/ --- .../mod_http_cache => conf/autoload_configs}/http_cache.conf.xml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {src/mod/applications/mod_http_cache => conf/autoload_configs}/http_cache.conf.xml (100%) diff --git a/src/mod/applications/mod_http_cache/http_cache.conf.xml b/conf/autoload_configs/http_cache.conf.xml similarity index 100% rename from src/mod/applications/mod_http_cache/http_cache.conf.xml rename to conf/autoload_configs/http_cache.conf.xml From f0efbd3f0895513690179fddf5f4db05d5351b16 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Fri, 7 Oct 2011 08:38:48 -0500 Subject: [PATCH 036/429] FS-3599 --resolve correct windows compiler warnings --- libs/freetdm/mod_freetdm/mod_freetdm.c | 2 +- libs/freetdm/src/ftdm_io.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 44accde0dd..ce1437af9d 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -507,7 +507,7 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session) const char *name = NULL; int span_id = 0; int chan_id = 0; - int t = 0; + uint32_t t = 0; uint32_t tokencnt; char *uuid = NULL; const char *token = NULL; diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 9da6e1e903..f08eeaacf7 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -3969,7 +3969,9 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_process_media(ftdm_channel_t *ftdmchan, v if (ftdmchan->state == FTDM_CHANNEL_STATE_CALLWAITING && (digit_char == 'D' || digit_char == 'A')) { ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK]++; } else { - char digit_str[2] = { digit_char, 0}; + char digit_str[2] = { 0 }; + + digit_str[0] = digit_char; if (!ftdmchan->span->sig_dtmf || (ftdmchan->span->sig_dtmf(ftdmchan, (const char*)digit_str) != FTDM_BREAK)) { ftdm_channel_queue_dtmf(ftdmchan, digit_str); From 1afda8fb39fcd9648be7155d11c7bd27b37580f1 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Fri, 7 Oct 2011 09:03:09 -0500 Subject: [PATCH 037/429] FS-3601 --resolve fix fs_cli for recent changes with windows --- libs/esl/fs_cli.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/esl/fs_cli.c b/libs/esl/fs_cli.c index 22906e710f..7c2f67dc49 100644 --- a/libs/esl/fs_cli.c +++ b/libs/esl/fs_cli.c @@ -236,7 +236,7 @@ static int console_bufferInput (char *addchars, int len, char *cmd, int key) return 0; } if (key == KEY_TAB) { - esl_console_complete(cmd, cmd+iCmdBuffer, &cmd[iCmdBuffer-1]); + esl_console_complete(cmd, cmd+iCmdBuffer, cmd+iCmdBuffer); return 0; } if (key == KEY_UP || key == KEY_DOWN || key == CLEAR_OP) { @@ -883,7 +883,14 @@ static const char *banner = "\n" "Type /help to see a list of commands\n\n\n"; -static void print_banner(FILE *stream) { fprintf(stream, "%s%s", output_text_color, banner); } +static void print_banner(FILE *stream) +{ +#ifndef WIN32 + fprintf(stream, "%s%s", output_text_color, banner); +#else + fprintf(stream, "%s", banner); +#endif +} static void set_fn_keys(cli_profile_t *profile) { From 78a9a6488a9db00c5f2a17da9b65f9669bb40e8b Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 7 Oct 2011 08:23:40 -0500 Subject: [PATCH 038/429] some chat fixes --- src/mod/applications/mod_dptools/mod_dptools.c | 2 +- src/mod/applications/mod_sms/mod_sms.c | 1 + src/switch_loadable_module.c | 10 ++++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index d0a6337166..011acd43c3 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -1562,7 +1562,7 @@ SWITCH_STANDARD_API(chat_api_function) if (!zstr(cmd) && (lbuf = strdup(cmd)) && (argc = switch_separate_string(lbuf, '|', argv, (sizeof(argv) / sizeof(argv[0])))) >= 4) { - if (switch_core_chat_send_args(argv[0], "dp", argv[1], argv[2], "", argv[3], !zstr(argv[4]) ? argv[4] : NULL, "") == SWITCH_STATUS_SUCCESS) { + if (switch_core_chat_send_args(argv[0], "global", argv[1], argv[2], "", argv[3], !zstr(argv[4]) ? argv[4] : NULL, "") == SWITCH_STATUS_SUCCESS) { stream->write_function(stream, "Sent"); } else { stream->write_function(stream, "Error! Message Not Sent"); diff --git a/src/mod/applications/mod_sms/mod_sms.c b/src/mod/applications/mod_sms/mod_sms.c index 70db75a5e3..5c1e8c5ab5 100644 --- a/src/mod/applications/mod_sms/mod_sms.c +++ b/src/mod/applications/mod_sms/mod_sms.c @@ -42,6 +42,7 @@ SWITCH_MODULE_DEFINITION(mod_sms, mod_sms_load, mod_sms_shutdown, NULL); static void event_handler(switch_event_t *event) { const char *dest_proto = switch_event_get_header(event, "dest_proto"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "skip_global_process", "true"); switch_core_chat_send(dest_proto, event); } diff --git a/src/switch_loadable_module.c b/src/switch_loadable_module.c index 48e9226080..d788908437 100644 --- a/src/switch_loadable_module.c +++ b/src/switch_loadable_module.c @@ -547,10 +547,15 @@ static switch_status_t do_chat_send(switch_event_t *message_event) hint = switch_event_get_header(message_event, "hint"); */ - proto = switch_event_get_header(message_event, "proto"); + if (!(proto = switch_event_get_header(message_event, "proto"))) { + proto = "global"; + switch_event_add_header(message_event, SWITCH_STACK_BOTTOM, "proto", proto); + } + + replying = switch_event_get_header(message_event, "replying"); - if (!switch_true(replying) && !switch_stristr("global", proto)) { + if (!switch_true(replying) && !switch_stristr("global", proto) && !switch_true(switch_event_get_header(message_event, "skip_global_process"))) { switch_mutex_lock(loadable_modules.mutex); for (hi = switch_hash_first(NULL, loadable_modules.chat_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, &var, NULL, &val); @@ -741,6 +746,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_chat_send_args(const char *dest_prot switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "subject", subject); switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "type", type); switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "hint", hint); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "skip_global_process", "true"); if (body) { switch_event_add_body(message_event, "%s", body); From ab26f768b2e4284ac42da216ed8448ebc91dec07 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 7 Oct 2011 08:24:14 -0500 Subject: [PATCH 039/429] fix build file --- build/modules.conf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/build/modules.conf.in b/build/modules.conf.in index b22d627b7c..b775a35bac 100644 --- a/build/modules.conf.in +++ b/build/modules.conf.in @@ -12,6 +12,7 @@ applications/mod_fifo #applications/mod_curl applications/mod_db applications/mod_hash +#applications/mod_http_cache #applications/mod_redis applications/mod_voicemail #applications/mod_directory From f9fc86ef966aa58ddf1290ec77dcaf2ff2859bcb Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 7 Oct 2011 09:06:49 -0500 Subject: [PATCH 040/429] fix typo --- src/mod/applications/mod_dptools/mod_dptools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 011acd43c3..fe82187d6c 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -3986,7 +3986,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) SAF_SUPPORT_NOMEDIA); SWITCH_ADD_APP(app_interface, "send_dtmf", "Send dtmf to be sent", "Send dtmf to be sent from a session", send_dtmf_function, "", SAF_SUPPORT_NOMEDIA); - SWITCH_ADD_APP(app_interface, "sched_cencel", "cancel scheduled tasks", "cancel scheduled tasks", sched_cancel_function, "[group]", + SWITCH_ADD_APP(app_interface, "sched_cancel", "cancel scheduled tasks", "cancel scheduled tasks", sched_cancel_function, "[group]", SAF_SUPPORT_NOMEDIA); SWITCH_ADD_APP(app_interface, "sched_hangup", SCHED_HANGUP_DESCR, SCHED_HANGUP_DESCR, sched_hangup_function, "[+]