[svn-commits] mjordan: trunk r420098 - in /trunk: ./ channels/ include/asterisk/ main/ res/...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Aug 5 16:44:16 CDT 2014


Author: mjordan
Date: Tue Aug  5 16:44:09 2014
New Revision: 420098

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=420098
Log:
Multiple revisions 420089-420090,420097

........
  r420089 | mjordan | 2014-08-05 15:10:52 -0500 (Tue, 05 Aug 2014) | 72 lines
  
  ARI: Add channel technology agnostic out of call text messaging
  
  This patch adds the ability to send and receive text messages from various
  technology stacks in Asterisk through ARI. This includes chan_sip (sip),
  res_pjsip_messaging (pjsip), and res_xmpp (xmpp). Messages are sent using the
  endpoints resource, and can be sent directly through that resource, or to a
  particular endpoint.
  
  For example, the following would send the message "Hello there" to PJSIP
  endpoint alice with a display URI of sip:asterisk at mycooldomain.org:
  
  ari/endpoints/sendMessage?to=pjsip:alice&from=sip:asterisk at mycooldomain.org&body=Hello+There
  
  This is equivalent to the following as well:
  
  ari/endpoints/PJSIP/alice/sendMessage?from=sip:asterisk at mycooldomain.org&body=Hello+There
  
  Both forms are available for message technologies that allow for arbitrary
  destinations, such as chan_sip.
  
  Inbound messages can now be received over ARI as well. An ARI application that
  subscribes to endpoints will receive messages from those endpoints:
  
  {
    "type": "TextMessageReceived",
    "timestamp": "2014-07-12T22:53:13.494-0500",
    "endpoint": {
      "technology": "PJSIP",
      "resource": "alice",
      "state": "online",
      "channel_ids": []
    },
    "message": {
      "from": "\"alice\" <sip:alice at 127.0.0.1>",
      "to": "pjsip:asterisk at 127.0.0.1",
      "body": "Watson, come here.",
      "variables": []
    },
    "application": "testsuite"
  }
  
  The above was made possible due to some rather major changes in the message
  core. This includes (but is not limited to):
  - Users of the message API can now register message handlers. A handler has
    two callbacks: one to determine if the handler has a destination for the
    message, and another to handle it.
  - All dialplan functionality of handling a message was moved into a message
    handler provided by the message API.
  - Messages can now have the technology/endpoint associated with them.
    Various other properties are also now more easily accessible.
  - A number of ao2 containers that weren't really needed were replaced with
    vectors. Iteration over ao2_containers is expensive and pointless when
    the lifetime of things is well defined and the number of things is very
    small.
  
  res_stasis now has a new file that makes up its structure, messaging. The
  messaging functionality implements a message handler, and passes received
  messages that match an interested endpoint over to the app for processing.
  
  Note that inadvertently while testing this, I reproduced ASTERISK-23969.
  res_pjsip_messaging was incorrectly parsing out the 'to' field, such that
  arbitrary SIP URIs mangled the endpoint lookup. This patch includes the
  fix for that as well.
  
  Review: https://reviewboard.asterisk.org/r/3726
  
  ASTERISK-23692 #close
  Reported by: Matt Jordan
  
  ASTERISK-23969 #close
  Reported by: Andrew Nagy
