Index: channels/chan_sip.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v
retrieving revision 1.914
diff -u -r1.914 chan_sip.c
--- channels/chan_sip.c	17 Nov 2005 20:25:40 -0000	1.914
+++ channels/chan_sip.c	18 Nov 2005 12:47:56 -0000
@@ -147,6 +147,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
@@ -388,6 +390,7 @@
 
 /*! \brief Protect the interface list (of sip_pvt's) */
 AST_MUTEX_DEFINE_STATIC(iflock);
+AST_MUTEX_DEFINE_STATIC(replacelock);
 
 /*! \brief Protect the monitoring thread, so only one process can kill or start it, and not
    when it's doing something critical. */
@@ -920,6 +923,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 */
@@ -979,6 +983,21 @@
 	return res;
 }
 
+/* --- sip_extract_tag: extract from and to tags from a callid ---*/
+static int sip_extract_tag(char **in) 
+{
+	char *tag;
+
+	if ((tag = strcasestr(*in, "tag="))) {
+		char *ptr;
+		tag += 4;
+		if ((ptr = strchr(tag, ';')))
+			*ptr = '\0';
+		return 0;
+	}
+	return -1;
+}
+
 /*! \brief  parse_sip_options: Parse supported header in incoming packet */
 unsigned int parse_sip_options(struct sip_pvt *pvt, char *supported)
 {
@@ -1915,9 +1934,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));
@@ -1955,7 +1974,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));
@@ -4095,6 +4120,9 @@
 		add_header(req, "From", ot);
 		add_header(req, "To", of);
 	}
+//	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);
@@ -4952,6 +4980,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));
@@ -5085,11 +5114,36 @@
 	case DIALOG_INFO_XML: /* SNOM subscribes in this format */
 		ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\"?>\n");
 		ast_build_string(&t, &maxbytes, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full":"partial", mto);
-		if ((state & AST_EXTENSION_RINGING) && global_notifyringing)
-			ast_build_string(&t, &maxbytes, "<dialog id=\"%s\" direction=\"recipient\">\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, "<dialog id=\"%s\" direction=\"recipient\" call-id=\"%s\" local-tag=\"as%08x\" remote-tag=\"%s\">\n", p->exten, np->callid, np->tag, np->theirtag);
+						ast_build_string(&t, &maxbytes, "<state>%s</state>\n", statestring);
+						ast_build_string(&t, &maxbytes, "<local><identity display=\"%s\">%s</identity><target uri=\"%s\"/></local>\n", p->exten, mfrom, mfrom);
+						ast_build_string(&t, &maxbytes, "<remote><identity display=\"%s\">%s</identity><target uri=\"%s\"/></remote>\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, "<dialog id=\"%s\">\n", p->exten);
-		ast_build_string(&t, &maxbytes, "<state>%s</state>\n", statestring);
+			ast_build_string(&t, &maxbytes, "<state>%s</state>\n", statestring);
+		}
 		ast_build_string(&t, &maxbytes, "</dialog>\n</dialog-info>\n");
 		break;
 	case NONE:
@@ -6594,19 +6648,44 @@
 }
 
 /*! \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 = 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);
@@ -6627,7 +6706,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;
@@ -6672,6 +6751,8 @@
 	if (referred_by)
 		referred_by += 4;
 	
+	refer_uri = ast_strdupa(refer_to);	
+
 	if ((ptr = strchr(refer_to, '?'))) {
 		/* Search for arguments */
 		*ptr = '\0';
@@ -6679,10 +6760,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';
@@ -6720,13 +6798,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 
@@ -7007,10 +7085,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));
 		}
@@ -8578,7 +8658,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") ||
@@ -8657,7 +8740,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;
 }
 
@@ -10154,6 +10237,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;
@@ -10219,13 +10303,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) {
@@ -10242,7 +10326,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';
 		}
 	}
 
@@ -10337,7 +10434,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);
@@ -10364,8 +10461,46 @@
 			/* 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) {
+					ast_mutex_lock(&replacelock);
+					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);
+								ast_mutex_unlock(&replacelock);
+								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 */
+							ast_mutex_unlock(&replacelock);
+							return 0;
+						}
+					} else {
+						ast_hangup(c);
+						transmit_response_with_allow(p, "481 Call/Transaction Does Not Exist", req, 0);
+						ast_mutex_unlock(&replacelock);
+						return 0;
+					}
+					ast_mutex_unlock(&replacelock);
+				} else
+					ast_mutex_lock(&c->lock); /* Pre-lock the call */
 			}
 		}
 		
@@ -11007,6 +11142,10 @@
 		if (!p->lastinvite && ast_strlen_zero(p->randdata))
 			ast_set_flag(p, SIP_NEEDDESTROY);	
 		break;
+	case SIP_PUBLISH:
+		if (debug)
+			ast_log(LOG_DEBUG, "PUBLISH: from %s\n", e);
+		break;
 	default:
 		transmit_response_with_allow(p, "501 Method Not Implemented", req, 0);
 		ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n", 
