[asterisk-commits] twilson: branch russell/messaging r315987 - in /team/russell/messaging: chann...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Apr 27 15:22:00 CDT 2011


Author: twilson
Date: Wed Apr 27 15:21:55 2011
New Revision: 315987

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=315987
Log:
Add MESSAGE_DATA function for handling custom headers

Modified:
    team/russell/messaging/channels/chan_sip.c
    team/russell/messaging/doc/asterisk-messaging.txt
    team/russell/messaging/include/asterisk/message.h
    team/russell/messaging/main/message.c

Modified: team/russell/messaging/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/messaging/channels/chan_sip.c?view=diff&rev=315987&r1=315986&r2=315987
==============================================================================
--- team/russell/messaging/channels/chan_sip.c (original)
+++ team/russell/messaging/channels/chan_sip.c Wed Apr 27 15:21:55 2011
@@ -1253,6 +1253,7 @@
 static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration);
 static int transmit_info_with_vidupdate(struct sip_pvt *p);
 static int transmit_message_with_text(struct sip_pvt *p, const char *text, int init, int auth);
+static int transmit_message_with_msg(struct sip_pvt *p, const struct ast_msg *msg);
 static int transmit_refer(struct sip_pvt *p, const char *dest);
 static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten);
 static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate);
@@ -13013,6 +13014,29 @@
 	return res;
 }
 
+/*! \brief Transmit text with SIP MESSAGE method based on an ast_msg */
+static int transmit_message_with_msg(struct sip_pvt *p, const struct ast_msg *msg)
+{
+	struct sip_request req;
+	struct ast_msg_var_iterator *i;
+	const char *var, *val;
+
+	initreqprep(&req, p, SIP_MESSAGE, NULL);
+	ast_string_field_set(p, msg_body, ast_msg_get_body(msg));
+	initialize_initreq(p, &req);
+
+	i = ast_msg_var_iterator_init(msg);
+	while (ast_msg_var_iterator_next(msg, i, &var, &val)) {
+		add_header(&req, var, val);
+		ast_msg_var_unref_current(i);
+	}
+	ast_msg_var_iterator_destroy(i);
+
+	add_text(&req, ast_msg_get_body(msg));
+
+	return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+}
+
 /*! \brief Transmit text with SIP MESSAGE method */
 static int transmit_message_with_text(struct sip_pvt *p, const char *text, int init, int auth)
 {
@@ -15820,6 +15844,24 @@
 	return res < 0 ? -1 : 0;
 }
 
+static void set_message_vars_from_req(struct ast_msg *msg, struct sip_request *req)
+{
+	size_t x;
+	char name_buf[1024] = "";
+	char val_buf[1024] = "";
+	char *c;
+
+	for (x = 0; x < req->headers; x++) {
+		const char *header = REQ_OFFSET_TO_STR(req, header[x]);
+		if ((c = strchr(header, ':'))) {
+			ast_copy_string(name_buf, header, MIN((c - header + 1), sizeof(name_buf)));
+			ast_copy_string(val_buf, ast_skip_blanks(c + 1), sizeof(val_buf));
+			ast_trim_blanks(name_buf);
+			ast_msg_set_var(msg, name_buf, val_buf);
+		}
+	}
+}
+
 AST_THREADSTORAGE(sip_msg_buf);
 
 /*! \brief  Receive SIP MESSAGE method messages
@@ -15939,6 +15981,7 @@
 	if (res) {
 		ast_msg_destroy(msg);
 	} else {
+		set_message_vars_from_req(msg, req);
 		ast_msg_queue(msg);
 	}
 
@@ -23402,7 +23445,7 @@
 
 	/* XXX Does pvt->expiry need to be set? */
 
-	res = transmit_message_with_text(pvt, ast_msg_get_body(msg), 1, 0);
+	res = transmit_message_with_msg(pvt, msg);
 
 	sip_pvt_unlock(pvt);
 	sip_scheddestroy(pvt, DEFAULT_TRANS_TIMEOUT);

