Index: pbx.c =================================================================== RCS file: /usr/cvsroot/asterisk/pbx.c,v retrieving revision 1.266 diff -u -r1.266 pbx.c --- pbx.c 22 Aug 2005 18:47:19 -0000 1.266 +++ pbx.c 24 Aug 2005 20:11:29 -0000 @@ -1738,7 +1738,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; @@ -1749,7 +1749,7 @@ do { rest = strchr(cur, '&'); if (rest) { - *rest = 0; + *rest = 0; rest++; } @@ -1760,7 +1760,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; @@ -1779,7 +1787,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; @@ -2428,6 +2442,12 @@ if (option_maxcalls) { if (countcalls >= option_maxcalls) { ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name); + manager_event(EVENT_FLAG_CALL, "MaxcallsExceeded", + "Callcount: %d\r\n" + "Channel: %s\r\n", + countcalls, + c->name); + res = -1; } } Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.814 diff -u -r1.814 chan_sip.c --- channels/chan_sip.c 23 Aug 2005 17:59:26 -0000 1.814 +++ channels/chan_sip.c 24 Aug 2005 20:11:32 -0000 @@ -135,6 +135,28 @@ bad things will happen. */ +enum subscriptiontype { + NONE, + XPIDF_XML, + DIALOG_INFO_XML, + CPIM_PIDF_XML, + PIDF_XML +}; + +static const struct cfsub_types { + enum subscriptiontype type; + const char *event; + const char *mediatype; + const char *text; +} sub_types[] = { + { NONE, "-", "Unknown", "Unknown" }, + /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ + { DIALOG_INFO_XML, "dialog", "application/dialog-info+xml", "Dialog-info+xml" }, + { CPIM_PIDF_XML, "presence", "application/cpim-pidf+xml", "cpim-pidf+xml" }, /* RFC 3863 */ + { PIDF_XML, "presence", "application/pidf+xml", "pidf+xml" }, /* RFC 3863 */ + { XPIDF_XML, "presence", "application/xpidf+xml", "xpidf+xml" } /* Pre-RFC 3863 with MS additions */ +}; + enum sipmethod { SIP_UNKNOWN, SIP_RESPONSE, @@ -269,7 +291,7 @@ /* SIP Methods we support */ -#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY" +#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY" /* SIP Extensions we support */ #define SUPPORTED_EXTENSIONS "replaces" @@ -281,6 +303,7 @@ #define DEFAULT_CONTEXT "default" static char default_context[AST_MAX_CONTEXT] = DEFAULT_CONTEXT; +static char default_subscribecontext[AST_MAX_CONTEXT]; #define DEFAULT_VMEXTEN "asterisk" static char global_vmexten[AST_MAX_EXTENSION] = DEFAULT_VMEXTEN; @@ -295,6 +318,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 */ @@ -541,6 +565,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 */ @@ -586,9 +611,9 @@ int rtptimeout; /* RTP timeout time */ int rtpholdtimeout; /* RTP timeout when on hold */ int rtpkeepalive; /* Send RTP packets for keepalive */ - - int subscribed; /* Is this call a subscription? */ + enum subscriptiontype subscribed; /* Is this call a subscription? */ int stateid; + int laststate; /* Last known state */ int dialogver; struct ast_dsp *vad; /* Voice Activation Detection dsp */ @@ -628,6 +653,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 */ @@ -659,6 +685,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) */ @@ -833,6 +860,7 @@ static void append_date(struct sip_request *req); /* Append date to SIP packet */ static int determine_firstline_parts(struct sip_request *req); static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to LOG_DEBUG at end of dialog, before destroying data */ +static struct cfsub_types *find_sub_type(enum subscriptiontype subtype); /* Definition of this channel for channel registration */ static const struct ast_channel_tech sip_tech = { @@ -2898,6 +2926,7 @@ p->method = intended_method; p->initid = -1; p->autokillid = -1; + p->subscribed = NONE; p->stateid = -1; p->prefs = prefs; #ifdef OSP_SUPPORT @@ -3800,7 +3829,7 @@ snprintf(tmp, sizeof(tmp), "%d", p->expiry); add_header(resp, "Expires", tmp); add_header(resp, "Contact", contact); - } else { + } else if (p->our_contact[0]) { add_header(resp, "Contact", p->our_contact); } if (p->maxforwards) { @@ -3811,7 +3840,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; @@ -3898,6 +3927,7 @@ return 0; } +/*--- __transmit_response: Base transmit response function */ static int __transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req, int reliable) { struct sip_request resp; @@ -4555,65 +4585,137 @@ char *t = tmp, *c, *a, *mfrom, *mto; size_t maxbytes = sizeof(tmp); struct sip_request req; + char hint[AST_MAX_EXTENSION]; + char *statestring = "terminated"; + struct cfsub_types *subscriptiontype; + 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; + } + + subscriptiontype = find_sub_type(p->subscribed); + + /* 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)); - - 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; + 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; - 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; + reqprep(&req, p, SIP_NOTIFY, 0, 1); + + add_header(&req, "Event", subscriptiontype->event); + add_header(&req, "Content-Type", subscriptiontype->mediatype); + add_header(&req, "Subscription-State", "active"); + switch (p->subscribed) { + case XPIDF_XML: + case CPIM_PIDF_XML: 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 { - add_header(&req, "Event", "dialog"); - add_header(&req, "Content-Type", "application/dialog-info+xml"); + break; + case PIDF_XML: /* Eyebeam supports this format */ + 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 */ 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"); @@ -5537,7 +5639,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. */ @@ -5721,10 +5823,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; @@ -5732,6 +5837,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); @@ -6362,6 +6468,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)); @@ -6424,6 +6531,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; @@ -6530,16 +6638,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); @@ -6550,7 +6670,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 @@ -7468,6 +7594,7 @@ ast_cli(fd, " Reg. default duration: %d secs\n", default_expiry); ast_cli(fd, " Outbound reg. timeout: %d secs\n", global_reg_timeout); ast_cli(fd, " Outbound reg. attempts: %d\n", global_regattempts_max); + ast_cli(fd, " Notify ringing state: %s\n", global_notifyringing ? "Yes" : "No"); ast_cli(fd, "\nDefault Settings:\n"); ast_cli(fd, "-----------------\n"); ast_cli(fd, " Context: %s\n", default_context); @@ -7495,6 +7622,34 @@ return RESULT_SUCCESS; } +/*--- sub_type2str: Show subscription type in string format */ +static char *sub_type2str(enum subscriptiontype subtype) { + int i; + char *res = NULL; + + for (i = 1; (i < (sizeof(sub_types) / sizeof(sub_types[0]))) && !res; i++) { + if (sub_types[i].type == subtype) { + res = (char (*))sub_types[i].text; + break; + } + } + return res; +} + +/*--- find_sub_type: Find subscription type in array */ +static struct cfsub_types *find_sub_type(enum subscriptiontype subtype) { + int i; + struct cfsub_types *res = NULL; + + for (i = 1; (i < (sizeof(sub_types) / sizeof(sub_types[0]))) && !res; i++) { + if (sub_types[i].type == subtype) { + res = (struct cfsub_types (*))&sub_types[i]; + break; + } + } + return res; +} + /* Forward declaration */ static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions); @@ -7512,7 +7667,7 @@ static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions) { -#define FORMAT3 "%-15.15s %-10.10s %-21.21s %-15.15s\n" +#define FORMAT3 "%-15.15s %-10.10s %-21.21s %-15.15s %10s (%-10.10s)\n" #define FORMAT2 "%-15.15s %-10.10s %-11.11s %-11.11s %-4.4s %-7.7s %s \n" #define FORMAT "%-15.15s %-10.10s %-11.11s %5.5d/%5.5d %-4.4s %-7.7s%s %s\n" struct sip_pvt *cur; @@ -7525,9 +7680,9 @@ if (!subscriptions) ast_cli(fd, FORMAT2, "Peer", "User/ANR", "Call ID", "Seq (Tx/Rx)", "Format", "Hold", "Last Msg"); else - ast_cli(fd, FORMAT3, "Peer", "User", "Call ID", "URI"); + ast_cli(fd, FORMAT3, "Peer", "User", "Call ID", "Extension", "Last state", "Type"); 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, @@ -7538,19 +7693,20 @@ 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, 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->exten, ast_state2str(cur->laststate), + sub_type2str(cur->subscribed)); + 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 @@ -7712,7 +7868,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"); @@ -7782,7 +7938,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"); @@ -8876,7 +9032,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); } } @@ -8898,6 +9054,7 @@ time(&p->ospstart); #endif ast_queue_control(p->owner, AST_CONTROL_ANSWER); + ast_setstate(p->owner, AST_STATE_UP); } else { struct ast_frame af = { AST_FRAME_NULL, }; ast_queue_frame(p->owner, &af); @@ -8906,6 +9063,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); @@ -9668,16 +9826,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; } @@ -9688,6 +9842,20 @@ int res = 0; struct ast_channel *c=NULL; + if (p->initreq.headers) { + /* We already have a dialog */ + if (p->initreq.method != SIP_SUBSCRIBE) { + /* This is a SUBSCRIBE within another SIP dialog, which we do not support */ + /* For transfers, this could happen, but since we haven't seen it happening, let us just refuse this */ + transmit_response(p, "403 Forbidden (within dialog)", req); + /* Do not destroy session, since we will break the call if we do */ + ast_log(LOG_DEBUG, "Got a subscription within the context of another call, can't handle that - %s (Method %s)\n", p->callid, sip_methods[p->initreq.method].text); + return 0; + } else { + if (debug) + ast_log(LOG_DEBUG, "Got a re-subscribe on existing subscription %s\n", p->callid); + } + } if (!ignore) { /* Use this as the basis */ if (debug) @@ -9713,7 +9881,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); @@ -9722,15 +9892,38 @@ 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/pidf+xml")) { + p->subscribed = PIDF_XML; /* RFC 3863 format */ + } else 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/xpidf+xml")) { + p->subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */ + } else if (strstr(p->useragent, "Polycom")) { + p->subscribed = XPIDF_XML; /* Polycoms subscribe for "event: dialog" but don't include an "accept:" header */ + } 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 @@ -9752,13 +9945,18 @@ transmit_response(p, "200 OK", req); ast_set_flag(p, SIP_NEEDDESTROY); } else { - transmit_response(p, "403 Forbidden", req); + transmit_response(p, "404 Not found", req); 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 @@ -9772,23 +9970,17 @@ 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 */ - if (p->rtp && !p->owner) { - ast_rtp_destroy(p->rtp); - p->rtp = NULL; - } - if (p->vrtp && !p->owner) { - 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; } @@ -10392,13 +10584,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; @@ -10728,6 +10934,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,'='))) { @@ -10812,6 +11020,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); @@ -10853,9 +11062,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 @@ -10886,6 +11095,7 @@ peer->chanvars = NULL; } strcpy(peer->context, default_context); + strcpy(peer->subscribecontext, default_subscribecontext); strcpy(peer->vmexten, global_vmexten); strcpy(peer->language, default_language); strcpy(peer->musicclass, global_musicclass); @@ -10936,6 +11146,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")) @@ -11131,6 +11343,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; @@ -11139,6 +11352,7 @@ externrefresh = 10; 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)); @@ -11233,6 +11447,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.65 diff -u -r1.65 sip.conf.sample --- configs/sip.conf.sample 23 Aug 2005 00:50:38 -0000 1.65 +++ configs/sip.conf.sample 24 Aug 2005 20:11:35 -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 ;vmexten=voicemail ; dialplan extension to reach mailbox sets the ; Message-Account in the MWI notify message @@ -208,6 +211,7 @@ ; User config options: Peer configuration: ; -------------------- ------------------- ; context context +; subscribecontext subscribecontext ; permit permit ; deny deny ; secret secret @@ -227,10 +231,10 @@ ; useclientcode useclientcode ; accountcode accountcode ; setvar setvar -; callerid callerid -; amaflags amaflags -; incominglimit incominglimit -; restrictcid restrictcid +; callerid callerid +; amaflags amaflags +; incominglimit incominglimit +; restrictcid restrictcid ; mailbox ; username ; template @@ -314,6 +318,7 @@ ;[snom] ;type=friend ; Friends place calls and receive calls ;context=from-sip ; Context for incoming calls from this user +;subscribecontext=mygroup ; context for subscriptions (hints) ;secret=blah ;language=de ; Use German prompts for this user ;host=dynamic ; This peer register with us 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 24 Aug 2005 20:11:47 -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 none are INUSE */ +#define AST_EXTENSION_RINGING (1 << 3) struct ast_context; struct ast_exten;