[Freeswitch-svn] [commit] r10525 - in freeswitch/trunk/src: . include mod/endpoints/mod_sofia

FreeSWITCH SVN mikej at freeswitch.org
Mon Nov 24 07:52:55 PST 2008


Author: mikej
Date: Mon Nov 24 10:52:55 2008
New Revision: 10525

Log:
MODENDP-77 Gateway event subscriptions.

Merged with minor modifications.
Still needs to add support for reload/rescan.

Modified:
   freeswitch/trunk/src/include/switch_types.h
   freeswitch/trunk/src/mod/endpoints/mod_sofia/mod_sofia.h
   freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia.c
   freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia_presence.c
   freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia_reg.c
   freeswitch/trunk/src/switch_event.c

Modified: freeswitch/trunk/src/include/switch_types.h
==============================================================================
--- freeswitch/trunk/src/include/switch_types.h	(original)
+++ freeswitch/trunk/src/include/switch_types.h	Mon Nov 24 10:52:55 2008
@@ -1063,6 +1063,7 @@
 	SWITCH_EVENT_DTMF				- DTMF was sent
 	SWITCH_EVENT_MESSAGE			- A Basic Message
 	SWITCH_EVENT_PRESENCE_IN		- Presence in
+	SWITCH_EVENT_NOTIFY_IN			- Received incoming NOTIFY from gateway subscription
 	SWITCH_EVENT_PRESENCE_OUT		- Presence out
 	SWITCH_EVENT_PRESENCE_PROBE		- Presence probe
 	SWITCH_EVENT_MESSAGE_WAITING	- A message is waiting
@@ -1121,6 +1122,7 @@
 	SWITCH_EVENT_DTMF,
 	SWITCH_EVENT_MESSAGE,
 	SWITCH_EVENT_PRESENCE_IN,
+	SWITCH_EVENT_NOTIFY_IN,
 	SWITCH_EVENT_PRESENCE_OUT,
 	SWITCH_EVENT_PRESENCE_PROBE,
 	SWITCH_EVENT_MESSAGE_WAITING,

Modified: freeswitch/trunk/src/mod/endpoints/mod_sofia/mod_sofia.h
==============================================================================
--- freeswitch/trunk/src/mod/endpoints/mod_sofia/mod_sofia.h	(original)
+++ freeswitch/trunk/src/mod/endpoints/mod_sofia/mod_sofia.h	Mon Nov 24 10:52:55 2008
@@ -60,6 +60,9 @@
 struct sofia_gateway;
 typedef struct sofia_gateway sofia_gateway_t;
 
+struct sofia_gateway_subscription;
+typedef struct sofia_gateway_subscription sofia_gateway_subscription_t;
+
 struct sofia_profile;
 typedef struct sofia_profile sofia_profile_t;
 #define NUA_MAGIC_T sofia_profile_t
@@ -272,6 +275,31 @@
 	SOFIA_GATEWAY_UP
 } sofia_gateway_status_t;
 
+typedef enum {
+	SUB_STATE_UNSUBED,
+	SUB_STATE_TRYING,
+	SUB_STATE_SUBSCRIBE,
+	SUB_STATE_SUBED,
+	SUB_STATE_UNSUBSCRIBE,
+	SUB_STATE_FAILED,
+	SUB_STATE_EXPIRED,
+	SUB_STATE_NOSUB,
+	v_STATE_LAST
+} sub_state_t;
+
+struct sofia_gateway_subscription {
+	sofia_gateway_t *gateway;
+	char *expires_str;
+	char *event;  /* eg, 'message-summary' to subscribe to MWI events */
+	char *content_type;  /* eg, application/simple-message-summary in the case of MWI events */
+	uint32_t freq;
+	int32_t retry_seconds;
+	time_t expires;
+	time_t retry;
+	sub_state_t state;
+	struct sofia_gateway_subscription *next;
+};
+
 struct sofia_gateway {
 	sofia_private_t *sofia_private;
 	nua_handle_t *nh;
@@ -306,6 +334,7 @@
 	switch_event_t *vars;
 	char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
 	struct sofia_gateway *next;
+	sofia_gateway_subscription_t *subscriptions;
 };
 
 typedef enum {
@@ -622,6 +651,7 @@
 void sofia_glue_actually_execute_sql(sofia_profile_t *profile, switch_bool_t master, char *sql, switch_mutex_t *mutex);
 void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot);
 void sofia_reg_check_gateway(sofia_profile_t *profile, time_t now);