Modified: team/russell/messaging/doc/asterisk-messaging.txt
URL: http://svnview.digium.com/svn/asterisk/team/russell/messaging/doc/asterisk-messaging.txt?view=diff&rev=315987&r1=315986&r2=315987
==============================================================================
--- team/russell/messaging/doc/asterisk-messaging.txt (original)
+++ team/russell/messaging/doc/asterisk-messaging.txt Wed Apr 27 15:21:55 2011
@@ -26,8 +26,9 @@
         1.1) Scope
     2) The Asterisk Dialplan
         2.1) MESSAGE() Function
-        2.2) MessageSend() Application
-        2.3) Dialplan Examples
+        2.2) MESSAGE_DATA() Function
+        2.3) MessageSend() Application
+        2.4) Dialplan Examples
     3) Implementation Notes
         3.1) Channels and Threads
         3.2) Message Protocols
@@ -112,7 +113,26 @@
                will always be UTF-8.
 
 
---- 2.2) MessageSend() Application
+--- 2.2) MESSAGE_DATA() Function
+
+    It is useful to be able to add or pass through custom signaling data to an
+outbound message. The MESSAGE_DATA function is used to read any additional data
+on an incoming message, and to set custom data on an outbound message. It was
+designed to be protocol-agnostic, but is currently only implemented for SIP
+messages. All SIP headers of an incoming SIP message are accessable.
+MESSAGE_DATA() may also be used to add custom SIP headers to an outbound
+message.
+
+    MESSAGE_DATA(<argument>):
+        argument - The key of the data to retrieve. For example, to retrieve
+                   the value of the SIP contact header, use "contact" as the
+                   argument.
+
+    To add a custom header "x-custom", call Set(MESSAGE_DATA(x-custom)=val).
+To pass a custom header from an inbound message, call
+Set(MESSAGE_DATA(x-custom)=${MESSAGE_DATA(x-custom)}).
+
+--- 2.3) MessageSend() Application
 
     One of the most important part of the dialplan for messages is forwarding
 the message on to another destination.  For that, a new dialplan application is
@@ -137,7 +157,7 @@
                                to deliver the message for some reason.
 
 
---- 2.3) Dialplan Examples
+--- 2.4) Dialplan Examples
 
 
 Example 1:

Modified: team/russell/messaging/include/asterisk/message.h
URL: http://svnview.digium.com/svn/asterisk/team/russell/messaging/include/asterisk/message.h?view=diff&rev=315987&r1=315986&r2=315987
==============================================================================
--- team/russell/messaging/include/asterisk/message.h (original)
+++ team/russell/messaging/include/asterisk/message.h Wed Apr 27 15:21:55 2011
@@ -115,30 +115,65 @@
 
 /*!
  * \brief Set the 'to' URI of a message
+ *
+ * \retval 0 success
+ * \retval -1 failure
  */
 int __attribute__((format(printf, 2, 3)))
 		ast_msg_set_to(struct ast_msg *msg, const char *fmt, ...);
 
 /*!
  * \brief Set the 'from' URI of a message
+ *
+ * \retval 0 success
+ * \retval -1 failure
  */
 int __attribute__((format(printf, 2, 3)))
 		ast_msg_set_from(struct ast_msg *msg, const char *fmt, ...);
 
 /*!
  * \brief Set the 'body' text of a message (in UTF-8)
+ *
+ * \retval 0 success
+ * \retval -1 failure
  */
 int __attribute__((format(printf, 2, 3)))
 		ast_msg_set_body(struct ast_msg *msg, const char *fmt, ...);
 
 /*!
  * \brief Set the dialplan context for this message
+ *
+ * \retval 0 success
+ * \retval -1 failure
  */
 int __attribute__((format(printf, 2, 3)))
 		ast_msg_set_context(struct ast_msg *msg, const char *fmt, ...);
 
 /*!
+ * \brief Set a variable on the message
+ * \note Setting a variable that already exists overwrites the existing variable value
+ *
+ * \param name Name of variable to set
+ * \param value Value of variable to set
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_msg_set_var(struct ast_msg *msg, const char *name, const char *value);
+
+/*!
+ * \brief Get the specified variable on the message
+ * \note The return value is valid only as long as the ast_message is valid. Hold a reference
+ *       to the message if you plan on storing the return value. 
+ *
+ * \return The value associated with variable "name". NULL if variable not found.
+ */
+const char *ast_msg_get_var(struct ast_msg *msg, const char *name);
+
+/*!
  * \brief Get the body of a message.
+ * \note The return value is valid only as long as the ast_message is valid. Hold a reference
+ *       to the message if you plan on storing the return value. 
  *
  * \return The body of the messsage, encoded in UTF-8.
  */
