1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2018, 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
31 typedef 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
68 struct variable {
69 const char *name;
70 replaceid id;
71 };
72
73
74 static 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
ourWriteOut(CURL * curl,struct OutStruct * outs,const char * writeinfo)111 void 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 }
318
319 fprintf(stream, version);
320 }
321 break;
322 case VAR_SCHEME:
323 if(CURLE_OK ==
324 curl_easy_getinfo(curl, CURLINFO_SCHEME,
325 &stringp))
326 fprintf(stream, "%s", stringp);
327 break;
328 case VAR_STDOUT:
329 stream = stdout;
330 break;
331 case VAR_STDERR:
332 stream = stderr;
333 break;
334 default:
335 break;
336 }
337 break;
338 }
339 }
340 if(!match) {
341 fprintf(stderr, "curl: unknown --write-out variable: '%s'\n", ptr);
342 }
343 ptr = end + 1; /* pass the end */
344 *end = keepit;
345 }
346 else {
347 /* illegal syntax, then just output the characters that are used */
348 fputc('%', stream);
349 fputc(ptr[1], stream);
350 ptr += 2;
351 }
352 }
353 }
354 else if('\\' == *ptr && ptr[1]) {
355 switch(ptr[1]) {
356 case 'r':
357 fputc('\r', stream);
358 break;
359 case 'n':
360 fputc('\n', stream);
361 break;
362 case 't':
363 fputc('\t', stream);
364 break;
365 default:
366 /* unknown, just output this */
367 fputc(*ptr, stream);
368 fputc(ptr[1], stream);
369 break;
370 }
371 ptr += 2;
372 }
373 else {
374 fputc(*ptr, stream);
375 ptr++;
376 }
377 }
378
379 }
380