+void sofia_sub_check_gateway(sofia_profile_t *profile, time_t now);
 void sofia_reg_unregister(sofia_profile_t *profile);
 switch_status_t sofia_glue_ext_address_lookup(sofia_profile_t *profile, private_object_t *tech_pvt, char **ip, switch_port_t *port, char *sourceip, switch_memory_pool_t *pool);
 
@@ -660,6 +690,8 @@
 sofia_gateway_t *sofia_reg_find_gateway__(const char *file, const char *func, int line, const char *key);
 #define sofia_reg_find_gateway(x) sofia_reg_find_gateway__(__FILE__, __SWITCH_FUNC__, __LINE__,  x)
 
+sofia_gateway_subscription_t *sofia_find_gateway_subscription(sofia_gateway_t *gateway_ptr, const char *event);
+
 void sofia_reg_release_gateway__(const char *file, const char *func, int line, sofia_gateway_t *gateway);
 #define sofia_reg_release_gateway(x) sofia_reg_release_gateway__(__FILE__, __SWITCH_FUNC__, __LINE__, x);
 

Modified: freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia.c
==============================================================================
--- freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia.c	(original)
+++ freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia.c	Mon Nov 24 10:52:55 2008
@@ -85,6 +85,7 @@
 	switch_channel_t *channel = NULL;
 	private_object_t *tech_pvt = NULL;
 	switch_event_t *s_event = NULL;
+	sofia_gateway_subscription_t *gw_sub_ptr;
 
 	/* make sure we have a proper event */
 	if (!sip || !sip->sip_event) {
@@ -172,21 +173,60 @@
 		nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END());
 	}
 
+	/* if no session, assume it could be an incoming notify from a gateway subscription */
+	if (session) {
+		/* make sure we have a proper "talk" event */
+		if (strcasecmp(sip->sip_event->o_type, "talk")) {
+			goto error;
+		}
 
-	/* make sure we have a proper "talk" event */
-	if (!session || strcasecmp(sip->sip_event->o_type, "talk")) {
-		goto error;
+		if (!switch_channel_test_flag(channel, CF_OUTBOUND)) {
+			switch_channel_answer(channel);
+			switch_channel_set_variable(channel, "auto_answer_destination", switch_channel_get_variable(channel, "destination_number"));
+			switch_ivr_session_transfer(session, "auto_answer", NULL, NULL);
+			nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END());
+			return;
+		}
 	}
-
 	
-	if (!switch_channel_test_flag(channel, CF_OUTBOUND)) {
-		switch_channel_answer(channel);
-		switch_channel_set_variable(channel, "auto_answer_destination", switch_channel_get_variable(channel, "destination_number"));
-		switch_ivr_session_transfer(session, "auto_answer", NULL, NULL);
-		nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END());
-		return;
+	if (!sofia_private || !sofia_private->gateway) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Gateway information missing\n");
+		goto error;	
+	}
+				
+	/* find the corresponding gateway subscription (if any) */
+	if (!(gw_sub_ptr = sofia_find_gateway_subscription(sofia_private->gateway, sip->sip_event->o_type))) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+						  "Could not find gateway subscription.  Gateway: %s.  Subscription Event: %s\n",
+						  sofia_private->gateway->name, sip->sip_event->o_type);
+		goto error;	
+	}
+
+	if (!(gw_sub_ptr->state == SUB_STATE_SUBED || gw_sub_ptr->state == SUB_STATE_SUBSCRIBE)) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+						  "Ignoring notify due to subscription state: %d\n",
+						  gw_sub_ptr->state);
+		goto error;	
+	}
+
+	/* dispatch freeswitch event */
+	if (switch_event_create(&s_event, SWITCH_EVENT_NOTIFY_IN) == SWITCH_STATUS_SUCCESS) {
+		switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "event", "%s", sip->sip_event->o_type);
+		switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "pl_data", "%s", sip->sip_payload->pl_data);
+		switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "sip_content_type", "%s", sip->sip_content_type->c_type);
+		switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", sofia_private->gateway->profile->sip_port);
+		switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "module_name", "%s", "mod_sofia");
+		switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "profile_name", "%s", sofia_private->gateway->profile->name);
+		switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "profile_uri", "%s", sofia_private->gateway->profile->url);
+		switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "gateway_name", "%s", sofia_private->gateway->name);
+		switch_event_fire(&s_event);
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "dispatched freeswitch event for message-summary NOTIFY\n");
+	} else {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create event\n");
+		goto error;	
 	}