@@ -154,6 +189,42 @@
  * \retval non-zero failure, message not sent to dialplan
  */
 int ast_msg_queue(struct ast_msg *msg);
+
+/*!
+ * \brief Opaque iterator for msg variables
+ */
+struct ast_msg_var_iterator;
+
+/*!
+ * \brief Create a new message variable iterator
+ * \param msg A message whose variables are to be iterated over
+ *
+ * \return An opaque pointer to the new iterator
+ */
+struct ast_msg_var_iterator *ast_msg_var_iterator_init(const struct ast_msg *msg);
+
+/*!
+ * \brief Get the next variable name and value that is set for sending outbound
+ * \param msg The message with the variables
+ * \param i An iterator created with ast_msg_var_iterator_init
+ * \param name A pointer to the name result pointer
+ * \param value A pointer to the value result pointer
+ *
+ * \retval 0 No more entries
+ * \retval 1 Valid entry
+ */
+int ast_msg_var_iterator_next(const struct ast_msg *msg, struct ast_msg_var_iterator *i, const char **name, const char **value);
+
+/*!
+ * \brief Destroy a message variable iterator
+ * \param i Iterator to be destroyed
+ */
+void ast_msg_var_iterator_destroy(struct ast_msg_var_iterator *i);
+
+/*!
+ * \brief Unref a message var from inside an iterator loop
+ */
+void ast_msg_var_unref_current(struct ast_msg_var_iterator *i);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }

Modified: team/russell/messaging/main/message.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/messaging/main/message.c?view=diff&rev=315987&r1=315986&r2=315987
==============================================================================
--- team/russell/messaging/main/message.c (original)
+++ team/russell/messaging/main/message.c Wed Apr 27 15:21:55 2011
@@ -75,6 +75,26 @@
 			<ref type="application">MessageSend</ref>
 		</see-also>
 	</function>
+	<function name="MESSAGE_DATA" language="en_US">
+		<synopsis>
+			Read or write custom data attached to a message.
+		</synopsis>
+		<syntax argsep="/">
+			<parameter name="argument" required="true">
+			<para>Field of the message to get or set.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>This function will read from or write a value to a text message.
+			It is used both to read the data out of an incoming message, as well as
+			modify a message that will be sent outbound.</para>
+			<para>NOTE: If you want to set an outbound message to carry data in the
+			current message, do Set(MESSAGE_DATA(key)=${MESSAGE_DATA(key)}).</para>
+		</description>
+		<see-also>
+			<ref type="application">MessageSend</ref>
+		</see-also>
+	</function>
 	<application name="MessageSend" language="en_US">
  		<synopsis>
 			Send a text message.
@@ -114,6 +134,16 @@
 	</application>
  ***/
 
