xref: /third_party/curl/src/tool_writeout_json.c (revision 13498266)
1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24#include "tool_setup.h"
25
26#define ENABLE_CURLX_PRINTF
27
28/* use our own printf() functions */
29#include "curlx.h"
30#include "tool_cfgable.h"
31#include "tool_writeout_json.h"
32#include "tool_writeout.h"
33
34#define MAX_JSON_STRING 100000
35
36/* provide the given string in dynbuf as a quoted json string, but without the
37   outer quotes. The buffer is not inited by this function.
38
39   Return 0 on success, non-zero on error.
40*/
41int jsonquoted(const char *in, size_t len,
42               struct curlx_dynbuf *out, bool lowercase)
43{
44  const unsigned char *i = (unsigned char *)in;
45  const unsigned char *in_end = &i[len];
46  CURLcode result = CURLE_OK;
47
48  for(; (i < in_end) && !result; i++) {
49    switch(*i) {
50    case '\\':
51      result = curlx_dyn_addn(out, "\\\\", 2);
52      break;
53    case '\"':
54      result = curlx_dyn_addn(out, "\\\"", 2);
55      break;
56    case '\b':
57      result = curlx_dyn_addn(out, "\\b", 2);
58      break;
59    case '\f':
60      result = curlx_dyn_addn(out, "\\f", 2);
61      break;
62    case '\n':
63      result = curlx_dyn_addn(out, "\\n", 2);
64      break;
65    case '\r':
66      result = curlx_dyn_addn(out, "\\r", 2);
67      break;
68    case '\t':
69      result = curlx_dyn_addn(out, "\\t", 2);
70      break;
71    default:
72      if(*i < 32)
73        result = curlx_dyn_addf(out, "\\u%04x", *i);
74      else {
75        char o = *i;
76        if(lowercase && (o >= 'A' && o <= 'Z'))
77          /* do not use tolower() since that's locale specific */
78          o |= ('a' - 'A');
79        result = curlx_dyn_addn(out, &o, 1);
80      }
81      break;
82    }
83  }
84  if(result)
85    return (int)result;
86  return 0;
87}
88
89void jsonWriteString(FILE *stream, const char *in, bool lowercase)
90{
91  struct curlx_dynbuf out;
92  curlx_dyn_init(&out, MAX_JSON_STRING);
93
94  if(!jsonquoted(in, strlen(in), &out, lowercase)) {
95    fputc('\"', stream);
96    if(curlx_dyn_len(&out))
97      fputs(curlx_dyn_ptr(&out), stream);
98    fputc('\"', stream);
99  }
100  curlx_dyn_free(&out);
101}
102
103void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[],
104                     struct per_transfer *per, CURLcode per_result)
105{
106  int i;
107
108  fputs("{", stream);
109
110  for(i = 0; mappings[i].name != NULL; i++) {
111    if(mappings[i].writefunc &&
112       mappings[i].writefunc(stream, &mappings[i], per, per_result, true))
113      fputs(",", stream);
114  }
115
116  /* The variables are sorted in alphabetical order but as a special case
117     curl_version (which is not actually a --write-out variable) is last. */
118  fprintf(stream, "\"curl_version\":");
119  jsonWriteString(stream, curl_version(), FALSE);
120  fprintf(stream, "}");
121}
122
123#ifdef _MSC_VER
124/* warning C4706: assignment within conditional expression */
125#pragma warning(disable:4706)
126#endif
127
128void headerJSON(FILE *stream, struct per_transfer *per)
129{
130  struct curl_header *header;
131  struct curl_header *prev = NULL;
132
133  fputc('{', stream);
134  while((header = curl_easy_nextheader(per->curl, CURLH_HEADER, -1,
135                                       prev))) {
136    if(header->amount > 1) {
137      if(!header->index) {
138        /* act on the 0-index entry and pull the others in, then output in a
139           JSON list */
140        size_t a = header->amount;
141        size_t i = 0;
142        char *name = header->name;
143        if(prev)
144          fputs(",\n", stream);
145        jsonWriteString(stream, header->name, TRUE);
146        fputc(':', stream);
147        prev = header;
148        fputc('[', stream);
149        do {
150          jsonWriteString(stream, header->value, FALSE);
151          if(++i >= a)
152            break;
153          fputc(',', stream);
154          if(curl_easy_header(per->curl, name, i, CURLH_HEADER,
155                              -1, &header))
156            break;
157        } while(1);
158        fputc(']', stream);
159      }
160    }
161    else {
162      if(prev)
163        fputs(",\n", stream);
164      jsonWriteString(stream, header->name, TRUE);
165      fputc(':', stream);
166      fputc('[', stream);
167      jsonWriteString(stream, header->value, FALSE);
168      fputc(']', stream);
169      prev = header;
170    }
171  }
172  fputs("\n}", stream);
173}
174