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