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