-	
+
+	return;
 
   error:
 	nua_respond(nh, 481, "Subscription Does Not Exist", NUTAG_WITH_THIS(nua), TAG_END());
@@ -565,6 +605,7 @@
 				sofia_reg_check_gateway(profile, switch_timestamp(NULL));
 				gateway_loops = 0;
 			}
+			sofia_sub_check_gateway(profile, time(NULL));
 			loops = 0;
 		}
 
@@ -859,9 +900,56 @@
 	}
 }
 
+static void parse_gateway_subscriptions(sofia_profile_t *profile, sofia_gateway_t *gateway, switch_xml_t gw_subs_tag)
+{
+	switch_xml_t subscription_tag, param;
+
+	for (subscription_tag = switch_xml_child(gw_subs_tag, "subscription"); subscription_tag; subscription_tag = subscription_tag->next) {
+		sofia_gateway_subscription_t *gw_sub;
+
+		if ((gw_sub = switch_core_alloc(profile->pool, sizeof(*gw_sub)))) {
+			char *expire_seconds = "3600", *retry_seconds = "30", *content_type = "NO_CONTENT_TYPE";
+			char *event = (char *) switch_xml_attr_soft(subscription_tag, "event");
+			gw_sub->event = switch_core_strdup(gateway->pool, event);			
+			gw_sub->gateway = gateway;
+			gw_sub->next = NULL;
+			
+			for (param = switch_xml_child(subscription_tag, "param"); param; param = param->next) {
+				char *var = (char *) switch_xml_attr_soft(param, "name");
+				char *val = (char *) switch_xml_attr_soft(param, "value");
+				if (!strcmp(var, "expire-seconds")) {
+					expire_seconds = val;
+				} else if (!strcmp(var, "retry-seconds")) {
+					retry_seconds = val;
+				} else if (!strcmp(var, "content-type")) {
+					content_type = val;
+				}
+			}
+			
+			gw_sub->retry_seconds = atoi(retry_seconds);
+			if (gw_sub->retry_seconds < 10) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "INVALID: retry_seconds correcting the value to 30\n");
+				gw_sub->retry_seconds = 30;
+			}
+			
+			gw_sub->expires_str = switch_core_strdup(gateway->pool, expire_seconds);  
+			
+			if ((gw_sub->freq = atoi(gw_sub->expires_str)) < 5) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+				"Invalid Freq: %d.  Setting Register-Frequency to 3600\n", gw_sub->freq);
+				gw_sub->freq = 3600;
+			}
+			gw_sub->freq -= 2;
+			gw_sub->content_type = switch_core_strdup(gateway->pool, content_type);
+			gw_sub->next = gateway->subscriptions;		
+		}
+		gateway->subscriptions = gw_sub;
+	}
+}
+
 static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag)
 {
-	switch_xml_t gateway_tag, param;
+	switch_xml_t gateway_tag, param, gw_subs_tag;
 	sofia_gateway_t *gp;
 
 	for (gateway_tag = switch_xml_child(gateways_tag, "gateway"); gateway_tag; gateway_tag = gateway_tag->next) {
@@ -969,6 +1057,10 @@
 				}
 			}
 
+			if ((gw_subs_tag = switch_xml_child(gateway_tag, "subscriptions"))) {
+				parse_gateway_subscriptions(profile, gateway, gw_subs_tag);
+			}
+			
 			if (switch_strlen_zero(realm)) {
 				realm = name;
 			}

Modified: freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia_presence.c
==============================================================================
--- freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia_presence.c	(original)
+++ freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia_presence.c	Mon Nov 24 10:52:55 2008
@@ -1617,12 +1617,66 @@
 	}
 }
 