+struct msg_data {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(name);
+		AST_STRING_FIELD(value);
+	);
+	unsigned int send:1; /* Whether to send out on outbound messages */
+};
+
+AST_LIST_HEAD_NOLOCK(outhead, msg_data);
+
 /*!
  * \brief A message.
  *
@@ -124,6 +154,7 @@
 	struct ast_str *from;
 	struct ast_str *body;
 	struct ast_str *context;
+	struct ao2_container *vars;
 };
 
 struct ast_msg_tech_holder {
@@ -161,6 +192,17 @@
 	.write = msg_func_write,
 };
 
+static int msg_data_func_read(struct ast_channel *chan, const char *function,
+		char *data, char *buf, size_t len);
+static int msg_data_func_write(struct ast_channel *chan, const char *function,
+		char *data, const char *value);
+
+static struct ast_custom_function msg_data_function = {
+	.name = "MESSAGE_DATA",
+	.read = msg_data_func_read,
+	.write = msg_data_func_write,
+};
+
 static struct ast_frame *chan_msg_read(struct ast_channel *chan);
 static int chan_msg_write(struct ast_channel *chan, struct ast_frame *fr);
 static int chan_msg_indicate(struct ast_channel *chan, int condition,
@@ -260,6 +302,24 @@
 	ao2_ref(msg, -1);
 }
 
+static int msg_data_hash_fn(const void *obj, const int flags)
+{
+	const struct msg_data *data = obj;
+	return ast_str_case_hash(data->name);
+}
+
+static int msg_data_cmp_fn(void *obj, void *arg, int flags)
+{
+	const struct msg_data *one = obj, *two = arg;
+	return !strcasecmp(one->name, two->name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static void msg_data_destructor(void *obj)
+{
+	struct msg_data *data = obj;
+	ast_string_field_free_memory(data);
+}
+
 static void msg_destructor(void *obj)
 {
 	struct ast_msg *msg = obj;
@@ -275,6 +335,8 @@
 
 	ast_free(msg->context);
 	msg->context = NULL;
+
+	ao2_ref(msg->vars, -1);
 }
 
 struct ast_msg *ast_msg_alloc(void)
@@ -304,6 +366,12 @@
 		ao2_ref(msg, -1);
 		return NULL;
 	}
+
+	if (!(msg->vars = ao2_container_alloc(1, msg_data_hash_fn, msg_data_cmp_fn))) {
+		ao2_ref(msg, -1);
+		return NULL;
+	}
+
 	ast_str_set(&msg->context, 0, "default");
 
 	return msg;
@@ -367,6 +435,133 @@
 const char *ast_msg_get_body(const struct ast_msg *msg)
 {
 	return ast_str_buffer(msg->body);
+}
+
+static struct msg_data *msg_data_alloc(void)
+{
+	struct msg_data *data;
+
+	if (!(data = ao2_alloc(sizeof(*data), msg_data_destructor))) {
+		return NULL;
+	}
+
+	if (ast_string_field_init(data, 32)) {
+		ao2_ref(data, -1);
+		return NULL;
+	}
+
+	return data;
+}
+
+static struct msg_data *msg_data_find(struct ao2_container *vars, const char *name)
+{
+	struct msg_data tmp = {
+		.name = name,
+	};
+	return ao2_find(vars, &tmp, OBJ_POINTER);
+}
+
+static int msg_set_var_full(struct ast_msg *msg, const char *name, const char *value, unsigned int outbound)
+{
+	struct msg_data *data;
+
+	if (!(data = msg_data_find(msg->vars, name))) {
+		if (!(data = msg_data_alloc())) {
+			return -1;
+		};
+
+		ast_string_field_set(data, name, name);
+		ast_string_field_set(data, value, value);
+		data->send = outbound;
+		ao2_link(msg->vars, data);
+	} else {
+		if (ast_strlen_zero(value)) {
+			ao2_unlink(msg->vars, data);
+		} else {
+			ast_string_field_set(data, value, value);
+			data->send = outbound;
+		}
+	}
+
+	ao2_ref(data, -1);
+
+	return 0;
+}
+
+static int msg_set_var_outbound(struct ast_msg *msg, const char *name, const char *value)
+{
+	return msg_set_var_full(msg, name, value, 1);
+}
+
+int ast_msg_set_var(struct ast_msg *msg, const char *name, const char *value)
+{
+	return msg_set_var_full(msg, name, value, 0);
+}
+
+const char *ast_msg_get_var(struct ast_msg *msg, const char *name)
+{
+	struct msg_data *data;
+
+	if (!(data = msg_data_find(msg->vars, name))) {
+		return NULL;
+	}
+
+	return data->value;
+}
+
+struct ast_msg_var_iterator {
+	struct ao2_iterator i;
+	struct msg_data *current_used;
+};
+
+struct ast_msg_var_iterator *ast_msg_var_iterator_init(const struct ast_msg *msg)
+{
+	struct ast_msg_var_iterator *i;
+	if (!(i = ast_calloc(1, sizeof(*i)))) {
+		return NULL;
+	}
+
+	i->i = ao2_iterator_init(msg->vars, 0);
+
+	return i;
+}
+
+int ast_msg_var_iterator_next(const struct ast_msg *msg, struct ast_msg_var_iterator *i, const char **name, const char **value)
+{
+	struct msg_data *data;
+
+	/* Skip any that aren't marked for sending out */
+	while ((data = ao2_iterator_next(&i->i)) && !data->send) {
+		ao2_ref(data, -1);
+	}
+
+	if (!data) {
+		return 0;
+	}
+
+	if (data->send) {
+		*name = data->name;
+		*value = data->value;
+	}
+
+	/* Leave the refcount to be cleaned up by the caller with
+	 * ast_msg_var_unref_current after they finish with the pointers to the data */
+	i->current_used = data;
+
+	return 1;
+}
+
+void ast_msg_var_unref_current(struct ast_msg_var_iterator *i) {
+	if (i->current_used) {
+		ao2_ref(i->current_used, -1);
+	}
+	i->current_used = NULL;
+}
+
+void ast_msg_var_iterator_destroy(struct ast_msg_var_iterator *i)
+{
+	ao2_iterator_destroy(&i->i);
+	ast_free(i);
 }
 
 static struct ast_channel *create_msg_q_chan(void)
