[svn-commits] mmichelson: branch group/CCSS r229505 - /team/group/CCSS/channels/chan_sip.c

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Nov 11 14:17:34 CST 2009


Author: mmichelson
Date: Wed Nov 11 14:17:30 2009
New Revision: 229505

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=229505
Log:
Initial work towards supporting PUBLISH in chan_sip.

For now, I've put structures in place for defining a general
purpose event state compositor, as well as some fundamental
code for handling incoming PUBLISH requests.

The code is intended to work as a base so that if we wish to
support PUBLISH for event types other than call-completion,
it is obvious how it should be done, and it shouldn't be
too difficult to do so either.


Modified:
    team/group/CCSS/channels/chan_sip.c

Modified: team/group/CCSS/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/group/CCSS/channels/chan_sip.c?view=diff&rev=229505&r1=229504&r2=229505
==============================================================================
--- team/group/CCSS/channels/chan_sip.c (original)
+++ team/group/CCSS/channels/chan_sip.c Wed Nov 11 14:17:30 2009
@@ -1648,6 +1648,202 @@
 	enum referstatus status;			/*!< REFER status */
 };
 
+/*!
+ * Used to create new entity IDs by ESCs.
+ */
+int esc_eid_counter;
+static const int DEFAULT_PUBLISH_EXPIRES = 3600;
+
+enum sip_publish_type {
+	SIP_PUBLISH_UNKNOWN,
+	SIP_PUBLISH_INITIAL,
+	SIP_PUBLISH_REFRESH,
+	SIP_PUBLISH_MODIFY,
+	SIP_PUBLISH_REMOVE,
+};
+
+/*!
+ * \brief common ESC items for all event types
+ *
+ * The entity_id field serves as a means by which
+ * A specific entry may be found.
+ */
+struct sip_esc_entry {
+	/*!
+	 * The name of the party who
+	 * sent us the PUBLISH. This will more
+	 * than likely correspond to a peer name.
+	 * 
+	 * This field's utility isn't really that
+	 * great. It's mainly just a user-recognizable
+	 * handle that can be printed in debug messages.
+	 */
+	const char *device_name;
+	/*!
+	 * The entity ID used when corresponding
+	 * with the EPA on the other side. As the
+	 * ESC, we generate an entity ID for each
+	 * received PUBLISH and store it in this
+	 * structure.
+	 */
+	char *entity_id;
+	/*!
+	 * The ID for the scheduler. We schedule
+	 * destruction of a sip_esc_entry when we
+	 * receive a PUBLISH. The destruction is
+	 * scheduled for the duration received in
+	 * the Expires header.
+	 */
+	int sched_id;
+	/*!
+	 * Each ESC entry will be for a specific
+	 * event type. Those entries will need to
+	 * carry data which is intrinsic to the
+	 * ESC entry but which is specific to
+	 * the event package
+	 */
+	void *event_specific_data;
+};
+
+enum sip_cc_state {
+	CC_CLOSED,
+	CC_OPEN,
+};
+
+/*!
+ * \brief Data specific to CC ESC entries.
+ *
+ * This structure lives in the event_specific_data
+ * section of a sip_esc_entry for the CC ESC.
+ */
+struct cc_esc_entry {
+	/*!
+	 * The current state of the device from which
+	 * we have received a PUBLISH.
+	 */
+	enum sip_cc_state current_state;
+	/*!
+	 * The core_id to which this particular ESC entry
+	 * pertains. We can determine this information using
+	 * either the Request-URI to which the received PUBLISH
+	 * was sent, or by seeing in which dialog the PUBLISH
+	 * arrived if sent in the same dialog as the SUBSCRIBE-
+	 * NOTIFY
+	 */
+	int core_id;
+};
+
+/*!
+ * \brief call-completion event state compositor
+ *
+ * The container is of sip_esc_entries. These can
+ * be looked up by their entity ID.
+ */
+struct ao2_container *cc_esc;
+
+/*!
+ * \brief The Event State Compositors
+ *
+ * An Event State Compositor is an entity which
+ * accepts PUBLISH requests and acts appropriately
+ * based on these requests.
+ *
+ * The actual event_state_compositor structure is simply
+ * an ao2_container of sip_esc_entrys. When an incoming
+ * PUBLISH is received, we can match the appropriate sip_esc_entry
+ * using the entity ID of the incoming PUBLISH.
+ */
+static struct event_state_compositor {
+	const char * name;
+	struct ao2_container **compositor;
+} event_state_compositors [] = {
+	{"call-completion", &cc_esc},
+};
+
+static const int ESC_MAX_BUCKETS = 37;
+
+static void esc_entry_destructor(void *obj)
+{
+	struct sip_esc_entry *esc_entry = obj;
+	if (esc_entry->sched_id > -1) {
+		AST_SCHED_DEL(sched, esc_entry->sched_id);
+	}
+	/* We need to add a switch statement or something
+	 * here so that we can call the appropriate event-package
+	 * destructor function on the event_specific_data
+	 */
+}
+
+static int esc_hash_fn(const void *obj, const int flags)
+{
+	const struct sip_esc_entry *entry = obj;
+	return ast_str_hash(entry->entity_id);
+}
+
+static int esc_cmp_fn(void *obj, void *arg, int flags)
+{
+	struct sip_esc_entry *entry1 = obj;
+	struct sip_esc_entry *entry2 = arg;
+
+	return (!strcmp(entry1->entity_id, entry2->entity_id)) ? (CMP_MATCH | CMP_STOP) : 0;
+}
+
+static struct event_state_compositor *get_esc(const char * const event_package) {
+	int i;
+	for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
+		if (!strcasecmp(event_package, event_state_compositors[i].name)) {
+			return &event_state_compositors[i];
+		}
+	}
+	return NULL;
+}
+
+static struct sip_esc_entry *get_esc_entry(const char * entity_id, struct event_state_compositor *esc) {
+	struct sip_esc_entry *entry;
+	struct sip_esc_entry finder = { .entity_id = (char *)entity_id };
+
+	entry = ao2_find(*esc->compositor, &finder, OBJ_POINTER);
+
+	return entry;
+}
+
+static struct sip_esc_entry *create_esc_entry(const char *event)
+{
+	struct sip_esc_entry *esc_entry;
+	struct event_state_compositor *esc = get_esc(event);
+
+	if (!esc) {
+		return NULL;
+	}
+
+	if (!(esc_entry = ao2_alloc(sizeof(*esc_entry), esc_entry_destructor))) {
+		return NULL;
+	}
+
+	ao2_link(*esc->compositor, esc_entry);
+	return esc_entry;
+}
+
+static int initialize_escs(void)
+{
+	int i, res = 0;
+	for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
+		if (!((*event_state_compositors[i].compositor) = 
+					ao2_container_alloc(ESC_MAX_BUCKETS, esc_hash_fn, esc_cmp_fn))) {
+			res = -1;
+		}
+	}
+	return res;
+}
+
+static void destroy_escs(void)
+{
+	int i;
+	for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
+		ao2_ref(event_state_compositors[i].compositor, -1);
+	}
+}
+
 /*! \brief Struct to handle custom SIP notify requests. Dynamically allocated when needed */
 struct sip_notify {
 	struct ast_variable *headers;
@@ -22204,6 +22400,121 @@
 	return 1;
 }
 
