Skip to content
Snippets Groups Projects
chan_sip.c 699 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	return send_request(p, &req, init ? XMIT_CRITICAL : XMIT_RELIABLE, p->ocseq);
    
    /*! \brief Used in the SUBSCRIBE notification subsystem */
    
    static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout)
    
    	struct ast_str *tmp = ast_str_alloca(4000);
    	char from[256], to[256];
    	char *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));
    
    
    	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 = "terminated";
    
    		local_state = NOTIFY_CLOSED;
    		pidfstate = "away";
    		pidfnote = "Unavailable";
    		break;
    
    		statestring = "confirmed";
    		local_state = NOTIFY_INUSE;
    		pidfstate = "busy";
    		pidfnote = "On hold";
    
    	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)) {
    
    		char *hint2 = hint, *individual_hint = NULL;
    
    		int hint_count = 0, unavailable_count = 0;
    
    
    		while ((individual_hint = strsep(&hint2, "&"))) {
    
    			hint_count++;
    
    			if (ast_device_state(individual_hint) == AST_DEVICE_UNAVAILABLE)
    				unavailable_count++;
    		}
    
    		/* If none of the hinted devices are registered, we will
    		 * override notification and show no availability.
    		 */
    		if (hint_count > 0 && hint_count == unavailable_count) {
    			local_state = NOTIFY_CLOSED;
    			pidfstate = "away";
    			pidfnote = "Not online";
    
    	ast_copy_string(from, get_header(&p->initreq, "From"), sizeof(from));
    
    	if (strncasecmp(c, "sip:", 4)) {
    
    		ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", c);
    		return -1;
    	}
    
    	ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to));
    	c = get_in_brackets(to);
    
    	if (strncasecmp(c, "sip:", 4)) {
    
    		ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", c);
    		return -1;
    	}
    
    	
    	add_header(&req, "Event", subscriptiontype->event);
    	add_header(&req, "Content-Type", subscriptiontype->mediatype);
    	switch(state) {
    	case AST_EXTENSION_DEACTIVATED:
    
    			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_str_append(&tmp, 0,
    			"<?xml version=\"1.0\"?>\n"
    			"<!DOCTYPE presence PUBLIC \"-//IETF//DTD RFCxxxx XPIDF 1.0//EN\" \"xpidf.dtd\">\n"
    			"<presence>\n");
    		ast_str_append(&tmp, 0, "<presentity uri=\"%s;method=SUBSCRIBE\" />\n", mfrom);
    		ast_str_append(&tmp, 0, "<atom id=\"%s\">\n", p->exten);
    		ast_str_append(&tmp, 0, "<address uri=\"%s;user=ip\" priority=\"0.800000\">\n", mto);
    		ast_str_append(&tmp, 0, "<status status=\"%s\" />\n", (local_state ==  NOTIFY_OPEN) ? "open" : (local_state == NOTIFY_INUSE) ? "inuse" : "closed");
    		ast_str_append(&tmp, 0, "<msnsubstatus substatus=\"%s\" />\n", (local_state == NOTIFY_OPEN) ? "online" : (local_state == NOTIFY_INUSE) ? "onthephone" : "offline");
    		ast_str_append(&tmp, 0, "</address>\n</atom>\n</presence>\n");
    
    		break;
    	case PIDF_XML: /* Eyebeam supports this format */
    
    		ast_str_append(&tmp, 0,
    			"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
    			"<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_str_append(&tmp, 0, "<pp:person><status>\n");
    
    			ast_str_append(&tmp, 0, "<ep:activities><ep:%s/></ep:activities>\n", pidfstate);
    		ast_str_append(&tmp, 0, "</status></pp:person>\n");
    		ast_str_append(&tmp, 0, "<note>%s</note>\n", pidfnote); /* Note */
    		ast_str_append(&tmp, 0, "<tuple id=\"%s\">\n", p->exten); /* Tuple start */
    		ast_str_append(&tmp, 0, "<contact priority=\"1\">%s</contact>\n", mto);
    
    		if (pidfstate[0] == 'b') /* Busy? Still open ... */
    
    			ast_str_append(&tmp, 0, "<status><basic>open</basic></status>\n");
    
    			ast_str_append(&tmp, 0, "<status><basic>%s</basic></status>\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed");
    		ast_str_append(&tmp, 0, "</tuple>\n</presence>\n");
    
    		break;
    	case DIALOG_INFO_XML: /* SNOM subscribes in this format */
    
    		ast_str_append(&tmp, 0, "<?xml version=\"1.0\"?>\n");
    		ast_str_append(&tmp, 0, "<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_str_append(&tmp, 0, "<dialog id=\"%s\" direction=\"recipient\">\n", p->exten);
    
    			ast_str_append(&tmp, 0, "<dialog id=\"%s\">\n", p->exten);
    		ast_str_append(&tmp, 0, "<state>%s</state>\n", statestring);
    
    		if (state == AST_EXTENSION_ONHOLD) {
    
    			ast_str_append(&tmp, 0, "<local>\n<target uri=\"%s\">\n"
    
    			                                "<param pname=\"+sip.rendering\" pvalue=\"no\">\n"
    			                                "</target>\n</local>\n", mto);
    		}
    
    		ast_str_append(&tmp, 0, "</dialog>\n</dialog-info>\n");
    
    	add_header_contentLength(&req, tmp->used);
    	add_line(&req, tmp->str);
    
    	return send_request(p, &req, XMIT_RELIABLE, 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;
    
    	struct ast_str *out = ast_str_alloca(500);
    
    	initreqprep(&req, p, SIP_NOTIFY);
    
    	add_header(&req, "Event", "message-summary");
    
    	add_header(&req, "Content-Type", default_notifymime);
    
    	ast_str_append(&out, 0, "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no");
    	ast_str_append(&out, 0, "Message-Account: sip:%s@%s\r\n",
    
    		S_OR(vmexten, default_vmexten), S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)));
    
    	/* Cisco has a bug in the SIP stack where it can't accept the
    		(0/0) notification. This can temporarily be disabled in
    
    Olle Johansson's avatar
    Olle Johansson committed
    		sip.conf with the "buggymwi" option */
    
    	ast_str_append(&out, 0, "Voice-Message: %d/%d%s\r\n",
    		newmsgs, oldmsgs, (ast_test_flag(&p->flags[1], SIP_PAGE2_BUGGY_MWI) ? "" : " (0/0)"));
    
    	if (p->subscribed) {
    		if (p->expiry)
    			add_header(&req, "Subscription-State", "active");
    		else	/* Expired */
    			add_header(&req, "Subscription-State", "terminated;reason=timeout");
    	}
    
    	add_header_contentLength(&req, out->used);
    	add_line(&req, out->str);
    
    	if (!p->initreq.headers) 
    		initialize_initreq(p, &req);
    
    	return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Transmit SIP request unreliably (only used in sip_notify subsystem) */
    
    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, XMIT_UNRELIABLE, 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);
    
    	p->lastnoninvite = p->ocseq;
    
    
    	return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
    
    static const struct _map_x_s regstatestrings[] = {
    	{ REG_STATE_FAILED,     "Failed" },
    	{ REG_STATE_UNREGISTERED, "Unregistered"},
    	{ REG_STATE_REGSENT, "Request Sent"},
    	{ REG_STATE_AUTHSENT, "Auth. Sent"},
    	{ REG_STATE_REGISTERED, "Registered"},
    	{ REG_STATE_REJECTED, "Rejected"},
    	{ REG_STATE_TIMEOUT, "Timeout"},
    	{ REG_STATE_NOAUTH, "No Authentication"},
    	{ -1, NULL } /* terminator */
    };
    
    
    /*! \brief Convert registration state status to string */
    
    static const char *regstate2str(enum sipregistrystate regstate)
    {
    	return map_x_s(regstatestrings, regstate, "Unknown");
    
    /*! \brief Update registration with SIP Proxy.
     * Called from the scheduler when the previous registration expires,
     * so we don't have to cancel the pending event.
     * We assume the reference so the sip_registry is valid, since it
     * is stored in the scheduled event anyways.
     */
    
    static int sip_reregister(const void *data) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* if we are here, we know that we need to reregister. */
    
    	struct sip_registry *r= registry_addref((struct sip_registry *) data);
    
    
    	/* if we couldn't get a reference to the registry object, punt */
    	if (!r)
    		return 0;
    
    	if (r->call && r->call->do_history)
    
    		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);
    
    /*! \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
     * Registered as a timeout handler during transmit_register(),
     * to retransmit the packet if a reply does not come back.
     * This is called by the scheduler so the event is not pending anymore when
     * we are called.
     */
    
    static int sip_reg_timeout(const void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    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 = registry_addref((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); 
    
    	/* If the initial tranmission failed, we may not have an existing dialog,
    	 * so it is possible that r->call == NULL.
    	 * Otherwise destroy it, as we have a timeout so we don't want it.
    	 */
    
    		/* Unlink us, destroy old call.  Locking is not relevant here because all this happens
    
    		   in the single SIP manager thread. */
    		p = r->call;
    
    		/* Pretend to ACK anything just in case */
    
    		__sip_pretend_ack(p); /* XXX we need p locked, not sure we have */
    
    
    		/* decouple the two objects */
    		/* p->registry == r, so r has 2 refs, and the unref won't take the object away */
    		if (p->registry)
    			p->registry = registry_unref(p->registry);
    		r->call = dialog_unref(r->call);
    
    	/* 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", "ChannelType: SIP\r\nUsername: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate));
    
    /*! \brief Transmit register to SIP proxy or UA
     * auth = NULL on the initial registration (from sip_reregister())
     */
    
    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_set(p, theirtag, NULL);	/* 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, internip.sin_addr, default_fromdomain);
    
    			r->callid_valid = TRUE;
    
    		/* Allocate SIP dialog for registration */
    
    		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;
    		}
    
    			append_history(p, "RegistryInit", "Account: %s@%s", r->username, r->hostname);
    
    
    		p->outboundproxy = obproxy_get(p, NULL);
    
    
    			/* we have what we hope is a temporary network error,
    			 * probably DNS.  We need to reschedule a registration try */
    
    				r->timeout = ast_sched_replace(r->timeout, 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);
    
    		if (r->portno) {
    
    			p->sa.sin_port = htons(r->portno);
    
    			p->recv.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 */
    
    		r->call = dialog_ref(p);		/* Save pointer to SIP dialog */
    
    		p->registry = registry_addref(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);
    
    		if (!ast_strlen_zero(r->callback))
    			ast_string_field_set(p, exten, r->callback);
    
    
    		/*
    		  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
    		 */
    
    		ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
    
    		build_contact(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* set up a timeout */
    
    			ast_log(LOG_WARNING, "Still have a registration timeout, #%d - deleting it\n", r->timeout);
    
    		r->timeout = ast_sched_replace(r->timeout, sched, global_reg_timeout * 1000, sip_reg_timeout, r);
    
    		ast_debug(1, "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 */
    
    	if (!ast_strlen_zero(p->fromdomain)) {
    		if (r->portno && r->portno != STANDARD_SIP_PORT)
    			snprintf(addr, sizeof(addr), "sip:%s:%d", p->fromdomain, r->portno);
    		else
    			snprintf(addr, sizeof(addr), "sip:%s", p->fromdomain);
    	} else {
    		if (r->portno && r->portno != STANDARD_SIP_PORT)
    			snprintf(addr, sizeof(addr), "sip:%s:%d", r->hostname, r->portno);
    		else
    			snprintf(addr, sizeof(addr), "sip:%s", 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);
    
    	add_header(&req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
    
    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);
    
    	if (!ast_strlen_zero(global_useragent))
    		add_header(&req, "User-Agent", global_useragent);
    
    		add_header(&req, authheader, auth);
    
    	else if (!ast_strlen_zero(r->nonce)) {
    
    		/* We have auth data to reuse, build a digest header.
    		 * Note, this is not always useful because some parties do not
    		 * like nonces to be reused (for good reasons!) so they will
    		 * challenge us anyways.
    		 */
    
    			ast_debug(1, "   >>> 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);
    
    	snprintf(tmp, sizeof(tmp), "%d", r->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);
    
    	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;
    
    	ast_debug(4, "REGISTER attempt %d to %s@%s\n", r->regattempts, r->username, r->hostname);
    
    	return send_request(p, &req, XMIT_CRITICAL, p->ocseq);
    
    /*! \brief Transmit text with SIP MESSAGE method */
    
    static int transmit_message_with_text(struct sip_pvt *p, const char *text)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request req;
    
    	reqprep(&req, p, SIP_MESSAGE, 0, 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_text(&req, text);
    
    	return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
    
    /*! \brief Allocate SIP refer structure */
    
    static int sip_refer_allocate(struct sip_pvt *p)
    {
    
    	p->refer = ast_calloc(1, sizeof(struct sip_refer)); 
    	return p->refer ? 1 : 0;
    
    /*! \brief Transmit SIP REFER message (initiated by the transfer() dialplan application
    	\note this is currently broken as we have no way of telling the dialplan
    	engine whether a transfer succeeds or fails.
    	\todo Fix the transfer() dialplan function so that a transfer may fail
    */
    
    static int transmit_refer(struct sip_pvt *p, const char *dest)
    
    	char from[256];
    
    	char referto[256];
    
    	char *ttag, *ftag;
    	char *theirtag = ast_strdupa(p->theirtag);
    
    	if (sipdebug)
    		ast_debug(1, "SIP transfer of %s to %s\n", p->callid, dest);
    
    
    	/* Are we transfering an inbound or outbound call ? */
    	if (ast_test_flag(&p->flags[0], SIP_OUTGOING))  {
    
    		of = get_header(&p->initreq, "To");
    
    		ttag = theirtag;
    		ftag = p->tag;
    	} else {
    
    		of = get_header(&p->initreq, "From");
    
    	ast_string_field_set(p, from, of);
    
    	if (strncasecmp(of, "sip:", 4))
    
    		ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
    
    		of += 4;
    	/* Get just the username part */
    
    	if ((c = strchr(dest, '@')))
    
    	else if ((c = strchr(of, '@')))
    		*c++ = '\0';
    
    		snprintf(referto, sizeof(referto), "<sip:%s@%s>", dest, c);
    
    		snprintf(referto, sizeof(referto), "<sip:%s>", dest);
    
    
    	/* save in case we get 407 challenge */
    
    	sip_refer_allocate(p);
    	ast_copy_string(p->refer->refer_to, referto, sizeof(p->refer->refer_to));
    	ast_copy_string(p->refer->referred_by, p->our_contact, sizeof(p->refer->referred_by));
    	p->refer->status = REFER_SENT;   /* Set refer status */
    
    	reqprep(&req, p, SIP_REFER, 0, 1);
    
    	add_header(&req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
    
    
    	add_header(&req, "Refer-To", referto);
    
    	add_header(&req, "Allow", ALLOWED_METHODS);
    	add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
    
    	if (!ast_strlen_zero(p->our_contact))
    		add_header(&req, "Referred-By", p->our_contact);
    
    	return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
    
    	/* We should propably wait for a NOTIFY here until we ack the transfer */
    	/* Maybe fork a new thread and wait for a STATUS of REFER_200OK on the refer status before returning to app_transfer */
    
    
    	/*! \todo In theory, we should hang around and wait for a reply, before
    	returning to the dial plan here. Don't know really how that would
    	affect the transfer() app or the pbx, but, well, to make this
    	useful we should have a STATUS code on transfer().
    	*/
    
    /*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */
    
    static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration)
    
    {
    	struct sip_request req;
    
    	reqprep(&req, p, SIP_INFO, 0, 1);
    
    	add_digit(&req, digit, duration, (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_SHORTINFO));
    
    	return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
    
    /*! \brief Send SIP INFO with video update request */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int transmit_info_with_vidupdate(struct sip_pvt *p)
    {
    	struct sip_request req;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	reqprep(&req, p, SIP_INFO, 0, 1);
    	add_vidupdate(&req);
    
    	return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
    
    	returns XMIT_ERROR if transmit failed with a critical error (don't retry)
    
    static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    
    	reqprep(&resp, p, sipmethod, seqno, newbranch);
    
    	if (sipmethod == SIP_CANCEL && p->answered_elsewhere) 
    
    		add_header(&resp, "Reason:", "SIP;cause=200;text=\"Call completed elsewhere\"");
    
    
    	add_header_contentLength(&resp, 0);
    
    	return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
    
    /*! \brief return the request and response heade for a 401 or 407 code */
    static void auth_headers(enum sip_auth_type code, char **header, char **respheader)
    {
    	if (code == WWW_AUTH) {			/* 401 */
    		*header = "WWW-Authenticate";
    		*respheader = "Authorization";
    	} else if (code == PROXY_AUTH) {	/* 407 */
    		*header = "Proxy-Authenticate";
    		*respheader = "Proxy-Authorization";
    	} else {
    		ast_verbose("-- wrong response code %d\n", code);
    		*header = *respheader = "Invalid";
    	}
    }
    
    
    /*! \brief Transmit SIP request, auth added */
    
    static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
    
    {
    	struct sip_request resp;
    
    
    	reqprep(&resp, p, sipmethod, seqno, newbranch);
    
    	if (!ast_strlen_zero(p->realm)) {
    
    		char digest[1024];
    
    		memset(digest, 0, sizeof(digest));
    
    		if(!build_reply_digest(p, sipmethod, digest, sizeof(digest))) {
    
    			char *dummy, *response;
    			enum sip_auth_type code = p->options ? p->options->auth_type : PROXY_AUTH; /* XXX force 407 if unknown */
    			auth_headers(code, &dummy, &response);
    			add_header(&resp, response, digest);
    
    			ast_log(LOG_WARNING, "No authentication available for call %s\n", p->callid);
    
    	/* If we are hanging up and know a cause for that, send it in clear text to make
    		debugging easier. */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (sipmethod == SIP_BYE && p->owner && p->owner->hangupcause)	{
    		char buf[10];
    
    		add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
    		snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
    		add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
    
    	add_header_contentLength(&resp, 0);
    
    	return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);	
    
    /*! \brief Remove registration data from realtime database or AST/DB when registration expires */
    
    static void destroy_association(struct sip_peer *peer)
    {
    
    	int realtimeregs = ast_check_realtime("sipregs");
    	char *tablename = (realtimeregs) ? "sipregs" : "sippeers";
    
    
    	if (!sip_cfg.ignore_regexpire) {
    
    		if (peer->rt_fromcontact)
    
    			ast_update_realtime(tablename, "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", "defaultuser", "", "regserver", "", NULL);
    
    /*! \brief Expire registration of SIP peer */
    
    static int expire_register(const void *data)
    
    	struct sip_peer *peer = (struct sip_peer *)data;
    
    	
    	if (!peer)		/* Hmmm. We have no peer. Weird. */
    		return 0;
    
    
    	memset(&peer->addr, 0, sizeof(peer->addr));
    
    	destroy_association(peer);	/* remove registration data from storage */
    
    	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
    
    	register_peer_exten(peer, FALSE);	/* Remove regexten */
    
    	peer->expire = -1;
    	ast_device_state_changed("SIP/%s", peer->name);
    
    
    	/* Do we need to release this peer from memory? 
    		Only for realtime peers and autocreated peers
    	*/
    
    		ast_debug(3,"-REALTIME- peer expired registration. Name: %s. Realtime peer objects now %d\n", peer->name, rpeerobjs);
    
    	if (peer->selfdestruct ||
    
    	    ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
    
    		peer = ASTOBJ_CONTAINER_UNLINK(&peerl, peer);	/* Remove from peer list */
    
    		unref_peer(peer);		/* Remove from memory */
    
    /*! \brief Poke peer (send qualify to check if peer is alive and well) */
    
    static int sip_poke_peer_s(const void *data)
    
    	struct sip_peer *peer = (struct sip_peer *)data;
    
    	peer->pokeexpire = -1;
    	sip_poke_peer(peer);
    	return 0;
    }
    
    
    /*! \brief Get registration details from Asterisk DB */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    static void reg_source_db(struct sip_peer *peer)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char data[256];
    
    	struct in_addr in;
    	int expiry;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	int port;
    	char *scan, *addr, *port_str, *expiry_str, *username, *contact;
    
    
    	if (peer->rt_fromcontact) 
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (ast_db_get("SIP/Registry", peer->name, data, sizeof(data)))
    		return;
    
    	scan = data;
    	addr = strsep(&scan, ":");
    	port_str = strsep(&scan, ":");
    	expiry_str = strsep(&scan, ":");
    	username = strsep(&scan, ":");
    
    	contact = scan;	/* Contact include sip: and has to be the last part of the database entry as long as we use : as a separator */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    	if (!inet_aton(addr, &in))
    		return;
    
    	if (port_str)
    		port = atoi(port_str);
    	else
    		return;
    
    	if (expiry_str)
    		expiry = atoi(expiry_str);
    	else
    		return;
    
    	if (username)
    
    		ast_copy_string(peer->username, username, sizeof(peer->username));
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (contact)
    
    		ast_copy_string(peer->fullcontact, contact, sizeof(peer->fullcontact));
    
    	ast_debug(2, "SIP Seeding peer from astdb: '%s' at %s@%s:%d for %d\n",
    	    peer->name, peer->username, ast_inet_ntoa(in), port, expiry);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    	memset(&peer->addr, 0, sizeof(peer->addr));
    	peer->addr.sin_family = AF_INET;
    	peer->addr.sin_addr = in;
    	peer->addr.sin_port = htons(port);
    	if (sipsock < 0) {
    		/* SIP isn't up yet, so schedule a poke only, pretty soon */
    
    		peer->pokeexpire = ast_sched_replace(peer->pokeexpire, sched, 
    			ast_random() % 5000 + 1, sip_poke_peer_s, peer);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	} else
    		sip_poke_peer(peer);
    
    	peer->expire = ast_sched_replace(peer->expire, sched, 
    		(expiry + 10) * 1000, expire_register, peer);
    
    /*! \brief Save contact header for 200 OK on INVITE */
    
    static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req)
    {
    
    	char contact[BUFSIZ]; 
    
    
    	/* Look for brackets */
    
    	ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
    
    	c = get_in_brackets(contact);
    
    
    	/* Save full contact to call pvt for later bye or re-invite */
    
    	ast_string_field_set(pvt, fullcontact, c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Save URI for later ACKs, BYE or RE-invites */
    
    	ast_string_field_set(pvt, okcontacturi, c);
    
    
    	/* We should return false for URI:s we can't handle,
    		like sips:, tel:, mailto:,ldap: etc */
    	return TRUE;		
    }
    
    /*! \brief Change the other partys IP address based on given contact */
    static int set_address_from_contact(struct sip_pvt *pvt)
    {
    	struct hostent *hp;
    	struct ast_hostent ahp;
    	int port;
    
    	char *contact;
    
    
    	if (ast_test_flag(&pvt->flags[0], SIP_NAT_ROUTE)) {
    		/* NAT: Don't trust the contact field.  Just use what they came to us
    		   with. */
    		pvt->sa = pvt->recv;
    		return 0;
    	}
    
    
    	/* Work on a copy */
    	contact = ast_strdupa(pvt->fullcontact);
    
    	/* We have only the part in <brackets> here so we just need to parse a SIP URI.*/
    
    	if (parse_uri(contact, "sip:", &contact, NULL, &host, &pt, NULL))
    
    		ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact);
    
    	port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_SIP_PORT;
    	ast_verbose("--- set_address_from_contact host '%s'\n", host);
    
    	/* XXX This could block for a long time XXX */
    	/* We should only do this if it's a name, not an IP */
    	hp = ast_gethostbyname(host, &ahp);
    	if (!hp)  {
    		ast_log(LOG_WARNING, "Invalid host name in Contact: (can't resolve in DNS) : '%s'\n", host);
    		return -1;
    
    	pvt->sa.sin_family = AF_INET;
    	memcpy(&pvt->sa.sin_addr, hp->h_addr, sizeof(pvt->sa.sin_addr));
    	pvt->sa.sin_port = htons(port);
    
    
    /*! \brief Parse contact header and save registration (peer registration) */
    static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *peer, struct sip_request *req)
    
    	char contact[BUFSIZ]; 
    	char data[BUFSIZ];
    
    	const char *expires = get_header(req, "Expires");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int expiry = atoi(expires);
    
    	char *curi, *host, *pt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int port;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct hostent *hp;
    
    	struct ast_hostent ahp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sockaddr_in oldsin;
    
    	ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
    
    
    	if (ast_strlen_zero(expires)) {	/* No expires header, try look in Contact: */
    		char *s = strcasestr(contact, ";expires=");
    		if (s) {
    			expires = strsep(&s, ";"); /* trim ; and beyond */
    
    			if (sscanf(expires + 9, "%d", &expiry) != 1)
    
    				expiry = default_expiry;
    		} else {
    			/* Nothing has been specified */
    			expiry = default_expiry;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Look for brackets */
    
    	curi = contact;
    
    	if (strchr(contact, '<') == NULL)	/* No <, check for ; and strip it */
    
    		strsep(&curi, ";");	/* This is Header options, not URI options */
    	curi = get_in_brackets(contact);
    
    	/* if they did not specify Contact: or Expires:, they are querying
    	   what we currently have stored as their contact address, so return
    	   it
    	*/
    
    	if (ast_strlen_zero(curi) && ast_strlen_zero(expires)) {
    
    		/* If we have an active registration, tell them when the registration is going to expire */
    
    		if (peer->expire > -1 && !ast_strlen_zero(peer->fullcontact))
    			pvt->expiry = ast_sched_when(sched, peer->expire);
    
    		return PARSE_REGISTER_QUERY;
    
    	} else if (!strcasecmp(curi, "*") || !expiry) {	/* Unregister this peer */
    
    		/* This means remove all registrations and return OK */
    
    		memset(&peer->addr, 0, sizeof(peer->addr));
    		if (peer->expire > -1)
    			ast_sched_del(sched, peer->expire);
    		peer->expire = -1;
    
    		destroy_association(peer);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		register_peer_exten(peer, FALSE);	/* Remove extension from regexten= setting in sip.conf */
    
    		peer->fullcontact[0] = '\0';
    		peer->useragent[0] = '\0';
    		peer->sipoptions = 0;
    		peer->lastms = 0;
    
    		ast_verb(3, "Unregistered SIP '%s'\n", peer->name);
    
    			manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\n", peer->name);
    
    		return PARSE_REGISTER_UPDATE;
    
    
    	/* Store whatever we got as a contact from the client */
    	ast_copy_string(peer->fullcontact, curi, sizeof(peer->fullcontact));
    
    
    	/* For the 200 OK, we should use the received contact */
    
    	ast_string_field_build(pvt, our_contact, "<%s>", curi);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Make sure it's a SIP URL */
    
    	if (parse_uri(curi, "sip:", &curi, NULL, &host, &pt, NULL))
    		ast_log(LOG_NOTICE, "Not a valid SIP contact (missing sip:) trying to use anyway\n");
    	port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_SIP_PORT;
    
    	oldsin = peer->addr;
    	if (!ast_test_flag(&peer->flags[0], SIP_NAT_ROUTE)) {
    
    		/* XXX This could block for a long time XXX */
    
    		hp = ast_gethostbyname(host, &ahp);
    
    		if (!hp)  {
    
    			ast_log(LOG_WARNING, "Invalid host '%s'\n", host);
    
    			return PARSE_REGISTER_FAILED;
    
    		peer->addr.sin_family = AF_INET;
    		memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
    		peer->addr.sin_port = htons(port);