1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2020, 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.haxx.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  ***************************************************************************/
22 #include "tool_setup.h"
23 
24 #define ENABLE_CURLX_PRINTF
25 
26 /* use our own printf() functions */
27 #include "curlx.h"
28 #include "tool_cfgable.h"
29 #include "tool_writeout_json.h"
30 #include "tool_writeout.h"
31 
32 
33 static const char *http_version[] = {
34   "0",   /* CURL_HTTP_VERSION_NONE */
35   "1",   /* CURL_HTTP_VERSION_1_0 */
36   "1.1", /* CURL_HTTP_VERSION_1_1 */
37   "2",   /* CURL_HTTP_VERSION_2 */
38   "3"    /* CURL_HTTP_VERSION_3 */
39 };
40 
jsonEscape(FILE * stream,const char * in)41 static void jsonEscape(FILE *stream, const char *in)
42 {
43   const char *i = in;
44   const char *in_end = in + strlen(in);
45 
46   for(; i < in_end; i++) {
47     switch(*i) {
48     case '\\':
49       fputs("\\\\", stream);
50       break;
51     case '\"':
52       fputs("\\\"", stream);
53       break;
54     case '\b':
55       fputs("\\b", stream);
56       break;
57     case '\f':
58       fputs("\\f", stream);
59       break;
60     case '\n':
61       fputs("\\n", stream);
62       break;
63     case '\r':
64       fputs("\\r", stream);
65       break;
66     case '\t':
67       fputs("\\t", stream);
68       break;
69     default:
70       if (*i < 32) {
71         fprintf(stream, "u%04x", *i);
72       }
73       else {
74         fputc(*i, stream);
75       }
76       break;
77     };
78   }
79 }
80 
writeTime(FILE * str,CURL * curl,const char * key,CURLINFO ci)81 static int writeTime(FILE *str, CURL *curl, const char *key, CURLINFO ci)
82 {
83   curl_off_t val = 0;
84   if(CURLE_OK == curl_easy_getinfo(curl, ci, &val)) {
85     curl_off_t s = val / 1000000l;
86     curl_off_t ms = val % 1000000l;
87     fprintf(str, "\"%s\":%" CURL_FORMAT_CURL_OFF_T
88             ".%06" CURL_FORMAT_CURL_OFF_T, key, s, ms);
89     return 1;
90   }
91   return 0;
92 }
93 
writeString(FILE * str,CURL * curl,const char * key,CURLINFO ci)94 static int writeString(FILE *str, CURL *curl, const char *key, CURLINFO ci)
95 {
96   char *valp = NULL;
97   if((CURLE_OK == curl_easy_getinfo(curl, ci, &valp)) && valp) {
98     fprintf(str, "\"%s\":\"", key);
99     jsonEscape(str, valp);
100     fprintf(str, "\"");
101     return 1;
102   }
103   return 0;
104 }
105 
writeLong(FILE * str,CURL * curl,const char * key,CURLINFO ci,struct per_transfer * per,const struct writeoutvar * wovar)106 static int writeLong(FILE *str, CURL *curl, const char *key, CURLINFO ci,
107                      struct per_transfer *per, const struct writeoutvar *wovar)
108 {
109   if(wovar->id == VAR_NUM_HEADERS) {
110     fprintf(str, "\"%s\":%ld", key, per->num_headers);
111     return 1;
112   }
113   else {
114     long val = 0;
115     if(CURLE_OK == curl_easy_getinfo(curl, ci, &val)) {
116       fprintf(str, "\"%s\":%ld", key, val);
117       return 1;
118     }
119   }
120   return 0;
121 }
122 
writeOffset(FILE * str,CURL * curl,const char * key,CURLINFO ci)123 static int writeOffset(FILE *str, CURL *curl, const char *key, CURLINFO ci)
124 {
125   curl_off_t val = 0;
126   if(CURLE_OK == curl_easy_getinfo(curl, ci, &val)) {
127     fprintf(str, "\"%s\":%" CURL_FORMAT_CURL_OFF_T, key, val);
128     return 1;
129   }
130   return 0;
131 }
132 
writeFilename(FILE * str,const char * key,const char * filename)133 static int writeFilename(FILE *str, const char *key, const char *filename)
134 {
135   if(filename) {
136     fprintf(str, "\"%s\":\"", key);
137     jsonEscape(str, filename);
138     fprintf(str, "\"");
139   }
140   else {
141     fprintf(str, "\"%s\":null", key);
142   }
143   return 1;
144 }
145 
writeVersion(FILE * str,CURL * curl,const char * key,CURLINFO ci)146 static int writeVersion(FILE *str, CURL *curl, const char *key, CURLINFO ci)
147 {
148   long version = 0;
149   if(CURLE_OK == curl_easy_getinfo(curl, ci, &version) &&
150      (version >= 0) &&
151      (version < (long)(sizeof(http_version)/sizeof(char *)))) {
152     fprintf(str, "\"%s\":\"%s\"", key, http_version[version]);
153     return 1;
154   }
155   return 0;
156 }
157 
ourWriteOutJSON(const struct writeoutvar mappings[],CURL * curl,struct per_transfer * per,FILE * stream)158 void ourWriteOutJSON(const struct writeoutvar mappings[], CURL *curl,
159                      struct per_transfer *per, FILE *stream)
160 {
161   int i;
162 
163   fputs("{", stream);
164   for(i = 0; mappings[i].name != NULL; i++) {
165     const struct writeoutvar *wovar = &mappings[i];
166     const char *name = mappings[i].name;
167     CURLINFO cinfo = mappings[i].cinfo;
168     int ok = 0;
169 
170     if(mappings[i].is_ctrl == 1) {
171       continue;
172     }
173 
174     switch(mappings[i].jsontype) {
175     case JSON_STRING:
176       ok = writeString(stream, curl, name, cinfo);
177       break;
178     case JSON_LONG:
179       ok = writeLong(stream, curl, name, cinfo, per, wovar);
180       break;
181     case JSON_OFFSET:
182       ok = writeOffset(stream, curl, name, cinfo);
183       break;
184     case JSON_TIME:
185       ok = writeTime(stream, curl, name, cinfo);
186       break;
187     case JSON_FILENAME:
188       ok = writeFilename(stream, name, per->outs.filename);
189       break;
190     case JSON_VERSION:
191       ok = writeVersion(stream, curl, name, cinfo);
192       break;
193     default:
194       break;
195     }
196 
197     if(ok) {
198       fputs(",", stream);
199     }
200   }
201 
202   fprintf(stream, "\"curl_version\":\"%s\"}", curl_version());
203 }
204