........
  r420090 | mjordan | 2014-08-05 15:16:37 -0500 (Tue, 05 Aug 2014) | 2 lines
  
  Remove automerge properties :-(
........
  r420097 | mjordan | 2014-08-05 16:36:25 -0500 (Tue, 05 Aug 2014) | 2 lines
  
  test_message: Fix strict-aliasing compilation issue
........

Merged revisions 420089-420090,420097 from http://svn.asterisk.org/svn/asterisk/branches/12

Added:
    trunk/res/stasis/messaging.c
      - copied unchanged from r420097, branches/12/res/stasis/messaging.c
    trunk/res/stasis/messaging.h
      - copied unchanged from r420097, branches/12/res/stasis/messaging.h
    trunk/tests/test_message.c
      - copied unchanged from r420097, branches/12/tests/test_message.c
Modified:
    trunk/   (props changed)
    trunk/CHANGES
    trunk/channels/chan_sip.c
    trunk/include/asterisk/json.h
    trunk/include/asterisk/manager.h
    trunk/include/asterisk/message.h
    trunk/main/json.c
    trunk/main/message.c
    trunk/res/ari/ari_model_validators.c
    trunk/res/ari/ari_model_validators.h
    trunk/res/ari/resource_channels.c
    trunk/res/ari/resource_endpoints.c
    trunk/res/ari/resource_endpoints.h
    trunk/res/res_ari_endpoints.c
    trunk/res/res_pjsip_messaging.c
    trunk/res/res_stasis.c
    trunk/res/res_xmpp.c
    trunk/res/stasis/app.c
    trunk/rest-api/api-docs/endpoints.json
    trunk/rest-api/api-docs/events.json

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-12-merged' - no diff available.

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=420098&r1=420097&r2=420098
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Tue Aug  5 16:44:09 2014
@@ -245,6 +245,17 @@
      recording. This is only available if max_silence_seconds was specified
      when the recording was started.
    Note that all duration values are reported in seconds.
+
+ * Users of ARI can now send and receive out of call text messages. Messages
+   can be sent directly to a particular endpoint, or can be sent to the
+   endpoints resource directly and inferred from the URI scheme. Text
+   messages are passed to ARI clients as TextMessageReceived events. ARI
+   clients can choose to receive text messages by subscribing to the particular
+   endpoint technology or endpoints that they are interested in.
+
+ * The applications resource now supports subscriptions to all endpoints of
+   a particular channel technology. For example, subscribing to an eventSource
+   of 'endpoint:PJSIP' will subscribe to all PJSIP endpoints.
 
 res_pjsip
 ------------------

Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=420098&r1=420097&r2=420098
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Tue Aug  5 16:44:09 2014
@@ -18871,6 +18871,7 @@
 	char *to;
 	char from_name[50];
 	char stripped[SIPBUFSIZE];
+	enum sip_get_dest_result dest_result;
 
 	if (strncmp(content_type, "text/plain", strlen("text/plain"))) { /* No text/plain attachment */
 		transmit_response(p, "415 Unsupported Media Type", req); /* Good enough, or? */
@@ -18980,7 +18981,8 @@
 		ast_string_field_set(p, context, sip_cfg.messagecontext);
 	}
 
-	switch (get_destination(p, NULL, NULL)) {
+	dest_result = get_destination(p, NULL, NULL);
+	switch (dest_result) {
 	case SIP_GET_DEST_REFUSED:
 		/* Okay to send 403 since this is after auth processing */
 		transmit_response(p, "403 Forbidden", req);
@@ -18990,12 +18992,9 @@
 		transmit_response(p, "416 Unsupported URI Scheme", req);
 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
 		return;
-	case SIP_GET_DEST_EXTEN_NOT_FOUND:
-	case SIP_GET_DEST_EXTEN_MATCHMORE:
-		transmit_response(p, "404 Not Found", req);
-		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
-		return;
-	case SIP_GET_DEST_EXTEN_FOUND:
+	default:
+		/* We may have something other than dialplan who wants
+		 * the message, so defer further error handling for now */
 		break;
 	}
 
@@ -19023,7 +19022,9 @@
 	res |= ast_msg_set_context(msg, "%s", p->context);
 
 	res |= ast_msg_set_var(msg, "SIP_RECVADDR", ast_sockaddr_stringify(&p->recv));
+	res |= ast_msg_set_tech(msg, "%s", "SIP");
 	if (!ast_strlen_zero(p->peername)) {
+		res |= ast_msg_set_endpoint(msg, "%s", p->peername);
 		res |= ast_msg_set_var(msg, "SIP_PEERNAME", p->peername);
 	}
 
@@ -19036,12 +19037,32 @@
 	if (res) {
 		ast_msg_destroy(msg);
 		transmit_response(p, "500 Internal Server Error", req);
-	} else {
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+		return;
+	}
+
+	if (ast_msg_has_destination(msg)) {
 		ast_msg_queue(msg);
 		transmit_response(p, "202 Accepted", req);
-	}
-
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+		return;
+	}
+
+	/* Find a specific error cause to send */
+	switch (dest_result) {
+	case SIP_GET_DEST_EXTEN_NOT_FOUND:
+	case SIP_GET_DEST_EXTEN_MATCHMORE:
+		transmit_response(p, "404 Not Found", req);
+		break;
+	case SIP_GET_DEST_EXTEN_FOUND:
+	default:
+		/* We should have sent the message already! */
+		ast_assert(0);
+		transmit_response(p, "500 Internal Server Error", req);
+		break;
+	}
 	sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+	ast_msg_destroy(msg);
 }
 
 /*! \brief  CLI Command to show calls within limits set by call_limit */

Modified: trunk/include/asterisk/json.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/json.h?view=diff&rev=420098&r1=420097&r2=420098
==============================================================================
--- trunk/include/asterisk/json.h (original)
+++ trunk/include/asterisk/json.h Tue Aug  5 16:44:09 2014
@@ -1010,6 +1010,27 @@
  */
 struct ast_json *ast_json_party_id(struct ast_party_id *party);
 
+/*!
+ * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
+ * \since 12.5.0
+ *
+ * \param json_variables The JSON blob containing the variable
+ * \param variables An out reference to the variables to populate.
+ *        The pointer to the variables should be NULL when calling this.
+ *
+ * \code
+ * struct ast_json *json_variables = ast_json_pack("[ { s: s } ]", "foo", "bar");
+ * struct ast_variable *variables = NULL;
+ * int res;
+ *
+ * res = ast_json_to_ast_variables(json_variables, &variables);
+ * \endcode
+ *
+ * \retval 0 success
+ * \retval -1 error
+ */
+int ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables);
+
 /*!@}*/
 
 #endif /* _ASTERISK_JSON_H */

Modified: trunk/include/asterisk/manager.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/manager.h?view=diff&rev=420098&r1=420097&r2=420098
==============================================================================
--- trunk/include/asterisk/manager.h (original)
+++ trunk/include/asterisk/manager.h Tue Aug  5 16:44:09 2014
@@ -94,8 +94,16 @@
 /*! \brief Export manager structures */
 #define AST_MAX_MANHEADERS 128
 
