1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2015, 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 "curl_setup.h"
24
25 #include <curl/curl.h>
26
27 #include "urldata.h"
28 #include "sendf.h"
29 #include "connect.h"
30 #include "vtls/vtls.h"
31 #include "ssh.h"
32 #include "multiif.h"
33 #include "non-ascii.h"
34 #include "curl_printf.h"
35 #include "strerror.h"
36
37 /* The last #include files should be: */
38 #include "curl_memory.h"
39 #include "memdebug.h"
40
41 #ifdef CURL_DO_LINEEND_CONV
42 /*
43 * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
44 * (\n), with special processing for CRLF sequences that are split between two
45 * blocks of data. Remaining, bare CRs are changed to LFs. The possibly new
46 * size of the data is returned.
47 */
convert_lineends(struct SessionHandle * data,char * startPtr,size_t size)48 static size_t convert_lineends(struct SessionHandle *data,
49 char *startPtr, size_t size)
50 {
51 char *inPtr, *outPtr;
52
53 /* sanity check */
54 if((startPtr == NULL) || (size < 1)) {
55 return size;
56 }
57
58 if(data->state.prev_block_had_trailing_cr) {
59 /* The previous block of incoming data
60 had a trailing CR, which was turned into a LF. */
61 if(*startPtr == '\n') {
62 /* This block of incoming data starts with the
63 previous block's LF so get rid of it */
64 memmove(startPtr, startPtr+1, size-1);
65 size--;
66 /* and it wasn't a bare CR but a CRLF conversion instead */
67 data->state.crlf_conversions++;
68 }
69 data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */
70 }
71
72 /* find 1st CR, if any */
73 inPtr = outPtr = memchr(startPtr, '\r', size);
74 if(inPtr) {
75 /* at least one CR, now look for CRLF */
76 while(inPtr < (startPtr+size-1)) {
77 /* note that it's size-1, so we'll never look past the last byte */
78 if(memcmp(inPtr, "\r\n", 2) == 0) {
79 /* CRLF found, bump past the CR and copy the NL */
80 inPtr++;
81 *outPtr = *inPtr;
82 /* keep track of how many CRLFs we converted */
83 data->state.crlf_conversions++;
84 }
85 else {
86 if(*inPtr == '\r') {
87 /* lone CR, move LF instead */
88 *outPtr = '\n';
89 }
90 else {
91 /* not a CRLF nor a CR, just copy whatever it is */
92 *outPtr = *inPtr;
93 }
94 }
95 outPtr++;
96 inPtr++;
97 } /* end of while loop */
98
99 if(inPtr < startPtr+size) {
100 /* handle last byte */
101 if(*inPtr == '\r') {
102 /* deal with a CR at the end of the buffer */
103 *outPtr = '\n'; /* copy a NL instead */
104 /* note that a CRLF might be split across two blocks */
105 data->state.prev_block_had_trailing_cr = TRUE;
106 }
107 else {
108 /* copy last byte */
109 *outPtr = *inPtr;
110 }
111 outPtr++;
112 }
113 if(outPtr < startPtr+size)
114 /* tidy up by null terminating the now shorter data */
115 *outPtr = '\0';
116
117 return (outPtr - startPtr);
118 }
119 return size;
120 }
121 #endif /* CURL_DO_LINEEND_CONV */
122
123 /* Curl_infof() is for info message along the way */
124
Curl_infof(struct SessionHandle * data,const char * fmt,...)125 void Curl_infof(struct SessionHandle *data, const char *fmt, ...)
126 {
127 if(data && data->set.verbose) {
128 va_list ap;
129 size_t len;
130 char print_buffer[2048 + 1];
131 va_start(ap, fmt);
132 vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
133 va_end(ap);
134 len = strlen(print_buffer);
135 Curl_debug(data, CURLINFO_TEXT, print_buffer, len, NULL);
136 }
137 }
138
139 /* Curl_failf() is for messages stating why we failed.
140 * The message SHALL NOT include any LF or CR.
141 */
142
Curl_failf(struct SessionHandle * data,const char * fmt,...)143 void Curl_failf(struct SessionHandle *data, const char *fmt, ...)
144 {
145 va_list ap;
146 size_t len;
147 va_start(ap, fmt);
148
149 vsnprintf(data->state.buffer, BUFSIZE, fmt, ap);
150
151 if(data->set.errorbuffer && !data->state.errorbuf) {
152 snprintf(data->set.errorbuffer, CURL_ERROR_SIZE, "%s", data->state.buffer);
153 data->state.errorbuf = TRUE; /* wrote error string */
154 }
155 if(data->set.verbose) {
156 len = strlen(data->state.buffer);
157 if(len < BUFSIZE - 1) {
158 data->state.buffer[len] = '\n';
159 data->state.buffer[++len] = '\0';
160 }
161 Curl_debug(data, CURLINFO_TEXT, data->state.buffer, len, NULL);
162 }
163
164 va_end(ap);
165 }
166
167 /* Curl_sendf() sends formated data to the server */
Curl_sendf(curl_socket_t sockfd,struct connectdata * conn,const char * fmt,...)168 CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn,
169 const char *fmt, ...)
170 {
171 struct SessionHandle *data = conn->data;
172 ssize_t bytes_written;
173 size_t write_len;
174 CURLcode result = CURLE_OK;
175 char *s;
176 char *sptr;
177 va_list ap;
178 va_start(ap, fmt);
179 s = vaprintf(fmt, ap); /* returns an allocated string */
180 va_end(ap);
181 if(!s)
182 return CURLE_OUT_OF_MEMORY; /* failure */
183
184 bytes_written=0;
185 write_len = strlen(s);
186 sptr = s;
187
188 for(;;) {
189 /* Write the buffer to the socket */
190 result = Curl_write(conn, sockfd, sptr, write_len, &bytes_written);
191
192 if(result)
193 break;
194
195 if(data->set.verbose)
196 Curl_debug(data, CURLINFO_DATA_OUT, sptr, (size_t)bytes_written, conn);
197
198 if((size_t)bytes_written != write_len) {
199 /* if not all was written at once, we must advance the pointer, decrease
200 the size left and try again! */
201 write_len -= bytes_written;
202 sptr += bytes_written;
203 }
204 else
205 break;
206 }
207
208 free(s); /* free the output string */
209
210 return result;
211 }
212
213 /*
214 * Curl_write() is an internal write function that sends data to the
215 * server. Works with plain sockets, SCP, SSL or kerberos.
216 *
217 * If the write would block (CURLE_AGAIN), we return CURLE_OK and
218 * (*written == 0). Otherwise we return regular CURLcode value.
219 */
Curl_write(struct connectdata * conn,curl_socket_t sockfd,const void * mem,size_t len,ssize_t * written)220 CURLcode Curl_write(struct connectdata *conn,
221 curl_socket_t sockfd,
222 const void *mem,
223 size_t len,
224 ssize_t *written)
225 {
226 ssize_t bytes_written;
227 CURLcode result = CURLE_OK;
228 int num = (sockfd == conn->sock[SECONDARYSOCKET]);
229
230 bytes_written = conn->send[num](conn, num, mem, len, &result);
231
232 *written = bytes_written;
233 if(bytes_written >= 0)
234 /* we completely ignore the curlcode value when subzero is not returned */
235 return CURLE_OK;
236
237 /* handle CURLE_AGAIN or a send failure */
238 switch(result) {
239 case CURLE_AGAIN:
240 *written = 0;
241 return CURLE_OK;
242
243 case CURLE_OK:
244 /* general send failure */
245 return CURLE_SEND_ERROR;
246
247 default:
248 /* we got a specific curlcode, forward it */
249 return result;
250 }
251 }
252
Curl_send_plain(struct connectdata * conn,int num,const void * mem,size_t len,CURLcode * code)253 ssize_t Curl_send_plain(struct connectdata *conn, int num,
254 const void *mem, size_t len, CURLcode *code)
255 {
256 curl_socket_t sockfd = conn->sock[num];
257 ssize_t bytes_written = swrite(sockfd, mem, len);
258
259 *code = CURLE_OK;
260 if(-1 == bytes_written) {
261 int err = SOCKERRNO;
262
263 if(
264 #ifdef WSAEWOULDBLOCK
265 /* This is how Windows does it */
266 (WSAEWOULDBLOCK == err)
267 #else
268 /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
269 due to its inability to send off data without blocking. We therefor
270 treat both error codes the same here */
271 (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
272 #endif
273 ) {
274 /* this is just a case of EWOULDBLOCK */
275 bytes_written=0;
276 *code = CURLE_AGAIN;
277 }
278 else {
279 failf(conn->data, "Send failure: %s",
280 Curl_strerror(conn, err));
281 conn->data->state.os_errno = err;
282 *code = CURLE_SEND_ERROR;
283 }
284 }
285 return bytes_written;
286 }
287
288 /*
289 * Curl_write_plain() is an internal write function that sends data to the
290 * server using plain sockets only. Otherwise meant to have the exact same
291 * proto as Curl_write()
292 */
Curl_write_plain(struct connectdata * conn,curl_socket_t sockfd,const void * mem,size_t len,ssize_t * written)293 CURLcode Curl_write_plain(struct connectdata *conn,
294 curl_socket_t sockfd,
295 const void *mem,
296 size_t len,
297 ssize_t *written)
298 {
299 ssize_t bytes_written;
300 CURLcode result;
301 int num = (sockfd == conn->sock[SECONDARYSOCKET]);
302
303 bytes_written = Curl_send_plain(conn, num, mem, len, &result);
304
305 *written = bytes_written;
306
307 return result;
308 }
309
Curl_recv_plain(struct connectdata * conn,int num,char * buf,size_t len,CURLcode * code)310 ssize_t Curl_recv_plain(struct connectdata *conn, int num, char *buf,
311 size_t len, CURLcode *code)
312 {
313 curl_socket_t sockfd = conn->sock[num];
314 ssize_t nread = sread(sockfd, buf, len);
315
316 *code = CURLE_OK;
317 if(-1 == nread) {
318 int err = SOCKERRNO;
319
320 if(
321 #ifdef WSAEWOULDBLOCK
322 /* This is how Windows does it */
323 (WSAEWOULDBLOCK == err)
324 #else
325 /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
326 due to its inability to send off data without blocking. We therefor
327 treat both error codes the same here */
328 (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
329 #endif
330 ) {
331 /* this is just a case of EWOULDBLOCK */
332 *code = CURLE_AGAIN;
333 }
334 else {
335 failf(conn->data, "Recv failure: %s",
336 Curl_strerror(conn, err));
337 conn->data->state.os_errno = err;
338 *code = CURLE_RECV_ERROR;
339 }
340 }
341 return nread;
342 }
343
pausewrite(struct SessionHandle * data,int type,const char * ptr,size_t len)344 static CURLcode pausewrite(struct SessionHandle *data,
345 int type, /* what type of data */
346 const char *ptr,
347 size_t len)
348 {
349 /* signalled to pause sending on this connection, but since we have data
350 we want to send we need to dup it to save a copy for when the sending
351 is again enabled */
352 struct SingleRequest *k = &data->req;
353 char *dupl = malloc(len);
354 if(!dupl)
355 return CURLE_OUT_OF_MEMORY;
356
357 memcpy(dupl, ptr, len);
358
359 /* store this information in the state struct for later use */
360 data->state.tempwrite = dupl;
361 data->state.tempwritesize = len;
362 data->state.tempwritetype = type;
363
364 /* mark the connection as RECV paused */
365 k->keepon |= KEEP_RECV_PAUSE;
366
367 DEBUGF(infof(data, "Pausing with %zu bytes in buffer for type %02x\n",
368 len, type));
369
370 return CURLE_OK;
371 }
372
373
374 /* Curl_client_chop_write() writes chunks of data not larger than
375 * CURL_MAX_WRITE_SIZE via client write callback(s) and
376 * takes care of pause requests from the callbacks.
377 */
Curl_client_chop_write(struct connectdata * conn,int type,char * ptr,size_t len)378 CURLcode Curl_client_chop_write(struct connectdata *conn,
379 int type,
380 char * ptr,
381 size_t len)
382 {
383 struct SessionHandle *data = conn->data;
384 curl_write_callback writeheader = NULL;
385 curl_write_callback writebody = NULL;
386
387 if(!len)
388 return CURLE_OK;
389
390 /* If reading is actually paused, we're forced to append this chunk of data
391 to the already held data, but only if it is the same type as otherwise it
392 can't work and it'll return error instead. */
393 if(data->req.keepon & KEEP_RECV_PAUSE) {
394 size_t newlen;
395 char *newptr;
396 if(type != data->state.tempwritetype)
397 /* major internal confusion */
398 return CURLE_RECV_ERROR;
399
400 DEBUGASSERT(data->state.tempwrite);
401
402 /* figure out the new size of the data to save */
403 newlen = len + data->state.tempwritesize;
404 /* allocate the new memory area */
405 newptr = realloc(data->state.tempwrite, newlen);
406 if(!newptr)
407 return CURLE_OUT_OF_MEMORY;
408 /* copy the new data to the end of the new area */
409 memcpy(newptr + data->state.tempwritesize, ptr, len);
410 /* update the pointer and the size */
411 data->state.tempwrite = newptr;
412 data->state.tempwritesize = newlen;
413 return CURLE_OK;
414 }
415
416 /* Determine the callback(s) to use. */
417 if(type & CLIENTWRITE_BODY)
418 writebody = data->set.fwrite_func;
419 if((type & CLIENTWRITE_HEADER) &&
420 (data->set.fwrite_header || data->set.writeheader)) {
421 /*
422 * Write headers to the same callback or to the especially setup
423 * header callback function (added after version 7.7.1).
424 */
425 writeheader =
426 data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func;
427 }
428
429 /* Chop data, write chunks. */
430 while(len) {
431 size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE;
432
433 if(writebody) {
434 size_t wrote = writebody(ptr, 1, chunklen, data->set.out);
435
436 if(CURL_WRITEFUNC_PAUSE == wrote) {
437 if(conn->handler->flags & PROTOPT_NONETWORK) {
438 /* Protocols that work without network cannot be paused. This is
439 actually only FILE:// just now, and it can't pause since the
440 transfer isn't done using the "normal" procedure. */
441 failf(data, "Write callback asked for PAUSE when not supported!");
442 return CURLE_WRITE_ERROR;
443 }
444 else
445 return pausewrite(data, type, ptr, len);
446 }
447 else if(wrote != chunklen) {
448 failf(data, "Failed writing body (%zu != %zu)", wrote, chunklen);
449 return CURLE_WRITE_ERROR;
450 }
451 }
452
453 if(writeheader) {
454 size_t wrote = writeheader(ptr, 1, chunklen, data->set.writeheader);
455
456 if(CURL_WRITEFUNC_PAUSE == wrote)
457 /* here we pass in the HEADER bit only since if this was body as well
458 then it was passed already and clearly that didn't trigger the
459 pause, so this is saved for later with the HEADER bit only */
460 return pausewrite(data, CLIENTWRITE_HEADER, ptr, len);
461
462 if(wrote != chunklen) {
463 failf (data, "Failed writing header");
464 return CURLE_WRITE_ERROR;
465 }
466 }
467
468 ptr += chunklen;
469 len -= chunklen;
470 }
471
472 return CURLE_OK;
473 }
474
475
476 /* Curl_client_write() sends data to the write callback(s)
477
478 The bit pattern defines to what "streams" to write to. Body and/or header.
479 The defines are in sendf.h of course.
480
481 If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
482 local character encoding. This is a problem and should be changed in
483 the future to leave the original data alone.
484 */
Curl_client_write(struct connectdata * conn,int type,char * ptr,size_t len)485 CURLcode Curl_client_write(struct connectdata *conn,
486 int type,
487 char *ptr,
488 size_t len)
489 {
490 struct SessionHandle *data = conn->data;
491
492 if(0 == len)
493 len = strlen(ptr);
494
495 /* FTP data may need conversion. */
496 if((type & CLIENTWRITE_BODY) &&
497 (conn->handler->protocol & PROTO_FAMILY_FTP) &&
498 conn->proto.ftpc.transfertype == 'A') {
499 /* convert from the network encoding */
500 CURLcode result = Curl_convert_from_network(data, ptr, len);
501 /* Curl_convert_from_network calls failf if unsuccessful */
502 if(result)
503 return result;
504
505 #ifdef CURL_DO_LINEEND_CONV
506 /* convert end-of-line markers */
507 len = convert_lineends(data, ptr, len);
508 #endif /* CURL_DO_LINEEND_CONV */
509 }
510
511 return Curl_client_chop_write(conn, type, ptr, len);
512 }
513
Curl_read_plain(curl_socket_t sockfd,char * buf,size_t bytesfromsocket,ssize_t * n)514 CURLcode Curl_read_plain(curl_socket_t sockfd,
515 char *buf,
516 size_t bytesfromsocket,
517 ssize_t *n)
518 {
519 ssize_t nread = sread(sockfd, buf, bytesfromsocket);
520
521 if(-1 == nread) {
522 int err = SOCKERRNO;
523 #ifdef USE_WINSOCK
524 if(WSAEWOULDBLOCK == err)
525 #else
526 if((EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err))
527 #endif
528 return CURLE_AGAIN;
529 else
530 return CURLE_RECV_ERROR;
531 }
532
533 /* we only return number of bytes read when we return OK */
534 *n = nread;
535 return CURLE_OK;
536 }
537
538 /*
539 * Internal read-from-socket function. This is meant to deal with plain
540 * sockets, SSL sockets and kerberos sockets.
541 *
542 * Returns a regular CURLcode value.
543 */
Curl_read(struct connectdata * conn,curl_socket_t sockfd,char * buf,size_t sizerequested,ssize_t * n)544 CURLcode Curl_read(struct connectdata *conn, /* connection data */
545 curl_socket_t sockfd, /* read from this socket */
546 char *buf, /* store read data here */
547 size_t sizerequested, /* max amount to read */
548 ssize_t *n) /* amount bytes read */
549 {
550 CURLcode result = CURLE_RECV_ERROR;
551 ssize_t nread = 0;
552 size_t bytesfromsocket = 0;
553 char *buffertofill = NULL;
554 bool pipelining = Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1);
555
556 /* Set 'num' to 0 or 1, depending on which socket that has been sent here.
557 If it is the second socket, we set num to 1. Otherwise to 0. This lets
558 us use the correct ssl handle. */
559 int num = (sockfd == conn->sock[SECONDARYSOCKET]);
560
561 *n=0; /* reset amount to zero */
562
563 /* If session can pipeline, check connection buffer */
564 if(pipelining) {
565 size_t bytestocopy = CURLMIN(conn->buf_len - conn->read_pos,
566 sizerequested);
567
568 /* Copy from our master buffer first if we have some unread data there*/
569 if(bytestocopy > 0) {
570 memcpy(buf, conn->master_buffer + conn->read_pos, bytestocopy);
571 conn->read_pos += bytestocopy;
572 conn->bits.stream_was_rewound = FALSE;
573
574 *n = (ssize_t)bytestocopy;
575 return CURLE_OK;
576 }
577 /* If we come here, it means that there is no data to read from the buffer,
578 * so we read from the socket */
579 bytesfromsocket = CURLMIN(sizerequested, BUFSIZE * sizeof (char));
580 buffertofill = conn->master_buffer;
581 }
582 else {
583 bytesfromsocket = CURLMIN((long)sizerequested,
584 conn->data->set.buffer_size ?
585 conn->data->set.buffer_size : BUFSIZE);
586 buffertofill = buf;
587 }
588
589 nread = conn->recv[num](conn, num, buffertofill, bytesfromsocket, &result);
590 if(nread < 0)
591 return result;
592
593 if(pipelining) {
594 memcpy(buf, conn->master_buffer, nread);
595 conn->buf_len = nread;
596 conn->read_pos = nread;
597 }
598
599 *n += nread;
600
601 return CURLE_OK;
602 }
603
604 /* return 0 on success */
showit(struct SessionHandle * data,curl_infotype type,char * ptr,size_t size)605 static int showit(struct SessionHandle *data, curl_infotype type,
606 char *ptr, size_t size)
607 {
608 static const char s_infotype[CURLINFO_END][3] = {
609 "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
610
611 #ifdef CURL_DOES_CONVERSIONS
612 char buf[BUFSIZE+1];
613 size_t conv_size = 0;
614
615 switch(type) {
616 case CURLINFO_HEADER_OUT:
617 /* assume output headers are ASCII */
618 /* copy the data into my buffer so the original is unchanged */
619 if(size > BUFSIZE) {
620 size = BUFSIZE; /* truncate if necessary */
621 buf[BUFSIZE] = '\0';
622 }
623 conv_size = size;
624 memcpy(buf, ptr, size);
625 /* Special processing is needed for this block if it
626 * contains both headers and data (separated by CRLFCRLF).
627 * We want to convert just the headers, leaving the data as-is.
628 */
629 if(size > 4) {
630 size_t i;
631 for(i = 0; i < size-4; i++) {
632 if(memcmp(&buf[i], "\x0d\x0a\x0d\x0a", 4) == 0) {
633 /* convert everything through this CRLFCRLF but no further */
634 conv_size = i + 4;
635 break;
636 }
637 }
638 }
639
640 Curl_convert_from_network(data, buf, conv_size);
641 /* Curl_convert_from_network calls failf if unsuccessful */
642 /* we might as well continue even if it fails... */
643 ptr = buf; /* switch pointer to use my buffer instead */
644 break;
645 default:
646 /* leave everything else as-is */
647 break;
648 }
649 #endif /* CURL_DOES_CONVERSIONS */
650
651 if(data->set.fdebug)
652 return (*data->set.fdebug)(data, type, ptr, size,
653 data->set.debugdata);
654
655 switch(type) {
656 case CURLINFO_TEXT:
657 case CURLINFO_HEADER_OUT:
658 case CURLINFO_HEADER_IN:
659 fwrite(s_infotype[type], 2, 1, data->set.err);
660 fwrite(ptr, size, 1, data->set.err);
661 #ifdef CURL_DOES_CONVERSIONS
662 if(size != conv_size) {
663 /* we had untranslated data so we need an explicit newline */
664 fwrite("\n", 1, 1, data->set.err);
665 }
666 #endif
667 break;
668 default: /* nada */
669 break;
670 }
671 return 0;
672 }
673
Curl_debug(struct SessionHandle * data,curl_infotype type,char * ptr,size_t size,struct connectdata * conn)674 int Curl_debug(struct SessionHandle *data, curl_infotype type,
675 char *ptr, size_t size,
676 struct connectdata *conn)
677 {
678 int rc;
679 if(data->set.printhost && conn && conn->host.dispname) {
680 char buffer[160];
681 const char *t=NULL;
682 const char *w="Data";
683 switch (type) {
684 case CURLINFO_HEADER_IN:
685 w = "Header";
686 /* FALLTHROUGH */
687 case CURLINFO_DATA_IN:
688 t = "from";
689 break;
690 case CURLINFO_HEADER_OUT:
691 w = "Header";
692 /* FALLTHROUGH */
693 case CURLINFO_DATA_OUT:
694 t = "to";
695 break;
696 default:
697 break;
698 }
699
700 if(t) {
701 snprintf(buffer, sizeof(buffer), "[%s %s %s]", w, t,
702 conn->host.dispname);
703 rc = showit(data, CURLINFO_TEXT, buffer, strlen(buffer));
704 if(rc)
705 return rc;
706 }
707 }
708 rc = showit(data, type, ptr, size);
709 return rc;
710 }
711