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