-/*! \brief Manager Helper Function */
-typedef int (*manager_hook_t)(int, const char *, char *);
+/*! \brief Manager Helper Function
+ *
+ * \param category The class authorization category of the event
+ * \param event The name of the event being raised
+ * \param body The body of the event
+ *
+ * \retval 0 Success
+ * \retval non-zero Error
+ */
+typedef int (*manager_hook_t)(int category, const char *event, char *body);
 
 struct manager_custom_hook {
 	/*! Identifier */

Modified: trunk/include/asterisk/message.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/message.h?view=diff&rev=420098&r1=420097&r2=420098
==============================================================================
--- trunk/include/asterisk/message.h (original)
+++ trunk/include/asterisk/message.h Tue Aug  5 16:44:09 2014
@@ -25,8 +25,9 @@
  *
  * The purpose of this API is to provide support for text messages that
  * are not session based.  The messages are passed into the Asterisk core
- * to be routed through the dialplan and potentially sent back out through
- * a message technology that has been registered through this API.
+ * to be routed through the dialplan or another interface and potentially
+ * sent back out through a message technology that has been registered
+ * through this API.
  */
 
 #ifndef __AST_MESSAGE_H__
@@ -91,6 +92,64 @@
 int ast_msg_tech_unregister(const struct ast_msg_tech *tech);
 
 /*!
+ * \brief An external processor of received messages
+ * \since 12.5.0
+ */
+struct ast_msg_handler {
+	/*!
+	 * \brief Name of the message handler
+	 */
+	const char *name;
+
+	/*!
+	 * \brief The function callback that will handle the message
+	 *
+	 * \param msg The message to handle
+	 *
+	 * \retval 0 The handler processed the message successfull
+	 * \retval non-zero The handler passed or could not process the message
+	 */
+	int (* const handle_msg)(struct ast_msg *msg);
+
+	/*!
+	 * \brief Return whether or not the message has a valid destination
+	 *
+	 * A message may be delivered to the dialplan and/or other locations,
+	 * depending on whether or not other handlers have been registered. This
+	 * function is called by the message core to determine if any handler can
+	 * process a message.
+	 *
+	 * \param msg The message to inspect
+	 *
+	 * \retval 0 The message does not have a valid destination
+	 * \retval 1 The message has a valid destination
+	 */
+	int (* const has_destination)(const struct ast_msg *msg);
+};
+
+/*!
+ * \brief Register a \c ast_msg_handler
+ * \since 12.5.0
+ *
+ * \param handler The handler to register
+ *
+ * \retval 0 Success
+ * \retval non-zero Error
+ */
+int ast_msg_handler_register(const struct ast_msg_handler *handler);
+
+/*!
+ * \brief Unregister a \c ast_msg_handler
+ * \since 12.5.0
+ *
+ * \param handler The handler to unregister
+ *
+ * \retval 0 Success
+ * \retval non-zero Error
+ */
+int ast_msg_handler_unregister(const struct ast_msg_handler *handler);
+
+/*!
  * \brief Allocate a message.
  *
  * Allocate a message for the purposes of passing it into the Asterisk core
@@ -162,7 +221,29 @@
  */
 int __attribute__((format(printf, 2, 3)))
 		ast_msg_set_exten(struct ast_msg *msg, const char *fmt, ...);
-	
+
+/*!
+ * \brief Set the technology associated with this message
+ *
+ * \since 12.5.0
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int __attribute__((format(printf, 2, 3)))
+		ast_msg_set_tech(struct ast_msg *msg, const char *fmt, ...);
+
+/*!
+ * \brief Set the technology's endpoint associated with this message
+ *
+ * \since 12.5.0
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int __attribute__((format(printf, 2, 3)))
+		ast_msg_set_endpoint(struct ast_msg *msg, const char *fmt, ...);
+
 /*!
  * \brief Set a variable on the message going to the dialplan.
  * \note Setting a variable that already exists overwrites the existing variable value
@@ -207,6 +288,66 @@
  * \return The body of the messsage, encoded in UTF-8.
  */
 const char *ast_msg_get_body(const struct ast_msg *msg);
+
+/*!
+ * \brief Retrieve the source of this message
+ *
+ * \since 12.5.0
+ *
+ * \param msg The message to get the soure from
+ *
+ * \retval The source of the message
+ * \retval NULL or empty string if the message has no source
+ */
+const char *ast_msg_get_from(const struct ast_msg *msg);
+
+/*!
+ * \brief Retrieve the destination of this message
+ *
+ * \since 12.5.0
+ *
+ * \param msg The message to get the destination from
+ *
+ * \retval The destination of the message
+ * \retval NULL or empty string if the message has no destination
+ */
+const char *ast_msg_get_to(const struct ast_msg *msg);
+
+/*!
+ * \brief Retrieve the technology associated with this message
+ *
+ * \since 12.5.0
+ *
+ * \param msg The message to get the technology from
+ *
+ * \retval The technology of the message
+ * \retval NULL or empty string if the message has no associated technology
+ */
+const char *ast_msg_get_tech(const struct ast_msg *msg);
+
+/*!
+ * \brief Retrieve the endpoint associated with this message
+ *
+ * \since 12.5.0
+ *
+ * \param msg The message to get the endpoint from
+ *
+ * \retval The endpoint associated with the message
+ * \retval NULL or empty string if the message has no associated endpoint
+ */
+const char *ast_msg_get_endpoint(const struct ast_msg *msg);
+
+/*!
+ * \brief Determine if a particular message has a destination via some handler
+ *
+ * \since 12.5.0
+ *
+ * \param msg The message to check
+ *
+ * \retval 0 if the message has no handler that can find a destination
+ * \retval 1 if the message has a handler that can find a destination
+ */
+int ast_msg_has_destination(const struct ast_msg *msg);
 
 /*!
  * \brief Queue a message for routing through the dialplan.

Modified: trunk/main/json.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/json.c?view=diff&rev=420098&r1=420097&r2=420098
==============================================================================
--- trunk/main/json.c (original)
+++ trunk/main/json.c Tue Aug  5 16:44:09 2014
@@ -881,3 +881,33 @@
 
 	return ast_json_ref(json_party_id);
 }
+
+int ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables)
+{
+	struct ast_json_iter *it_json_var;
+
+	*variables = NULL;
+
+	for (it_json_var = ast_json_object_iter(json_variables); it_json_var;
+		 it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) {
+		struct ast_variable *new_var;
+		const char *key = ast_json_object_iter_key(it_json_var);
+
+		if (ast_strlen_zero(key)) {
+			continue;
+		}
+
+		new_var = ast_variable_new(key,
+		                           ast_json_string_get(ast_json_object_iter_value(it_json_var)),
+		                           "");
+		if (!new_var) {
+			ast_variables_destroy(*variables);
+			*variables = NULL;
+			return -1;
+		}
+
+		ast_variable_list_append(variables, new_var);
+	}
+
+	return 0;
+}

Modified: trunk/main/message.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/message.c?view=diff&rev=420098&r1=420097&r2=420098
==============================================================================
--- trunk/main/message.c (original)
+++ trunk/main/message.c Tue Aug  5 16:44:09 2014
@@ -39,6 +39,7 @@
 #include "asterisk/manager.h"
 #include "asterisk/strings.h"
 #include "asterisk/astobj2.h"
+#include "asterisk/vector.h"
 #include "asterisk/app.h"
 #include "asterisk/taskprocessor.h"
 #include "asterisk/message.h"
@@ -201,37 +202,46 @@
 		AST_STRING_FIELD(name);
 		AST_STRING_FIELD(value);
 	);
-	unsigned int send:1; /* Whether to send out on outbound messages */
+	unsigned int send; /* Whether to send out on outbound messages */
 };
 
 AST_LIST_HEAD_NOLOCK(outhead, msg_data);
 
 /*!
  * \brief A message.
- *
- * \todo Consider whether stringfields would be an appropriate optimization here.
  */
 struct ast_msg {
-	struct ast_str *to;
-	struct ast_str *from;
-	struct ast_str *body;
-	struct ast_str *context;
-	struct ast_str *exten;
+	AST_DECLARE_STRING_FIELDS(
+		/*! Where the message is going */
+		AST_STRING_FIELD(to);
+		/*! Where we "say" the message came from */
+		AST_STRING_FIELD(from);
+		/*! The text to send */
+		AST_STRING_FIELD(body);
+		/*! The dialplan context for the message */
+		AST_STRING_FIELD(context);
+		/*! The dialplan extension for the message */
+		AST_STRING_FIELD(exten);
+		/*! An endpoint associated with this message */
+		AST_STRING_FIELD(endpoint);
+		/*! The technology of the endpoint associated with this message */
+		AST_STRING_FIELD(tech);
+	);
+	/*! Technology/dialplan specific variables associated with the message */
 	struct ao2_container *vars;
 };
 
