113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci#include "tool_setup.h"
2513498266Sopenharmony_ci
2613498266Sopenharmony_ci#define ENABLE_CURLX_PRINTF
2713498266Sopenharmony_ci
2813498266Sopenharmony_ci/* use our own printf() functions */
2913498266Sopenharmony_ci#include "curlx.h"
3013498266Sopenharmony_ci#include "tool_cfgable.h"
3113498266Sopenharmony_ci#include "tool_writeout_json.h"
3213498266Sopenharmony_ci#include "tool_writeout.h"
3313498266Sopenharmony_ci
3413498266Sopenharmony_ci#define MAX_JSON_STRING 100000
3513498266Sopenharmony_ci
3613498266Sopenharmony_ci/* provide the given string in dynbuf as a quoted json string, but without the
3713498266Sopenharmony_ci   outer quotes. The buffer is not inited by this function.
3813498266Sopenharmony_ci
3913498266Sopenharmony_ci   Return 0 on success, non-zero on error.
4013498266Sopenharmony_ci*/
4113498266Sopenharmony_ciint jsonquoted(const char *in, size_t len,
4213498266Sopenharmony_ci               struct curlx_dynbuf *out, bool lowercase)
4313498266Sopenharmony_ci{
4413498266Sopenharmony_ci  const unsigned char *i = (unsigned char *)in;
4513498266Sopenharmony_ci  const unsigned char *in_end = &i[len];
4613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
4713498266Sopenharmony_ci
4813498266Sopenharmony_ci  for(; (i < in_end) && !result; i++) {
4913498266Sopenharmony_ci    switch(*i) {
5013498266Sopenharmony_ci    case '\\':
5113498266Sopenharmony_ci      result = curlx_dyn_addn(out, "\\\\", 2);
5213498266Sopenharmony_ci      break;
5313498266Sopenharmony_ci    case '\"':
5413498266Sopenharmony_ci      result = curlx_dyn_addn(out, "\\\"", 2);
5513498266Sopenharmony_ci      break;
5613498266Sopenharmony_ci    case '\b':
5713498266Sopenharmony_ci      result = curlx_dyn_addn(out, "\\b", 2);
5813498266Sopenharmony_ci      break;
5913498266Sopenharmony_ci    case '\f':
6013498266Sopenharmony_ci      result = curlx_dyn_addn(out, "\\f", 2);
6113498266Sopenharmony_ci      break;
6213498266Sopenharmony_ci    case '\n':
6313498266Sopenharmony_ci      result = curlx_dyn_addn(out, "\\n", 2);
6413498266Sopenharmony_ci      break;
6513498266Sopenharmony_ci    case '\r':
6613498266Sopenharmony_ci      result = curlx_dyn_addn(out, "\\r", 2);
6713498266Sopenharmony_ci      break;
6813498266Sopenharmony_ci    case '\t':
6913498266Sopenharmony_ci      result = curlx_dyn_addn(out, "\\t", 2);
7013498266Sopenharmony_ci      break;
7113498266Sopenharmony_ci    default:
7213498266Sopenharmony_ci      if(*i < 32)
7313498266Sopenharmony_ci        result = curlx_dyn_addf(out, "\\u%04x", *i);
7413498266Sopenharmony_ci      else {
7513498266Sopenharmony_ci        char o = *i;
7613498266Sopenharmony_ci        if(lowercase && (o >= 'A' && o <= 'Z'))
7713498266Sopenharmony_ci          /* do not use tolower() since that's locale specific */
7813498266Sopenharmony_ci          o |= ('a' - 'A');
7913498266Sopenharmony_ci        result = curlx_dyn_addn(out, &o, 1);
8013498266Sopenharmony_ci      }
8113498266Sopenharmony_ci      break;
8213498266Sopenharmony_ci    }
8313498266Sopenharmony_ci  }
8413498266Sopenharmony_ci  if(result)
8513498266Sopenharmony_ci    return (int)result;
8613498266Sopenharmony_ci  return 0;
8713498266Sopenharmony_ci}
8813498266Sopenharmony_ci
8913498266Sopenharmony_civoid jsonWriteString(FILE *stream, const char *in, bool lowercase)
9013498266Sopenharmony_ci{
9113498266Sopenharmony_ci  struct curlx_dynbuf out;
9213498266Sopenharmony_ci  curlx_dyn_init(&out, MAX_JSON_STRING);
9313498266Sopenharmony_ci
9413498266Sopenharmony_ci  if(!jsonquoted(in, strlen(in), &out, lowercase)) {
9513498266Sopenharmony_ci    fputc('\"', stream);
9613498266Sopenharmony_ci    if(curlx_dyn_len(&out))
9713498266Sopenharmony_ci      fputs(curlx_dyn_ptr(&out), stream);
9813498266Sopenharmony_ci    fputc('\"', stream);
9913498266Sopenharmony_ci  }
10013498266Sopenharmony_ci  curlx_dyn_free(&out);
10113498266Sopenharmony_ci}
10213498266Sopenharmony_ci
10313498266Sopenharmony_civoid ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[],
10413498266Sopenharmony_ci                     struct per_transfer *per, CURLcode per_result)
10513498266Sopenharmony_ci{
10613498266Sopenharmony_ci  int i;
10713498266Sopenharmony_ci
10813498266Sopenharmony_ci  fputs("{", stream);
10913498266Sopenharmony_ci
11013498266Sopenharmony_ci  for(i = 0; mappings[i].name != NULL; i++) {
11113498266Sopenharmony_ci    if(mappings[i].writefunc &&
11213498266Sopenharmony_ci       mappings[i].writefunc(stream, &mappings[i], per, per_result, true))
11313498266Sopenharmony_ci      fputs(",", stream);
11413498266Sopenharmony_ci  }
11513498266Sopenharmony_ci
11613498266Sopenharmony_ci  /* The variables are sorted in alphabetical order but as a special case
11713498266Sopenharmony_ci     curl_version (which is not actually a --write-out variable) is last. */
11813498266Sopenharmony_ci  fprintf(stream, "\"curl_version\":");
11913498266Sopenharmony_ci  jsonWriteString(stream, curl_version(), FALSE);
12013498266Sopenharmony_ci  fprintf(stream, "}");
12113498266Sopenharmony_ci}
12213498266Sopenharmony_ci
12313498266Sopenharmony_ci#ifdef _MSC_VER
12413498266Sopenharmony_ci/* warning C4706: assignment within conditional expression */
12513498266Sopenharmony_ci#pragma warning(disable:4706)
12613498266Sopenharmony_ci#endif
12713498266Sopenharmony_ci
12813498266Sopenharmony_civoid headerJSON(FILE *stream, struct per_transfer *per)
12913498266Sopenharmony_ci{
13013498266Sopenharmony_ci  struct curl_header *header;
13113498266Sopenharmony_ci  struct curl_header *prev = NULL;
13213498266Sopenharmony_ci
13313498266Sopenharmony_ci  fputc('{', stream);
13413498266Sopenharmony_ci  while((header = curl_easy_nextheader(per->curl, CURLH_HEADER, -1,
13513498266Sopenharmony_ci                                       prev))) {
13613498266Sopenharmony_ci    if(header->amount > 1) {
13713498266Sopenharmony_ci      if(!header->index) {
13813498266Sopenharmony_ci        /* act on the 0-index entry and pull the others in, then output in a
13913498266Sopenharmony_ci           JSON list */
14013498266Sopenharmony_ci        size_t a = header->amount;
14113498266Sopenharmony_ci        size_t i = 0;
14213498266Sopenharmony_ci        char *name = header->name;
14313498266Sopenharmony_ci        if(prev)
14413498266Sopenharmony_ci          fputs(",\n", stream);
14513498266Sopenharmony_ci        jsonWriteString(stream, header->name, TRUE);
14613498266Sopenharmony_ci        fputc(':', stream);
14713498266Sopenharmony_ci        prev = header;
14813498266Sopenharmony_ci        fputc('[', stream);
14913498266Sopenharmony_ci        do {
15013498266Sopenharmony_ci          jsonWriteString(stream, header->value, FALSE);
15113498266Sopenharmony_ci          if(++i >= a)
15213498266Sopenharmony_ci            break;
15313498266Sopenharmony_ci          fputc(',', stream);
15413498266Sopenharmony_ci          if(curl_easy_header(per->curl, name, i, CURLH_HEADER,
15513498266Sopenharmony_ci                              -1, &header))
15613498266Sopenharmony_ci            break;
15713498266Sopenharmony_ci        } while(1);
15813498266Sopenharmony_ci        fputc(']', stream);
15913498266Sopenharmony_ci      }
16013498266Sopenharmony_ci    }
16113498266Sopenharmony_ci    else {
16213498266Sopenharmony_ci      if(prev)
16313498266Sopenharmony_ci        fputs(",\n", stream);
16413498266Sopenharmony_ci      jsonWriteString(stream, header->name, TRUE);
16513498266Sopenharmony_ci      fputc(':', stream);
16613498266Sopenharmony_ci      fputc('[', stream);
16713498266Sopenharmony_ci      jsonWriteString(stream, header->value, FALSE);
16813498266Sopenharmony_ci      fputc(']', stream);
16913498266Sopenharmony_ci      prev = header;
17013498266Sopenharmony_ci    }
17113498266Sopenharmony_ci  }
17213498266Sopenharmony_ci  fputs("\n}", stream);
17313498266Sopenharmony_ci}
174