Skip to content
Snippets Groups Projects
chan_sip.c 586 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*--- transmit_response_with_t38_sdp: Used for 200 OK and 183 early media ---*/
    static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans)
    {
    	struct sip_request resp;
    	int seqno;
    	
    	if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) {
    		ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
    		return -1;
    	}
    	respprep(&resp, p, msg, req);
    	if (p->udptl) {
    		ast_udptl_offered_from_local(p->udptl, 0);
    		add_t38_sdp(&resp, p);
    	} else {
    		ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid);
    	}
    	return send_response(p, &resp, retrans, seqno);
    }
    
    
    /*! \brief copy SIP request (mostly used to save request for responses) */
    
    static void copy_request(struct sip_request *dst, const struct sip_request *src)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	long offset;
    	int x;
    	offset = ((void *)dst) - ((void *)src);
    	/* First copy stuff */
    	memcpy(dst, src, sizeof(*dst));
    	/* Now fix pointer arithmetic */
    
    	for (x=0; x < src->headers; x++)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		dst->header[x] += offset;
    
    	for (x=0; x < src->lines; x++)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		dst->line[x] += offset;
    }
    
    
    /*! \brief Used for 200 OK and 183 early media */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    
    	int seqno;
    
    	if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) {
    
    		ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	respprep(&resp, p, msg, req);
    
    		ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid);
    	}
    
    	return send_response(p, &resp, reliable, seqno);
    
    /*! \brief Parse first line of incoming SIP request */
    
    static int determine_firstline_parts(struct sip_request *req) 
    
    	char *e = ast_skip_blanks(req->header[0]);	/* there shouldn't be any */
    
    	if (!*e)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    	req->rlPart1 = e;	/* method or protocol */
    	e = ast_skip_nonblanks(e);
    
    	/* Get URI or status code */
    
    	e = ast_skip_blanks(e);
    	if ( !*e )
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    	ast_trim_blanks(e);
    
    	if (!strcasecmp(req->rlPart1, "SIP/2.0") ) { /* We have a response */
    		if (strlen(e) < 3)	/* status code is 3 digits */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    
    		req->rlPart2 = e;
    	} else { /* We have a request */
    		if ( *e == '<' ) { /* XXX the spec says it must not be in <> ! */
    			ast_log(LOG_WARNING, "bogus uri in <> %s\n", e);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			e++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return -1; 
    		}
    
    		req->rlPart2 = e;	/* URI */
    
    		e = ast_skip_nonblanks(e);
    		if (*e)
    			*e++ = '\0';
    		e = ast_skip_blanks(e);
    		if (strcasecmp(e, "SIP/2.0") ) {
    			ast_log(LOG_WARNING, "Bad request protocol %s\n", e);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    	}
    	return 1;
    
    /*! \brief Transmit reinvite with SDP
    \note 	A re-invite is basically a new INVITE with the same CALL-ID and TAG as the
    
    	INVITE that opened the SIP dialogue 
    	We reinvite so that the audio stream (RTP) go directly between
    	the SIP UAs. SIP Signalling stays with * in the path.
    */
    
    static int transmit_reinvite_with_sdp(struct sip_pvt *p)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    	reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ?  SIP_UPDATE : SIP_INVITE, 0, 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(&req, "Allow", ALLOWED_METHODS);
    
    	add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
    
    		add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)");
    
    	if (recordhistory)
    
    Olle Johansson's avatar
    Olle Johansson committed
    		append_history(p, "ReInv", "Re-invite sent");
    
    	p->lastinvite = p->ocseq;
    	return send_request(p, &req, 1, p->ocseq);
    
    /*! \brief Transmit reinvite with T38 SDP 
    
           We reinvite so that the T38 processing can take place.
           SIP Signalling stays with * in the path.
    */
    static int transmit_reinvite_with_t38_sdp(struct sip_pvt *p)
    {
    	struct sip_request req;
    
    	reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ?  SIP_UPDATE : SIP_INVITE, 0, 1);
    	
    	add_header(&req, "Allow", ALLOWED_METHODS);
    	add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
    	if (sipdebug)
    		add_header(&req, "X-asterisk-info", "SIP re-invite (T38 switchover)");
    	ast_udptl_offered_from_local(p->udptl, 1);
    	add_t38_sdp(&req, p);
    	/* Use this as the basis */
    	initialize_initreq(p, &req);
    	p->lastinvite = p->ocseq;
    	return send_request(p, &req, 1, p->ocseq);
    }
    
    
    /*! \brief Check Contact: URI of SIP message */
    
    static void extract_uri(struct sip_pvt *p, struct sip_request *req)
    {
    
    	ast_copy_string(stripped, get_header(req, "Contact"), sizeof(stripped));
    
    	c = get_in_brackets(stripped);
    
    	c = strsep(&c, ";");	/* trim ; and beyond */
    
    		ast_string_field_set(p, uri, c);
    
    /*! \brief Build contact header - the contact header we send out */
    
    static void build_contact(struct sip_pvt *p)
    {
    	/* Construct Contact: header */
    
    	if (ourport != 5060)	/* Needs to be 5060, according to the RFC */
    
    		ast_string_field_build(p, our_contact, "<sip:%s%s%s:%d>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip), ourport);
    
    		ast_string_field_build(p, our_contact, "<sip:%s%s%s>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip));
    
    /*! \brief Build the Remote Party-ID & From using callingpres options */
    
    	int send_pres_tags = TRUE;
    
    	const char *privacy=NULL;
    	const char *screen=NULL;
    
    	char buf[256];
    	const char *clid = default_callerid;
    	const char *clin = NULL;
    	const char *fromdomain;
    
    
    	if (!ast_strlen_zero(p->rpid) || !ast_strlen_zero(p->rpid_from))  
    
    	if (p->owner && p->owner->cid.cid_num)
    		clid = p->owner->cid.cid_num;
    	if (p->owner && p->owner->cid.cid_name)
    		clin = p->owner->cid.cid_name;
    
    		clin = clid;
    
    	switch (p->callingpres) {
    	case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
    		privacy = "off";
    		screen = "no";
    		break;
    	case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
    		privacy = "off";
    		screen = "pass";
    		break;
    	case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
    		privacy = "off";
    		screen = "fail";
    		break;
    	case AST_PRES_ALLOWED_NETWORK_NUMBER:
    		privacy = "off";
    		screen = "yes";
    		break;
    	case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
    		privacy = "full";
    		screen = "no";
    		break;
    	case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
    		privacy = "full";
    		screen = "pass";
    		break;
    	case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
    		privacy = "full";
    		screen = "fail";
    		break;
    	case AST_PRES_PROHIB_NETWORK_NUMBER:
    		privacy = "full";
    
    		screen = "pass";
    
    		send_pres_tags = FALSE;
    
    		break;
    	default:
    		ast_log(LOG_WARNING, "Unsupported callingpres (%d)\n", p->callingpres);
    		if ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)
    			privacy = "full";
    		else
    			privacy = "off";
    		screen = "no";
    		break;
    	}
    	
    
    	fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip));
    
    
    	snprintf(buf, sizeof(buf), "\"%s\" <sip:%s@%s>", clin, clid, fromdomain);
    	if (send_pres_tags)
    		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ";privacy=%s;screen=%s", privacy, screen);
    
    	ast_string_field_set(p, rpid, buf);
    
    	ast_string_field_build(p, rpid_from, "\"%s\" <sip:%s@%s>;tag=%s", clin,
    
    			       S_OR(p->fromuser, clid),
    
    /*! \brief Initiate new SIP request to peer/user */
    
    static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod)
    
    	char invite_buf[256] = "";
    	char *invite = invite_buf;
    	size_t invite_max = sizeof(invite_buf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char from[256];
    	char to[256];
    
    	char tmp[BUFSIZ/2];
    	char tmp2[BUFSIZ/2];
    
    	const char *l = NULL, *n = NULL;
    
    	int x;
    	char urioptions[256]="";
    
    
    	if (ast_test_flag(&p->flags[0], SIP_USEREQPHONE)) {
    
    	 	char onlydigits = TRUE;
    
    		/* Test p->username against allowed characters in AST_DIGIT_ANY
    
    Olle Johansson's avatar
    Olle Johansson committed
    			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] == '+')
    
    		for (; x < strlen(p->username); x++) {
    
    			if (!strchr(AST_DIGIT_ANYNUM, p->username[x])) {
    
                    		onlydigits = FALSE;
    
    		/* If we have only digits, add ;user=phone to the uri */
    		if (onlydigits)
    			strcpy(urioptions, ";user=phone");
    
    	snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text);
    
    	if (p->owner) {
    		l = p->owner->cid.cid_num;
    		n = p->owner->cid.cid_name;
    	}
    
    	/* if we are not sending RPID and user wants his callerid restricted */
    
    	if (!ast_test_flag(&p->flags[0], SIP_SENDRPID) &&
    	    ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)) {
    
    	if (ast_strlen_zero(l))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		n = l;
    
    	if (!ast_strlen_zero(p->fromuser))
    
    	else /* Save for any further attempts */
    
    		ast_string_field_set(p, fromuser, l);
    
    
    	/* Allow user to be overridden */
    	if (!ast_strlen_zero(p->fromname))
    		n = p->fromname;
    	else /* Save for any further attempts */
    
    		ast_string_field_set(p, fromname, n);
    
    	if (pedanticsipchecking) {
    		ast_uri_encode(n, tmp, sizeof(tmp), 0);
    		n = tmp;
    		ast_uri_encode(l, tmp2, sizeof(tmp2), 0);
    		l = tmp2;
    	}
    
    
    	if ((ourport != 5060) && ast_strlen_zero(p->fromdomain))	/* Needs to be 5060 */
    
    		snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s:%d>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)), ourport, p->tag);
    
    		snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)), p->tag);
    
    	/* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */
    
    	if (!ast_strlen_zero(p->fullcontact)) {
    		/* If we have full contact, trust it */
    
    		ast_build_string(&invite, &invite_max, "%s", p->fullcontact);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    		/* Otherwise, use the username while waiting for registration */
    		ast_build_string(&invite, &invite_max, "sip:");
    		if (!ast_strlen_zero(p->username)) {
    			n = p->username;
    			if (pedanticsipchecking) {
    				ast_uri_encode(n, tmp, sizeof(tmp), 0);
    				n = tmp;
    			}
    			ast_build_string(&invite, &invite_max, "%s@", n);
    		}
    		ast_build_string(&invite, &invite_max, "%s", p->tohost);
    		if (ntohs(p->sa.sin_port) != 5060)		/* Needs to be 5060 */
    			ast_build_string(&invite, &invite_max, ":%d", ntohs(p->sa.sin_port));
    		ast_build_string(&invite, &invite_max, "%s", urioptions);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* If custom URI options have been provided, append them */
    
    	if (p->options && p->options->uri_options)
    		ast_build_string(&invite, &invite_max, ";%s", p->options->uri_options);
    
    	
    	ast_string_field_set(p, uri, invite_buf);
    
    	if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) { 
    		/* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */
    		snprintf(to, sizeof(to), "<sip:%s>;tag=%s", p->uri, p->theirtag);
    	} else if (p->options && p->options->vxml_url) {
    		/* If there is a VXML URL append it to the SIP URL */
    
    		snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
    	} else {
    		snprintf(to, sizeof(to), "<%s>", p->uri);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	init_req(req, sipmethod, p->uri);
    
    	snprintf(tmp, sizeof(tmp), "%d %s", ++p->ocseq, sip_methods[sipmethod].text);
    
    	add_header(req, "Via", p->via);
    
    	/* SLD: FIXME?: do Route: here too?  I think not cos this is the first request.
    	 * OTOH, then we won't have anything in p->route anyway */
    
    	if (ast_test_flag(&p->flags[0], SIP_SENDRPID) && (sipmethod == SIP_INVITE)) {
    
    		add_header(req, "From", p->rpid_from);
    	} else {
    		add_header(req, "From", from);
    	}
    
    	ast_string_field_set(p, exten, l);
    
    	build_contact(p);
    
    	add_header(req, "Contact", p->our_contact);
    
    	add_header(req, "Call-ID", p->callid);
    	add_header(req, "CSeq", tmp);
    
    	add_header(req, "User-Agent", global_useragent);
    
    	add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
    
    	if (!ast_strlen_zero(p->rpid))
    
    		add_header(req, "Remote-Party-ID", p->rpid);
    
    /*! \brief Build REFER/INVITE/OPTIONS message and transmit it */
    
    static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
    
    {
    	struct sip_request req;
    
    	if (init) {		/* Seems like init always is 2 */
    
    		/* Bump branch even on initial requests */
    
    		if (init > 1)
    			initreqprep(&req, p, sipmethod);
    		else
    			reqprep(&req, p, sipmethod, 0, 1);
    
    		reqprep(&req, p, sipmethod, 0, 1);
    
    	if (p->options && p->options->auth)
    		add_header(&req, p->options->authheader, p->options->auth);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	append_date(&req);
    
    	if (sipmethod == SIP_REFER) {	/* Call transfer */
    
    		if (p->refer) {
    			char buf[BUFSIZ];
    			if (!ast_strlen_zero(p->refer->refer_to))
    				add_header(&req, "Refer-To", p->refer->refer_to);
    			if (!ast_strlen_zero(p->refer->referred_by)) {
    				sprintf(buf, "%s <%s>", p->refer->referred_by_name, p->refer->referred_by);
    				add_header(&req, "Referred-By", buf);
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* This new INVITE is part of an attended transfer. Make sure that the
    	other end knows and replace the current call with this new call */
    	if (p->options && p->options->replaces && !ast_strlen_zero(p->options->replaces)) {
    		add_header(&req, "Replaces", p->options->replaces);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		add_header(&req, "Require", "replaces");
    
    	}
    
    	if (p->options && !ast_strlen_zero(p->options->distinctive_ring)) {
    
    		add_header(&req, "Alert-Info", p->options->distinctive_ring);
    
    	add_header(&req, "Allow", ALLOWED_METHODS);
    
    	add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
    
    	if (p->options && p->options->addsipheaders ) {
    
    		struct ast_channel *ast;
    
    		struct varshead *headp = NULL;
    
    
    		ast = p->owner;	/* The owner channel */
    		if (ast) {
    
    			if (!headp)
    				ast_log(LOG_WARNING,"No Headp for the channel...ooops!\n");
    			else {
    
    Olle Johansson's avatar
    Olle Johansson committed
    					/* SIPADDHEADER: Add SIP header to outgoing call */
    
    					if (!strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
    
    						char *content, *end;
    						const char *header = ast_var_value(current);
    
    
    						/* Strip of the starting " (if it's there) */
    
    						if (*headdup == '"')
    					 		headdup++;
    						if ((content = strchr(headdup, ':'))) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    							*content++ = '\0';
    							content = ast_skip_blanks(content); /* Skip white space */
    
    							/* Strip the ending " (if it's there) */
    					 		end = content + strlen(content) -1;	
    							if (*end == '"')
    
    								ast_log(LOG_DEBUG, "Adding SIP Header \"%s\" with content :%s: \n", headdup, content);
    
    	if (sdp) {
    		if (p->udptl && p->t38.state == T38_LOCAL_DIRECT) {
    			ast_udptl_offered_from_local(p->udptl, 1);
    			if (option_debug)
    				ast_log(LOG_DEBUG, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
    			add_t38_sdp(&req, p);
    		} else if (p->rtp) {
    			add_sdp(&req, p);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    		add_header_contentLength(&req, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (!p->initreq.headers)
    		initialize_initreq(p, &req);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->lastinvite = p->ocseq;
    
    	return send_request(p, &req, init ? 2 : 1, p->ocseq);
    
    /*! \brief Used in the SUBSCRIBE notification subsystem */
    
    static int transmit_state_notify(struct sip_pvt *p, int state, int full)
    
    	char tmp[4000], from[256], to[256];
    
    	char *t = tmp, *c, *mfrom, *mto;
    
    	char hint[AST_MAX_EXTENSION];
    	char *statestring = "terminated";
    	const struct cfsubscription_types *subscriptiontype;
    	enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN;
    	char *pidfstate = "--";
    	char *pidfnote= "Ready";
    
    
    	memset(from, 0, sizeof(from));
    	memset(to, 0, sizeof(to));
    	memset(tmp, 0, sizeof(tmp));
    
    
    	switch (state) {
    	case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE):
    
    		statestring = (global_notifyringing) ? "early" : "confirmed";
    
    		local_state = NOTIFY_INUSE;
    		pidfstate = "busy";
    		pidfnote = "Ringing";
    		break;
    	case AST_EXTENSION_RINGING:
    		statestring = "early";
    		local_state = NOTIFY_INUSE;
    		pidfstate = "busy";
    		pidfnote = "Ringing";
    		break;
    	case AST_EXTENSION_INUSE:
    		statestring = "confirmed";
    		local_state = NOTIFY_INUSE;
    		pidfstate = "busy";
    		pidfnote = "On the phone";
    		break;
    	case AST_EXTENSION_BUSY:
    		statestring = "confirmed";
    		local_state = NOTIFY_CLOSED;
    		pidfstate = "busy";
    		pidfnote = "On the phone";
    		break;
    	case AST_EXTENSION_UNAVAILABLE:
    		statestring = "confirmed";
    		local_state = NOTIFY_CLOSED;
    		pidfstate = "away";
    		pidfnote = "Unavailable";
    		break;
    
    	case AST_EXTENSION_NOT_INUSE:
    	default:
    		/* Default setting */
    		break;
    	}
    
    	subscriptiontype = find_subscription_type(p->subscribed);
    	
    	/* Check which device/devices we are watching  and if they are registered */
    	if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten)) {
    		/* If they are not registered, we will override notification and show no availability */
    		if (ast_device_state(hint) == AST_DEVICE_UNAVAILABLE) {
    			local_state = NOTIFY_CLOSED;
    			pidfstate = "away";
    			pidfnote = "Not online";
    		}
    	}
    
    	ast_copy_string(from, get_header(&p->initreq, "From"), sizeof(from));
    
    	if (strncmp(c, "sip:", 4)) {
    		ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", c);
    		return -1;
    	}
    
    	mfrom = strsep(&c, ";");	/* trim ; and beyond */
    
    	ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to));
    	c = get_in_brackets(to);
    	if (strncmp(c, "sip:", 4)) {
    		ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", c);
    		return -1;
    	}
    
    	mto = strsep(&c, ";");	/* trim ; and beyond */
    
    	
    	add_header(&req, "Event", subscriptiontype->event);
    	add_header(&req, "Content-Type", subscriptiontype->mediatype);
    	switch(state) {
    	case AST_EXTENSION_DEACTIVATED:
    		if (p->subscribed == TIMEOUT)
    			add_header(&req, "Subscription-State", "terminated;reason=timeout");
    		else {
    			add_header(&req, "Subscription-State", "terminated;reason=probation");
    			add_header(&req, "Retry-After", "60");
    
    		break;
    	case AST_EXTENSION_REMOVED:
    		add_header(&req, "Subscription-State", "terminated;reason=noresource");
    		break;
    	default:
    
    		if (p->expiry)
    			add_header(&req, "Subscription-State", "active");
    		else	/* Expired */
    			add_header(&req, "Subscription-State", "terminated;reason=timeout");
    
    	}
    	switch (p->subscribed) {
    	case XPIDF_XML:
    	case CPIM_PIDF_XML:
    
    		ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\"?>\n");
    		ast_build_string(&t, &maxbytes, "<!DOCTYPE presence PUBLIC \"-//IETF//DTD RFCxxxx XPIDF 1.0//EN\" \"xpidf.dtd\">\n");
    		ast_build_string(&t, &maxbytes, "<presence>\n");
    		ast_build_string(&t, &maxbytes, "<presentity uri=\"%s;method=SUBSCRIBE\" />\n", mfrom);
    		ast_build_string(&t, &maxbytes, "<atom id=\"%s\">\n", p->exten);
    		ast_build_string(&t, &maxbytes, "<address uri=\"%s;user=ip\" priority=\"0.800000\">\n", mto);
    
    		ast_build_string(&t, &maxbytes, "<status status=\"%s\" />\n", (local_state ==  NOTIFY_OPEN) ? "open" : (local_state == NOTIFY_INUSE) ? "inuse" : "closed");
    		ast_build_string(&t, &maxbytes, "<msnsubstatus substatus=\"%s\" />\n", (local_state == NOTIFY_OPEN) ? "online" : (local_state == NOTIFY_INUSE) ? "onthephone" : "offline");
    
    		ast_build_string(&t, &maxbytes, "</address>\n</atom>\n</presence>\n");
    
    		break;
    	case PIDF_XML: /* Eyebeam supports this format */
    		ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
    
    		ast_build_string(&t, &maxbytes, "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" \nxmlns:pp=\"urn:ietf:params:xml:ns:pidf:person\"\nxmlns:es=\"urn:ietf:params:xml:ns:pidf:rpid:status:rpid-status\"\nxmlns:ep=\"urn:ietf:params:xml:ns:pidf:rpid:rpid-person\"\nentity=\"%s\">\n", mfrom);
    
    		ast_build_string(&t, &maxbytes, "<pp:person><status>\n");
    		if (pidfstate[0] != '-')
    
    			ast_build_string(&t, &maxbytes, "<ep:activities><ep:%s/></ep:activities>\n", pidfstate);
    
    		ast_build_string(&t, &maxbytes, "</status></pp:person>\n");
    		ast_build_string(&t, &maxbytes, "<note>%s</note>\n", pidfnote); /* Note */
    		ast_build_string(&t, &maxbytes, "<tuple id=\"%s\">\n", p->exten); /* Tuple start */
    		ast_build_string(&t, &maxbytes, "<contact priority=\"1\">%s</contact>\n", mto);
    		if (pidfstate[0] == 'b') /* Busy? Still open ... */
    			ast_build_string(&t, &maxbytes, "<status><basic>open</basic></status>\n");
    		else
    			ast_build_string(&t, &maxbytes, "<status><basic>%s</basic></status>\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed");
    		ast_build_string(&t, &maxbytes, "</tuple>\n</presence>\n");
    		break;
    	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
    			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, "</dialog>\n</dialog-info>\n");
    		break;
    	case NONE:
    	default:
    		break;
    
    	if (t > tmp + sizeof(tmp))
    		ast_log(LOG_WARNING, "Buffer overflow detected!!  (Please file a bug report)\n");
    
    	add_header_contentLength(&req, strlen(tmp));
    
    	add_line(&req, tmp);
    
    	return send_request(p, &req, 1, p->ocseq);
    }
    
    
    /*! \brief Notify user of messages waiting in voicemail
    \note	- Notification only works for registered peers with mailbox= definitions
    	in sip.conf
    	- 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, char *vmexten)
    
    {
    	struct sip_request req;
    
    	char tmp[500];
    	char *t = tmp;
    	size_t maxbytes = sizeof(tmp);
    
    
    	initreqprep(&req, p, SIP_NOTIFY);
    
    	add_header(&req, "Event", "message-summary");
    
    	add_header(&req, "Content-Type", default_notifymime);
    
    	ast_build_string(&t, &maxbytes, "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no");
    
    	ast_build_string(&t, &maxbytes, "Message-Account: sip:%s@%s\r\n",
    
    		S_OR(vmexten, default_vmexten), S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)));
    
    	ast_build_string(&t, &maxbytes, "Voice-Message: %d/%d (0/0)\r\n", newmsgs, oldmsgs);
    
    	if (p->subscribed) {
    		if (p->expiry)
    			add_header(&req, "Subscription-State", "active");
    		else	/* Expired */
    			add_header(&req, "Subscription-State", "terminated;reason=timeout");
    	}
    
    
    	if (t > tmp + sizeof(tmp))
    		ast_log(LOG_WARNING, "Buffer overflow detected!!  (Please file a bug report)\n");
    
    	add_header_contentLength(&req, strlen(tmp));
    
    	add_line(&req, tmp);
    
    
    	if (!p->initreq.headers) 
    		initialize_initreq(p, &req);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return send_request(p, &req, 1, p->ocseq);
    
    /*! \brief Transmit SIP request */
    
    static int transmit_sip_request(struct sip_pvt *p, struct sip_request *req)
    
    	if (!p->initreq.headers) 	/* Initialize first request before sending */
    
    	return send_request(p, req, 0, p->ocseq);
    
    /*! \brief Notify a transferring party of the status of transfer */
    static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate)
    
    	reqprep(&req, p, SIP_NOTIFY, 0, 1);
    
    	snprintf(tmp, sizeof(tmp), "refer;id=%d", cseq);
    	add_header(&req, "Event", tmp);
    
    	add_header(&req, "Subscription-state", terminate ? "terminated;reason=noresource" : "active");
    
    	add_header(&req, "Content-Type", "message/sipfrag;version=2.0");
    
    	add_header(&req, "Allow", ALLOWED_METHODS);
    	add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
    
    	snprintf(tmp, sizeof(tmp), "SIP/2.0 %s\r\n", message);
    
    	add_header_contentLength(&req, strlen(tmp));
    
    	if (!p->initreq.headers)
    		initialize_initreq(p, &req);
    
    /*! \brief Convert registration state status to string */
    
    Olle Johansson's avatar
    Olle Johansson committed
    static char *regstate2str(enum sipregistrystate regstate)
    
    {
    	switch(regstate) {
    
    	case REG_STATE_FAILED:
    		return "Failed";
    
    	case REG_STATE_UNREGISTERED:
    		return "Unregistered";
    	case REG_STATE_REGSENT:
    		return "Request Sent";
    	case REG_STATE_AUTHSENT:
    		return "Auth. Sent";
    	case REG_STATE_REGISTERED:
    		return "Registered";
    	case REG_STATE_REJECTED:
    		return "Rejected";
    	case REG_STATE_TIMEOUT:
    		return "Timeout";
    	case REG_STATE_NOAUTH:
    		return "No Authentication";
    	default:
    		return "Unknown";
    	}
    }
    
    
    /*! \brief Update registration with SIP Proxy */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_reregister(void *data) 
    {
    	/* if we are here, we know that we need to reregister. */
    
    	struct sip_registry *r= ASTOBJ_REF((struct sip_registry *) data);
    
    	/* if we couldn't get a reference to the registry object, punt */
    	if (!r)
    		return 0;
    
    	if (r->call && recordhistory)
    		append_history(r->call, "RegistryRenew", "Account: %s@%s", r->username, r->hostname);
    
    	/* Since registry's are only added/removed by the the monitor thread, this
    	   may be overkill to reference/dereference at all here */
    
    		ast_log(LOG_NOTICE, "   -- Re-registration for  %s@%s\n", r->username, r->hostname);
    
    
    	__sip_do_register(r);
    
    	ASTOBJ_UNREF(r, sip_registry_destroy);
    
    /*! \brief Register with SIP proxy */
    
    static int __sip_do_register(struct sip_registry *r)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res;
    
    
    	res = transmit_register(r, SIP_REGISTER, NULL, NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*! \brief Registration timeout, register again */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_reg_timeout(void *data)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* if we are here, our registration timed out, so we'll just do it over */
    
    	struct sip_registry *r = ASTOBJ_REF((struct sip_registry *) data);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    	/* if we couldn't get a reference to the registry object, punt */
    	if (!r)
    		return 0;
    
    
    	ast_log(LOG_NOTICE, "   -- Registration for '%s@%s' timed out, trying again (Attempt #%d)\n", r->username, r->hostname, r->regattempts); 
    
    		/* Unlink us, destroy old call.  Locking is not relevant here because all this happens
    
    		   in the single SIP manager thread. */
    		p = r->call;
    
    		if (p->registry)
    			ASTOBJ_UNREF(p->registry, sip_registry_destroy);
    
    		ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    
    		/* Pretend to ACK anything just in case */
    
    		__sip_pretend_ack(p); /* XXX we need p locked, not sure we have */
    
    	/* If we have a limit, stop registration and give up */
    
    	if (global_regattempts_max && (r->regattempts > global_regattempts_max)) {
    
    		/* Ok, enough is enough. Don't try any more */
    		/* We could add an external notification here... 
    			steal it from app_voicemail :-) */
    
    		ast_log(LOG_NOTICE, "   -- Giving up forever trying to register '%s@%s'\n", r->username, r->hostname);
    
    		r->regstate = REG_STATE_FAILED;
    
    		r->regstate = REG_STATE_UNREGISTERED;
    
    		r->timeout = -1;
    		res=transmit_register(r, SIP_REGISTER, NULL, NULL);
    	}
    
    	manager_event(EVENT_FLAG_SYSTEM, "Registry", "Channel: SIP\r\nUsername: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate));
    
    	ASTOBJ_UNREF(r, sip_registry_destroy);
    
    /*! \brief Transmit register to SIP proxy or UA */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request req;
    	char from[256];
    	char to[256];
    	char tmp[80];
    	char addr[80];
    	struct sip_pvt *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* exit if we are already in process with this registrar ?*/
    
    	if ( r == NULL || ((auth==NULL) && (r->regstate==REG_STATE_REGSENT || r->regstate==REG_STATE_AUTHSENT))) {
    
    		ast_log(LOG_NOTICE, "Strange, trying to register %s@%s when registration already pending\n", r->username, r->hostname);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    
    	if (r->call) {	/* We have a registration */
    
    		if (!auth) {
    
    			ast_log(LOG_WARNING, "Already have a REGISTER going on to %s@%s?? \n", r->username, r->hostname);
    
    			return 0;
    
    			p = r->call;
    
    			make_our_tag(p->tag, sizeof(p->tag));	/* create a new local tag for every register attempt */
    
    			ast_string_field_free(p, theirtag);	/* forget their old tag, so we don't match tags when getting response */
    
    	} else {
    
    		/* Build callid for registration if we haven't registered before */
    
    		if (!r->callid_valid) {
    
    			build_callid_registry(r, __ourip, default_fromdomain);
    
    			r->callid_valid = TRUE;
    
    		if (!(p = sip_alloc( r->callid, NULL, 0, SIP_REGISTER))) {
    			ast_log(LOG_WARNING, "Unable to allocate registration transaction (memory or socket error)\n");
    
    			return 0;
    		}
    
    		if (recordhistory)
    			append_history(p, "RegistryInit", "Account: %s@%s", r->username, r->hostname);
    
    			/* we have what we hope is a temporary network error,
    			 * probably DNS.  We need to reschedule a registration try */
    
    			if (r->timeout > -1) {
    				ast_sched_del(sched, r->timeout);
    
    				r->timeout = ast_sched_add(sched, global_reg_timeout*1000, sip_reg_timeout, r);
    				ast_log(LOG_WARNING, "Still have a registration timeout for %s@%s (create_addr() error), %d\n", r->username, r->hostname, r->timeout);
    			} else {
    				r->timeout = ast_sched_add(sched, global_reg_timeout*1000, sip_reg_timeout, r);
    
    				ast_log(LOG_WARNING, "Probably a DNS error for registration to %s@%s, trying REGISTER again (after %d seconds)\n", r->username, r->hostname, global_reg_timeout);
    
    		/* Copy back Call-ID in case create_addr changed it */
    
    		ast_string_field_set(r, callid, p->callid);
    
    			p->sa.sin_port = htons(r->portno);
    
    		else 	/* Set registry port to the port set from the peer definition/srv or default */
    
    			r->portno = ntohs(p->sa.sin_port);
    
    		ast_set_flag(&p->flags[0], SIP_OUTGOING);	/* Registration is outgoing call */
    
    		p->registry = ASTOBJ_REF(r);	/* Add pointer to registry in packet */
    
    		if (!ast_strlen_zero(r->secret))	/* Secret (password) */
    
    			ast_string_field_set(p, peersecret, r->secret);
    
    		if (!ast_strlen_zero(r->md5secret))
    
    			ast_string_field_set(p, peermd5secret, r->md5secret);
    
    		/* User name in this realm  
    		- if authuser is set, use that, otherwise use username */
    		if (!ast_strlen_zero(r->authuser)) {	
    
    			ast_string_field_set(p, peername, r->authuser);
    			ast_string_field_set(p, authname, r->authuser);
    
    		} else if (!ast_strlen_zero(r->username)) {
    			ast_string_field_set(p, peername, r->username);
    			ast_string_field_set(p, authname, r->username);
    			ast_string_field_set(p, fromuser, r->username);
    
    		if (!ast_strlen_zero(r->username))
    
    			ast_string_field_set(p, username, r->username);
    
    		ast_string_field_set(p, exten, r->contact);
    
    
    		/*
    		  check which address we should use in our contact header 
    		  based on whether the remote host is on the external or
    		  internal network so we can register through nat
    		 */
    
    		if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip))
    
    			p->ourip = bindaddr.sin_addr;
    
    		build_contact(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* set up a timeout */
    
    		if (r->timeout > -1) {
    
    			ast_log(LOG_WARNING, "Still have a registration timeout, #%d - deleting it\n", r->timeout);
    
    			ast_sched_del(sched, r->timeout);
    		}
    
    		r->timeout = ast_sched_add(sched, global_reg_timeout * 1000, sip_reg_timeout, r);
    
    		ast_log(LOG_DEBUG, "Scheduled a registration timeout for %s id  #%d \n", r->hostname, r->timeout);
    
    	if (strchr(r->username, '@')) {
    
    		snprintf(from, sizeof(from), "<sip:%s>;tag=%s", r->username, p->tag);
    
    		if (!ast_strlen_zero(p->theirtag))
    			snprintf(to, sizeof(to), "<sip:%s>;tag=%s", r->username, p->theirtag);
    		else
    			snprintf(to, sizeof(to), "<sip:%s>", r->username);
    
    		snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", r->username, p->tohost, p->tag);
    
    		if (!ast_strlen_zero(p->theirtag))
    			snprintf(to, sizeof(to), "<sip:%s@%s>;tag=%s", r->username, p->tohost, p->theirtag);
    		else
    			snprintf(to, sizeof(to), "<sip:%s@%s>", r->username, p->tohost);
    
    	/* Fromdomain is what we are registering to, regardless of actual
    	   host name from SRV */
    
    	snprintf(addr, sizeof(addr), "sip:%s", S_OR(p->fromdomain, r->hostname));
    
    	ast_string_field_set(p, uri, addr);
    
    	init_req(&req, sipmethod, addr);
    
    	snprintf(tmp, sizeof(tmp), "%u %s", ++r->ocseq, sip_methods[sipmethod].text);
    
    	p->ocseq = r->ocseq;
    
    	build_via(p);
    	add_header(&req, "Via", p->via);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(&req, "From", from);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(&req, "To", to);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(&req, "Call-ID", p->callid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(&req, "CSeq", tmp);
    
    	add_header(&req, "User-Agent", global_useragent);
    
    	add_header(&req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
    
    		add_header(&req, authheader, auth);
    
    	else if (!ast_strlen_zero(r->nonce)) {
    
    		char digest[1024];
    
    		/* We have auth data to reuse, build a digest header! */
    
    			ast_log(LOG_DEBUG, "   >>> Re-using Auth data for %s@%s\n", r->username, r->hostname);
    
    		ast_string_field_set(p, realm, r->realm);
    		ast_string_field_set(p, nonce, r->nonce);
    		ast_string_field_set(p, domain, r->domain);
    		ast_string_field_set(p, opaque, r->opaque);
    		ast_string_field_set(p, qop, r->qop);
    
    		p->noncecount = r->noncecount++;
    
    		if(!build_reply_digest(p, sipmethod, digest, sizeof(digest)))
    			add_header(&req, "Authorization", digest);
    		else
    			ast_log(LOG_NOTICE, "No authorization available for authentication of registration to %s@%s\n", r->username, r->hostname);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	snprintf(tmp, sizeof(tmp), "%d", default_expiry);
    
    	add_header(&req, "Expires", tmp);
    
    	add_header(&req, "Contact", p->our_contact);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(&req, "Event", "registration");
    
    	add_header_contentLength(&req, 0);
    
    
    	initialize_initreq(p, &req);
    	if (sip_debug_test_pvt(p))
    
    		ast_verbose("REGISTER %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	r->regstate = auth ? REG_STATE_AUTHSENT : REG_STATE_REGSENT;
    
    	r->regattempts++;	/* Another attempt */
    	if (option_debug > 3)
    		ast_verbose("REGISTER attempt %d to %s@%s\n", r->regattempts, r->username, r->hostname);
    
    	return send_request(p, &req, 2, p->ocseq);
    
    /*! \brief Transmit text with SIP MESSAGE method */
    
    static int transmit_message_with_text(struct sip_pvt *p, const char *text)