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 #define ENABLE_CURLX_PRINTF
24 /* use our own printf() functions */
25 #include "curlx.h"
26 #include "tool_cfgable.h"
27 #include "tool_writeout.h"
28 #include "tool_writeout_json.h"
29
30 #include "memdebug.h" /* keep this as LAST include */
31
32 static const struct writeoutvar variables[] = {
33 {"url_effective", VAR_EFFECTIVE_URL, 0,
34 CURLINFO_EFFECTIVE_URL, JSON_STRING},
35 {"http_code", VAR_HTTP_CODE, 0,
36 CURLINFO_RESPONSE_CODE, JSON_LONG},
37 {"response_code", VAR_HTTP_CODE, 0,
38 CURLINFO_RESPONSE_CODE, JSON_LONG},
39 {"http_connect", VAR_HTTP_CODE_PROXY, 0,
40 CURLINFO_HTTP_CONNECTCODE, JSON_LONG},
41 {"time_total", VAR_TOTAL_TIME, 0,
42 CURLINFO_TOTAL_TIME_T, JSON_TIME},
43 {"time_namelookup", VAR_NAMELOOKUP_TIME, 0,
44 CURLINFO_NAMELOOKUP_TIME_T, JSON_TIME},
45 {"time_connect", VAR_CONNECT_TIME, 0,
46 CURLINFO_CONNECT_TIME_T, JSON_TIME},
47 {"time_appconnect", VAR_APPCONNECT_TIME, 0,
48 CURLINFO_APPCONNECT_TIME_T, JSON_TIME},
49 {"time_pretransfer", VAR_PRETRANSFER_TIME, 0,
50 CURLINFO_PRETRANSFER_TIME_T, JSON_TIME},
51 {"time_starttransfer", VAR_STARTTRANSFER_TIME, 0,
52 CURLINFO_STARTTRANSFER_TIME_T, JSON_TIME},
53 {"size_header", VAR_HEADER_SIZE, 0,
54 CURLINFO_HEADER_SIZE, JSON_LONG},
55 {"size_request", VAR_REQUEST_SIZE, 0,
56 CURLINFO_REQUEST_SIZE, JSON_LONG},
57 {"size_download", VAR_SIZE_DOWNLOAD, 0,
58 CURLINFO_SIZE_DOWNLOAD_T, JSON_OFFSET},
59 {"size_upload", VAR_SIZE_UPLOAD, 0,
60 CURLINFO_SIZE_UPLOAD_T, JSON_OFFSET},
61 {"speed_download", VAR_SPEED_DOWNLOAD, 0,
62 CURLINFO_SPEED_DOWNLOAD_T, JSON_OFFSET},
63 {"speed_upload", VAR_SPEED_UPLOAD, 0,
64 CURLINFO_SPEED_UPLOAD_T, JSON_OFFSET},
65 {"content_type", VAR_CONTENT_TYPE, 0,
66 CURLINFO_CONTENT_TYPE, JSON_STRING},
67 {"num_connects", VAR_NUM_CONNECTS, 0,
68 CURLINFO_NUM_CONNECTS, JSON_LONG},
69 {"time_redirect", VAR_REDIRECT_TIME, 0,
70 CURLINFO_REDIRECT_TIME_T, JSON_TIME},
71 {"num_redirects", VAR_REDIRECT_COUNT, 0,
72 CURLINFO_REDIRECT_COUNT, JSON_LONG},
73 {"ftp_entry_path", VAR_FTP_ENTRY_PATH, 0,
74 CURLINFO_FTP_ENTRY_PATH, JSON_STRING},
75 {"redirect_url", VAR_REDIRECT_URL, 0,
76 CURLINFO_REDIRECT_URL, JSON_STRING},
77 {"ssl_verify_result", VAR_SSL_VERIFY_RESULT, 0,
78 CURLINFO_SSL_VERIFYRESULT, JSON_LONG},
79 {"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT, 0,
80 CURLINFO_PROXY_SSL_VERIFYRESULT, JSON_LONG},
81 {"filename_effective", VAR_EFFECTIVE_FILENAME, 0,
82 0, JSON_FILENAME},
83 {"remote_ip", VAR_PRIMARY_IP, 0,
84 CURLINFO_PRIMARY_IP, JSON_STRING},
85 {"remote_port", VAR_PRIMARY_PORT, 0,
86 CURLINFO_PRIMARY_PORT, JSON_LONG},
87 {"local_ip", VAR_LOCAL_IP, 0,
88 CURLINFO_LOCAL_IP, JSON_STRING},
89 {"local_port", VAR_LOCAL_PORT, 0,
90 CURLINFO_LOCAL_PORT, JSON_LONG},
91 {"http_version", VAR_HTTP_VERSION, 0,
92 CURLINFO_HTTP_VERSION, JSON_VERSION},
93 {"scheme", VAR_SCHEME, 0,
94 CURLINFO_SCHEME, JSON_STRING},
95 {"stdout", VAR_STDOUT, 1,
96 0, JSON_NONE},
97 {"stderr", VAR_STDERR, 1,
98 0, JSON_NONE},
99 {"json", VAR_JSON, 1,
100 0, JSON_NONE},
101 {NULL, VAR_NONE, 1,
102 0, JSON_NONE}
103 };
104
ourWriteOut(CURL * curl,struct OutStruct * outs,const char * writeinfo)105 void ourWriteOut(CURL *curl, struct OutStruct *outs, const char *writeinfo)
106 {
107 FILE *stream = stdout;
108 const char *ptr = writeinfo;
109 char *stringp = NULL;
110 long longinfo;
111 double doubleinfo;
112
113 while(ptr && *ptr) {
114 if('%' == *ptr && ptr[1]) {
115 if('%' == ptr[1]) {
116 /* an escaped %-letter */
117 fputc('%', stream);
118 ptr += 2;
119 }
120 else {
121 /* this is meant as a variable to output */
122 char *end;
123 if('{' == ptr[1]) {
124 char keepit;
125 int i;
126 bool match = FALSE;
127 end = strchr(ptr, '}');
128 ptr += 2; /* pass the % and the { */
129 if(!end) {
130 fputs("%{", stream);
131 continue;
132 }
133 keepit = *end;
134 *end = 0; /* null-terminate */
135 for(i = 0; variables[i].name; i++) {
136 if(curl_strequal(ptr, variables[i].name)) {
137 match = TRUE;
138 switch(variables[i].id) {
139 case VAR_EFFECTIVE_URL:
140 if((CURLE_OK ==
141 curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &stringp))
142 && stringp)
143 fputs(stringp, stream);
144 break;
145 case VAR_HTTP_CODE:
146 if(CURLE_OK ==
147 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &longinfo))
148 fprintf(stream, "%03ld", longinfo);
149 break;
150 case VAR_HTTP_CODE_PROXY:
151 if(CURLE_OK ==
152 curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE,
153 &longinfo))
154 fprintf(stream, "%03ld", longinfo);
155 break;
156 case VAR_HEADER_SIZE:
157 if(CURLE_OK ==
158 curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &longinfo))
159 fprintf(stream, "%ld", longinfo);
160 break;
161 case VAR_REQUEST_SIZE:
162 if(CURLE_OK ==
163 curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &longinfo))
164 fprintf(stream, "%ld", longinfo);
165 break;
166 case VAR_NUM_CONNECTS:
167 if(CURLE_OK ==
168 curl_easy_getinfo(curl, CURLINFO_NUM_CONNECTS, &longinfo))
169 fprintf(stream, "%ld", longinfo);
170 break;
171 case VAR_REDIRECT_COUNT:
172 if(CURLE_OK ==
173 curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &longinfo))
174 fprintf(stream, "%ld", longinfo);
175 break;
176 case VAR_REDIRECT_TIME:
177 if(CURLE_OK ==
178 curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME,
179 &doubleinfo))
180 fprintf(stream, "%.6f", doubleinfo);
181 break;
182 case VAR_TOTAL_TIME:
183 if(CURLE_OK ==
184 curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &doubleinfo))
185 fprintf(stream, "%.6f", doubleinfo);
186 break;
187 case VAR_NAMELOOKUP_TIME:
188 if(CURLE_OK ==
189 curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME,
190 &doubleinfo))
191 fprintf(stream, "%.6f", doubleinfo);
192 break;
193 case VAR_CONNECT_TIME:
194 if(CURLE_OK ==
195 curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &doubleinfo))
196 fprintf(stream, "%.6f", doubleinfo);
197 break;
198 case VAR_APPCONNECT_TIME:
199 if(CURLE_OK ==
200 curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME,
201 &doubleinfo))
202 fprintf(stream, "%.6f", doubleinfo);
203 break;
204 case VAR_PRETRANSFER_TIME:
205 if(CURLE_OK ==
206 curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME,
207 &doubleinfo))
208 fprintf(stream, "%.6f", doubleinfo);
209 break;
210 case VAR_STARTTRANSFER_TIME:
211 if(CURLE_OK ==
212 curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME,
213 &doubleinfo))
214 fprintf(stream, "%.6f", doubleinfo);
215 break;
216 case VAR_SIZE_UPLOAD:
217 if(CURLE_OK ==
218 curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, &doubleinfo))
219 fprintf(stream, "%.0f", doubleinfo);
220 break;
221 case VAR_SIZE_DOWNLOAD:
222 if(CURLE_OK ==
223 curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD,
224 &doubleinfo))
225 fprintf(stream, "%.0f", doubleinfo);
226 break;
227 case VAR_SPEED_DOWNLOAD:
228 if(CURLE_OK ==
229 curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD,
230 &doubleinfo))
231 fprintf(stream, "%.3f", doubleinfo);
232 break;
233 case VAR_SPEED_UPLOAD:
234 if(CURLE_OK ==
235 curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &doubleinfo))
236 fprintf(stream, "%.3f", doubleinfo);
237 break;
238 case VAR_CONTENT_TYPE:
239 if((CURLE_OK ==
240 curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &stringp))
241 && stringp)
242 fputs(stringp, stream);
243 break;
244 case VAR_FTP_ENTRY_PATH:
245 if((CURLE_OK ==
246 curl_easy_getinfo(curl, CURLINFO_FTP_ENTRY_PATH, &stringp))
247 && stringp)
248 fputs(stringp, stream);
249 break;
250 case VAR_REDIRECT_URL:
251 if((CURLE_OK ==
252 curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &stringp))
253 && stringp)
254 fputs(stringp, stream);
255 break;
256 case VAR_SSL_VERIFY_RESULT:
257 if(CURLE_OK ==
258 curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT,
259 &longinfo))
260 fprintf(stream, "%ld", longinfo);
261 break;
262 case VAR_PROXY_SSL_VERIFY_RESULT:
263 if(CURLE_OK ==
264 curl_easy_getinfo(curl, CURLINFO_PROXY_SSL_VERIFYRESULT,
265 &longinfo))
266 fprintf(stream, "%ld", longinfo);
267 break;
268 case VAR_EFFECTIVE_FILENAME:
269 if(outs->filename)
270 fprintf(stream, "%s", outs->filename);
271 break;
272 case VAR_PRIMARY_IP:
273 if(CURLE_OK ==
274 curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP,
275 &stringp))
276 fprintf(stream, "%s", stringp);
277 break;
278 case VAR_PRIMARY_PORT:
279 if(CURLE_OK ==
280 curl_easy_getinfo(curl, CURLINFO_PRIMARY_PORT,
281 &longinfo))
282 fprintf(stream, "%ld", longinfo);
283 break;
284 case VAR_LOCAL_IP:
285 if(CURLE_OK ==
286 curl_easy_getinfo(curl, CURLINFO_LOCAL_IP,
287 &stringp))
288 fprintf(stream, "%s", stringp);
289 break;
290 case VAR_LOCAL_PORT:
291 if(CURLE_OK ==
292 curl_easy_getinfo(curl, CURLINFO_LOCAL_PORT,
293 &longinfo))
294 fprintf(stream, "%ld", longinfo);
295 break;
296 case VAR_HTTP_VERSION:
297 if(CURLE_OK ==
298 curl_easy_getinfo(curl, CURLINFO_HTTP_VERSION,
299 &longinfo)) {
300 const char *version = "0";
301 switch(longinfo) {
302 case CURL_HTTP_VERSION_1_0:
303 version = "1.0";
304 break;
305 case CURL_HTTP_VERSION_1_1:
306 version = "1.1";
307 break;
308 case CURL_HTTP_VERSION_2_0:
309 version = "2";
310 break;
311 case CURL_HTTP_VERSION_3:
312 version = "3";
313 break;
314 }
315
316 fprintf(stream, version);
317 }
318 break;
319 case VAR_SCHEME:
320 if(CURLE_OK ==
321 curl_easy_getinfo(curl, CURLINFO_SCHEME,
322 &stringp))
323 fprintf(stream, "%s", stringp);
324 break;
325 case VAR_STDOUT:
326 stream = stdout;
327 break;
328 case VAR_STDERR:
329 stream = stderr;
330 break;
331 case VAR_JSON:
332 ourWriteOutJSON(variables, curl, outs, stream);
333 default:
334 break;
335 }
336 break;
337 }
338 }
339 if(!match) {
340 fprintf(stderr, "curl: unknown --write-out variable: '%s'\n", ptr);
341 }
342 ptr = end + 1; /* pass the end */
343 *end = keepit;
344 }
345 else {
346 /* illegal syntax, then just output the characters that are used */
347 fputc('%', stream);
348 fputc(ptr[1], stream);
349 ptr += 2;
350 }
351 }
352 }
353 else if('\\' == *ptr && ptr[1]) {
354 switch(ptr[1]) {
355 case 'r':
356 fputc('\r', stream);
357 break;
358 case 'n':
359 fputc('\n', stream);
360 break;
361 case 't':
362 fputc('\t', stream);
363 break;
364 default:
365 /* unknown, just output this */
366 fputc(*ptr, stream);
367 fputc(ptr[1], stream);
368 break;
369 }
370 ptr += 2;
371 }
372 else {
373 fputc(*ptr, stream);
374 ptr++;
375 }
376 }
377
378 }
379