1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2014, 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#include "tool_setup.h"
23
24#define ENABLE_CURLX_PRINTF
25/* use our own printf() functions */
26#include "curlx.h"
27
28#include "tool_cfgable.h"
29#include "tool_msgs.h"
30#include "tool_cb_dbg.h"
31#include "tool_util.h"
32
33#include "memdebug.h" /* keep this as LAST include */
34
35static void dump(const char *timebuf, const char *text,
36                 FILE *stream, const unsigned char *ptr, size_t size,
37                 trace tracetype, curl_infotype infotype);
38
39/*
40** callback for CURLOPT_DEBUGFUNCTION
41*/
42
43int tool_debug_cb(CURL *handle, curl_infotype type,
44                  unsigned char *data, size_t size,
45                  void *userdata)
46{
47  struct OperationConfig *operation = userdata;
48  struct GlobalConfig *config = operation->global;
49  FILE *output = config->errors;
50  const char *text;
51  struct timeval tv;
52  struct tm *now;
53  char timebuf[20];
54  time_t secs;
55  static time_t epoch_offset;
56  static int    known_offset;
57
58  (void)handle; /* not used */
59
60  if(config->tracetime) {
61    tv = tvnow();
62    if(!known_offset) {
63      epoch_offset = time(NULL) - tv.tv_sec;
64      known_offset = 1;
65    }
66    secs = epoch_offset + tv.tv_sec;
67    now = localtime(&secs);  /* not thread safe but we don't care */
68    snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld ",
69             now->tm_hour, now->tm_min, now->tm_sec, (long)tv.tv_usec);
70  }
71  else
72    timebuf[0] = 0;
73
74  if(!config->trace_stream) {
75    /* open for append */
76    if(curlx_strequal("-", config->trace_dump))
77      config->trace_stream = stdout;
78    else if(curlx_strequal("%", config->trace_dump))
79      /* Ok, this is somewhat hackish but we do it undocumented for now */
80      config->trace_stream = config->errors;  /* aka stderr */
81    else {
82      config->trace_stream = fopen(config->trace_dump, "w");
83      config->trace_fopened = TRUE;
84    }
85  }
86
87  if(config->trace_stream)
88    output = config->trace_stream;
89
90  if(!output) {
91    warnf(operation, "Failed to create/open output");
92    return 0;
93  }
94
95  if(config->tracetype == TRACE_PLAIN) {
96    /*
97     * This is the trace look that is similar to what libcurl makes on its
98     * own.
99     */
100    static const char * const s_infotype[] = {
101      "*", "<", ">", "{", "}", "{", "}"
102    };
103    size_t i;
104    size_t st = 0;
105    static bool newl = FALSE;
106    static bool traced_data = FALSE;
107
108    switch(type) {
109    case CURLINFO_HEADER_OUT:
110      if(size > 0) {
111        for(i = 0; i < size - 1; i++) {
112          if(data[i] == '\n') { /* LF */
113            if(!newl) {
114              fprintf(output, "%s%s ", timebuf, s_infotype[type]);
115            }
116            (void)fwrite(data + st, i - st + 1, 1, output);
117            st = i + 1;
118            newl = FALSE;
119          }
120        }
121        if(!newl)
122          fprintf(output, "%s%s ", timebuf, s_infotype[type]);
123        (void)fwrite(data + st, i - st + 1, 1, output);
124      }
125      newl = (size && (data[size - 1] != '\n')) ? TRUE : FALSE;
126      traced_data = FALSE;
127      break;
128    case CURLINFO_TEXT:
129    case CURLINFO_HEADER_IN:
130      if(!newl)
131        fprintf(output, "%s%s ", timebuf, s_infotype[type]);
132      (void)fwrite(data, size, 1, output);
133      newl = (size && (data[size - 1] != '\n')) ? TRUE : FALSE;
134      traced_data = FALSE;
135      break;
136    case CURLINFO_DATA_OUT:
137    case CURLINFO_DATA_IN:
138    case CURLINFO_SSL_DATA_IN:
139    case CURLINFO_SSL_DATA_OUT:
140      if(!traced_data) {
141        /* if the data is output to a tty and we're sending this debug trace
142           to stderr or stdout, we don't display the alert about the data not
143           being shown as the data _is_ shown then just not via this
144           function */
145        if(!config->isatty || ((output != stderr) && (output != stdout))) {
146          if(!newl)
147            fprintf(output, "%s%s ", timebuf, s_infotype[type]);
148          fprintf(output, "[data not shown]\n");
149          newl = FALSE;
150          traced_data = TRUE;
151        }
152      }
153      break;
154    default: /* nada */
155      newl = FALSE;
156      traced_data = FALSE;
157      break;
158    }
159
160    return 0;
161  }
162
163#ifdef CURL_DOES_CONVERSIONS
164  /* Special processing is needed for CURLINFO_HEADER_OUT blocks
165   * if they contain both headers and data (separated by CRLFCRLF).
166   * We dump the header text and then switch type to CURLINFO_DATA_OUT.
167   */
168  if((type == CURLINFO_HEADER_OUT) && (size > 4)) {
169    size_t i;
170    for(i = 0; i < size - 4; i++) {
171      if(memcmp(&data[i], "\r\n\r\n", 4) == 0) {
172        /* dump everything through the CRLFCRLF as a sent header */
173        text = "=> Send header";
174        dump(timebuf, text, output, data, i + 4, config->tracetype, type);
175        data += i + 3;
176        size -= i + 4;
177        type = CURLINFO_DATA_OUT;
178        data += 1;
179        break;
180      }
181    }
182  }
183#endif /* CURL_DOES_CONVERSIONS */
184
185  switch (type) {
186  case CURLINFO_TEXT:
187    fprintf(output, "%s== Info: %s", timebuf, data);
188  default: /* in case a new one is introduced to shock us */
189    return 0;
190
191  case CURLINFO_HEADER_OUT:
192    text = "=> Send header";
193    break;
194  case CURLINFO_DATA_OUT:
195    text = "=> Send data";
196    break;
197  case CURLINFO_HEADER_IN:
198    text = "<= Recv header";
199    break;
200  case CURLINFO_DATA_IN:
201    text = "<= Recv data";
202    break;
203  case CURLINFO_SSL_DATA_IN:
204    text = "<= Recv SSL data";
205    break;
206  case CURLINFO_SSL_DATA_OUT:
207    text = "=> Send SSL data";
208    break;
209  }
210
211  dump(timebuf, text, output, data, size, config->tracetype, type);
212  return 0;
213}
214
215static void dump(const char *timebuf, const char *text,
216                 FILE *stream, const unsigned char *ptr, size_t size,
217                 trace tracetype, curl_infotype infotype)
218{
219  size_t i;
220  size_t c;
221
222  unsigned int width = 0x10;
223
224  if(tracetype == TRACE_ASCII)
225    /* without the hex output, we can fit more on screen */
226    width = 0x40;
227
228  fprintf(stream, "%s%s, %zd bytes (0x%zx)\n", timebuf, text, size, size);
229
230  for(i = 0; i < size; i += width) {
231
232    fprintf(stream, "%04zx: ", i);
233
234    if(tracetype == TRACE_BIN) {
235      /* hex not disabled, show it */
236      for(c = 0; c < width; c++)
237        if(i+c < size)
238          fprintf(stream, "%02x ", ptr[i+c]);
239        else
240          fputs("   ", stream);
241    }
242
243    for(c = 0; (c < width) && (i+c < size); c++) {
244      /* check for 0D0A; if found, skip past and start a new line of output */
245      if((tracetype == TRACE_ASCII) &&
246         (i+c+1 < size) && (ptr[i+c] == 0x0D) && (ptr[i+c+1] == 0x0A)) {
247        i += (c+2-width);
248        break;
249      }
250#ifdef CURL_DOES_CONVERSIONS
251      /* repeat the 0D0A check above but use the host encoding for CRLF */
252      if((tracetype == TRACE_ASCII) &&
253         (i+c+1 < size) && (ptr[i+c] == '\r') && (ptr[i+c+1] == '\n')) {
254        i += (c+2-width);
255        break;
256      }
257      /* convert to host encoding and print this character */
258      fprintf(stream, "%c", convert_char(infotype, ptr[i+c]));
259#else
260      (void)infotype;
261      fprintf(stream, "%c", ((ptr[i+c] >= 0x20) && (ptr[i+c] < 0x80)) ?
262              ptr[i+c] : UNPRINTABLE_CHAR);
263#endif /* CURL_DOES_CONVERSIONS */
264      /* check again for 0D0A, to avoid an extra \n if it's at width */
265      if((tracetype == TRACE_ASCII) &&
266         (i+c+2 < size) && (ptr[i+c+1] == 0x0D) && (ptr[i+c+2] == 0x0A)) {
267        i += (c+3-width);
268        break;
269      }
270    }
271    fputc('\n', stream); /* newline */
272  }
273  fflush(stream);
274}
275
276