-struct ast_msg_tech_holder {
-	const struct ast_msg_tech *tech;
-	/*!
-	 * \brief A rwlock for this object
-	 *
-	 * a read/write lock must be used to protect the wrapper instead
-	 * of the ao2 lock. A rdlock must be held to read tech_holder->tech.
-	 */
-	ast_rwlock_t tech_lock;
-};
-
-static struct ao2_container *msg_techs;
+/*! \brief Lock for \c msg_techs vector */
+static ast_rwlock_t msg_techs_lock;
+
+/*! \brief Vector of message technologies */
+AST_VECTOR(, const struct ast_msg_tech *) msg_techs;
+
+/*! \brief Lock for \c msg_handlers vector */
+static ast_rwlock_t msg_handlers_lock;
+
+/*! \brief Vector of received message handlers */
+AST_VECTOR(, const struct ast_msg_handler *) msg_handlers;
 
 static struct ast_taskprocessor *msg_q_tp;
 
@@ -387,21 +397,7 @@
 {
 	struct ast_msg *msg = obj;
 
-	ast_free(msg->to);
-	msg->to = NULL;
-
-	ast_free(msg->from);
-	msg->from = NULL;
-
-	ast_free(msg->body);
-	msg->body = NULL;
-
-	ast_free(msg->context);
-	msg->context = NULL;
-
-	ast_free(msg->exten);
-	msg->exten = NULL;
-
+	ast_string_field_free_memory(msg);
 	ao2_ref(msg->vars, -1);
 }
 
@@ -413,27 +409,7 @@
 		return NULL;
 	}
 
-	if (!(msg->to = ast_str_create(32))) {
-		ao2_ref(msg, -1);
-		return NULL;
-	}
-
-	if (!(msg->from = ast_str_create(32))) {
-		ao2_ref(msg, -1);
-		return NULL;
-	}
-
-	if (!(msg->body = ast_str_create(128))) {
-		ao2_ref(msg, -1);
-		return NULL;
-	}
-
-	if (!(msg->context = ast_str_create(16))) {
-		ao2_ref(msg, -1);
-		return NULL;
-	}
-
-	if (!(msg->exten = ast_str_create(16))) {
+	if (ast_string_field_init(msg, 128)) {
 		ao2_ref(msg, -1);
 		return NULL;
 	}
@@ -442,8 +418,7 @@
 		ao2_ref(msg, -1);
 		return NULL;
 	}
-
-	ast_str_set(&msg->context, 0, "default");
+	ast_string_field_set(msg, context, "default");
 
 	return msg;
 }
