Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 38414) +++ channels/chan_sip.c (working copy) @@ -149,6 +149,8 @@ #define RTP 1 #define NO_RTP 0 +#define SIP_RETVAL_IGNORE 1 + /* Do _NOT_ make any changes to this enum, or the array following it; if you think you are doing the right thing, you are probably not doing the right thing. If you think there are changes @@ -929,6 +931,7 @@ static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to LOG_DEBUG at end of dialog, before destroying data */ static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype); static int transmit_state_notify(struct sip_pvt *p, int state, int full, int substate); +static int sip_addheader(struct ast_channel *chan, void *data); static char *gettag(struct sip_request *req, char *header, char *tagbuf, int tagbufsize); /*! \brief Definition of this channel for PBX channel registration */ @@ -1938,9 +1941,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)); @@ -1978,7 +1981,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(dialog->tohost, peer, sizeof(dialog->tohost)); memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr)); @@ -5059,6 +5068,7 @@ enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN; char *pidfstate = "--"; char *pidfnote= "Ready"; + struct sip_pvt *np = NULL; memset(from, 0, sizeof(from)); memset(to, 0, sizeof(to)); @@ -5192,11 +5202,36 @@ case DIALOG_INFO_XML: /* SNOM subscribes in this format */ ast_build_string(&t, &maxbytes, "\n"); ast_build_string(&t, &maxbytes, "\n", p->dialogver++, full ? "full":"partial", mto); - if ((state & AST_EXTENSION_RINGING) && global_notifyringing) - ast_build_string(&t, &maxbytes, "\n", p->exten); - else + + if (!ast_strlen_zero(hint)) { + /* 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)) { + struct ast_channel *chan = NULL; + int hintlen = strlen(hint); + + while ((chan = ast_channel_walk_locked(chan)) != NULL) { + if (chan->_state == AST_STATE_RINGING && !strncasecmp(chan->name, "SIP", 3) && chan->tech_pvt && !strncasecmp(chan->name, hint, hintlen)) { + np = chan->tech_pvt; + ast_build_string(&t, &maxbytes, "\n", p->exten, np->callid, np->tag, np->theirtag); + ast_build_string(&t, &maxbytes, "%s\n", statestring); + ast_build_string(&t, &maxbytes, "%s\n", p->exten, mfrom, mfrom); + ast_build_string(&t, &maxbytes, "%s\n", np->fromname, mto, mto); + + if (option_debug > 1) + ast_log(LOG_NOTICE, "Transmitting CallID in NOTIFY message - DialogID: %s CallID: %s\n", p->exten, np->callid); + + ast_mutex_unlock(&chan->lock); + break; + } + ast_mutex_unlock(&chan->lock); + } + } + } + + if (!np) { ast_build_string(&t, &maxbytes, "\n", p->exten); - ast_build_string(&t, &maxbytes, "%s\n", statestring); + ast_build_string(&t, &maxbytes, "%s\n", statestring); + } ast_build_string(&t, &maxbytes, "\n\n"); break; case NONE: @@ -6732,19 +6767,42 @@ } /*! \brief 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[128], real_fromtag[128]; + 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) { + if (!gettag(req, "To", real_totag, sizeof(real_totag))) + bzero(real_totag, sizeof(real_totag)); + if (strcmp(real_totag, totag)) + match = 0; + } + if (match && fromtag) { + if(!gettag(req, "From", real_fromtag, sizeof(real_fromtag))) + bzero(real_fromtag, sizeof(real_fromtag)); + 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); @@ -6765,7 +6823,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; @@ -6811,6 +6869,8 @@ if (referred_by) referred_by += 4; + refer_uri = ast_strdupa(refer_to); + if ((ptr = strchr(refer_to, '?'))) { /* Search for arguments */ *ptr = '\0'; @@ -6818,10 +6878,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 - */ + replaces_header = ast_strdupa(replace_callid); ast_uri_decode(replace_callid); if ((ptr = strchr(replace_callid, '%'))) *ptr = '\0'; @@ -6863,13 +6920,13 @@ 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 @@ -7155,10 +7212,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)); } @@ -8727,7 +8786,10 @@ { char buf[1024]; unsigned int event; - char *c; + char *c = NULL; + + if (sip_debug_test_pvt(p)) + ast_log(LOG_NOTICE, "Receiving INFO from %s\n", p->callid); /* Need to check the media/type */ if (!strcasecmp(get_header(req, "Content-Type"), "application/dtmf-relay") || @@ -8808,7 +8870,7 @@ /* if (get_msg_text(buf, sizeof(buf), req)) { */ ast_log(LOG_WARNING, "Unable to parse INFO message from %s. Content %s\n", p->callid, buf); - transmit_response(p, "415 Unsupported media type", req); + transmit_response(p, "501 Not Implemented", req); return; } @@ -10333,6 +10395,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; @@ -10398,13 +10461,13 @@ /*! \brief 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) { @@ -10421,7 +10484,20 @@ if (!p->lastinvite) 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, '%')) + ast_uri_decode(replace_id); + if ((ptr = strchr(replace_id, ';'))) + *ptr = '\0'; } } @@ -10522,7 +10598,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); @@ -10549,8 +10625,41 @@ /* Save Record-Route for any later requests we make on this dialogue */ build_route(p, req, 0); if (c) { - /* Pre-lock the call */ - ast_mutex_lock(&c->lock); + if (replace_id) { + if ((refer_pvt = get_sip_pvt_byid_locked(replace_id, req, NULL, p->theirtag))) { + if (refer_pvt->owner && refer_pvt->owner->_state == AST_STATE_RINGING) { + transmit_response(p, "100 Trying", req); + 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 */ + ast_mutex_unlock(&refer_pvt->owner->lock); + return 0; + } + } else { + if (refer_pvt->owner) + 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 { + ast_hangup(c); + transmit_response_with_allow(p, "481 Call/Transaction Does Not Exist", req, 0); + return 0; + } + } else + ast_mutex_lock(&c->lock); /* Pre-lock the call */ } } @@ -11248,6 +11357,14 @@ if (!p->lastinvite && ast_strlen_zero(p->randdata)) ast_set_flag(p, SIP_NEEDDESTROY); break; + case SIP_PUBLISH: + transmit_response_with_allow(p, "501 Method Not Implemented", req, 0); + if (debug) + ast_log(LOG_DEBUG, "PUBLISH: from %s\n", e); + /* If this is some new method, and we don't have a call, destroy it now */ + if (!p->initreq.headers) + ast_set_flag(p, SIP_NEEDDESTROY); + break; default: transmit_response_with_allow(p, "501 Method Not Implemented", req, 0); ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n",