Index: pbx.c =================================================================== RCS file: /usr/cvsroot/asterisk/pbx.c,v retrieving revision 1.262 diff -u -r1.262 pbx.c --- pbx.c 15 Jul 2005 23:24:51 -0000 1.262 +++ pbx.c 18 Jul 2005 18:01:11 -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; @@ -1750,7 +1752,7 @@ do { rest = strchr(cur, '&'); if (rest) { - *rest = 0; + *rest = 0; rest++; } @@ -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.781 diff -u -r1.781 chan_sip.c --- channels/chan_sip.c 15 Jul 2005 23:00:46 -0000 1.781 +++ channels/chan_sip.c 18 Jul 2005 18:01:13 -0000 @@ -114,6 +114,8 @@ #define DEFAULT_RETRANS 2000 /* How frequently to retransmit */ #define MAX_RETRANS 5 /* Try only 5 times for retransmissions */ +#define DEFAULT_MAX_FORWARDS 70 +#define SIP_RETVAL_IGNORE 1 #define DEBUG_READ 0 /* Recieved data */ #define DEBUG_SEND 1 /* Transmit data */ @@ -139,6 +141,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 { @@ -182,6 +189,10 @@ { "Allow-Events", "u" }, { "Event", "o" }, { "Via", "v" }, + { "Accept-Contact", "a" }, + { "Reject-Contact", "j" }, + { "Request-Disposition", "d" }, + { "Session-Expires", "x" }, }; /* Define SIP option tags, used in Require: and Supported: headers */ @@ -264,6 +275,13 @@ #define DEFAULT_CONTEXT "default" static char default_context[AST_MAX_CONTEXT] = DEFAULT_CONTEXT; +static char default_subscribecontext[AST_MAX_CONTEXT]; + +static int global_notifyringing = 1; /* Send notifications on ringing */ + +#define DEFAULT_VMEXTEN "asterisk" +static char global_vmexten[AST_MAX_EXTENSION] = DEFAULT_VMEXTEN; + static char default_language[MAX_LANGUAGE] = ""; #define DEFAULT_CALLERID "asterisk" @@ -517,6 +535,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 +584,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 +621,7 @@ char secret[80]; /* Password */ char md5secret[80]; /* Password in md5 */ char context[AST_MAX_CONTEXT]; /* Default context for incoming calls */ + char subscribecontext[AST_MAX_CONTEXT]; /* Default context for subscriptions */ char cid_num[80]; /* Caller ID num */ char cid_name[80]; /* Caller ID name */ char accountcode[AST_MAX_ACCOUNT_CODE]; /* Account code */ @@ -632,6 +653,7 @@ char md5secret[80]; /* Password in MD5 */ struct sip_auth *auth; /* Realm authentication list */ char context[AST_MAX_CONTEXT]; /* Default context for incoming calls */ + char subscribecontext[AST_MAX_CONTEXT]; /* Default context for subscriptions */ char username[80]; /* Temporary username until registration */ char accountcode[AST_MAX_ACCOUNT_CODE]; /* Account code */ int amaflags; /* AMA Flags (for billing) */ @@ -647,6 +669,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_MUSICCLASS];/* Music on Hold class */ @@ -785,6 +808,7 @@ static struct sip_user *build_user(const char *name, struct ast_variable *v, int realtime); static int sip_do_reload(void); static int expire_register(void *data); +static int sip_addheader(struct ast_channel *chan, void *data); static int callevents = 0; static struct ast_channel *sip_request(const char *type, int format, void *data, int *cause); @@ -803,6 +827,13 @@ static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno); /* Add realm authentication in list */ static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, char *realm); /* Find authentication for a specific realm */ static void append_date(struct sip_request *req); /* Append date to SIP packet */ +static void sip_destroy(struct sip_pvt *p); +static void parse(struct sip_request *req); +static char *get_header(struct sip_request *req, char *name); +static void copy_request(struct sip_request *dst,struct sip_request *src); +static int transmit_response_reliable(struct sip_pvt *p, char *msg, struct sip_request *req, int fatal); +static int transmit_register(struct sip_registry *r, int sipmethod, char *auth, char *authheader); +static int sip_poke_peer(struct sip_peer *peer); /* Definition of this channel for channel registration */ static const struct ast_channel_tech sip_tech = { @@ -838,24 +870,64 @@ return res; } -/* - * If there is a string in , strip everything around and return - * the content. Otherwise return the original argument. - */ -static char *get_in_brackets(char *c) +/* --- sip_extract_tag: extract from and to tags from a callid ---*/ +static int sip_extract_tag(char **in) { - char *n = strchr(c, '<'); + char *tag; - if (n) { + if ((tag = strcasestr(*in, "tag="))) { + char *ptr; + tag += 4; + if ((ptr = strchr(tag, ';'))) + *ptr = '\0'; + return 0; + } + return -1; +} + +/*--- ditch_braces: Pick out text in braces from character string ---*/ +static char *ditch_braces(char *tmp, int mode) +{ + char *c = tmp, *n, *q; + + if (!mode && (q = strchr(tmp, '"')) ) { + c = q + 1; + if ((q = strchr(c, '"')) ) + c = q + 1; + else { + ast_log(LOG_WARNING, "No closing quote in '%s'\n", tmp); + c = tmp; + } + } + if ((n = strchr(c, '<')) ) { c = n + 1; - n = strchr(c, '>'); - /* Lose the part after the > */ - if (n) - *n = '\0'; + while (*c && *c != '>') c++; + if (*c != '>') { + ast_log(LOG_WARNING, "No closing brace in '%s'\n", tmp); + } else + *c = '\0'; + return n+1; } return c; } +/*--- url_decode: Decode SIP URL (overwrite the string) ---*/ +static void url_decode(char *s) +{ + char *o; + unsigned int tmp; + + for (o = s; *s; s++, o++) { + if (*s == '%' && strlen(s) > 2 && sscanf(s + 1, "%2x", &tmp) == 1) { + /* have '%', two chars and correct parsing */ + *o = tmp; + s += 2; /* Will be incremented once more when we break out */ + } else /* all other cases, just copy */ + *o = *s; + } + *o = '\0'; +} + /*--- parse_sip_options: Parse supported header in incoming packet */ unsigned int parse_sip_options(struct sip_pvt *pvt, char *supported) { @@ -895,10 +967,11 @@ ast_log(LOG_DEBUG, "Found no match for SIP option: %s (Please file bug report!)\n", next); next = sep; } - if (pvt) + if (pvt) { pvt->sipoptions = profile; - - ast_log(LOG_DEBUG, "* SIP extension value: %d for call %s\n", profile, pvt->callid); + if (option_debug) + ast_log(LOG_DEBUG, "* SIP extension value: %d for call %s\n", profile, pvt->callid); + } return(profile); } @@ -941,8 +1014,6 @@ return res; } -static void sip_destroy(struct sip_pvt *p); - /*--- build_via: Build a Via header for a request ---*/ static void build_via(struct sip_pvt *p, char *buf, int len) { @@ -955,7 +1026,7 @@ snprintf(buf, len, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); } -/*--- ast_sip_ouraddrfor: NAT fix - decide which IP address to use for ASterisk server? ---*/ +/*--- ast_sip_ouraddrfor: NAT fix - decide which IP address to use for Asterisk server? ---*/ /* Only used for outbound registrations */ static int ast_sip_ouraddrfor(struct in_addr *them, struct in_addr *us) { @@ -1243,10 +1314,6 @@ return res; } -static void parse(struct sip_request *req); -static char *get_header(struct sip_request *req, char *name); -static void copy_request(struct sip_request *dst,struct sip_request *src); - /*--- parse_copy: Copy SIP request, parse it */ static void parse_copy(struct sip_request *dst, struct sip_request *src) { @@ -1321,51 +1388,6 @@ return res; } -/*--- url_decode: Decode SIP URL (overwrite the string) ---*/ -static void url_decode(char *s) -{ - char *o; - unsigned int tmp; - - for (o = s; *s; s++, o++) { - if (*s == '%' && strlen(s) > 2 && sscanf(s + 1, "%2x", &tmp) == 1) { - /* have '%', two chars and correct parsing */ - *o = tmp; - s += 2; /* Will be incremented once more when we break out */ - } else /* all other cases, just copy */ - *o = *s; - } - *o = '\0'; -} - -/*--- ditch_braces: Pick out text in braces from character string ---*/ -static char *ditch_braces(char *tmp) -{ - char *c = tmp; - char *n; - char *q; - if ((q = strchr(tmp, '"')) ) { - c = q + 1; - if ((q = strchr(c, '"')) ) - c = q + 1; - else { - ast_log(LOG_WARNING, "No closing quote in '%s'\n", tmp); - c = tmp; - } - } - if ((n = strchr(c, '<')) ) { - c = n + 1; - while(*c && *c != '>') c++; - if (*c != '>') { - ast_log(LOG_WARNING, "No closing brace in '%s'\n", tmp); - } else { - *c = '\0'; - } - return n+1; - } - return c; -} - /*--- sip_sendtext: Send SIP MESSAGE text within a call ---*/ /* Called from PBX core text message functions */ static int sip_sendtext(struct ast_channel *ast, const char *text) @@ -1386,16 +1408,16 @@ } /*--- realtime_update_peer: Update peer object in realtime storage ---*/ -static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, int expirey) +static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, int expiry) { char port[10] = ""; char ipaddr[20] = ""; char regseconds[20] = "0"; - if (expirey) { /* Registration */ + if (expiry) { /* Registration */ time_t nowtime; time(&nowtime); - nowtime += expirey; + nowtime += expiry; snprintf(regseconds, sizeof(regseconds), "%ld", nowtime); /* Expiration time */ ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin->sin_addr); snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port)); @@ -1707,9 +1729,9 @@ struct ast_hostent ahp; struct sip_peer *p; int found=0; - char *port; + char *port, *ptr, *hostp, *hostn; int portno; - char host[MAXHOSTNAMELEN], *hostn; + char host[MAXHOSTNAMELEN]; char peer[256]=""; ast_copy_string(peer, opeer, sizeof(peer)); @@ -1746,7 +1768,13 @@ portno = tportno; } } - hp = ast_gethostbyname(hostn, &ahp); + if ((hostp = ast_strdupa(hostn))) { + if ((ptr = strchr(hostp, '?'))) + *ptr = '\0'; + } else + hostp = peer; + + hp = ast_gethostbyname(hostp, &ahp); if (hp) { ast_copy_string(r->tohost, peer, sizeof(r->tohost)); memcpy(&r->sa.sin_addr, hp->h_addr, sizeof(r->sa.sin_addr)); @@ -2057,9 +2085,6 @@ ast_mutex_unlock(&iflock); } - -static int transmit_response_reliable(struct sip_pvt *p, char *msg, struct sip_request *req, int fatal); - /*--- hangup_sip2cause: Convert SIP hangup causes to Asterisk hangup causes ---*/ static int hangup_sip2cause(int cause) { @@ -2393,9 +2418,6 @@ return res; } -#define DEFAULT_MAX_FORWARDS 70 - - /*--- sip_transfer: Transfer SIP call */ static int sip_transfer(struct ast_channel *ast, const char *dest) { @@ -3710,7 +3732,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; @@ -3750,7 +3772,7 @@ /* We have no URI, use To: or From: header as URI (depending on direction) */ c = get_header(orig, (ast_test_flag(p, SIP_OUTGOING)) ? "To" : "From"); ast_copy_string(stripped, c, sizeof(stripped)); - c = get_in_brackets(stripped); + c = ditch_braces(stripped, 1); n = strchr(c, ';'); if (n) *n = '\0'; @@ -3789,7 +3811,10 @@ 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); @@ -4222,7 +4247,7 @@ char stripped[256]=""; char *c, *n; ast_copy_string(stripped, get_header(req, "Contact"), sizeof(stripped)); - c = get_in_brackets(stripped); + c = ditch_braces(stripped, 1); n = strchr(c, ';'); if (n) *n = '\0'; @@ -4454,101 +4479,176 @@ 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]; + struct ast_channel *chan = NULL; + struct sip_pvt *np; + char notify_cid[AST_MAX_EXTENSION] = ""; + char notify_callid[80] = ""; + int notify_ourtag = 0; + char notify_theirtag[256] = ""; + int hintlen; + 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"; + } + + /* check if the device is ringing and if so get the callid to enable pickup functionality (e.g. for snom phones) */ + if (state == AST_EXTENSION_RINGING_AND_INUSE || state == AST_EXTENSION_RINGING) { + hintlen = strlen(hint); + while ((chan = ast_channel_walk_locked(chan)) != NULL) { + if (chan->_state == AST_STATE_RINGING && chan->tech_pvt && !strncasecmp(chan->name, hint, hintlen)) { + np = chan->tech_pvt; + ast_copy_string(notify_callid, np->callid, sizeof(notify_callid)); + ast_copy_string(notify_theirtag, np->theirtag, sizeof(notify_theirtag)); + ast_copy_string(notify_cid, np->fromname, sizeof(notify_cid)); + notify_ourtag = np->tag; + ast_mutex_unlock(&chan->lock); + break; + } + ast_mutex_unlock(&chan->lock); + } + if (ast_strlen_zero(notify_callid)) + ast_log(LOG_NOTICE, "CallID for hint %s not found\n", hint); + } + } 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); + c = ditch_braces(from, 0); 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, 0); + 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 (!ast_strlen_zero(notify_callid)) { + ast_build_string(&t, &maxbytes, "\n", p->exten, notify_callid, notify_ourtag, notify_theirtag); + if (option_debug > 1) + ast_log(LOG_NOTICE, "Transmitting CallID in NOTIFY message - DialogID: %s CallID: %s\n", p->exten, notify_callid); + } else + ast_build_string(&t, &maxbytes, "\n", p->exten); + ast_build_string(&t, &maxbytes, "%s\n", statestring); + if (state == AST_EXTENSION_RINGING) { + ast_build_string(&t, &maxbytes, "%s\n", p->exten, mfrom, mfrom); + ast_build_string(&t, &maxbytes, "%s\n", notify_cid, mto, mto); + } + 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"); @@ -4565,25 +4665,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 (0/0)\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 (0/0)\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)) @@ -4666,8 +4771,6 @@ } } -static int transmit_register(struct sip_registry *r, int sipmethod, char *auth, char *authheader); - /*--- sip_reregister: Update registration with SIP Proxy---*/ static int sip_reregister(void *data) { @@ -4944,7 +5047,7 @@ else of = get_header(&p->initreq, "From"); ast_copy_string(from, of, sizeof(from)); - of = ditch_braces(from); + of = ditch_braces(from, 0); ast_copy_string(p->from,of,sizeof(p->from)); if (strncmp(of, "sip:", 4)) { ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); @@ -5044,7 +5147,6 @@ return 0; } -static int sip_poke_peer(struct sip_peer *peer); static int sip_poke_peer_s(void *data) { @@ -5125,7 +5227,7 @@ /* Look for brackets */ ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact)); - c = get_in_brackets(contact); + c = ditch_braces(contact, 1); /* Save full contact to call pvt for later bye or re-invite */ ast_copy_string(pvt->fullcontact, c, sizeof(pvt->fullcontact)); @@ -5210,7 +5312,7 @@ } /* Look for brackets */ ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact)); - c = get_in_brackets(contact); + c = ditch_braces(contact, 1); if (!strcasecmp(c, "*") || !expiry) { /* Unregister this peer */ /* This means remove all registrations and return OK */ @@ -5457,7 +5559,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. */ @@ -5641,10 +5743,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; @@ -5652,6 +5757,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); @@ -5674,7 +5780,7 @@ *t = '\0'; ast_copy_string(tmp, get_header(req, "To"), sizeof(tmp)); - c = ditch_braces(tmp); + c = ditch_braces(tmp, 0); /* Ditch ;user=phone */ name = strchr(c, ';'); if (name) @@ -5770,7 +5876,7 @@ ast_copy_string(tmp, get_header(req, "Diversion"), sizeof(tmp)); if (ast_strlen_zero(tmp)) return 0; - c = ditch_braces(tmp); + c = ditch_braces(tmp, 0); if (strncmp(c, "sip:", 4)) { ast_log(LOG_WARNING, "Huh? Not an RDNIS SIP header (%s)?\n", c); return -1; @@ -5798,10 +5904,10 @@ req = &p->initreq; if (req->rlPart2) ast_copy_string(tmp, req->rlPart2, sizeof(tmp)); - c = ditch_braces(tmp); + c = ditch_braces(tmp, 0); ast_copy_string(tmpf, get_header(req, "From"), sizeof(tmpf)); - fr = ditch_braces(tmpf); + fr = ditch_braces(tmpf, 0); if (strncmp(c, "sip:", 4)) { ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); @@ -5853,19 +5959,44 @@ } /*--- get_sip_pvt_byid_locked: Lock interface lock and find matching pvt lock ---*/ -static struct sip_pvt *get_sip_pvt_byid_locked(char *callid) +static struct sip_pvt *get_sip_pvt_byid_locked(char *callid, struct sip_request *req, char *totag, char *fromtag) { struct sip_pvt *sip_pvt_ptr = NULL; + char *real_totag = NULL, *real_fromtag = NULL; + int match = 1; /* Search interfaces and find the match */ ast_mutex_lock(&iflock); sip_pvt_ptr = iflist; - while(sip_pvt_ptr) { + while (sip_pvt_ptr) { if (!strcmp(sip_pvt_ptr->callid, callid)) { /* Go ahead and lock it (and its owner) before returning */ ast_mutex_lock(&sip_pvt_ptr->lock); + + if (req && pedanticsipchecking) { + if (totag) { + real_totag = ast_strdupa(get_header(req, "To")); + if (sip_extract_tag(&real_totag)) + real_totag = NULL; + if (strcmp(real_totag, totag)) + match = 0; + } + if (match && fromtag) { + real_fromtag = ast_strdupa(get_header(req, "From")); + if (sip_extract_tag(&real_fromtag)) + real_fromtag = NULL; + if (strcmp(real_fromtag, fromtag)) + match = 0; + } + } + + if (!match) { + ast_mutex_unlock(&sip_pvt_ptr->lock); + break; + } + if (sip_pvt_ptr->owner) { - while(ast_mutex_trylock(&sip_pvt_ptr->owner->lock)) { + while (ast_mutex_trylock(&sip_pvt_ptr->owner->lock)) { ast_mutex_unlock(&sip_pvt_ptr->lock); usleep(1); ast_mutex_lock(&sip_pvt_ptr->lock); @@ -5886,7 +6017,7 @@ { char *p_refer_to = NULL, *p_referred_by = NULL, *h_refer_to = NULL, *h_referred_by = NULL, *h_contact = NULL; - char *replace_callid = "", *refer_to = NULL, *referred_by = NULL, *ptr = NULL; + char *replace_callid = "", *refer_to = NULL, *referred_by = NULL, *ptr = NULL, *replaces_header=NULL, *refer_uri; struct sip_request *req = NULL; struct sip_pvt *sip_pvt_ptr = NULL; struct ast_channel *chan = NULL, *peer = NULL; @@ -5902,13 +6033,13 @@ return -1; } - refer_to = ditch_braces(h_refer_to); + refer_to = ditch_braces(h_refer_to, 0); if (!( (p_referred_by = get_header(req, "Referred-By")) && (h_referred_by = ast_strdupa(p_referred_by)) )) { ast_log(LOG_WARNING, "No Referrred-By Header That's not illegal\n"); return -1; } else { - referred_by = ditch_braces(h_referred_by); + referred_by = ditch_braces(h_referred_by, 0); } h_contact = get_header(req, "Contact"); @@ -5928,6 +6059,8 @@ if (referred_by) referred_by += 4; + refer_uri = ast_strdupa(refer_to); + if ((ptr = strchr(refer_to, '?'))) { /* Search for arguments */ *ptr = '\0'; @@ -5935,10 +6068,7 @@ if (!strncasecmp(ptr, "REPLACES=", 9)) { char *p; replace_callid = ast_strdupa(ptr + 9); - /* someday soon to support invite/replaces properly! replaces_header = ast_strdupa(replace_callid); - -anthm - */ url_decode(replace_callid); if ((ptr = strchr(replace_callid, '%'))) *ptr = '\0'; @@ -5976,18 +6106,47 @@ ast_copy_string(sip_pvt->referred_by, "", sizeof(sip_pvt->referred_by)); ast_copy_string(sip_pvt->refer_contact, "", sizeof(sip_pvt->refer_contact)); sip_pvt->refer_call = NULL; - if ((sip_pvt_ptr = get_sip_pvt_byid_locked(replace_callid))) { + if ((sip_pvt_ptr = get_sip_pvt_byid_locked(replace_callid, req, NULL, NULL))) { sip_pvt->refer_call = sip_pvt_ptr; if (sip_pvt->refer_call == sip_pvt) { ast_log(LOG_NOTICE, "Supervised transfer attempted to transfer into same call id (%s == %s)!\n", replace_callid, sip_pvt->callid); sip_pvt->refer_call = NULL; - } else - return 0; + } + return 0; } else { - ast_log(LOG_NOTICE, "Supervised transfer requested, but unable to find callid '%s'. Both legs must reside on Asterisk box to transfer at this time.\n", replace_callid); - /* XXX The refer_to could contain a call on an entirely different machine, requiring an - INVITE with a replaces header -anthm XXX */ - /* The only way to find out is to use the dialplan - oej */ + /* Don't ask me =0 ?, SIP made do it! */ + int cause = 0, res = -1; + struct ast_channel *ichan = NULL; + transmit_notify_with_sipfrag(sip_pvt, sip_pvt->ocseq); + if ((ptr = strchr(refer_uri, ';'))) + *ptr = '\0'; + + if ((ichan = sip_request("SIP", sip_pvt->owner ? sip_pvt->owner->readformat : AST_FORMAT_ULAW, refer_uri, &cause))) { + struct ast_frame *f; + char *rbuf; + if (sip_debug_test_pvt(sip_pvt)) + ast_log(LOG_DEBUG, "Going hunting for a remote INVITE/Replaces at [%s] Wish me luck!\n", refer_uri); + if ((rbuf = alloca(strlen(replaces_header) + 10))) { + sprintf(rbuf, "Replaces: %s", replaces_header); + sip_addheader(ichan, rbuf); + sip_call(ichan, refer_uri, 20000); + ast_channel_masquerade(sip_pvt->owner, ichan); + if ((f = ast_read(ichan))) { + ast_log(LOG_NOTICE, "INVITE/Replaces successful for URI %s\n", refer_uri); + ast_frfree(f); + transmit_response(sip_pvt, "202 Accepted", req); + res = SIP_RETVAL_IGNORE; /* means do nothing more */ + } else + res = -1; + } else { + ast_log(LOG_ERROR,"Memory Error!\n"); + res = -1; + } + + ast_hangup(ichan); + } else + res = -1; + return res; } } else if (ast_exists_extension(NULL, sip_pvt->context, refer_to, 1, NULL) || !strcmp(refer_to, ast_parking_ext())) { /* This is an unsupervised transfer (blind transfer) */ @@ -6026,7 +6185,7 @@ req = &p->initreq; ast_copy_string(tmp, get_header(req, "Also"), sizeof(tmp)); - c = ditch_braces(tmp); + c = ditch_braces(tmp, 0); if (strncmp(c, "sip:", 4)) { @@ -6204,7 +6363,7 @@ if (!ast_strlen_zero(rpid)) p->callingpres = get_rpid_num(rpid,rpid_num, sizeof(rpid_num)); - of = ditch_braces(from); + of = ditch_braces(from, 0); if (ast_strlen_zero(p->exten)) { t = uri; if (!strncmp(t, "sip:", 4)) @@ -6252,10 +6411,12 @@ } if (p->rtp) { + if (option_debug > 1) ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); ast_rtp_setnat(p->rtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); } if (p->vrtp) { + if (option_debug > 1) ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); ast_rtp_setnat(p->vrtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); } @@ -6279,6 +6440,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)); @@ -6341,6 +6503,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; @@ -6445,18 +6608,31 @@ return 0; } - /*--- 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); @@ -6467,7 +6643,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 @@ -7336,6 +7520,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" @@ -7349,7 +7534,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), @@ -7362,22 +7547,23 @@ numchans++; } if (cur->subscribed && subscriptions) { - ast_cli(fd, FORMAT3, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr), + 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->uri); - + 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 ---*/ @@ -7565,7 +7751,7 @@ ast_cli(fd, " Promiscuous Redir: %s\n", ast_test_flag(cur, SIP_PROMISCREDIR) ? "Yes" : "No"); ast_cli(fd, " Route: %s\n", cur->route ? cur->route->hop : "N/A"); ast_cli(fd, " DTMF Mode: %s\n", dtmfmode2str(ast_test_flag(cur, SIP_DTMF))); - ast_cli(fd, " SIP Options : "); + ast_cli(fd, " SIP Options: "); if (cur->sipoptions) { int x; for (x=0 ; (x < (sizeof(sip_options) / sizeof(sip_options[0]))); x++) { @@ -8184,7 +8370,7 @@ char tmp[256] = ""; char *s, *e; ast_copy_string(tmp, get_header(req, "Contact"), sizeof(tmp)); - s = ditch_braces(tmp); + s = ditch_braces(tmp, 0); e = strchr(s, ';'); if (e) *e = '\0'; @@ -8513,6 +8699,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); @@ -8853,6 +9040,7 @@ } peerc->cdr = NULL; + ast_log(LOG_DEBUG, "Trying to masquerade %s and %s\n", peerb->name, peerc->name); if (ast_channel_masquerade(peerb, peerc)) { ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", peerb->name, peerc->name); res = -1; @@ -8896,13 +9084,13 @@ /*--- handle_request_invite: Handle incoming INVITE request */ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int ignore, int seqno, struct sockaddr_in *sin, int *recount, char *e) { - int res = 1; - struct ast_channel *c=NULL; - int gotdest; - struct ast_frame af = { AST_FRAME_NULL, }; - char *supported; - char *required; + int res = 1, gotdest = 0; + struct ast_channel *c = NULL; + struct ast_frame af = { AST_FRAME_NULL, }, *f = NULL; + char *supported = NULL, *required = NULL; unsigned int required_profile = 0; + char *ptr, *p_replaces = NULL, *replace_id = NULL; + struct sip_pvt *refer_pvt = NULL; /* Find out what they support */ if (!p->sipoptions) { @@ -8918,7 +9106,20 @@ transmit_response_with_unsupported(p, "420 Bad extension", req, required); ast_set_flag(p, SIP_NEEDDESTROY); return -1; + } + } + if ((p_replaces = get_header(req, "Replaces"))) { + if (ast_strlen_zero(p_replaces)) { + p_replaces = NULL; + } else { + if (debug) + ast_log(LOG_DEBUG, "Found a Replaces header %s\n", get_header(req, "Replaces")); + replace_id = ast_strdupa(p_replaces); + if (strchr(replace_id, '%')) + url_decode(replace_id); + if ((ptr = strchr(replace_id, ';'))) + *ptr = '\0'; } } @@ -8991,7 +9192,8 @@ if (ast_strlen_zero(p->context)) strcpy(p->context, default_context); /* Check number of concurrent calls -vs- incoming limit HERE */ - ast_log(LOG_DEBUG, "Checking SIP call limits for device %s\n", p->username); + if (option_debug > 1) + ast_log(LOG_DEBUG, "Checking SIP call limits for device %s\n", p->username); res = update_user_counter(p, INC_IN_USE); if (res) { if (res < 0) { @@ -9010,7 +9212,7 @@ extract_uri(p, req); build_contact(p); - if (gotdest) { + if (!replace_id && gotdest) { if (gotdest < 0) { if (ignore) transmit_response(p, "404 Not Found", req); @@ -9037,6 +9239,38 @@ /* Save Record-Route for any later requests we make on this dialogue */ build_route(p, req, 0); if (c) { + if (replace_id) { + if ((refer_pvt = get_sip_pvt_byid_locked(replace_id, req, NULL, p->theirtag))) { + if (refer_pvt->owner->_state == AST_STATE_RINGING) { + transmit_response(p, "100 Trying", req); + ast_mutex_unlock(&refer_pvt->owner->lock); + ast_mutex_unlock(&refer_pvt->lock); + ast_channel_masquerade(refer_pvt->owner, c ); + ast_hangup(c); + c = refer_pvt->owner; + if ((f = ast_read(c))) { + if (option_debug > 1) + ast_log(LOG_DEBUG, "SIP Call Replacement (pickup) successful for CallID: %s\n", p_replaces); + ast_frfree(f); + ast_setstate(c, AST_STATE_UP); + } else { + ast_log(LOG_WARNING, "SIP Call Replacement (pickup) not successful for CallID: %s\n", p_replaces); + transmit_response_with_allow(p, "403 Call Can Not Be Read From", req, 0); /* Don't know if this is the correct answer, but seems to work */ + return 0; + } + } else { + ast_mutex_unlock(&refer_pvt->owner->lock); + ast_mutex_unlock(&refer_pvt->lock); + ast_hangup(c); + ast_log(LOG_WARNING, "SIP Call Replacement (pickup) not possible. Call already answered\n"); + transmit_response_with_allow(p, "403 Call Already Answered Or Hung Up", req, 0); /* Don't know if this is the correct answer, but seems to work */ + return 0; + } + } else { + transmit_response_with_allow(p, "481 Call/Transaction Does Not Exist", req, 0); + return 0; + } + } /* Pre-lock the call */ ast_mutex_lock(&c->lock); } @@ -9273,16 +9507,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); } else { - transmit_response(p, "405 Method Not Allowed", req); - ast_set_flag(p, SIP_NEEDDESTROY); + transmit_response(p, "202 Accepted", req); } return 1; } @@ -9318,7 +9548,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); @@ -9327,15 +9559,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 @@ -9346,9 +9597,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++; } @@ -9361,8 +9611,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); } @@ -9378,10 +9634,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) { @@ -9399,8 +9655,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; @@ -9738,7 +9998,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; } @@ -10013,13 +10273,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; @@ -10349,6 +10631,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,'='))) { @@ -10433,6 +10717,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); @@ -10474,9 +10759,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 @@ -10507,6 +10792,8 @@ 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); ast_copy_flags(peer, &global_flags, SIP_USEREQPHONE); @@ -10556,7 +10843,9 @@ 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, "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); @@ -10645,6 +10934,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")) { @@ -10749,6 +11040,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; @@ -10758,6 +11050,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)); @@ -10780,6 +11073,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'; @@ -10826,6 +11120,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); @@ -10847,6 +11143,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 18 Jul 2005 18:01:16 -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.49 diff -u -r1.49 pbx.h --- include/asterisk/pbx.h 15 Jul 2005 23:24:51 -0000 1.49 +++ include/asterisk/pbx.h 18 Jul 2005 18:01:23 -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;