@@ -457,73 +432,109 @@
 struct ast_msg *ast_msg_destroy(struct ast_msg *msg)
 {
 	ao2_ref(msg, -1);
-
 	return NULL;
 }
 
 int ast_msg_set_to(struct ast_msg *msg, const char *fmt, ...)
 {
 	va_list ap;
-	int res;
 
 	va_start(ap, fmt);
-	res = ast_str_set_va(&msg->to, 0, fmt, ap);
+	ast_string_field_build_va(msg, to, fmt, ap);
 	va_end(ap);
 
-	return res < 0 ? -1 : 0;
+	return 0;
 }
 
 int ast_msg_set_from(struct ast_msg *msg, const char *fmt, ...)
 {
 	va_list ap;
-	int res;
 
 	va_start(ap, fmt);
-	res = ast_str_set_va(&msg->from, 0, fmt, ap);
+	ast_string_field_build_va(msg, from, fmt, ap);
 	va_end(ap);
 
-	return res < 0 ? -1 : 0;
+	return 0;
 }
 
 int ast_msg_set_body(struct ast_msg *msg, const char *fmt, ...)
 {
 	va_list ap;
-	int res;
 
 	va_start(ap, fmt);
-	res = ast_str_set_va(&msg->body, 0, fmt, ap);
+	ast_string_field_build_va(msg, body, fmt, ap);
 	va_end(ap);
 
-	return res < 0 ? -1 : 0;
+	return 0;
 }
 
 int ast_msg_set_context(struct ast_msg *msg, const char *fmt, ...)
 {
 	va_list ap;
-	int res;
 
 	va_start(ap, fmt);
-	res = ast_str_set_va(&msg->context, 0, fmt, ap);
+	ast_string_field_build_va(msg, context, fmt, ap);
 	va_end(ap);
 
-	return res < 0 ? -1 : 0;
+	return 0;
 }
 
 int ast_msg_set_exten(struct ast_msg *msg, const char *fmt, ...)
 {
 	va_list ap;
-	int res;
 
 	va_start(ap, fmt);
-	res = ast_str_set_va(&msg->exten, 0, fmt, ap);
+	ast_string_field_build_va(msg, exten, fmt, ap);
 	va_end(ap);
 
-	return res < 0 ? -1 : 0;
+	return 0;
+}
+
+int ast_msg_set_tech(struct ast_msg *msg, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	ast_string_field_build_va(msg, tech, fmt, ap);
+	va_end(ap);
+
+	return 0;
+}
+
+int ast_msg_set_endpoint(struct ast_msg *msg, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	ast_string_field_build_va(msg, endpoint, fmt, ap);
+	va_end(ap);
+
+	return 0;
 }
 
 const char *ast_msg_get_body(const struct ast_msg *msg)
 {
-	return ast_str_buffer(msg->body);
+	return msg->body;
+}
+
+const char *ast_msg_get_from(const struct ast_msg *msg)
+{
+	return msg->from;
+}
+
+const char *ast_msg_get_to(const struct ast_msg *msg)
+{
+	return msg->to;
+}
+
+const char *ast_msg_get_tech(const struct ast_msg *msg)
+{
+	return msg->tech;
+}
+
+const char *ast_msg_get_endpoint(const struct ast_msg *msg)
+{
+	return msg->endpoint;
 }
 
 static struct msg_data *msg_data_alloc(void)
