dect
/
asterisk
Archived
13
0
Fork 0

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
This commit is contained in:
markster 2003-04-06 18:19:51 +00:00
parent c30aaa8906
commit 249face569
8 changed files with 568 additions and 155 deletions

View File

@ -1,3 +1,4 @@
-- Add "hint" support
-- Improve call forwarding using new "Local" channel driver.
-- Add "Local" channel
-- Substantial SIP enhancements including retransmissions

View File

@ -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/

View File

@ -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;
}

View File

@ -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, "<?xml version=\"1.0\"?>\n");
t = tmp + strlen(tmp);
sprintf(t, "<!DOCTYPE presence PUBLIC \"-//IETF//DTD RFCxxxx XPIDF 1.0//EN\" \"xpidf.dtd\">\n");
t = tmp + strlen(tmp);
sprintf(t, "<presence>\n");
t = tmp + strlen(tmp);
sprintf(t, "<presentity uri=\"%s;method=SUBSCRIBE\" />\n", mfrom);
t = tmp + strlen(tmp);
sprintf(t, "<atom id=\"%s\">\n", p->exten);
t = tmp + strlen(tmp);
sprintf(t, "<address uri=\"%s;user=ip\" priority=\"0,800000\">\n", mto);
t = tmp + strlen(tmp);
sprintf(t, "<status status=\"%s\" />\n", !state ? "open" : (state==1) ? "inuse" : "closed");
t = tmp + strlen(tmp);
sprintf(t, "<msnsubstatus substatus=\"%s\" />\n", !state ? "online" : (state==1) ? "onthephone" : "offline");
t = tmp + strlen(tmp);
sprintf(t, "</address>\n</atom>\n</presence>\n");
} else {
add_header(&req, "Event", "dialog");
add_header(&req, "Content-Type", "application/dialog-info+xml");
t = tmp;
sprintf(t, "<?xml version=\"1.0\"?>\n");
t = tmp + strlen(tmp);
sprintf(t, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full":"partial", mfrom);
t = tmp + strlen(tmp);
sprintf(t, "<dialog id=\"%s\">\n", p->exten);
t = tmp + strlen(tmp);
sprintf(t, "<state>%s</state>\n", state ? "confirmed" : "terminated");
t = tmp + strlen(tmp);
sprintf(t, "</dialog>\n</dialog-info>\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)) {

View File

@ -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
/*!

View File

@ -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);

380
pbx.c
View File

@ -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(&notifylock);
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(&notifylock);
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(&notifylock);
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(&notifylock);
return -1;
}
list = malloc(sizeof(struct ast_notify));
if (!list) {
pthread_mutex_unlock(&notifylock);
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(&notifylock);
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(&notifylock);
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(&notifylock);
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(&notifylock);
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(&notifylock);
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(&notifylock);
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 */

View File

@ -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++)