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 25 Jul 2005 18:52:34 -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; @@ -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_RINGING_AND_INUSE; + 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.787 diff -u -r1.787 chan_sip.c --- channels/chan_sip.c 25 Jul 2005 17:39:48 -0000 1.787 +++ channels/chan_sip.c 25 Jul 2005 18:52:43 -0000 @@ -139,6 +139,11 @@ #define SIP_PUBLISH 14 #define SIP_RESPONSE 100 +#define SUBSCRIBE_XPIDF_XML 1 +#define SUBSCRIBE_DIALOG_INFO_XML 2 +#define SUBSCRIBE_CPIM_PIDF_XML 3 +#define SUBSCRIBE_PIDF_XML 4 + #define RTP 1 #define NO_RTP 0 const struct cfsip_methods { @@ -263,6 +268,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] = ""; @@ -274,6 +280,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 */ @@ -517,6 +524,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 */ @@ -565,6 +573,7 @@ int subscribed; /* Is this call a subscription? */ int stateid; + int laststate; /* Last known state */ int dialogver; struct ast_dsp *vad; /* Voice Activation Detection dsp */ @@ -601,6 +610,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 */ @@ -632,6 +642,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) */ @@ -3797,7 +3808,12 @@ add_header(req, "From", ot); add_header(req, "To", of); } - add_header(req, "Contact", p->our_contact); + if (sipmethod == SIP_MESSAGE) { + /* Add date header to MESSAGE */ + append_date(req); + } else { + add_header(req, "Contact", p->our_contact); + } copy_header(req, orig, "Call-ID"); add_header(req, "CSeq", tmp); @@ -4465,101 +4481,144 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full) { char tmp[4000]; - int maxbytes = 0; - int bytes = 0; + char *t = tmp; + int maxbytes = sizeof(tmp); char from[256], to[256]; - char *t, *c, *a; - char *mfrom, *mto; + char *mfrom, *mto, *c, *a; struct sip_request req; char clen[20]; + char hint[AST_MAX_EXTENSION]; + char *statestring = "terminated"; + enum state { + NOTIFY_OPEN = 0, + NOTIFY_INUSE = 1, + NOTIFY_CLOSED = 2, + } local_state = NOTIFY_OPEN; + char *pidfstate = "--"; + char *pidfnote= "Ready"; + + switch (state) { + case AST_EXTENSION_RINGING_AND_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 = 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, ';'))) { + 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 = 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; - 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; + reqprep(&req, p, SIP_NOTIFY, 0, 1); + switch (p->subscribed) { + case SUBSCRIBE_XPIDF_XML: + case SUBSCRIBE_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; + 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", (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"); + break; + case SUBSCRIBE_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 - state = 0; - - t = tmp; - maxbytes = sizeof(tmp); - bytes = snprintf(t, maxbytes, "\n"); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "\n"); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "\n"); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "\n", mfrom); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "\n", p->exten); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "
\n", mto); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "\n", !state ? "open" : (state==1) ? "inuse" : "closed"); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "\n", !state ? "online" : (state==1) ? "onthephone" : "offline"); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "
\n
\n
\n"); - } else { + ast_build_string(&t, &maxbytes, "%s\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed"); + ast_build_string(&t, &maxbytes, "
\n
\n"); + break; + case SUBSCRIBE_DIALOG_INFO_XML: /* SNOM subscribes in this format */ add_header(&req, "Event", "dialog"); add_header(&req, "Content-Type", "application/dialog-info+xml"); - - t = tmp; - maxbytes = sizeof(tmp); - bytes = snprintf(t, maxbytes, "\n"); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "\n", p->dialogver++, full ? "full":"partial", mfrom); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "\n", p->exten); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "%s\n", state ? "confirmed" : "terminated"); - t += bytes; - maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "\n\n"); + ast_build_string(&t, &maxbytes, "\n"); + ast_build_string(&t, &maxbytes, "\n", p->dialogver++, full ? "full":"partial", mto); + if (state == AST_EXTENSION_RINGING || (state==AST_EXTENSION_RINGING_AND_INUSE && 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"); } + if (t > tmp + sizeof(tmp)) ast_log(LOG_WARNING, "Buffer overflow detected!! (Please file a bug report)\n"); @@ -5474,7 +5533,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. */ @@ -5658,10 +5717,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; @@ -5669,6 +5731,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); @@ -6297,6 +6360,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)); @@ -6359,6 +6423,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; @@ -6465,16 +6530,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); @@ -6485,7 +6562,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 @@ -7354,6 +7437,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" @@ -7367,7 +7451,7 @@ 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) { ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr), @@ -7380,22 +7464,23 @@ 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); - - } + 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 subscriptions%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 ---*/ @@ -8617,6 +8702,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); @@ -9377,16 +9463,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; } @@ -9422,7 +9504,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); @@ -9431,15 +9515,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 = SUBSCRIBE_DIALOG_INFO_XML; + /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ + /* Should not be used for SUBSCRIBE, but anyway */ + else if (!p->subscribed && strstr(accept, "application/cpim-pidf+xml")) + p->subscribed = SUBSCRIBE_CPIM_PIDF_XML; /* RFC 3863 format */ + else if (!p->subscribed && strstr(accept, "application/pidf+xml")) + p->subscribed = SUBSCRIBE_PIDF_XML; /* RFC 3863 format */ + else if (!p->subscribed && strstr(accept, "application/xpidf+xml")) + p->subscribed = SUBSCRIBE_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 @@ -9450,9 +9553,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++; } @@ -9465,8 +9567,13 @@ ast_set_flag(p, SIP_NEEDDESTROY); } return 0; - } else - p->subscribed = 1; + } 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) p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p); } @@ -9503,9 +9610,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; } @@ -10117,13 +10226,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; @@ -10453,6 +10576,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,'='))) { @@ -10537,6 +10662,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); @@ -10578,9 +10704,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 @@ -10611,6 +10737,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); @@ -10660,6 +10787,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")) @@ -10853,6 +10982,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; @@ -10862,6 +10992,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)); @@ -10951,6 +11082,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 25 Jul 2005 18:52:44 -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 25 Jul 2005 18:52:53 -0000 @@ -42,6 +42,10 @@ #define AST_EXTENSION_BUSY 2 /*! All devices UNAVAILABLE/UNREGISTERED */ #define AST_EXTENSION_UNAVAILABLE 3 +/*! One or more devices are RINGING and none are INUSE */ +#define AST_EXTENSION_RINGING 4 +/*! One or more devices are RINGING and one or more are INUSE */ +#define AST_EXTENSION_RINGING_AND_INUSE 5 struct ast_context; struct ast_exten;