+sofia_gateway_subscription_t *sofia_find_gateway_subscription(sofia_gateway_t *gateway_ptr, const char *event) {
+	sofia_gateway_subscription_t *gw_sub_ptr;
+	for (gw_sub_ptr = gateway_ptr->subscriptions; gw_sub_ptr; gw_sub_ptr = gw_sub_ptr->next) {
+		if (!strcasecmp(gw_sub_ptr->event, event)) {
+			/* this is the gateway subscription we are interested in */
+			return gw_sub_ptr;
+		}
+	}
+	return NULL;
+}
+
 void sofia_presence_handle_sip_r_subscribe(int status,
 										   char const *phrase,
 										   nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
 										   tagi_t tags[])
 {
+	sip_event_t const *o = NULL;
+	sofia_gateway_subscription_t *gw_sub_ptr;
+	
+	if (!sip) {
+		return;
+	}
 
+	tl_gets(tags, SIPTAG_EVENT_REF(o), TAG_END());
+	/* o->o_type: message-summary (for example) */
+	if (!o) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Event information not given\n");
+		return;
+	}
+
+	if (!sofia_private || !sofia_private->gateway) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Gateway information missing\n");
+		return;
+	}
+	
+	/* Find the subscription if one exists */
+	if (!(gw_sub_ptr = sofia_find_gateway_subscription(sofia_private->gateway, o->o_type))) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not find gateway subscription.  Gateway: %s.  Subscription Event: %s\n",
+				sofia_private->gateway->name, o->o_type);
+		return;
+	}
+	
+	/* Update the subscription status for the subscription */
+	switch (status) {
+	case 200:
+		/* TODO: in the spec it is possible for the other side to change the original expiry time,
+		 * this needs to be researched (eg, what sip header this information will be in) and implemented.
+		 * Although, since it seems the sofia stack is pretty much handling the subscription expiration
+		 * anyway, then maybe its not even worth bothering.
+		 */
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "got 200 OK response, updated state to SUB_STATE_SUBSCRIBE.\n");
+		gw_sub_ptr->state = SUB_STATE_SUBSCRIBE;
+		break;
+	case 100:
+		break;
+	default:
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "status (%d) != 200, updated state to SUB_STATE_FAILED.\n", status);
+		gw_sub_ptr->state = SUB_STATE_FAILED;
+		break;
+	}
 }
 
 void sofia_presence_handle_sip_i_publish(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,

Modified: freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia_reg.c
==============================================================================
--- freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia_reg.c	(original)
+++ freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia_reg.c	Mon Nov 24 10:52:55 2008
@@ -77,6 +77,107 @@
 	}
 }
 
