ast_str_append(tmp, 0, "<?xml version=\"1.0\"?>\n");
ast_str_append(tmp, 0, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%u\" state=\"%s\" entity=\"%s\">\n", p->dialogver, full ? "full" : "partial", mto);
if (data->state > 0 && (data->state & AST_EXTENSION_RINGING) && sip_cfg.notifyringing) {
- const char *local_display = exten;
+ /* Twice the extension length should be enough for XML encoding */
+ char local_display[AST_MAX_EXTENSION * 2];
char *local_target = ast_strdupa(mto);
const char *remote_display = exten;
/* It may seem odd to base the remote_target on the To header here,
*/
char *remote_target = ast_strdupa(mto);
+ ast_xml_escape(exten, local_display, sizeof(local_display));
+
/* There are some limitations to how this works. The primary one is that the
callee must be dialing the same extension that is being monitored. Simply dialing
the hint'd device is not sufficient. */
local_target = ast_alloca(need);
snprintf(local_target, need, "sip:%s@%s", cid_num, p->fromdomain);
- local_display = ast_strdupa(S_COR(ast_channel_caller(callee)->id.name.valid,
- ast_channel_caller(callee)->id.name.str, ""));
+ ast_xml_escape(S_COR(ast_channel_caller(callee)->id.name.valid,
+ ast_channel_caller(callee)->id.name.str, ""),
+ local_display, sizeof(local_display));
connected_num = S_COR(ast_channel_connected(callee)->id.number.valid,
ast_channel_connected(callee)->id.number.str, "");
return outbuf;
}
+int ast_xml_escape(const char *string, char * const outbuf, const size_t buflen)
+{
+ char *dst = outbuf;
+ char *end = outbuf + buflen - 1; /* save one for the null terminator */
+
+ /* Handle the case for the empty output buffer */
+ if (buflen == 0) {
+ return -1;
+ }
+
+ /* Escaping rules from http://www.w3.org/TR/REC-xml/#syntax */
+ /* This also prevents partial entities at the end of a string */
+ while (*string && dst < end) {
+ const char *entity = NULL;
+ int len = 0;
+
+ switch (*string) {
+ case '<':
+ entity = "<";
+ len = 4;
+ break;
+ case '&':
+ entity = "&";
+ len = 5;
+ break;
+ case '>':
+ /* necessary if ]]> is in the string; easier to escape them all */
+ entity = ">";
+ len = 4;
+ break;
+ case '\'':
+ /* necessary in single-quoted strings; easier to escape them all */
+ entity = "'";
+ len = 6;
+ break;
+ case '"':
+ /* necessary in double-quoted strings; easier to escape them all */
+ entity = """;
+ len = 6;
+ break;
+ default:
+ *dst++ = *string++;
+ break;
+ }
+
+ if (entity) {
+ ast_assert(len == strlen(entity));
+ if (end - dst < len) {
+ /* no room for the entity; stop */
+ break;
+ }
+ /* just checked for length; strcpy is fine */
+ strcpy(dst, entity);
+ dst += len;
+ ++string;
+ }
+ }
+ /* Write null terminator */
+ *dst = '\0';
+ /* If any chars are left in string, return failure */
+ return *string == '\0' ? 0 : -1;
+}
+
/*! \brief ast_inet_ntoa: Recursive thread safe replacement of inet_ntoa */
const char *ast_inet_ntoa(struct in_addr ia)
{
--- /dev/null
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Test ast_xml_escape
+ *
+ * \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
+ *
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+ <depend>TEST_FRAMEWORK</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/utils.h"
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+
+static enum ast_test_result_state test_res = AST_TEST_PASS;
+
+static void test_xml(struct ast_test *test, const char *input, const char *expected, int max_len, int expected_res)
+{
+ char actual[256] = "";
+ int res;
+
+ if (max_len == -1) {
+ max_len = sizeof(actual);
+ }
+
+ res = ast_xml_escape(input, actual, max_len);
+ if (res != expected_res) {
+ ast_test_status_update(test, "Expected result '%d', got '%d'\n", expected_res, res);
+ test_res = AST_TEST_FAIL;
+ }
+
+ if (strcmp(expected, actual) != 0) {
+ ast_test_status_update(test, "Expected output '%s', got '%s'\n", expected, actual);
+ test_res = AST_TEST_FAIL;
+ }
+}
+
+AST_TEST_DEFINE(xml_escape_test)
+{
+ char *input;
+ char *expected;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "xml_escape_test";
+ info->category = "/main/xml_escape/";
+ info->summary = "Test XML escaping";
+ info->description =
+ "Test XML escaping";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ test_res = AST_TEST_PASS;
+
+ /* happy path */
+ input = "encode me: <&>'\"";
+ expected = "encode me: <&>'"";
+ test_xml(test, input, expected, -1, 0);
+
+ /* size 0 should fail without changing anything */
+ input = "foo";
+ expected = "";
+ test_xml(test, input, expected, 0, -1);
+
+ /* truncate chars */
+ input = "<truncated>";
+ expected = "<trunc";
+ test_xml(test, input, expected, 10, -1);
+
+ /* truncate entity */
+ input = "trunc<";
+ expected = "trunc";
+ test_xml(test, input, expected, 9, -1);
+
+ return test_res;
+}
+
+static int unload_module(void)
+{
+ AST_TEST_UNREGISTER(xml_escape_test);
+ return 0;
+}
+
+static int load_module(void)
+{
+ AST_TEST_REGISTER(xml_escape_test);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Skeleton (sample) Test");