diff --git a/src/doveadm/Makefile.am b/src/doveadm/Makefile.am index 0e0b2307d9..ac1196af87 100644 --- a/src/doveadm/Makefile.am +++ b/src/doveadm/Makefile.am @@ -127,12 +127,14 @@ doveadm_SOURCES = \ doveadm-print-pager.c \ doveadm-print-tab.c \ doveadm-print-table.c \ + doveadm-print-json.c \ doveadm-pw.c doveadm_server_SOURCES = \ $(common) \ client-connection.c \ doveadm-print-server.c \ + doveadm-print-json.c \ main.c pkginc_libdir = $(pkgincludedir) diff --git a/src/doveadm/doveadm-print-json.c b/src/doveadm/doveadm-print-json.c new file mode 100644 index 0000000000..d5ac28d30f --- /dev/null +++ b/src/doveadm/doveadm-print-json.c @@ -0,0 +1,152 @@ +#include "lib.h" +#include "array.h" +#include "str.h" +#include "strescape.h" +#include "ostream.h" +#include "json-parser.h" +#include "client-connection.h" +#include "doveadm-server.h" +#include "doveadm-print.h" +#include "doveadm-print-private.h" + +struct doveadm_print_json_context { + unsigned int header_idx, header_count; + bool first_row; + ARRAY(struct doveadm_print_header) headers; + pool_t pool; + string_t *str; +}; + +static struct doveadm_print_json_context ctx; + +static void doveadm_print_json_flush_internal(void); + +static void doveadm_print_json_init(void) +{ + ctx.pool = pool_alloconly_create("doveadm json print", 1024); + ctx.str = str_new(ctx.pool, 256); + p_array_init(&ctx.headers, ctx.pool, 1); + ctx.first_row = TRUE; +} + +static void +doveadm_print_json_header(const struct doveadm_print_header *hdr) +{ + struct doveadm_print_header *lhdr; + lhdr = array_append_space(&ctx.headers); + lhdr->key = p_strdup(ctx.pool, hdr->key); + lhdr->flags = hdr->flags; + ctx.header_count++; +} + +static void +doveadm_print_json_value_header(const struct doveadm_print_header *hdr) +{ + // get header name + if (ctx.header_idx == 0) { + if (ctx.first_row == TRUE) { + ctx.first_row = FALSE; + str_append_c(ctx.str, '['); + } else { + str_append_c(ctx.str, ','); + } + str_append_c(ctx.str, '{'); + } else { + str_append_c(ctx.str, ','); + } + + str_append_c(ctx.str, '"'); + json_append_escaped(ctx.str, hdr->key); + str_append_c(ctx.str, '"'); + str_append_c(ctx.str, ':'); +} + +static void +doveadm_print_json_value_footer(void) { + if (++ctx.header_idx == ctx.header_count) { + ctx.header_idx = 0; + str_append_c(ctx.str, '}'); + doveadm_print_json_flush_internal(); + } +} + +static void doveadm_print_json_print(const char *value) +{ + const struct doveadm_print_header *hdr = array_idx(&ctx.headers, ctx.header_idx); + + doveadm_print_json_value_header(hdr); + + if (value == NULL) { + str_append(ctx.str, "null"); + } else if ((hdr->flags & DOVEADM_PRINT_HEADER_FLAG_NUMBER) != 0) { + i_assert(str_is_float(value, '\0')); + str_append(ctx.str, value); + } else { + str_append_c(ctx.str, '"'); + json_append_escaped(ctx.str, value); + str_append_c(ctx.str, '"'); + } + + doveadm_print_json_value_footer(); +} + +static void +doveadm_print_json_print_stream(const unsigned char *value, size_t size) +{ + if (size == 0) { + doveadm_print_json_print(""); + return; + } + + const struct doveadm_print_header *hdr = array_idx(&ctx.headers, ctx.header_idx); + + doveadm_print_json_value_header(hdr); + + if ((hdr->flags & DOVEADM_PRINT_HEADER_FLAG_NUMBER) != 0) { + i_assert(str_is_float((const char*)value, (char)value[size])); + str_append_data(ctx.str, value, size); + } else { + str_append_c(ctx.str, '"'); + json_append_escaped_data(ctx.str, value, size); + str_append_c(ctx.str, '"'); + } + + doveadm_print_json_value_footer(); + + if (str_len(ctx.str) >= IO_BLOCK_SIZE) + doveadm_print_json_flush_internal(); +} + +static void doveadm_print_json_flush_internal(void) +{ + o_stream_nsend(doveadm_print_ostream, str_data(ctx.str), str_len(ctx.str)); + str_truncate(ctx.str, 0); +} + +static void doveadm_print_json_flush(void) +{ + if (ctx.first_row == FALSE) + str_append_c(ctx.str,']'); + else { + str_append_c(ctx.str,'['); + str_append_c(ctx.str,']'); + } + doveadm_print_json_flush_internal(); +} + +static void doveadm_print_json_deinit(void) +{ + pool_unref(&ctx.pool); +} + +struct doveadm_print_vfuncs doveadm_print_json_vfuncs = { + "json", + + doveadm_print_json_init, + doveadm_print_json_deinit, + doveadm_print_json_header, + doveadm_print_json_print, + doveadm_print_json_print_stream, + doveadm_print_json_flush +}; + diff --git a/src/doveadm/doveadm-print-private.h b/src/doveadm/doveadm-print-private.h index 854bfc70e7..f504727fbb 100644 --- a/src/doveadm/doveadm-print-private.h +++ b/src/doveadm/doveadm-print-private.h @@ -25,5 +25,6 @@ extern struct doveadm_print_vfuncs doveadm_print_flow_vfuncs; extern struct doveadm_print_vfuncs doveadm_print_tab_vfuncs; extern struct doveadm_print_vfuncs doveadm_print_table_vfuncs; extern struct doveadm_print_vfuncs doveadm_print_pager_vfuncs; +extern struct doveadm_print_vfuncs doveadm_print_json_vfuncs; #endif diff --git a/src/doveadm/doveadm-print.h b/src/doveadm/doveadm-print.h index 233a020fa8..647d32051d 100644 --- a/src/doveadm/doveadm-print.h +++ b/src/doveadm/doveadm-print.h @@ -5,6 +5,7 @@ #define DOVEADM_PRINT_TYPE_FLOW "flow" #define DOVEADM_PRINT_TYPE_TABLE "table" #define DOVEADM_PRINT_TYPE_SERVER "server" +#define DOVEADM_PRINT_TYPE_JSON "json" enum doveadm_print_header_flags { DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY = 0x01, diff --git a/src/doveadm/doveadm.c b/src/doveadm/doveadm.c index 7a85a310f9..67a595be67 100644 --- a/src/doveadm/doveadm.c +++ b/src/doveadm/doveadm.c @@ -24,6 +24,7 @@ const struct doveadm_print_vfuncs *doveadm_print_vfuncs_all[] = { &doveadm_print_tab_vfuncs, &doveadm_print_table_vfuncs, &doveadm_print_pager_vfuncs, + &doveadm_print_json_vfuncs, NULL }; diff --git a/src/doveadm/main.c b/src/doveadm/main.c index 1b6c468afb..f9314f16b9 100644 --- a/src/doveadm/main.c +++ b/src/doveadm/main.c @@ -62,6 +62,7 @@ static void main_init(void) doveadm_mail_init(); doveadm_load_modules(); doveadm_print_init(DOVEADM_PRINT_TYPE_SERVER); + doveadm_print_init(DOVEADM_PRINT_TYPE_JSON); } static void main_deinit(void)