8e81f1be32bfe8a8cd1858b137f3e9bda168e35c
[platform/upstream/curl.git] / src / tool_cb_dbg.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2016, 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
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
35 static 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
43 int 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(!strcmp("-", config->trace_dump))
77       config->trace_stream = stdout;
78     else if(!strcmp("%", 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, FOPEN_WRITETEXT);
83       config->trace_fopened = TRUE;
84     }
85   }
86
87   if(config->trace_stream)
88     output = config->trace_stream;
89
90   if(!output) {
91     warnf(config, "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, "[%zd bytes data]\n", size);
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
215 static 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