Index: main/manager.c =================================================================== --- main/manager.c (revision 195520) +++ main/manager.c (working copy) @@ -3682,12 +3682,14 @@ FORMAT_RAW, FORMAT_HTML, FORMAT_XML, + FORMAT_JSON, }; static char *contenttype[] = { - [FORMAT_RAW] = "plain", - [FORMAT_HTML] = "html", - [FORMAT_XML] = "xml", + [FORMAT_RAW] = "text/plain", + [FORMAT_HTML] = "text/html", + [FORMAT_XML] = "text/xml", + [FORMAT_JSON] = "application/json", }; /*! @@ -3823,6 +3825,8 @@ * mode & 1 -> lowercase; * mode & 2 -> replace non-alphanumeric chars with underscore */ +#define LOWERCASE 1 +#define UNDERSCORE_REPLACE 2 static void xml_copy_escape(struct ast_str **out, const char *src, int mode) { /* store in a local buffer to avoid calling ast_str_append too often */ @@ -3841,7 +3845,7 @@ } } - if ( (mode & 2) && !isalnum(*src)) { + if ((mode & UNDERSCORE_REPLACE) && !isalnum(*src)) { *dst++ = '_'; space--; continue; @@ -3874,7 +3878,7 @@ break; default: - *dst++ = mode ? tolower(*src) : *src; + *dst++ = mode & LOWERCASE ? tolower(*src) : *src; space--; } } @@ -3938,8 +3942,8 @@ char *var, *val; const char *objtype = NULL; int in_data = 0; /* parsing data */ - int inobj = 0; - int xml = (format == FORMAT_XML); + int inobj = 0, first = 1; + int xml = (format == FORMAT_XML), json = (format == FORMAT_JSON); struct variable_count *vc = NULL; struct ao2_container *vco = NULL; @@ -3972,13 +3976,15 @@ /* empty line */ if (in_data) { /* close data in Opaque mode */ - ast_str_append(out, 0, xml ? "'" : "\n"); + ast_str_append(out, 0, xml ? "'" : + json ? "\"" : "\n"); in_data = 0; } if (inobj) { /* close block */ ast_str_append(out, 0, xml ? " />\n" : + json ? "}," : "
\r\n"); inobj = 0; ao2_ref(vco, -1); @@ -3991,6 +3997,9 @@ /* start new block */ if (xml) { ast_str_append(out, 0, "<%s", dest, objtype); + } else if (json) { + ast_str_append(out, 0, "{"); + first = 1; } vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn); inobj = 1; @@ -3999,7 +4008,7 @@ if (in_data) { /* Process data field in Opaque mode */ xml_copy_escape(out, val, 0); /* data field */ - ast_str_append(out, 0, xml ? "\n" : "
\n"); + ast_str_append(out, 0, xml ? "\n" : json ? "\n" : "
\n"); continue; } @@ -4017,7 +4026,8 @@ } - ast_str_append(out, 0, xml ? " " : ""); + ast_str_append(out, 0, xml ? " " : json ? (first ? "\"" : ",\"") : ""); + first = 0; if ((vc = ao2_find(vco, var, 0))) { vc->count++; } else { @@ -4028,18 +4038,19 @@ ao2_link(vco, vc); } - xml_copy_escape(out, var, xml ? 1 | 2 : 0); /* data name */ + xml_copy_escape(out, var, xml ? LOWERCASE | UNDERSCORE_REPLACE : 0); /* data name */ if (vc->count > 1) { ast_str_append(out, 0, "-%d", vc->count); } ao2_ref(vc, -1); - ast_str_append(out, 0, xml ? "='" : ""); + ast_str_append(out, 0, xml ? "='" : json ? "\":\"" : ""); xml_copy_escape(out, val, 0); /* data field */ - ast_str_append(out, 0, xml ? "'" : "\n"); + ast_str_append(out, 0, xml ? "'" : json ? "\"" : "\n"); } if (inobj) { ast_str_append(out, 0, xml ? " />
\n" : + json ? "}" : "
\r\n"); ao2_ref(vco, -1); } @@ -4157,7 +4168,7 @@ } ast_str_append(&http_header, 0, - "Content-type: text/%s\r\n" + "Content-type: %s\r\n" "Cache-Control: no-cache;\r\n" "Set-Cookie: mansession_id=\"%08x\"; Version=\"1\"; Max-Age=%d" "Pragma: SuppressEvents\r\n", @@ -4166,6 +4177,8 @@ if (format == FORMAT_XML) { ast_str_append(&out, 0, "\n"); + } else if (format == FORMAT_JSON) { + ast_str_append(&out, 0, "["); } else if (format == FORMAT_HTML) { /* * When handling AMI-over-HTTP in HTML format, we provide a simple form for @@ -4202,14 +4215,14 @@ if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, s.fd, 0))) { ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n"); } else { - if (format == FORMAT_XML || format == FORMAT_HTML) { + if (format == FORMAT_XML || format == FORMAT_HTML || format == FORMAT_JSON) { xml_translate(&out, buf, params, format); } else { ast_str_append(&out, 0, "%s", buf); } munmap(buf, l); } - } else if (format == FORMAT_XML || format == FORMAT_HTML) { + } else if (format == FORMAT_XML || format == FORMAT_HTML || format == FORMAT_JSON) { xml_translate(&out, "", params, format); } fclose(s.f); @@ -4219,6 +4232,8 @@ if (format == FORMAT_XML) { ast_str_append(&out, 0, "\n"); + } else if (format == FORMAT_JSON) { + ast_str_append(&out, 0, "0]"); } else if (format == FORMAT_HTML) { ast_str_append(&out, 0, "\r\n"); } @@ -4606,6 +4621,11 @@ return generic_http_callback(ser, method, FORMAT_RAW, &ser->remote_address, uri, get_params, headers); } +static int mjson_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers) +{ + return generic_http_callback(ser, method, FORMAT_JSON, &ser->remote_address, uri, params, headers); +} + struct ast_http_uri rawmanuri = { .description = "Raw HTTP Manager Event Interface", .uri = "rawman", @@ -4630,7 +4650,15 @@ .key = __FILE__, }; +struct ast_http_uri managerjsonuri = { + .description = "JSON Manager Event Interface", + .uri = "mjson", + .callback = mjson_http_callback, + .data = NULL, + .key = __FILE__, +}; + /* Callback with Digest authentication */ static int auth_manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers) { @@ -5037,6 +5065,9 @@ ast_http_uri_link(&arawmanuri); ast_http_uri_link(&amanageruri); ast_http_uri_link(&amanagerxmluri); + + ast_http_uri_link(&managerjsonuri); + webregged = 1; } } else { @@ -5048,6 +5079,9 @@ ast_http_uri_unlink(&arawmanuri); ast_http_uri_unlink(&amanageruri); ast_http_uri_unlink(&amanagerxmluri); + + ast_http_uri_unlink(&managerjsonuri); + webregged = 0; } }