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