Index: pbx.c =================================================================== RCS file: /usr/cvsroot/asterisk/pbx.c,v retrieving revision 1.256 diff -u -r1.256 pbx.c --- pbx.c 20 Jun 2005 23:37:01 -0000 1.256 +++ pbx.c 23 Jun 2005 10:28:22 -0000 @@ -1740,6 +1740,8 @@ int res = -1; int allunavailable = 1, allbusy = 1, allfree = 1; int busy = 0; + int inuse = 0; + int ring = 0; if (!e) return -1; @@ -1761,7 +1763,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; @@ -1780,7 +1790,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.770 diff -u -r1.770 chan_sip.c --- channels/chan_sip.c 22 Jun 2005 21:10:02 -0000 1.770 +++ channels/chan_sip.c 23 Jun 2005 10:28:23 -0000 @@ -104,8 +104,6 @@ #define CALLERID_UNKNOWN "Unknown" - - #define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */ #define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */ #define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */ @@ -113,7 +111,6 @@ #define DEFAULT_RETRANS 2000 /* How frequently to retransmit */ #define MAX_RETRANS 5 /* Try only 5 times for retransmissions */ - #define DEBUG_READ 0 /* Recieved data */ #define DEBUG_SEND 1 /* Transmit data */ @@ -138,6 +135,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 { @@ -262,17 +264,23 @@ #define DEFAULT_CONTEXT "default" static char default_context[AST_MAX_EXTENSION] = DEFAULT_CONTEXT; +static char default_subscribecontext[AST_MAX_EXTENSION]; +static char regcontext[AST_MAX_EXTENSION] = ""; /* Context for auto-extensions */ static char default_language[MAX_LANGUAGE] = ""; #define DEFAULT_CALLERID "asterisk" static char default_callerid[AST_MAX_EXTENSION] = DEFAULT_CALLERID; +#define DEFAULT_VMEXTEN DEFAULT_CALLERID +static char global_vmexten[AST_MAX_EXTENSION] = DEFAULT_VMEXTEN; + static char default_fromdomain[AST_MAX_EXTENSION] = ""; #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 */ @@ -350,7 +358,6 @@ static char global_musicclass[MAX_LANGUAGE] = ""; /* Global music on hold class */ #define DEFAULT_REALM "asterisk" static char global_realm[MAXHOSTNAMELEN] = DEFAULT_REALM; /* Default realm */ -static char regcontext[AST_MAX_EXTENSION] = ""; /* Context for auto-extensions */ /* Expire slowly */ #define DEFAULT_EXPIRY 900 @@ -516,6 +523,7 @@ char from[256]; /* The From: header */ char useragent[256]; /* User agent in SIP request */ char context[AST_MAX_EXTENSION]; /* Context for this call */ + char subscribecontext[AST_MAX_EXTENSION]; /* 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 */ @@ -563,7 +571,8 @@ int rtpkeepalive; /* Send RTP packets for keepalive */ int subscribed; /* Is this call a subscription? */ - int stateid; + int stateid; + int laststate; /* Last known state */ int dialogver; struct ast_dsp *vad; /* Voice Activation Detection dsp */ @@ -600,6 +609,7 @@ char secret[80]; /* Password */ char md5secret[80]; /* Password in md5 */ char context[AST_MAX_EXTENSION]; /* Default context for incoming calls */ + char subscribecontext[AST_MAX_EXTENSION]; /* 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 */ @@ -631,6 +641,7 @@ char md5secret[80]; /* Password in MD5 */ struct sip_auth *auth; /* Realm authentication list */ char context[AST_MAX_EXTENSION]; /* Default context for incoming calls */ + char subscribecontext[AST_MAX_EXTENSION]; /* 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) */ @@ -646,6 +657,7 @@ int incominglimit; /* Limit of incoming calls */ int outUse; /* disabled */ int outgoinglimit; /* disabled */ + char vmexten[AST_MAX_EXTENSION]; /* Dialplan extension for MWI notify message*/ char mailbox[AST_MAX_EXTENSION]; /* Mailbox setting for MWI checks */ char language[MAX_LANGUAGE]; /* Default language for prompts */ char musicclass[MAX_LANGUAGE]; /* Music on Hold class */ @@ -1604,8 +1616,8 @@ if (ast_test_flag((&global_flags_page2), SIP_PAGE2_RTCACHEFRIENDS)) { ast_set_flag((&user->flags_page2), SIP_PAGE2_RTCACHEFRIENDS); suserobjs++; - ASTOBJ_CONTAINER_LINK(&userl,user); - } else { + ASTOBJ_CONTAINER_LINK(&userl,user); + } else { /* Move counter from s to r... */ suserobjs--; ruserobjs++; @@ -2840,11 +2852,11 @@ ast_copy_string(p->callid, callid, sizeof(p->callid)); ast_copy_flags(p, (&global_flags), SIP_PROMISCREDIR | SIP_TRUSTRPID | SIP_DTMF | SIP_REINVITE | SIP_PROG_INBAND | SIP_OSPAUTH); /* Assign default music on hold class */ - strcpy(p->musicclass, global_musicclass); + ast_copy_string(p->musicclass, global_musicclass, sizeof(p->musicclass)); p->capability = global_capability; if (ast_test_flag(p, SIP_DTMF) == SIP_DTMF_RFC2833) p->noncodeccapability |= AST_RTP_DTMF; - strcpy(p->context, default_context); + ast_copy_string(p->context, default_context, sizeof(p->context)); /* Add to active dialog list */ ast_mutex_lock(&iflock); @@ -3697,7 +3709,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; @@ -3711,7 +3723,7 @@ snprintf(p->lastmsg, sizeof(p->lastmsg), "Tx: %s", sip_methods[sipmethod].text); - if (!seqno) { + if (!seqno) { /* Raise our CSEQ */ p->ocseq++; seqno = p->ocseq; } @@ -3724,7 +3736,7 @@ c = p->initreq.rlPart2; /* Use original URI */ } else if (sipmethod == SIP_ACK) { /* Use URI from Contact: in 200 OK (if INVITE) - (we only have the contacturi on INVITEs) */ + (we only have the contacturi on INVITEs) */ if (!ast_strlen_zero(p->okcontacturi)) c = p->okcontacturi; else @@ -3776,7 +3788,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); @@ -4242,31 +4259,25 @@ char urioptions[256]=""; if (ast_test_flag(p, SIP_USEREQPHONE)) { - char onlydigits = 1; - x=0; - - /* Test p->username against allowed characters in AST_DIGIT_ANY - If it matches the allowed characters list, then sipuser = ";user=phone" - - If not, then sipuser = "" - */ - /* + is allowed in first position in a tel: uri */ - if (p->username && p->username[0] == '+') - x=1; - - for (; xusername); x++) { - if (!strchr(AST_DIGIT_ANYNUM, p->username[x])) { - onlydigits = 0; - break; - } - } - - /* If we have only digits, add ;user=phone to the uri */ - if (onlydigits) - strcpy(urioptions, ";user=phone"); + char onlydigits = 1; + x=0; + /* Test p->username against allowed characters in AST_DIGIT_ANY + If it matches the allowed characters list, then sipuser = ";user=phone" + If not, then sipuser = "" + + is allowed in first position in a tel: uri */ + if (p->username && p->username[0] == '+') + x=1; + for (; xusername); x++) { + if (!strchr(AST_DIGIT_ANYNUM, p->username[x])) { + onlydigits = 0; + break; + } + } + /* If we have only digits, add ;user=phone to the uri */ + if (onlydigits) + ast_copy_string(urioptions, ";user=phone", sizeof(urioptions)); } - snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text); if (p->owner) { @@ -4274,7 +4285,7 @@ n = p->owner->cid.cid_name; } if (!l || (!ast_isphonenumber(l) && default_callerid[0])) - l = default_callerid; + l = default_callerid; /* if user want's his callerid restricted */ if ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) { l = CALLERID_UNKNOWN; @@ -4441,101 +4452,146 @@ 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]; + int hintstate = 1; + char *statestring; + char *pidfstate; + char *pidfnote; + int local_state = 0; + + /* Check which device/devices we are watching and if they are registred */ + /* If they are not registred, we will override notification and show no availability */ + if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten)) { + if(ast_device_state(hint) == AST_DEVICE_UNAVAILABLE) + hintstate = 0; /* Not registred */ + } + pidfnote = "Ready"; + statestring = "terminated"; + pidfstate = "--"; + + switch(state) { + case AST_EXTENSION_RINGING_AND_INUSE: + if (global_notifyringing) + statestring = "early"; + else + statestring = "confirmed"; + local_state = 1; /* In use */ + pidfstate = "busy"; + pidfnote = "Ringing"; + break; + case AST_EXTENSION_RINGING: + statestring = "early"; + local_state = 1; /* In use */ + pidfstate = "busy"; + pidfnote = "Ringing"; + break; + case AST_EXTENSION_INUSE: + statestring = "confirmed"; + local_state = 1; /* In use */ + pidfstate = "busy"; + pidfnote = "On the phone"; + break; + case AST_EXTENSION_BUSY: + statestring = "confirmed"; + local_state = 2; /* Closed */ + pidfstate = "busy"; + pidfnote = "On the phone"; + break; + case AST_EXTENSION_UNAVAILABLE: + statestring = "confirmed"; + local_state = 2; /* Closed */ + pidfstate = "away"; + pidfnote = "Unavailable"; + break; + case AST_EXTENSION_NOT_INUSE: + default: + /* Default setting */ + break; + } + if (!hintstate) { /* Not registred, override */ + local_state = 2; /* 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)); - - 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; + 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; + 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 ? "open" : (local_state==1) ? "inuse" : "closed"); + ast_build_string(&t, &maxbytes, "\n", !local_state ? "online" : (local_state==1) ? "onthephone" : "offline"); + ast_build_string(&t, &maxbytes, "
\n
\n
\n"); + break; + case SUBSCRIBE_PIDF_XML: /* 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 != 2 ? "open" : "closed"); + ast_build_string(&t, &maxbytes, "
\n
\n"); + break; + case SUBSCRIBE_DIALOG_INFO_XML: /* SNOM 360 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"); @@ -4552,25 +4608,30 @@ * We use the SIP Event package message-summary * MIME type defaults to "application/simple-message-summary"; */ -static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs) +static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, char *vmexten) { struct sip_request req; - char tmp[256]; - char tmp2[256]; + char tmp[1000]; + char *t = tmp; + int maxbytes = sizeof(tmp); char clen[20]; + initreqprep(&req, p, SIP_NOTIFY, NULL); add_header(&req, "Event", "message-summary"); add_header(&req, "Content-Type", default_notifymime); - snprintf(tmp, sizeof(tmp), "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no"); - snprintf(tmp2, sizeof(tmp2), "Voice-Message: %d/%d\r\n", newmsgs, oldmsgs); - snprintf(clen, sizeof(clen), "%d", (int)(strlen(tmp) + strlen(tmp2))); + ast_build_string(&t, &maxbytes, "Message-Account: sip:%s@%s\r\n", (vmexten && !ast_strlen_zero(vmexten)) ? vmexten : global_vmexten, p->fromdomain); + ast_build_string(&t, &maxbytes, "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no"); + ast_build_string(&t, &maxbytes, "Voice-Message: %d/%d\r\n", newmsgs, oldmsgs); + + if (t > tmp + sizeof(tmp)) + ast_log(LOG_WARNING, "Buffer overflow detected!! (Please file a bug report)\n"); + + snprintf(clen, sizeof(clen), "%d", (int)strlen(tmp)); add_header(&req, "Content-Length", clen); add_line(&req, tmp); - add_line(&req, tmp2); - if (!p->initreq.headers) { - /* Use this as the basis */ + if (!p->initreq.headers) { /* Use this as the basis */ copy_request(&p->initreq, &req); parse(&p->initreq); if (sip_debug_test_pvt(p)) @@ -5444,7 +5505,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. */ @@ -5628,10 +5689,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; + struct sip_pvt *p = data; /* The actual subscription data */ + if (state == -1) { sip_scheddestroy(p, 15000); p->stateid = -1; @@ -5639,6 +5703,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); @@ -6263,6 +6328,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)); @@ -6325,6 +6391,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; @@ -6431,16 +6498,30 @@ /*--- 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); @@ -6451,7 +6532,15 @@ 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 @@ -7320,6 +7409,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" @@ -7333,7 +7423,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), @@ -7346,11 +7436,11 @@ 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); @@ -7362,6 +7452,7 @@ #undef FORMAT #undef FORMAT2 #undef FORMAT3 +#undef FORMAT4 } /*--- complete_sipch: Support routine for 'sip show channel' CLI ---*/ @@ -8352,7 +8443,7 @@ } } if (!peer->lastms) - statechanged = 1; + statechanged = 1; peer->lastms = pingtime; peer->call = NULL; if (statechanged) { @@ -8467,9 +8558,8 @@ ast_log(LOG_WARNING, "Notify answer on an owned channel?\n"); ast_queue_hangup(p->owner); } else { - if (!p->subscribed) { - ast_set_flag(p, SIP_NEEDDESTROY); - } + if (!p->subscribed) + ast_set_flag(p, SIP_NEEDDESTROY); } } else if (sipmethod == SIP_INVITE) { /* 200 OK on invite - someone's answering our call */ @@ -8862,7 +8952,7 @@ build_contact(p); /* XXX Should we authenticate OPTIONS? XXX */ if (ast_strlen_zero(p->context)) - strcpy(p->context, default_context); + ast_copy_string(p->context, default_context, sizeof(p->context)); if (res < 0) transmit_response_with_allow(p, "404 Not Found", req, 0); else if (res > 0) @@ -8973,7 +9063,7 @@ ast_queue_frame(p->owner, &af); /* Initialize the context if it hasn't been already */ if (ast_strlen_zero(p->context)) - strcpy(p->context, default_context); + ast_copy_string(p->context, default_context, sizeof(p->context)); /* Check number of concurrent calls -vs- incoming limit HERE */ ast_log(LOG_DEBUG, "Checking SIP call limits for device %s\n", p->username); res = update_user_counter(p, INC_IN_USE); @@ -9119,7 +9209,7 @@ if (option_debug > 2) ast_log(LOG_DEBUG, "SIP call transfer received for call %s (REFER)!\n", p->callid); if (ast_strlen_zero(p->context)) - strcpy(p->context, default_context); + ast_copy_string(p->context, default_context, sizeof(p->context)); res = get_refer_info(p, req); if (res < 0) transmit_response_with_allow(p, "404 Not Found", req, 1); @@ -9228,7 +9318,7 @@ ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method. Ask vendor to support REFER instead\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr)); if (ast_strlen_zero(p->context)) - strcpy(p->context, default_context); + ast_copy_string(p->context, default_context, sizeof(p->context)); res = get_also_info(p, req); if (!res) { c = p->owner; @@ -9257,16 +9347,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; } @@ -9302,8 +9388,10 @@ return 0; } /* Initialize the context if it hasn't been already */ - if (ast_strlen_zero(p->context)) - strcpy(p->context, default_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)) + ast_copy_string(p->context, default_context, sizeof(p->context)); /* Get destination right away */ gotdest = get_destination(p, NULL); build_contact(p); @@ -9311,15 +9399,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 @@ -9330,12 +9437,10 @@ 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. - - */ - if (!ast_strlen_zero(mailbox)) { + config section. */ + + if (!ast_strlen_zero(mailbox)) found++; - } if (found){ transmit_response(p, "200 OK", req); @@ -9345,8 +9450,14 @@ 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); } @@ -9362,10 +9473,10 @@ return 0; } /* The next line can be removed if the SNOM200 Expires bug is fixed */ - if (p->subscribed == 1) { + /* if (p->subscribed != 0) { if (p->expiry>max_expiry) p->expiry = max_expiry; - } + } */ /* Go ahead and free RTP port */ if (p->rtp) { if (p->owner) { @@ -9383,8 +9494,12 @@ 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); + /* Set timer for destruction of call at expiration */ sip_scheddestroy(p, (p->expiry+10)*1000); + /* Send first notification */ transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); } return 1; @@ -9724,7 +9839,7 @@ build_callid(p->callid, sizeof(p->callid), p->ourip, p->fromdomain); /* Send MWI */ ast_set_flag(p, SIP_OUTGOING); - transmit_notify_with_mwi(p, newmsgs, oldmsgs); + transmit_notify_with_mwi(p, newmsgs, oldmsgs, peer->vmexten); sip_scheddestroy(p, 15000); return 0; } @@ -9999,13 +10114,35 @@ 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 have 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; @@ -10324,9 +10461,9 @@ user->capability = global_capability; user->prefs = prefs; /* set default context */ - strcpy(user->context, default_context); - strcpy(user->language, default_language); - strcpy(user->musicclass, global_musicclass); + ast_copy_string(user->context, default_context, sizeof(user->context)); + ast_copy_string(user->language, default_language, sizeof(user->language)); + ast_copy_string(user->musicclass, global_musicclass, sizeof(user->musicclass)); while(v) { if (handle_common_options(&userflags, &mask, v)) { v = v->next; @@ -10335,6 +10472,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,'='))) { @@ -10403,11 +10542,11 @@ { struct sip_peer *peer; - peer = malloc(sizeof(*peer)); + peer = malloc(sizeof(struct sip_peer)); if (!peer) return NULL; - memset(peer, 0, sizeof(*peer)); + memset(peer, 0, sizeof(struct sip_peer)); apeerobjs++; ASTOBJ_INIT(peer); @@ -10418,9 +10557,10 @@ SIP_PROMISCREDIR | SIP_USEREQPHONE | SIP_TRUSTRPID | SIP_USECLIENTCODE | SIP_DTMF | SIP_NAT | SIP_REINVITE | SIP_INSECURE_PORT | SIP_INSECURE_INVITE | SIP_PROG_INBAND | SIP_OSPAUTH); - strcpy(peer->context, default_context); - strcpy(peer->language, default_language); - strcpy(peer->musicclass, global_musicclass); + ast_copy_string(peer->context, default_context, sizeof(peer->context)); + ast_copy_string(peer->subscribecontext, default_subscribecontext, sizeof(peer->subscribecontext)); + ast_copy_string(peer->language, default_language, sizeof(peer->language)); + ast_copy_string(peer->musicclass, global_musicclass, sizeof(peer->musicclass)); peer->addr.sin_port = htons(DEFAULT_SIP_PORT); peer->addr.sin_family = AF_INET; peer->expiry = expiry; @@ -10492,9 +10632,11 @@ ast_variables_destroy(peer->chanvars); peer->chanvars = NULL; } - strcpy(peer->context, default_context); - strcpy(peer->language, default_language); - strcpy(peer->musicclass, global_musicclass); + ast_copy_string(peer->vmexten, global_vmexten, sizeof(peer->vmexten)); + ast_copy_string(peer->context, default_context, sizeof(peer->context)); + ast_copy_string(peer->subscribecontext, default_subscribecontext, sizeof(peer->subscribecontext)); + ast_copy_string(peer->language, default_language, sizeof(peer->language)); + ast_copy_string(peer->musicclass, global_musicclass, sizeof(peer->musicclass)); ast_copy_flags(peer, &global_flags, SIP_USEREQPHONE); peer->secret[0] = '\0'; peer->md5secret[0] = '\0'; @@ -10528,9 +10670,9 @@ if (realtime && !strcasecmp(v->name, "regseconds")) { if (sscanf(v->value, "%li", ®seconds) != 1) regseconds = 0; - } else if (realtime && !strcasecmp(v->name, "ipaddr") && !ast_strlen_zero(v->value) ) { + } else if (realtime && !strcasecmp(v->name, "ipaddr") && !ast_strlen_zero(v->value) ) inet_aton(v->value, &(peer->addr.sin_addr)); - } else if (realtime && !strcasecmp(v->name, "name")) + else if (realtime && !strcasecmp(v->name, "name")) ast_copy_string(peer->name, v->value, sizeof(peer->name)); else if (!strcasecmp(v->name, "secret")) ast_copy_string(peer->secret, v->value, sizeof(peer->secret)); @@ -10538,11 +10680,13 @@ ast_copy_string(peer->md5secret, v->value, sizeof(peer->md5secret)); else if (!strcasecmp(v->name, "auth")) peer->auth = add_realm_authentication(peer->auth, v->value, v->lineno); - else if (!strcasecmp(v->name, "callerid")) { + else if (!strcasecmp(v->name, "callerid")) 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")) { + else if (!strcasecmp(v->name, "context")) ast_copy_string(peer->context, v->value, sizeof(peer->context)); - } else if (!strcasecmp(v->name, "fromdomain")) + 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")) ast_set2_flag(peer, ast_true(v->value), SIP_USEREQPHONE); @@ -10631,6 +10775,8 @@ ast_copy_string(peer->musicclass, v->value, sizeof(peer->musicclass)); } else if (!strcasecmp(v->name, "mailbox")) { ast_copy_string(peer->mailbox, v->value, sizeof(peer->mailbox)); + } else if (!strcasecmp(v->name, "vmexten")) { + ast_copy_string(peer->vmexten, v->value, sizeof(peer->vmexten)); } else if (!strcasecmp(v->name, "callgroup")) { peer->callgroup = ast_get_group(v->value); } else if (!strcasecmp(v->name, "pickupgroup")) { @@ -10735,6 +10881,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; @@ -10744,6 +10891,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)); @@ -10766,6 +10914,7 @@ ast_set_flag(&global_flags, SIP_NAT_RFC3581); ast_set_flag(&global_flags, SIP_CAN_REINVITE); global_mwitime = DEFAULT_MWITIME; + ast_copy_string(global_vmexten, DEFAULT_VMEXTEN, sizeof(global_vmexten)); srvlookup = 0; autocreatepeer = 0; regcontext[0] = '\0'; @@ -10812,6 +10961,8 @@ ast_log(LOG_WARNING, "'%s' is not a valid MWI time setting at line %d. Using default (10).\n", v->value, v->lineno); global_mwitime = DEFAULT_MWITIME; } + } else if (!strcasecmp(v->name, "vmexten")) { + ast_copy_string(global_vmexten, v->value, sizeof(global_vmexten)); } else if (!strcasecmp(v->name, "rtptimeout")) { if ((sscanf(v->value, "%d", &global_rtptimeout) != 1) || (global_rtptimeout < 0)) { ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno); @@ -10833,6 +10984,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 23 Jun 2005 10:28:24 -0000 @@ -48,7 +48,13 @@ ;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 + ; defaults to "asterisk" ;videosupport=yes ; Turn on support for SIP video ;recordhistory=yes ; Record SIP history by default ; (see sip history / sip no history) @@ -318,6 +324,9 @@ ;defaultip=192.168.0.59 ; IP used until peer registers ;username=snom ; Username to use in INVITE until peer registers ;mailbox=1234@context,2345 ; Mailbox(-es) for message waiting indicator +;vmexten=voicemail ; dialplan extension to reach mailbox + ; sets the Message-Account in the MWI notify message + ; defaults to global vmexten which defaults to "asterisk" ;restrictcid=yes ; To have the callerid restriced -> sent as ANI ;disallow=all ;allow=ulaw ; dtmfmode=inband only works with ulaw or alaw! Index: include/asterisk/pbx.h =================================================================== RCS file: /usr/cvsroot/asterisk/include/asterisk/pbx.h,v retrieving revision 1.47 diff -u -r1.47 pbx.h --- include/asterisk/pbx.h 18 May 2005 01:49:13 -0000 1.47 +++ include/asterisk/pbx.h 23 Jun 2005 10:28:24 -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;