diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c index df72400809..08c0516161 100644 --- a/apps/app_confbridge.c +++ b/apps/app_confbridge.c @@ -848,6 +848,94 @@ static int handle_conf_user_leave(struct conference_bridge_user *cbu) return 0; } +void conf_moh_stop(struct conference_bridge_user *user) +{ + user->playing_moh = 0; + if (!user->suspended_moh) { + int in_bridge; + + /* + * Locking the ast_bridge here is the only way to hold off the + * call to ast_bridge_join() in confbridge_exec() from + * interfering with the bridge and MOH operations here. + */ + ast_bridge_lock(user->conference_bridge->bridge); + + /* + * Temporarily suspend the user from the bridge so we have + * control to stop MOH if needed. + */ + in_bridge = !ast_bridge_suspend(user->conference_bridge->bridge, user->chan); + ast_moh_stop(user->chan); + if (in_bridge) { + ast_bridge_unsuspend(user->conference_bridge->bridge, user->chan); + } + + ast_bridge_unlock(user->conference_bridge->bridge); + } +} + +void conf_moh_start(struct conference_bridge_user *user) +{ + user->playing_moh = 1; + if (!user->suspended_moh) { + int in_bridge; + + /* + * Locking the ast_bridge here is the only way to hold off the + * call to ast_bridge_join() in confbridge_exec() from + * interfering with the bridge and MOH operations here. + */ + ast_bridge_lock(user->conference_bridge->bridge); + + /* + * Temporarily suspend the user from the bridge so we have + * control to start MOH if needed. + */ + in_bridge = !ast_bridge_suspend(user->conference_bridge->bridge, user->chan); + ast_moh_start(user->chan, user->u_profile.moh_class, NULL); + if (in_bridge) { + ast_bridge_unsuspend(user->conference_bridge->bridge, user->chan); + } + + ast_bridge_unlock(user->conference_bridge->bridge); + } +} + +/*! + * \internal + * \brief Unsuspend MOH for the conference user. + * + * \param user Conference user to unsuspend MOH on. + * + * \return Nothing + */ +static void conf_moh_unsuspend(struct conference_bridge_user *user) +{ + ao2_lock(user->conference_bridge); + if (--user->suspended_moh == 0 && user->playing_moh) { + ast_moh_start(user->chan, user->u_profile.moh_class, NULL); + } + ao2_unlock(user->conference_bridge); +} + +/*! + * \internal + * \brief Suspend MOH for the conference user. + * + * \param user Conference user to suspend MOH on. + * + * \return Nothing + */ +static void conf_moh_suspend(struct conference_bridge_user *user) +{ + ao2_lock(user->conference_bridge); + if (user->suspended_moh++ == 0 && user->playing_moh) { + ast_moh_stop(user->chan); + } + ao2_unlock(user->conference_bridge); +} + int conf_handle_first_marked_common(struct conference_bridge_user *cbu) { if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET) && play_prompt_to_user(cbu, conf_get_sound(CONF_SOUND_PLACE_IN_CONF, cbu->b_profile.sounds))) { @@ -858,19 +946,12 @@ int conf_handle_first_marked_common(struct conference_bridge_user *cbu) int conf_handle_inactive_waitmarked(struct conference_bridge_user *cbu) { - /* Be sure we are muted so we can't talk to anybody else waiting */ - cbu->features.mute = 1; /* If we have not been quieted play back that they are waiting for the leader */ if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET) && play_prompt_to_user(cbu, conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, cbu->b_profile.sounds))) { /* user hungup while the sound was playing */ return -1; } - /* Start music on hold if needed */ - if (ast_test_flag(&cbu->u_profile, USER_OPT_MUSICONHOLD)) { - ast_moh_start(cbu->chan, cbu->u_profile.moh_class, NULL); - cbu->playing_moh = 1; - } return 0; } @@ -901,7 +982,7 @@ int conf_add_post_join_action(struct conference_bridge_user *cbu, int (*func)(st void conf_handle_first_join(struct conference_bridge *conference_bridge) { - ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name); + ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name); } void conf_handle_second_active(struct conference_bridge *conference_bridge) @@ -909,11 +990,8 @@ void conf_handle_second_active(struct conference_bridge *conference_bridge) /* If we are the second participant we may need to stop music on hold on the first */ struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->active_list); - /* 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); + if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD)) { + conf_moh_stop(first_participant); } if (!ast_test_flag(&first_participant->u_profile, USER_OPT_STARTMUTED)) { first_participant->features.mute = 0; @@ -1038,6 +1116,13 @@ static struct conference_bridge *join_conference_bridge(const char *name, struct ao2_lock(conference_bridge); + /* + * Suspend any MOH until the user actually joins the bridge of + * the conference. This way any pre-join file playback does not + * need to worry about MOH. + */ + conference_bridge_user->suspended_moh = 1; + if (handle_conf_user_join(conference_bridge_user)) { /* Invalid event, nothing was done, so we don't want to process a leave. */ ao2_unlock(conference_bridge); @@ -1455,21 +1540,18 @@ 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, ""); ast_autoservice_start(chan); play_sound_file(conference_bridge, join_sound); ast_autoservice_stop(chan); - if (conference_bridge_user.playing_moh) { - ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL); - } } /* See if we need to automatically set this user as a video source or not */ handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER)); + conf_moh_unsuspend(&conference_bridge_user); + /* Join our conference bridge for real */ send_join_event(conference_bridge_user.chan, conference_bridge->name); ast_bridge_join(conference_bridge->bridge, @@ -1810,25 +1892,14 @@ int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel, struct conf_menu_entry *menu_entry, struct conf_menu *menu) { - struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge; - /* See if music on hold is playing */ - ao2_lock(conference_bridge); - if (conference_bridge_user->playing_moh) { - /* MOH is going, let's stop it */ - ast_moh_stop(bridge_channel->chan); - } - ao2_unlock(conference_bridge); + conf_moh_suspend(conference_bridge_user); /* execute the list of actions associated with this menu entry */ - execute_menu_entry(conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu); + execute_menu_entry(conference_bridge_user->conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu); /* See if music on hold needs to be started back up again */ - ao2_lock(conference_bridge); - if (conference_bridge_user->playing_moh) { - ast_moh_start(bridge_channel->chan, conference_bridge_user->u_profile.moh_class, NULL); - } - ao2_unlock(conference_bridge); + conf_moh_unsuspend(conference_bridge_user); return 0; } @@ -2718,13 +2789,7 @@ void conf_mute_only_active(struct conference_bridge *conference_bridge) /* Turn on MOH/mute if the single participant is set up for it */ if (ast_test_flag(&only_participant->u_profile, USER_OPT_MUSICONHOLD)) { only_participant->features.mute = 1; - if (!only_participant->chan->bridge || !ast_bridge_suspend(conference_bridge->bridge, only_participant->chan)) { - ast_moh_start(only_participant->chan, only_participant->u_profile.moh_class, NULL); - only_participant->playing_moh = 1; - if (only_participant->chan->bridge) { - ast_bridge_unsuspend(conference_bridge->bridge, only_participant->chan); - } - } + conf_moh_start(only_participant); } } diff --git a/apps/app_meetme.c b/apps/app_meetme.c index bd63299324..28ddd6ea21 100644 --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -2650,7 +2650,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struc /* This device changed state now - if this is the first user */ if (conf->users == 1) - ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno); + ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno); ast_mutex_unlock(&conf->playlock); @@ -3967,7 +3967,7 @@ bailoutandtrynormal: /* Change any states */ if (!conf->users) { - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno); + ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno); } /* Return the number of seconds the user was in the conf */ @@ -5457,8 +5457,8 @@ static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk || trunk_ref == exclude) continue; trunk_ref->state = state; - ast_devstate_changed(sla_state_to_devstate(state), - "SLA:%s_%s", station->name, trunk->name); + ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE, + "SLA:%s_%s", station->name, trunk->name); break; } } @@ -5956,8 +5956,8 @@ static void sla_handle_hold_event(struct sla_event *event) { ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1); event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME; - ast_devstate_changed(AST_DEVICE_ONHOLD, "SLA:%s_%s", - event->station->name, event->trunk_ref->trunk->name); + ast_devstate_changed(AST_DEVICE_ONHOLD, AST_DEVSTATE_CACHABLE, "SLA:%s_%s", + event->station->name, event->trunk_ref->trunk->name); sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, INACTIVE_TRUNK_REFS, event->trunk_ref); @@ -6466,8 +6466,8 @@ static int sla_station_exec(struct ast_channel *chan, const char *data) sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL); else { trunk_ref->state = SLA_TRUNK_STATE_UP; - ast_devstate_changed(AST_DEVICE_INUSE, - "SLA:%s_%s", station->name, trunk_ref->trunk->name); + ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, + "SLA:%s_%s", station->name, trunk_ref->trunk->name); } } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) { struct sla_ringing_trunk *ringing_trunk; diff --git a/apps/app_queue.c b/apps/app_queue.c index 142c5281ba..0aac83dd48 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -3192,7 +3192,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies if (newstate != tmp->member->status) { ast_log(LOG_WARNING, "Found a channel matching iterface %s while status was %s changed to %s\n", tmp->member->interface, ast_devstate2str(tmp->member->status), ast_devstate2str(newstate)); - ast_devstate_changed_literal(newstate, tmp->member->interface); + ast_devstate_changed_literal(newstate, AST_DEVSTATE_CACHABLE, tmp->member->interface); } } if ((tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { @@ -3306,18 +3306,8 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies ast_channel_unlock(tmp->chan); ast_channel_unlock(qe->chan); - ao2_lock(tmp->member); - update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member)); - if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { - ast_verb(1, "Member %s is busy, cannot dial", tmp->member->interface); - res = -1; - } - else { - /* Place the call, but don't wait on the answer */ - res = ast_call(tmp->chan, location, 0); - } - ao2_unlock(tmp->member); - if (res) { + /* Place the call, but don't wait on the answer */ + if ((res = ast_call(tmp->chan, location, 0))) { /* Again, keep going even if there's an error */ ast_verb(3, "Couldn't call %s\n", tmp->interface); do_hang(tmp); diff --git a/apps/confbridge/conf_state.c b/apps/confbridge/conf_state.c index e3684058a4..ea5ab10f08 100644 --- a/apps/confbridge/conf_state.c +++ b/apps/confbridge/conf_state.c @@ -47,9 +47,28 @@ void conf_invalid_event_fn(struct conference_bridge_user *cbu) ast_log(LOG_ERROR, "Invalid event for confbridge user '%s'\n", cbu->u_profile.name); } +/*! + * \internal + * \brief Mute the user and play MOH if the user requires it. + * + * \param user Conference user to mute and optionally start MOH on. + * + * \return Nothing + */ +static void conf_mute_moh_inactive_waitmarked(struct conference_bridge_user *user) +{ + /* Be sure we are muted so we can't talk to anybody else waiting */ + user->features.mute = 1; + /* Start music on hold if needed */ + if (ast_test_flag(&user->u_profile, USER_OPT_MUSICONHOLD)) { + conf_moh_start(user); + } +} + void conf_default_join_waitmarked(struct conference_bridge_user *cbu) { conf_add_user_waiting(cbu->conference_bridge, cbu); + conf_mute_moh_inactive_waitmarked(cbu); conf_add_post_join_action(cbu, conf_handle_inactive_waitmarked); } diff --git a/apps/confbridge/conf_state_empty.c b/apps/confbridge/conf_state_empty.c index 22997ad2c6..afc736d4a2 100644 --- a/apps/confbridge/conf_state_empty.c +++ b/apps/confbridge/conf_state_empty.c @@ -81,6 +81,6 @@ static void join_marked(struct conference_bridge_user *cbu) static void transition_to_empty(struct conference_bridge_user *cbu) { /* Set device state to "not in use" */ - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", cbu->conference_bridge->name); + ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", cbu->conference_bridge->name); conf_ended(cbu->conference_bridge); } diff --git a/apps/confbridge/conf_state_multi_marked.c b/apps/confbridge/conf_state_multi_marked.c index 69850b18dc..2529266099 100644 --- a/apps/confbridge/conf_state_multi_marked.c +++ b/apps/confbridge/conf_state_multi_marked.c @@ -107,12 +107,8 @@ static void leave_marked(struct conference_bridge_user *cbu) cbu_iter->conference_bridge->waitingusers++; /* Handle muting/moh of cbu_iter if necessary */ if (ast_test_flag(&cbu_iter->u_profile, USER_OPT_MUSICONHOLD)) { - cbu_iter->features.mute = 1; - if (!ast_bridge_suspend(cbu_iter->conference_bridge->bridge, cbu_iter->chan)) { - ast_moh_start(cbu_iter->chan, cbu_iter->u_profile.moh_class, NULL); - cbu_iter->playing_moh = 1; - ast_bridge_unsuspend(cbu_iter->conference_bridge->bridge, cbu_iter->chan); - } + cbu_iter->features.mute = 1; + conf_moh_start(cbu_iter); } } } @@ -173,10 +169,8 @@ static void transition_to_marked(struct conference_bridge_user *cbu) cbu->conference_bridge->waitingusers--; AST_LIST_INSERT_TAIL(&cbu->conference_bridge->active_list, cbu_iter, list); cbu->conference_bridge->activeusers++; - if (cbu_iter->playing_moh && !ast_bridge_suspend(cbu->conference_bridge->bridge, cbu_iter->chan)) { - cbu_iter->playing_moh = 0; - ast_moh_stop(cbu_iter->chan); - ast_bridge_unsuspend(cbu->conference_bridge->bridge, cbu_iter->chan); + if (cbu_iter->playing_moh) { + conf_moh_stop(cbu_iter); } /* only unmute them if they are not supposed to start muted */ if (!ast_test_flag(&cbu_iter->u_profile, USER_OPT_STARTMUTED)) { diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h index ea544513d2..c52d3305f4 100644 --- a/apps/confbridge/include/confbridge.h +++ b/apps/confbridge/include/confbridge.h @@ -231,6 +231,7 @@ struct conference_bridge_user { struct ast_channel *chan; /*!< Asterisk channel participating */ struct ast_bridge_features features; /*!< Bridge features structure */ struct ast_bridge_tech_optimizations tech_args; /*!< Bridge technology optimizations for talk detection */ + unsigned int suspended_moh; /*!< Count of active suspended MOH actions. */ 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_HEAD_NOLOCK(, post_join_action) post_join_list; /*!< List of sounds to play after joining */; @@ -353,6 +354,24 @@ int play_sound_file(struct conference_bridge *conference_bridge, const char *fil */ void conf_ended(struct conference_bridge *conference_bridge); +/*! + * \brief Stop MOH for the conference user. + * + * \param user Conference user to stop MOH on. + * + * \return Nothing + */ +void conf_moh_stop(struct conference_bridge_user *user); + +/*! + * \brief Start MOH for the conference user. + * + * \param user Conference user to start MOH on. + * + * \return Nothing + */ +void conf_moh_start(struct conference_bridge_user *user); + /*! \brief Attempt to mute/play MOH to the only user in the conference if they require it * \param conference_bridge A conference bridge containing a single user */ diff --git a/channels/chan_agent.c b/channels/chan_agent.c index c3454ec2b0..b0f868be7e 100644 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -617,7 +617,7 @@ static struct ast_frame *agent_read(struct ast_channel *ast) if (p->chan) { p->chan->_bridge = NULL; p->chan = NULL; - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent); + ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent); p->acknowledged = 0; } } else { @@ -875,7 +875,7 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout) } else { /* Agent hung-up */ p->chan = NULL; - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent); + ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent); } if (!res) { @@ -995,7 +995,7 @@ static int agent_hangup(struct ast_channel *ast) if (!p->loginstart) { p->logincallerid[0] = '\0'; } else { - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent); + ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent); } if (p->abouttograb) { @@ -2143,7 +2143,7 @@ static int login_exec(struct ast_channel *chan, const char *data) } ast_mutex_unlock(&p->lock); AST_LIST_UNLOCK(&agents); - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent); + ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent); while (res >= 0) { ast_mutex_lock(&p->lock); if (p->deferlogoff && p->chan) { @@ -2164,7 +2164,7 @@ static int login_exec(struct ast_channel *chan, const char *data) if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) { ast_debug(1, "Wrapup time for %s expired!\n", p->agent); p->lastdisc = ast_tv(0, 0); - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent); + ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent); if (p->ackcall) { check_beep(p, 0); } else { @@ -2224,7 +2224,7 @@ static int login_exec(struct ast_channel *chan, const char *data) ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime); ast_verb(2, "Agent '%s' logged out\n", p->agent); /* If there is no owner, go ahead and kill it now */ - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent); + ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent); if (p->dead && !p->owner) { ast_mutex_destroy(&p->lock); ast_cond_destroy(&p->app_complete_cond); diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 726c1f9bd2..37e216a55d 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -3401,7 +3401,7 @@ static void dahdi_pri_update_span_devstate(struct sig_pri_span *pri) } if (pri->congestion_devstate != new_state) { pri->congestion_devstate = new_state; - ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/congestion", pri->span); + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_NOT_CACHABLE, "DAHDI/I%d/congestion", pri->span); } #if defined(THRESHOLD_DEVSTATE_PLACEHOLDER) /* Update the span threshold device state and report any change. */ @@ -3417,7 +3417,7 @@ static void dahdi_pri_update_span_devstate(struct sig_pri_span *pri) } if (pri->threshold_devstate != new_state) { pri->threshold_devstate = new_state; - ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/threshold", pri->span); + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_NOT_CACHABLE, "DAHDI/I%d/threshold", pri->span); } #endif /* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */ } @@ -9888,7 +9888,8 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb if (dashptr) { *dashptr = '\0'; } - ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, device_name); + tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE; + ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_NOT_CACHABLE, device_name); for (v = i->vars ; v ; v = v->next) pbx_builtin_setvar_helper(tmp, v->name, v->value); diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index bdbb685c91..0e84935d00 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -5825,7 +5825,7 @@ static int iax2_getpeertrunk(struct sockaddr_in sin) } /*! \brief Create new call, interface with the PBX core */ -static struct ast_channel *ast_iax2_new(int callno, int state, iax2_format capability, const char *linkedid) +static struct ast_channel *ast_iax2_new(int callno, int state, iax2_format capability, const char *linkedid, unsigned int cachable) { struct ast_channel *tmp; struct chan_iax2_pvt *i; @@ -5900,6 +5900,10 @@ static struct ast_channel *ast_iax2_new(int callno, int state, iax2_format capab i->owner = tmp; i->capability = capability; + if (!cachable) { + tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE; + } + /* Set inherited variables */ if (i->vars) { for (v = i->vars ; v ; v = v->next) @@ -8188,7 +8192,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies * /* if challenge has been sent, but no challenge response if given, reject. */ goto return_unref; } - ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */ + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */ /* either Authentication has taken place, or a REGAUTH must be sent before verifying registration */ res = 0; @@ -8742,7 +8746,7 @@ static void __expire_registry(const void *data) if (!ast_test_flag64(peer, IAX_TEMPONLY)) ast_db_del("IAX/Registry", peer->name); register_peer_exten(peer, 0); - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", peer->name); /* Activate notification */ + ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */ if (iax2_regfunk) iax2_regfunk(peer->name, 0); @@ -8797,7 +8801,7 @@ static void reg_source_db(struct iax2_peer *p) } } - ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */ + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */ p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p)); if (p->expire == -1) { @@ -8874,14 +8878,14 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Registered\r\nAddress: %s\r\nPost: %d\r\nPort: %d\r\n", p->name, ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), ntohs(sin->sin_port)); register_peer_exten(p, 1); - ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */ + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */ } else if (!ast_test_flag64(p, IAX_TEMPONLY)) { ast_verb(3, "Unregistered IAX2 '%s' (%s)\n", p->name, ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED"); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unregistered\r\n", p->name); register_peer_exten(p, 0); ast_db_del("IAX/Registry", p->name); - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", p->name); /* Activate notification */ + ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */ } /* Update the host */ /* Verify that the host is really there */ @@ -10397,7 +10401,8 @@ static int socket_process(struct iax2_thread *thread) (f.frametype == AST_FRAME_IAX)) { if (ast_test_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART)) { ast_clear_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART); - if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat, NULL)) { + if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat, NULL, + ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED))) { ast_variables_destroy(ies.vars); ast_mutex_unlock(&iaxsl[fr->callno]); return 1; @@ -11036,13 +11041,13 @@ static int socket_process(struct iax2_thread *thread) if (iaxs[fr->callno]->pingtime <= peer->maxms) { ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE! Time: %d\n", peer->name, iaxs[fr->callno]->pingtime); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Reachable\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime); - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "IAX2/%s", peer->name); /* Activate notification */ + ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */ } } else if ((peer->historicms > 0) && (peer->historicms <= peer->maxms)) { if (iaxs[fr->callno]->pingtime > peer->maxms) { ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED (%d ms)!\n", peer->name, iaxs[fr->callno]->pingtime); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Lagged\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime); - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", peer->name); /* Activate notification */ + ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */ } } peer->lastms = iaxs[fr->callno]->pingtime; @@ -11286,7 +11291,7 @@ static int socket_process(struct iax2_thread *thread) using_prefs); ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED); - if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, NULL))) + if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, NULL, 1))) iax2_destroy(fr->callno); else if (ies.vars) { struct ast_datastore *variablestore; @@ -11357,7 +11362,7 @@ immediatedial: iax2_getformatname_multiple(tmp, sizeof(tmp), iaxs[fr->callno]->peerformat)); ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED); send_command(iaxs[fr->callno], AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, 0, NULL, 0, -1); - if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat, NULL))) + if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat, NULL, 1))) iax2_destroy(fr->callno); else if (ies.vars) { struct ast_datastore *variablestore; @@ -12113,7 +12118,7 @@ static void __iax2_poke_noanswer(const void *data) if (peer->lastms > -1) { ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Time: %d\n", peer->name, peer->lastms); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, peer->lastms); - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", peer->name); /* Activate notification */ + ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */ } if ((callno = peer->callno) > 0) { ast_mutex_lock(&iaxsl[callno]); @@ -12279,7 +12284,7 @@ static struct ast_channel *iax2_request(const char *type, struct ast_format_cap if (cai.found) ast_string_field_set(iaxs[callno], host, pds.peer); - c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, requestor ? requestor->linkedid : NULL); + c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, requestor ? requestor->linkedid : NULL, cai.found); ast_mutex_unlock(&iaxsl[callno]); diff --git a/channels/chan_local.c b/channels/chan_local.c index 768fafc6a0..4e54ba6ca8 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -150,8 +150,6 @@ struct local_pvt { struct ast_jb_conf jb_conf; /*!< jitterbuffer configuration for this local channel */ struct ast_channel *owner; /*!< Master Channel - Bridging happens here */ struct ast_channel *chan; /*!< Outbound channel - PBX is run here */ - struct ast_module_user *u_owner;/*!< reference to keep the module loaded while in use */ - struct ast_module_user *u_chan; /*!< reference to keep the module loaded while in use */ }; #define LOCAL_ALREADY_MASQED (1 << 0) /*!< Already masqueraded */ @@ -577,6 +575,7 @@ static void check_bridge(struct ast_channel *ast, struct local_pvt *p) } if (p->chan->audiohooks) { struct ast_audiohook_list *audiohooks_swapper; + audiohooks_swapper = p->chan->audiohooks; p->chan->audiohooks = p->owner->audiohooks; p->owner->audiohooks = audiohooks_swapper; @@ -678,7 +677,7 @@ static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) ao2_lock(p); if ((p->owner != oldchan) && (p->chan != oldchan)) { - ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan); + ast_log(LOG_WARNING, "Old channel %p wasn't %p or %p\n", oldchan, p->owner, p->chan); ao2_unlock(p); return -1; } @@ -1046,16 +1045,15 @@ static int local_hangup(struct ast_channel *ast) if (isoutbound) { const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS"); - if ((status) && (p->owner)) { + + if (status && p->owner) { p->owner->hangupcause = p->chan->hangupcause; pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status); } ast_clear_flag(p, LOCAL_LAUNCHED_PBX); - ast_module_user_remove(p->u_chan); p->chan = NULL; } else { - ast_module_user_remove(p->u_owner); if (p->chan) { ast_queue_hangup(p->chan); } @@ -1066,6 +1064,7 @@ static int local_hangup(struct ast_channel *ast) if (!p->owner && !p->chan) { ao2_unlock(p); + /* Remove from list */ ao2_unlink(locals, p); ao2_ref(p, -1); @@ -1085,6 +1084,10 @@ local_hangup_cleanup: ao2_unlock(p); ao2_ref(p, -1); } + if (owner) { + ast_channel_unlock(owner); + owner = ast_channel_unref(owner); + } if (chan) { ast_channel_unlock(chan); if (hangup_chan) { @@ -1092,20 +1095,27 @@ local_hangup_cleanup: } chan = ast_channel_unref(chan); } - if (owner) { - ast_channel_unlock(owner); - owner = ast_channel_unref(owner); - } /* leave with the same stupid channel locked that came in */ ast_channel_lock(ast); return res; } -static void local_destroy(void *obj) +/*! + * \internal + * \brief struct local_pvt destructor. + * + * \param vdoomed Void local_pvt to destroy. + * + * \return Nothing + */ +static void local_pvt_destructor(void *vdoomed) { - struct local_pvt *pvt = obj; - pvt->reqcap = ast_format_cap_destroy(pvt->reqcap); + struct local_pvt *doomed = vdoomed; + + doomed->reqcap = ast_format_cap_destroy(doomed->reqcap); + + ast_module_unref(ast_module_info->self); } /*! \brief Create a call structure */ @@ -1114,7 +1124,7 @@ static struct local_pvt *local_alloc(const char *data, struct ast_format_cap *ca struct local_pvt *tmp = NULL; char *c = NULL, *opts = NULL; - if (!(tmp = ao2_alloc(sizeof(*tmp), local_destroy))) { + if (!(tmp = ao2_alloc(sizeof(*tmp), local_pvt_destructor))) { return NULL; } if (!(tmp->reqcap = ast_format_cap_dup(cap))) { @@ -1122,6 +1132,8 @@ static struct local_pvt *local_alloc(const char *data, struct ast_format_cap *ca return NULL; } + ast_module_ref(ast_module_info->self); + /* Initialize private structure information */ ast_copy_string(tmp->exten, data, sizeof(tmp->exten)); @@ -1221,10 +1233,11 @@ static struct ast_channel *local_new(struct local_pvt *p, int state, const char tmp->tech_pvt = p; tmp2->tech_pvt = p; + tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE; + tmp2->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE; + p->owner = tmp; p->chan = tmp2; - p->u_owner = ast_module_user_add(p->owner); - p->u_chan = ast_module_user_add(p->chan); ast_copy_string(tmp->context, p->context, sizeof(tmp->context)); ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context)); @@ -1254,9 +1267,7 @@ static struct ast_channel *local_request(const char *type, struct ast_format_cap } else if (ast_channel_cc_params_init(chan, requestor ? ast_channel_get_cc_config_params((struct ast_channel *)requestor) : NULL)) { ao2_unlink(locals, p); p->owner = ast_channel_release(p->owner); - ast_module_user_remove(p->u_owner); p->chan = ast_channel_release(p->chan); - ast_module_user_remove(p->u_chan); chan = NULL; } ao2_ref(p, -1); /* kill the ref from the alloc */ diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 9a101e349e..499d592ed9 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -2565,19 +2565,20 @@ static int sip_tls_read(struct sip_request *req, struct sip_request *reqcpy, str int authenticated, time_t start, struct sip_threadinfo *me) { int res, content_length, after_poll = 1, need_poll = 1; + size_t datalen = ast_str_strlen(req->data); char buf[1024] = ""; int timeout = -1; - - /* Read in headers one line at a time */ - while (ast_str_strlen(req->data) < 4 || strncmp(REQ_OFFSET_TO_STR(req, data->used - 4), "\r\n\r\n", 4)) { - if (!tcptls_session->client && !authenticated) { - if ((timeout = sip_check_authtimeout(start)) < 0) { - ast_debug(2, "SIP SSL server failed to determine authentication timeout\n"); + + /* Read in headers one line at a time */ + while (datalen < 4 || strncmp(REQ_OFFSET_TO_STR(req, data->used - 4), "\r\n\r\n", 4)) { + if (!tcptls_session->client && !authenticated) { + if ((timeout = sip_check_authtimeout(start)) < 0) { + ast_debug(2, "SIP TLS server failed to determine authentication timeout\n"); return -1; } if (timeout == 0) { - ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP"); + ast_debug(2, "SIP TLS server timed out\n"); return -1; } } else { @@ -2592,11 +2593,11 @@ static int sip_tls_read(struct sip_request *req, struct sip_request *reqcpy, str after_poll = 1; res = ast_wait_for_input(tcptls_session->fd, timeout); if (res < 0) { - ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res); + ast_debug(2, "SIP TLS server :: ast_wait_for_input returned %d\n", res); return -1; } else if (res == 0) { /* timeout */ - ast_debug(2, "SIP TCP server timed out\n"); + ast_debug(2, "SIP TLS server timed out\n"); return -1; } } @@ -2617,6 +2618,13 @@ static int sip_tls_read(struct sip_request *req, struct sip_request *reqcpy, str return -1; } ast_str_append(&req->data, 0, "%s", buf); + + datalen = ast_str_strlen(req->data); + if (datalen > SIP_MAX_PACKET_SIZE) { + ast_log(LOG_WARNING, "Rejecting TLS packet from '%s' because way too large: %zu\n", + ast_sockaddr_stringify(&tcptls_session->remote_address), datalen); + return -1; + } } copy_request(reqcpy, req); parse_request(reqcpy); @@ -2630,7 +2638,7 @@ static int sip_tls_read(struct sip_request *req, struct sip_request *reqcpy, str } if (timeout == 0) { - ast_debug(2, "SIP SSL server timed out\n"); + ast_debug(2, "SIP TLS server timed out\n"); return -1; } } else { @@ -2642,11 +2650,11 @@ static int sip_tls_read(struct sip_request *req, struct sip_request *reqcpy, str after_poll = 1; res = ast_wait_for_input(tcptls_session->fd, timeout); if (res < 0) { - ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res); + ast_debug(2, "SIP TLS server :: ast_wait_for_input returned %d\n", res); return -1; } else if (res == 0) { /* timeout */ - ast_debug(2, "SIP TCP server timed out\n"); + ast_debug(2, "SIP TLS server timed out\n"); return -1; } } @@ -2669,6 +2677,13 @@ static int sip_tls_read(struct sip_request *req, struct sip_request *reqcpy, str } content_length -= strlen(buf); ast_str_append(&req->data, 0, "%s", buf); + + datalen = ast_str_strlen(req->data); + if (datalen > SIP_MAX_PACKET_SIZE) { + ast_log(LOG_WARNING, "Rejecting TLS packet from '%s' because way too large: %zu\n", + ast_sockaddr_stringify(&tcptls_session->remote_address), datalen); + return -1; + } } } /*! \todo XXX If there's no Content-Length or if the content-length and what @@ -2842,6 +2857,8 @@ static int sip_tcp_read(struct sip_request *req, struct ast_tcptls_session_insta enum message_integrity message_integrity = MESSAGE_FRAGMENT; while (message_integrity == MESSAGE_FRAGMENT) { + size_t datalen; + if (ast_str_strlen(tcptls_session->overflow_buf) == 0) { char readbuf[4097]; int timeout; @@ -2881,6 +2898,13 @@ static int sip_tcp_read(struct sip_request *req, struct ast_tcptls_session_insta ast_str_append(&req->data, 0, "%s", ast_str_buffer(tcptls_session->overflow_buf)); ast_str_reset(tcptls_session->overflow_buf); } + + datalen = ast_str_strlen(req->data); + if (datalen > SIP_MAX_PACKET_SIZE) { + ast_log(LOG_WARNING, "Rejecting TCP packet from '%s' because way too large: %zu\n", + ast_sockaddr_stringify(&tcptls_session->remote_address), datalen); + return -1; + } message_integrity = check_message_integrity(&req->data, &tcptls_session->overflow_buf); } @@ -2952,7 +2976,7 @@ static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_s } me->threadid = pthread_self(); - ast_debug(2, "Starting thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP"); + ast_debug(2, "Starting thread for %s server\n", tcptls_session->ssl ? "TLS" : "TCP"); /* set up pollfd to watch for reads on both the socket and the alert_pipe */ fds[0].fd = tcptls_session->fd; @@ -2986,7 +3010,7 @@ static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_s } if (timeout == 0) { - ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP"); + ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "TLS": "TCP"); goto cleanup; } } else { @@ -2996,11 +3020,11 @@ static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_s if (ast_str_strlen(tcptls_session->overflow_buf) == 0) { res = ast_poll(fds, 2, timeout); /* polls for both socket and alert_pipe */ if (res < 0) { - ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res); + ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "TLS": "TCP", res); goto cleanup; } else if (res == 0) { /* timeout */ - ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP"); + ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "TLS": "TCP"); goto cleanup; } } @@ -3082,7 +3106,7 @@ static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_s } } - ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP"); + ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "TLS" : "TCP"); cleanup: if (tcptls_session && !tcptls_session->client && !authenticated) { @@ -6462,7 +6486,7 @@ static int update_call_counter(struct sip_pvt *fup, int event) } if (p) { - ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", p->name); + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", p->name); sip_unref_peer(p, "update_call_counter: sip_unref_peer from call counter"); } return 0; @@ -7714,6 +7738,9 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit ast_jb_configure(tmp, &global_jbconf); } + if (!i->relatedpeer) { + tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE; + } /* Set channel variables for this call from configuration */ for (v = i->chanvars ; v ; v = v->next) { char valuebuf[1024]; @@ -9850,6 +9877,15 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action type, value, (processed == TRUE)? "OK." : "UNSUPPORTED OR FAILED."); } + + /* Ensure crypto lines are provided where necessary */ + if (audio && secure_audio && !processed_crypto) { + ast_log(LOG_WARNING, "Rejecting secure audio stream without encryption details: %s\n", m); + return -1; + } else if (video && secure_video && !processed_crypto) { + ast_log(LOG_WARNING, "Rejecting secure video stream without encryption details: %s\n", m); + return -1; + } } /* Sanity checks */ @@ -13137,7 +13173,9 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, } /* Add Session-Timers related headers */ - if (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE) { + if (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE + || (st_get_mode(p, 0) == SESSION_TIMER_MODE_ACCEPT + && st_get_se(p, FALSE) != DEFAULT_MIN_SE)) { char i2astr[10]; if (!p->stimer->st_interval) { @@ -13145,9 +13183,11 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, } p->stimer->st_active = TRUE; - - snprintf(i2astr, sizeof(i2astr), "%d", p->stimer->st_interval); - add_header(&req, "Session-Expires", i2astr); + if (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE) { + snprintf(i2astr, sizeof(i2astr), "%d", p->stimer->st_interval); + add_header(&req, "Session-Expires", i2astr); + } + snprintf(i2astr, sizeof(i2astr), "%d", st_get_se(p, FALSE)); add_header(&req, "Min-SE", i2astr); } @@ -14708,7 +14748,7 @@ static int expire_register(const void *data) manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name); register_peer_exten(peer, FALSE); /* Remove regexten */ - ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name); /* Do we need to release this peer from memory? Only for realtime peers and autocreated peers @@ -15492,8 +15532,9 @@ static void sip_peer_hold(struct sip_pvt *p, int hold) ast_atomic_fetchadd_int(&p->relatedpeer->onHold, (hold ? +1 : -1)); /* Request device state update */ - ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", p->relatedpeer->name); - + ast_devstate_changed(AST_DEVICE_UNKNOWN, (p->owner->flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), + "SIP/%s", p->relatedpeer->name); + return; } @@ -15938,7 +15979,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock } else { update_peer_lastmsgssent(peer, -1, 0); } - ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name); } if (res < 0) { switch (res) { @@ -22149,7 +22190,7 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req ast_log(LOG_NOTICE, "Peer '%s' is now %s. (%dms / %dms)\n", peer->name, s, pingtime, peer->maxms); - ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name); if (sip_cfg.peer_rtupdate) { ast_update_realtime(ast_check_realtime("sipregs") ? "sipregs" : "sippeers", "name", peer->name, "lastms", str_lastms, SENTINEL); } @@ -27790,7 +27831,10 @@ static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp) ast_log(LOG_WARNING, "Parsing of Min-SE header failed %s\n", p_hdrval); return; } - p->stimer->st_interval = minse; + p->stimer->st_cached_min_se = minse; + if (p->stimer->st_interval < minse) { + p->stimer->st_interval = minse; + } transmit_invite(p, SIP_INVITE, 1, 2, NULL); } @@ -27902,7 +27946,7 @@ static int sip_poke_noanswer(const void *data) /* Don't send a devstate change if nothing changed. */ if (peer->lastms > -1) { peer->lastms = -1; - ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name); } /* Try again quickly */ @@ -29291,8 +29335,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str ast_log(LOG_WARNING, "Invalid session-minse '%s' at line %d of %s\n", v->value, v->lineno, config); peer->stimer.st_min_se = global_min_se; } - if (peer->stimer.st_min_se < 90) { - ast_log(LOG_WARNING, "session-minse '%s' at line %d of %s is not allowed to be < 90 secs\n", v->value, v->lineno, config); + if (peer->stimer.st_min_se < DEFAULT_MIN_SE) { + ast_log(LOG_WARNING, "session-minse '%s' at line %d of %s is not allowed to be < %d secs\n", v->value, v->lineno, config, DEFAULT_MIN_SE); peer->stimer.st_min_se = global_min_se; } } else if (!strcasecmp(v->name, "session-refresher")) { @@ -30300,8 +30344,8 @@ static int reload_config(enum channelreloadreason reason) ast_log(LOG_WARNING, "Invalid session-minse '%s' at line %d of %s\n", v->value, v->lineno, config); global_min_se = DEFAULT_MIN_SE; } - if (global_min_se < 90) { - ast_log(LOG_WARNING, "session-minse '%s' at line %d of %s is not allowed to be < 90 secs\n", v->value, v->lineno, config); + if (global_min_se < DEFAULT_MIN_SE) { + ast_log(LOG_WARNING, "session-minse '%s' at line %d of %s is not allowed to be < %d secs\n", v->value, v->lineno, config, DEFAULT_MIN_SE); global_min_se = DEFAULT_MIN_SE; } } else if (!strcasecmp(v->name, "session-refresher")) { diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index e3e3830e80..6605fb836b 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -2109,7 +2109,7 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s) AST_LIST_TRAVERSE(&l->sublines, subline, list) { ast_extension_state_add(subline->context, subline->exten, skinny_extensionstate_cb, subline->container); } - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s", l->name); + ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name); } --instance; } @@ -2147,7 +2147,7 @@ static int skinny_unregister(struct skinny_req *req, struct skinnysession *s) l->instance = 0; manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Unregistered\r\n", l->name, d->name); unregister_exten(l); - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Skinny/%s", l->name); + ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name); } } } @@ -5909,7 +5909,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession ast_verb(1, "RECEIVED UNKNOWN STIMULUS: %d(%d/%d)\n", event, instance, callreference); break; } - ast_devstate_changed(AST_DEVICE_UNKNOWN, "Skinny/%s", l->name); + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name); return 1; } @@ -5951,7 +5951,7 @@ static int handle_offhook_message(struct skinny_req *req, struct skinnysession * transmit_ringer_mode(d, SKINNY_RING_OFF); d->hookstate = SKINNY_OFFHOOK; - ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s", l->name); + ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name); if (sub && sub->substate == SUBSTATE_HOLD) { return 1; @@ -6018,7 +6018,7 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s return 0; } - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s", l->name); + ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name); dumpsub(sub, 0); @@ -6348,7 +6348,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse return 0; } - ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s", l->name); + ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name); switch(event) { case SOFTKEY_NONE: @@ -6509,8 +6509,8 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse return 0; } - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s", l->name); - + ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name); + if (sub) { dumpsub(sub, 1); } else { /* No sub, maybe an SLA call */ diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index b18435a36d..16fa099063 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -97,6 +97,7 @@ #define SIP_MAX_HEADERS 64 /*!< Max amount of SIP headers to read */ #define SIP_MAX_LINES 256 /*!< Max amount of lines in SIP attachment (like SDP) */ +#define SIP_MAX_PACKET_SIZE 20480 /*!< Max SIP packet size */ #define SIP_MIN_PACKET 4096 /*!< Initialize size of memory to allocate for packets */ #define MAX_HISTORY_ENTRIES 50 /*!< Max entires in the history list for a sip_pvt */ diff --git a/funcs/func_devstate.c b/funcs/func_devstate.c index e1f34387a1..6472610c8a 100644 --- a/funcs/func_devstate.c +++ b/funcs/func_devstate.c @@ -132,7 +132,7 @@ static int devstate_write(struct ast_channel *chan, const char *cmd, char *data, ast_db_put(astdb_family, data, value); - ast_devstate_changed(state_val, "Custom:%s", data); + ast_devstate_changed(state_val, AST_DEVSTATE_CACHABLE, "Custom:%s", data); return 0; } @@ -295,7 +295,7 @@ static char *handle_cli_devstate_change(struct ast_cli_entry *e, int cmd, struct ast_db_put(astdb_family, dev, state); - ast_devstate_changed(state_val, "Custom:%s", dev); + ast_devstate_changed(state_val, AST_DEVSTATE_CACHABLE, "Custom:%s", dev); return CLI_SUCCESS; } @@ -341,7 +341,7 @@ static int load_module(void) if (dev_name <= (const char *) 1) continue; ast_devstate_changed(ast_devstate_val(db_entry->data), - "Custom:%s\n", dev_name); + AST_DEVSTATE_CACHABLE, "Custom:%s\n", dev_name); } ast_db_freetree(db_tree); db_tree = NULL; diff --git a/include/asterisk/bridging.h b/include/asterisk/bridging.h index 3c117b7cd2..4a11853fca 100644 --- a/include/asterisk/bridging.h +++ b/include/asterisk/bridging.h @@ -267,6 +267,32 @@ struct ast_bridge { */ struct ast_bridge *ast_bridge_new(uint32_t capabilities, int flags); +/*! + * \brief Lock the bridge. + * + * \param bridge Bridge to lock + * + * \return Nothing + */ +#define ast_bridge_lock(bridge) _ast_bridge_lock(bridge, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge) +static inline void _ast_bridge_lock(struct ast_bridge *bridge, const char *file, const char *function, int line, const char *var) +{ + __ao2_lock(bridge, file, function, line, var); +} + +/*! + * \brief Unlock the bridge. + * + * \param bridge Bridge to unlock + * + * \return Nothing + */ +#define ast_bridge_unlock(bridge) _ast_bridge_unlock(bridge, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge) +static inline void _ast_bridge_unlock(struct ast_bridge *bridge, const char *file, const char *function, int line, const char *var) +{ + __ao2_unlock(bridge, file, function, line, var); +} + /*! \brief See if it is possible to create a bridge * * \param capabilities The capabilities that the bridge will use diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 2820fc09b6..4ea50e7958 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -942,6 +942,12 @@ enum { * some non-traditional dialplans (like AGI) to continue to function. */ AST_FLAG_DISABLE_WORKAROUNDS = (1 << 20), + /*! Disable device state event caching. This allows allows channel + * drivers to selectively prevent device state events from being cached + * by certain channels such as anonymous calls which have no persistent + * represenatation that can be tracked. + */ + AST_FLAG_DISABLE_DEVSTATE_CACHE = (1 << 21), }; /*! \brief ast_bridge_config flags */ diff --git a/include/asterisk/devicestate.h b/include/asterisk/devicestate.h index 66ca2bd1a3..86740bc2fd 100644 --- a/include/asterisk/devicestate.h +++ b/include/asterisk/devicestate.h @@ -61,6 +61,14 @@ enum ast_device_state { AST_DEVICE_TOTAL, /*/ Total num of device states, used for testing */ }; +/*! \brief Device State Cachability + * \note This is used to define the cachability of a device state when set. + */ +enum ast_devstate_cache { + AST_DEVSTATE_NOT_CACHABLE, /*!< This device state is not cachable */ + AST_DEVSTATE_CACHABLE, /*!< This device state is cachable */ +}; + /*! \brief Devicestate provider call back */ typedef enum ast_device_state (*ast_devstate_prov_cb_type)(const char *data); @@ -129,6 +137,7 @@ enum ast_device_state ast_device_state(const char *device); * \brief Tells Asterisk the State for Device is changed * * \param state the new state of the device + * \param cachable whether this device state is cachable * \param fmt device name like a dial string with format parameters * * The new state of the device will be sent off to any subscribers @@ -138,13 +147,14 @@ enum ast_device_state ast_device_state(const char *device); * \retval 0 on success * \retval -1 on failure */ -int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); +int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt, ...) + __attribute__((format(printf, 3, 4))); /*! * \brief Tells Asterisk the State for Device is changed * * \param state the new state of the device + * \param cachable whether this device state is cachable * \param device device name like a dial string with format parameters * * The new state of the device will be sent off to any subscribers @@ -154,7 +164,7 @@ int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...) * \retval 0 on success * \retval -1 on failure */ -int ast_devstate_changed_literal(enum ast_device_state state, const char *device); +int ast_devstate_changed_literal(enum ast_device_state state, enum ast_devstate_cache cachable, const char *device); /*! * \brief Tells Asterisk the State for Device is changed. diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h index 88b41699b2..fc84173c74 100644 --- a/include/asterisk/event_defs.h +++ b/include/asterisk/event_defs.h @@ -294,8 +294,14 @@ enum ast_event_ie_type { AST_EVENT_IE_PRESENCE_SUBTYPE = 0x003b, AST_EVENT_IE_PRESENCE_MESSAGE = 0x003c, + /*! + * \brief Event non-cachability flag + * Used by: All events + * Payload type: UINT + */ + AST_EVENT_IE_CACHABLE = 0x0039, /*! \brief Must be the last IE value +1 */ - AST_EVENT_IE_TOTAL = 0x0039, + AST_EVENT_IE_TOTAL = 0x003a, }; /*! diff --git a/main/aoc.c b/main/aoc.c index 817bb9225f..dcc00725b4 100644 --- a/main/aoc.c +++ b/main/aoc.c @@ -1605,7 +1605,12 @@ static struct ast_cli_entry aoc_cli[] = { AST_CLI_DEFINE(aoc_cli_debug_enable, "enable cli debugging of AOC messages"), }; +static void aoc_shutdown(void) +{ + ast_cli_unregister_multiple(aoc_cli, ARRAY_LEN(aoc_cli)); +} int ast_aoc_cli_init(void) { + ast_register_atexit(aoc_shutdown); return ast_cli_register_multiple(aoc_cli, ARRAY_LEN(aoc_cli)); } diff --git a/main/ccss.c b/main/ccss.c index b7e8fcc1fa..c757c1f826 100644 --- a/main/ccss.c +++ b/main/ccss.c @@ -637,7 +637,7 @@ static void ccss_notify_device_state_change(const char *device, enum cc_state st "Notification of CCSS state change to '%s', device state '%s' for device '%s'\n", cc_state_to_string(state), ast_devstate2str(devstate), device); - ast_devstate_changed(devstate, "ccss:%s", device); + ast_devstate_changed(devstate, AST_DEVSTATE_CACHABLE, "ccss:%s", device); } #define CC_OFFER_TIMER_DEFAULT 20 /* Seconds */ diff --git a/main/cel.c b/main/cel.c index 117c5de4a3..02b37dc11d 100644 --- a/main/cel.c +++ b/main/cel.c @@ -715,6 +715,7 @@ static void ast_cel_engine_term(void) ao2_ref(linkedids, -1); linkedids = NULL; } + ast_cli_unregister(&cli_status); } int ast_cel_engine_init(void) diff --git a/main/channel.c b/main/channel.c index 8f71b05ed7..009931c0e3 100644 --- a/main/channel.c +++ b/main/channel.c @@ -72,6 +72,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/global_datastores.h" #include "asterisk/data.h" #include "asterisk/features.h" +#include "asterisk/test.h" #ifdef HAVE_EPOLL #include @@ -296,6 +297,7 @@ static void channel_data_add_flags(struct ast_data *tree, ast_data_add_bool(tree, "BRIDGE_HANGUP_RUN", ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_RUN)); ast_data_add_bool(tree, "BRIDGE_HANGUP_DONT", ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT)); ast_data_add_bool(tree, "DISABLE_WORKAROUNDS", ast_test_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS)); + ast_data_add_bool(tree, "DISABLE_DEVSTATE_CACHE", ast_test_flag(chan, AST_FLAG_DISABLE_DEVSTATE_CACHE)); } #if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED) @@ -2542,7 +2544,7 @@ static void ast_channel_destructor(void *obj) * instance is dead, we don't know the state of all other possible * instances. */ - ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, device_name); + ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, (chan->flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), device_name); } chan->nativeformats = ast_format_cap_destroy(chan->nativeformats); @@ -3263,6 +3265,7 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, now = ast_tvnow(); diff = ast_tvsub(c[x]->whentohangup, now); if (diff.tv_sec < 0 || ast_tvzero(diff)) { + ast_test_suite_event_notify("HANGUP_TIME", "Channel: %s", c[x]->name); /* Should already be hungup */ c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT; ast_channel_unlock(c[x]); @@ -3331,6 +3334,7 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, now = ast_tvnow(); for (x = 0; x < n; x++) { if (!ast_tvzero(c[x]->whentohangup) && ast_tvcmp(c[x]->whentohangup, now) <= 0) { + ast_test_suite_event_notify("HANGUP_TIME", "Channel: %s", c[x]->name); c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT; if (winner == NULL) winner = c[x]; @@ -7271,7 +7275,7 @@ int ast_setstate(struct ast_channel *chan, enum ast_channel_state state) /* We have to pass AST_DEVICE_UNKNOWN here because it is entirely possible that the channel driver * for this channel is using the callback method for device state. If we pass in an actual state here * we override what they are saying the state is and things go amuck. */ - ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, name); + ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, (chan->flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), name); /* setstate used to conditionally report Newchannel; this is no more */ ast_manager_event(chan, EVENT_FLAG_CALL, "Newstate", @@ -7767,6 +7771,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha bridge_playfile(c1, c0, config->end_sound, 0); *fo = NULL; res = 0; + ast_test_suite_event_notify("BRIDGE_TIMELIMIT", "Channel1: %s\r\nChannel2: %s", c0->name, c1->name); break; } @@ -8331,6 +8336,7 @@ static const struct ast_data_entry channel_providers[] = { static void channels_shutdown(void) { ast_data_unregister(NULL); + ast_cli_unregister_multiple(cli_channel, ARRAY_LEN(cli_channel)); if (channels) { ao2_ref(channels, -1); channels = NULL; diff --git a/main/data.c b/main/data.c index 9205d8a929..3d77b573c0 100644 --- a/main/data.c +++ b/main/data.c @@ -3318,7 +3318,9 @@ AST_TEST_DEFINE(test_data_get) static void data_shutdown(void) { ast_manager_unregister("DataGet"); + ast_cli_unregister_multiple(cli_data, ARRAY_LEN(cli_data)); ao2_t_ref(root_data.container, -1, "Unref root_data.container in data_shutdown"); + root_data.container = NULL; ast_rwlock_destroy(&root_data.lock); } diff --git a/main/devicestate.c b/main/devicestate.c index 84eedc81c5..aabf806e74 100644 --- a/main/devicestate.c +++ b/main/devicestate.c @@ -174,6 +174,7 @@ static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov); struct state_change { AST_LIST_ENTRY(state_change) list; + enum ast_devstate_cache cachable; char device[1]; }; @@ -191,6 +192,7 @@ struct devstate_change { AST_LIST_ENTRY(devstate_change) entry; uint32_t state; struct ast_eid eid; + enum ast_devstate_cache cachable; char device[1]; }; @@ -424,7 +426,7 @@ static int getproviderstate(const char *provider, const char *address) return res; } -static void devstate_event(const char *device, enum ast_device_state state) +static void devstate_event(const char *device, enum ast_device_state state, int cachable) { struct ast_event *event; enum ast_event_type event_type; @@ -440,18 +442,23 @@ static void devstate_event(const char *device, enum ast_device_state state) ast_debug(3, "device '%s' state '%d'\n", device, state); if (!(event = ast_event_new(event_type, - AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device, - AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state, - AST_EVENT_IE_END))) { + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device, + AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state, + AST_EVENT_IE_CACHABLE, AST_EVENT_IE_PLTYPE_UINT, cachable, + AST_EVENT_IE_END))) { return; } - ast_event_queue_and_cache(event); + if (cachable) { + ast_event_queue_and_cache(event); + } else { + ast_event_queue(event); + } } /*! Called by the state change thread to find out what the state is, and then * to queue up the state change event */ -static void do_state_change(const char *device) +static void do_state_change(const char *device, int cachable) { enum ast_device_state state; @@ -459,10 +466,10 @@ static void do_state_change(const char *device) ast_debug(3, "Changing state for %s - state %d (%s)\n", device, state, ast_devstate2str(state)); - devstate_event(device, state); + devstate_event(device, state, cachable); } -int ast_devstate_changed_literal(enum ast_device_state state, const char *device) +int ast_devstate_changed_literal(enum ast_device_state state, enum ast_devstate_cache cachable, const char *device) { struct state_change *change; @@ -483,14 +490,15 @@ int ast_devstate_changed_literal(enum ast_device_state state, const char *device */ if (state != AST_DEVICE_UNKNOWN) { - devstate_event(device, state); + devstate_event(device, state, cachable); } else if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) { /* we could not allocate a change struct, or */ /* there is no background thread, so process the change now */ - do_state_change(device); + do_state_change(device, cachable); } else { /* queue the change */ strcpy(change->device, device); + change->cachable = cachable; AST_LIST_LOCK(&state_changes); AST_LIST_INSERT_TAIL(&state_changes, change, list); ast_cond_signal(&change_pending); @@ -502,10 +510,10 @@ int ast_devstate_changed_literal(enum ast_device_state state, const char *device int ast_device_state_changed_literal(const char *dev) { - return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, dev); + return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, dev); } -int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...) +int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt, ...) { char buf[AST_MAX_EXTENSION]; va_list ap; @@ -514,7 +522,7 @@ int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - return ast_devstate_changed_literal(state, buf); + return ast_devstate_changed_literal(state, cachable, buf); } int ast_device_state_changed(const char *fmt, ...) @@ -526,7 +534,7 @@ int ast_device_state_changed(const char *fmt, ...) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf); + return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, buf); } /*! \brief Go through the dev state change queue and update changes in the dev state thread */ @@ -546,7 +554,7 @@ static void *do_devstate_changes(void *data) /* Process each state change */ while ((current = next)) { next = AST_LIST_NEXT(current, list); - do_state_change(current->device); + do_state_change(current->device, current->cachable); ast_free(current); } } @@ -590,7 +598,7 @@ static void devstate_cache_cb(const struct ast_event *event, void *data) collection->num_states++; } -static void process_collection(const char *device, struct change_collection *collection) +static void process_collection(const char *device, enum ast_devstate_cache cachable, struct change_collection *collection) { int i; struct ast_devstate_aggregate agg; @@ -641,7 +649,11 @@ static void process_collection(const char *device, struct change_collection *col return; } - ast_event_queue_and_cache(event); + if (cachable) { + ast_event_queue_and_cache(event); + } else { + ast_event_queue(event); + } } static void handle_devstate_change(struct devstate_change *sc) @@ -667,7 +679,7 @@ static void handle_devstate_change(struct devstate_change *sc) /* Populate the collection of device states from the cache */ ast_event_dump_cache(tmp_sub); - process_collection(sc->device, &collection); + process_collection(sc->device, sc->cachable, &collection); ast_event_sub_destroy(tmp_sub); } @@ -696,10 +708,12 @@ static void devstate_change_collector_cb(const struct ast_event *event, void *da const char *device; const struct ast_eid *eid; uint32_t state; + enum ast_devstate_cache cachable = AST_DEVSTATE_CACHABLE; device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE); eid = ast_event_get_ie_raw(event, AST_EVENT_IE_EID); state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE); + cachable = ast_event_get_ie_uint(event, AST_EVENT_IE_CACHABLE); if (ast_strlen_zero(device) || !eid) { ast_log(LOG_ERROR, "Invalid device state change event received\n"); @@ -712,6 +726,7 @@ static void devstate_change_collector_cb(const struct ast_event *event, void *da strcpy(sc->device, device); sc->eid = *eid; sc->state = state; + sc->cachable = cachable; ast_mutex_lock(&devstate_collector.lock); AST_LIST_INSERT_TAIL(&devstate_collector.devstate_change_q, sc, entry); diff --git a/main/event.c b/main/event.c index 60535bbae6..7f80141a4a 100644 --- a/main/event.c +++ b/main/event.c @@ -278,7 +278,7 @@ static const struct ie_map { [AST_EVENT_IE_RECEIVED_HASH] = { AST_EVENT_IE_PLTYPE_STR, "ReceivedHash" }, [AST_EVENT_IE_USING_PASSWORD] = { AST_EVENT_IE_PLTYPE_UINT, "UsingPassword" }, [AST_EVENT_IE_ATTEMPTED_TRANSPORT] = { AST_EVENT_IE_PLTYPE_STR, "AttemptedTransport" }, - + [AST_EVENT_IE_CACHABLE] = { AST_EVENT_IE_PLTYPE_UINT, "Cachable" }, }; const char *ast_event_get_type_name(const struct ast_event *event) diff --git a/main/features.c b/main/features.c index 5520135de3..b6ddab4fae 100644 --- a/main/features.c +++ b/main/features.c @@ -1106,7 +1106,7 @@ static void notify_metermaids(const char *exten, char *context, enum ast_device_ ast_debug(4, "Notification of state change to metermaids %s@%s\n to state '%s'", exten, context, ast_devstate2str(state)); - ast_devstate_changed(state, "park:%s@%s", exten, context); + ast_devstate_changed(state, AST_DEVSTATE_CACHABLE, "park:%s@%s", exten, context); } /*! \brief metermaids callback from devicestate.c */ diff --git a/main/file.c b/main/file.c index 31f9239b34..f3fab29f0b 100644 --- a/main/file.c +++ b/main/file.c @@ -1584,8 +1584,14 @@ static struct ast_cli_entry cli_file[] = { AST_CLI_DEFINE(handle_cli_core_show_file_formats, "Displays file formats") }; +static void file_shutdown(void) +{ + ast_cli_unregister_multiple(cli_file, ARRAY_LEN(cli_file)); +} + int ast_file_init(void) { ast_cli_register_multiple(cli_file, ARRAY_LEN(cli_file)); + ast_register_atexit(file_shutdown); return 0; } diff --git a/main/http.c b/main/http.c index 55ade21d79..6f6bde80e4 100644 --- a/main/http.c +++ b/main/http.c @@ -603,6 +603,7 @@ struct ast_variable *ast_http_get_post_vars( int content_length = 0; struct ast_variable *v, *post_vars=NULL, *prev = NULL; char *buf, *var, *val; + int res; for (v = headers; v; v = v->next) { if (!strcasecmp(v->name, "Content-Type")) { @@ -615,20 +616,28 @@ struct ast_variable *ast_http_get_post_vars( for (v = headers; v; v = v->next) { if (!strcasecmp(v->name, "Content-Length")) { - content_length = atoi(v->value) + 1; + content_length = atoi(v->value); break; } } - if (!content_length) { + if (content_length <= 0) { return NULL; } - buf = ast_alloca(content_length); - if (!fgets(buf, content_length, ser->f)) { + buf = ast_malloc(content_length + 1); + if (!buf) { return NULL; } + res = fread(buf, 1, content_length, ser->f); + if (res < content_length) { + /* Error, distinguishable by ferror() or feof(), but neither + * is good. */ + goto done; + } + buf[content_length] = '\0'; + while ((val = strsep(&buf, "&"))) { var = strsep(&val, "="); if (val) { @@ -646,6 +655,9 @@ struct ast_variable *ast_http_get_post_vars( prev = v; } } + +done: + ast_free(buf); return post_vars; } @@ -1195,11 +1207,17 @@ static struct ast_cli_entry cli_http[] = { AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"), }; +static void http_shutdown(void) +{ + ast_cli_unregister_multiple(cli_http, ARRAY_LEN(cli_http)); +} + int ast_http_init(void) { ast_http_uri_link(&statusuri); ast_http_uri_link(&staticuri); ast_cli_register_multiple(cli_http, ARRAY_LEN(cli_http)); + ast_register_atexit(http_shutdown); return __ast_http_load(0); } diff --git a/main/image.c b/main/image.c index e9b4758d9c..24316e21f8 100644 --- a/main/image.c +++ b/main/image.c @@ -206,8 +206,14 @@ static struct ast_cli_entry cli_image[] = { AST_CLI_DEFINE(handle_core_show_image_formats, "Displays image formats") }; +static void image_shutdown(void) +{ + ast_cli_unregister_multiple(cli_image, ARRAY_LEN(cli_image)); +} + int ast_image_init(void) { ast_cli_register_multiple(cli_image, ARRAY_LEN(cli_image)); + ast_register_atexit(image_shutdown); return 0; } diff --git a/main/loader.c b/main/loader.c index 9cbfaf7242..f4b68c2ef9 100644 --- a/main/loader.c +++ b/main/loader.c @@ -233,9 +233,18 @@ void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u) if (!u) { return; } + AST_LIST_LOCK(&mod->users); - AST_LIST_REMOVE(&mod->users, u, entry); + u = AST_LIST_REMOVE(&mod->users, u, entry); AST_LIST_UNLOCK(&mod->users); + if (!u) { + /* + * Was not in the list. Either a bad pointer or + * __ast_module_user_hangup_all() has been called. + */ + return; + } + ast_atomic_fetchadd_int(&mod->usecount, -1); ast_free(u); @@ -554,15 +563,26 @@ int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode f } if (!error) { + /* Request any channels attached to the module to hangup. */ __ast_module_user_hangup_all(mod); - res = mod->info->unload(); + res = mod->info->unload(); if (res) { ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name); - if (force <= AST_FORCE_FIRM) + if (force <= AST_FORCE_FIRM) { error = 1; - else + } else { ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n"); + } + } + + if (!error) { + /* + * Request hangup on any channels that managed to get attached + * while we called the module unload function. + */ + __ast_module_user_hangup_all(mod); + sched_yield(); } } diff --git a/main/stun.c b/main/stun.c index ca3183839a..7203f270c6 100644 --- a/main/stun.c +++ b/main/stun.c @@ -499,8 +499,14 @@ static struct ast_cli_entry cli_stun[] = { AST_CLI_DEFINE(handle_cli_stun_set_debug, "Enable/Disable STUN debugging"), }; +static void stun_shutdown(void) +{ + ast_cli_unregister_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry)); +} + /*! \brief Initialize the STUN system in Asterisk */ void ast_stun_init(void) { ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry)); + ast_register_atexit(stun_shutdown); } diff --git a/main/taskprocessor.c b/main/taskprocessor.c index a5e106b280..c637ba24fd 100644 --- a/main/taskprocessor.c +++ b/main/taskprocessor.c @@ -122,6 +122,17 @@ static struct ast_cli_entry taskprocessor_clis[] = { AST_CLI_DEFINE(cli_tps_report, "List instantiated task processors and statistics"), }; +/*! + * \internal + * \brief Clean up resources on Asterisk shutdown + */ +static void tps_shutdown(void) +{ + ast_cli_unregister_multiple(taskprocessor_clis, ARRAY_LEN(taskprocessor_clis)); + ao2_t_ref(tps_singletons, -1, "Unref tps_singletons in shutdown"); + tps_singletons = NULL; +} + /* initialize the taskprocessor container and register CLI operations */ int ast_tps_init(void) { @@ -133,6 +144,9 @@ int ast_tps_init(void) ast_cond_init(&cli_ping_cond, NULL); ast_cli_register_multiple(taskprocessor_clis, ARRAY_LEN(taskprocessor_clis)); + + ast_register_atexit(tps_shutdown); + return 0; } diff --git a/main/timing.c b/main/timing.c index 05a52214c1..e5bb7d6728 100644 --- a/main/timing.c +++ b/main/timing.c @@ -299,11 +299,21 @@ static struct ast_cli_entry cli_timing[] = { AST_CLI_DEFINE(timing_test, "Run a timing test"), }; +static void timing_shutdown(void) +{ + ast_cli_unregister_multiple(cli_timing, ARRAY_LEN(cli_timing)); + + ast_heap_destroy(timing_interfaces); + timing_interfaces = NULL; +} + int ast_timing_init(void) { if (!(timing_interfaces = ast_heap_create(2, timing_holder_cmp, 0))) { return -1; } + ast_register_atexit(timing_shutdown); + return ast_cli_register_multiple(cli_timing, ARRAY_LEN(cli_timing)); } diff --git a/main/udptl.c b/main/udptl.c index 75c48dcd77..29d24b713a 100644 --- a/main/udptl.c +++ b/main/udptl.c @@ -1421,8 +1421,20 @@ int ast_udptl_reload(void) return 0; } +/*! + * \internal + * \brief Clean up resources on Asterisk shutdown + */ +static void udptl_shutdown(void) +{ + ast_cli_unregister_multiple(cli_udptl, ARRAY_LEN(cli_udptl)); +} + void ast_udptl_init(void) { - ast_cli_register_multiple(cli_udptl, ARRAY_LEN(cli_udptl)); __ast_udptl_reload(0); + + ast_cli_register_multiple(cli_udptl, ARRAY_LEN(cli_udptl)); + + ast_register_atexit(udptl_shutdown); } diff --git a/res/res_calendar.c b/res/res_calendar.c index 18d778ce4f..369ecd559c 100644 --- a/res/res_calendar.c +++ b/res/res_calendar.c @@ -599,9 +599,9 @@ static struct ast_calendar_event *destroy_event(struct ast_calendar_event *event * but haven't hit the end event yet, go ahead and set the devicestate to the current busy status */ if (event->bs_start_sched < 0 && event->bs_end_sched >= 0) { if (!calendar_is_busy(event->owner)) { - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Calendar:%s", event->owner->name); + ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name); } else { - ast_devstate_changed(AST_DEVICE_BUSY, "Calendar:%s", event->owner->name); + ast_devstate_changed(AST_DEVICE_BUSY, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name); } } @@ -859,9 +859,9 @@ static int calendar_devstate_change(const void *data) /* We can have overlapping events, so ignore the event->busy_state and check busy state * based on all events in the calendar */ if (!calendar_is_busy(event->owner)) { - ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Calendar:%s", event->owner->name); + ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name); } else { - ast_devstate_changed(AST_DEVICE_BUSY, "Calendar:%s", event->owner->name); + ast_devstate_changed(AST_DEVICE_BUSY, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name); } event = ast_calendar_unref_event(event); diff --git a/res/res_clialiases.c b/res/res_clialiases.c index d67f3ba39b..bb1a78d3b7 100644 --- a/res/res_clialiases.c +++ b/res/res_clialiases.c @@ -209,7 +209,10 @@ static void load_config(int reload) alias->cli_entry.command = alias->alias; alias->cli_entry.usage = "Aliased CLI Command\n"; - ast_cli_register(&alias->cli_entry); + if (ast_cli_register(&alias->cli_entry)) { + ao2_ref(alias, -1); + continue; + } ao2_link(cli_aliases, alias); ast_verbose(VERBOSE_PREFIX_2 "Aliased CLI command '%s' to '%s'\n", v1->name, v1->value); ao2_ref(alias, -1); diff --git a/res/res_jabber.c b/res/res_jabber.c index 92ff98cf45..9c6ba740dc 100644 --- a/res/res_jabber.c +++ b/res/res_jabber.c @@ -350,7 +350,7 @@ static char *aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd, static void aji_create_affiliations(struct aji_client *client, const char *node); static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type); static void aji_publish_device_state(struct aji_client *client, const char * device, - const char *device_state); + const char *device_state, unsigned int cachable); static int aji_handle_pubsub_error(void *data, ikspak *pak); static int aji_handle_pubsub_event(void *data, ikspak *pak); static void aji_pubsub_subscribe(struct aji_client *client, const char *node); @@ -364,7 +364,7 @@ static void aji_publish_mwi(struct aji_client *client, const char *mailbox, static void aji_devstate_cb(const struct ast_event *ast_event, void *data); static void aji_mwi_cb(const struct ast_event *ast_event, void *data); static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node, - const char *event_type); + const char *event_type, unsigned int cachable); /* No transports in this version */ /* static int aji_create_transport(char *label, struct aji_client *client); @@ -774,7 +774,7 @@ static struct ast_custom_function jabberstatus_function = { */ static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen) { - char *aux = NULL, *parse = NULL; + char *parse = NULL; int timeout; int jidlen, resourcelen; struct timeval start; @@ -891,7 +891,7 @@ static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, ch continue; } found = 1; - aux = ast_strdupa(tmp->message); + ast_copy_string(buf, tmp->message, buflen); AST_LIST_REMOVE_CURRENT(list); aji_message_destroy(tmp); break; @@ -916,7 +916,6 @@ static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, ch ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid); return -1; } - ast_copy_string(buf, aux, buflen); return 0; } @@ -3269,6 +3268,7 @@ static void aji_devstate_cb(const struct ast_event *ast_event, void *data) { const char *device; const char *device_state; + unsigned int cachable; struct aji_client *client; if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) { @@ -3280,7 +3280,8 @@ static void aji_devstate_cb(const struct ast_event *ast_event, void *data) client = ASTOBJ_REF((struct aji_client *) data); device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE); device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE)); - aji_publish_device_state(client, device, device_state); + cachable = ast_event_get_ie_uint(ast_event, AST_EVENT_IE_CACHABLE); + aji_publish_device_state(client, device, device_state, cachable); ASTOBJ_UNREF(client, ast_aji_client_destroy); } @@ -3320,11 +3321,13 @@ static void aji_init_event_distribution(struct aji_client *client) */ static int aji_handle_pubsub_event(void *data, ikspak *pak) { - char *item_id, *device_state, *context; + char *item_id, *device_state, *context, *cachable_str; int oldmsgs, newmsgs; iks *item, *item_content; struct ast_eid pubsub_eid; struct ast_event *event; + unsigned int cachable = AST_DEVSTATE_CACHABLE; + item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item"); if (!item) { ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n"); @@ -3339,11 +3342,14 @@ static int aji_handle_pubsub_event(void *data, ikspak *pak) } if (!strcasecmp(iks_name(item_content), "state")) { device_state = iks_find_cdata(item, "state"); + if ((cachable_str = iks_find_cdata(item, "cachable"))) { + sscanf(cachable_str, "%30d", &cachable); + } if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE, - AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE, - AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID, - AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid), - AST_EVENT_IE_END))) { + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE, + AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID, + AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid), + AST_EVENT_IE_END))) { return IKS_FILTER_EAT; } } else if (!strcasecmp(iks_name(item_content), "mailbox")) { @@ -3363,7 +3369,13 @@ static int aji_handle_pubsub_event(void *data, ikspak *pak) iks_name(item_content)); return IKS_FILTER_EAT; } - ast_event_queue_and_cache(event); + + if (cachable == AST_DEVSTATE_CACHABLE) { + ast_event_queue_and_cache(event); + } else { + ast_event_queue(event); + } + return IKS_FILTER_EAT; } @@ -3438,7 +3450,7 @@ static void aji_pubsub_subscribe(struct aji_client *client, const char *node) * \return iks * */ static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node, - const char *event_type) + const char *event_type, unsigned int cachable) { iks *request = aji_pubsub_iq_create(client, "set"); iks *pubsub, *publish, *item; @@ -3452,8 +3464,24 @@ static iks* aji_build_publish_skeleton(struct aji_client *client, const char *no } item = iks_insert(publish, "item"); iks_insert_attrib(item, "id", node); - return item; + if (cachable == AST_DEVSTATE_NOT_CACHABLE) { + iks *options, *x, *field_form_type, *field_persist; + + options = iks_insert(pubsub, "publish-options"); + x = iks_insert(options, "x"); + iks_insert_attrib(x, "xmlns", "jabber:x:data"); + iks_insert_attrib(x, "type", "submit"); + field_form_type = iks_insert(x, "field"); + iks_insert_attrib(field_form_type, "var", "FORM_TYPE"); + iks_insert_attrib(field_form_type, "type", "hidden"); + iks_insert_cdata(iks_insert(field_form_type, "value"), "http://jabber.org/protocol/pubsub#publish-options", 0); + field_persist = iks_insert(x, "field"); + iks_insert_attrib(field_persist, "var", "pubsub#persist_items"); + iks_insert_cdata(iks_insert(field_persist, "value"), "0", 1); + } + + return item; } /*! @@ -3464,11 +3492,11 @@ static iks* aji_build_publish_skeleton(struct aji_client *client, const char *no * \return void */ static void aji_publish_device_state(struct aji_client *client, const char *device, - const char *device_state) + const char *device_state, unsigned int cachable) { - iks *request = aji_build_publish_skeleton(client, device, "device_state"); + iks *request = aji_build_publish_skeleton(client, device, "device_state", cachable); iks *state; - char eid_str[20]; + char eid_str[20], cachable_str[2]; if (ast_test_flag(&pubsubflags, AJI_PUBSUB_AUTOCREATE)) { if (ast_test_flag(&pubsubflags, AJI_XEP0248)) { aji_create_pubsub_node(client, "leaf", device, "device_state"); @@ -3480,6 +3508,8 @@ static void aji_publish_device_state(struct aji_client *client, const char *devi state = iks_insert(request, "state"); iks_insert_attrib(state, "xmlns", "http://asterisk.org"); iks_insert_attrib(state, "eid", eid_str); + snprintf(cachable_str, sizeof(cachable_str), "%u", cachable); + iks_insert_attrib(state, "cachable", cachable_str); iks_insert_cdata(state, device_state, strlen(device_state)); ast_aji_send(client, iks_root(request)); iks_delete(request); @@ -3499,7 +3529,7 @@ static void aji_publish_mwi(struct aji_client *client, const char *mailbox, char eid_str[20]; iks *mailbox_node, *request; snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context); - request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting"); + request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting", 1); ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default); mailbox_node = iks_insert(request, "mailbox"); iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org"); diff --git a/sounds/Makefile b/sounds/Makefile index 7e9d963eac..2bea13bd89 100644 --- a/sounds/Makefile +++ b/sounds/Makefile @@ -20,7 +20,7 @@ SOUNDS_DIR:=$(DESTDIR)$(ASTDATADIR)/sounds SOUNDS_CACHE_DIR?= MOH_DIR:=$(DESTDIR)$(ASTDATADIR)/moh CORE_SOUNDS_VERSION:=1.4.22 -EXTRA_SOUNDS_VERSION:=1.4.11 +EXTRA_SOUNDS_VERSION:=1.4.12 MOH_VERSION:=2.03 SOUNDS_URL:=http://downloads.asterisk.org/pub/telephony/sounds/releases MCS:=$(subst -EN-,-en-,$(MENUSELECT_CORE_SOUNDS))