+static enum sip_publish_type determine_sip_publish_type(struct sip_request *req, const char * const event, const char * const eid)
+{
+	const char *expires = get_header(req, "Expires");
+	int expires_int;
+	int eid_present = !ast_strlen_zero(eid);
+	int body_present = req->lines > 0;
+
+	if (ast_strlen_zero(expires)) {
+		/* Section 6, item 4, second bullet point of RFC 3903 says to
+		 * use a locally-configured default expiration if none is provided
+		 * in the request
+		 */
+		expires_int = DEFAULT_PUBLISH_EXPIRES;
+	} else if (sscanf(expires, "%30d", &expires_int) != 1) {
+		return SIP_PUBLISH_UNKNOWN;
+	}
+
+	if (expires_int == 0) {
+		return SIP_PUBLISH_REMOVE;
+	} else if (!eid_present && body_present) {
+		return SIP_PUBLISH_INITIAL;
+	} else if (eid_present && !body_present) {
+		return SIP_PUBLISH_REFRESH;
+	} else if (eid_present && body_present) {
+		return SIP_PUBLISH_MODIFY;
+	}
+
+	return SIP_PUBLISH_UNKNOWN;
+}
+
+static int handle_sip_publish_initial(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc)
+{
+	/* XXX STUB */
+	return 0;
+}
+static int handle_sip_publish_refresh(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const eid)
+{
+	/* XXX STUB */
+	return 0;
+}
+static int handle_sip_publish_modify(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const eid)
+{
+	/* XXX STUB */
+	return 0;
+}
+static int handle_sip_publish_remove(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const eid)
+{
+	/* XXX STUB */
+	return 0;
+}
+
+static int handle_request_publish(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, const char *uri)
+{
+	const char *eid = get_header(req, "SIP-If-Match");
+	const char *event = get_header(req, "Event");
+	struct event_state_compositor *esc;
+	enum sip_publish_type publish_type;
+	int res;
+
+	if (ast_strlen_zero(event)) {
+		transmit_response(p, "489 Bad Event", req);
+		return -1;
+	}
+
+	if (!(esc = get_esc(event))) {
+		transmit_response(p, "489 Bad Event", req);
+		return -1;
+	}
+
+	/* Okay, we've taken care of the Bad Event scenarios.
+	 * We're going to have a hard line that PUBLISHes must
+	 * be authenticated.
+	 */
+
+	res = check_user(p, req, SIP_PUBLISH, uri, XMIT_RELIABLE, sin);
+	if (res == AUTH_CHALLENGE_SENT) {
+		return 0;
+	} else if (res < 0) {
+		if (res == AUTH_FAKE_AUTH) {
+			ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From"));
+			transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE);
+		} else {
+			ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
+			transmit_response_reliable(p, "403 Forbidden", req);
+		}
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+		ast_string_field_set(p, theirtag, NULL);
+		return 0;
+	}
+
+	publish_type = determine_sip_publish_type(req, event, eid);
+
+	switch (publish_type) {
+	case SIP_PUBLISH_UNKNOWN:
+		transmit_response(p, "400 Bad Request", req);
+		break;
+	case SIP_PUBLISH_INITIAL:
+		handle_sip_publish_initial(p, req, esc);
+		break;
+	case SIP_PUBLISH_REFRESH:
+		handle_sip_publish_refresh(p, req, esc, eid);
+		break;
+	case SIP_PUBLISH_MODIFY:
+		handle_sip_publish_modify(p, req, esc, eid);
+		break;
+	case SIP_PUBLISH_REMOVE:
+		handle_sip_publish_remove(p, req, esc, eid);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static void add_peer_mwi_subs(struct sip_peer *peer)
 {
 	struct sip_mailbox *mailbox;
@@ -22737,7 +23048,7 @@
 		}
 	}
 
-	if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY)) {
+	if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY || p->method == SIP_PUBLISH)) {
 		transmit_response(p, "400 Bad request", req);
 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
 		return -1;
@@ -22762,6 +23073,9 @@
 		break;
 	case SIP_MESSAGE:
 		res = handle_request_message(p, req);
+		break;
+	case SIP_PUBLISH:
+		res = handle_request_publish(p, req, sin, e);
 		break;
 	case SIP_SUBSCRIBE:
 		res = handle_request_subscribe(p, req, sin, seqno, e);
@@ -26912,6 +27226,7 @@
 	sip_poke_all_peers();	
 	sip_send_all_registers();
 	sip_send_all_mwi_subscriptions();
+	initialize_escs();
 
 	/* And start the monitor for the first time */
 	restart_monitor();
@@ -27019,6 +27334,7 @@
 
 	clear_realm_authentication(authl);
 
+	destroy_escs();
 
 	if (default_tls_cfg.certfile)
 		ast_free(default_tls_cfg.certfile);




More information about the svn-commits mailing list