diff options
author | Patrick McHardy <kaber@trash.net> | 2011-06-17 08:11:11 +0200 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-06-17 08:11:11 +0200 |
commit | 9364aaccb699c6d19ac2cbe760c208b34ba7838a (patch) | |
tree | 28c0aef25a69f14117caf64e09fc9597d2c915ea | |
parent | 84c94e92c153057470194aadf7ae22a2569f1bd4 (diff) | |
parent | 62b90dd0a4ea809bdb11bc27288b6acf717edcd8 (diff) |
Merge 192.168.0.100:/repos/git/asterisk
-rw-r--r-- | CHANGES | 7 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | apps/app_confbridge.c | 119 | ||||
-rw-r--r-- | apps/app_directed_pickup.c | 24 | ||||
-rw-r--r-- | apps/app_queue.c | 4 | ||||
-rw-r--r-- | apps/confbridge/conf_config_parser.c | 12 | ||||
-rw-r--r-- | apps/confbridge/include/confbridge.h | 1 | ||||
-rw-r--r-- | channels/chan_local.c | 22 | ||||
-rw-r--r-- | channels/chan_sip.c | 100 | ||||
-rw-r--r-- | channels/chan_skinny.c | 160 | ||||
-rw-r--r-- | channels/sip/include/sip.h | 3 | ||||
-rw-r--r-- | configs/sip.conf.sample | 5 | ||||
-rw-r--r-- | include/asterisk/astdb.h | 2 | ||||
-rw-r--r-- | include/asterisk/channel.h | 4 | ||||
-rw-r--r-- | include/asterisk/features.h | 20 | ||||
-rw-r--r-- | include/asterisk/rtp_engine.h | 50 | ||||
-rw-r--r-- | include/asterisk/utils.h | 2 | ||||
-rw-r--r-- | main/channel.c | 20 | ||||
-rw-r--r-- | main/db.c | 299 | ||||
-rw-r--r-- | main/dnsmgr.c | 8 | ||||
-rw-r--r-- | main/event.c | 29 | ||||
-rw-r--r-- | main/features.c | 99 | ||||
-rw-r--r-- | main/manager.c | 78 | ||||
-rw-r--r-- | main/netsock2.c | 19 | ||||
-rw-r--r-- | main/rtp_engine.c | 21 | ||||
-rw-r--r-- | res/res_agi.c | 2 | ||||
-rw-r--r-- | res/res_config_pgsql.c | 44 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 45 | ||||
-rw-r--r-- | tests/test_db.c | 222 | ||||
-rw-r--r-- | tests/test_event.c | 90 |
30 files changed, 1101 insertions, 411 deletions
@@ -19,8 +19,8 @@ Text Messaging SIP MESSAGE and XMPP are currently supported. There are options in jabber.conf and sip.conf to allow enabling these features. -> jabber.conf: see the "sendtodialplan" and "context" options. - -> sip.conf: see the "accept_outofcall_message" and "auth_message_requests" - options. + -> sip.conf: see the "accept_outofcall_message", "auth_message_requests" + and "outofcall_message_context" options. The MESSAGE() dialplan function and MessageSend() application have been added to go along with this functionality. More detailed usage information can be found on the Asterisk wiki (http://wiki.asterisk.org/). @@ -79,6 +79,9 @@ ConfBridge mixing audio at sample rates ranging from 8khz-96khz. * CONFBRIDGE dialplan function capable of creating dynamic ConfBridge user and bridge profiles on a channel. + * CONFBRIDGE_INFO dialplan function capable of retreiving information + about a conference such as locked status and number of parties, admins, + and marked users. Dialplan Variables ------------------ @@ -557,6 +557,7 @@ installdirs: $(INSTALL) -d "$(DESTDIR)$(ASTDATADIR)/static-http" $(INSTALL) -d "$(DESTDIR)$(ASTMANDIR)/man8" $(INSTALL) -d "$(DESTDIR)$(AGI_DIR)" + $(INSTALL) -d "$(DESTDIR)$(ASTDBDIR)" bininstall: _all installdirs $(SUBDIRS_INSTALL) $(INSTALL) -m 755 main/asterisk $(DESTDIR)$(ASTSBINDIR)/ diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c index 7cede133e..177ee3717 100644 --- a/apps/app_confbridge.c +++ b/apps/app_confbridge.c @@ -106,6 +106,22 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <para>exten => 1,n,ConfBridge(1)</para> </description> </function> + <function name="CONFBRIDGE_INFO" language="en_US"> + <synopsis> + Get information about a ConfBridge conference. + </synopsis> + <syntax> + <parameter name="type" required="true"> + <para>Type can be <literal>parties</literal>, <literal>admins</literal>, <literal>marked</literal>, or <literal>locked</literal>.</para> + </parameter> + <parameter name="conf" required="true"> + <para>Conf refers to the name of the conference being referenced.</para> + </parameter> + </syntax> + <description> + <para>This function returns a non-negative integer for valid conference identifiers (0 or 1 for <literal>locked</literal>) and "" for invalid conference identifiers.</para> + </description> + </function> <manager name="ConfbridgeList" language="en_US"> <synopsis> List participants in a conference. @@ -618,7 +634,8 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct if (other_conference_bridge_user == conference_bridge_user) { continue; } - if (ast_test_flag(&other_conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) { + if (other_conference_bridge_user->playing_moh && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) { + other_conference_bridge_user->playing_moh = 0; ast_moh_stop(other_conference_bridge_user->chan); ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan); } @@ -664,6 +681,7 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct */ if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) { ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL); + conference_bridge_user->playing_moh = 1; } } } @@ -692,6 +710,7 @@ static void post_join_unmarked(struct conference_bridge *conference_bridge, stru */ if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) { ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL); + conference_bridge_user->playing_moh = 1; } return; } @@ -709,6 +728,7 @@ static void post_join_unmarked(struct conference_bridge *conference_bridge, stru /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */ if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) { + first_participant->playing_moh = 0; ast_moh_stop(first_participant->chan); ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan); } @@ -924,6 +944,7 @@ static void leave_conference_bridge(struct conference_bridge *conference_bridge, ast_bridge_remove(conference_bridge->bridge, other_participant->chan); } else if (ast_test_flag(&other_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) { ast_moh_start(other_participant->chan, other_participant->u_profile.moh_class, NULL); + other_participant->playing_moh = 1; ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan); } } @@ -933,6 +954,7 @@ static void leave_conference_bridge(struct conference_bridge *conference_bridge, if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) { ast_moh_start(first_participant->chan, first_participant->u_profile.moh_class, NULL); + first_participant->playing_moh = 1; ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan); } } @@ -1192,14 +1214,22 @@ static int confbridge_exec(struct ast_channel *chan, const char *data) if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) { b_profile_name = args.b_profile_name; } - conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile); + if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) { + ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name); + res = -1; + goto confbridge_cleanup; + } /* user profile name */ if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) { u_profile_name = args.u_profile_name; } - conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile); + if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) { + ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name); + res = -1; + goto confbridge_cleanup; + } quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET); /* ask for a PIN immediately after finding user profile. This has to be @@ -1301,7 +1331,13 @@ static int confbridge_exec(struct ast_channel *chan, const char *data) /* Play the Join sound to both the conference and the user entering. */ if (!quiet) { const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds); + if (conference_bridge_user.playing_moh) { + ast_moh_stop(chan); + } ast_stream_and_wait(chan, join_sound, ""); + if (conference_bridge_user.playing_moh) { + ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL); + } ast_autoservice_start(chan); play_sound_file(conference_bridge, join_sound); ast_autoservice_stop(chan); @@ -1635,8 +1671,8 @@ int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel, /* See if music on hold is playing */ ao2_lock(conference_bridge); - if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) { - /* Just us so MOH is probably indeed going, let's stop it */ + if (conference_bridge_user->playing_moh) { + /* MOH is going, let's stop it */ ast_moh_stop(bridge_channel->chan); } ao2_unlock(conference_bridge); @@ -1646,7 +1682,7 @@ int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel, /* See if music on hold needs to be started back up again */ ao2_lock(conference_bridge); - if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) { + if (conference_bridge_user->playing_moh) { ast_moh_start(bridge_channel->chan, conference_bridge_user->u_profile.moh_class, NULL); } ao2_unlock(conference_bridge); @@ -2059,6 +2095,12 @@ static struct ast_custom_function confbridge_function = { .write = func_confbridge_helper, }; +static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len); +static struct ast_custom_function confbridge_info_function = { + .name = "CONFBRIDGE_INFO", + .read = func_confbridge_info, +}; + static int action_confbridgelist(struct mansession *s, const struct message *m) { const char *actionid = astman_get_header(m, "ActionID"); @@ -2370,7 +2412,68 @@ static int action_confbridgestoprecord(struct mansession *s, const struct messag return 0; } +static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + char *parse = NULL; + struct conference_bridge *bridge = NULL; + struct conference_bridge_user *participant = NULL; + struct conference_bridge tmp; + int count = 0; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(type); + AST_APP_ARG(confno); + ); + /* parse all the required arguments and make sure they exist. */ + if (ast_strlen_zero(data)) { + return -1; + } + parse = ast_strdupa(data); + AST_STANDARD_APP_ARGS(args, parse); + if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) { + return -1; + } + if (!ao2_container_count(conference_bridges)) { + ast_log(LOG_ERROR, "No active conferneces.\n"); + return -1; + } + ast_copy_string(tmp.name, args.confno, sizeof(tmp.name)); + bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); + if (!bridge) { + ast_log(LOG_ERROR, "Confernece '%s' not found.\n", args.confno); + return -1; + } + + /* get the correct count for the type requested */ + ao2_lock(bridge); + if (!strncasecmp(args.type, "parties", 7)) { + AST_LIST_TRAVERSE(&bridge->users_list, participant, list) { + count++; + } + } else if (!strncasecmp(args.type, "admins", 6)) { + AST_LIST_TRAVERSE(&bridge->users_list, participant, list) { + if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) { + count++; + } + } + } else if (!strncasecmp(args.type, "marked", 6)) { + AST_LIST_TRAVERSE(&bridge->users_list, participant, list) { + if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) { + count++; + } + } + } else if (!strncasecmp(args.type, "locked", 6)) { + count = bridge->locked; + } else { + ao2_unlock(bridge); + ao2_ref(bridge, -1); + return -1; + } + snprintf(buf, len, "%d", count); + ao2_unlock(bridge); + ao2_ref(bridge, -1); + return 0; +} /*! \brief Called when module is being unloaded */ static int unload_module(void) @@ -2378,6 +2481,7 @@ static int unload_module(void) int res = ast_unregister_application(app); ast_custom_function_unregister(&confbridge_function); + ast_custom_function_unregister(&confbridge_info_function); ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry)); @@ -2409,6 +2513,9 @@ static int load_module(void) if ((ast_custom_function_register(&confbridge_function))) { return AST_MODULE_LOAD_FAILURE; } + if ((ast_custom_function_register(&confbridge_info_function))) { + return AST_MODULE_LOAD_FAILURE; + } if (!(record_tech.capabilities = ast_format_cap_alloc())) { return AST_MODULE_LOAD_FAILURE; } diff --git a/apps/app_directed_pickup.c b/apps/app_directed_pickup.c index 38f24effc..63a7621ff 100644 --- a/apps/app_directed_pickup.c +++ b/apps/app_directed_pickup.c @@ -97,19 +97,6 @@ static const char app[] = "Pickup"; static const char app2[] = "PickupChan"; /*! \todo This application should return a result code, like PICKUPRESULT */ -/* Helper function that determines whether a channel is capable of being picked up */ -static int can_pickup(struct ast_channel *chan) -{ - if (!chan->pbx && !chan->masq && - !ast_test_flag(chan, AST_FLAG_ZOMBIE) && - (chan->_state == AST_STATE_RINGING || - chan->_state == AST_STATE_RING || - chan->_state == AST_STATE_DOWN)) { - return 1; - } - return 0; -} - struct pickup_by_name_args { const char *name; size_t len; @@ -121,7 +108,7 @@ static int pickup_by_name_cb(void *obj, void *arg, void *data, int flags) struct pickup_by_name_args *args = data; ast_channel_lock(target); - if (!strncasecmp(target->name, args->name, args->len) && can_pickup(target)) { + if (!strncasecmp(target->name, args->name, args->len) && ast_can_pickup(target)) { /* Return with the channel still locked on purpose */ return CMP_MATCH | CMP_STOP; } @@ -190,7 +177,7 @@ static int pickup_by_exten(struct ast_channel *chan, const char *exten, const ch while ((target = ast_channel_iterator_next(iter))) { ast_channel_lock(target); - if ((chan != target) && can_pickup(target)) { + if ((chan != target) && ast_can_pickup(target)) { ast_log(LOG_NOTICE, "%s pickup by %s\n", target->name, chan->name); break; } @@ -217,7 +204,7 @@ static int find_by_mark(void *obj, void *arg, void *data, int flags) ast_channel_lock(target); tmp = pbx_builtin_getvar_helper(target, PICKUPMARK); - if (tmp && !strcasecmp(tmp, mark) && can_pickup(target)) { + if (tmp && !strcasecmp(tmp, mark) && ast_can_pickup(target)) { /* Return with the channel still locked on purpose */ return CMP_MATCH | CMP_STOP; } @@ -249,7 +236,8 @@ static int find_channel_by_group(void *obj, void *arg, void *data, int flags) struct ast_channel *chan = data;/*!< Channel wanting to pickup call */ ast_channel_lock(target); - if (chan != target && (chan->pickupgroup & target->callgroup) && can_pickup(target)) { + if (chan != target && (chan->pickupgroup & target->callgroup) + && ast_can_pickup(target)) { /* Return with the channel still locked on purpose */ return CMP_MATCH | CMP_STOP; } @@ -316,7 +304,7 @@ static int find_by_part(void *obj, void *arg, void *data, int flags) ast_channel_lock(target); if (len <= strlen(target->name) && !strncmp(target->name, part, len) - && can_pickup(target)) { + && ast_can_pickup(target)) { /* Return with the channel still locked on purpose */ return CMP_MATCH | CMP_STOP; } diff --git a/apps/app_queue.c b/apps/app_queue.c index 146cf63d6..be929c1dc 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -3482,7 +3482,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte if (o->stillgoing) { /* Keep track of important channels */ stillgoing = 1; if (o->chan) { - watchers[pos++] = o->chan; + if (pos < AST_MAX_WATCHERS) { + watchers[pos++] = o->chan; + } if (!start) start = o; else diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c index dbec32bba..3d1f31326 100644 --- a/apps/confbridge/conf_config_parser.c +++ b/apps/confbridge/conf_config_parser.c @@ -582,7 +582,7 @@ static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum con static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names) { - struct conf_menu_entry *menu_entry = NULL; + struct conf_menu_entry *menu_entry = NULL, *cur = NULL; int res = 0; char *tmp_action_names = ast_strdupa(action_names); char *action = NULL; @@ -691,6 +691,16 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char * return -1; } + /* remove any list entry with an identical DTMF sequence for overrides */ + AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) { + if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) { + AST_LIST_REMOVE_CURRENT(entry); + ast_free(cur); + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry); return 0; diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h index 4b6c0615e..de467b5f7 100644 --- a/apps/confbridge/include/confbridge.h +++ b/apps/confbridge/include/confbridge.h @@ -215,6 +215,7 @@ struct conference_bridge_user { struct ast_bridge_features features; /*!< Bridge features structure */ struct ast_bridge_tech_optimizations tech_args; /*!< Bridge technology optimizations for talk detection */ unsigned int kicked:1; /*!< User has been kicked from the conference */ + unsigned int playing_moh:1; /*!< MOH is currently being played to the user */ AST_LIST_ENTRY(conference_bridge_user) list; /*!< Linked list information */ }; diff --git a/channels/chan_local.c b/channels/chan_local.c index 0ceb432b0..3c16fc51a 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -217,6 +217,7 @@ static void awesome_locking(struct local_pvt *p, struct ast_channel **outchan, s *outchan = p->chan; } +/* Called with ast locked */ static int local_setoption(struct ast_channel *ast, int option, void * data, int datalen) { int res = 0; @@ -225,27 +226,22 @@ static int local_setoption(struct ast_channel *ast, int option, void * data, int ast_chan_write_info_t *write_info; if (option != AST_OPTION_CHANNEL_WRITE) { - res = -1; - goto setoption_cleanup; + return -1; } write_info = data; if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) { ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n"); - res = -1; - goto setoption_cleanup; + return -1; } /* get the tech pvt */ - ast_channel_lock(ast); if (!(p = ast->tech_pvt)) { - ast_channel_unlock(ast); - res = -1; - goto setoption_cleanup; + return -1; } ao2_ref(p, 1); - ast_channel_unlock(ast); + ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */ /* get the channel we are supposed to write to */ ao2_lock(p); @@ -272,6 +268,7 @@ setoption_cleanup: if (otherchan) { ast_channel_unref(otherchan); } + ast_channel_lock(ast); /* Lock back before we leave */ return res; } @@ -348,6 +345,7 @@ static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct return bridged; } +/* Called with ast locked */ static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen) { struct local_pvt *p; @@ -361,21 +359,18 @@ static int local_queryoption(struct ast_channel *ast, int option, void *data, in } /* for some reason the channel is not locked in channel.c when this function is called */ - ast_channel_lock(ast); if (!(p = ast->tech_pvt)) { - ast_channel_unlock(ast); return -1; } ao2_lock(p); if (!(tmp = IS_OUTBOUND(ast, p) ? p->owner : p->chan)) { ao2_unlock(p); - ast_channel_unlock(ast); return -1; } ast_channel_ref(tmp); ao2_unlock(p); - ast_channel_unlock(ast); + ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */ ast_channel_lock(tmp); if (!(bridged = ast_bridged_channel(tmp))) { @@ -394,6 +389,7 @@ query_cleanup: if (tmp) { tmp = ast_channel_unref(tmp); } + ast_channel_lock(ast); /* Lock back before we leave */ return res; } diff --git a/channels/chan_sip.c b/channels/chan_sip.c index bcb4ea73a..4c0e2a66c 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -4262,15 +4262,23 @@ static int sip_setoption(struct ast_channel *chan, int option, void *data, int d int res = -1; struct sip_pvt *p = chan->tech_pvt; + sip_pvt_lock(p); + switch (option) { case AST_OPTION_FORMAT_READ: - res = ast_rtp_instance_set_read_format(p->rtp, (struct ast_format *) data); + if (p->rtp) { + res = ast_rtp_instance_set_read_format(p->rtp, (struct ast_format *) data); + } break; case AST_OPTION_FORMAT_WRITE: - res = ast_rtp_instance_set_write_format(p->rtp, (struct ast_format *) data); + if (p->rtp) { + res = ast_rtp_instance_set_write_format(p->rtp, (struct ast_format *) data); + } break; case AST_OPTION_MAKE_COMPATIBLE: - res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data); + if (p->rtp) { + res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data); + } break; case AST_OPTION_DIGIT_DETECT: if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || @@ -4298,6 +4306,8 @@ static int sip_setoption(struct ast_channel *chan, int option, void *data, int d break; } + sip_pvt_unlock(p); + return res; } @@ -4309,16 +4319,16 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int struct sip_pvt *p = (struct sip_pvt *) chan->tech_pvt; char *cp; + sip_pvt_lock(p); + switch (option) { case AST_OPTION_T38_STATE: /* Make sure we got an ast_t38_state enum passed in */ if (*datalen != sizeof(enum ast_t38_state)) { ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option. Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen); - return -1; + break; } - sip_pvt_lock(p); - /* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */ if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) { switch (p->t38.state) { @@ -4337,8 +4347,6 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int } } - sip_pvt_unlock(p); - *((enum ast_t38_state *) data) = state; res = 0; @@ -4370,6 +4378,8 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int break; } + sip_pvt_unlock(p); + return res; } @@ -5061,6 +5071,7 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog) } ast_rtp_instance_set_timeout(dialog->vrtp, global_rtptimeout); ast_rtp_instance_set_hold_timeout(dialog->vrtp, global_rtpholdtimeout); + ast_rtp_instance_set_keepalive(dialog->vrtp, global_rtpholdtimeout); ast_rtp_instance_set_prop(dialog->vrtp, AST_RTP_PROPERTY_RTCP, 1); } @@ -5071,12 +5082,14 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog) } ast_rtp_instance_set_timeout(dialog->trtp, global_rtptimeout); ast_rtp_instance_set_hold_timeout(dialog->trtp, global_rtpholdtimeout); + ast_rtp_instance_set_keepalive(dialog->trtp, global_rtpholdtimeout); ast_rtp_instance_set_prop(dialog->trtp, AST_RTP_PROPERTY_RTCP, 1); } ast_rtp_instance_set_timeout(dialog->rtp, global_rtptimeout); ast_rtp_instance_set_hold_timeout(dialog->rtp, global_rtpholdtimeout); + ast_rtp_instance_set_keepalive(dialog->rtp, global_rtpkeepalive); ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_RTCP, 1); ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); @@ -5144,6 +5157,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); ast_rtp_instance_set_timeout(dialog->rtp, peer->rtptimeout); ast_rtp_instance_set_hold_timeout(dialog->rtp, peer->rtpholdtimeout); + ast_rtp_instance_set_keepalive(dialog->rtp, peer->rtpkeepalive); /* Set Frame packetization */ ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(dialog->rtp), dialog->rtp, &dialog->prefs); dialog->autoframing = peer->autoframing; @@ -5151,10 +5165,12 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) if (dialog->vrtp) { /* Video */ ast_rtp_instance_set_timeout(dialog->vrtp, peer->rtptimeout); ast_rtp_instance_set_hold_timeout(dialog->vrtp, peer->rtpholdtimeout); + ast_rtp_instance_set_keepalive(dialog->vrtp, peer->rtpkeepalive); } if (dialog->trtp) { /* Realtime text */ ast_rtp_instance_set_timeout(dialog->trtp, peer->rtptimeout); ast_rtp_instance_set_hold_timeout(dialog->trtp, peer->rtpholdtimeout); + ast_rtp_instance_set_keepalive(dialog->trtp, peer->rtpkeepalive); } /* XXX TODO: get fields directly from peer only as they are needed using dialog->relatedpeer */ @@ -6344,7 +6360,11 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame) } if (p) { sip_pvt_lock(p); - if (p->rtp) { + if (p->t38.state == T38_ENABLED) { + /* drop frame, can't sent VOICE frames while in T.38 mode */ + sip_pvt_unlock(p); + break; + } else if (p->rtp) { /* If channel is not up, activate early media session */ if ((ast->_state != AST_STATE_UP) && !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && @@ -6355,12 +6375,9 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame) transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE); ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); } - } else if (p->t38.state == T38_ENABLED) { - /* drop frame, can't sent VOICE frames while in T.38 mode */ - } else { - p->lastrtptx = time(NULL); - res = ast_rtp_instance_write(p->rtp, frame); } + p->lastrtptx = time(NULL); + res = ast_rtp_instance_write(p->rtp, frame); } sip_pvt_unlock(p); } @@ -7274,12 +7291,19 @@ static struct ast_frame *sip_read(struct ast_channel *ast) /* If we detect a CNG tone and fax detection is enabled then send us off to the fax extension */ if (faxdetected && ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) { - ast_channel_lock(ast); if (strcmp(ast->exten, "fax")) { const char *target_context = S_OR(ast->macrocontext, ast->context); + /* We need to unlock 'ast' here because + * ast_exists_extension has the potential to start and + * stop an autoservice on the channel. Such action is + * prone to deadlock if the channel is locked. + */ + sip_pvt_unlock(p); ast_channel_unlock(ast); if (ast_exists_extension(ast, target_context, "fax", 1, S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, NULL))) { + ast_channel_lock(ast); + sip_pvt_lock(p); ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension due to CNG detection\n", ast->name); pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten); if (ast_async_goto(ast, target_context, "fax", 1)) { @@ -7287,10 +7311,10 @@ static struct ast_frame *sip_read(struct ast_channel *ast) } fr = &ast_null_frame; } else { + ast_channel_lock(ast); + sip_pvt_lock(p); ast_log(LOG_NOTICE, "FAX CNG detected but no fax extension\n"); } - } else { - ast_channel_unlock(ast); } } @@ -15749,8 +15773,12 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, p->callingpres = peer->callingpres; } ast_string_field_set(p, fullcontact, peer->fullcontact); - if (!ast_strlen_zero(peer->context)) + if (!ast_strlen_zero(peer->context)) { ast_string_field_set(p, context, peer->context); + } + if (!ast_strlen_zero(peer->messagecontext)) { + ast_string_field_set(p, messagecontext, peer->messagecontext); + } ast_string_field_set(p, peersecret, peer->secret); ast_string_field_set(p, peermd5secret, peer->md5secret); ast_string_field_set(p, language, peer->language); @@ -16080,6 +16108,10 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a if (ast_strlen_zero(peer->secret) && ast_strlen_zero(peer->md5secret)) { ast_string_field_set(p, context, peer->context); } + if (!ast_strlen_zero(peer->messagecontext)) { + ast_string_field_set(p, messagecontext, peer->messagecontext); + } + ast_string_field_set(p, peername, peer->name); peer = unref_peer(peer, "from find_peer() in receive_message"); } } @@ -16100,7 +16132,19 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a res = ast_msg_set_to(msg, "%s", to); res |= ast_msg_set_from(msg, "%s", get_in_brackets(from)); res |= ast_msg_set_body(msg, "%s", ast_str_buffer(buf)); - res |= ast_msg_set_context(msg, "%s", p->context); + + if (!ast_strlen_zero(p->messagecontext)) { + res |= ast_msg_set_context(msg, "%s", p->messagecontext); + } else if (!ast_strlen_zero(sip_cfg.messagecontext)) { + res |= ast_msg_set_context(msg, "%s", sip_cfg.messagecontext); + } else { + res |= ast_msg_set_context(msg, "%s", p->context); + } + + if (!ast_strlen_zero(p->peername)) { + res |= ast_msg_set_var(msg, "SIP_PEERNAME", p->peername); + } + res |= ast_msg_set_exten(msg, "%s", p->exten); if (res) { @@ -25368,11 +25412,19 @@ static void check_rtp_timeout(struct sip_pvt *dialog, time_t t) } /* If we have no timers set, return now */ - if (!ast_rtp_instance_get_timeout(dialog->rtp) && !ast_rtp_instance_get_hold_timeout(dialog->rtp)) { + if (!ast_rtp_instance_get_keepalive(dialog->rtp) && !ast_rtp_instance_get_timeout(dialog->rtp) && !ast_rtp_instance_get_hold_timeout(dialog->rtp)) { dialog_unlink_rtpcheck(dialog); return; } + /* Check AUDIO RTP keepalives */ + if (dialog->lastrtptx && ast_rtp_instance_get_keepalive(dialog->rtp) && + (t > dialog->lastrtptx + ast_rtp_instance_get_keepalive(dialog->rtp))) { + /* Need to send an empty RTP packet */ + dialog->lastrtptx = time(NULL); + ast_rtp_instance_sendcng(dialog->rtp, 0); + } + /*! \todo Check video RTP keepalives Do we need to move the lastrtptx to the RTP structure to have one for audio and one @@ -26671,6 +26723,7 @@ static void set_peer_defaults(struct sip_peer *peer) ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY); ast_copy_flags(&peer->flags[2], &global_flags[2], SIP_PAGE3_FLAGS_TO_COPY); ast_string_field_set(peer, context, sip_cfg.default_context); + ast_string_field_set(peer, messagecontext, sip_cfg.messagecontext); ast_string_field_set(peer, subscribecontext, sip_cfg.default_subscribecontext); ast_string_field_set(peer, language, default_language); ast_string_field_set(peer, mohinterpret, default_mohinterpret); @@ -26965,6 +27018,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str } else if (!strcasecmp(v->name, "context")) { ast_string_field_set(peer, context, v->value); ast_set_flag(&peer->flags[1], SIP_PAGE2_HAVEPEERCONTEXT); + } else if (!strcasecmp(v->name, "outofcall_message_context")) { + ast_string_field_set(peer, messagecontext, v->value); } else if (!strcasecmp(v->name, "subscribecontext")) { ast_string_field_set(peer, subscribecontext, v->value); } else if (!strcasecmp(v->name, "fromdomain")) { @@ -27351,7 +27406,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str * specified, use that address instead. */ /* XXX May need to revisit the final argument; does the realtime DB store whether * the original contact was over TLS or not? XXX */ - if (!ast_test_flag(&peer->flags[0], SIP_NAT_RPORT_PRESENT) || ast_sockaddr_isnull(&peer->addr)) { + if (!ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) || ast_sockaddr_isnull(&peer->addr)) { __set_address_from_contact(fullcontact->str, &peer->addr, 0); } } @@ -27653,6 +27708,7 @@ static int reload_config(enum channelreloadreason reason) sip_cfg.alwaysauthreject = DEFAULT_ALWAYSAUTHREJECT; sip_cfg.auth_options_requests = DEFAULT_AUTH_OPTIONS; sip_cfg.auth_message_requests = DEFAULT_AUTH_MESSAGE; + sip_cfg.messagecontext[0] = '\0'; sip_cfg.accept_outofcall_message = DEFAULT_ACCEPT_OUTOFCALL_MESSAGE; sip_cfg.allowsubscribe = FALSE; sip_cfg.disallowed_methods = SIP_UNKNOWN; @@ -27906,6 +27962,8 @@ static int reload_config(enum channelreloadreason reason) sip_cfg.auth_message_requests = ast_true(v->value) ? 1 : 0; } else if (!strcasecmp(v->name, "accept_outofcall_message")) { sip_cfg.accept_outofcall_message = ast_true(v->value) ? 1 : 0; + } else if (!strcasecmp(v->name, "outofcall_message_context")) { + ast_copy_string(sip_cfg.messagecontext, v->value, sizeof(sip_cfg.messagecontext)); } else if (!strcasecmp(v->name, "mohinterpret")) { ast_copy_string(default_mohinterpret, v->value, sizeof(default_mohinterpret)); } else if (!strcasecmp(v->name, "mohsuggest")) { diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index b90ba7eba..ea7d16c9e 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -1126,6 +1126,10 @@ static int callnums = 1; #define SKINNY_ALERT 0x24 #define SKINNY_REORDER 0x25 #define SKINNY_CALLWAITTONE 0x2D +#define SKINNY_ZIPZIP 0x31 +#define SKINNY_ZIP 0x32 +#define SKINNY_BEEPBONK 0x33 +#define SKINNY_BARGIN 0x43 #define SKINNY_NOTONE 0x7F #define SKINNY_LAMP_OFF 1 @@ -1164,18 +1168,10 @@ static const char * const skinny_cxmodes[] = { /* driver scheduler */ static struct ast_sched_context *sched = NULL; -static struct io_context *io; -/* Protect the monitoring thread, so only one process can kill or start it, and not - when it's doing something critical. */ -AST_MUTEX_DEFINE_STATIC(monlock); /* Protect the network socket */ AST_MUTEX_DEFINE_STATIC(netlock); -/* This is the thread for the monitor which checks for input on the channels - which are not currently in use. */ -static pthread_t monitor_thread = AST_PTHREADT_NULL; - /* Wait up to 16 seconds for first digit */ static int firstdigittimeout = 16000; @@ -1215,7 +1211,9 @@ struct skinny_subchannel { int blindxfer; int xferor; int substate; - + int aa_sched; + int aa_beep; + int aa_mute; AST_LIST_ENTRY(skinny_subchannel) list; struct skinny_subchannel *related; @@ -1712,6 +1710,16 @@ static struct ast_variable *add_var(const char *buf, struct ast_variable *list) return list; } +static int skinny_sched_del(int sched_id) +{ + return ast_sched_del(sched, sched_id); +} + +static int skinny_sched_add(int when, ast_sched_cb callback, const void *data) +{ + return ast_sched_add(sched, when, callback, data); +} + /* It's quicker/easier to find the subchannel when we know the instance number too */ static struct skinny_subchannel *find_subchannel_by_instance_reference(struct skinny_device *d, int instance, int reference) { @@ -2235,7 +2243,7 @@ static void transmit_speaker_mode(struct skinny_device *d, int mode) req->data.setspeaker.mode = htolel(mode); transmit_response(d, req); } -/* + static void transmit_microphone_mode(struct skinny_device *d, int mode) { struct skinny_req *req; @@ -2246,7 +2254,6 @@ static void transmit_microphone_mode(struct skinny_device *d, int mode) req->data.setmicrophone.mode = htolel(mode); transmit_response(d, req); } -*/ static void transmit_callinfo(struct skinny_subchannel *sub) { @@ -4044,7 +4051,13 @@ static void *skinny_ss(void *data) return NULL; } - +static int skinny_autoanswer_cb(const void *data) +{ + struct skinny_subchannel *sub = (struct skinny_subchannel *)data; + sub->aa_sched = 0; + setsubstate(sub, SKINNY_CONNECTED); + return 0; +} static int skinny_call(struct ast_channel *ast, char *dest, int timeout) { @@ -4052,6 +4065,8 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout) struct skinny_subchannel *sub = ast->tech_pvt; struct skinny_line *l = sub->line; struct skinny_device *d = l->device; + struct ast_var_t *current; + int doautoanswer = 0; if (!d->registered) { ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest); @@ -4075,8 +4090,40 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout) ast_queue_control(ast, AST_CONTROL_BUSY); return -1; } - + + AST_LIST_TRAVERSE(&ast->varshead, current, entries) { + if (!(strcasecmp(ast_var_name(current),"SKINNY_AUTOANSWER"))) { + if (d->hookstate == SKINNY_ONHOOK && !sub->aa_sched) { + char buf[24]; + int aatime; + char *stringp = buf, *curstr; + ast_copy_string(buf, ast_var_value(current), sizeof(buf)); + curstr = strsep(&stringp, ":"); + ast_verb(3, "test %s\n", curstr); + aatime = atoi(curstr); + while ((curstr = strsep(&stringp, ":"))) { + if (!(strcasecmp(curstr,"BEEP"))) { + sub->aa_beep = 1; + } else if (!(strcasecmp(curstr,"MUTE"))) { + sub->aa_mute = 1; + } + } + if (skinnydebug) + ast_verb(3, "Sub %d - setting autoanswer time=%dms %s%s\n", sub->callid, aatime, sub->aa_beep?"BEEP ":"", sub->aa_mute?"MUTE":""); + if (aatime) { + //sub->aa_sched = ast_sched_add(sched, aatime, skinny_autoanswer_cb, sub); + sub->aa_sched = skinny_sched_add(aatime, skinny_autoanswer_cb, sub); + } else { + doautoanswer = 1; + } + } + } + } + setsubstate(sub, SUBSTATE_RINGIN); + if (doautoanswer) { + setsubstate(sub, SUBSTATE_CONNECTED); + } return res; } @@ -4637,6 +4684,13 @@ static void setsubstate(struct skinny_subchannel *sub, int state) if (sub->substate == SUBSTATE_ONHOOK) { return; } + + if (state != SUBSTATE_RINGIN && sub->aa_sched) { + skinny_sched_del(sub->aa_sched); + sub->aa_sched = 0; + sub->aa_beep = 0; + sub->aa_mute = 0; + } if ((state == SUBSTATE_RINGIN) && ((d->hookstate == SKINNY_OFFHOOK) || (AST_LIST_NEXT(AST_LIST_FIRST(&l->sub), list)))) { actualstate = SUBSTATE_CALLWAIT; @@ -4789,6 +4843,7 @@ static void setsubstate(struct skinny_subchannel *sub, int state) ast_queue_control(sub->owner, AST_CONTROL_UNHOLD); transmit_connect(d, sub); } + transmit_ringer_mode(d, SKINNY_RING_OFF); transmit_activatecallplane(d, l); transmit_stop_tone(d, l->instance, sub->callid); transmit_callinfo(sub); @@ -4798,6 +4853,12 @@ static void setsubstate(struct skinny_subchannel *sub, int state) if (!sub->rtp) { start_rtp(sub); } + if (sub->aa_beep) { + transmit_start_tone(d, SKINNY_ZIP, l->instance, sub->callid); + } + if (sub->aa_mute) { + transmit_microphone_mode(d, SKINNY_MICOFF); + } if (sub->substate == SUBSTATE_RINGIN || sub->substate == SUBSTATE_CALLWAIT) { ast_queue_control(sub->owner, AST_CONTROL_ANSWER); } @@ -6499,59 +6560,6 @@ static void *accept_thread(void *ignore) return 0; } -static void *do_monitor(void *data) -{ - int res; - - /* This thread monitors all the interfaces which are not yet in use - (and thus do not have a separate thread) indefinitely */ - /* From here on out, we die whenever asked */ - for(;;) { - pthread_testcancel(); - /* Wait for sched or io */ - res = ast_sched_wait(sched); - if ((res < 0) || (res > 1000)) { - res = 1000; - } - res = ast_io_wait(io, res); - ast_mutex_lock(&monlock); - if (res >= 0) { - ast_sched_runq(sched); - } - ast_mutex_unlock(&monlock); - } - /* Never reached */ - return NULL; - -} - -static int restart_monitor(void) -{ - /* If we're supposed to be stopped -- stay stopped */ - if (monitor_thread == AST_PTHREADT_STOP) - return 0; - - ast_mutex_lock(&monlock); - if (monitor_thread == pthread_self()) { - ast_mutex_unlock(&monlock); - ast_log(LOG_WARNING, "Cannot kill myself\n"); - return -1; - } - if (monitor_thread != AST_PTHREADT_NULL) { - /* Wake up the thread */ - pthread_kill(monitor_thread, SIGURG); - } else { - /* Start a new monitor */ - if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) { - ast_mutex_unlock(&monlock); - ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); - return -1; - } - } - ast_mutex_unlock(&monlock); - return 0; -} - static int skinny_devicestate(void *data) { struct skinny_line *l; @@ -6591,7 +6599,6 @@ static struct ast_channel *skinny_request(const char *type, struct ast_format_ca if (!tmpc) { ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp); } - restart_monitor(); return tmpc; } @@ -7425,13 +7432,13 @@ static int load_module(void) sched = ast_sched_context_create(); if (!sched) { ast_log(LOG_WARNING, "Unable to create schedule context\n"); + return AST_MODULE_LOAD_FAILURE; } - io = io_context_create(); - if (!io) { - ast_log(LOG_WARNING, "Unable to create I/O context\n"); + if (ast_sched_start_thread(sched)) { + ast_sched_context_destroy(sched); + sched = NULL; + return AST_MODULE_LOAD_FAILURE; } - /* And start the monitor for the first time */ - restart_monitor(); return AST_MODULE_LOAD_SUCCESS; } @@ -7483,15 +7490,6 @@ static int unload_module(void) delete_devices(); - ast_mutex_lock(&monlock); - if ((monitor_thread != AST_PTHREADT_NULL) && (monitor_thread != AST_PTHREADT_STOP)) { - pthread_cancel(monitor_thread); - pthread_kill(monitor_thread, SIGURG); - pthread_join(monitor_thread, NULL); - } - monitor_thread = AST_PTHREADT_STOP; - ast_mutex_unlock(&monlock); - ast_mutex_lock(&netlock); if (accept_t && (accept_t != AST_PTHREADT_STOP)) { pthread_cancel(accept_t); diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index 0eb8be350..e8dba3067 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -691,6 +691,7 @@ struct sip_settings { int legacy_useroption_parsing; /*!< Whether to strip useroptions in URI via semicolons */ int matchexternaddrlocally; /*!< Match externaddr/externhost setting against localnet setting */ char regcontext[AST_MAX_CONTEXT]; /*!< Context for auto-extensions */ + char messagecontext[AST_MAX_CONTEXT]; /*!< Default context for out of dialog msgs. */ unsigned int disallowed_methods; /*!< methods that we should never try to use */ int notifyringing; /*!< Send notifications on ringing */ int notifyhold; /*!< Send notifications on hold */ @@ -939,6 +940,7 @@ struct sip_pvt { AST_STRING_FIELD(useragent); /*!< User agent in SIP request */ AST_STRING_FIELD(exten); /*!< Extension where to start */ AST_STRING_FIELD(context); /*!< Context for this call */ + AST_STRING_FIELD(messagecontext); /*!< Default context for outofcall messages. */ AST_STRING_FIELD(subscribecontext); /*!< Subscribecontext */ AST_STRING_FIELD(subscribeuri); /*!< Subscribecontext */ AST_STRING_FIELD(fromdomain); /*!< Domain to show in the from field */ @@ -1172,6 +1174,7 @@ struct sip_peer { AST_STRING_FIELD(description); /*!< Description of this peer */ AST_STRING_FIELD(remotesecret); /*!< Remote secret (trunks, remote devices) */ AST_STRING_FIELD(context); /*!< Default context for incoming calls */ + AST_STRING_FIELD(messagecontext); /*!< Default context for outofcall messages. */ AST_STRING_FIELD(subscribecontext); /*!< Default context for subscriptions */ AST_STRING_FIELD(username); /*!< Temporary username until registration */ AST_STRING_FIELD(accountcode); /*!< Account code */ diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index 49277d64f..21235a6ef 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -389,6 +389,11 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; call. By default, this option is enabled. When enabled, MESSAGE ; requests are passed in to the dialplan. +;outofcall_message_context = messages ; Context all out of dialog msgs are sent to. When this + ; option is not set, the context used during peer matching + ; is used. This option can be defined at both the peer and + ; global level. + ;auth_message_requests = yes ; Enabling this option will authenticate MESSAGE requests. ; By default this option is enabled. However, it can be disabled ; should an application desire to not load the Asterisk server with diff --git a/include/asterisk/astdb.h b/include/asterisk/astdb.h index 4c1fa5890..cfbebbc30 100644 --- a/include/asterisk/astdb.h +++ b/include/asterisk/astdb.h @@ -47,8 +47,8 @@ int ast_db_del(const char *family, const char *key); * only keytree is NULL, all entries within the family will be purged. * It is an error for keytree to have a value when family is NULL. * - * \retval 0 Entries were deleted * \retval -1 An error occurred + * \retval >= 0 Number of records deleted */ int ast_db_deltree(const char *family, const char *keytree); diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 5798e92bb..031c6f7e9 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -576,10 +576,10 @@ struct ast_channel_tech { /*! \brief Fix up a channel: If a channel is consumed, this is called. Basically update any ->owner links */ int (* const fixup)(struct ast_channel *oldchan, struct ast_channel *newchan); - /*! \brief Set a given option */ + /*! \brief Set a given option. Called with chan locked */ int (* const setoption)(struct ast_channel *chan, int option, void *data, int datalen); - /*! \brief Query a given option */ + /*! \brief Query a given option. Called with chan locked */ int (* const queryoption)(struct ast_channel *chan, int option, void *data, int *datalen); /*! \brief Blind transfer other side (see app_transfer.c and ast_transfer() */ diff --git a/include/asterisk/features.h b/include/asterisk/features.h index 4ea941383..7e749f2e0 100644 --- a/include/asterisk/features.h +++ b/include/asterisk/features.h @@ -119,12 +119,28 @@ const char *ast_pickup_ext(void); /*! \brief Bridge a call, optionally allowing redirection */ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config); +/*! + * \brief Test if a channel can be picked up. + * + * \param chan Channel to test if can be picked up. + * + * \note This function assumes that chan is locked. + * + * \return TRUE if channel can be picked up. + */ +int ast_can_pickup(struct ast_channel *chan); + /*! \brief Pickup a call */ int ast_pickup_call(struct ast_channel *chan); /*! - * \brief Pickup a call target - * \note This function assumes that target is locked + * \brief Pickup a call target. + * + * \param chan channel that initiated pickup. + * \param target channel to be picked up. + * + * \note This function assumes that target is locked. + * * \retval 0 on success. * \retval -1 on failure. */ diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index f8bf74931..e04303ca6 100644 --- a/include/asterisk/rtp_engine.h +++ b/include/asterisk/rtp_engine.h @@ -377,6 +377,8 @@ struct ast_rtp_engine { void (*stun_request)(struct ast_rtp_instance *instance, struct ast_sockaddr *suggestion, const char *username); /*! Callback to get the transcodeable formats supported. result returned in ast_format_cap *result */ void (*available_formats)(struct ast_rtp_instance *instance, struct ast_format_cap *to_endpoint, struct ast_format_cap *to_asterisk, struct ast_format_cap *result); + /*! Callback to send CNG */ + int (*sendcng)(struct ast_rtp_instance *instance, int level); /*! Linked list information */ AST_RWLIST_ENTRY(ast_rtp_engine) entry; }; @@ -1713,6 +1715,24 @@ void ast_rtp_instance_set_timeout(struct ast_rtp_instance *instance, int timeout void ast_rtp_instance_set_hold_timeout(struct ast_rtp_instance *instance, int timeout); /*! + * \brief Set the RTP keepalive interval + * + * \param instance The RTP instance + * \param period Value to set the keepalive interval to + * + * Example usage: + * + * \code + * ast_rtp_instance_set_keepalive(instance, 5000); + * \endcode + * + * This sets the RTP keepalive interval on 'instance' to be 5000. + * + * \since 1.8 + */ +void ast_rtp_instance_set_keepalive(struct ast_rtp_instance *instance, int timeout); + +/*! * \brief Get the RTP timeout value * * \param instance The RTP instance @@ -1751,6 +1771,25 @@ int ast_rtp_instance_get_timeout(struct ast_rtp_instance *instance); int ast_rtp_instance_get_hold_timeout(struct ast_rtp_instance *instance); /*! + * \brief Get the RTP keepalive interval + * + * \param instance The RTP instance + * + * \retval period Keepalive interval value + * + * Example usage: + * + * \code + * int interval = ast_rtp_instance_get_keepalive(instance); + * \endcode + * + * This gets the RTP keepalive interval value for the RTP instance pointed to by 'instance'. + * + * \since 1.8 + */ +int ast_rtp_instance_get_keepalive(struct ast_rtp_instance *instance); + +/*! * \brief Get the RTP engine in use on an RTP instance * * \param instance The RTP instance @@ -1809,6 +1848,17 @@ struct ast_rtp_glue *ast_rtp_instance_get_active_glue(struct ast_rtp_instance *i */ struct ast_channel *ast_rtp_instance_get_chan(struct ast_rtp_instance *instance); +/*! + * \brief Send a comfort noise packet to the RTP instance + * + * \param instance The RTP instance + * \param level Magnitude of the noise level + * + * \retval 0 Success + * \retval non-zero Failure + */ +int ast_rtp_instance_sendcng(struct ast_rtp_instance *instance, int level); + int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy *policy); struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance); diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h index 823af26c4..44eeb5f8e 100644 --- a/include/asterisk/utils.h +++ b/include/asterisk/utils.h @@ -682,7 +682,7 @@ void ast_enable_packet_fragmentation(int sock); */ int ast_mkdir(const char *path, int mode); -#define ARRAY_LEN(a) (sizeof(a) / sizeof(0[a])) +#define ARRAY_LEN(a) (size_t) (sizeof(a) / sizeof(0[a])) /* Definition for Digest authorization */ diff --git a/main/channel.c b/main/channel.c index 9c9d6ca5a..57629dbc5 100644 --- a/main/channel.c +++ b/main/channel.c @@ -7387,7 +7387,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha config->nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000)); if ((caller_warning || callee_warning) && config->play_warning) { long next_warn = config->play_warning; - if (time_left_ms < config->play_warning) { + if (time_left_ms < config->play_warning && config->warning_freq > 0) { /* At least one warning was played, which means we are returning after feature */ long warns_passed = (config->play_warning - time_left_ms) / config->warning_freq; /* It is 'warns_passed * warning_freq' NOT '(warns_passed + 1) * warning_freq', @@ -7608,28 +7608,42 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha /*! \brief Sets an option on a channel */ int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block) { + int res; + + ast_channel_lock(chan); if (!chan->tech->setoption) { errno = ENOSYS; + ast_channel_unlock(chan); return -1; } if (block) ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n"); - return chan->tech->setoption(chan, option, data, datalen); + res = chan->tech->setoption(chan, option, data, datalen); + ast_channel_unlock(chan); + + return res; } int ast_channel_queryoption(struct ast_channel *chan, int option, void *data, int *datalen, int block) { + int res; + + ast_channel_lock(chan); if (!chan->tech->queryoption) { errno = ENOSYS; + ast_channel_unlock(chan); return -1; } if (block) ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n"); - return chan->tech->queryoption(chan, option, data, datalen); + res = chan->tech->queryoption(chan, option, data, datalen); + ast_channel_unlock(chan); + + return res; } struct tonepair_def { @@ -100,9 +100,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </manager> ***/ +#define MAX_DB_FIELD 256 + static DB *astdb; AST_MUTEX_DEFINE_STATIC(dblock); static ast_cond_t dbcond; +typedef int (*process_keys_cb)(DBT *key, DBT *value, const char *filter, void *data); static void db_sync(void); @@ -143,15 +146,84 @@ static inline int subkeymatch(const char *key, const char *suffix) return 0; } -int ast_db_deltree(const char *family, const char *keytree) +static const char *dbt_data2str(DBT *dbt) { - char prefix[256]; - DBT key, data; - char *keys; - int res; - int pass; + char *data = ""; + + if (dbt->size) { + data = dbt->data; + data[dbt->size - 1] = '\0'; + } + + return data; +} + +static inline const char *dbt_data2str_full(DBT *dbt, const char *def) +{ + return S_OR(dbt_data2str(dbt), def); +} + +static int process_db_keys(process_keys_cb cb, void *data, const char *filter, int sync) +{ + DBT key = { 0, }, value = { 0, }, last_key = { 0, }; int counter = 0; - + int res, last = 0; + char last_key_s[MAX_DB_FIELD]; + + ast_mutex_lock(&dblock); + if (dbinit()) { + ast_mutex_unlock(&dblock); + return -1; + } + + /* Somehow, the database can become corrupted such that astdb->seq will continue looping through + * the database indefinitely. The pointer to last_key.data ends up getting re-used by the BDB lib + * so this specifically queries for the last entry, makes a copy of the key, and then uses it as + * a sentinel to avoid repeatedly looping over the list. */ + + if (astdb->seq(astdb, &last_key, &value, R_LAST)) { + /* Empty database */ + ast_mutex_unlock(&dblock); + return 0; + } + + memcpy(last_key_s, last_key.data, MIN(last_key.size - 1, sizeof(last_key_s))); + last_key_s[last_key.size - 1] = '\0'; + for (res = astdb->seq(astdb, &key, &value, R_FIRST); + !res; + res = astdb->seq(astdb, &key, &value, R_NEXT)) { + /* The callback might delete the key, so we have to check it before calling */ + last = !strcmp(dbt_data2str_full(&key, "<bad key>"), last_key_s); + counter += cb(&key, &value, filter, data); + if (last) { + break; + } + } + + if (sync) { + db_sync(); + } + + ast_mutex_unlock(&dblock); + + return counter; +} + +static int db_deltree_cb(DBT *key, DBT *value, const char *filter, void *data) +{ + int res = 0; + + if (keymatch(dbt_data2str_full(key, "<bad key>"), filter)) { + astdb->del(astdb, key, 0); + res = 1; + } + return res; +} + +int ast_db_deltree(const char *family, const char *keytree) +{ + char prefix[MAX_DB_FIELD]; + if (family) { if (keytree) { snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree); @@ -163,36 +235,13 @@ int ast_db_deltree(const char *family, const char *keytree) } else { prefix[0] = '\0'; } - - ast_mutex_lock(&dblock); - if (dbinit()) { - ast_mutex_unlock(&dblock); - return -1; - } - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - pass = 0; - while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) { - if (key.size) { - keys = key.data; - keys[key.size - 1] = '\0'; - } else { - keys = "<bad key>"; - } - if (keymatch(keys, prefix)) { - astdb->del(astdb, &key, 0); - counter++; - } - } - db_sync(); - ast_mutex_unlock(&dblock); - return counter; + + return process_db_keys(db_deltree_cb, NULL, prefix, 1); } int ast_db_put(const char *family, const char *keys, const char *value) { - char fullkey[256]; + char fullkey[MAX_DB_FIELD]; DBT key, data; int res, fullkeylen; @@ -214,12 +263,13 @@ int ast_db_put(const char *family, const char *keys, const char *value) ast_mutex_unlock(&dblock); if (res) ast_log(LOG_WARNING, "Unable to put value '%s' for key '%s' in family '%s'\n", value, keys, family); + return res; } int ast_db_get(const char *family, const char *keys, char *value, int valuelen) { - char fullkey[256] = ""; + char fullkey[MAX_DB_FIELD] = ""; DBT key, data; int res, fullkeylen; @@ -263,7 +313,7 @@ int ast_db_get(const char *family, const char *keys, char *value, int valuelen) int ast_db_del(const char *family, const char *keys) { - char fullkey[256]; + char fullkey[MAX_DB_FIELD]; DBT key; int res, fullkeylen; @@ -319,7 +369,7 @@ static char *handle_cli_database_put(struct ast_cli_entry *e, int cmd, struct as static char *handle_cli_database_get(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { int res; - char tmp[256]; + char tmp[MAX_DB_FIELD]; switch (cmd) { case CLI_INIT: @@ -402,13 +452,23 @@ static char *handle_cli_database_deltree(struct ast_cli_entry *e, int cmd, struc return CLI_SUCCESS; } +static int db_show_cb(DBT *key, DBT *value, const char *filter, void *data) +{ + struct ast_cli_args *a = data; + const char *key_s = dbt_data2str_full(key, "<bad key>"); + const char *value_s = dbt_data2str_full(value, "<bad value>"); + + if (keymatch(key_s, filter)) { + ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s); + return 1; + } + + return 0; +} + static char *handle_cli_database_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char prefix[256]; - DBT key, data; - char *keys, *values; - int res; - int pass; + char prefix[MAX_DB_FIELD]; int counter = 0; switch (cmd) { @@ -435,45 +495,33 @@ static char *handle_cli_database_show(struct ast_cli_entry *e, int cmd, struct a } else { return CLI_SHOWUSAGE; } - ast_mutex_lock(&dblock); - if (dbinit()) { - ast_mutex_unlock(&dblock); + + if((counter = process_db_keys(db_show_cb, a, prefix, 0)) < 0) { ast_cli(a->fd, "Database unavailable\n"); - return CLI_SUCCESS; - } - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - pass = 0; - while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) { - if (key.size) { - keys = key.data; - keys[key.size - 1] = '\0'; - } else { - keys = "<bad key>"; - } - if (data.size) { - values = data.data; - values[data.size - 1]='\0'; - } else { - values = "<bad value>"; - } - if (keymatch(keys, prefix)) { - ast_cli(a->fd, "%-50s: %-25s\n", keys, values); - counter++; - } + return CLI_SUCCESS; } - ast_mutex_unlock(&dblock); + ast_cli(a->fd, "%d results found.\n", counter); - return CLI_SUCCESS; + return CLI_SUCCESS; +} + +static int db_showkey_cb(DBT *key, DBT *value, const char *filter, void *data) +{ + struct ast_cli_args *a = data; + const char *key_s = dbt_data2str_full(key, "<bad key>"); + const char *value_s = dbt_data2str_full(value, "<bad value>"); + + if (subkeymatch(key_s, filter)) { + ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s); + return 1; + } + + return 0; } static char *handle_cli_database_showkey(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char suffix[256]; - DBT key, data; - char *keys, *values; - int res; - int pass; + char suffix[MAX_DB_FIELD]; int counter = 0; switch (cmd) { @@ -493,48 +541,40 @@ static char *handle_cli_database_showkey(struct ast_cli_entry *e, int cmd, struc } else { return CLI_SHOWUSAGE; } - ast_mutex_lock(&dblock); - if (dbinit()) { - ast_mutex_unlock(&dblock); + + if ((counter = process_db_keys(db_showkey_cb, a, suffix, 0)) < 0) { ast_cli(a->fd, "Database unavailable\n"); - return CLI_SUCCESS; - } - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - pass = 0; - while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) { - if (key.size) { - keys = key.data; - keys[key.size - 1] = '\0'; - } else { - keys = "<bad key>"; - } - if (data.size) { - values = data.data; - values[data.size - 1]='\0'; - } else { - values = "<bad value>"; - } - if (subkeymatch(keys, suffix)) { - ast_cli(a->fd, "%-50s: %-25s\n", keys, values); - counter++; - } + return CLI_SUCCESS; } - ast_mutex_unlock(&dblock); + ast_cli(a->fd, "%d results found.\n", counter); - return CLI_SUCCESS; + return CLI_SUCCESS; +} + +static int db_gettree_cb(DBT *key, DBT *value, const char *filter, void *data) +{ + struct ast_db_entry **ret = data; + struct ast_db_entry *cur; + const char *key_s = dbt_data2str_full(key, "<bad key>"); + const char *value_s = dbt_data2str_full(value, "<bad value>"); + size_t key_slen = strlen(key_s) + 1, value_slen = strlen(value_s) + 1; + + if (keymatch(key_s, filter) && (cur = ast_malloc(sizeof(*cur) + key_slen + value_slen))) { + cur->next = *ret; + cur->key = cur->data + value_slen; + strcpy(cur->data, value_s); + strcpy(cur->key, key_s); + *ret = cur; + return 1; + } + + return 0; } struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree) { - char prefix[256]; - DBT key, data; - char *keys, *values; - int values_len; - int res; - int pass; - struct ast_db_entry *last = NULL; - struct ast_db_entry *cur, *ret=NULL; + char prefix[MAX_DB_FIELD]; + struct ast_db_entry *ret = NULL; if (!ast_strlen_zero(family)) { if (!ast_strlen_zero(keytree)) { @@ -547,44 +587,13 @@ struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree) } else { prefix[0] = '\0'; } - ast_mutex_lock(&dblock); - if (dbinit()) { - ast_mutex_unlock(&dblock); + + if (process_db_keys(db_gettree_cb, &ret, prefix, 0) < 0) { ast_log(LOG_WARNING, "Database unavailable\n"); - return NULL; - } - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - pass = 0; - while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) { - if (key.size) { - keys = key.data; - keys[key.size - 1] = '\0'; - } else { - keys = "<bad key>"; - } - if (data.size) { - values = data.data; - values[data.size - 1] = '\0'; - } else { - values = "<bad value>"; - } - values_len = strlen(values) + 1; - if (keymatch(keys, prefix) && (cur = ast_malloc(sizeof(*cur) + strlen(keys) + 1 + values_len))) { - cur->next = NULL; - cur->key = cur->data + values_len; - strcpy(cur->data, values); - strcpy(cur->key, keys); - if (last) { - last->next = cur; - } else { - ret = cur; - } - last = cur; - } + return NULL; } - ast_mutex_unlock(&dblock); - return ret; + + return ret; } void ast_db_freetree(struct ast_db_entry *dbe) @@ -637,7 +646,7 @@ static int manager_dbget(struct mansession *s, const struct message *m) char idText[256] = ""; const char *family = astman_get_header(m, "Family"); const char *key = astman_get_header(m, "Key"); - char tmp[256]; + char tmp[MAX_DB_FIELD]; int res; if (ast_strlen_zero(family)) { diff --git a/main/dnsmgr.c b/main/dnsmgr.c index 23e1ab6ef..999bd92f4 100644 --- a/main/dnsmgr.c +++ b/main/dnsmgr.c @@ -131,6 +131,14 @@ int ast_dnsmgr_lookup(const char *name, struct ast_sockaddr *result, struct ast_ return 0; } + /* + * If it's actually an IP address and not a name, there's no + * need for a managed lookup. + */ + if (ast_sockaddr_parse(result, name, PARSE_PORT_FORBID)) { + return 0; + } + ast_verb(4, "doing dnsmgr_lookup for '%s'\n", name); /* do a lookup now but add a manager so it will automagically get updated in the background */ diff --git a/main/event.c b/main/event.c index 246821eac..fad8e66ee 100644 --- a/main/event.c +++ b/main/event.c @@ -381,12 +381,12 @@ static int match_sub_ie_val_to_event(const struct ast_event_ie_val *sub_ie_val, int res = 0; AST_LIST_TRAVERSE(&check_ie_vals->ie_vals, event_ie_val, entry) { - if (event_ie_val->ie_type == sub_ie_val->ie_type) { + if (sub_ie_val->ie_type == event_ie_val->ie_type) { break; } } if (!event_ie_val) { - /* The did not find the event ie the subscriber cares about. */ + /* We did not find the event ie the subscriber cares about. */ return 0; } @@ -444,12 +444,14 @@ enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type typ }; const enum ast_event_type event_types[] = { type, AST_EVENT_ALL }; int i; + int want_specific_event;/* TRUE if looking for subscribers wanting specific parameters. */ if (type >= AST_EVENT_TOTAL) { ast_log(LOG_ERROR, "%u is an invalid type!\n", type); return res; } + want_specific_event = 0; va_start(ap, type); for (ie_type = va_arg(ap, enum ast_event_ie_type); ie_type != AST_EVENT_IE_END; @@ -492,6 +494,7 @@ enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type typ } if (insert) { + want_specific_event = 1; AST_LIST_INSERT_TAIL(&check_ie_vals.ie_vals, ie_value, entry); } else { ast_log(LOG_WARNING, "Unsupported PLTYPE(%d)\n", ie_value->ie_pltype); @@ -501,17 +504,22 @@ enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type typ for (i = 0; i < ARRAY_LEN(event_types); i++) { AST_RWDLLIST_RDLOCK(&ast_event_subs[event_types[i]]); - AST_RWDLLIST_TRAVERSE(&ast_event_subs[event_types[i]], sub, entry) { - AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) { - if (!match_sub_ie_val_to_event(ie_val, &check_ie_vals)) { - /* The current subscription ie did not match an event ie. */ + if (want_specific_event) { + AST_RWDLLIST_TRAVERSE(&ast_event_subs[event_types[i]], sub, entry) { + AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) { + if (!match_sub_ie_val_to_event(ie_val, &check_ie_vals)) { + /* The current subscription ie did not match an event ie. */ + break; + } + } + if (!ie_val) { + /* Everything matched. A subscriber is looking for this event. */ break; } } - if (!ie_val) { - /* Everything matched. A subscriber is looking for this event. */ - break; - } + } else { + /* Just looking to see if there are ANY subscribers to the event type. */ + sub = AST_RWLIST_FIRST(&ast_event_subs[event_types[i]]); } AST_RWDLLIST_UNLOCK(&ast_event_subs[event_types[i]]); if (sub) { @@ -1340,6 +1348,7 @@ struct ast_event *ast_event_get_cached(enum ast_event_type type, ...) void *data = va_arg(ap, void *); size_t datalen = va_arg(ap, size_t); ast_event_append_ie_raw(&cache_arg_event, ie_type, data, datalen); + break; } case AST_EVENT_IE_PLTYPE_EXISTS: ast_log(LOG_WARNING, "PLTYPE_EXISTS not supported by this function\n"); diff --git a/main/features.c b/main/features.c index 81421f17b..728118188 100644 --- a/main/features.c +++ b/main/features.c @@ -577,7 +577,7 @@ static const struct ast_datastore_info dial_features_info = { .type = "dial-features", .destroy = dial_features_destroy, .duplicate = dial_features_duplicate, - }; +}; /* Forward declarations */ static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot); @@ -2898,8 +2898,12 @@ int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, con /*! \brief Check if a feature exists */ static int feature_check(struct ast_channel *chan, struct ast_flags *features, char *code) { + char *chan_dynamic_features; + ast_channel_lock(chan); + chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),"")); + ast_channel_unlock(chan); - return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, FEATURE_INTERPRET_CHECK, NULL); + return feature_interpret_helper(chan, NULL, NULL, code, 0, chan_dynamic_features, features, FEATURE_INTERPRET_CHECK, NULL); } static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config) @@ -5747,17 +5751,42 @@ static int manager_park(struct mansession *s, const struct message *m) return 0; } +/*! + * The presence of this datastore on the channel indicates that + * someone is attemting to pickup or has picked up the channel. + * The purpose is to prevent a race between two channels + * attempting to pickup the same channel. + */ +static const struct ast_datastore_info pickup_active = { + .type = "pickup-active", +}; + +int ast_can_pickup(struct ast_channel *chan) +{ + if (!chan->pbx && !chan->masq && !ast_test_flag(chan, AST_FLAG_ZOMBIE) + && (chan->_state == AST_STATE_RINGING + || chan->_state == AST_STATE_RING + /* + * Check the down state as well because some SIP devices do not + * give 180 ringing when they can just give 183 session progress + * instead. Issue 14005. (Some ISDN switches as well for that + * matter.) + */ + || chan->_state == AST_STATE_DOWN) + && !ast_channel_datastore_find(chan, &pickup_active, NULL)) { + return 1; + } + return 0; +} + static int find_channel_by_group(void *obj, void *arg, void *data, int flags) { struct ast_channel *target = obj;/*!< Potential pickup target */ struct ast_channel *chan = data;/*!< Channel wanting to pickup call */ ast_channel_lock(target); - if (chan != target && (chan->pickupgroup & target->callgroup) && - !target->pbx && - ((target->_state == AST_STATE_RINGING) || (target->_state == AST_STATE_RING)) && - !target->masq && - !ast_test_flag(target, AST_FLAG_ZOMBIE)) { + if (chan != target && (chan->pickupgroup & target->callgroup) + && ast_can_pickup(target)) { /* Return with the channel still locked on purpose */ return CMP_MATCH | CMP_STOP; } @@ -5808,23 +5837,30 @@ int ast_pickup_call(struct ast_channel *chan) return res; } -/*! - * \brief Pickup a call target, Common Code. - * \param chan channel that initiated pickup. - * \param target channel. - * - * Answer calling channel, flag channel as answered on queue, masq channels together. - */ int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target) { struct ast_party_connected_line connected_caller; struct ast_channel *chans[2] = { chan, target }; + struct ast_datastore *ds_pickup; + const char *chan_name;/*!< A masquerade changes channel names. */ + const char *target_name;/*!< A masquerade changes channel names. */ + int res = -1; - ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name); - ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan); + target_name = ast_strdupa(target->name); + ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, chan->name); + + /* Mark the target to block any call pickup race. */ + ds_pickup = ast_datastore_alloc(&pickup_active, NULL); + if (!ds_pickup) { + ast_log(LOG_WARNING, + "Unable to create channel datastore on '%s' for call pickup\n", target_name); + return -1; + } + ast_channel_datastore_add(target, ds_pickup); ast_party_connected_line_init(&connected_caller); ast_party_connected_line_copy(&connected_caller, &target->connected); + ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; if (ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) { ast_channel_update_connected_line(chan, &connected_caller, NULL); @@ -5832,37 +5868,48 @@ int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target) ast_party_connected_line_free(&connected_caller); ast_channel_lock(chan); + chan_name = ast_strdupa(chan->name); ast_connected_line_copy_from_caller(&connected_caller, &chan->caller); ast_channel_unlock(chan); connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ast_channel_queue_connected_line_update(chan, &connected_caller, NULL); ast_party_connected_line_free(&connected_caller); + ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan); + if (ast_answer(chan)) { - ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name); - return -1; + ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name); + goto pickup_failed; } if (ast_queue_control(chan, AST_CONTROL_ANSWER)) { - ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name); - return -1; + ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name); + goto pickup_failed; } if (ast_channel_masquerade(target, chan)) { - ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name); - return -1; + ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name, + target_name); + goto pickup_failed; } /* If you want UniqueIDs, set channelvars in manager.conf to CHANNEL(uniqueid) */ ast_manager_event_multichan(EVENT_FLAG_CALL, "Pickup", 2, chans, - "Channel: %s\r\nTargetChannel: %s\r\n", chan->name, target->name); + "Channel: %s\r\n" + "TargetChannel: %s\r\n", + chan_name, target_name); - /* Do the masquerade manually to make sure that is is completed. */ - ast_channel_unlock(target); + /* Do the masquerade manually to make sure that it is completed. */ ast_do_masquerade(target); + res = 0; + +pickup_failed: ast_channel_lock(target); + if (!ast_channel_datastore_remove(target, ds_pickup)) { + ast_datastore_free(ds_pickup); + } - return 0; + return res; } static char *app_bridge = "Bridge"; diff --git a/main/manager.c b/main/manager.c index ea2e2a975..c4d1e1101 100644 --- a/main/manager.c +++ b/main/manager.c @@ -5480,6 +5480,39 @@ static void xml_translate(struct ast_str **out, char *in, struct ast_variable *g } } +static void process_output(struct mansession *s, struct ast_str *out, struct ast_variable *params, enum output_format format) +{ + char *buf; + size_t l; + + if (!s->f) + return; + + /* Ensure buffer is NULL-terminated */ + fprintf(s->f, "%c", 0); + fflush(s->f); + + if ((l = ftell(s->f))) { + if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, s->fd, 0))) { + ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n"); + } else { + if (format == FORMAT_XML || format == FORMAT_HTML) { + xml_translate(&out, buf, params, format); + } else { + ast_str_append(&out, 0, "%s", buf); + } + munmap(buf, l); + } + } else if (format == FORMAT_XML || format == FORMAT_HTML) { + xml_translate(&out, "", params, format); + } + + fclose(s->f); + s->f = NULL; + close(s->fd); + s->fd = -1; +} + static int generic_http_callback(struct ast_tcptls_session_instance *ser, enum ast_http_method method, enum output_format format, @@ -5629,29 +5662,7 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser, ast_str_append(&out, 0, ROW_FMT, TEST_STRING); } - if (s.f != NULL) { /* have temporary output */ - char *buf; - size_t l; - - if ((l = ftell(s.f))) { - if (MAP_FAILED == (buf = mmap(NULL, l + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, s.fd, 0))) { - ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n"); - } else { - buf[l] = '\0'; - if (format == FORMAT_XML || format == FORMAT_HTML) { - xml_translate(&out, buf, params, format); - } else { - ast_str_append(&out, 0, "%s", buf); - } - munmap(buf, l + 1); - } - } else if (format == FORMAT_XML || format == FORMAT_HTML) { - xml_translate(&out, "", params, format); - } - fclose(s.f); - s.f = NULL; - s.fd = -1; - } + process_output(&s, out, params, format); if (format == FORMAT_XML) { ast_str_append(&out, 0, "</ajax-response>\n"); @@ -5963,26 +5974,7 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser, "<input type=\"submit\" value=\"Send request\" /></th></tr>\r\n"); } - if (s.f != NULL) { /* have temporary output */ - char *buf; - size_t l = ftell(s.f); - - if (l) { - if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s.fd, 0))) { - if (format == FORMAT_XML || format == FORMAT_HTML) { - xml_translate(&out, buf, params, format); - } else { - ast_str_append(&out, 0, "%s", buf); - } - munmap(buf, l); - } - } else if (format == FORMAT_XML || format == FORMAT_HTML) { - xml_translate(&out, "", params, format); - } - fclose(s.f); - s.f = NULL; - s.fd = -1; - } + process_output(&s, out, params, format); if (format == FORMAT_XML) { ast_str_append(&out, 0, "</ajax-response>\n"); diff --git a/main/netsock2.c b/main/netsock2.c index 25f15a2fc..d6561fba2 100644 --- a/main/netsock2.c +++ b/main/netsock2.c @@ -121,8 +121,10 @@ char *ast_sockaddr_stringify_fmt(const struct ast_sockaddr *sa, int format) int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags) { char *s = str; + char *orig_str = str;/* Original string in case the port presence is incorrect. */ + char *host_end = NULL;/* Delay terminating the host in case the port presence is incorrect. */ - ast_debug(5, "Splitting '%s' gives...\n", str); + ast_debug(5, "Splitting '%s' into...\n", str); *host = NULL; *port = NULL; if (*s == '[') { @@ -130,7 +132,8 @@ int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags) for (; *s && *s != ']'; ++s) { } if (*s == ']') { - *s++ = '\0'; + host_end = s; + ++s; } if (*s == ':') { *port = s + 1; @@ -148,11 +151,10 @@ int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags) } } if (*port) { - **port = '\0'; + host_end = *port; ++*port; } } - ast_debug(5, "...host '%s' and port '%s'.\n", *host, *port); switch (flags & PARSE_PORT_MASK) { case PARSE_PORT_IGNORE: @@ -160,18 +162,23 @@ int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags) break; case PARSE_PORT_REQUIRE: if (*port == NULL) { - ast_log(LOG_WARNING, "missing port\n"); + ast_log(LOG_WARNING, "Port missing in %s\n", orig_str); return 0; } break; case PARSE_PORT_FORBID: if (*port != NULL) { - ast_log(LOG_WARNING, "port disallowed\n"); + ast_log(LOG_WARNING, "Port disallowed in %s\n", orig_str); return 0; } break; } + /* Can terminate the host string now if needed. */ + if (host_end) { + *host_end = '\0'; + } + ast_debug(5, "...host '%s' and port '%s'.\n", *host, *port ? *port : ""); return 1; } diff --git a/main/rtp_engine.c b/main/rtp_engine.c index 3c5ef8b36..a1c460578 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -66,6 +66,8 @@ struct ast_rtp_instance { int timeout; /*! RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */ int holdtimeout; + /*! RTP keepalive interval */ + int keepalive; /*! DTMF mode in use */ enum ast_rtp_dtmf_mode dtmf_mode; /*! Glue currently in use */ @@ -1781,6 +1783,11 @@ void ast_rtp_instance_set_hold_timeout(struct ast_rtp_instance *instance, int ti instance->holdtimeout = timeout; } +void ast_rtp_instance_set_keepalive(struct ast_rtp_instance *instance, int interval) +{ + instance->keepalive = interval; +} + int ast_rtp_instance_get_timeout(struct ast_rtp_instance *instance) { return instance->timeout; @@ -1791,6 +1798,11 @@ int ast_rtp_instance_get_hold_timeout(struct ast_rtp_instance *instance) return instance->holdtimeout; } +int ast_rtp_instance_get_keepalive(struct ast_rtp_instance *instance) +{ + return instance->keepalive; +} + struct ast_rtp_engine *ast_rtp_instance_get_engine(struct ast_rtp_instance *instance) { return instance->engine; @@ -1850,6 +1862,15 @@ struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance) return instance->srtp; } +int ast_rtp_instance_sendcng(struct ast_rtp_instance *instance, int level) +{ + if (instance->engine->sendcng) { + return instance->engine->sendcng(instance, level); + } + + return -1; +} + static void set_next_mime_type(const struct ast_format *format, int rtp_code, char *type, char *subtype, unsigned int sample_rate) { int x = mime_types_len; diff --git a/res/res_agi.c b/res/res_agi.c index 53aa6804f..b57c498df 100644 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -297,7 +297,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </agi> <agi name="hangup" language="en_US"> <synopsis> - Hangup the current channel. + Hangup a channel. </synopsis> <syntax> <parameter name="channelname" /> diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c index caafb52df..bbc683a47 100644 --- a/res/res_config_pgsql.c +++ b/res/res_config_pgsql.c @@ -345,6 +345,7 @@ static struct tables *find_table(const char *database, const char *orig_tablenam if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) { ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n"); + PQclear(result); AST_LIST_UNLOCK(&psql_tables); return NULL; } @@ -363,6 +364,7 @@ static struct tables *find_table(const char *database, const char *orig_tablenam if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) { ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname); + PQclear(result); destroy_table(table); AST_LIST_UNLOCK(&psql_tables); return NULL; @@ -479,6 +481,7 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab ast_mutex_lock(&pgsql_lock); if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) { + PQclear(result); ast_mutex_unlock(&pgsql_lock); return NULL; } @@ -494,8 +497,8 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows); if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) { - ast_mutex_unlock(&pgsql_lock); PQclear(result); + ast_mutex_unlock(&pgsql_lock); return NULL; } for (i = 0; i < numFields; i++) @@ -523,8 +526,8 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database); } - ast_mutex_unlock(&pgsql_lock); PQclear(result); + ast_mutex_unlock(&pgsql_lock); return var; } @@ -614,8 +617,22 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) { ast_mutex_unlock(&pgsql_lock); - return NULL; - } + return NULL; + } else { + ExecStatusType result_status = PQresultStatus(result); + if (result_status != PGRES_COMMAND_OK + && result_status != PGRES_TUPLES_OK + && result_status != PGRES_NONFATAL_ERROR) { + ast_log(LOG_WARNING, + "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database); + ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql)); + ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n", + PQresultErrorMessage(result), PQresStatus(result_status)); + PQclear(result); + ast_mutex_unlock(&pgsql_lock); + return NULL; + } + } ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql)); @@ -628,8 +645,8 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows); if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) { - ast_mutex_unlock(&pgsql_lock); PQclear(result); + ast_mutex_unlock(&pgsql_lock); return NULL; } for (i = 0; i < numFields; i++) @@ -659,8 +676,8 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table); } - ast_mutex_unlock(&pgsql_lock); PQclear(result); + ast_mutex_unlock(&pgsql_lock); return cfg; } @@ -763,6 +780,20 @@ static int update_pgsql(const char *database, const char *tablename, const char if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) { ast_mutex_unlock(&pgsql_lock); return -1; + } else { + ExecStatusType result_status = PQresultStatus(result); + if (result_status != PGRES_COMMAND_OK + && result_status != PGRES_TUPLES_OK + && result_status != PGRES_NONFATAL_ERROR) { + ast_log(LOG_WARNING, + "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n"); + ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql)); + ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n", + PQresultErrorMessage(result), PQresStatus(result_status)); + PQclear(result); + ast_mutex_unlock(&pgsql_lock); + return -1; + } } numrows = atoi(PQcmdTuples(result)); @@ -950,6 +981,7 @@ static int store_pgsql(const char *database, const char *table, va_list ap) } insertid = PQoidValue(result); + PQclear(result); ast_mutex_unlock(&pgsql_lock); ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid); diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index dd71342d1..60f7edacf 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -272,6 +272,7 @@ static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_ins static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct ast_sockaddr *suggestion, const char *username); static void ast_rtp_stop(struct ast_rtp_instance *instance); static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc); +static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level); /* RTP Engine Declaration */ static struct ast_rtp_engine asterisk_rtp_engine = { @@ -297,6 +298,7 @@ static struct ast_rtp_engine asterisk_rtp_engine = { .stun_request = ast_rtp_stun_request, .stop = ast_rtp_stop, .qos = ast_rtp_qos_set, + .sendcng = ast_rtp_sendcng, }; static inline int rtp_debug_test_addr(struct ast_sockaddr *addr) @@ -2591,6 +2593,49 @@ static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, return ast_set_qos(rtp->s, tos, cos, desc); } +/*! \brief generate comfort noice (CNG) */ +static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level) +{ + unsigned int *rtpheader; + int hdrlen = 12; + int res; + int payload; + char data[256]; + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct ast_sockaddr remote_address = { {0,} }; + + ast_rtp_instance_get_remote_address(instance, &remote_address); + + if (ast_sockaddr_isnull(&remote_address)) { + return -1; + } + + payload = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 0, NULL, AST_RTP_CN); + + level = 127 - (level & 0x7f); + + rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); + + /* Get a pointer to the header */ + rtpheader = (unsigned int *)data; + rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno++)); + rtpheader[1] = htonl(rtp->lastts); + rtpheader[2] = htonl(rtp->ssrc); + data[12] = level; + + res = rtp_sendto(instance, (void *) rtpheader, hdrlen + 1, 0, &remote_address); + + if (res < 0) { + ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s: %s\n", ast_sockaddr_stringify(&remote_address), strerror(errno)); + } else if (rtp_debug_test_addr(&remote_address)) { + ast_verbose("Sent Comfort Noise RTP packet to %s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", + ast_sockaddr_stringify(&remote_address), + AST_RTP_CN, rtp->seqno, rtp->lastdigitts, res - hdrlen); + } + + return res; +} + static char *rtp_do_debug_ip(struct ast_cli_args *a) { char *arg = ast_strdupa(a->argv[4]); diff --git a/tests/test_db.c b/tests/test_db.c new file mode 100644 index 000000000..105a6ab1e --- /dev/null +++ b/tests/test_db.c @@ -0,0 +1,222 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2011, Digium, Inc. + * + * Terry Wilson <twilson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief AstDB Unit Tests + * + * \author Terry Wilson <twilson@digium.com> + * + */ + +/*** MODULEINFO + <depend>TEST_FRAMEWORK</depend> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "") + +#include "asterisk/test.h" +#include "asterisk/module.h" +#include "asterisk/astdb.h" +#include "asterisk/logger.h" + +enum { + FAMILY = 0, + KEY = 1, + VALUE = 2, +}; + +/* Longest value we can support is 256 for family/key/ so, with + * family = astdbtest and two slashes we are left with 244 bytes */ +static const char long_val[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + +AST_TEST_DEFINE(put_get_del) +{ + int res = AST_TEST_PASS; + const char *inputs[][3] = { + {"family", "key", "value"}, + {"astdbtest", "a", "b"}, + {"astdbtest", "a", "a"}, + {"astdbtest", "b", "a"}, + {"astdbtest", "b", "b"}, + {"astdbtest", "b", "!@#$%^&*()|+-<>?"}, + {"astdbtest", long_val, "b"}, + {"astdbtest", "b", long_val}, + {"astdbtest", "!@#$%^&*()|+-<>?", "b"}, + }; + size_t x; + char buf[sizeof(long_val)] = { 0, }; + + switch (cmd) { + case TEST_INIT: + info->name = "put_get_del"; + info->category = "/main/astdb/"; + info->summary = "ast_db_(put|get|del) unit test"; + info->description = + "Ensures that the ast_db put, get, and del functions work"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + for (x = 0; x < ARRAY_LEN(inputs); x++) { + if (ast_db_put(inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE])) { + ast_test_status_update(test, "Failed to put %s : %s : %s\n", inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE]); + res = AST_TEST_FAIL; + } + if (ast_db_get(inputs[x][FAMILY], inputs[x][KEY], buf, sizeof(buf))) { + ast_test_status_update(test, "Failed to get %s : %s : %s\n", inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE]); + res = AST_TEST_FAIL; + } else if (strcmp(buf, inputs[x][VALUE])) { + ast_test_status_update(test, "Failed to match key '%s/%s' value '%s' to '%s'\n", inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE], buf); + res = AST_TEST_FAIL; + } + if (ast_db_del(inputs[x][FAMILY], inputs[x][KEY])) { + ast_test_status_update(test, "Failed to del %s : %s\n", inputs[x][FAMILY], inputs[x][KEY]); + res = AST_TEST_FAIL; + } + } + + return res; +} + +AST_TEST_DEFINE(gettree_deltree) +{ + int res = AST_TEST_PASS; + const char *inputs[][3] = { +#define BASE "astdbtest" +#define SUB1 "one" +#define SUB2 "two" +#define FAM1 BASE "/" SUB1 +#define FAM2 BASE "/" SUB2 + {FAM1, "one", "blah"}, + {FAM1, "two", "bling"}, + {FAM1, "three", "blast"}, + {FAM2, "one", "blah"}, + {FAM2, "two", "bling"}, + {FAM2, "three", "blast"}, + }; + size_t x; + struct ast_db_entry *dbes, *cur; + int num_deleted; + + switch (cmd) { + case TEST_INIT: + info->name = "gettree_deltree"; + info->category = "/main/astdb/"; + info->summary = "ast_db_(gettree|deltree) unit test"; + info->description = + "Ensures that the ast_db gettree and deltree functions work"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + for (x = 0; x < ARRAY_LEN(inputs); x++) { + if (ast_db_put(inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE])) { + ast_test_status_update(test, "Failed to put %s : %s : %s\n", inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE]); + res = AST_TEST_FAIL; + } + } + + if (!(dbes = ast_db_gettree(BASE, NULL))) { + ast_test_status_update(test, "Failed to ast_db_gettree family %s\n", BASE); + res = AST_TEST_FAIL; + } + + for (cur = dbes, x = 0; cur; cur = cur->next, x++) { + int found = 0; + size_t z; + for (z = 0; z < ARRAY_LEN(inputs); z++) { + char buf[256]; + snprintf(buf, sizeof(buf), "/%s/%s", inputs[z][FAMILY], inputs[z][KEY]); + if (!strcmp(buf, cur->key) && !strcmp(inputs[z][VALUE], cur->data)) { + found = 1; + } + } + if (!found) { + ast_test_status_update(test, "inputs array has no entry for %s == %s\n", cur->key, cur->data); + res = AST_TEST_FAIL; + } + } + + if (x != ARRAY_LEN(inputs)) { + ast_test_status_update(test, "ast_db_gettree returned %zu entries when we expected %zu\n", x, ARRAY_LEN(inputs)); + res = AST_TEST_FAIL; + } + + ast_db_freetree(dbes); + + if (!(dbes = ast_db_gettree(BASE, SUB1))) { + ast_test_status_update(test, "Failed to ast_db_gettree for %s/%s\n", BASE, SUB1); + res = AST_TEST_FAIL; + } + + for (cur = dbes, x = 0; cur; cur = cur->next, x++) { + int found = 0; + size_t z; + for (z = 0; z < ARRAY_LEN(inputs); z++) { + char buf[256]; + snprintf(buf, sizeof(buf), "/%s/%s", inputs[z][FAMILY], inputs[z][KEY]); + if (!strcmp(buf, cur->key) && !strcmp(inputs[z][VALUE], cur->data)) { + found = 1; + } + } + if (!found) { + ast_test_status_update(test, "inputs array has no entry for %s == %s\n", cur->key, cur->data); + res = AST_TEST_FAIL; + } + } + + if (x != (ARRAY_LEN(inputs) / 2)) { + ast_test_status_update(test, "ast_db_gettree returned %zu entries when we expected %zu\n", x, ARRAY_LEN(inputs) / 2); + res = AST_TEST_FAIL; + } + + ast_db_freetree(dbes); + + if ((num_deleted = ast_db_deltree(BASE, SUB2)) != ARRAY_LEN(inputs) / 2) { + ast_test_status_update(test, "Failed to deltree %s/%s, expected %zu deletions and got %d\n", BASE, SUB2, ARRAY_LEN(inputs) / 2, num_deleted); + res = AST_TEST_FAIL; + } + + if ((num_deleted = ast_db_deltree(BASE, NULL)) != ARRAY_LEN(inputs) / 2) { + ast_test_status_update(test, "Failed to deltree %s, expected %zu deletions and got %d\n", BASE, ARRAY_LEN(inputs) / 2, num_deleted); + res = AST_TEST_FAIL; + } + + return res; +} + +static int unload_module(void) +{ + AST_TEST_UNREGISTER(put_get_del); + AST_TEST_UNREGISTER(gettree_deltree); + return 0; +} + +static int load_module(void) +{ + AST_TEST_REGISTER(put_get_del); + AST_TEST_REGISTER(gettree_deltree); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "AstDB test module"); diff --git a/tests/test_event.c b/tests/test_event.c index 5f0c1003c..e6f345133 100644 --- a/tests/test_event.c +++ b/tests/test_event.c @@ -312,7 +312,7 @@ AST_TEST_DEFINE(event_sub_test) .expected_count = 2, }, [TEST_SUBS_CUSTOM_RAW] = { - .expected_count = 1, + .expected_count = 2, }, [TEST_SUBS_CUSTOM_UINT] = { .expected_count = 1, @@ -344,28 +344,13 @@ AST_TEST_DEFINE(event_sub_test) break; } - /* - * Subscription TEST_SUBS_ALL_STR: - * - allocate normally - * - subscribe to ALL events with a DEVICE STR IE check - */ - ast_test_status_update(test, "Adding TEST_SUBS_ALL_STR subscription\n"); - test_subs[TEST_SUBS_ALL_STR].sub = ast_event_subscribe(AST_EVENT_ALL, event_sub_cb, - test_subs_class_type_str(TEST_SUBS_ALL_STR), &test_subs[TEST_SUBS_ALL_STR].data, - AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar", + ast_test_status_update(test, "Check that NO CUSTOM subscribers exist\n"); + sub_res = ast_event_check_subscriber(AST_EVENT_CUSTOM, AST_EVENT_IE_END); - if (!test_subs[TEST_SUBS_ALL_STR].sub) { - ast_test_status_update(test, "Failed to create TEST_SUBS_ALL_STR subscription\n"); - res = AST_TEST_FAIL; - goto return_cleanup; - } - - if (strcmp(ast_event_subscriber_get_description(test_subs[TEST_SUBS_ALL_STR].sub), - test_subs_class_type_str(TEST_SUBS_ALL_STR))) { - ast_test_status_update(test, - "Unexpected subscription description on TEST_SUBS_ALL_STR subscription\n"); + if (sub_res != AST_EVENT_SUB_NONE) { + ast_test_status_update(test, "CUSTOM subscriptions should not exist! (%d)\n", + sub_res); res = AST_TEST_FAIL; - goto return_cleanup; } /* @@ -392,6 +377,39 @@ AST_TEST_DEFINE(event_sub_test) goto return_cleanup; } + ast_test_status_update(test, "Check that a CUSTOM subscriber exists\n"); + sub_res = ast_event_check_subscriber(AST_EVENT_CUSTOM, + AST_EVENT_IE_END); + if (sub_res != AST_EVENT_SUB_EXISTS) { + ast_test_status_update(test, "A CUSTOM subscription should exist! (%d)\n", + sub_res); + res = AST_TEST_FAIL; + } + + /* + * Subscription TEST_SUBS_ALL_STR: + * - allocate normally + * - subscribe to ALL events with a DEVICE STR IE check + */ + ast_test_status_update(test, "Adding TEST_SUBS_ALL_STR subscription\n"); + test_subs[TEST_SUBS_ALL_STR].sub = ast_event_subscribe(AST_EVENT_ALL, event_sub_cb, + test_subs_class_type_str(TEST_SUBS_ALL_STR), &test_subs[TEST_SUBS_ALL_STR].data, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar", + AST_EVENT_IE_END); + if (!test_subs[TEST_SUBS_ALL_STR].sub) { + ast_test_status_update(test, "Failed to create TEST_SUBS_ALL_STR subscription\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + if (strcmp(ast_event_subscriber_get_description(test_subs[TEST_SUBS_ALL_STR].sub), + test_subs_class_type_str(TEST_SUBS_ALL_STR))) { + ast_test_status_update(test, + "Unexpected subscription description on TEST_SUBS_ALL_STR subscription\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + /* * Subscription TEST_SUBS_CUSTOM_RAW: * - allocate normally @@ -576,7 +594,7 @@ AST_TEST_DEFINE(event_sub_test) /* * Exercise the API call to check for existing subscriptions. */ - ast_test_status_update(test, "Checking for subscribers to events\n"); + ast_test_status_update(test, "Checking for subscribers to specific events\n"); /* Check STR matching. */ sub_res = ast_event_check_subscriber(AST_EVENT_CUSTOM, @@ -678,6 +696,34 @@ AST_TEST_DEFINE(event_sub_test) res = AST_TEST_FAIL; } + ast_test_status_update(test, "Special event posting test\n"); + + /* + * Event to check if event is even posted. + * + * Matching subscriptions: + * TEST_SUBS_CUSTOM_RAW + */ + event = ast_event_new(AST_EVENT_CUSTOM, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "Mula", + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_RAW, "FOO/bar", sizeof("FOO/bar"), + AST_EVENT_IE_END); + if (!event) { + ast_test_status_update(test, "Failed to create event\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + if (ast_event_queue(event)) { + ast_event_destroy(event); + event = NULL; + ast_test_status_update(test, "Failed to queue event\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + ast_test_status_update(test, "Sleeping a few seconds to allow event propagation...\n"); + sleep(3); + /* * Subscription TEST_SUBS_CUSTOM_ANY: * - allocate normally |