+void sofia_sub_check_gateway(sofia_profile_t *profile, time_t now)
+{
+	/* NOTE: A lot of the mechanism in place here for refreshing subscriptions is
+	 * pretty much redundant, as the sofia stack takes it upon itself to
+	 * refresh subscriptions on its own, based on the value of the Expires
+	 * header (which we control in the outgoing subscription request)
+	 */
+	sofia_gateway_t *gateway_ptr;
+	
+	for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) {
+		sofia_gateway_subscription_t *gw_sub_ptr;
+
+		for (gw_sub_ptr = gateway_ptr->subscriptions; gw_sub_ptr; gw_sub_ptr = gw_sub_ptr->next) {
+			int ss_state = nua_callstate_authenticating;
+			sub_state_t ostate = gw_sub_ptr->state;
+			
+			if (!now) {
+				gw_sub_ptr->state = ostate = SUB_STATE_UNSUBED;
+				gw_sub_ptr->expires_str = "0";
+			}
+			
+			switch (ostate) {
+			case SUB_STATE_NOSUB:
+				break;
+			case SUB_STATE_SUBSCRIBE:
+				gw_sub_ptr->expires = now + gw_sub_ptr->freq;
+				gw_sub_ptr->state = SUB_STATE_SUBED;
+				break;
+			case SUB_STATE_UNSUBSCRIBE:
+				gw_sub_ptr->state = SUB_STATE_NOSUB;
+				
+				/* not tested .. */
+				nua_unsubscribe(gateway_ptr->nh,
+						NUTAG_URL(gateway_ptr->register_url),
+						SIPTAG_EVENT_STR(gw_sub_ptr->event),
+						SIPTAG_ACCEPT_STR(gw_sub_ptr->content_type),
+						SIPTAG_TO_STR(gateway_ptr->register_from),
+						SIPTAG_FROM_STR(gateway_ptr->register_from),
+						SIPTAG_CONTACT_STR(gateway_ptr->register_contact),
+						TAG_NULL());
+				
+				break;
+			case SUB_STATE_UNSUBED:
+				if ((gateway_ptr->nh = nua_handle(gateway_ptr->profile->nua, NULL,
+												  NUTAG_URL(gateway_ptr->register_proxy),
+												  SIPTAG_TO_STR(gateway_ptr->register_to),
+												  NUTAG_CALLSTATE_REF(ss_state), 
+												  SIPTAG_FROM_STR(gateway_ptr->register_from), TAG_END()))) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "subscribing to [%s] on gateway [%s]\n", gw_sub_ptr->event, gateway_ptr->name);
+				}
+				
+				gateway_ptr->sofia_private = malloc(sizeof(*gateway_ptr->sofia_private));
+				switch_assert(gateway_ptr->sofia_private);
+
+				memset(gateway_ptr->sofia_private, 0, sizeof(*gateway_ptr->sofia_private));
+
+				gateway_ptr->sofia_private->gateway = gateway_ptr;
+				nua_handle_bind(gateway_ptr->nh, gateway_ptr->sofia_private);
+
+				if (now) {
+					nua_subscribe(gateway_ptr->nh,
+							NUTAG_URL(gateway_ptr->register_url),
+							SIPTAG_EVENT_STR(gw_sub_ptr->event),
+							SIPTAG_ACCEPT_STR(gw_sub_ptr->content_type),  
+							SIPTAG_TO_STR(gateway_ptr->register_from),
+							SIPTAG_FROM_STR(gateway_ptr->register_from),
+							SIPTAG_CONTACT_STR(gateway_ptr->register_contact),
+							SIPTAG_EXPIRES_STR(gw_sub_ptr->expires_str),  // sofia stack bases its auto-refresh stuff on this
+							TAG_NULL());
+					gw_sub_ptr->retry = now + gw_sub_ptr->retry_seconds;
+				} else {
+					nua_unsubscribe(gateway_ptr->nh,
+							NUTAG_URL(gateway_ptr->register_url),
+							SIPTAG_EVENT_STR(gw_sub_ptr->event),
+							SIPTAG_ACCEPT_STR(gw_sub_ptr->content_type),
+							SIPTAG_FROM_STR(gateway_ptr->register_from),
+							SIPTAG_TO_STR(gateway_ptr->register_from),
+							SIPTAG_CONTACT_STR(gateway_ptr->register_contact),
+							SIPTAG_EXPIRES_STR(gw_sub_ptr->expires_str),
+							TAG_NULL());
+				}
+				gw_sub_ptr->state = SUB_STATE_TRYING;
+				break;
+				
+			case SUB_STATE_FAILED:
+			case SUB_STATE_TRYING:
+				if (gw_sub_ptr->retry && now >= gw_sub_ptr->retry) {
+					gw_sub_ptr->state = SUB_STATE_UNSUBED;
+					gw_sub_ptr->retry = 0;
+				}
+				break;
+			default:
+				if (now >= gw_sub_ptr->expires) {
+					gw_sub_ptr->state = SUB_STATE_UNSUBED;
+				}
+				break;
+			}
+		}
+	}
+}
+
 void sofia_reg_check_gateway(sofia_profile_t *profile, time_t now)
 {
 	sofia_gateway_t *gateway_ptr, *last = NULL;

Modified: freeswitch/trunk/src/switch_event.c
==============================================================================
--- freeswitch/trunk/src/switch_event.c	(original)
+++ freeswitch/trunk/src/switch_event.c	Mon Nov 24 10:52:55 2008
@@ -142,6 +142,7 @@
 	"DTMF",
 	"MESSAGE",
 	"PRESENCE_IN",
+	"NOTIFY_IN",
 	"PRESENCE_OUT",
 	"PRESENCE_PROBE",
 	"MESSAGE_WAITING",



More information about the Freeswitch-svn mailing list