Index: pbx.c =================================================================== RCS file: /usr/cvsroot/asterisk/pbx.c,v retrieving revision 1.263 diff -u -r1.263 pbx.c --- pbx.c 19 Jul 2005 15:54:17 -0000 1.263 +++ pbx.c 3 Aug 2005 08:38:25 -0000 @@ -1736,7 +1736,7 @@ char *cur, *rest; int res = -1; int allunavailable = 1, allbusy = 1, allfree = 1; - int busy = 0; + int busy = 0, inuse = 0, ring = 0; if (!e) return -1; @@ -1747,7 +1747,7 @@ do { rest = strchr(cur, '&'); if (rest) { - *rest = 0; + *rest = 0; rest++; } @@ -1758,7 +1758,15 @@ allbusy = 0; break; case AST_DEVICE_INUSE: - return AST_EXTENSION_INUSE; + inuse = 1; + allunavailable = 0; + allfree = 0; + break; + case AST_DEVICE_RINGING: + ring = 1; + allunavailable = 0; + allfree = 0; + break; case AST_DEVICE_BUSY: allunavailable = 0; allfree = 0; @@ -1777,7 +1785,13 @@ cur = rest; } while (cur); - if (allfree) + if (!inuse && ring) + return AST_EXTENSION_RINGING; + if (inuse && ring) + return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING); + if (inuse) + return AST_EXTENSION_INUSE; + if (allfree) return AST_EXTENSION_NOT_INUSE; if (allbusy) return AST_EXTENSION_BUSY; Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.801 diff -u -r1.801 chan_sip.c --- channels/chan_sip.c 3 Aug 2005 04:32:09 -0000 1.801 +++ channels/chan_sip.c 3 Aug 2005 08:38:29 -0000 @@ -276,6 +276,7 @@ #define DEFAULT_CONTEXT "default" static char default_context[AST_MAX_CONTEXT] = DEFAULT_CONTEXT; +static char default_subscribecontext[AST_MAX_CONTEXT]; static char default_language[MAX_LANGUAGE] = ""; @@ -287,6 +288,7 @@ #define DEFAULT_NOTIFYMIME "application/simple-message-summary" static char default_notifymime[AST_MAX_EXTENSION] = DEFAULT_NOTIFYMIME; +static int global_notifyringing = 1; /* Send notifications on ringing */ static int default_qualify = 0; /* Default Qualify= setting */ @@ -531,6 +533,7 @@ char from[256]; /* The From: header */ char useragent[256]; /* User agent in SIP request */ char context[AST_MAX_CONTEXT]; /* Context for this call */ + char subscribecontext[AST_MAX_CONTEXT]; /* Subscribecontext */ char fromdomain[MAXHOSTNAMELEN]; /* Domain to show in the from field */ char fromuser[AST_MAX_EXTENSION]; /* User to show in the user field */ char fromname[AST_MAX_EXTENSION]; /* Name to show in the user field */ @@ -577,8 +580,10 @@ int rtpholdtimeout; /* RTP timeout when on hold */ int rtpkeepalive; /* Send RTP packets for keepalive */ - int subscribed; /* Is this call a subscription? */ + /* Is this call a subscription? */ + enum subscriptionstate { NONE, XPIDF_XML, DIALOG_INFO_XML, CPIM_PIDF_XML, PIDF_XML, } subscribed; int stateid; + int laststate; /* Last known state */ int dialogver; struct ast_dsp *vad; /* Voice Activation Detection dsp */ @@ -616,6 +621,7 @@ char secret[80]; /* Password */ char md5secret[80]; /* Password in md5 */ char context[AST_MAX_CONTEXT]; /* Default context for incoming calls */ + char subscribecontext[AST_MAX_CONTEXT]; /* Default context for subscriptions */ char cid_num[80]; /* Caller ID num */ char cid_name[80]; /* Caller ID name */ char accountcode[AST_MAX_ACCOUNT_CODE]; /* Account code */ @@ -647,6 +653,7 @@ char md5secret[80]; /* Password in MD5 */ struct sip_auth *auth; /* Realm authentication list */ char context[AST_MAX_CONTEXT]; /* Default context for incoming calls */ + char subscribecontext[AST_MAX_CONTEXT]; /* Default context for subscriptions */ char username[80]; /* Temporary username until registration */ char accountcode[AST_MAX_ACCOUNT_CODE]; /* Account code */ int amaflags; /* AMA Flags (for billing) */ @@ -2826,6 +2833,7 @@ p->method = intended_method; p->initid = -1; p->autokillid = -1; + p->subscribed = NONE; p->stateid = -1; p->prefs = prefs; #ifdef OSP_SUPPORT @@ -3747,7 +3755,7 @@ return 0; } -/*--- reqprep: Initialize a SIP request packet ---*/ +/*--- reqprep: Initialize a SIP request response packet ---*/ static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch) { struct sip_request *orig = &p->initreq; @@ -4489,65 +4497,138 @@ char *t = tmp, *c, *a, *mfrom, *mto; size_t maxbytes = sizeof(tmp); struct sip_request req; + char hint[AST_MAX_EXTENSION]; + char *statestring = "terminated"; + enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED, } local_state = NOTIFY_OPEN; + char *pidfstate = "--"; + char *pidfnote= "Ready"; + + switch (state) { + case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE): + if (global_notifyringing) + statestring = "early"; + else + statestring = "confirmed"; + local_state = NOTIFY_INUSE; + pidfstate = "busy"; + pidfnote = "Ringing"; + break; + case AST_EXTENSION_RINGING: + statestring = "early"; + local_state = NOTIFY_INUSE; + pidfstate = "busy"; + pidfnote = "Ringing"; + break; + case AST_EXTENSION_INUSE: + statestring = "confirmed"; + local_state = NOTIFY_INUSE; + pidfstate = "busy"; + pidfnote = "On the phone"; + break; + case AST_EXTENSION_BUSY: + statestring = "confirmed"; + local_state = NOTIFY_CLOSED; + pidfstate = "busy"; + pidfnote = "On the phone"; + break; + case AST_EXTENSION_UNAVAILABLE: + statestring = "confirmed"; + local_state = NOTIFY_CLOSED; + pidfstate = "away"; + pidfnote = "Unavailable"; + break; + case AST_EXTENSION_NOT_INUSE: + default: + /* Default setting */ + break; + } + + /* Check which device/devices we are watching and if they are registered */ + if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten)) { + /* If they are not registered, we will override notification and show no availability */ + if (ast_device_state(hint) == AST_DEVICE_UNAVAILABLE) { + local_state = NOTIFY_CLOSED; + pidfstate = "away"; + pidfnote = "Not online"; + } + } memset(from, 0, sizeof(from)); - memset(to, 0, sizeof(to)); ast_copy_string(from, get_header(&p->initreq, "From"), sizeof(from)); - c = get_in_brackets(from); if (strncmp(c, "sip:", 4)) { ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); return -1; } - if ((a = strchr(c, ';'))) { + if ((a = strchr(c, ';'))) *a = '\0'; - } mfrom = c; - reqprep(&req, p, SIP_NOTIFY, 0, 1); - - if (p->subscribed == 1) { - ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to)); + memset(to, 0, sizeof(to)); + ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to)); + c = get_in_brackets(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; - c = get_in_brackets(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; + reqprep(&req, p, SIP_NOTIFY, 0, 1); + switch (p->subscribed) { + case XPIDF_XML: + case CPIM_PIDF_XML: add_header(&req, "Event", "presence"); add_header(&req, "Subscription-State", "active"); 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; - ast_build_string(&t, &maxbytes, "\n"); ast_build_string(&t, &maxbytes, "\n"); ast_build_string(&t, &maxbytes, "\n"); ast_build_string(&t, &maxbytes, "\n", mfrom); ast_build_string(&t, &maxbytes, "\n", p->exten); ast_build_string(&t, &maxbytes, "
\n", mto); - ast_build_string(&t, &maxbytes, "\n", !state ? "open" : (state==1) ? "inuse" : "closed"); - ast_build_string(&t, &maxbytes, "\n", !state ? "online" : (state==1) ? "onthephone" : "offline"); + ast_build_string(&t, &maxbytes, "\n", (local_state == NOTIFY_OPEN) ? "open" : (local_state == NOTIFY_INUSE) ? "inuse" : "closed"); + ast_build_string(&t, &maxbytes, "\n", (local_state == NOTIFY_OPEN) ? "online" : (local_state == NOTIFY_INUSE) ? "onthephone" : "offline"); ast_build_string(&t, &maxbytes, "
\n
\n
\n"); - } else { + break; + case PIDF_XML: /* Eyebeam supports this format */ + add_header(&req, "Event", "presence"); + add_header(&req, "Subscription-State", "active"); + add_header(&req, "Content-Type", "application/pidf+xml"); + ast_build_string(&t, &maxbytes, "\n"); + ast_build_string(&t, &maxbytes, "\n", mfrom); + ast_build_string(&t, &maxbytes, "\n"); + if (pidfstate[0] != '-') + ast_build_string(&t, &maxbytes, "%s\n", pidfstate); + ast_build_string(&t, &maxbytes, "\n"); + ast_build_string(&t, &maxbytes, "%s\n", pidfnote); /* Note */ + ast_build_string(&t, &maxbytes, "\n", p->exten); /* Tuple start */ + ast_build_string(&t, &maxbytes, "%s\n", mto); + if (pidfstate[0] == 'b') /* Busy? Still open ... */ + ast_build_string(&t, &maxbytes, "open\n"); + else + ast_build_string(&t, &maxbytes, "%s\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed"); + ast_build_string(&t, &maxbytes, "\n\n"); + break; + case DIALOG_INFO_XML: /* SNOM subscribes in this format */ add_header(&req, "Event", "dialog"); add_header(&req, "Content-Type", "application/dialog-info+xml"); ast_build_string(&t, &maxbytes, "\n"); - ast_build_string(&t, &maxbytes, "\n", p->dialogver++, full ? "full":"partial", mfrom); - ast_build_string(&t, &maxbytes, "\n", p->exten); - ast_build_string(&t, &maxbytes, "%s\n", state ? "confirmed" : "terminated"); - ast_build_string(&t, &maxbytes, "\n\n"); + ast_build_string(&t, &maxbytes, "\n", p->dialogver++, full ? "full":"partial", mto); + if ((state & AST_EXTENSION_RINGING) && global_notifyringing) + ast_build_string(&t, &maxbytes, "\n", p->exten); + else + ast_build_string(&t, &maxbytes, "\n", p->exten); + ast_build_string(&t, &maxbytes, "%s\n", statestring); + ast_build_string(&t, &maxbytes, "\n\n"); + break; + case NONE: + default: + break; } + if (t > tmp + sizeof(tmp)) ast_log(LOG_WARNING, "Buffer overflow detected!! (Please file a bug report)\n"); @@ -5461,7 +5542,7 @@ #endif ) return 0; - if (sipmethod == SIP_REGISTER) { + if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) { /* On a REGISTER, we have to use 401 and its family of headers instead of 407 and its family of headers -- GO SIP! Whoo hoo! Two things that do the same thing but are used in different circumstances! What a surprise. */ @@ -5645,10 +5726,13 @@ return res; } -/*--- cb_extensionstate: Part of thte SUBSCRIBE support subsystem ---*/ +/*--- 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; + if (state == -1) { sip_scheddestroy(p, 15000); p->stateid = -1; @@ -5656,6 +5740,7 @@ } transmit_state_notify(p, state, 1); + p->laststate = state; if (option_debug > 1) ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %d for Notify User %s\n", exten, state, p->username); @@ -6284,6 +6369,7 @@ ast_copy_string(p->cid_name, user->cid_name, sizeof(p->cid_name)); ast_copy_string(p->username, user->name, sizeof(p->username)); ast_copy_string(p->peersecret, user->secret, sizeof(p->peersecret)); + ast_copy_string(p->subscribecontext, user->subscribecontext, sizeof(p->subscribecontext)); ast_copy_string(p->peermd5secret, user->md5secret, sizeof(p->peermd5secret)); ast_copy_string(p->accountcode, user->accountcode, sizeof(p->accountcode)); ast_copy_string(p->language, user->language, sizeof(p->language)); @@ -6346,6 +6432,7 @@ } ast_copy_string(p->peersecret, peer->secret, sizeof(p->peersecret)); p->peersecret[sizeof(p->peersecret)-1] = '\0'; + ast_copy_string(p->subscribecontext, peer->subscribecontext, sizeof(p->subscribecontext)); ast_copy_string(p->peermd5secret, peer->md5secret, sizeof(p->peermd5secret)); p->peermd5secret[sizeof(p->peermd5secret)-1] = '\0'; p->callingpres = peer->callingpres; @@ -6452,16 +6539,28 @@ /*--- receive_message: Receive SIP MESSAGE method messages ---*/ -/* we handle messages within current calls currently */ +/* We only handle messages within current calls currently */ +/* Reference: RFC 3428 */ static void receive_message(struct sip_pvt *p, struct sip_request *req) { char buf[1024]; struct ast_frame f; + char *content_type; + + content_type = get_header(req, "Content-Type"); + if (strcmp(content_type, "text/plain")) { /* No text/plain attachment */ + transmit_response(p, "415 Unsupported Media Type", req); /* Good enough, or? */ + ast_set_flag(p, SIP_NEEDDESTROY); + return; + } if (get_msg_text(buf, sizeof(buf), req)) { ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid); + transmit_response(p, "202 Accepted", req); + ast_set_flag(p, SIP_NEEDDESTROY); return; } + if (p->owner) { if (sip_debug_test_pvt(p)) ast_verbose("Message received: '%s'\n", buf); @@ -6472,7 +6571,13 @@ f.data = buf; f.datalen = strlen(buf); ast_queue_frame(p->owner, &f); + transmit_response(p, "202 Accepted", req); /* We respond 202 accepted, since we relay the message */ + } else { /* Message outside of a call, we do not support that */ + ast_log(LOG_WARNING,"Received message to %s from %s, dropped it...\n Content-Type:%s\n Message: %s\n", get_header(req,"To"), get_header(req,"From"), content_type, buf); + transmit_response(p, "405 Method Not Allowed", req); /* Good enough, or? */ } + ast_set_flag(p, SIP_NEEDDESTROY); + return; } /*--- sip_show_inuse: CLI Command to show calls within limits set by @@ -7341,6 +7446,7 @@ static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions) { +#define FORMAT4 "%-15.15s %-10.10s %-21.21s %-15.15s %10s\n" #define FORMAT3 "%-15.15s %-10.10s %-21.21s %-15.15s\n" #define FORMAT2 "%-15.15s %-10.10s %-11.11s %-11.11s %s %s\n" #define FORMAT "%-15.15s %-10.10s %-11.11s %5.5d/%5.5d %-6.6s%s %s\n" @@ -7354,9 +7460,9 @@ if (!subscriptions) ast_cli(fd, FORMAT2, "Peer", "User/ANR", "Call ID", "Seq (Tx/Rx)", "Format", "Last Msg"); else - ast_cli(fd, FORMAT3, "Peer", "User", "Call ID", "URI"); + ast_cli(fd, FORMAT4, "Peer", "User", "Call ID", "Extension", "Last state"); while (cur) { - if (!cur->subscribed && !subscriptions) { + if (cur->subscribed == NONE && !subscriptions) { ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr), ast_strlen_zero(cur->username) ? ( ast_strlen_zero(cur->cid_num) ? "(None)" : cur->cid_num ) : cur->username, cur->callid, @@ -7366,23 +7472,24 @@ cur->lastmsg ); numchans++; } - if (cur->subscribed && subscriptions) { - ast_cli(fd, FORMAT3, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr), - ast_strlen_zero(cur->username) ? ( ast_strlen_zero(cur->cid_num) ? "(None)" : cur->cid_num ) : cur->username, - cur->callid, cur->uri); - - } + if (cur->subscribed != NONE && subscriptions) { + ast_cli(fd, FORMAT4, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr), + ast_strlen_zero(cur->username) ? ( ast_strlen_zero(cur->cid_num) ? "(None)" : cur->cid_num ) : cur->username, + cur->callid, cur->exten, ast_state2str(cur->laststate)); + numchans++; + } cur = cur->next; } ast_mutex_unlock(&iflock); if (!subscriptions) - ast_cli(fd, "%d active SIP channel(s)\n", numchans); + ast_cli(fd, "%d active SIP channel%s\n", numchans, (numchans != 1) ? "s" : ""); else - ast_cli(fd, "%d active SIP subscriptions(s)\n", numchans); + ast_cli(fd, "%d active SIP subscription%s\n", numchans, (numchans != 1) ? "s" : ""); return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 #undef FORMAT3 +#undef FORMAT4 } /*--- complete_sipch: Support routine for 'sip show channel' CLI ---*/ @@ -7540,7 +7647,7 @@ while(cur) { if (!strncasecmp(cur->callid, argv[3],len)) { ast_cli(fd,"\n"); - if (cur->subscribed) + if (cur->subscribed != NONE) ast_cli(fd, " * Subscription\n"); else ast_cli(fd, " * SIP Call\n"); @@ -7609,7 +7716,7 @@ while(cur) { if (!strncasecmp(cur->callid, argv[3],len)) { ast_cli(fd,"\n"); - if (cur->subscribed) + if (cur->subscribed != NONE) ast_cli(fd, " * Subscription\n"); else ast_cli(fd, " * SIP Call\n"); @@ -8597,7 +8704,7 @@ ast_log(LOG_WARNING, "Notify answer on an owned channel?\n"); ast_queue_hangup(p->owner); } else { - if (!p->subscribed) { + if (p->subscribed == NONE) { ast_set_flag(p, SIP_NEEDDESTROY); } } @@ -8627,6 +8734,7 @@ by sending CANCEL */ ast_set_flag(p, SIP_PENDINGBYE); p->authtries = 0; + ast_device_state_changed("SIP/%s", p->peername); /* If I understand this right, the branch is different for a non-200 ACK only */ transmit_request(p, SIP_ACK, seqno, 0, 1); check_pendings(p); @@ -9386,16 +9494,12 @@ /*--- handle_request_message: Handle incoming MESSAGE request ---*/ static int handle_request_message(struct sip_pvt *p, struct sip_request *req, int debug, int ignore) { - if (p->lastinvite) { - if (!ignore) { - if (debug) - ast_verbose("Receiving message!\n"); - receive_message(p, req); - } - transmit_response(p, "200 OK", req); + if (!ignore) { + if (debug) + ast_verbose("Receiving message!\n"); + receive_message(p, req); } else { - transmit_response(p, "405 Method Not Allowed", req); - ast_set_flag(p, SIP_NEEDDESTROY); + transmit_response(p, "202 Accepted", req); } return 1; } @@ -9431,7 +9535,9 @@ return 0; } /* Initialize the context if it hasn't been already */ - if (ast_strlen_zero(p->context)) + if (p->subscribecontext && !ast_strlen_zero(p->subscribecontext)) + ast_copy_string(p->context, p->subscribecontext, sizeof(p->context)); + else if (ast_strlen_zero(p->context)) strcpy(p->context, default_context); /* Get destination right away */ gotdest = get_destination(p, NULL); @@ -9440,15 +9546,34 @@ if (gotdest < 0) transmit_response(p, "404 Not Found", req); else - transmit_response(p, "484 Address Incomplete", req); + transmit_response(p, "484 Address Incomplete", req); /* Overlap dialing on SUBSCRIBE?? */ ast_set_flag(p, SIP_NEEDDESTROY); } else { + char *event = get_header(req, "Event"); /* Get Event package name */ + char *accept = get_header(req, "Accept"); + /* Initialize tag */ p->tag = rand(); - if (!strcmp(get_header(req, "Accept"), "application/dialog-info+xml")) - p->subscribed = 2; - else if (!strcmp(get_header(req, "Accept"), "application/simple-message-summary")) { - /* Looks like they actually want a mailbox */ + if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */ + /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */ + if (strstr(accept, "application/dialog-info+xml")) { + p->subscribed = DIALOG_INFO_XML; + /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ + /* Should not be used for SUBSCRIBE, but anyway */ + } else if (strstr(accept, "application/cpim-pidf+xml")) { + p->subscribed = CPIM_PIDF_XML; /* RFC 3863 format */ + } else if (strstr(accept, "application/pidf+xml")) { + p->subscribed = PIDF_XML; /* RFC 3863 format */ + } else if (strstr(accept, "application/xpidf+xml")) { + p->subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */ + } else { + /* Can't find a format for events that we know about */ + transmit_response(p, "489 Bad Event", req); + ast_set_flag(p, SIP_NEEDDESTROY); + return 0; + } + } else if (!strcmp(event, "message-summary") && !strcmp(accept, "application/simple-message-summary")) { + /* Looks like they actually want a mailbox status */ /* At this point, we should check if they subscribe to a mailbox that has the same extension as the peer or the mailbox id. If we configure @@ -9459,9 +9584,8 @@ Since we do not act on this subscribe anyway, we might as well accept any authenticated peer with a mailbox definition in their - config section. + config section. */ - */ if (!ast_strlen_zero(mailbox)) { found++; } @@ -9474,9 +9598,14 @@ ast_set_flag(p, SIP_NEEDDESTROY); } return 0; - } else - p->subscribed = 1; - if (p->subscribed) + } else { /* At this point, Asterisk does not understand the specified event */ + transmit_response(p, "489 Bad Event", req); + if (option_debug > 1) + ast_log(LOG_DEBUG, "Received SIP subscribe for unknown event package: %s\n", event); + ast_set_flag(p, SIP_NEEDDESTROY); + return 0; + } + if (p->subscribed != NONE) p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p); } } else @@ -9490,9 +9619,10 @@ ast_set_flag(p, SIP_NEEDDESTROY); return 0; } + /* TODO: Do we need this any longer? And what exactly to remove? */ /* The next line can be removed if the SNOM200 Expires bug is fixed */ - if (p->subscribed == 1) { - if (p->expiry>max_expiry) + if (p->subscribed == DIALOG_INFO_XML) { + if (p->expiry > max_expiry) p->expiry = max_expiry; } /* Go ahead and free RTP port */ @@ -9504,9 +9634,11 @@ ast_rtp_destroy(p->vrtp); p->vrtp = NULL; } + if (sipdebug || option_debug > 1) + ast_log(LOG_DEBUG, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username); transmit_response(p, "200 OK", req); - sip_scheddestroy(p, (p->expiry+10)*1000); - transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); + sip_scheddestroy(p, (p->expiry + 10) * 1000); /* Set timer for destruction of call at expiration */ + transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); /* Send first notification */ } return 1; } @@ -10109,13 +10241,27 @@ if (p) { found++; res = AST_DEVICE_UNAVAILABLE; + if (option_debug > 2) + ast_log(LOG_DEBUG, "Checking device state for peer %s\n", dest); 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; + /* Peer is registred, or has default IP address and a valid registration */ + /* Now check if we know anything about the state. The only way is by implementing + * call control with incominglimit=X in sip.conf where X > 0 + * Check if the device has incominglimit, and if qualify=on, if the device + * is reachable */ + if (p->incominglimit && (p->lastms == 0 || p->lastms <= p->maxms)) { /* Free for a call */ + res = AST_DEVICE_NOT_INUSE; + if (p->inUse) /* On a call */ + res = AST_DEVICE_BUSY; + } else { /* peer found and valid, state unknown */ + res = AST_DEVICE_UNKNOWN; + } } } if (!p && !found) { + if (option_debug > 2) + ast_log(LOG_DEBUG, "Checking device state for DNS host %s\n", dest); hp = ast_gethostbyname(host, &ahp); if (hp) res = AST_DEVICE_UNKNOWN; @@ -10445,6 +10591,8 @@ if (!strcasecmp(v->name, "context")) { ast_copy_string(user->context, v->value, sizeof(user->context)); + } else if (!strcasecmp(v->name, "subscribecontext")) { + ast_copy_string(user->subscribecontext, v->value, sizeof(user->subscribecontext)); } else if (!strcasecmp(v->name, "setvar")) { varname = ast_strdupa(v->value); if (varname && (varval = strchr(varname,'='))) { @@ -10529,6 +10677,7 @@ SIP_DTMF | SIP_NAT | SIP_REINVITE | SIP_INSECURE_PORT | SIP_INSECURE_INVITE | SIP_PROG_INBAND | SIP_OSPAUTH); strcpy(peer->context, default_context); + strcpy(peer->subscribecontext, default_subscribecontext); strcpy(peer->language, default_language); strcpy(peer->musicclass, global_musicclass); peer->addr.sin_port = htons(DEFAULT_SIP_PORT); @@ -10570,9 +10719,9 @@ /* Already in the list, remove it and it will be added back (or FREE'd) */ found++; } else { - peer = malloc(sizeof(struct sip_peer)); + peer = malloc(sizeof(*peer)); if (peer) { - memset(peer, 0, sizeof(struct sip_peer)); + memset(peer, 0, sizeof(*peer)); if (realtime) rpeerobjs++; else @@ -10603,6 +10752,7 @@ peer->chanvars = NULL; } strcpy(peer->context, default_context); + strcpy(peer->subscribecontext, default_subscribecontext); strcpy(peer->language, default_language); strcpy(peer->musicclass, global_musicclass); ast_copy_flags(peer, &global_flags, SIP_USEREQPHONE); @@ -10652,6 +10802,8 @@ ast_callerid_split(v->value, peer->cid_name, sizeof(peer->cid_name), peer->cid_num, sizeof(peer->cid_num)); } else if (!strcasecmp(v->name, "context")) { ast_copy_string(peer->context, v->value, sizeof(peer->context)); + } else if (!strcasecmp(v->name, "subscribecontext")) { + ast_copy_string(peer->subscribecontext, v->value, sizeof(peer->subscribecontext)); } else if (!strcasecmp(v->name, "fromdomain")) ast_copy_string(peer->fromdomain, v->value, sizeof(peer->fromdomain)); else if (!strcasecmp(v->name, "usereqphone")) @@ -10845,6 +10997,7 @@ /* Initialize some reasonable defaults at SIP reload */ ast_copy_string(default_context, DEFAULT_CONTEXT, sizeof(default_context)); + default_subscribecontext[0] = '\0'; default_language[0] = '\0'; default_fromdomain[0] = '\0'; default_qualify = 0; @@ -10854,6 +11007,7 @@ sipdebug = 0; ast_copy_string(default_useragent, DEFAULT_USERAGENT, sizeof(default_useragent)); ast_copy_string(default_notifymime, DEFAULT_NOTIFYMIME, sizeof(default_notifymime)); + global_notifyringing = 1; ast_copy_string(global_realm, DEFAULT_REALM, sizeof(global_realm)); ast_copy_string(global_musicclass, "default", sizeof(global_musicclass)); ast_copy_string(default_callerid, DEFAULT_CALLERID, sizeof(default_callerid)); @@ -10943,6 +11097,8 @@ compactheaders = ast_true(v->value); } else if (!strcasecmp(v->name, "notifymimetype")) { ast_copy_string(default_notifymime, v->value, sizeof(default_notifymime)); + } else if (!strcasecmp(v->name, "notifyringing")) { + global_notifyringing = ast_true(v->value); } else if (!strcasecmp(v->name, "musicclass") || !strcasecmp(v->name, "musiconhold")) { ast_copy_string(global_musicclass, v->value, sizeof(global_musicclass)); } else if (!strcasecmp(v->name, "language")) { Index: configs/sip.conf.sample =================================================================== RCS file: /usr/cvsroot/asterisk/configs/sip.conf.sample,v retrieving revision 1.64 diff -u -r1.64 sip.conf.sample --- configs/sip.conf.sample 9 Jun 2005 21:11:30 -0000 1.64 +++ configs/sip.conf.sample 3 Aug 2005 08:38:34 -0000 @@ -48,6 +48,9 @@ ;maxexpirey=3600 ; Max length of incoming registration we allow ;defaultexpirey=120 ; Default length of incoming/outoing registration ;notifymimetype=text/plain ; Allow overriding of mime type in MWI NOTIFY +;notifyringing=no ; Sent ringing (yes) or inuse (no) as state in + ; NOTIFY messages when observed extension is + ; in state RINGING_AND_INUSE (defaults to "yes") ;checkmwi=10 ; Default time between mailbox checks for peers ;videosupport=yes ; Turn on support for SIP video ;recordhistory=yes ; Record SIP history by default Index: include/asterisk/pbx.h =================================================================== RCS file: /usr/cvsroot/asterisk/include/asterisk/pbx.h,v retrieving revision 1.49 diff -u -r1.49 pbx.h --- include/asterisk/pbx.h 15 Jul 2005 23:24:51 -0000 1.49 +++ include/asterisk/pbx.h 3 Aug 2005 08:38:38 -0000 @@ -34,14 +34,16 @@ #define PRIORITY_HINT -1 /*! Extension states */ -/*! No device INUSE or BUSY */ -#define AST_EXTENSION_NOT_INUSE 0 +/*! No device INUSE, BUSY, UNAVAILABLE or RINGING */ +#define AST_EXTENSION_NOT_INUSE 0 /*! One or more devices INUSE */ -#define AST_EXTENSION_INUSE 1 +#define AST_EXTENSION_INUSE (1 << 0) /*! All devices BUSY */ -#define AST_EXTENSION_BUSY 2 +#define AST_EXTENSION_BUSY (1 << 1) /*! All devices UNAVAILABLE/UNREGISTERED */ -#define AST_EXTENSION_UNAVAILABLE 3 +#define AST_EXTENSION_UNAVAILABLE (1 << 2) +/*! One or more devices are RINGING */ +#define AST_EXTENSION_RINGING (1 << 3) struct ast_context; struct ast_exten;