/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strescape.h" #include "ostream.h" #include "json-parser.h" #include "doveadm.h" #include "doveadm-server.h" #include "doveadm-print.h" #include "doveadm-print-private.h" #include "client-connection.h" struct doveadm_print_json_context { unsigned int header_idx, header_count; bool first_row; bool in_stream; bool flushed; 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) { i_zero(&ctx); 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; ctx.in_stream = FALSE; } 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 (!ctx.in_stream) { const struct doveadm_print_header *hdr = array_idx(&ctx.headers, ctx.header_idx); doveadm_print_json_value_header(hdr); i_assert((hdr->flags & DOVEADM_PRINT_HEADER_FLAG_NUMBER) == 0); str_append_c(ctx.str, '"'); ctx.in_stream = TRUE; } if (size == 0) { str_append_c(ctx.str, '"'); doveadm_print_json_value_footer(); ctx.in_stream = FALSE; return; } json_append_escaped_data(ctx.str, value, size); 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.flushed) return; ctx.flushed = TRUE; 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 };