From 249face569822d37bb4d950272923c28f365842d Mon Sep 17 00:00:00 2001 From: markster Date: Sun, 6 Apr 2003 18:19:51 +0000 Subject: [PATCH] Merge enhanced status changes, add SIP subscribe from Andre git-svn-id: http://svn.digium.com/svn/asterisk/trunk@759 f38db490-d61c-443f-a65b-d21fe96a405b --- CHANGES | 1 + CREDITS | 1 + channel.c | 2 - channels/chan_sip.c | 296 +++++++++++++++++++++++++++++--- include/asterisk/pbx.h | 11 +- manager.c | 20 ++- pbx.c | 380 ++++++++++++++++++++++++++++------------- pbx/pbx_config.c | 12 +- 8 files changed, 568 insertions(+), 155 deletions(-) diff --git a/CHANGES b/CHANGES index cc9cad3da..334c47813 100755 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,4 @@ + -- Add "hint" support -- Improve call forwarding using new "Local" channel driver. -- Add "Local" channel -- Substantial SIP enhancements including retransmissions diff --git a/CREDITS b/CREDITS index a3ad07c35..4ee9df741 100755 --- a/CREDITS +++ b/CREDITS @@ -24,6 +24,7 @@ PhoneJack and Linejack card to the project. (http://www.quicknet.net) === MISCELLANEOUS PATCHES === James Golovich - Innumerable contributions You can find him and asterisk-perl at http://asterisk.gnuinter.net +Andre Bierwirth - Extension hints and status Oliver Daudey - ISDN4Linux fixes Pauline Middelink - ISDN4Linux patches and some general patches. She can be found at http://www.polyware.nl/~middelink/En/ diff --git a/channel.c b/channel.c index ed9bd2511..56528b812 100755 --- a/channel.c +++ b/channel.c @@ -1496,8 +1496,6 @@ int ast_device_state(char *device) } chanls = chanls->next; } - if (!chanls) - ast_log(LOG_WARNING, "No channel type registered for '%s'\n", tech); PTHREAD_MUTEX_UNLOCK(&chlock); return AST_DEVICE_INVALID; } diff --git a/channels/chan_sip.c b/channels/chan_sip.c index e54c1bae3..272a5a8a6 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -197,6 +197,10 @@ static struct sip_pvt { int initid; /* Auto-congest ID if appropriate */ int autokillid; /* Auto-kill ID */ + int subscribed; + int stateid; + int dialogver; + int dtmfmode; struct ast_dsp *vad; @@ -718,10 +722,13 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner) { struct sip_pvt *cur, *prev = NULL; struct sip_pkt *cp; + if (p->stateid > -1) + ast_extension_state_del(p->stateid, NULL); if (p->initid > -1) ast_sched_del(sched, p->initid); if (p->autokillid > -1) ast_sched_del(sched, p->autokillid); + if (p->rtp) { ast_rtp_destroy(p->rtp); } @@ -1246,9 +1253,11 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg memset(p, 0, sizeof(struct sip_pvt)); p->initid = -1; p->autokillid = -1; + p->stateid = -1; p->rtp = ast_rtp_new(NULL, NULL); p->branch = rand(); p->tag = rand(); + /* Start with 101 instead of 1 */ p->ocseq = 101; if (!p->rtp) { @@ -1863,10 +1872,10 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, stru char contact[256]; char *c; if ((c=getsipuri(ot))) { - snprintf(contact, sizeof(contact), "<%s@%s:%d>", c, inet_ntoa(p->ourip), ourport); + snprintf(contact, sizeof(contact), "<%s@%s:%d>;expires=%d", c, inet_ntoa(p->ourip), ourport, p->expirey); free(c); } else { - snprintf(contact, sizeof(contact), "<%s:%d>", inet_ntoa(p->ourip), ourport); + snprintf(contact, sizeof(contact), "<%s:%d>;expires=%d", inet_ntoa(p->ourip), ourport, p->expirey); } snprintf(tmp, sizeof(tmp), "%d", p->expirey); add_header(resp, "Expires", tmp); @@ -2284,6 +2293,92 @@ static int transmit_invite(struct sip_pvt *p, char *cmd, int sdp, char *auth, ch return send_request(p, &req, 1, p->ocseq); } +static int transmit_state_notify(struct sip_pvt *p, int state, int full) +{ + char tmp[2000]; + char from[256], to[256]; + char *t, *c, *a; + char *mfrom, *mto; + struct sip_request req; + char clen[20]; + + strncpy(from, get_header(&p->initreq, "From"), sizeof(from)-1); + + c = ditch_braces(from); + if (strncmp(c, "sip:", 4)) { + ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); + return -1; + } + if ((a = strchr(c, ';'))) { + *a = '\0'; + } + mfrom = c; + + reqprep(&req, p, "NOTIFY", 1); + + if (p->subscribed == 1) { + strncpy(to, get_header(&p->initreq, "To"), sizeof(to)-1); + + c = ditch_braces(to); + if (strncmp(c, "sip:", 4)) { + ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); + return -1; + } + if ((a = strchr(c, ';'))) { + *a = '\0'; + } + mto = c; + + add_header(&req, "Content-Type", "application/xpidf+xml"); + + if ((state==AST_EXTENSION_UNAVAILABLE) || (state==AST_EXTENSION_BUSY)) + state = 2; + else if (state==AST_EXTENSION_INUSE) + state = 1; + else + state = 0; + + t = tmp; + sprintf(t, "\n"); + t = tmp + strlen(tmp); + sprintf(t, "\n"); + t = tmp + strlen(tmp); + sprintf(t, "\n"); + t = tmp + strlen(tmp); + sprintf(t, "\n", mfrom); + t = tmp + strlen(tmp); + sprintf(t, "\n", p->exten); + t = tmp + strlen(tmp); + sprintf(t, "
\n", mto); + t = tmp + strlen(tmp); + sprintf(t, "\n", !state ? "open" : (state==1) ? "inuse" : "closed"); + t = tmp + strlen(tmp); + sprintf(t, "\n", !state ? "online" : (state==1) ? "onthephone" : "offline"); + t = tmp + strlen(tmp); + sprintf(t, "
\n
\n
\n"); + } else { + add_header(&req, "Event", "dialog"); + add_header(&req, "Content-Type", "application/dialog-info+xml"); + + t = tmp; + sprintf(t, "\n"); + t = tmp + strlen(tmp); + sprintf(t, "\n", p->dialogver++, full ? "full":"partial", mfrom); + t = tmp + strlen(tmp); + sprintf(t, "\n", p->exten); + t = tmp + strlen(tmp); + sprintf(t, "%s\n", state ? "confirmed" : "terminated"); + t = tmp + strlen(tmp); + sprintf(t, "\n\n"); + } + + snprintf(clen, sizeof(clen), "%d", strlen(tmp)); + add_header(&req, "Content-Length", clen); + add_line(&req, tmp); + + return send_request(p, &req, 1, p->ocseq); +} + static int transmit_notify(struct sip_pvt *p, int newmsgs, int oldmsgs) { struct sip_request req; @@ -2307,8 +2402,7 @@ static int transmit_notify(struct sip_pvt *p, int newmsgs, int oldmsgs) parse(&p->initreq); } - p->lastinvite = p->ocseq; - return send_request(p, &req, 1, p->ocseq); + return send_request(p, &req, 0, p->ocseq); } static int transmit_register(struct sip_registry *r, char *cmd, char *auth); @@ -2447,6 +2541,7 @@ static int expire_register(void *data) struct sip_peer *p = data; memset(&p->addr, 0, sizeof(p->addr)); p->expire = -1; + ast_device_state_changed("SIP/%s", p->name); return 0; } @@ -2478,14 +2573,14 @@ static int parse_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_req if (n) *n = '\0'; } - if (!strcasecmp(c, "*")) { + if (!strcasecmp(c, "*") || !expirey) { /* This means remove all registrations and return OK */ memset(&p->addr, 0, sizeof(p->addr)); if (p->expire > -1) ast_sched_del(sched, p->expire); p->expire = -1; if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Unegistered SIP '%s'\n", p->username); + ast_verbose(VERBOSE_PREFIX_3 "Unregistered SIP '%s'\n", p->username); return 0; } /* Make sure it's a SIP URL */ @@ -2755,6 +2850,22 @@ static int check_auth(struct sip_pvt *p, struct sip_request *req, char *randdata return res; } +static int cb_extensionstate(char *context, char* exten, int state, void *data) +{ + struct sip_pvt *p = data; + if (state == -1) { + sip_scheddestroy(p, 15000); + p->stateid = -1; + return 0; + } + + transmit_state_notify(p, state, 1); + + if (option_debug) + ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %d for Notify User %s\n", exten, state, p->username); + return 0; +} + static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct sip_request *req, char *uri) { int res = -1; @@ -2801,6 +2912,9 @@ static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct si peer = peer->next; } ast_pthread_mutex_unlock(&peerl.lock); + if (!res) { + ast_device_state_changed("SIP/%s", peer->name); + } if (res < 0) transmit_response(p, "401 Unauthorized", &p->initreq); return res; @@ -2995,7 +3109,7 @@ static int check_via(struct sip_pvt *p, struct sip_request *req) return 0; } -static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, char *uri) +static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, char *uri, int reliable) { struct sip_user *user; struct sip_peer *peer; @@ -3031,7 +3145,7 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, cha ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", p->nat); ast_rtp_setnat(p->rtp, p->nat); } - if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, cmd, uri, 1))) { + if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, cmd, uri, reliable))) { sip_cancel_destroy(p); strncpy(p->context, user->context, sizeof(p->context) - 1); if (strlen(user->callerid) && strlen(p->callerid)) @@ -3229,6 +3343,7 @@ static int sip_show_channels(int fd, int argc, char *argv[]) cur = iflist; ast_cli(fd, FORMAT2, "Peer", "Username", "Call ID", "Seq (Tx/Rx)", "Lag", "Jitter", "Format"); while (cur) { + if (!cur->subscribed) { ast_cli(fd, FORMAT, inet_ntoa(cur->sa.sin_addr), strlen(cur->username) ? cur->username : "(None)", cur->callid, @@ -3236,8 +3351,9 @@ static int sip_show_channels(int fd, int argc, char *argv[]) 0, 0, cur->owner ? cur->owner->nativeformats : 0); - cur = cur->next; numchans++; + } + cur = cur->next; } ast_pthread_mutex_unlock(&iflock); ast_cli(fd, "%d active SIP channel(s)\n", numchans); @@ -3525,6 +3641,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ Well, as long as it's not a 100 response... since we might need to hang around for something more "difinitive" */ if (resp != 100) { + int statechanged = 0; peer = p->peerpoke; gettimeofday(&tv, NULL); pingtime = (tv.tv_sec - peer->ps.tv_sec) * 1000 + @@ -3532,14 +3649,23 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ if (pingtime < 1) pingtime = 1; if ((peer->lastms < 0) || (peer->lastms > peer->maxms)) { - if (pingtime <= peer->maxms) - ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE!\n", peer->name); + if (pingtime <= peer->maxms) { + ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE!\n", peer->name); + statechanged = 1; + } } else if ((peer->lastms > 0) && (peer->lastms <= peer->maxms)) { - if (pingtime > peer->maxms) + if (pingtime > peer->maxms) { ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED!\n", peer->name); + statechanged = 1; + } } + if (!peer->lastms) + statechanged = 1; peer->lastms = pingtime; peer->call = NULL; + if (statechanged) + ast_device_state_changed("SIP/%s", peer->name); + if (peer->pokeexpire > -1) ast_sched_del(sched, peer->pokeexpire); if (!strcasecmp(msg, "INVITE")) @@ -3547,7 +3673,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ p->needdestroy = 1; /* Try again eventually */ if ((peer->lastms < 0) || (peer->lastms > peer->maxms)) - peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer); + peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer); else peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_OK, sip_poke_peer_s, peer); } @@ -3594,8 +3720,10 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ ast_log(LOG_WARNING, "Notify answer on an owned channel?\n"); ast_queue_hangup(p->owner, 0); } else { - sip_destroy(p); - p = NULL; + if (!p->subscribed) { + sip_destroy(p); + p = NULL; + } } } else if (!strcasecmp(msg, "INVITE")) { if (strlen(get_header(req, "Content-Type"))) @@ -3699,13 +3827,18 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ ast_log(LOG_NOTICE, "Dunno anything about a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : inet_ntoa(p->sa.sin_addr)); } } else { - if (sipdebug) - ast_verbose("Message is %s\n", msg); - switch(resp) { - case 200: - if (!strcasecmp(msg, "INVITE") || !strcasecmp(msg, "REGISTER") ) - transmit_request(p, "ACK", 0, 0); - break; + if (p->subscribed) { + /* Acknowledge sequence number */ + __sip_ack(p, seqno, 0); + } else { + if (sipdebug) + ast_verbose("Message is %s\n", msg); + switch(resp) { + case 200: + if (!strcasecmp(msg, "INVITE") || !strcasecmp(msg, "REGISTER") ) + transmit_request(p, "ACK", 0, 0); + break; + } } } if (owner) @@ -3922,7 +4055,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc ast_verbose("Ignoring this request\n"); if (!p->lastinvite) { /* Handle authentication if this is our first invite */ - res = check_user(p, req, cmd, e); + res = check_user(p, req, cmd, e, 1); if (res) { if (res < 0) { ast_log(LOG_NOTICE, "Failed to authenticate user %s\n", get_header(req, "From")); @@ -4046,6 +4179,71 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc ast_verbose("Receiving message!\n"); receive_message(p, req); transmit_response(p, "200 OK", req); + } else if (!strcasecmp(cmd, "SUBSCRIBE")) { + if (!ignore) { + /* Use this as the basis */ + if (sipdebug) + ast_verbose("Using latest SUBSCRIBE request as basis request\n"); + /* This call is no longer outgoing if it ever was */ + p->outgoing = 0; + copy_request(&p->initreq, req); + check_via(p, req); + } else if (sipdebug) + ast_verbose("Ignoring this request\n"); + + if (!p->lastinvite) { + /* Handle authentication if this is our first subscribe */ + res = check_user(p, req, cmd, e, 0); + if (res) { + if (res < 0) { + ast_log(LOG_NOTICE, "Failed to authenticate user %s for SUBSCRIBE\n", get_header(req, "From")); + sip_destroy(p); + } + return 0; + } + /* Initialize the context if it hasn't been already */ + if (!strlen(p->context)) + strncpy(p->context, context, sizeof(p->context) - 1); + if ((res = get_destination(p, NULL))) { + if (res < 0) + transmit_response(p, "404 Not Found", req); + else + transmit_response(p, "484 Address Incomplete", req); + sip_destroy(p); + p = NULL; + c = NULL; + } else { + /* Initialize tag */ + p->tag = rand(); + + if (!strcmp(get_header(req, "Accept"), "application/dialog-info+xml")) + p->subscribed = 2; + else + p->subscribed = 1; + + p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p); + } + + } else + c = p->owner; + + if (!ignore && p) + p->lastinvite = seqno; + if (p) { + if (!(p->expirey = atoi(get_header(req, "Expires")))) { + transmit_response(p, "200 OK", req); + sip_destroy(p); + return 0; + } + // The next line can be removed if the SNOM200 Expires bug is fixed + if (p->subscribed == 1) { + if (p->expirey>max_expirey) + p->expirey = max_expirey; + } + transmit_response(p, "200 OK", req); + sip_scheddestroy(p, (p->expirey+10)*1000); + transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); + } } else if (!strcasecmp(cmd, "INFO")) { if (sipdebug) ast_verbose("Receiving DTMF!\n"); @@ -4276,6 +4474,7 @@ static int restart_monitor(void) static int sip_poke_noanswer(void *data) { struct sip_peer *peer = data; + peer->pokeexpire = -1; if (peer->lastms > -1) ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE!\n", peer->name); @@ -4283,6 +4482,7 @@ static int sip_poke_noanswer(void *data) sip_destroy(peer->call); peer->call = NULL; peer->lastms = -1; + ast_device_state_changed("SIP/%s", peer->name); /* Try again quickly */ peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer); return 0; @@ -4332,6 +4532,52 @@ static int sip_poke_peer(struct sip_peer *peer) return 0; } +static int sip_devicestate(void *data) +{ + char *ext, *host; + char tmp[256] = ""; + char *dest = data; + + struct hostent *hp; + struct sip_peer *p; + int found = 0; + + int res = AST_DEVICE_INVALID; + + strncpy(tmp, dest, sizeof(tmp) - 1); + host = strchr(tmp, '@'); + if (host) { + *host = '\0'; + host++; + ext = tmp; + } else { + host = tmp; + ext = NULL; + } + + ast_pthread_mutex_lock(&peerl.lock); + p = peerl.peers; + while (p) { + if (!strcasecmp(p->name, host)) { + found++; + res = AST_DEVICE_UNAVAILABLE; + if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) && + (!p->maxms || ((p->lastms > -1) && (p->lastms <= p->maxms)))) { + /* peer found and valid */ + res = AST_DEVICE_UNKNOWN; + break; + } + } + p = p->next; + } + ast_pthread_mutex_unlock(&peerl.lock); + if (!p && !found) { + hp = gethostbyname(host); + if (hp) + res = AST_DEVICE_UNKNOWN; + } + return res; +} static struct ast_channel *sip_request(char *type, int format, void *data) { @@ -4841,7 +5087,7 @@ int load_module() res = reload_config(); if (!res) { /* Make sure we can register our sip channel type */ - if (ast_channel_register(type, tdesc, capability, sip_request)) { + if (ast_channel_register_ex(type, tdesc, capability, sip_request, sip_devicestate)) { ast_log(LOG_ERROR, "Unable to register channel class %s\n", type); return -1; } @@ -4871,6 +5117,7 @@ int load_module() sip_do_register(reg); ast_pthread_mutex_unlock(&peerl.lock); + /* And start the monitor for the first time */ restart_monitor(); } @@ -4961,6 +5208,7 @@ int reload(void) int unload_module() { struct sip_pvt *p, *pl; + /* First, take us out of the channel loop */ ast_channel_unregister(type); if (!ast_pthread_mutex_lock(&iflock)) { diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h index a616414e3..de7c00ec6 100755 --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -48,7 +48,7 @@ struct ast_include; struct ast_ignorepat; struct ast_sw; -typedef int (*ast_notify_cb_type)(char *context, char* id, int state, void *data); +typedef int (*ast_state_cb_type)(char *context, char* id, int state, void *data); //! Data structure associated with an asterisk switch struct ast_switch { @@ -215,12 +215,13 @@ int ast_extension_state(struct ast_channel *c, char *context, char *exten); //! Tells Asterisk the State for Device is changed /*! - * \param device devicename like a dialstring + * \param fmt devicename like a dialstring with format parameters * Asterisk polls the new extensionstates and calls the registered * callbacks for the changed extensions * Returns 0 on success, -1 on failure */ -int ast_device_state_changed(char *device); +int ast_device_state_changed(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); //! Registers a state change callback /*! @@ -232,7 +233,7 @@ int ast_device_state_changed(char *device); * Return -1 on failure, ID on success */ int ast_extension_state_add(char *context, char *exten, - ast_notify_cb_type callback, void *data); + ast_state_cb_type callback, void *data); //! Deletes a registered state change callback by ID /*! @@ -240,7 +241,7 @@ int ast_extension_state_add(char *context, char *exten, * Removes the callback from list of callbacks * Return 0 on success, -1 on failure */ -int ast_extension_state_del(int id); +int ast_extension_state_del(int id, ast_state_cb_type callback); //! If an extension exists, return non-zero /*! diff --git a/manager.c b/manager.c index 6491ed2a9..dba21443b 100755 --- a/manager.c +++ b/manager.c @@ -425,7 +425,24 @@ static int action_mailboxstatus(struct mansession *s, struct message *m) return 0; } astman_send_ack(s, "Mailbox status will follow"); - manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting:%d\r\n", mailbox, ast_app_has_voicemail(mailbox)); + manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", mailbox, ast_app_has_voicemail(mailbox)); + return 0; +} + +static int action_extensionstate(struct mansession *s, struct message *m) +{ + char *exten = astman_get_header(m, "Exten"); + char *context = astman_get_header(m, "Context"); + int status; + if (!exten || !strlen(exten)) { + astman_send_error(s, "Extension not specified"); + return 0; + } + if (!context || !strlen(context)) + context = "default"; + astman_send_ack(s, "Extension status will follow"); + status = ast_extension_state(NULL, context, exten); + manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, status); return 0; } @@ -710,6 +727,7 @@ int init_manager(void) ast_manager_register( "Originate", EVENT_FLAG_CALL, action_originate, "Originate Call" ); ast_manager_register( "MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox" ); ast_manager_register( "Command", EVENT_FLAG_COMMAND, action_command, "Execute Command" ); + ast_manager_register( "ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status" ); ast_cli_register(&show_mancmds_cli); ast_cli_register(&show_manconn_cli); diff --git a/pbx.c b/pbx.c index 011bd2687..ac1b00cc4 100755 --- a/pbx.c +++ b/pbx.c @@ -133,18 +133,18 @@ struct ast_app { }; /* An extension state notify */ -struct ast_notify_cb { +struct ast_state_cb { int id; void *data; - ast_notify_cb_type callback; - struct ast_notify_cb *next; + ast_state_cb_type callback; + struct ast_state_cb *next; }; -struct ast_notify { +struct ast_hint { struct ast_exten *exten; int laststate; - struct ast_notify_cb *callbacks; - struct ast_notify *next; + struct ast_state_cb *callbacks; + struct ast_hint *next; }; @@ -311,9 +311,10 @@ static pthread_mutex_t switchlock = AST_MUTEX_INITIALIZER; struct ast_switch *switches = NULL; /* Lock for extension state notifys */ -static pthread_mutex_t notifylock = AST_MUTEX_INITIALIZER; -static int notifycnt = 0; -struct ast_notify *notifys = NULL; +static pthread_mutex_t hintlock = AST_MUTEX_INITIALIZER; +static int stateid = 1; +struct ast_hint *hints = NULL; +struct ast_state_cb *statecbs = NULL; int pbx_exec(struct ast_channel *c, /* Channel */ struct ast_app *app, @@ -1192,17 +1193,24 @@ int ast_extension_state(struct ast_channel *c, char *context, char *exten) return ast_extension_state2(e); } -int ast_device_state_changed(char *device) +int ast_device_state_changed(const char *fmt, ...) { - struct ast_notify *list; - struct ast_notify_cb *cblist; + struct ast_hint *list; + struct ast_state_cb *cblist; char hint[AST_MAX_EXTENSION]; + char device[AST_MAX_EXTENSION]; char *cur, *rest; int state; - - pthread_mutex_lock(¬ifylock); + + va_list ap; - list = notifys; + va_start(ap, fmt); + vsnprintf(device, sizeof(device)-1, fmt, ap); + va_end(ap); + + pthread_mutex_lock(&hintlock); + + list = hints; while (list) { @@ -1219,11 +1227,20 @@ int ast_device_state_changed(char *device) // Found extension execute callbacks state = ast_extension_state2(list->exten); if ((state != -1) && (state != list->laststate)) { + // For general callbacks + cblist = statecbs; + while (cblist) { + cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data); + cblist = cblist->next; + } + + // For extension callbacks cblist = list->callbacks; while (cblist) { cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data); cblist = cblist->next; } + list->laststate = state; } break; @@ -1234,24 +1251,60 @@ int ast_device_state_changed(char *device) list = list->next; } - pthread_mutex_unlock(¬ifylock); + pthread_mutex_unlock(&hintlock); return 1; } int ast_extension_state_add(char *context, char *exten, - ast_notify_cb_type callback, void *data) + ast_state_cb_type callback, void *data) { - struct ast_notify *list; - struct ast_notify_cb *cblist; + struct ast_hint *list; + struct ast_state_cb *cblist; struct ast_exten *e; + /* No context and extension add callback to statecbs list */ + if (!context && !exten) { + pthread_mutex_lock(&hintlock); + + cblist = statecbs; + while (cblist) { + if (cblist->callback == callback) { + cblist->data = data; + pthread_mutex_unlock(&hintlock); + } + + cblist = cblist->next; + } + + /* Now inserts the callback */ + cblist = malloc(sizeof(struct ast_state_cb)); + if (!cblist) { + pthread_mutex_unlock(&hintlock); + return -1; + } + memset(cblist, 0, sizeof(struct ast_state_cb)); + cblist->id = 0; + cblist->callback = callback; + cblist->data = data; + + cblist->next = statecbs; + statecbs = cblist; + + pthread_mutex_unlock(&hintlock); + return 0; + } + + if (!context || !exten) + return -1; + + /* This callback type is for only one hint */ e = ast_hint_extension(NULL, context, exten); if (!e) { return -1; } - pthread_mutex_lock(¬ifylock); - list = notifys; + pthread_mutex_lock(&hintlock); + list = hints; while (list) { if (list->exten == e) @@ -1260,135 +1313,188 @@ int ast_extension_state_add(char *context, char *exten, } if (!list) { - if (!e) { - pthread_mutex_unlock(¬ifylock); - return -1; - } - list = malloc(sizeof(struct ast_notify)); - if (!list) { - pthread_mutex_unlock(¬ifylock); - return -1; - } - /* Initialize and insert new item */ - memset(list, 0, sizeof(struct ast_notify)); - list->exten = e; - list->laststate = ast_extension_state2(e); - list->next = notifys; - notifys = list; - } - - /* Now inserts the callback */ - cblist = malloc(sizeof(struct ast_notify_cb)); - if (!cblist) { - pthread_mutex_unlock(¬ifylock); + pthread_mutex_unlock(&hintlock); return -1; } - memset(cblist, 0, sizeof(struct ast_notify_cb)); - cblist->id = notifycnt++; + + /* Now inserts the callback */ + cblist = malloc(sizeof(struct ast_state_cb)); + if (!cblist) { + pthread_mutex_unlock(&hintlock); + return -1; + } + memset(cblist, 0, sizeof(struct ast_state_cb)); + cblist->id = stateid++; cblist->callback = callback; cblist->data = data; cblist->next = list->callbacks; list->callbacks = cblist; - pthread_mutex_unlock(¬ifylock); + pthread_mutex_unlock(&hintlock); return cblist->id; } -static int ast_extension_state_clean(struct ast_exten *e) +int ast_extension_state_del(int id, ast_state_cb_type callback) +{ + struct ast_hint *list; + struct ast_state_cb *cblist, *cbprev; + + if (!id && !callback) + return -1; + + pthread_mutex_lock(&hintlock); + + /* id is zero is a callback without extension */ + if (!id) { + cbprev = NULL; + cblist = statecbs; + while (cblist) { + if (cblist->callback == callback) { + if (!cbprev) + statecbs = cblist->next; + else + cbprev->next = cblist->next; + + free(cblist); + + pthread_mutex_unlock(&hintlock); + return 0; + } + cbprev = cblist; + cblist = cblist->next; + } + + pthread_mutex_lock(&hintlock); + return -1; + } + + /* id greater zero is a callback with extension */ + list = hints; + while (list) { + cblist = list->callbacks; + cbprev = NULL; + while (cblist) { + if (cblist->id==id) { + if (!cbprev) + list->callbacks = cblist->next; + else + cbprev->next = cblist->next; + + free(cblist); + + pthread_mutex_unlock(&hintlock); + return 0; + } + cbprev = cblist; + cblist = cblist->next; + } + list = list->next; + } + + pthread_mutex_unlock(&hintlock); + return -1; +} + +static int ast_add_hint(struct ast_exten *e) +{ + struct ast_hint *list; + + if (!e) return -1; + + pthread_mutex_lock(&hintlock); + list = hints; + + /* Search if hint exists, do nothing */ + while (list) { + if (list->exten == e) { + pthread_mutex_unlock(&hintlock); + return -1; + } + list = list->next; + } + + list = malloc(sizeof(struct ast_hint)); + if (!list) { + pthread_mutex_unlock(&hintlock); + return -1; + } + /* Initialize and insert new item */ + memset(list, 0, sizeof(struct ast_hint)); + list->exten = e; + list->laststate = ast_extension_state2(e); + list->next = hints; + hints = list; + + pthread_mutex_unlock(&hintlock); + return 0; +} + +static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne) +{ + struct ast_hint *list; + + pthread_mutex_lock(&hintlock); + + list = hints; + + while(list) { + if (list->exten == oe) { + list->exten = ne; + pthread_mutex_unlock(&hintlock); + return 0; + } + list = list->next; + } + pthread_mutex_unlock(&hintlock); + + return -1; +} + +static int ast_remove_hint(struct ast_exten *e) { /* Cleanup the Notifys if hint is removed */ - struct ast_notify *list, *prev = NULL; - struct ast_notify_cb *cblist, *cbprev; + struct ast_hint *list, *prev = NULL; + struct ast_state_cb *cblist, *cbprev; - pthread_mutex_lock(¬ifylock); + if (!e) + return -1; - list = notifys; + pthread_mutex_lock(&hintlock); + + list = hints; while(list) { if (list->exten==e) { cbprev = NULL; cblist = list->callbacks; - while (cblist) { + while (cblist) { + /* Notify with -1 and remove all callbacks */ cbprev = cblist; cblist = cblist->next; - cblist->callback(list->exten->parent->name, list->exten->exten, -1, cblist->data); + cbprev->callback(list->exten->parent->name, list->exten->exten, -1, cbprev->data); free(cbprev); } list->callbacks = NULL; - if (!prev) { - notifys = list->next; - free(list); - list = notifys; - } else { + if (!prev) + hints = list->next; + else prev->next = list->next; - free(list); - list = prev->next; - } + + free(list); + + pthread_mutex_unlock(&hintlock); + return 0; } else { prev = list; list = list->next; } } - pthread_mutex_unlock(¬ifylock); - return 1; + pthread_mutex_unlock(&hintlock); + return -1; } -int ast_extension_state_del(int id) -{ - struct ast_notify *list, *prev = NULL; - struct ast_notify_cb *cblist, *cbprev; - int res = -1; - - pthread_mutex_lock(¬ifylock); - - list = notifys; - while (list) { - cblist = list->callbacks; - cbprev = NULL; - while (cblist) { - if (cblist->id==id) { - if (!cbprev) { - list->callbacks = cblist->next; - free(cblist); - cblist = list->callbacks; - } else { - cbprev->next = cblist->next; - free(cblist); - cblist = cbprev->next; - } - - if (!list->callbacks) { - if (!prev) { - notifys = list->next; - free(list); - list = notifys; - } else { - prev->next = list->next; - free(list); - list = prev->next; - } - } - res = 0; - break; - } else { - cbprev = cblist; - cblist = cblist->next; - } - } - - // we can have only one item - if (cblist || !list) - break; - - prev = list; - list = list->next; - } - - pthread_mutex_unlock(¬ifylock); - return res; -} int ast_get_hint(char *hint, int maxlen, struct ast_channel *c, char *context, char *exten) { @@ -1867,7 +1973,7 @@ int ast_context_remove_extension2(struct ast_context *con, char *extension, int exten = peer->peer; if (!peer->priority==PRIORITY_HINT) - ast_extension_state_clean(peer); + ast_remove_hint(peer); peer->datad(peer->data); free(peer); @@ -1914,7 +2020,7 @@ int ast_context_remove_extension2(struct ast_context *con, char *extension, int /* now, free whole priority extension */ if (peer->priority==PRIORITY_HINT) - ast_extension_state_clean(peer); + ast_remove_hint(peer); peer->datad(peer->data); free(peer); @@ -3337,10 +3443,14 @@ int ast_add_extension2(struct ast_context *con, tmp->next = e->next; tmp->peer = e->peer; } + if (tmp->priority == PRIORITY_HINT) + ast_change_hint(e,tmp); /* Destroy the old one */ e->datad(e->data); free(e); ast_pthread_mutex_unlock(&con->lock); + if (tmp->priority == PRIORITY_HINT) + ast_change_hint(e, tmp); /* And immediately return success. */ LOG; return 0; @@ -3373,6 +3483,9 @@ int ast_add_extension2(struct ast_context *con, } ast_pthread_mutex_unlock(&con->lock); /* And immediately return success. */ + if (tmp->priority == PRIORITY_HINT) + ast_add_hint(tmp); + LOG; return 0; } @@ -3383,6 +3496,9 @@ int ast_add_extension2(struct ast_context *con, ep *must* be defined or we couldn't have gotten here. */ ep->peer = tmp; ast_pthread_mutex_unlock(&con->lock); + if (tmp->priority == PRIORITY_HINT) + ast_add_hint(tmp); + /* And immediately return success. */ LOG; return 0; @@ -3399,6 +3515,9 @@ int ast_add_extension2(struct ast_context *con, con->root = tmp; } ast_pthread_mutex_unlock(&con->lock); + if (tmp->priority == PRIORITY_HINT) + ast_add_hint(tmp); + /* And immediately return success. */ LOG; return 0; @@ -3413,6 +3532,8 @@ int ast_add_extension2(struct ast_context *con, else con->root = tmp; ast_pthread_mutex_unlock(&con->lock); + if (tmp->priority == PRIORITY_HINT) + ast_add_hint(tmp); LOG; return 0; } @@ -3647,11 +3768,19 @@ int ast_pbx_outgoing_app(char *type, int format, void *data, int timeout, char * return res; } +static void destroy_exten(struct ast_exten *e) +{ + if (e->datad) + e->datad(e->data); + free(e); +} + void ast_context_destroy(struct ast_context *con, char *registrar) { struct ast_context *tmp, *tmpl=NULL; struct ast_include *tmpi, *tmpil= NULL; struct ast_sw *sw, *swl= NULL; + struct ast_exten *e, *el, *en; ast_pthread_mutex_lock(&conlock); tmp = contexts; while(tmp) { @@ -3683,6 +3812,21 @@ void ast_context_destroy(struct ast_context *con, char *registrar) free(swl); swl = sw; } + for (e = tmp->root; e; ) { + if (e->priority == PRIORITY_HINT) + ast_remove_hint(e); + e = e->next; + } + for (e = tmp->root; e;) { + for (en = e->peer; en;) { + el = en; + en = en->peer; + destroy_exten(el); + } + el = e; + e = e->next; + destroy_exten(el); + } free(tmp); if (!con) { /* Might need to get another one -- restart */ diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c index b957f29e4..baed0f9fe 100755 --- a/pbx/pbx_config.c +++ b/pbx/pbx_config.c @@ -1084,13 +1084,15 @@ static int handle_context_add_extension(int fd, int argc, char *argv[]) cidmatch = NULL; } prior = strsep(&whole_exten,","); - if (!strcmp(prior, "hint")) { - iprior = PRIORITY_HINT; - } else { - iprior = atoi(prior); + if (prior) { + if (!strcmp(prior, "hint")) { + iprior = PRIORITY_HINT; + } else { + iprior = atoi(prior); + } } app = strsep(&whole_exten,","); - if ((start = strchr(app, '(')) && (end = strrchr(app, ')'))) { + if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) { *start = *end = '\0'; app_data = start + 1; for (start = app_data; *start; start++)