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 | |
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 | , |
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 | |
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 | 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 | |