1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2011, 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 http://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
23#include "setup.h"
24
25#include <stdio.h>
26#include <string.h>
27
28#ifdef HAVE_SYS_TYPES_H
29#include <sys/types.h>
30#endif
31#ifdef HAVE_SYS_SELECT_H
32#include <sys/select.h>
33#endif
34
35#include <curl/curl.h>
36
37#define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
38#include <curl/mprintf.h>
39
40#include "writeout.h"
41
42typedef enum {
43  VAR_NONE,       /* must be the first */
44  VAR_TOTAL_TIME,
45  VAR_NAMELOOKUP_TIME,
46  VAR_CONNECT_TIME,
47  VAR_APPCONNECT_TIME,
48  VAR_PRETRANSFER_TIME,
49  VAR_STARTTRANSFER_TIME,
50  VAR_SIZE_DOWNLOAD,
51  VAR_SIZE_UPLOAD,
52  VAR_SPEED_DOWNLOAD,
53  VAR_SPEED_UPLOAD,
54  VAR_HTTP_CODE,
55  VAR_HTTP_CODE_PROXY,
56  VAR_HEADER_SIZE,
57  VAR_REQUEST_SIZE,
58  VAR_EFFECTIVE_URL,
59  VAR_CONTENT_TYPE,
60  VAR_NUM_CONNECTS,
61  VAR_REDIRECT_TIME,
62  VAR_REDIRECT_COUNT,
63  VAR_FTP_ENTRY_PATH,
64  VAR_REDIRECT_URL,
65  VAR_SSL_VERIFY_RESULT,
66  VAR_NUM_OF_VARS /* must be the last */
67} replaceid;
68
69struct variable {
70  const char *name;
71  replaceid id;
72};
73
74
75static const struct variable replacements[]={
76  {"url_effective", VAR_EFFECTIVE_URL},
77  {"http_code", VAR_HTTP_CODE},
78  {"response_code", VAR_HTTP_CODE},
79  {"http_connect", VAR_HTTP_CODE_PROXY},
80  {"time_total", VAR_TOTAL_TIME},
81  {"time_namelookup", VAR_NAMELOOKUP_TIME},
82  {"time_connect", VAR_CONNECT_TIME},
83  {"time_appconnect", VAR_APPCONNECT_TIME},
84  {"time_pretransfer", VAR_PRETRANSFER_TIME},
85  {"time_starttransfer", VAR_STARTTRANSFER_TIME},
86  {"size_header", VAR_HEADER_SIZE},
87  {"size_request", VAR_REQUEST_SIZE},
88  {"size_download", VAR_SIZE_DOWNLOAD},
89  {"size_upload", VAR_SIZE_UPLOAD},
90  {"speed_download", VAR_SPEED_DOWNLOAD},
91  {"speed_upload", VAR_SPEED_UPLOAD},
92  {"content_type", VAR_CONTENT_TYPE},
93  {"num_connects", VAR_NUM_CONNECTS},
94  {"time_redirect", VAR_REDIRECT_TIME},
95  {"num_redirects", VAR_REDIRECT_COUNT},
96  {"ftp_entry_path", VAR_FTP_ENTRY_PATH},
97  {"redirect_url", VAR_REDIRECT_URL},
98  {"ssl_verify_result", VAR_SSL_VERIFY_RESULT},
99  {NULL, VAR_NONE}
100};
101
102void ourWriteOut(CURL *curl, const char *writeinfo)
103{
104  FILE *stream = stdout;
105  const char *ptr=writeinfo;
106  char *stringp;
107  long longinfo;
108  double doubleinfo;
109
110  while(ptr && *ptr) {
111    if('%' == *ptr) {
112      if('%' == ptr[1]) {
113        /* an escaped %-letter */
114        fputc('%', stream);
115        ptr+=2;
116      }
117      else {
118        /* this is meant as a variable to output */
119        char *end;
120        char keepit;
121        int i;
122        if(('{' == ptr[1]) && ((end = strchr(ptr, '}')) != NULL)) {
123          bool match = FALSE;
124          ptr+=2; /* pass the % and the { */
125          keepit=*end;
126          *end=0; /* zero terminate */
127          for(i=0; replacements[i].name; i++) {
128            if(curl_strequal(ptr, replacements[i].name)) {
129              match = TRUE;
130              switch(replacements[i].id) {
131              case VAR_EFFECTIVE_URL:
132                if((CURLE_OK ==
133                    curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &stringp))
134                   && stringp)
135                  fputs(stringp, stream);
136                break;
137              case VAR_HTTP_CODE:
138                if(CURLE_OK ==
139                   curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &longinfo))
140                  fprintf(stream, "%03ld", longinfo);
141                break;
142              case VAR_HTTP_CODE_PROXY:
143                if(CURLE_OK ==
144                   curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE,
145                                     &longinfo))
146                  fprintf(stream, "%03ld", longinfo);
147                break;
148              case VAR_HEADER_SIZE:
149                if(CURLE_OK ==
150                   curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &longinfo))
151                  fprintf(stream, "%ld", longinfo);
152                break;
153              case VAR_REQUEST_SIZE:
154                if(CURLE_OK ==
155                   curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &longinfo))
156                  fprintf(stream, "%ld", longinfo);
157                break;
158              case VAR_NUM_CONNECTS:
159                if(CURLE_OK ==
160                   curl_easy_getinfo(curl, CURLINFO_NUM_CONNECTS, &longinfo))
161                  fprintf(stream, "%ld", longinfo);
162                break;
163              case VAR_REDIRECT_COUNT:
164                if(CURLE_OK ==
165                   curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &longinfo))
166                  fprintf(stream, "%ld", longinfo);
167                break;
168              case VAR_REDIRECT_TIME:
169                if(CURLE_OK ==
170                   curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME,
171                                     &doubleinfo))
172                  fprintf(stream, "%.3f", doubleinfo);
173                break;
174              case VAR_TOTAL_TIME:
175                if(CURLE_OK ==
176                   curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &doubleinfo))
177                  fprintf(stream, "%.3f", doubleinfo);
178                break;
179              case VAR_NAMELOOKUP_TIME:
180                if(CURLE_OK ==
181                   curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME,
182                                     &doubleinfo))
183                  fprintf(stream, "%.3f", doubleinfo);
184                break;
185              case VAR_CONNECT_TIME:
186                if(CURLE_OK ==
187                   curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &doubleinfo))
188                  fprintf(stream, "%.3f", doubleinfo);
189                break;
190              case VAR_APPCONNECT_TIME:
191                if(CURLE_OK ==
192                   curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME,
193                                     &doubleinfo))
194                  fprintf(stream, "%.3f", doubleinfo);
195                break;
196              case VAR_PRETRANSFER_TIME:
197                if(CURLE_OK ==
198                   curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME,
199                                     &doubleinfo))
200                  fprintf(stream, "%.3f", doubleinfo);
201                break;
202              case VAR_STARTTRANSFER_TIME:
203                if(CURLE_OK ==
204                   curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME,
205                                     &doubleinfo))
206                  fprintf(stream, "%.3f", doubleinfo);
207                break;
208              case VAR_SIZE_UPLOAD:
209                if(CURLE_OK ==
210                   curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, &doubleinfo))
211                  fprintf(stream, "%.0f", doubleinfo);
212                break;
213              case VAR_SIZE_DOWNLOAD:
214                if(CURLE_OK ==
215                   curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD,
216                                     &doubleinfo))
217                  fprintf(stream, "%.0f", doubleinfo);
218                break;
219              case VAR_SPEED_DOWNLOAD:
220                if(CURLE_OK ==
221                   curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD,
222                                     &doubleinfo))
223                  fprintf(stream, "%.3f", doubleinfo);
224                break;
225              case VAR_SPEED_UPLOAD:
226                if(CURLE_OK ==
227                   curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &doubleinfo))
228                  fprintf(stream, "%.3f", doubleinfo);
229                break;
230              case VAR_CONTENT_TYPE:
231                if((CURLE_OK ==
232                    curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &stringp))
233                   && stringp)
234                  fputs(stringp, stream);
235                break;
236              case VAR_FTP_ENTRY_PATH:
237                if((CURLE_OK ==
238                    curl_easy_getinfo(curl, CURLINFO_FTP_ENTRY_PATH, &stringp))
239                   && stringp)
240                  fputs(stringp, stream);
241                break;
242              case VAR_REDIRECT_URL:
243                if((CURLE_OK ==
244                    curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &stringp))
245                   && stringp)
246                  fputs(stringp, stream);
247                break;
248              case VAR_SSL_VERIFY_RESULT:
249                if(CURLE_OK ==
250                   curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT,
251                                     &longinfo))
252                  fprintf(stream, "%ld", longinfo);
253                break;
254              default:
255                break;
256              }
257              break;
258            }
259          }
260          if(FALSE == match) {
261            fprintf(stderr, "curl: unknown --write-out variable: '%s'\n", ptr);
262          }
263          ptr=end+1; /* pass the end */
264          *end = keepit;
265        }
266        else {
267          /* illegal syntax, then just output the characters that are used */
268          fputc('%', stream);
269          fputc(ptr[1], stream);
270          ptr+=2;
271        }
272      }
273    }
274    else if('\\' == *ptr) {
275      switch(ptr[1]) {
276      case 'r':
277        fputc('\r', stream);
278        break;
279      case 'n':
280        fputc('\n', stream);
281        break;
282      case 't':
283        fputc('\t', stream);
284        break;
285      default:
286        /* unknown, just output this */
287        fputc(*ptr, stream);
288        fputc(ptr[1], stream);
289        break;
290      }
291      ptr+=2;
292    }
293    else {
294      fputc(*ptr, stream);
295      ptr++;
296    }
297  }
298
299}
300