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