@@ -626,6 +821,63 @@
 	return 0;
 }
 
+static int msg_data_func_read(struct ast_channel *chan, const char *function,
+		char *data, char *buf, size_t len)
+{
+	struct ast_datastore *ds;
+	struct ast_msg *msg;
+	const char *val;
+
+	ast_channel_lock(chan);
+
+	if (!(ds = ast_channel_datastore_find(chan, &msg_datastore, NULL))) {
+		ast_channel_unlock(chan);
+		ast_log(LOG_ERROR, "No MESSAGE data found on the channel to read.\n");
+		return -1;
+	}
+
+	msg = ds->data;
+	ao2_ref(msg, +1);
+	ast_channel_unlock(chan);
+
+	ao2_lock(msg);
+
+	if ((val = ast_msg_get_var(msg, data))) {
+		ast_copy_string(buf, val, len);
+	}
+
+	ao2_unlock(msg);
+	ao2_ref(msg, -1);
+
+	return 0;
+}
+
+static int msg_data_func_write(struct ast_channel *chan, const char *function,
+		char *data, const char *value)
+{
+	struct ast_datastore *ds;
+	struct ast_msg *msg;
+
+	ast_channel_lock(chan);
+
+	if (!(ds = msg_datastore_find_or_create(chan))) {
+		ast_channel_unlock(chan);
+		return -1;
+	}
+
+	msg = ds->data;
+	ao2_ref(msg, +1);
+	ast_channel_unlock(chan);
+
+	ao2_lock(msg);
+
+	msg_set_var_outbound(msg, data, value);
+
+	ao2_unlock(msg);
+	ao2_ref(msg, -1);
+
+	return 0;
+}
 static int msg_tech_hash(const void *obj, const int flags)
 {
 	struct ast_msg_tech_holder *tech_holder = (struct ast_msg_tech_holder *) obj;
@@ -831,6 +1083,7 @@
 	}
 
 	res = __ast_custom_function_register(&msg_function, NULL);
+	res |= __ast_custom_function_register(&msg_data_function, NULL);
 	res |= ast_register_application2(app_msg_send, msg_send_exec, NULL, NULL, NULL);
 
 	return res;




More information about the asterisk-commits mailing list