@@ -713,7 +724,7 @@
 {
 	struct ast_pbx_args pbx_args;
 
-	ast_explicit_goto(chan, ast_str_buffer(msg->context), AS_OR(msg->exten, "s"), 1);
+	ast_explicit_goto(chan, msg->context, S_OR(msg->exten, "s"), 1);
 
 	memset(&pbx_args, 0, sizeof(pbx_args));
 	pbx_args.no_hangup_chan = 1,
@@ -787,18 +798,9 @@
 
 AST_THREADSTORAGE_CUSTOM(msg_q_chan, NULL, destroy_msg_q_chan);
 
-/*!
- * \internal
- * \brief Message queue task processor callback
- *
- * \retval 0 success
- * \retval -1 failure
- *
- * \note Even though this returns a value, the taskprocessor code ignores the value.
- */
-static int msg_q_cb(void *data)
-{
-	struct ast_msg *msg = data;
+/*! \internal \brief Handle a message bound for the dialplan */
+static int dialplan_handle_msg_cb(struct ast_msg *msg)
+{
 	struct ast_channel **chan_p, *chan;
 	struct ast_datastore *ds;
 
@@ -824,15 +826,90 @@
 	msg_route(chan, msg);
 	chan_cleanup(chan);
 
+	return 0;
+}
+
+/*! \internal \brief Determine if a message has a destination in the dialplan */
+static int dialplan_has_destination_cb(const struct ast_msg *msg)
+{
+	if (ast_strlen_zero(msg->context)) {
+		return 0;
+	}
+
+	return ast_exists_extension(NULL, msg->context, S_OR(msg->exten, "s"), 1, NULL);
+}
+
+static struct ast_msg_handler dialplan_msg_handler = {
+	.name = "dialplan",
+	.handle_msg = dialplan_handle_msg_cb,
+	.has_destination = dialplan_has_destination_cb,
+};
+
+/*!
+ * \internal
+ * \brief Message queue task processor callback
+ *
+ * \retval 0 success
+ * \retval non-zero failure
+ *
+ * \note Even though this returns a value, the taskprocessor code ignores the value.
+ */
+static int msg_q_cb(void *data)
+{
+	struct ast_msg *msg = data;
+	int res = 1;
+	int i;
+
+	ast_rwlock_rdlock(&msg_handlers_lock);
+	for (i = 0; i < AST_VECTOR_SIZE(&msg_handlers); i++) {
+		const struct ast_msg_handler *handler = AST_VECTOR_GET(&msg_handlers, i);
+
+		if (!handler->has_destination(msg)) {
+			ast_debug(5, "Handler %s doesn't want message, moving on\n", handler->name);
+			continue;
+		}
+
+		ast_debug(5, "Dispatching message to %s handler", handler->name);
+		res &= handler->handle_msg(msg);
+	}
+	ast_rwlock_unlock(&msg_handlers_lock);
+
+	if (res != 0) {
+		ast_log(LOG_WARNING, "No handler processed message from %s to %s\n",
+			S_OR(msg->from, "<unknown>"), S_OR(msg->to, "<unknown>"));
+	}
+
 	ao2_ref(msg, -1);
 
-	return 0;
+	return res;
+}
+
+int ast_msg_has_destination(const struct ast_msg *msg)
+{
+	int i;
+	int result = 0;
+
+	ast_rwlock_rdlock(&msg_handlers_lock);
+	for (i = 0; i < AST_VECTOR_SIZE(&msg_handlers); i++) {
+		const struct ast_msg_handler *handler = AST_VECTOR_GET(&msg_handlers, i);
+
+		ast_debug(5, "Seeing if %s can handle message\n", handler->name);
+		if (handler->has_destination(msg)) {
+			ast_debug(5, "%s can handle message\n", handler->name);
+			result = 1;
+			break;
+		}
+	}
+	ast_rwlock_unlock(&msg_handlers_lock);
+
+	return result;
 }
 
 int ast_msg_queue(struct ast_msg *msg)
 {
 	int res;
-
+	ast_log(LOG_ERROR, "@@@@@ to: %s from: %s exten: %s context: %s\n",
+		msg->to, msg->from, msg->exten, msg->context);
 	res = ast_taskprocessor_push(msg_q_tp, msg_q_cb, msg);
 	if (res == -1) {
 		ao2_ref(msg, -1);
@@ -899,11 +976,11 @@
 	ao2_lock(msg);
 
 	if (!strcasecmp(data, "to")) {
-		ast_copy_string(buf, ast_str_buffer(msg->to), len);
+		ast_copy_string(buf, msg->to, len);
 	} else if (!strcasecmp(data, "from")) {
-		ast_copy_string(buf, ast_str_buffer(msg->from), len);
+		ast_copy_string(buf, msg->from, len);
 	} else if (!strcasecmp(data, "body")) {
-		ast_copy_string(buf, ast_msg_get_body(msg), len);
+		ast_copy_string(buf, msg->body, len);
 	} else {
 		ast_log(LOG_WARNING, "Invalid argument to MESSAGE(): '%s'\n", data);
 	}
@@ -1041,57 +1118,57 @@
 
 	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;
-	int res = 0;
-
-	ast_rwlock_rdlock(&tech_holder->tech_lock);
-	if (tech_holder->tech) {
-		res = ast_str_case_hash(tech_holder->tech->name);
-	}
-	ast_rwlock_unlock(&tech_holder->tech_lock);
-
-	return res;
-}
-
-static int msg_tech_cmp(void *obj, void *arg, int flags)
-{
-	struct ast_msg_tech_holder *tech_holder = obj;
-	const struct ast_msg_tech_holder *tech_holder2 = arg;
-	int res = 1;
-
-	ast_rwlock_rdlock(&tech_holder->tech_lock);
-	/*
-	 * tech_holder2 is a temporary fake tech_holder.
-	 */
-	if (tech_holder->tech) {
-		res = strcasecmp(tech_holder->tech->name, tech_holder2->tech->name) ? 0 : CMP_MATCH | CMP_STOP;
-	}
-	ast_rwlock_unlock(&tech_holder->tech_lock);
-
-	return res;
-}
-
-static struct ast_msg_tech_holder *msg_find_by_tech(const struct ast_msg_tech *msg_tech, int ao2_flags)
-{
-	struct ast_msg_tech_holder *tech_holder;
-	struct ast_msg_tech_holder tmp_tech_holder = {
-		.tech = msg_tech,
-	};
-
-	ast_rwlock_init(&tmp_tech_holder.tech_lock);
-	tech_holder = ao2_find(msg_techs, &tmp_tech_holder, ao2_flags);
-	ast_rwlock_destroy(&tmp_tech_holder.tech_lock);
-	return tech_holder;
-}
-
-static struct ast_msg_tech_holder *msg_find_by_tech_name(const char *tech_name, int ao2_flags)
-{
-	struct ast_msg_tech tmp_msg_tech = {
-		.name = tech_name,
-	};
-	return msg_find_by_tech(&tmp_msg_tech, ao2_flags);
+
+/*!
+ * \internal \brief Find a \c ast_msg_tech by its technology name
+ *
+ * \param tech_name The name of the message technology
+ *
+ * \note \c msg_techs should be locked via \c msg_techs_lock prior to
+ *       calling this function
+ *
+ * \retval NULL if no \c ast_msg_tech has been registered
+ * \retval \c ast_msg_tech if registered
+ */
+static const struct ast_msg_tech *msg_find_by_tech_name(const char *tech_name)
+{
+	const struct ast_msg_tech *current;
+	int i;
+
+	for (i = 0; i < AST_VECTOR_SIZE(&msg_techs); i++) {
+		current = AST_VECTOR_GET(&msg_techs, i);
+		if (!strcmp(current->name, tech_name)) {
+			return current;
+		}
+	}
+
+	return NULL;
+}
+
+/*!
+ * \internal \brief Find a \c ast_msg_handler by its technology name
+ *
+ * \param tech_name The name of the message technology
+ *
+ * \note \c msg_handlers should be locked via \c msg_handlers_lock
+ *       prior to calling this function
+ *
+ * \retval NULL if no \c ast_msg_handler has been registered
+ * \retval \c ast_msg_handler if registered
+ */
+static const struct ast_msg_handler *msg_handler_find_by_tech_name(const char *tech_name)
+{
+	const struct ast_msg_handler *current;
+	int i;
+
+	for (i = 0; i < AST_VECTOR_SIZE(&msg_handlers); i++) {
+		current = AST_VECTOR_GET(&msg_handlers, i);
+		if (!strcmp(current->name, tech_name)) {
+			return current;
+		}
+	}
+
+	return NULL;
 }
 
 /*!
@@ -1103,7 +1180,7 @@
 	struct ast_datastore *ds;
 	struct ast_msg *msg;
 	char *tech_name;
-	struct ast_msg_tech_holder *tech_holder = NULL;
+	const struct ast_msg_tech *msg_tech;
 	char *parse;
 	int res = -1;
 	AST_DECLARE_APP_ARGS(args,
@@ -1142,9 +1219,10 @@
 	tech_name = ast_strdupa(args.to);
 	tech_name = strsep(&tech_name, ":");
 
-	tech_holder = msg_find_by_tech_name(tech_name, OBJ_POINTER);
-
-	if (!tech_holder) {
+	ast_rwlock_rdlock(&msg_techs_lock);
+	msg_tech = msg_find_by_tech_name(tech_name);
+
+	if (!msg_tech) {
 		ast_log(LOG_WARNING, "No message technology '%s' found.\n", tech_name);
 		pbx_builtin_setvar_helper(chan, "MESSAGE_SEND_STATUS", "INVALID_PROTOCOL");
 		goto exit_cleanup;
@@ -1156,22 +1234,13 @@
 	 * that they could change.
 	 */
 	ao2_lock(msg);
-	ast_rwlock_rdlock(&tech_holder->tech_lock);
-	if (tech_holder->tech) {
-		res = tech_holder->tech->msg_send(msg, S_OR(args.to, ""),
-							S_OR(args.from, ""));
-	}
-	ast_rwlock_unlock(&tech_holder->tech_lock);
+	res = msg_tech->msg_send(msg, S_OR(args.to, ""), S_OR(args.from, ""));
 	ao2_unlock(msg);
 
 	pbx_builtin_setvar_helper(chan, "MESSAGE_SEND_STATUS", res ? "FAILURE" : "SUCCESS");
 
 exit_cleanup:
-	if (tech_holder) {
-		ao2_ref(tech_holder, -1);
-		tech_holder = NULL;
-	}
-
+	ast_rwlock_unlock(&msg_techs_lock);
 	ao2_ref(msg, -1);
 
 	return 0;
@@ -1187,7 +1256,7 @@
 	char *tech_name = NULL;
 	struct ast_variable *vars = NULL;
 	struct ast_variable *data = NULL;
-	struct ast_msg_tech_holder *tech_holder = NULL;
+	const struct ast_msg_tech *msg_tech;
 	struct ast_msg *msg;
 	int res = -1;
 
@@ -1204,15 +1273,16 @@
 	tech_name = ast_strdupa(to);
 	tech_name = strsep(&tech_name, ":");
 
-	tech_holder = msg_find_by_tech_name(tech_name, OBJ_POINTER);
-
-	if (!tech_holder) {
+	ast_rwlock_rdlock(&msg_techs_lock);
+	msg_tech = msg_find_by_tech_name(tech_name);
+	if (!msg_tech) {
+		ast_rwlock_unlock(&msg_techs_lock);
 		astman_send_error(s, m, "Message technology not found.");
-		return -1;
+		return 0;
 	}
 
 	if (!(msg = ast_msg_alloc())) {
-		ao2_ref(tech_holder, -1);
+		ast_rwlock_unlock(&msg_techs_lock);
 		astman_send_error(s, m, "Internal failure\n");
 		return -1;
 	}
@@ -1224,14 +1294,11 @@
 
 	ast_msg_set_body(msg, "%s", body);
 
-	ast_rwlock_rdlock(&tech_holder->tech_lock);
-	if (tech_holder->tech) {
-		res = tech_holder->tech->msg_send(msg, S_OR(to, ""), S_OR(from, ""));
-	}
-	ast_rwlock_unlock(&tech_holder->tech_lock);
+	res = msg_tech->msg_send(msg, S_OR(to, ""), S_OR(from, ""));
+
+	ast_rwlock_unlock(&msg_techs_lock);
 
 	ast_variables_destroy(vars);
-	ao2_ref(tech_holder, -1);
 	ao2_ref(msg, -1);
 
 	if (res) {
@@ -1245,7 +1312,7 @@
 int ast_msg_send(struct ast_msg *msg, const char *to, const char *from)
 {
 	char *tech_name = NULL;
-	struct ast_msg_tech_holder *tech_holder = NULL;
+	const struct ast_msg_tech *msg_tech;
 	int res = -1;
 
 	if (ast_strlen_zero(to)) {
@@ -1256,20 +1323,19 @@
 	tech_name = ast_strdupa(to);
 	tech_name = strsep(&tech_name, ":");
 
-	tech_holder = msg_find_by_tech_name(tech_name, OBJ_POINTER);
-
-	if (!tech_holder) {
-		ao2_ref(msg, -1);
-		return -1;
-	}
-
-	ast_rwlock_rdlock(&tech_holder->tech_lock);
-	if (tech_holder->tech) {
-		res = tech_holder->tech->msg_send(msg, S_OR(to, ""), S_OR(from, ""));
-	}
-	ast_rwlock_unlock(&tech_holder->tech_lock);
-
-	ao2_ref(tech_holder, -1);
+	ast_rwlock_rdlock(&msg_techs_lock);
+	msg_tech = msg_find_by_tech_name(tech_name);
+
+	if (!msg_tech) {
+		ast_log(LOG_ERROR, "Unknown message tech: %s\n", tech_name);
+		ast_rwlock_unlock(&msg_techs_lock);
+		return -1;
+	}
+
+	res = msg_tech->msg_send(msg, S_OR(to, ""), S_OR(from, ""));
+
+	ast_rwlock_unlock(&msg_techs_lock);
+
 	ao2_ref(msg, -1);
 
 	return res;
@@ -1277,52 +1343,111 @@
 
 int ast_msg_tech_register(const struct ast_msg_tech *tech)
 {
-	struct ast_msg_tech_holder *tech_holder;
-
-	if ((tech_holder = msg_find_by_tech(tech, OBJ_POINTER))) {
-		ao2_ref(tech_holder, -1);
+	const struct ast_msg_tech *match;
+
+	ast_rwlock_wrlock(&msg_techs_lock);
+
+	match = msg_find_by_tech_name(tech->name);
+	if (match) {
 		ast_log(LOG_ERROR, "Message technology already registered for '%s'\n",
-				tech->name);
-		return -1;
-	}
-
-	if (!(tech_holder = ao2_alloc(sizeof(*tech_holder), NULL))) {
-		return -1;
-	}
-
-	ast_rwlock_init(&tech_holder->tech_lock);
-	tech_holder->tech = tech;
-
-	ao2_link(msg_techs, tech_holder);
-
-	ao2_ref(tech_holder, -1);
-	tech_holder = NULL;
-
-	ast_verb(3, "Message technology handler '%s' registered.\n", tech->name);
-
-	return 0;
+		        tech->name);
+		ast_rwlock_unlock(&msg_techs_lock);
+		return -1;
+	}
+
+	AST_VECTOR_APPEND(&msg_techs, tech);
+	ast_verb(3, "Message technology '%s' registered.\n", tech->name);
+
+	ast_rwlock_unlock(&msg_techs_lock);
+
+	return 0;
+}
+
+/*!
+ * \brief Comparison callback for \c ast_msg_tech vector removal
+ *
+ * \param vec_elem The element in the vector being compared
+ * \param srch The element being looked up
+ *
+ * \retval non-zero The items are equal
+ * \retval 0 The items are not equal
+ */
+static int msg_tech_cmp(const struct ast_msg_tech *vec_elem, const struct ast_msg_tech *srch)
+{
+	return !strcmp(vec_elem->name, srch->name);
 }
 
 int ast_msg_tech_unregister(const struct ast_msg_tech *tech)
 {
-	struct ast_msg_tech_holder *tech_holder;
-
-	tech_holder = msg_find_by_tech(tech, OBJ_POINTER | OBJ_UNLINK);
-
-	if (!tech_holder) {
+	int match;
+
+	ast_rwlock_wrlock(&msg_techs_lock);
+	match = AST_VECTOR_REMOVE_CMP_UNORDERED(&msg_techs, tech, msg_tech_cmp,
+	                                        AST_VECTOR_ELEM_CLEANUP_NOOP);
+	ast_rwlock_unlock(&msg_techs_lock);
+
+	if (match) {
 		ast_log(LOG_ERROR, "No '%s' message technology found.\n", tech->name);
 		return -1;
 	}
 
-	ast_rwlock_wrlock(&tech_holder->tech_lock);
-	tech_holder->tech = NULL;
-	ast_rwlock_unlock(&tech_holder->tech_lock);
-
-	ao2_ref(tech_holder, -1);
-	tech_holder = NULL;
-
-	ast_verb(3, "Message technology handler '%s' unregistered.\n", tech->name);
-
+	ast_verb(2, "Message technology '%s' unregistered.\n", tech->name);
+
+	return 0;
+}
+
+int ast_msg_handler_register(const struct ast_msg_handler *handler)
+{
+	const struct ast_msg_handler *match;
+
+	ast_rwlock_wrlock(&msg_handlers_lock);
+
+	match = msg_handler_find_by_tech_name(handler->name);
+	if (match) {
+		ast_log(LOG_ERROR, "Message handler already registered for '%s'\n",
+		        handler->name);
+		ast_rwlock_unlock(&msg_handlers_lock);
+		return -1;
+	}
+
+	AST_VECTOR_APPEND(&msg_handlers, handler);
+	ast_verb(2, "Message handler '%s' registered.\n", handler->name);
+
+	ast_rwlock_unlock(&msg_handlers_lock);
+
+	return 0;
+
+}
+
+/*!
+ * \brief Comparison callback for \c ast_msg_handler vector removal
+ *
+ * \param vec_elem The element in the vector being compared
+ * \param srch The element being looked up
+ *
+ * \retval non-zero The items are equal
+ * \retval 0 The items are not equal
+ */
+static int msg_handler_cmp(const struct ast_msg_handler *vec_elem, const struct ast_msg_handler *srch)
+{
+	return !strcmp(vec_elem->name, srch->name);
+}
+
+int ast_msg_handler_unregister(const struct ast_msg_handler *handler)
+{
+	int match;
+
+	ast_rwlock_wrlock(&msg_handlers_lock);
+	match = AST_VECTOR_REMOVE_CMP_UNORDERED(&msg_handlers, handler, msg_handler_cmp,
+	                                        AST_VECTOR_ELEM_CLEANUP_NOOP);
+	ast_rwlock_unlock(&msg_handlers_lock);
+
+	if (match) {
+		ast_log(LOG_ERROR, "No '%s' message handler found.\n", handler->name);
+		return -1;
+	}
+
+	ast_verb(3, "Message handler '%s' unregistered.\n", handler->name);
 	return 0;
 }
 
@@ -1343,15 +1468,18 @@
  */
 static void message_shutdown(void)
 {
+	ast_msg_handler_unregister(&dialplan_msg_handler);
+
 	ast_custom_function_unregister(&msg_function);
 	ast_custom_function_unregister(&msg_data_function);
 	ast_unregister_application(app_msg_send);
 	ast_manager_unregister("MessageSend");
 
-	if (msg_techs) {
-		ao2_ref(msg_techs, -1);
-		msg_techs = NULL;
-	}
+	AST_VECTOR_FREE(&msg_techs);
+	ast_rwlock_destroy(&msg_techs_lock);
+
+	AST_VECTOR_FREE(&msg_handlers);
+	ast_rwlock_destroy(&msg_handlers_lock);
 }
 
 /*
@@ -1373,12 +1501,19 @@
 		return -1;
 	}
 
-	msg_techs = ao2_container_alloc(17, msg_tech_hash, msg_tech_cmp);
-	if (!msg_techs) {
-		return -1;
-	}
-
-	res = __ast_custom_function_register(&msg_function, NULL);
+	ast_rwlock_init(&msg_techs_lock);
+	if (AST_VECTOR_INIT(&msg_techs, 8)) {
+		return -1;
+	}
+
+	ast_rwlock_init(&msg_handlers_lock);
+	if (AST_VECTOR_INIT(&msg_handlers, 4)) {
+		return -1;
+	}
+
+	res = ast_msg_handler_register(&dialplan_msg_handler);
+
+	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);
 	res |= ast_manager_register_xml_core("MessageSend", EVENT_FLAG_MESSAGE, action_messagesend);

Modified: trunk/res/ari/ari_model_validators.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/ari/ari_model_validators.c?view=diff&rev=420098&r1=420097&r2=420098
==============================================================================
--- trunk/res/ari/ari_model_validators.c (original)
+++ trunk/res/ari/ari_model_validators.c Tue Aug  5 16:44:09 2014
@@ -588,6 +588,140 @@
 	return ast_ari_validate_endpoint;
 }
 
+int ast_ari_validate_text_message(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_body = 0;
+	int has_from = 0;
+	int has_to = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("body", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_body = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI TextMessage field body failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("from", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_from = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {

[... 1476 lines stripped ...]



More information about the svn-commits mailing list