Skip to content
Snippets Groups Projects
chan_sip.c 586 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	for (a = authlist; a; a = a->next) {
    		if (!strcasecmp(a->realm, realm))
    
    /*! \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;
    
    	char *varname = NULL, *varval = NULL;
    	struct ast_variable *tmpvar = 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;
    
    	/* 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")) {
    			varname = ast_strdupa(v->value);
    
    				if ((tmpvar = ast_variable_new(varname, varval))) {
    					tmpvar->next = user->chanvars;
    					user->chanvars = tmpvar;
    
    		} else if (!strcasecmp(v->name, "permit") ||
    				   !strcasecmp(v->name, "deny")) {
    			user->ha = ast_append_ha(v->name, v->value, user->ha);
    
    		} 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, "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") 
    			|| !strcasecmp(v->name, "musicclass") || !strcasecmp(v->name, "musiconhold")) {
    			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, "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")) {
    			ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 1);
    		} else if (!strcasecmp(v->name, "disallow")) {
    			ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 0);
    		} 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;
    
     		} else if (!strcasecmp(v->name, "t38pt_udptl")) {
    			if (ast_true(v->value)) {
    				ast_set_flag(&user->flags[1], SIP_PAGE2_T38SUPPORT_UDPTL);
    			} else
    				ast_clear_flag(&user->flags[1], SIP_PAGE2_T38SUPPORT_UDPTL);
    		} else if (!strcasecmp(v->name, "t38pt_rtp")) {
    			if (ast_true(v->value)) {
    				ast_set_flag(&user->flags[1], SIP_PAGE2_T38SUPPORT_RTP);
    			} else
    				ast_clear_flag(&user->flags[1], SIP_PAGE2_T38SUPPORT_RTP);
    		} else if (!strcasecmp(v->name, "t38pt_tcp")) {
    			if (ast_true(v->value)) {
    				ast_set_flag(&user->flags[1], SIP_PAGE2_T38SUPPORT_TCP);
    			} else
    				ast_clear_flag(&user->flags[1], SIP_PAGE2_T38SUPPORT_TCP);
    		}
    
    	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(DEFAULT_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;
    
    	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->mailbox[0] = '\0';
    	peer->callgroup = 0;
    	peer->pickupgroup = 0;
    	peer->maxms = default_qualify;
    	peer->prefs = default_prefs;
    }
    
    /*! \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));
    
    
    	ast_set_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT);
    	ast_set_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC);
    
    	reg_source_db(peer);
    
    /*! \brief Build peer from configuration (file or realtime static/dynamic) */
    
    static struct sip_peer *build_peer(const char *name, struct ast_variable *v, 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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *varname = NULL, *varval = NULL;
    	struct ast_variable *tmpvar = NULL;
    
    	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
    		*/
    		peer = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&peerl, name, name, 0, 0, strcmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (peer) {
    
    		/* Already in the list, remove it and it will be added back (or FREE'd)  */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		found++;
     	} else {
    
    		if (!(peer = ast_calloc(1, sizeof(*peer))))
    
    
    		if (realtime)
    			rpeerobjs++;
    		else
    			speerobjs++;
    		ASTOBJ_INIT(peer);
    
    	/* Note that our peer HAS had its reference count incrased */
    
    
    	peer->lastmsgssent = -1;
    
    	oldha = peer->ha;
    	peer->ha = NULL;
    	set_peer_defaults(peer);	/* Set peer defaults */
    
    			ast_copy_string(peer->name, name, sizeof(peer->name));
    
    	/* If we have channel variables, remove them (reload) */
    	if (peer->chanvars) {
    		ast_variables_destroy(peer->chanvars);
    		peer->chanvars = NULL;
    
    		/* XXX should unregister ? */
    
    		if (handle_common_options(&peerflags[0], &mask[0], v))
    
    			continue;
    		if (realtime && !strcasecmp(v->name, "regseconds")) {
    
    			ast_get_time_t(v->value, &regseconds, 0, NULL);
    
    		} else if (realtime && !strcasecmp(v->name, "ipaddr") && !ast_strlen_zero(v->value) ) {
    			inet_aton(v->value, &(peer->addr.sin_addr));
    		} else if (realtime && !strcasecmp(v->name, "name"))
    			ast_copy_string(peer->name, v->value, sizeof(peer->name));
    
    		else if (realtime && !strcasecmp(v->name, "fullcontact")) {
    			ast_copy_string(peer->fullcontact, v->value, sizeof(peer->fullcontact));
    
    			ast_set_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT);
    
    		} else if (!strcasecmp(v->name, "secret")) 
    
    			ast_copy_string(peer->secret, v->value, sizeof(peer->secret));
    		else if (!strcasecmp(v->name, "md5secret")) 
    			ast_copy_string(peer->md5secret, v->value, sizeof(peer->md5secret));
    		else if (!strcasecmp(v->name, "auth"))
    			peer->auth = add_realm_authentication(peer->auth, v->value, v->lineno);
    		else if (!strcasecmp(v->name, "callerid")) {
    			ast_callerid_split(v->value, peer->cid_name, sizeof(peer->cid_name), peer->cid_num, sizeof(peer->cid_num));
    
    		} else if (!strcasecmp(v->name, "context")) {
    
    			ast_copy_string(peer->context, v->value, sizeof(peer->context));
    
    		} else if (!strcasecmp(v->name, "subscribecontext")) {
    			ast_copy_string(peer->subscribecontext, v->value, sizeof(peer->subscribecontext));
    
    		} else if (!strcasecmp(v->name, "fromdomain")) {
    
    			ast_copy_string(peer->fromdomain, v->value, sizeof(peer->fromdomain));
    
    		} else if (!strcasecmp(v->name, "usereqphone")) {
    			ast_set2_flag(&peer->flags[0], ast_true(v->value), SIP_USEREQPHONE);
    		} else if (!strcasecmp(v->name, "fromuser")) {
    
    			ast_copy_string(peer->fromuser, v->value, sizeof(peer->fromuser));
    
    		} else if (!strcasecmp(v->name, "host") || !strcasecmp(v->name, "outboundproxy")) {
    
    			if (!strcasecmp(v->value, "dynamic")) {
    				if (!strcasecmp(v->name, "outboundproxy") || obproxyfound) {
    					ast_log(LOG_WARNING, "You can't have a dynamic outbound proxy, you big silly head at line %d.\n", v->lineno);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				} else {
    
    					/* They'll register with us */
    
    					ast_set_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC);
    
    					if (!found) {
    						/* Initialize stuff iff we're not found, otherwise
    						   we keep going with what we had */
    						memset(&peer->addr.sin_addr, 0, 4);
    						if (peer->addr.sin_port) {
    							/* If we've already got a port, make it the default rather than absolute */
    							peer->defaddr.sin_port = peer->addr.sin_port;
    							peer->addr.sin_port = 0;
    
    			} else {
    				/* Non-dynamic.  Make sure we become that way if we're not */
    				if (peer->expire > -1)
    					ast_sched_del(sched, peer->expire);
    				peer->expire = -1;
    
    				ast_clear_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC);
    
    				if (!obproxyfound || !strcasecmp(v->name, "outboundproxy")) {
    					if (ast_get_ip_or_srv(&peer->addr, v->value, "_sip._udp")) {
    						ASTOBJ_UNREF(peer, sip_destroy_peer);
    						return NULL;
    
    				if (!strcasecmp(v->name, "outboundproxy"))
    					obproxyfound=1;
    
    					ast_copy_string(peer->tohost, v->value, sizeof(peer->tohost));
    
    					if (!peer->addr.sin_port)
    						peer->addr.sin_port = htons(DEFAULT_SIP_PORT);
    				}
    
    			}
    		} else if (!strcasecmp(v->name, "defaultip")) {
    			if (ast_get_ip(&peer->defaddr, v->value)) {
    				ASTOBJ_UNREF(peer, sip_destroy_peer);
    				return NULL;
    			}
    		} else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
    			peer->ha = ast_append_ha(v->name, v->value, peer->ha);
    		} else if (!strcasecmp(v->name, "port")) {
    
    			if (!realtime && ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC))
    
    				peer->defaddr.sin_port = htons(atoi(v->value));
    			else
    				peer->addr.sin_port = htons(atoi(v->value));
    		} else if (!strcasecmp(v->name, "callingpres")) {
    			peer->callingpres = ast_parse_caller_presentation(v->value);
    			if (peer->callingpres == -1)
    				peer->callingpres = atoi(v->value);
    		} else if (!strcasecmp(v->name, "username")) {
    			ast_copy_string(peer->username, v->value, sizeof(peer->username));
    		} else if (!strcasecmp(v->name, "language")) {
    			ast_copy_string(peer->language, v->value, sizeof(peer->language));
    		} else if (!strcasecmp(v->name, "regexten")) {
    			ast_copy_string(peer->regexten, v->value, sizeof(peer->regexten));
    
    		} else if (!strcasecmp(v->name, "call-limit") || !strcasecmp(v->name, "incominglimit")) {
    			peer->call_limit = atoi(v->value);
    			if (peer->call_limit < 0)
    				peer->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 for peer: %s at line %d\n", v->value, v->lineno);
    			} else {
    				peer->amaflags = format;
    			}
    		} else if (!strcasecmp(v->name, "accountcode")) {
    			ast_copy_string(peer->accountcode, v->value, sizeof(peer->accountcode));
    
    		} else if (!strcasecmp(v->name, "mohinterpret")
    			|| !strcasecmp(v->name, "musicclass") || !strcasecmp(v->name, "musiconhold")) {
    			ast_copy_string(peer->mohinterpret, v->value, sizeof(peer->mohinterpret));
    		} else if (!strcasecmp(v->name, "mohsuggest")) {
    			ast_copy_string(peer->mohsuggest, v->value, sizeof(peer->mohsuggest));
    
    		} else if (!strcasecmp(v->name, "mailbox")) {
    			ast_copy_string(peer->mailbox, v->value, sizeof(peer->mailbox));
    
    		} else if (!strcasecmp(v->name, "subscribemwi")) {
    			ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_SUBSCRIBEMWIONLY);
    
    		} else if (!strcasecmp(v->name, "vmexten")) {
    			ast_copy_string(peer->vmexten, v->value, sizeof(peer->vmexten));
    
    		} else if (!strcasecmp(v->name, "callgroup")) {
    			peer->callgroup = ast_get_group(v->value);
    
    		} else if (!strcasecmp(v->name, "allowtransfer")) {
    			peer->allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
    
    		} else if (!strcasecmp(v->name, "pickupgroup")) {
    			peer->pickupgroup = ast_get_group(v->value);
    		} else if (!strcasecmp(v->name, "allow")) {
    			ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 1);
    		} else if (!strcasecmp(v->name, "disallow")) {
    			ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 0);
    		} else if (!strcasecmp(v->name, "rtptimeout")) {
    			if ((sscanf(v->value, "%d", &peer->rtptimeout) != 1) || (peer->rtptimeout < 0)) {
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d.  Using default.\n", v->value, v->lineno);
    				peer->rtptimeout = global_rtptimeout;
    			}
    		} else if (!strcasecmp(v->name, "rtpholdtimeout")) {
    			if ((sscanf(v->value, "%d", &peer->rtpholdtimeout) != 1) || (peer->rtpholdtimeout < 0)) {
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d.  Using default.\n", v->value, v->lineno);
    				peer->rtpholdtimeout = global_rtpholdtimeout;
    			}
    		} else if (!strcasecmp(v->name, "rtpkeepalive")) {
    			if ((sscanf(v->value, "%d", &peer->rtpkeepalive) != 1) || (peer->rtpkeepalive < 0)) {
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d.  Using default.\n", v->value, v->lineno);
    				peer->rtpkeepalive = global_rtpkeepalive;
    			}
    		} else if (!strcasecmp(v->name, "setvar")) {
    			/* Set peer channel variable */
    			varname = ast_strdupa(v->value);
    
    				if ((tmpvar = ast_variable_new(varname, varval))) {
    					tmpvar->next = peer->chanvars;
    					peer->chanvars = tmpvar;
    
    		} else if (!strcasecmp(v->name, "qualify")) {
    			if (!strcasecmp(v->value, "no")) {
    				peer->maxms = 0;
    			} else if (!strcasecmp(v->value, "yes")) {
    				peer->maxms = DEFAULT_MAXMS;
    			} else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
    				ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", peer->name, v->lineno);
    				peer->maxms = 0;
    
    Olle Johansson's avatar
    Olle Johansson committed
    		} else if (!strcasecmp(v->name, "maxcallbitrate")) {
    			peer->maxcallbitrate = atoi(v->value);
    			if (peer->maxcallbitrate < 0)
    				peer->maxcallbitrate = default_maxcallbitrate;
    
    		} else if (!strcasecmp(v->name, "t38pt_udptl")) {
    			if (ast_true(v->value)) {
    				ast_set_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_UDPTL);
    			} else
    				ast_clear_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_UDPTL);
    		} else if (!strcasecmp(v->name, "t38pt_rtp")) {
    			if (ast_true(v->value)) {
    				ast_set_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_RTP);
    			} else
    				ast_clear_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_RTP);
    		} else if (!strcasecmp(v->name, "t38pt_tcp")) {
    			if (ast_true(v->value)) {
    				ast_set_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_TCP);
    			} else
    				ast_clear_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_TCP);
    
    	if (!ast_test_flag(&global_flags[1], SIP_PAGE2_IGNOREREGEXPIRE) && ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC) && realtime) {
    
    
    		if ((nowtime - regseconds) > 0) {
    
    			memset(&peer->addr, 0, sizeof(peer->addr));
    			if (option_debug)
    
    				ast_log(LOG_DEBUG, "Bah, we're expired (%d/%d/%d)!\n", (int)(nowtime - regseconds), (int)regseconds, (int)nowtime);
    
    	ast_copy_flags(&peer->flags[0], &peerflags[0], mask[0].flags);
    	ast_copy_flags(&peer->flags[1], &peerflags[1], mask[1].flags);
    	if (ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE))
    		global_allowsubscribe = TRUE;	/* No global ban any more */
    	if (!found && ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC) && !ast_test_flag(&peer->flags[0], SIP_REALTIME))
    
    		reg_source_db(peer);
    	ASTOBJ_UNMARK(peer);
    
    	ast_free_ha(oldha);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return peer;
    }
    
    
    /*! \brief Re-read SIP.conf config file
    \note	This function reloads all config data, except for
    
    	active peers (with registrations). They will only
    	change configuration data at restart, not at reload.
    	SIP debug and recordhistory state will not change
     */
    
    static int reload_config(enum channelreloadreason reason)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_config *cfg;
    	struct ast_variable *v;
    	struct sip_peer *peer;
    	struct sip_user *user;
    
    	struct ast_hostent ahp;
    
    	char *cat, *stringp, *context, *oldregcontext;
    	char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct hostent *hp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int format;
    
    	int auto_sip_domains = FALSE;
    
    	struct sockaddr_in old_bindaddr = bindaddr;
    
    	int registry_count = 0, peer_count = 0, user_count = 0;
    
    	unsigned int temp_tos = 0;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	struct ast_flags debugflag = {0};
    
    	cfg = ast_config_load(config);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* We *must* have a config file otherwise stop immediately */
    	if (!cfg) {
    
    		ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
    		return -1;
    
    	/* Initialize copy of current global_regcontext for later use in removing stale contexts */
    	ast_copy_string(oldcontexts, global_regcontext, sizeof(oldcontexts));
    	oldregcontext = oldcontexts;
    
    
    	/* Clear all flags before setting default values */
    
    	/* Preserve debugging settings for console */
    
    	ast_copy_flags(&debugflag, &global_flags[1], SIP_PAGE2_DEBUG_CONSOLE);
    	ast_clear_flag(&global_flags[0], AST_FLAGS_ALL);
    	ast_clear_flag(&global_flags[1], AST_FLAGS_ALL);
    	ast_copy_flags(&global_flags[1], &debugflag, SIP_PAGE2_DEBUG_CONSOLE);
    
    	/* Reset IP addresses  */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	memset(&bindaddr, 0, sizeof(bindaddr));
    
    	memset(&localaddr, 0, sizeof(localaddr));
    
    	memset(&externip, 0, sizeof(externip));
    
    	memset(&default_prefs, 0 , sizeof(default_prefs));
    
    	outboundproxyip.sin_port = htons(DEFAULT_SIP_PORT);
    	outboundproxyip.sin_family = AF_INET;	/* Type of address: IPv4 */
    	ourport = DEFAULT_SIP_PORT;
    	srvlookup = DEFAULT_SRVLOOKUP;
    
    	global_tos_sip = DEFAULT_TOS_SIP;
    	global_tos_audio = DEFAULT_TOS_AUDIO;
    	global_tos_video = DEFAULT_TOS_VIDEO;
    
    	externhost[0] = '\0';			/* External host name (for behind NAT DynDNS support) */
    	externexpire = 0;			/* Expiration for DNS re-issuing */
    
    	memset(&outboundproxyip, 0, sizeof(outboundproxyip));
    
    	/* Reset channel settings to default before re-configuring */
    	allow_external_domains = DEFAULT_ALLOW_EXT_DOM;				/* Allow external invites */
    
    	expiry = DEFAULT_EXPIRY;
    	global_notifyringing = DEFAULT_NOTIFYRINGING;
    
    	global_allowsubscribe = FALSE;
    
    	ast_copy_string(global_useragent, DEFAULT_USERAGENT, sizeof(global_useragent));
    
    	ast_copy_string(default_notifymime, DEFAULT_NOTIFYMIME, sizeof(default_notifymime));
    
    	if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME))
    		ast_copy_string(global_realm, DEFAULT_REALM, sizeof(global_realm));
    	else
    		ast_copy_string(global_realm, ast_config_AST_SYSTEM_NAME, sizeof(global_realm));
    
    	ast_copy_string(default_callerid, DEFAULT_CALLERID, sizeof(default_callerid));
    
    	compactheaders = DEFAULT_COMPACTHEADERS;
    	global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT;
    	global_regattempts_max = 0;
    	pedanticsipchecking = DEFAULT_PEDANTIC;
    	global_mwitime = DEFAULT_MWITIME;
    	autocreatepeer = DEFAULT_AUTOCREATEPEER;
    	global_allowguest = DEFAULT_ALLOWGUEST;
    
    	global_rtptimeout = 0;
    	global_rtpholdtimeout = 0;
    
    	global_rtpkeepalive = 0;
    
    	global_allowtransfer = TRANSFER_OPENFORALL;	/* Merrily accept all transfers by default */
    
    	ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE);	/* Default for peers, users: TRUE */
    	ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP);		/* Default for peers, users: TRUE */
    	ast_set_flag(&global_flags[1], SIP_PAGE2_RTUPDATE);
    
    
    	/* Initialize some reasonable defaults at SIP reload (used both for channel and as default for peers and users */
    	ast_copy_string(default_context, DEFAULT_CONTEXT, sizeof(default_context));
    	default_subscribecontext[0] = '\0';
    	default_language[0] = '\0';
    	default_fromdomain[0] = '\0';
    	default_qualify = DEFAULT_QUALIFY;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE;
    
    	ast_copy_string(default_mohinterpret, DEFAULT_MOHINTERPRET, sizeof(default_mohinterpret));
    	ast_copy_string(default_mohsuggest, DEFAULT_MOHSUGGEST, sizeof(default_mohsuggest));
    
    	ast_copy_string(default_vmexten, DEFAULT_VMEXTEN, sizeof(default_vmexten));
    
    	ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833);			/*!< Default DTMF setting: RFC2833 */
    	ast_set_flag(&global_flags[0], SIP_NAT_RFC3581);			/*!< NAT support if requested by device with rport */
    	ast_set_flag(&global_flags[0], SIP_CAN_REINVITE);			/*!< Allow re-invites */
    
    
    	/* Debugging settings, always default to off */
    	dumphistory = FALSE;
    	recordhistory = FALSE;
    
    	ast_clear_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONFIG);
    
    
    	/* Misc settings for the channel */
    
    	global_relaxdtmf = FALSE;
    	global_callevents = FALSE;
    
    
    	/* Copy the default jb config over global_jbconf */
    	memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
    
    
    	ast_clear_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT);
    
    
    	/* Read the [general] config section of sip.conf (or from realtime config) */
    
    	for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
    
    		if (handle_common_options(&global_flags[0], &dummy[0], v))
    
    		/* handle jb conf */
    		if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
    			continue;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Create the interface list */
    		if (!strcasecmp(v->name, "context")) {
    
    			ast_copy_string(default_context, v->value, sizeof(default_context));
    
    		} else if (!strcasecmp(v->name, "realm")) {
    
    			ast_copy_string(global_realm, v->value, sizeof(global_realm));
    
    		} else if (!strcasecmp(v->name, "useragent")) {
    
    			ast_copy_string(global_useragent, v->value, sizeof(global_useragent));
    
    			if (option_debug)
    				ast_log(LOG_DEBUG, "Setting SIP channel User-Agent Name to %s\n", global_useragent);
    		} else if (!strcasecmp(v->name, "allowtransfer")) {
    			global_allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
    
    		} else if (!strcasecmp(v->name, "rtcachefriends")) {
    
    			ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTCACHEFRIENDS);	
    
    		} else if (!strcasecmp(v->name, "rtsavesysname")) {
    			ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTSAVE_SYSNAME);	
    
    		} else if (!strcasecmp(v->name, "rtupdate")) {
    
    			ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTUPDATE);	
    
    		} else if (!strcasecmp(v->name, "ignoreregexpire")) {
    
    			ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_IGNOREREGEXPIRE);	
    
    		} else if (!strcasecmp(v->name, "t1min")) {
    			global_t1min = atoi(v->value);
    
    		} else if (!strcasecmp(v->name, "rtautoclear")) {
    			int i = atoi(v->value);
    
    				global_rtautoclear = i;
    			else
    				i = 0;
    
    			ast_set2_flag(&global_flags[1], i || ast_true(v->value), SIP_PAGE2_RTAUTOCLEAR);
    
    		} else if (!strcasecmp(v->name, "usereqphone")) {
    
    			ast_set2_flag(&global_flags[0], ast_true(v->value), SIP_USEREQPHONE);	
    
    		} else if (!strcasecmp(v->name, "relaxdtmf")) {
    
    			global_relaxdtmf = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "checkmwi")) {
    			if ((sscanf(v->value, "%d", &global_mwitime) != 1) || (global_mwitime < 0)) {
    				ast_log(LOG_WARNING, "'%s' is not a valid MWI time setting at line %d.  Using default (10).\n", v->value, v->lineno);
    				global_mwitime = DEFAULT_MWITIME;
    			}
    
    		} else if (!strcasecmp(v->name, "vmexten")) {
    
    			ast_copy_string(default_vmexten, v->value, sizeof(default_vmexten));
    
    		} else if (!strcasecmp(v->name, "rtptimeout")) {
    
    			if ((sscanf(v->value, "%d", &global_rtptimeout) != 1) || (global_rtptimeout < 0)) {
    
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d.  Using default.\n", v->value, v->lineno);
    
    			}
    		} else if (!strcasecmp(v->name, "rtpholdtimeout")) {
    
    			if ((sscanf(v->value, "%d", &global_rtpholdtimeout) != 1) || (global_rtpholdtimeout < 0)) {
    
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d.  Using default.\n", v->value, v->lineno);
    
    		} else if (!strcasecmp(v->name, "rtpkeepalive")) {
    			if ((sscanf(v->value, "%d", &global_rtpkeepalive) != 1) || (global_rtpkeepalive < 0)) {
    				ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d.  Using default.\n", v->value, v->lineno);
    				global_rtpkeepalive = 0;
    			}
    
    		} else if (!strcasecmp(v->name, "compactheaders")) {
    			compactheaders = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "notifymimetype")) {
    
    			ast_copy_string(default_notifymime, v->value, sizeof(default_notifymime));
    
    		} else if (!strcasecmp(v->name, "notifyringing")) {
    			global_notifyringing = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "alwaysauthreject")) {
    			global_alwaysauthreject = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "mohinterpret") 
    			|| !strcasecmp(v->name, "musicclass") || !strcasecmp(v->name, "musiconhold")) {
    			ast_copy_string(default_mohinterpret, v->value, sizeof(default_mohinterpret));
    		} else if (!strcasecmp(v->name, "mohsuggest")) {
    			ast_copy_string(default_mohsuggest, v->value, sizeof(default_mohsuggest));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "language")) {
    
    			ast_copy_string(default_language, v->value, sizeof(default_language));
    
    		} else if (!strcasecmp(v->name, "regcontext")) {
    
    			ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
    			stringp = newcontexts;
    			/* Let's remove any contexts that are no longer defined in regcontext */
    			cleanup_stale_contexts(stringp, oldregcontext);
    			/* Create contexts if they don't exist already */
    			while ((context = strsep(&stringp, "&"))) {
    				if (!ast_context_find(context))
    					ast_context_create(NULL, context,"SIP");
    			}
    
    			ast_copy_string(global_regcontext, v->value, sizeof(global_regcontext));
    
    		} else if (!strcasecmp(v->name, "callerid")) {
    
    			ast_copy_string(default_callerid, v->value, sizeof(default_callerid));
    
    		} else if (!strcasecmp(v->name, "fromdomain")) {
    
    			ast_copy_string(default_fromdomain, v->value, sizeof(default_fromdomain));
    
    		} else if (!strcasecmp(v->name, "outboundproxy")) {
    			if (ast_get_ip_or_srv(&outboundproxyip, v->value, "_sip._udp") < 0)
    				ast_log(LOG_WARNING, "Unable to locate host '%s'\n", v->value);
    		} else if (!strcasecmp(v->name, "outboundproxyport")) {
    			/* Port needs to be after IP */
    
    			outboundproxyip.sin_port = htons(format);
    
    		} else if (!strcasecmp(v->name, "autocreatepeer")) {
    			autocreatepeer = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "srvlookup")) {
    			srvlookup = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "pedantic")) {
    			pedanticsipchecking = ast_true(v->value);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "maxexpirey") || !strcasecmp(v->name, "maxexpiry")) {
    			max_expiry = atoi(v->value);
    			if (max_expiry < 1)
    				max_expiry = DEFAULT_MAX_EXPIRY;
    
    		} else if (!strcasecmp(v->name, "minexpirey") || !strcasecmp(v->name, "minexpiry")) {
    			min_expiry = atoi(v->value);
    			if (min_expiry < 1)
    				min_expiry = DEFAULT_MIN_EXPIRY;
    
    		} else if (!strcasecmp(v->name, "defaultexpiry") || !strcasecmp(v->name, "defaultexpirey")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			default_expiry = atoi(v->value);
    			if (default_expiry < 1)
    				default_expiry = DEFAULT_DEFAULT_EXPIRY;
    
    		} else if (!strcasecmp(v->name, "sipdebug")) {
    
    			if (ast_true(v->value))
    
    				ast_set_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONFIG);
    
    		} else if (!strcasecmp(v->name, "dumphistory")) {
    			dumphistory = ast_true(v->value);
    		} else if (!strcasecmp(v->name, "recordhistory")) {
    			recordhistory = ast_true(v->value);
    		} else if (!strcasecmp(v->name, "registertimeout")) {
    
    			global_reg_timeout = atoi(v->value);
    			if (global_reg_timeout < 1)
    				global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT;
    
    		} else if (!strcasecmp(v->name, "registerattempts")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "bindaddr")) {
    
    			if (!(hp = ast_gethostbyname(v->value, &ahp))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
    			} else {
    				memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
    			}
    
    		} else if (!strcasecmp(v->name, "localnet")) {
    
    			struct ast_ha *na;
    			if (!(na = ast_append_ha("d", v->value, localaddr)))
    				ast_log(LOG_WARNING, "Invalid localnet value: %s\n", v->value);
    
    				localaddr = na;
    		} else if (!strcasecmp(v->name, "localmask")) {
    
    			ast_log(LOG_WARNING, "Use of localmask is no long supported -- use localnet with mask syntax\n");
    
    			if (!(hp = ast_gethostbyname(v->value, &ahp))) 
    
    				ast_log(LOG_WARNING, "Invalid address for externip keyword: %s\n", v->value);
    
    			else
    				memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr));
    
    			externexpire = 0;
    		} else if (!strcasecmp(v->name, "externhost")) {
    
    			ast_copy_string(externhost, v->value, sizeof(externhost));
    
    			if (!(hp = ast_gethostbyname(externhost, &ahp))) 
    				ast_log(LOG_WARNING, "Invalid address for externhost keyword: %s\n", externhost);
    			else
    				memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr));
    
    		} else if (!strcasecmp(v->name, "externrefresh")) {
    
    				ast_log(LOG_WARNING, "Invalid externrefresh value '%s', must be an integer >0 at line %d\n", v->value, v->lineno);
    				externrefresh = 10;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "allow")) {
    
    			ast_parse_allow_disallow(&default_prefs, &global_capability, v->value, 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "disallow")) {
    
    			ast_parse_allow_disallow(&default_prefs, &global_capability, v->value, 0);
    
    		} else if (!strcasecmp(v->name, "allowexternaldomains")) {
    			allow_external_domains = ast_true(v->value);
    
    		} else if (!strcasecmp(v->name, "autodomain")) {
    			auto_sip_domains = ast_true(v->value);
    		} else if (!strcasecmp(v->name, "domain")) {
    			char *domain = ast_strdupa(v->value);
    			char *context = strchr(domain, ',');
    
    			if (context)
    				*context++ = '\0';
    
    
    			if (option_debug && ast_strlen_zero(context))
    				ast_log(LOG_DEBUG, "No context specified at line %d for domain '%s'\n", v->lineno, domain);
    
    			if (ast_strlen_zero(domain))
    				ast_log(LOG_WARNING, "Empty domain specified at line %d\n", v->lineno);
    			else
    				add_sip_domain(ast_strip(domain), SIP_DOMAIN_CONFIG, context ? ast_strip(context) : "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "register")) {
    
    			if (sip_register(v->value, v->lineno) == 0)
    				registry_count++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (!strcasecmp(v->name, "tos")) {
    
    			if (!ast_str2tos(v->value, &temp_tos)) {
    				global_tos_sip = temp_tos;
    				global_tos_audio = temp_tos;
    				global_tos_video = temp_tos;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				ast_log(LOG_WARNING, "tos value at line %d is deprecated.  See doc/ip-tos.txt for more information.", v->lineno);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				ast_log(LOG_WARNING, "Invalid tos value at line %d, See doc/ip-tos.txt for more information.\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "tos_sip")) {
    			if (ast_str2tos(v->value, &global_tos_sip))
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				ast_log(LOG_WARNING, "Invalid tos_sip value at line %d, recommended value is 'cs3'. See doc/ip-tos.txt.\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "tos_audio")) {
    			if (ast_str2tos(v->value, &global_tos_audio))
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, recommended value is 'ef'. See doc/ip-tos.txt.\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "tos_video")) {
    			if (ast_str2tos(v->value, &global_tos_video))
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				ast_log(LOG_WARNING, "Invalid tos_video value at line %d, recommended value is 'af41'. See doc/ip-tos.txt.\n", v->lineno);
    
    		} else if (!strcasecmp(v->name, "bindport")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				bindaddr.sin_port = htons(ourport);
    			} else {
    				ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config);
    			}
    
    		} else if (!strcasecmp(v->name, "qualify")) {
    			if (!strcasecmp(v->value, "no")) {
    				default_qualify = 0;
    			} else if (!strcasecmp(v->value, "yes")) {
    				default_qualify = DEFAULT_MAXMS;
    			} else if (sscanf(v->value, "%d", &default_qualify) != 1) {
    				ast_log(LOG_WARNING, "Qualification default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno);
    				default_qualify = 0;
    			}
    
    		} else if (!strcasecmp(v->name, "callevents")) {
    
    			global_callevents = ast_true(v->value);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		} else if (!strcasecmp(v->name, "maxcallbitrate")) {
    			default_maxcallbitrate = atoi(v->value);
    			if (default_maxcallbitrate < 0)
    				default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE;
    
    		} else if (!strcasecmp(v->name, "t38pt_udptl")) {
    			if (ast_true(v->value)) {
    				ast_set_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_UDPTL);
    			}
    		} else if (!strcasecmp(v->name, "t38pt_rtp")) {
    			if (ast_true(v->value)) {
    				ast_set_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_RTP);
    			}
    		} else if (!strcasecmp(v->name, "t38pt_tcp")) {
    			if (ast_true(v->value)) {
    				ast_set_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_TCP);
    			}
    
    	if (!allow_external_domains && AST_LIST_EMPTY(&domain_list)) {
    		ast_log(LOG_WARNING, "To disallow external domains, you need to configure local SIP domains.\n");
    		allow_external_domains = 1;
    
    	/* Build list of authentication to various SIP realms, i.e. service providers */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
     	for (v = ast_variable_browse(cfg, "authentication"); v ; v = v->next) {
    
     		/* Format for authentication is auth = username:password@realm */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
     		if (!strcasecmp(v->name, "auth"))
    
     			authl = add_realm_authentication(authl, v->value, v->lineno);
     	}
    	
    
    	/* Load peers, users and friends */
    
    	cat = NULL;
    	while ( (cat = ast_category_browse(cfg, cat)) ) {
    		const char *utype;
    		if (!strcasecmp(cat, "general") || !strcasecmp(cat, "authentication"))
    			continue;
    		utype = ast_variable_retrieve(cfg, cat, "type");
    		if (!utype) {
    			ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
    			continue;
    		} else {
    			int is_user = 0, is_peer = 0;
    			if (!strcasecmp(utype, "user"))
    				is_user = 1;
    			else if (!strcasecmp(utype, "friend"))
    				is_user = is_peer = 1;
    			else if (!strcasecmp(utype, "peer"))
    				is_peer = 1;
    			else {
    				ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, "sip.conf");
    				continue;
    			}
    			if (is_user) {
    				user = build_user(cat, ast_variable_browse(cfg, cat), 0);
    				if (user) {
    					ASTOBJ_CONTAINER_LINK(&userl,user);
    					ASTOBJ_UNREF(user, sip_destroy_user);
    
    			}
    			if (is_peer) {
    				peer = build_peer(cat, ast_variable_browse(cfg, cat), 0);
    				if (peer) {
    					ASTOBJ_CONTAINER_LINK(&peerl,peer);
    					ASTOBJ_UNREF(peer, sip_destroy_peer);
    
    	if (ast_find_ourip(&__ourip, bindaddr)) {
    		ast_log(LOG_WARNING, "Unable to get own IP address, SIP disabled\n");
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!ntohs(bindaddr.sin_port))
    		bindaddr.sin_port = ntohs(DEFAULT_SIP_PORT);
    	bindaddr.sin_family = AF_INET;
    
    	if ((sipsock > -1) && (memcmp(&old_bindaddr, &bindaddr, sizeof(struct sockaddr_in)))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		close(sipsock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		sipsock = -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sipsock < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		sipsock = socket(AF_INET, SOCK_DGRAM, 0);
    		if (sipsock < 0) {
    			ast_log(LOG_WARNING, "Unable to create SIP socket: %s\n", strerror(errno));
    		} else {
    
    			/* Allow SIP clients on the same host to access us: */
    			const int reuseFlag = 1;
    
    			setsockopt(sipsock, SOL_SOCKET, SO_REUSEADDR,
    				   (const char*)&reuseFlag,
    				   sizeof reuseFlag);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (bind(sipsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
    				ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
    
    				ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
    
    Mark Spencer's avatar
    Mark Spencer committed
    				close(sipsock);
    				sipsock = -1;
    
    					ast_verbose(VERBOSE_PREFIX_2 "SIP Listening on %s:%d\n", 
    
    					ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
    
    					ast_verbose(VERBOSE_PREFIX_2 "Using SIP TOS: %s\n", ast_tos2str(global_tos_sip));
    
    				if (setsockopt(sipsock, IPPROTO_IP, IP_TOS, &global_tos_sip, sizeof(global_tos_sip))) 
    					ast_log(LOG_WARNING, "Unable to set SIP TOS to %s\n", ast_tos2str(global_tos_sip));
    
    	/* Add default domains - host name, IP address and IP:port */
    	/* Only do this if user added any sip domain with "localdomains" */
    	/* In order to *not* break backwards compatibility */
    	/* 	Some phones address us at IP only, some with additional port number */
    	if (auto_sip_domains) {
    		char temp[MAXHOSTNAMELEN];
    
    		/* First our default IP address */
    
    		if (bindaddr.sin_addr.s_addr)
    			add_sip_domain(ast_inet_ntoa(bindaddr.sin_addr), SIP_DOMAIN_AUTO, NULL);
    		else
    
    			ast_log(LOG_NOTICE, "Can't add wildcard IP address to domain list, please add IP address to domain manually.\n");
    
    		/* Our extern IP address, if configured */
    
    		if (externip.sin_addr.s_addr)
    			add_sip_domain(ast_inet_ntoa(externip.sin_addr), SIP_DOMAIN_AUTO, NULL);
    
    
    		/* Extern host name (NAT traversal support) */
    		if (!ast_strlen_zero(externhost))
    			add_sip_domain(externhost, SIP_DOMAIN_AUTO, NULL);
    		
    		/* Our host name */
    		if (!gethostname(temp, sizeof(temp)))
    			add_sip_domain(temp, SIP_DOMAIN_AUTO, NULL);
    	}
    
    
    	/* Release configuration from memory */
    
    	ast_config_destroy(cfg);
    
    
    	/* Load the list of manual NOTIFY types to support */
    	if (notify_types)
    
    		ast_config_destroy(notify_types);
    	notify_types = ast_config_load(notify_config);
    
    	/* Done, tell the manager */
    	manager_event(EVENT_FLAG_SYSTEM, "ChannelReload", "Channel: SIP\r\nReloadReason: %s\r\nRegistry_Count: %d\r\nPeer_Count: %d\r\nUser_Count: %d\r\n\r\n", channelreloadreason2txt(reason), registry_count, peer_count, user_count);
    
    
    static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan)
    {
    	struct sip_pvt *p;
    	struct ast_udptl *udptl = NULL;
    	
    	p = chan->tech_pvt;
    	if (!p)
    		return NULL;
    	
    	ast_mutex_lock(&p->lock);
    	if (p->udptl && ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
    		udptl = p->udptl;
    	ast_mutex_unlock(&p->lock);
    	return udptl;
    }
    
    static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl)
    {
    	struct sip_pvt *p;
    	
    	p = chan->tech_pvt;
    	if (!p)
    		return -1;
    	ast_mutex_lock(&p->lock);
    	if (udptl)
    		ast_udptl_get_peer(udptl, &p->udptlredirip);
    	else
    		memset(&p->udptlredirip, 0, sizeof(p->udptlredirip));
    	if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
    		if (!p->pendinginvite) {
    			if (option_debug > 2) {
    
    				ast_log(LOG_DEBUG, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip), udptl ? ntohs(p->udptlredirip.sin_port) : 0);
    
    			}
    			transmit_reinvite_with_t38_sdp(p);
    		} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
    			if (option_debug > 2) {
    
    				ast_log(LOG_DEBUG, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip), udptl ? ntohs(p->udptlredirip.sin_port) : 0);
    
    			}
    			ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
    		}
    	}
    	/* Reset lastrtprx timer */
    	p->lastrtprx = p->lastrtptx = time(NULL);
    	ast_mutex_unlock(&p->lock);
    	return 0;
    }
    
    static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt, int reinvite)
    {
    	struct sip_pvt *p;
    	int flag = 0;
    	
    	p = chan->tech_pvt;
    	if (!p || !pvt->udptl)
    		return -1;
    	
    	/* Setup everything on the other side like offered/responded from first side */
    	ast_mutex_lock(&p->lock);
    	p->t38.jointcapability = p->t38.peercapability = pvt->t38.jointcapability;
    	ast_udptl_set_far_max_datagram(p->udptl, ast_udptl_get_local_max_datagram(pvt->udptl));
    	ast_udptl_set_local_max_datagram(p->udptl, ast_udptl_get_local_max_datagram(pvt->udptl));
    	ast_udptl_set_error_correction_scheme(p->udptl, ast_udptl_get_error_correction_scheme(pvt->udptl));
    	
    	if (reinvite) {		/* If we are handling sending re-invite to the other side of the bridge */
    		if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE) && ast_test_flag(&pvt->flags[0], SIP_CAN_REINVITE)) {