Skip to content
Snippets Groups Projects
chan_sip.c 699 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	/* Check AUDIO RTP keepalives */
    	if (dialog->lastrtptx && ast_rtp_get_rtpkeepalive(dialog->rtp) &&
    		    (t > dialog->lastrtptx + ast_rtp_get_rtpkeepalive(dialog->rtp))) {
    		/* Need to send an empty RTP packet */
    		dialog->lastrtptx = time(NULL);
    		ast_rtp_sendcng(dialog->rtp, 0);
    	}
    
    	/*! \todo Check video RTP keepalives
    
    		Do we need to move the lastrtptx to the RTP structure to have one for audio and one
    		for video? It really does belong to the RTP structure.
    	*/
    
    	/* Check AUDIO RTP timers */
    	if (dialog->lastrtprx && (ast_rtp_get_rtptimeout(dialog->rtp) || ast_rtp_get_rtpholdtimeout(dialog->rtp)) &&
    		    (t > dialog->lastrtprx + ast_rtp_get_rtptimeout(dialog->rtp))) {
    
    		/* Might be a timeout now -- see if we're on hold */
    		struct sockaddr_in sin;
    		ast_rtp_get_peer(dialog->rtp, &sin);
    		if (sin.sin_addr.s_addr || (ast_rtp_get_rtpholdtimeout(dialog->rtp) &&
    		     (t > dialog->lastrtprx + ast_rtp_get_rtpholdtimeout(dialog->rtp)))) {
    			/* Needs a hangup */
    
    			if (ast_rtp_get_rtptimeout(dialog->rtp)) {
    
    				while (dialog->owner && ast_channel_trylock(dialog->owner)) {
    					sip_pvt_unlock(dialog);
    					usleep(1);
    					sip_pvt_lock(dialog);
    				}
    
    				ast_log(LOG_NOTICE, "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n",
    					dialog->owner->name, (long) (t - dialog->lastrtprx));
    				/* Issue a softhangup */
    				ast_softhangup_nolock(dialog->owner, AST_SOFTHANGUP_DEV);
    
    				ast_channel_unlock(dialog->owner);
    				/* forget the timeouts for this call, since a hangup
    				   has already been requested and we don't want to
    				   repeatedly request hangups
    				*/
    				ast_rtp_set_rtptimeout(dialog->rtp, 0);
    				ast_rtp_set_rtpholdtimeout(dialog->rtp, 0);
    				if (dialog->vrtp) {
    					ast_rtp_set_rtptimeout(dialog->vrtp, 0);
    					ast_rtp_set_rtpholdtimeout(dialog->vrtp, 0);
    
    /*! \brief The SIP monitoring thread 
    \note	This thread monitors all the SIP sessions and peers that needs notification of mwi
    	(and thus do not have a separate thread) indefinitely 
    */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void *do_monitor(void *data)
    {
    	int res;
    
    	struct sip_pvt *dialog;
    
    	/* Add an I/O event to our SIP UDP socket */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sipsock > -1) 
    
    		sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	/* From here on out, we die whenever asked */
    	for(;;) {
    
    		/* Check for a reload request */
    		ast_mutex_lock(&sip_reload_lock);
    		reloading = sip_reloading;
    
    		sip_reloading = FALSE;
    
    		ast_mutex_unlock(&sip_reload_lock);
    		if (reloading) {
    
    			ast_verb(1, "Reloading SIP\n");
    
    			sip_do_reload(sip_reloadreason);
    
    
    			/* Change the I/O fd of our UDP socket */
    
    			if (sipsock > -1) {
    				if (sipsock_read_id)
    					sipsock_read_id = ast_io_change(io, sipsock_read_id, sipsock, NULL, 0, NULL);
    				else
    					sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
    
    			} else if (sipsock_read_id) {
    				ast_io_remove(io, sipsock_read_id);
    				sipsock_read_id = NULL;
    
    		dialoglist_lock();
    
    Mark Spencer's avatar
    Mark Spencer committed
    restartsearch:		
    
    		/* don't scan the dialogs list if it hasn't been a reasonable period
    
    		   of time since the last time we did it (when MWI is being sent, we can
    		   get back to this point every millisecond or less)
    		*/
    
    		for (dialog = dialoglist; dialog; dialog = dialog->next) {
    
    			sip_pvt_lock(dialog);
    
    			/* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */
    
    			check_rtp_timeout(dialog, t);
    
    			/* If we have sessions that needs to be destroyed, do it now */
    
    			/* Check if we have outstanding requests not responsed to or an active call
    				- if that's the case, wait with destruction */
    
    			if (dialog->needdestroy && !dialog->packets && !dialog->owner) {
    
    				sip_pvt_unlock(dialog);
    				__sip_destroy(dialog, TRUE, FALSE);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				goto restartsearch;
    			}
    
    			sip_pvt_unlock(dialog);
    
    		dialoglist_unlock();
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_testcancel();
    		/* Wait for sched or io */
    		res = ast_sched_wait(sched);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if ((res < 0) || (res > 1000))
    			res = 1000;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ast_io_wait(io, res);
    
    		if (res > 20)
    			ast_debug(1, "chan_sip: ast_io_wait ran %d all at once\n", res);
    
    		if (res >= 0)  {
    			res = ast_sched_runq(sched);
    
    			if (res >= 20)
    				ast_debug(1, "chan_sip: ast_sched_runq ran %d all at once\n", res);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Never reached */
    	return NULL;
    }
    
    
    /*! \brief Start the channel monitor thread */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int restart_monitor(void)
    {
    	/* If we're supposed to be stopped -- stay stopped */
    
    	if (monitor_thread == AST_PTHREADT_STOP)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (monitor_thread == pthread_self()) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Cannot kill myself\n");
    		return -1;
    	}
    
    	if (monitor_thread != AST_PTHREADT_NULL) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Wake up the thread */
    		pthread_kill(monitor_thread, SIGURG);
    	} else {
    		/* Start a new monitor */
    
    		if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
    			return -1;
    		}
    	}
    
    /*! \brief React to lack of answer to Qualify poke */
    
    static int sip_poke_noanswer(const void *data)
    
    	struct sip_peer *peer = (struct sip_peer *)data;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	peer->pokeexpire = -1;
    
    	if (peer->lastms > -1) {
    
    		ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE!  Last qualify: %d\n", peer->name, peer->lastms);
    
    		manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, -1);
    
    		if (global_regextenonqualify)
    			register_peer_exten(peer, FALSE);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (peer->call)
    
    		peer->call = sip_destroy(peer->call);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	peer->lastms = -1;
    
    	ast_device_state_changed("SIP/%s", peer->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Try again quickly */
    
    	peer->pokeexpire = ast_sched_replace(peer->pokeexpire, sched, 
    		DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer);
    
    /*! \brief Check availability of peer, also keep NAT open
    \note	This is done with the interval in qualify= configuration option
    	Default is 2 seconds */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_poke_peer(struct sip_peer *peer)
    {
    	struct sip_pvt *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
    		/* IF we have no IP, or this isn't to be monitored, return
    		  imeediately after clearing things out */
    
    		if (peer->pokeexpire > -1)
    			ast_sched_del(sched, peer->pokeexpire);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		peer->lastms = 0;
    		peer->pokeexpire = -1;
    		peer->call = NULL;
    		return 0;
    	}
    
    	if (peer->call) {
    
    			ast_log(LOG_NOTICE, "Still have a QUALIFY dialog active, deleting\n");
    
    		peer->call = sip_destroy(peer->call);
    
    	if (!(p = peer->call = sip_alloc(NULL, NULL, 0, SIP_OPTIONS)))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    	p->sa = peer->addr;
    	p->recv = peer->addr;
    
    	ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
    	ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    
    	/* Send OPTIONs to peer's fullcontact */
    
    	if (!ast_strlen_zero(peer->fullcontact))
    		ast_string_field_set(p, fullcontact, peer->fullcontact);
    
    	if (!ast_strlen_zero(peer->tohost))
    
    		ast_string_field_set(p, tohost, peer->tohost);
    
    	else
    		ast_string_field_set(p, tohost, ast_inet_ntoa(peer->addr.sin_addr));
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Recalculate our side, and recalculate Call ID */
    
    	ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (peer->pokeexpire > -1)
    		ast_sched_del(sched, peer->pokeexpire);
    
    	ast_set_flag(&p->flags[0], SIP_OUTGOING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #ifdef VOCAL_DATA_HACK
    
    	ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username));
    
    Mark Spencer's avatar
    Mark Spencer committed
    #else
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    	if (xmitres == XMIT_ERROR)
    
    		sip_poke_noanswer(peer);	/* Immediately unreachable, network problems */
    
    		peer->pokeexpire = ast_sched_replace(peer->pokeexpire, sched, 
    
    			peer->maxms * 2, sip_poke_noanswer, peer);
    
    /*! \brief Part of PBX channel interface
    \note
    \par	Return values:---
    
    
    	If we have qualify on and the device is not reachable, regardless of registration
    	state we return AST_DEVICE_UNAVAILABLE
    
    	For peers with call limit:
    
    		- not registered			AST_DEVICE_UNAVAILABLE
    
    		- registered, no call			AST_DEVICE_NOT_INUSE
    		- registered, active calls		AST_DEVICE_INUSE
    
    		- registered, call limit reached	AST_DEVICE_BUSY
    
    		- registered, onhold			AST_DEVICE_ONHOLD
    		- registered, ringing			AST_DEVICE_RINGING
    
    
    		- not registered			AST_DEVICE_UNAVAILABLE
    
    		- registered				AST_DEVICE_NOT_INUSE
    		- fixed IP (!dynamic)			AST_DEVICE_NOT_INUSE
    
    	
    	Peers that does not have a known call and can't be reached by OPTIONS
    		- unreachable				AST_DEVICE_UNAVAILABLE
    
    
    	If we return AST_DEVICE_UNKNOWN, the device state engine will try to find
    	out a state by walking the channel list.
    
    	The queue system (\ref app_queue.c) treats a member as "active"
    	if devicestate is != AST_DEVICE_UNAVAILBALE && != AST_DEVICE_INVALID
    
    	When placing a call to the queue member, queue system sets a member to busy if
    	!= AST_DEVICE_NOT_INUSE and != AST_DEVICE_UNKNOWN
    
    
    static int sip_devicestate(void *data)
    {
    
    	struct sip_peer *p;
    
    	int res = AST_DEVICE_INVALID;
    
    
    	/* make sure data is not null. Maybe unnecessary, but better be safe */
    	host = ast_strdupa(data ? data : "");
    
    	if ((tmp = strchr(host, '@')))
    		host = tmp + 1;
    
    	ast_debug(3, "Checking device state for peer %s\n", host);
    
    
    	if ((p = find_peer(host, NULL, 1))) {
    		if (p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) {
    			/* we have an address for the peer */
    
    		
    			/* Check status in this order
    				- Hold
    				- Ringing
    				- Busy (enforced only by call limit)
    				- Inuse (we have a call)
    				- Unreachable (qualify)
    			   If we don't find any of these state, report AST_DEVICE_NOT_INUSE
    			   for registered devices */
    
    			if (p->onHold)
    				/* First check for hold or ring states */
    				res = AST_DEVICE_ONHOLD;
    			else if (p->inRinging) {
    				if (p->inRinging == p->inUse)
    					res = AST_DEVICE_RINGING;
    
    					res = AST_DEVICE_RINGINUSE;
    			} else if (p->call_limit && (p->inUse == p->call_limit))
    				/* check call limit */
    				res = AST_DEVICE_BUSY;
    
    			else if (p->call_limit && p->busy_level && p->inUse >= p->busy_level)
    
    				/* We're forcing busy before we've reached the call limit */
    				res = AST_DEVICE_BUSY;
    
    			else if (p->call_limit && p->inUse)
    				/* Not busy, but we do have a call */
    				res = AST_DEVICE_INUSE;
    
    			else if (p->maxms && ((p->lastms > p->maxms) || (p->lastms < 0))) 
    
    				/* We don't have a call. Are we reachable at all? Requires qualify= */
    				res = AST_DEVICE_UNAVAILABLE;
    			else	/* Default reply if we're registered and have no other data */
    				res = AST_DEVICE_NOT_INUSE;
    
    		} else {
    			/* there is no address, it's unavailable */
    			res = AST_DEVICE_UNAVAILABLE;
    
    /*! \brief PBX interface function -build SIP pvt structure 
    
    	SIP calls initiated by the PBX arrive here 
    
    	SIP Dial string syntax
    		SIP/exten@host!dnid
    	or	SIP/host/exten!dnid
    	or	SIP/host!dnid
    */
    
    static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_pvt *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *tmpc = NULL;
    	char *ext, *host;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *dest = data;
    
    	int oldformat = format;
    
    	/* mask request with some set of allowed formats.
    	 * XXX this needs to be fixed.
    	 * The original code uses AST_FORMAT_AUDIO_MASK, but it is
    	 * unclear what to use here. We have global_capabilities, which is
    	 * configured from sip.conf, and sip_tech.capabilities, which is
    	 * hardwired to all audio formats.
    	 */
    	format &= AST_FORMAT_AUDIO_MASK;
    	if (!format) {
    
    		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format %s while capability is %s\n", ast_getformatname(oldformat), ast_getformatname(global_capability));
    
    		*cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;	/* Can't find codec to connect to host */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    	}
    
    	ast_debug(1, "Asked to create a SIP channel with formats: %s\n", ast_getformatname_multiple(tmp, sizeof(tmp), oldformat));
    
    	if (!(p = sip_alloc(NULL, NULL, 0, SIP_INVITE))) {
    
    		ast_log(LOG_ERROR, "Unable to build sip pvt data for '%s' (Out of memory or socket error)\n", dest);
    
    		*cause = AST_CAUSE_SWITCH_CONGESTION;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    
    	p->outgoing_call = TRUE;
    
    	if (!(p->options = ast_calloc(1, sizeof(*p->options)))) {
    		sip_destroy(p);
    		ast_log(LOG_ERROR, "Unable to build option SIP data structure - Out of memory\n");
    
    		*cause = AST_CAUSE_SWITCH_CONGESTION;
    
    	/* Save the destination, the SIP dial string */
    
    
    
    	/* Find DNID and take it away */
    	dnid = strchr(tmp, '!');
    	if (dnid != NULL) {
    		*dnid++ = '\0';
    		ast_string_field_set(p, todnid, dnid);
    	}
    
    	/* Find at sign - @ */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	host = strchr(tmp, '@');
    	if (host) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ext = tmp;
    	} else {
    
    		ext = strchr(tmp, '/');
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (ext) 
    
    			*ext++ = '\0';
    
    Olle Johansson's avatar
    Olle Johansson committed
    		host = tmp;
    
    	/* We now have 
    		host = peer name, DNS host name or DNS domain (for SRV) 
    		ext = extension (user part of URI)
    		dnid = destination of the call (applies to the To: header)
    	*/
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (create_addr(p, host)) {
    
    		ast_debug(3, "Cant create SIP call - target device not registred\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		sip_destroy(p);
    		return NULL;
    	}
    
    	if (ast_strlen_zero(p->peername) && ext)
    
    		ast_string_field_set(p, peername, ext);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Recalculate our side, and recalculate Call ID */
    
    	ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
    
    	
    	/* We have an extension to call, don't use the full contact here */
    
    	/* This to enable dialing registered peers with extension dialling,
    
    	   SIP/peername will still use the full contact 
    	 */
    
    		ast_string_field_set(p, username, ext);
    
    		ast_string_field_set(p, fullcontact, NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #if 0
    
    Mark Spencer's avatar
    Mark Spencer committed
    	printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "<none>", host);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    	p->prefcodec = oldformat;				/* Format for this call */
    
    	p->jointcapability = oldformat;
    
    	tmpc = sip_new(p, AST_STATE_DOWN, host);	/* Place the call */
    
    	if (global_callevents)
    		manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
    			"Channel: %s\r\nChanneltype: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\nPeername: %s\r\n",
    			p->owner? p->owner->name : "", "SIP", p->callid, p->fullcontact, p->peername);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!tmpc)
    		sip_destroy(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_update_use_count();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	restart_monitor();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return tmpc;
    
    /*! Parse insecure= setting in sip.conf and set flags according to setting */
    
    static void set_insecure_flags (struct ast_flags *flags, const char *value, int lineno)
    {
    
    	if (ast_strlen_zero(value))
    		return;
    
    
    	if (!ast_false(value)) {
    		char buf[64];
    		char *word, *next;
    
    		ast_copy_string(buf, value, sizeof(buf));
    		next = buf;
    		while ((word = strsep(&next, ","))) {
    			if (!strcasecmp(word, "port"))
    				ast_set_flag(&flags[0], SIP_INSECURE_PORT);
    			else if (!strcasecmp(word, "invite"))
    				ast_set_flag(&flags[0], SIP_INSECURE_INVITE);
    			else
    				ast_log(LOG_WARNING, "Unknown insecure mode '%s' on line %d\n", value, lineno);
    		}
    	}
    }
    
    
      \brief Handle flag-type options common to configuration of devices - users and peers
    
      \param flags array of two struct ast_flags
      \param mask array of two struct ast_flags
      \param v linked list of config variables to process
      \returns non-zero if any config options were handled, zero otherwise
    */
    
    static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v)
    {
    
    
    	if (!strcasecmp(v->name, "trustrpid")) {
    
    		ast_set_flag(&mask[0], SIP_TRUSTRPID);
    		ast_set2_flag(&flags[0], ast_true(v->value), SIP_TRUSTRPID);
    
    	} else if (!strcasecmp(v->name, "sendrpid")) {
    
    		ast_set_flag(&mask[0], SIP_SENDRPID);
    		ast_set2_flag(&flags[0], ast_true(v->value), SIP_SENDRPID);
    
    	} else if (!strcasecmp(v->name, "g726nonstandard")) {
    		ast_set_flag(&mask[0], SIP_G726_NONSTANDARD);
    		ast_set2_flag(&flags[0], ast_true(v->value), SIP_G726_NONSTANDARD);
    
    	} else if (!strcasecmp(v->name, "useclientcode")) {
    
    		ast_set_flag(&mask[0], SIP_USECLIENTCODE);
    		ast_set2_flag(&flags[0], ast_true(v->value), SIP_USECLIENTCODE);
    
    	} else if (!strcasecmp(v->name, "dtmfmode")) {
    
    		ast_set_flag(&mask[0], SIP_DTMF);
    		ast_clear_flag(&flags[0], SIP_DTMF);
    
    		if (!strcasecmp(v->value, "inband"))
    
    			ast_set_flag(&flags[0], SIP_DTMF_INBAND);
    
    		else if (!strcasecmp(v->value, "rfc2833"))
    
    			ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
    
    		else if (!strcasecmp(v->value, "info"))
    
    			ast_set_flag(&flags[0], SIP_DTMF_INFO);
    
    		else if (!strcasecmp(v->value, "shortinfo"))
    			ast_set_flag(&flags[0], SIP_DTMF_SHORTINFO);
    
    			ast_set_flag(&flags[0], SIP_DTMF_AUTO);
    
    		else {
    			ast_log(LOG_WARNING, "Unknown dtmf mode '%s' on line %d, using rfc2833\n", v->value, v->lineno);
    
    			ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
    
    		}
    	} else if (!strcasecmp(v->name, "nat")) {
    
    		ast_set_flag(&mask[0], SIP_NAT);
    		ast_clear_flag(&flags[0], SIP_NAT);
    
    		if (!strcasecmp(v->value, "never"))
    
    			ast_set_flag(&flags[0], SIP_NAT_NEVER);
    
    		else if (!strcasecmp(v->value, "route"))
    
    			ast_set_flag(&flags[0], SIP_NAT_ROUTE);
    
    		else if (ast_true(v->value))
    
    			ast_set_flag(&flags[0], SIP_NAT_ALWAYS);
    
    			ast_set_flag(&flags[0], SIP_NAT_RFC3581);
    
    	} else if (!strcasecmp(v->name, "canreinvite")) {
    
    		ast_set_flag(&mask[0], SIP_REINVITE);
    		ast_clear_flag(&flags[0], SIP_REINVITE);
    
    		if (ast_true(v->value)) {
    			ast_set_flag(&flags[0], SIP_CAN_REINVITE | SIP_CAN_REINVITE_NAT);
    		} else if (!ast_false(v->value)) {
    			char buf[64];
    			char *word, *next = buf;
    
    			ast_copy_string(buf, v->value, sizeof(buf));
    			while ((word = strsep(&next, ","))) {
    				if (!strcasecmp(word, "update")) {
    					ast_set_flag(&flags[0], SIP_REINVITE_UPDATE | SIP_CAN_REINVITE);
    				} else if (!strcasecmp(word, "nonat")) {
    					ast_set_flag(&flags[0], SIP_CAN_REINVITE);
    					ast_clear_flag(&flags[0], SIP_CAN_REINVITE_NAT);
    				} else {
    					ast_log(LOG_WARNING, "Unknown canreinvite mode '%s' on line %d\n", v->value, v->lineno);
    				}
    			}
    		}
    
    	} else if (!strcasecmp(v->name, "insecure")) {
    
    		ast_set_flag(&mask[0], SIP_INSECURE);
    		ast_clear_flag(&flags[0], SIP_INSECURE);
    
    		set_insecure_flags(&flags[0], v->value, v->lineno);	
    
    	} else if (!strcasecmp(v->name, "progressinband")) {
    
    		ast_set_flag(&mask[0], SIP_PROG_INBAND);
    		ast_clear_flag(&flags[0], SIP_PROG_INBAND);
    
    			ast_set_flag(&flags[0], SIP_PROG_INBAND_YES);
    
    		else if (strcasecmp(v->value, "never"))
    
    			ast_set_flag(&flags[0], SIP_PROG_INBAND_NO);
    
    	} else if (!strcasecmp(v->name, "promiscredir")) {
    
    		ast_set_flag(&mask[0], SIP_PROMISCREDIR);
    		ast_set2_flag(&flags[0], ast_true(v->value), SIP_PROMISCREDIR);
    	} else if (!strcasecmp(v->name, "videosupport")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_VIDEOSUPPORT);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_VIDEOSUPPORT);
    
    	} else if (!strcasecmp(v->name, "textsupport")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_TEXTSUPPORT);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_TEXTSUPPORT);
    		res = 1;
    
    	} else if (!strcasecmp(v->name, "allowoverlap")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_ALLOWOVERLAP);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWOVERLAP);
    	} else if (!strcasecmp(v->name, "allowsubscribe")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_ALLOWSUBSCRIBE);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWSUBSCRIBE);
    
    	} else if (!strcasecmp(v->name, "t38pt_udptl")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_UDPTL);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_UDPTL);
    
    #ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
    
    	} else if (!strcasecmp(v->name, "t38pt_rtp")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_RTP);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_RTP);
    	} else if (!strcasecmp(v->name, "t38pt_tcp")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_TCP);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_TCP);
    
    	} else if (!strcasecmp(v->name, "rfc2833compensate")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE);
    
    	} else if (!strcasecmp(v->name, "buggymwi")) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		ast_set_flag(&mask[1], SIP_PAGE2_BUGGY_MWI);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_BUGGY_MWI);
    
    /*! \brief Add SIP domain to list of domains we are responsible for */
    
    static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context)
    {
    	struct domain *d;
    
    
    		ast_log(LOG_WARNING, "Zero length domain.\n");
    		return 1;
    	}
    
    
    	if (!(d = ast_calloc(1, sizeof(*d))))
    
    		return 0;
    
    	ast_copy_string(d->domain, domain, sizeof(d->domain));
    
    
    		ast_copy_string(d->context, context, sizeof(d->context));
    
    	d->mode = mode;
    
    	AST_LIST_LOCK(&domain_list);
    	AST_LIST_INSERT_TAIL(&domain_list, d, list);
    	AST_LIST_UNLOCK(&domain_list);
    
    
    		ast_debug(1, "Added local SIP domain '%s'\n", domain);
    
    /*! \brief  check_sip_domain: Check if domain part of uri is local to our server */
    
    static int check_sip_domain(const char *domain, char *context, size_t len)
    {
    	struct domain *d;
    	int result = 0;
    
    	AST_LIST_LOCK(&domain_list);
    	AST_LIST_TRAVERSE(&domain_list, d, list) {
    		if (strcasecmp(d->domain, domain))
    			continue;
    
    		if (len && !ast_strlen_zero(d->context))
    			ast_copy_string(context, d->context, len);
    		
    		result = 1;
    		break;
    	}
    	AST_LIST_UNLOCK(&domain_list);
    
    	return result;
    }
    
    
    /*! \brief Clear our domain list (at reload) */
    
    static void clear_sip_domains(void)
    {
    	struct domain *d;
    
    	AST_LIST_LOCK(&domain_list);
    	while ((d = AST_LIST_REMOVE_HEAD(&domain_list, list)))
    
    /*! \brief Add realm authentication in list */
    
    static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, const char *configuration, int lineno)
    
    	char *username=NULL, *realm=NULL, *secret=NULL, *md5secret=NULL;
    
    	char *stringp;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	struct sip_auth *a, *b, *auth;
    
    	ast_debug(1, "Auth config ::  %s\n", configuration);
    
    	ast_copy_string(authcopy, configuration, sizeof(authcopy));
    	stringp = authcopy;
    
    	username = stringp;
    	realm = strrchr(stringp, '@');
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (realm)
    		*realm++ = '\0';
    
    	if (ast_strlen_zero(username) || ast_strlen_zero(realm)) {
    
    		ast_log(LOG_WARNING, "Format for authentication entry is user[:secret]@realm at line %d\n", lineno);
    		return authlist;
    	}
    	stringp = username;
    	username = strsep(&stringp, ":");
    	if (username) {
    		secret = strsep(&stringp, ":");
    
    		if (!secret) {
    
    			stringp = username;
    
    			md5secret = strsep(&stringp,"#");
    		}
    
    	if (!(auth = ast_calloc(1, sizeof(*auth))))
    
    
    	ast_copy_string(auth->realm, realm, sizeof(auth->realm));
    	ast_copy_string(auth->username, username, sizeof(auth->username));
    	if (secret)
    		ast_copy_string(auth->secret, secret, sizeof(auth->secret));
    	if (md5secret)
    		ast_copy_string(auth->md5secret, md5secret, sizeof(auth->md5secret));
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	/* find the end of the list */
    	for (b = NULL, a = authlist; a ; b = a, a = a->next)
    		;
    	if (b)
    		b->next = auth;	/* Add structure add end of list */
    	else
    		authlist = auth;
    
    	ast_verb(3, "Added authentication for realm %s\n", realm);
    
    /*! \brief Clear realm authentication list (at reload) */
    
    static int clear_realm_authentication(struct sip_auth *authlist)
    {
    	struct sip_auth *a = authlist;
    	struct sip_auth *b;
    
    
    	while (a) {
    		b = a;
    		a = a->next;
    
    /*! \brief Find authentication for a specific realm */
    
    static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm)
    
    	for (a = authlist; a; a = a->next) {
    		if (!strcasecmp(a->realm, realm))
    
    /*!
     * implement the servar config line
     */
    static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
    {
    	struct ast_variable *tmpvar = NULL;
    	char *varname = ast_strdupa(buf), *varval = NULL;
    	
    	if ((varval = strchr(varname,'='))) {
    		*varval++ = '\0';
    
    		if ((tmpvar = ast_variable_new(varname, varval, ""))) {
    
    			tmpvar->next = list;
    			list = tmpvar;
    		}
    	}
    	return list;
    }
    
    
    /*! \brief Initiate a SIP user structure from configuration (configuration or realtime) */
    
    static struct sip_user *build_user(const char *name, struct ast_variable *v, int realtime)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_user *user;
    	int format;
    
    	struct ast_ha *oldha = NULL;
    
    	struct ast_flags userflags[2] = {{(0)}};
    	struct ast_flags mask[2] = {{(0)}};
    
    	if (!(user = ast_calloc(1, sizeof(*user))))
    
    	suserobjs++;
    	ASTOBJ_INIT(user);
    	ast_copy_string(user->name, name, sizeof(user->name));
    	oldha = user->ha;
    	user->ha = NULL;
    
    	ast_copy_flags(&user->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
    	ast_copy_flags(&user->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    
    	user->capability = global_capability;
    
    	user->allowtransfer = global_allowtransfer;
    
    	user->maxcallbitrate = default_maxcallbitrate;
    
    	user->autoframing = global_autoframing;
    
    	/* set default context */
    	strcpy(user->context, default_context);
    	strcpy(user->language, default_language);
    
    	strcpy(user->mohinterpret, default_mohinterpret);
    	strcpy(user->mohsuggest, default_mohsuggest);
    
    		if (handle_common_options(&userflags[0], &mask[0], v))
    
    		if (!strcasecmp(v->name, "context")) {
    			ast_copy_string(user->context, v->value, sizeof(user->context));
    
    		} else if (!strcasecmp(v->name, "subscribecontext")) {
    			ast_copy_string(user->subscribecontext, v->value, sizeof(user->subscribecontext));
    
    		} else if (!strcasecmp(v->name, "setvar")) {
    
    			user->chanvars = add_var(v->value, user->chanvars);
    
    		} else if (!strcasecmp(v->name, "permit") ||
    				   !strcasecmp(v->name, "deny")) {
    
    			int ha_error = 0;
    
    			user->ha = ast_append_ha(v->name, v->value, user->ha, &ha_error);
    			if (ha_error)
    				ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
    
    		} else if (!strcasecmp(v->name, "allowtransfer")) {
    			user->allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
    
    		} else if (!strcasecmp(v->name, "secret")) {
    			ast_copy_string(user->secret, v->value, sizeof(user->secret)); 
    		} else if (!strcasecmp(v->name, "md5secret")) {
    			ast_copy_string(user->md5secret, v->value, sizeof(user->md5secret));
    		} else if (!strcasecmp(v->name, "callerid")) {
    			ast_callerid_split(v->value, user->cid_name, sizeof(user->cid_name), user->cid_num, sizeof(user->cid_num));
    
    		} else if (!strcasecmp(v->name, "fullname")) {
    			ast_copy_string(user->cid_name, v->value, sizeof(user->cid_name));
    		} else if (!strcasecmp(v->name, "cid_number")) {
    			ast_copy_string(user->cid_num, v->value, sizeof(user->cid_num));
    
    		} else if (!strcasecmp(v->name, "callgroup")) {
    			user->callgroup = ast_get_group(v->value);
    		} else if (!strcasecmp(v->name, "pickupgroup")) {
    			user->pickupgroup = ast_get_group(v->value);
    		} else if (!strcasecmp(v->name, "language")) {
    			ast_copy_string(user->language, v->value, sizeof(user->language));
    
    		} else if (!strcasecmp(v->name, "mohinterpret")) {
    
    			ast_copy_string(user->mohinterpret, v->value, sizeof(user->mohinterpret));
    		} else if (!strcasecmp(v->name, "mohsuggest")) {
    			ast_copy_string(user->mohsuggest, v->value, sizeof(user->mohsuggest));
    
    		} else if (!strcasecmp(v->name, "accountcode")) {
    			ast_copy_string(user->accountcode, v->value, sizeof(user->accountcode));
    
    		} else if (!strcasecmp(v->name, "callcounter")) {
    
    			user->call_limit = ast_true(v->value) ? 999 : 0;
    
    		} else if (!strcasecmp(v->name, "call-limit")) {
    
    			user->call_limit = atoi(v->value);
    			if (user->call_limit < 0)
    				user->call_limit = 0;
    
    		} else if (!strcasecmp(v->name, "amaflags")) {
    			format = ast_cdr_amaflags2int(v->value);
    			if (format < 0) {
    				ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
    			} else {
    				user->amaflags = format;
    			}
    		} else if (!strcasecmp(v->name, "allow")) {
    
    			int error =  ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, TRUE);
    			if (error)
    				ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
    
    		} else if (!strcasecmp(v->name, "disallow")) {
    
    			int error =  ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, FALSE);
    			if (error)
    				ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
    
    		} else if (!strcasecmp(v->name, "autoframing")) {
    			user->autoframing = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "callingpres")) {
    			user->callingpres = ast_parse_caller_presentation(v->value);
    			if (user->callingpres == -1)
    				user->callingpres = atoi(v->value);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		} else if (!strcasecmp(v->name, "maxcallbitrate")) {
    			user->maxcallbitrate = atoi(v->value);
    			if (user->maxcallbitrate < 0)
    				user->maxcallbitrate = default_maxcallbitrate;
    
    		}
    		/* We can't just report unknown options here because this may be a
    		 * type=friend entry.  All user options are valid for a peer, but not
    		 * the other way around.  */
    
    	ast_copy_flags(&user->flags[0], &userflags[0], mask[0].flags);
    	ast_copy_flags(&user->flags[1], &userflags[1], mask[1].flags);
    	if (ast_test_flag(&user->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE))
    		global_allowsubscribe = TRUE;	/* No global ban any more */
    
    	ast_free_ha(oldha);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return user;
    }
    
    
    /*! \brief Set peer defaults before configuring specific configurations */
    static void set_peer_defaults(struct sip_peer *peer)
    
    	if (peer->expire == 0) {
    		/* Don't reset expire or port time during reload 
    		   if we have an active registration 
    		*/
    		peer->expire = -1;
    		peer->pokeexpire = -1;
    
    		peer->addr.sin_port = htons(STANDARD_SIP_PORT);
    
    	ast_copy_flags(&peer->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
    	ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    
    	strcpy(peer->context, default_context);
    
    	strcpy(peer->subscribecontext, default_subscribecontext);
    
    	strcpy(peer->language, default_language);
    
    	strcpy(peer->mohinterpret, default_mohinterpret);
    	strcpy(peer->mohsuggest, default_mohsuggest);
    
    	peer->addr.sin_family = AF_INET;
    
    	peer->defaddr.sin_family = AF_INET;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	peer->maxcallbitrate = default_maxcallbitrate;
    
    	peer->rtptimeout = global_rtptimeout;
    	peer->rtpholdtimeout = global_rtpholdtimeout;
    
    	peer->rtpkeepalive = global_rtpkeepalive;
    
    	peer->allowtransfer = global_allowtransfer;
    
    	peer->autoframing = global_autoframing;
    
    	strcpy(peer->vmexten, default_vmexten);
    	peer->secret[0] = '\0';
    	peer->md5secret[0] = '\0';
    	peer->cid_num[0] = '\0';
    	peer->cid_name[0] = '\0';
    	peer->fromdomain[0] = '\0';
    	peer->fromuser[0] = '\0';
    	peer->regexten[0] = '\0';
    	peer->callgroup = 0;
    	peer->pickupgroup = 0;
    	peer->maxms = default_qualify;
    	peer->prefs = default_prefs;
    
    	peer->timer_t1 = global_t1;
    	peer->timer_b = global_timer_b;
    
    }
    
    /*! \brief Create temporary peer (used in autocreatepeer mode) */
    static struct sip_peer *temp_peer(const char *name)
    {
    	struct sip_peer *peer;
    
    	if (!(peer = ast_calloc(1, sizeof(*peer))))
    		return NULL;
    
    	apeerobjs++;
    	ASTOBJ_INIT(peer);
    	set_peer_defaults(peer);
    
    	ast_copy_string(peer->name, name, sizeof(peer->name));
    
    
    	peer->selfdestruct = TRUE;
    	peer->host_dynamic = TRUE;
    
    	reg_source_db(peer);
    
    static void add_peer_mailboxes(struct sip_peer *peer, const char *value)
    {
    	char *next, *mbox, *context;
    
    	next = ast_strdupa(value);
    
    	while ((mbox = context = strsep(&next, ","))) {
    		struct sip_mailbox *mailbox;
    
    		if (!(mailbox = ast_calloc(1, sizeof(*mailbox))))
    			continue;
    
    		strsep(&context, "@");
    		if (ast_strlen_zero(mbox)) {
    
    			continue;
    		}
    		mailbox->mailbox = ast_strdup(mbox);
    		mailbox->context = ast_strdup(context);
    
    		AST_LIST_INSERT_TAIL(&peer->mailboxes, mailbox, entry);
    	}
    }
    
    
    /*! \brief Build peer from configuration (file or realtime static/dynamic) */
    
    static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime)
    
    	struct sip_peer *peer = NULL;
    
    	struct ast_ha *oldha = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int found=0;
    
    	int format=0;		/* Ama flags */
    
    	struct ast_flags peerflags[2] = {{(0)}};
    	struct ast_flags mask[2] = {{(0)}};
    
    	if (!realtime)
    
    		/* Note we do NOT use find_peer here, to avoid realtime recursion */
    
    		/* We also use a case-sensitive comparison (unlike find_peer) so
    		   that case changes made to the peer name will be properly handled
    		   during reload