Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 8215) +++ channels/chan_sip.c (working copy) @@ -516,6 +516,14 @@ struct sip_auth *next; /*!< Next auth structure in list */ }; +/*! \brief hintmgrparam: A structure to hold on to pointers for cb_hintmanager */ +struct hintmgrparam { + int state; /*!< The original state causing the cb */ + char *context; /*!< Context the hint is for */ + char *exten; /*!< Extension the hint is for */ + struct sip_pvt *p; /*!< SIP Session Structure Ptr */ +}; + #define SIP_ALREADYGONE (1 << 0) /*!< Whether or not we've already been destroyed by our peer */ #define SIP_NEEDDESTROY (1 << 1) /*!< if we need to be destroyed */ #define SIP_NOVIDEO (1 << 2) /*!< Didn't get video in invite, don't offer */ @@ -934,6 +942,7 @@ static int determine_firstline_parts(struct sip_request *req); static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to LOG_DEBUG at end of dialog, before destroying data */ static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype); +static int cb_extensionstate(char *context, char* exten, int state, void *data); static int transmit_state_notify(struct sip_pvt *p, int state, int full, int substate); static char *gettag(struct sip_request *req, char *header, char *tagbuf, int tagbufsize); int find_sip_method(char *msg); @@ -6367,30 +6376,71 @@ return res; } +/*! \brief cb_hintmanager: Callback to check on the status of an AST_EXTENSION_DEACTIVATED/REMOVED call already received ---*/ +/* When you do a 'reload' in Asterisk the ast_merge_contexts_and_delete() function call that gets + called during the reload will rebuild hints sending AST_EXTENSION_DEACTIVATED state msgs. Now, instead of + acting immediately on those msgs, we schedule this callback for 5 secs later and if the hint is + still not there, then we tear down the subscription, otherwise, we re-establish our connection with + the core and re-sync and re-transmit current state. */ +static int cb_hintmanager(void *data) +{ + struct hintmgrparam *hmgrp = data; + struct sip_pvt *p = NULL; + int firststate = hmgrp->state; + + /* Verify first that the SIP session is still around after having waited 5 secs */ + if (hmgrp->p) { + p = hmgrp->p; + p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p); + ast_log(LOG_DEBUG, "ast_extension_state on %s@%s is %d\n", p->exten, p->context, ast_extension_state(NULL, p->context, p->exten)); + if ((firststate = ast_extension_state(NULL, p->context, p->exten)) < 0) { + ast_log(LOG_DEBUG, "Still cannot retrieve state on %s@%s. Time to throw in the towel.\n", p->exten, p->context); + if (p->autokillid > -1) + sip_cancel_destroy(p); /* Remove subscription expiry for renewals */ + sip_scheddestroy(p, 10000); /* Delete subscription in 10 more secs since we've already waited 5 */ + ast_verbose(VERBOSE_PREFIX_2 "Extension state: Watcher for hint %s %s. Notify User %s\n", hmgrp->exten, hmgrp->state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username); + p->stateid = -1; + p->subscribed = NONE; + append_history(p, "Subscribestatus", "%s", hmgrp->state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated"); + transmit_state_notify(p, hmgrp->state, 1, 1); + } else { + if (option_debug > 1) + ast_verbose(VERBOSE_PREFIX_1 "Extension State Resync %s new state %s for Notify User %s\n", hmgrp->exten, ast_extension_state2str(firststate), p->username); + transmit_state_notify(p, firststate, 1, 1); /* Send current notification */ + } + } else + ast_log(LOG_DEBUG, "The SIP Session has gone away while we were waiting to be called back. Cleaning up and done.\n"); + + free(hmgrp); + return 0; +} + /*! \brief cb_extensionstate: Callback for the devicestate notification (SUBSCRIBE) support subsystem ---*/ /* If you add an "hint" priority to the extension in the dial plan, you will get notifications on device state changes */ static int cb_extensionstate(char *context, char* exten, int state, void *data) { struct sip_pvt *p = data; + struct hintmgrparam *hmgrp = NULL; switch(state) { case AST_EXTENSION_DEACTIVATED: /* Retry after a while */ case AST_EXTENSION_REMOVED: /* Extension is gone */ - if (p->autokillid > -1) - sip_cancel_destroy(p); /* Remove subscription expiry for renewals */ - sip_scheddestroy(p, 15000); /* Delete subscription in 15 secs */ - ast_verbose(VERBOSE_PREFIX_2 "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username); - p->stateid = -1; - p->subscribed = NONE; - append_history(p, "Subscribestatus", "%s", state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated"); + hmgrp = ast_calloc(1, sizeof(*hmgrp)); + hmgrp->state = state; + hmgrp->context = context; + hmgrp->exten = exten; + hmgrp->p = p; + ast_sched_add(sched, 5000, cb_hintmanager, hmgrp); break; default: /* Tell user */ p->laststate = state; break; } - transmit_state_notify(p, state, 1, 1); + if (state != AST_EXTENSION_DEACTIVATED && state != AST_EXTENSION_REMOVED) + transmit_state_notify(p, state, 1, 1); + if (option_debug > 1) ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %s for Notify User %s\n", exten, ast_extension_state2str(state), p->username); return 0;