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 #include "curlx.h"
27 #include "tool_cfgable.h"
28 #include "tool_writeout.h"
29 #include "tool_writeout_json.h"
30 #include "dynbuf.h"
31
32 #include "memdebug.h" /* keep this as LAST include */
33
34 static int writeTime(FILE *stream, const struct writeoutvar *wovar,
35 struct per_transfer *per, CURLcode per_result,
36 bool use_json);
37
38 static int writeString(FILE *stream, const struct writeoutvar *wovar,
39 struct per_transfer *per, CURLcode per_result,
40 bool use_json);
41
42 static int writeLong(FILE *stream, const struct writeoutvar *wovar,
43 struct per_transfer *per, CURLcode per_result,
44 bool use_json);
45
46 static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
47 struct per_transfer *per, CURLcode per_result,
48 bool use_json);
49
50 struct httpmap {
51 const char *str;
52 int num;
53 };
54
55 static const struct httpmap http_version[] = {
56 { "0", CURL_HTTP_VERSION_NONE},
57 { "1", CURL_HTTP_VERSION_1_0},
58 { "1.1", CURL_HTTP_VERSION_1_1},
59 { "2", CURL_HTTP_VERSION_2},
60 { "3", CURL_HTTP_VERSION_3},
61 { NULL, 0} /* end of list */
62 };
63
64 /* The designated write function should be the same as the CURLINFO return type
65 with exceptions special cased in the respective function. For example,
66 http_version uses CURLINFO_HTTP_VERSION which returns the version as a long,
67 however it is output as a string and therefore is handled in writeString.
68
69 Yes: "http_version": "1.1"
70 No: "http_version": 1.1
71
72 Variable names MUST be in alphabetical order.
73 */
74 static const struct writeoutvar variables[] = {
75 {"certs", VAR_CERT, CURLINFO_NONE, writeString},
76 {"conn_id", VAR_CONN_ID, CURLINFO_CONN_ID, writeOffset},
77 {"content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString},
78 {"errormsg", VAR_ERRORMSG, CURLINFO_NONE, writeString},
79 {"exitcode", VAR_EXITCODE, CURLINFO_NONE, writeLong},
80 {"filename_effective", VAR_EFFECTIVE_FILENAME, CURLINFO_NONE, writeString},
81 {"ftp_entry_path", VAR_FTP_ENTRY_PATH, CURLINFO_FTP_ENTRY_PATH, writeString},
82 {"header_json", VAR_HEADER_JSON, CURLINFO_NONE, NULL},
83 {"http_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong},
84 {"http_connect", VAR_HTTP_CODE_PROXY, CURLINFO_HTTP_CONNECTCODE, writeLong},
85 {"http_version", VAR_HTTP_VERSION, CURLINFO_HTTP_VERSION, writeString},
86 {"json", VAR_JSON, CURLINFO_NONE, NULL},
87 {"local_ip", VAR_LOCAL_IP, CURLINFO_LOCAL_IP, writeString},
88 {"local_port", VAR_LOCAL_PORT, CURLINFO_LOCAL_PORT, writeLong},
89 {"method", VAR_EFFECTIVE_METHOD, CURLINFO_EFFECTIVE_METHOD, writeString},
90 {"num_certs", VAR_NUM_CERTS, CURLINFO_NONE, writeLong},
91 {"num_connects", VAR_NUM_CONNECTS, CURLINFO_NUM_CONNECTS, writeLong},
92 {"num_headers", VAR_NUM_HEADERS, CURLINFO_NONE, writeLong},
93 {"num_redirects", VAR_REDIRECT_COUNT, CURLINFO_REDIRECT_COUNT, writeLong},
94 {"num_retries", VAR_NUM_RETRY, CURLINFO_NONE, writeLong},
95 {"onerror", VAR_ONERROR, CURLINFO_NONE, NULL},
96 {"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT,
97 CURLINFO_PROXY_SSL_VERIFYRESULT, writeLong},
98 {"proxy_used", VAR_PROXY_USED, CURLINFO_USED_PROXY, writeLong},
99 {"redirect_url", VAR_REDIRECT_URL, CURLINFO_REDIRECT_URL, writeString},
100 {"referer", VAR_REFERER, CURLINFO_REFERER, writeString},
101 {"remote_ip", VAR_PRIMARY_IP, CURLINFO_PRIMARY_IP, writeString},
102 {"remote_port", VAR_PRIMARY_PORT, CURLINFO_PRIMARY_PORT, writeLong},
103 {"response_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong},
104 {"scheme", VAR_SCHEME, CURLINFO_SCHEME, writeString},
105 {"size_download", VAR_SIZE_DOWNLOAD, CURLINFO_SIZE_DOWNLOAD_T, writeOffset},
106 {"size_header", VAR_HEADER_SIZE, CURLINFO_HEADER_SIZE, writeLong},
107 {"size_request", VAR_REQUEST_SIZE, CURLINFO_REQUEST_SIZE, writeLong},
108 {"size_upload", VAR_SIZE_UPLOAD, CURLINFO_SIZE_UPLOAD_T, writeOffset},
109 {"speed_download", VAR_SPEED_DOWNLOAD, CURLINFO_SPEED_DOWNLOAD_T,
110 writeOffset},
111 {"speed_upload", VAR_SPEED_UPLOAD, CURLINFO_SPEED_UPLOAD_T, writeOffset},
112 {"ssl_verify_result", VAR_SSL_VERIFY_RESULT, CURLINFO_SSL_VERIFYRESULT,
113 writeLong},
114 {"stderr", VAR_STDERR, CURLINFO_NONE, NULL},
115 {"stdout", VAR_STDOUT, CURLINFO_NONE, NULL},
116 {"time_appconnect", VAR_APPCONNECT_TIME, CURLINFO_APPCONNECT_TIME_T,
117 writeTime},
118 {"time_connect", VAR_CONNECT_TIME, CURLINFO_CONNECT_TIME_T, writeTime},
119 {"time_namelookup", VAR_NAMELOOKUP_TIME, CURLINFO_NAMELOOKUP_TIME_T,
120 writeTime},
121 {"time_posttransfer", VAR_POSTTRANSFER_TIME, CURLINFO_POSTTRANSFER_TIME_T,
122 writeTime},
123 {"time_pretransfer", VAR_PRETRANSFER_TIME, CURLINFO_PRETRANSFER_TIME_T,
124 writeTime},
125 {"time_queue", VAR_QUEUE_TIME, CURLINFO_QUEUE_TIME_T, writeTime},
126 {"time_redirect", VAR_REDIRECT_TIME, CURLINFO_REDIRECT_TIME_T, writeTime},
127 {"time_starttransfer", VAR_STARTTRANSFER_TIME, CURLINFO_STARTTRANSFER_TIME_T,
128 writeTime},
129 {"time_total", VAR_TOTAL_TIME, CURLINFO_TOTAL_TIME_T, writeTime},
130 {"url", VAR_INPUT_URL, CURLINFO_NONE, writeString},
131 {"url.fragment", VAR_INPUT_URLFRAGMENT, CURLINFO_NONE, writeString},
132 {"url.host", VAR_INPUT_URLHOST, CURLINFO_NONE, writeString},
133 {"url.options", VAR_INPUT_URLOPTIONS, CURLINFO_NONE, writeString},
134 {"url.password", VAR_INPUT_URLPASSWORD, CURLINFO_NONE, writeString},
135 {"url.path", VAR_INPUT_URLPATH, CURLINFO_NONE, writeString},
136 {"url.port", VAR_INPUT_URLPORT, CURLINFO_NONE, writeString},
137 {"url.query", VAR_INPUT_URLQUERY, CURLINFO_NONE, writeString},
138 {"url.scheme", VAR_INPUT_URLSCHEME, CURLINFO_NONE, writeString},
139 {"url.user", VAR_INPUT_URLUSER, CURLINFO_NONE, writeString},
140 {"url.zoneid", VAR_INPUT_URLZONEID, CURLINFO_NONE, writeString},
141 {"url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString},
142 {"urle.fragment", VAR_INPUT_URLEFRAGMENT, CURLINFO_NONE, writeString},
143 {"urle.host", VAR_INPUT_URLEHOST, CURLINFO_NONE, writeString},
144 {"urle.options", VAR_INPUT_URLEOPTIONS, CURLINFO_NONE, writeString},
145 {"urle.password", VAR_INPUT_URLEPASSWORD, CURLINFO_NONE, writeString},
146 {"urle.path", VAR_INPUT_URLEPATH, CURLINFO_NONE, writeString},
147 {"urle.port", VAR_INPUT_URLEPORT, CURLINFO_NONE, writeString},
148 {"urle.query", VAR_INPUT_URLEQUERY, CURLINFO_NONE, writeString},
149 {"urle.scheme", VAR_INPUT_URLESCHEME, CURLINFO_NONE, writeString},
150 {"urle.user", VAR_INPUT_URLEUSER, CURLINFO_NONE, writeString},
151 {"urle.zoneid", VAR_INPUT_URLEZONEID, CURLINFO_NONE, writeString},
152 {"urlnum", VAR_URLNUM, CURLINFO_NONE, writeLong},
153 {"xfer_id", VAR_EASY_ID, CURLINFO_XFER_ID, writeOffset}
154 };
155
writeTime(FILE * stream,const struct writeoutvar * wovar,struct per_transfer * per,CURLcode per_result,bool use_json)156 static int writeTime(FILE *stream, const struct writeoutvar *wovar,
157 struct per_transfer *per, CURLcode per_result,
158 bool use_json)
159 {
160 bool valid = false;
161 curl_off_t us = 0;
162
163 (void)per;
164 (void)per_result;
165 DEBUGASSERT(wovar->writefunc == writeTime);
166
167 if(wovar->ci) {
168 if(!curl_easy_getinfo(per->curl, wovar->ci, &us))
169 valid = true;
170 }
171 else {
172 DEBUGASSERT(0);
173 }
174
175 if(valid) {
176 curl_off_t secs = us / 1000000;
177 us %= 1000000;
178
179 if(use_json)
180 fprintf(stream, "\"%s\":", wovar->name);
181
182 fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU
183 ".%06" CURL_FORMAT_CURL_OFF_TU, secs, us);
184 }
185 else {
186 if(use_json)
187 fprintf(stream, "\"%s\":null", wovar->name);
188 }
189
190 return 1; /* return 1 if anything was written */
191 }
192
urlpart(struct per_transfer * per,writeoutid vid,const char ** contentp)193 static int urlpart(struct per_transfer *per, writeoutid vid,
194 const char **contentp)
195 {
196 CURLU *uh = curl_url();
197 int rc = 0;
198 if(uh) {
199 CURLUPart cpart = CURLUPART_HOST;
200 char *part = NULL;
201 const char *url = NULL;
202
203 if(vid >= VAR_INPUT_URLESCHEME) {
204 if(curl_easy_getinfo(per->curl, CURLINFO_EFFECTIVE_URL, &url))
205 rc = 5;
206 }
207 else
208 url = per->url;
209
210 if(!rc) {
211 switch(vid) {
212 case VAR_INPUT_URLSCHEME:
213 case VAR_INPUT_URLESCHEME:
214 cpart = CURLUPART_SCHEME;
215 break;
216 case VAR_INPUT_URLUSER:
217 case VAR_INPUT_URLEUSER:
218 cpart = CURLUPART_USER;
219 break;
220 case VAR_INPUT_URLPASSWORD:
221 case VAR_INPUT_URLEPASSWORD:
222 cpart = CURLUPART_PASSWORD;
223 break;
224 case VAR_INPUT_URLOPTIONS:
225 case VAR_INPUT_URLEOPTIONS:
226 cpart = CURLUPART_OPTIONS;
227 break;
228 case VAR_INPUT_URLHOST:
229 case VAR_INPUT_URLEHOST:
230 cpart = CURLUPART_HOST;
231 break;
232 case VAR_INPUT_URLPORT:
233 case VAR_INPUT_URLEPORT:
234 cpart = CURLUPART_PORT;
235 break;
236 case VAR_INPUT_URLPATH:
237 case VAR_INPUT_URLEPATH:
238 cpart = CURLUPART_PATH;
239 break;
240 case VAR_INPUT_URLQUERY:
241 case VAR_INPUT_URLEQUERY:
242 cpart = CURLUPART_QUERY;
243 break;
244 case VAR_INPUT_URLFRAGMENT:
245 case VAR_INPUT_URLEFRAGMENT:
246 cpart = CURLUPART_FRAGMENT;
247 break;
248 case VAR_INPUT_URLZONEID:
249 case VAR_INPUT_URLEZONEID:
250 cpart = CURLUPART_ZONEID;
251 break;
252 default:
253 /* not implemented */
254 rc = 4;
255 break;
256 }
257 }
258 if(!rc && curl_url_set(uh, CURLUPART_URL, url,
259 CURLU_GUESS_SCHEME|CURLU_NON_SUPPORT_SCHEME))
260 rc = 2;
261
262 if(!rc && curl_url_get(uh, cpart, &part, CURLU_DEFAULT_PORT))
263 rc = 3;
264
265 if(!rc && part)
266 *contentp = part;
267 curl_url_cleanup(uh);
268 }
269 else
270 return 1;
271 return rc;
272 }
273
certinfo(struct per_transfer * per)274 static void certinfo(struct per_transfer *per)
275 {
276 if(!per->certinfo) {
277 struct curl_certinfo *certinfo;
278 CURLcode res = curl_easy_getinfo(per->curl, CURLINFO_CERTINFO, &certinfo);
279 per->certinfo = (!res && certinfo) ? certinfo : NULL;
280 }
281 }
282
writeString(FILE * stream,const struct writeoutvar * wovar,struct per_transfer * per,CURLcode per_result,bool use_json)283 static int writeString(FILE *stream, const struct writeoutvar *wovar,
284 struct per_transfer *per, CURLcode per_result,
285 bool use_json)
286 {
287 bool valid = false;
288 const char *strinfo = NULL;
289 const char *freestr = NULL;
290 struct dynbuf buf;
291 curlx_dyn_init(&buf, 256*1024);
292
293 DEBUGASSERT(wovar->writefunc == writeString);
294
295 if(wovar->ci) {
296 if(wovar->ci == CURLINFO_HTTP_VERSION) {
297 long version = 0;
298 if(!curl_easy_getinfo(per->curl, CURLINFO_HTTP_VERSION, &version)) {
299 const struct httpmap *m = &http_version[0];
300 while(m->str) {
301 if(m->num == version) {
302 strinfo = m->str;
303 valid = true;
304 break;
305 }
306 m++;
307 }
308 }
309 }
310 else {
311 if(!curl_easy_getinfo(per->curl, wovar->ci, &strinfo) && strinfo)
312 valid = true;
313 }
314 }
315 else {
316 switch(wovar->id) {
317 case VAR_CERT:
318 certinfo(per);
319 if(per->certinfo) {
320 int i;
321 bool error = FALSE;
322 for(i = 0; (i < per->certinfo->num_of_certs) && !error; i++) {
323 struct curl_slist *slist;
324
325 for(slist = per->certinfo->certinfo[i]; slist; slist = slist->next) {
326 size_t len;
327 if(curl_strnequal(slist->data, "cert:", 5)) {
328 if(curlx_dyn_add(&buf, &slist->data[5])) {
329 error = TRUE;
330 break;
331 }
332 }
333 else {
334 if(curlx_dyn_add(&buf, slist->data)) {
335 error = TRUE;
336 break;
337 }
338 }
339 len = curlx_dyn_len(&buf);
340 if(len) {
341 char *ptr = curlx_dyn_ptr(&buf);
342 if(ptr[len -1] != '\n') {
343 /* add a newline to make things look better */
344 if(curlx_dyn_addn(&buf, "\n", 1)) {
345 error = TRUE;
346 break;
347 }
348 }
349 }
350 }
351 }
352 if(!error) {
353 strinfo = curlx_dyn_ptr(&buf);
354 if(!strinfo)
355 /* maybe not a TLS protocol */
356 strinfo = "";
357 valid = true;
358 }
359 }
360 else
361 strinfo = ""; /* no cert info */
362 break;
363 case VAR_ERRORMSG:
364 if(per_result) {
365 strinfo = (per->errorbuffer && per->errorbuffer[0]) ?
366 per->errorbuffer : curl_easy_strerror(per_result);
367 valid = true;
368 }
369 break;
370 case VAR_EFFECTIVE_FILENAME:
371 if(per->outs.filename) {
372 strinfo = per->outs.filename;
373 valid = true;
374 }
375 break;
376 case VAR_INPUT_URL:
377 if(per->url) {
378 strinfo = per->url;
379 valid = true;
380 }
381 break;
382 case VAR_INPUT_URLSCHEME:
383 case VAR_INPUT_URLUSER:
384 case VAR_INPUT_URLPASSWORD:
385 case VAR_INPUT_URLOPTIONS:
386 case VAR_INPUT_URLHOST:
387 case VAR_INPUT_URLPORT:
388 case VAR_INPUT_URLPATH:
389 case VAR_INPUT_URLQUERY:
390 case VAR_INPUT_URLFRAGMENT:
391 case VAR_INPUT_URLZONEID:
392 case VAR_INPUT_URLESCHEME:
393 case VAR_INPUT_URLEUSER:
394 case VAR_INPUT_URLEPASSWORD:
395 case VAR_INPUT_URLEOPTIONS:
396 case VAR_INPUT_URLEHOST:
397 case VAR_INPUT_URLEPORT:
398 case VAR_INPUT_URLEPATH:
399 case VAR_INPUT_URLEQUERY:
400 case VAR_INPUT_URLEFRAGMENT:
401 case VAR_INPUT_URLEZONEID:
402 if(per->url) {
403 if(!urlpart(per, wovar->id, &strinfo)) {
404 freestr = strinfo;
405 valid = true;
406 }
407 }
408 break;
409 default:
410 DEBUGASSERT(0);
411 break;
412 }
413 }
414
415 if(valid) {
416 DEBUGASSERT(strinfo);
417 if(use_json) {
418 fprintf(stream, "\"%s\":", wovar->name);
419 jsonWriteString(stream, strinfo, FALSE);
420 }
421 else
422 fputs(strinfo, stream);
423 }
424 else {
425 if(use_json)
426 fprintf(stream, "\"%s\":null", wovar->name);
427 }
428 curl_free((char *)freestr);
429
430 curlx_dyn_free(&buf);
431 return 1; /* return 1 if anything was written */
432 }
433
writeLong(FILE * stream,const struct writeoutvar * wovar,struct per_transfer * per,CURLcode per_result,bool use_json)434 static int writeLong(FILE *stream, const struct writeoutvar *wovar,
435 struct per_transfer *per, CURLcode per_result,
436 bool use_json)
437 {
438 bool valid = false;
439 long longinfo = 0;
440
441 DEBUGASSERT(wovar->writefunc == writeLong);
442
443 if(wovar->ci) {
444 if(!curl_easy_getinfo(per->curl, wovar->ci, &longinfo))
445 valid = true;
446 }
447 else {
448 switch(wovar->id) {
449 case VAR_NUM_RETRY:
450 longinfo = per->num_retries;
451 valid = true;
452 break;
453 case VAR_NUM_CERTS:
454 certinfo(per);
455 longinfo = per->certinfo ? per->certinfo->num_of_certs : 0;
456 valid = true;
457 break;
458 case VAR_NUM_HEADERS:
459 longinfo = per->num_headers;
460 valid = true;
461 break;
462 case VAR_EXITCODE:
463 longinfo = (long)per_result;
464 valid = true;
465 break;
466 case VAR_URLNUM:
467 if(per->urlnum <= INT_MAX) {
468 longinfo = (long)per->urlnum;
469 valid = true;
470 }
471 break;
472 default:
473 DEBUGASSERT(0);
474 break;
475 }
476 }
477
478 if(valid) {
479 if(use_json)
480 fprintf(stream, "\"%s\":%ld", wovar->name, longinfo);
481 else {
482 if(wovar->id == VAR_HTTP_CODE || wovar->id == VAR_HTTP_CODE_PROXY)
483 fprintf(stream, "%03ld", longinfo);
484 else
485 fprintf(stream, "%ld", longinfo);
486 }
487 }
488 else {
489 if(use_json)
490 fprintf(stream, "\"%s\":null", wovar->name);
491 }
492
493 return 1; /* return 1 if anything was written */
494 }
495
writeOffset(FILE * stream,const struct writeoutvar * wovar,struct per_transfer * per,CURLcode per_result,bool use_json)496 static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
497 struct per_transfer *per, CURLcode per_result,
498 bool use_json)
499 {
500 bool valid = false;
501 curl_off_t offinfo = 0;
502
503 (void)per;
504 (void)per_result;
505 DEBUGASSERT(wovar->writefunc == writeOffset);
506
507 if(wovar->ci) {
508 if(!curl_easy_getinfo(per->curl, wovar->ci, &offinfo))
509 valid = true;
510 }
511 else {
512 DEBUGASSERT(0);
513 }
514
515 if(valid) {
516 if(use_json)
517 fprintf(stream, "\"%s\":", wovar->name);
518
519 fprintf(stream, "%" CURL_FORMAT_CURL_OFF_T, offinfo);
520 }
521 else {
522 if(use_json)
523 fprintf(stream, "\"%s\":null", wovar->name);
524 }
525
526 return 1; /* return 1 if anything was written */
527 }
528
529 static int
matchvar(const void * m1,const void * m2)530 matchvar(const void *m1, const void *m2)
531 {
532 const struct writeoutvar *v1 = m1;
533 const struct writeoutvar *v2 = m2;
534
535 return strcmp(v1->name, v2->name);
536 }
537
538 #define MAX_WRITEOUT_NAME_LENGTH 24
539
ourWriteOut(struct OperationConfig * config,struct per_transfer * per,CURLcode per_result)540 void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
541 CURLcode per_result)
542 {
543 FILE *stream = stdout;
544 const char *writeinfo = config->writeout;
545 const char *ptr = writeinfo;
546 bool done = FALSE;
547 bool fclose_stream = FALSE;
548 struct dynbuf name;
549
550 if(!writeinfo)
551 return;
552
553 curlx_dyn_init(&name, MAX_WRITEOUT_NAME_LENGTH);
554 while(ptr && *ptr && !done) {
555 if('%' == *ptr && ptr[1]) {
556 if('%' == ptr[1]) {
557 /* an escaped %-letter */
558 fputc('%', stream);
559 ptr += 2;
560 }
561 else {
562 /* this is meant as a variable to output */
563 char *end;
564 size_t vlen;
565 if('{' == ptr[1]) {
566 struct writeoutvar *wv = NULL;
567 struct writeoutvar find = { 0 };
568 end = strchr(ptr, '}');
569 ptr += 2; /* pass the % and the { */
570 if(!end) {
571 fputs("%{", stream);
572 continue;
573 }
574 vlen = end - ptr;
575
576 curlx_dyn_reset(&name);
577 if(!curlx_dyn_addn(&name, ptr, vlen)) {
578 find.name = curlx_dyn_ptr(&name);
579 wv = bsearch(&find,
580 variables, sizeof(variables)/sizeof(variables[0]),
581 sizeof(variables[0]), matchvar);
582 }
583 if(wv) {
584 switch(wv->id) {
585 case VAR_ONERROR:
586 if(per_result == CURLE_OK)
587 /* this is not error so skip the rest */
588 done = TRUE;
589 break;
590 case VAR_STDOUT:
591 if(fclose_stream)
592 fclose(stream);
593 fclose_stream = FALSE;
594 stream = stdout;
595 break;
596 case VAR_STDERR:
597 if(fclose_stream)
598 fclose(stream);
599 fclose_stream = FALSE;
600 stream = tool_stderr;
601 break;
602 case VAR_JSON:
603 ourWriteOutJSON(stream, variables,
604 sizeof(variables)/sizeof(variables[0]),
605 per, per_result);
606 break;
607 case VAR_HEADER_JSON:
608 headerJSON(stream, per);
609 break;
610 default:
611 (void)wv->writefunc(stream, wv, per, per_result, false);
612 break;
613 }
614 }
615 else {
616 fprintf(tool_stderr,
617 "curl: unknown --write-out variable: '%.*s'\n",
618 (int)vlen, ptr);
619 }
620 ptr = end + 1; /* pass the end */
621 }
622 else if(!strncmp("header{", &ptr[1], 7)) {
623 ptr += 8;
624 end = strchr(ptr, '}');
625 if(end) {
626 char hname[256]; /* holds the longest header field name */
627 struct curl_header *header;
628 vlen = end - ptr;
629 if(vlen < sizeof(hname)) {
630 memcpy(hname, ptr, vlen);
631 hname[vlen] = 0;
632 if(CURLHE_OK == curl_easy_header(per->curl, hname, 0,
633 CURLH_HEADER, -1, &header))
634 fputs(header->value, stream);
635 }
636 ptr = end + 1;
637 }
638 else
639 fputs("%header{", stream);
640 }
641 else if(!strncmp("output{", &ptr[1], 7)) {
642 bool append = FALSE;
643 ptr += 8;
644 if((ptr[0] == '>') && (ptr[1] == '>')) {
645 append = TRUE;
646 ptr += 2;
647 }
648 end = strchr(ptr, '}');
649 if(end) {
650 char fname[512]; /* holds the longest filename */
651 size_t flen = end - ptr;
652 if(flen < sizeof(fname)) {
653 FILE *stream2;
654 memcpy(fname, ptr, flen);
655 fname[flen] = 0;
656 stream2 = fopen(fname, append ? FOPEN_APPENDTEXT :
657 FOPEN_WRITETEXT);
658 if(stream2) {
659 /* only change if the open worked */
660 if(fclose_stream)
661 fclose(stream);
662 stream = stream2;
663 fclose_stream = TRUE;
664 }
665 }
666 ptr = end + 1;
667 }
668 else
669 fputs("%output{", stream);
670 }
671 else {
672 /* illegal syntax, then just output the characters that are used */
673 fputc('%', stream);
674 fputc(ptr[1], stream);
675 ptr += 2;
676 }
677 }
678 }
679 else if('\\' == *ptr && ptr[1]) {
680 switch(ptr[1]) {
681 case 'r':
682 fputc('\r', stream);
683 break;
684 case 'n':
685 fputc('\n', stream);
686 break;
687 case 't':
688 fputc('\t', stream);
689 break;
690 default:
691 /* unknown, just output this */
692 fputc(*ptr, stream);
693 fputc(ptr[1], stream);
694 break;
695 }
696 ptr += 2;
697 }
698 else {
699 fputc(*ptr, stream);
700 ptr++;
701 }
702 }
703 if(fclose_stream)
704 fclose(stream);
705 curlx_dyn_free(&name);
706 }
707