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