Remove $Id$ tags from most of webcit
[citadel.git] / webcit / tcp_sockets.c
1 /*
2  * Copyright (c) 1987-2010 by the citadel.org team
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 /*
20  * Uncomment this to log all communications with the Citadel server
21 #define SERV_TRACE 1
22  */
23
24
25 #include "webcit.h"
26 #include "webserver.h"
27
28 extern int DisableGzip;
29 long MaxRead = -1; /* should we do READ scattered or all at once? */
30
31 /*
32  * register the timeout
33  */
34 RETSIGTYPE timeout(int signum)
35 {
36         lprintf(1, "Connection timed out; unable to reach citserver\n");
37         /* no exit here, since we need to server the connection unreachable thing. exit(3); */
38 }
39
40
41 /*
42  * Client side - connect to a unix domain socket
43  */
44 int uds_connectsock(char *sockpath)
45 {
46         struct sockaddr_un addr;
47         int s;
48
49         memset(&addr, 0, sizeof(addr));
50         addr.sun_family = AF_UNIX;
51         strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
52
53         s = socket(AF_UNIX, SOCK_STREAM, 0);
54         if (s < 0) {
55                 lprintf(1, "Can't create socket[%s]: %s\n", sockpath, strerror(errno));
56                 return(-1);
57         }
58
59         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
60                 lprintf(1, "Can't connect [%s]: %s\n", sockpath, strerror(errno));
61                 close(s);
62                 return(-1);
63         }
64
65         return s;
66 }
67
68
69 /*
70  * TCP client - connect to a host/port 
71  */
72 int tcp_connectsock(char *host, char *service)
73 {
74         struct in6_addr serveraddr;
75         struct addrinfo hints;
76         struct addrinfo *res = NULL;
77         struct addrinfo *ai = NULL;
78         int rc = (-1);
79         int s = (-1);
80
81         if ((host == NULL) || IsEmptyStr(host))
82                 return (-1);
83         if ((service == NULL) || IsEmptyStr(service))
84                 return (-1);
85
86         lprintf(9, "tcp_connectsock(%s,%s)\n", host, service);
87
88         memset(&hints, 0x00, sizeof(hints));
89         hints.ai_flags = AI_NUMERICSERV;
90         hints.ai_family = AF_UNSPEC;
91         hints.ai_socktype = SOCK_STREAM;
92
93         /*
94          * Handle numeric IPv4 and IPv6 addresses
95          */
96         rc = inet_pton(AF_INET, host, &serveraddr);
97         if (rc == 1) {                                          /* dotted quad */
98                 hints.ai_family = AF_INET;
99                 hints.ai_flags |= AI_NUMERICHOST;
100         } else {
101                 rc = inet_pton(AF_INET6, host, &serveraddr);
102                 if (rc == 1) {                                  /* IPv6 address */
103                         hints.ai_family = AF_INET6;
104                         hints.ai_flags |= AI_NUMERICHOST;
105                 }
106         }
107
108         /* Begin the connection process */
109
110         rc = getaddrinfo(host, service, &hints, &res);
111         if (rc != 0) {
112                 lprintf(1, "%s: %s\n", host, gai_strerror(rc));
113                 return(-1);
114         }
115
116         /*
117          * Try all available addresses until we connect to one or until we run out.
118          */
119         for (ai = res; ai != NULL; ai = ai->ai_next) {
120
121                 if (ai->ai_family == AF_INET) lprintf(9, "Trying IPv4\n");
122                 else if (ai->ai_family == AF_INET6) lprintf(9, "Trying IPv6\n");
123                 else lprintf(9, "This is going to fail.\n");
124
125                 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
126                 if (s < 0) {
127                         lprintf(1, "socket() failed: %s\n", strerror(errno));
128                         return(-1);
129                 }
130                 rc = connect(s, ai->ai_addr, ai->ai_addrlen);
131                 if (rc >= 0) {
132                         return(s);
133                 }
134                 else {
135                         lprintf(1, "connect() failed: %s\n", strerror(errno));
136                         close(s);
137                 }
138         }
139
140         return(-1);
141 }
142
143
144 /*
145  *  input string from pipe
146  */
147 int serv_getln(char *strbuf, int bufsize)
148 {
149         wcsession *WCC = WC;
150         int len;
151
152         *strbuf = '\0';
153         StrBuf_ServGetln(WCC->MigrateReadLineBuf);
154         len = StrLength(WCC->MigrateReadLineBuf);
155         if (len > bufsize)
156                 len = bufsize - 1;
157         memcpy(strbuf, ChrPtr(WCC->MigrateReadLineBuf), len);
158         FlushStrBuf(WCC->MigrateReadLineBuf);
159         strbuf[len] = '\0';
160 #ifdef SERV_TRACE
161         lprintf(9, "%3d<<<%s\n", WC->serv_sock, strbuf);
162 #endif
163         return len;
164 }
165
166
167 int StrBuf_ServGetln(StrBuf *buf)
168 {
169         wcsession *WCC = WC;
170         const char *ErrStr = NULL;
171         int rc;
172         
173         FlushStrBuf(buf);
174         rc = StrBufTCP_read_buffered_line_fast(buf, 
175                                                WCC->ReadBuf, 
176                                                &WCC->ReadPos, 
177                                                &WCC->serv_sock, 
178                                                5, 1, 
179                                                &ErrStr);
180         if (rc < 0)
181         {
182                 lprintf(1, "Server connection broken: %s\n",
183                         (ErrStr)?ErrStr:"");
184                 wc_backtrace();
185                 WCC->serv_sock = (-1);
186                 WCC->connected = 0;
187                 WCC->logged_in = 0;
188         }
189 #ifdef SERV_TRACE
190         else 
191         {
192                 long pos = 0;
193                 if (WCC->ReadPos != NULL)
194                         pos = WCC->ReadPos - ChrPtr(WCC->ReadBuf);
195                 lprintf(9, "%3d<<<[%ld]%s\n", WC->serv_sock, pos, ChrPtr(buf));
196         }
197 #endif
198         return rc;
199 }
200
201 int StrBuf_ServGetBLOBBuffered(StrBuf *buf, long BlobSize)
202 {
203         wcsession *WCC = WC;
204         const char *ErrStr;
205         int rc;
206         
207         rc = StrBufReadBLOBBuffered(buf, 
208                                     WCC->ReadBuf, 
209                                     &WCC->ReadPos,
210                                     &WCC->serv_sock, 
211                                     1, 
212                                     BlobSize, 
213                                     NNN_TERM,
214                                     &ErrStr);
215         if (rc < 0)
216         {
217                 lprintf(1, "Server connection broken: %s\n",
218                         (ErrStr)?ErrStr:"");
219                 wc_backtrace();
220                 WCC->serv_sock = (-1);
221                 WCC->connected = 0;
222                 WCC->logged_in = 0;
223         }
224 #ifdef SERV_TRACE
225         else
226                 lprintf(9, "%3d<<<BLOB: %ld bytes\n", WC->serv_sock, StrLength(buf));
227 #endif
228
229         return rc;
230 }
231
232 int StrBuf_ServGetBLOB(StrBuf *buf, long BlobSize)
233 {
234         wcsession *WCC = WC;
235         const char *ErrStr;
236         int rc;
237         
238         WCC->ReadPos = NULL;
239         rc = StrBufReadBLOB(buf, &WCC->serv_sock, 1, BlobSize, &ErrStr);
240         if (rc < 0)
241         {
242                 lprintf(1, "Server connection broken: %s\n",
243                         (ErrStr)?ErrStr:"");
244                 wc_backtrace();
245                 WCC->serv_sock = (-1);
246                 WCC->connected = 0;
247                 WCC->logged_in = 0;
248         }
249 #ifdef SERV_TRACE
250         else
251                 lprintf(9, "%3d<<<BLOB: %ld bytes\n", WC->serv_sock, StrLength(buf));
252 #endif
253
254         return rc;
255 }
256
257
258 void FlushReadBuf (void)
259 {
260         long len;
261         const char *pch;
262         const char *pche;
263         wcsession *WCC = WC;
264
265         len = StrLength(WCC->ReadBuf);
266         if ((len > 0) &&
267             (WCC->ReadPos != NULL) && 
268             (WCC->ReadPos != StrBufNOTNULL))
269                 
270         {
271                 pch = ChrPtr(WCC->ReadBuf);
272                 pche = pch + len;
273                 if (WCC->ReadPos != pche)
274                 {
275                         lprintf(1, "ERROR: somebody didn't eat his soup! Remaing Chars: %d [%s]\n", 
276                                 pche - WCC->ReadPos, pche);
277                         lprintf(1, 
278                                 "--------------------------------------------------------------------------------\n"
279                                 "Whole buf: [%s]\n"
280                                 "--------------------------------------------------------------------------------\n", 
281                                 pch);
282                         AppendImportantMessage(HKEY("Suppenkasper alert! watch your webcit logfile and get connected to your favourite opensource Crew."));
283                 }
284         }
285
286         FlushStrBuf(WCC->ReadBuf);
287         WCC->ReadPos = NULL;
288
289
290 }
291
292
293 /*
294  *  send binary to server
295  *  buf the buffer to write to citadel server
296  *  nbytes how many bytes to send to citadel server
297  */
298 void serv_write(const char *buf, int nbytes)
299 {
300         wcsession *WCC = WC;
301         int bytes_written = 0;
302         int retval;
303
304         FlushReadBuf();
305         while (bytes_written < nbytes) {
306                 retval = write(WCC->serv_sock, &buf[bytes_written],
307                                nbytes - bytes_written);
308                 if (retval < 1) {
309                         const char *ErrStr = strerror(errno);
310                         lprintf(1, "Server connection broken: %s\n",
311                                 (ErrStr)?ErrStr:"");
312                         close(WCC->serv_sock);
313                         WCC->serv_sock = (-1);
314                         WCC->connected = 0;
315                         WCC->logged_in = 0;
316                         return;
317                 }
318                 bytes_written = bytes_written + retval;
319         }
320 }
321
322
323 /*
324  *  send line to server
325  *  string the line to send to the citadel server
326  */
327 void serv_puts(const char *string)
328 {
329 #ifdef SERV_TRACE
330         lprintf(9, "%3d>>>%s\n", WC->serv_sock, string);
331 #endif
332         FlushReadBuf();
333
334         serv_write(string, strlen(string));
335         serv_write("\n", 1);
336 }
337
338 /*
339  *  send line to server
340  *  string the line to send to the citadel server
341  */
342 void serv_putbuf(const StrBuf *string)
343 {
344 #ifdef SERV_TRACE
345         lprintf(9, "%3d>>>%s\n", WC->serv_sock, ChrPtr(string));
346 #endif
347         FlushReadBuf();
348
349         serv_write(ChrPtr(string), StrLength(string));
350         serv_write("\n", 1);
351 }
352
353
354 /*
355  *  convenience function to send stuff to the server
356  *  format the formatstring
357  *  ... the entities to insert into format 
358  */
359 void serv_printf(const char *format,...)
360 {
361         va_list arg_ptr;
362         char buf[SIZ];
363         size_t len;
364
365         FlushReadBuf();
366
367         va_start(arg_ptr, format);
368         vsnprintf(buf, sizeof buf, format, arg_ptr);
369         va_end(arg_ptr);
370
371         len = strlen(buf);
372         buf[len++] = '\n';
373         buf[len] = '\0';
374         serv_write(buf, len);
375 #ifdef SERV_TRACE
376         lprintf(9, ">>>%s", buf);
377 #endif
378 }
379
380
381
382 /**
383  * Read binary data from server into memory using a series of
384  * server READ commands.
385  * \return the read content as StrBuf
386  */
387 int serv_read_binary(StrBuf *Ret, size_t total_len, StrBuf *Buf) 
388 {
389         wcsession *WCC = WC;
390         size_t bytes = 0;
391         size_t thisblock = 0;
392         
393         if (Ret == NULL)
394             return -1;
395
396         if (MaxRead == -1)
397         {
398                 serv_printf("READ %d|"SIZE_T_FMT, 0, total_len);
399                 if (StrBuf_ServGetln(Buf) > 0)
400                 {
401                         long YetRead;
402                         const char *ErrStr;
403                         const char *pch;
404                         int rc;
405
406                         if (GetServerStatus(Buf, NULL) == 6)
407                         {
408                             StrBufCutLeft(Buf, 4);
409                             thisblock = StrTol(Buf);
410                             if (WCC->serv_sock==-1) {
411                                     FlushStrBuf(Ret); 
412                                     return -1; 
413                             }
414
415                             if (WCC->ReadPos != NULL) {
416                                     pch = ChrPtr(WCC->ReadBuf);
417
418                                     YetRead = WCC->ReadPos - pch;
419                                     if (YetRead > 0)
420                                     {
421                                             long StillThere;
422                                             
423                                             StillThere = StrLength(WCC->ReadBuf) - 
424                                                     YetRead;
425                                             
426                                             StrBufPlain(Ret, 
427                                                         WCC->ReadPos,
428                                                         StillThere);
429                                             total_len -= StillThere;
430                                     }
431                                     FlushStrBuf(WCC->ReadBuf);
432                                     WCC->ReadPos = NULL;
433                             } 
434                             if (total_len > 0)
435                             {
436                                     rc = StrBufReadBLOB(Ret, 
437                                                         &WCC->serv_sock, 
438                                                         1, 
439                                                         total_len,
440                                                         &ErrStr);
441                                     if (rc < 0)
442                                     {
443                                             lprintf(1, "Server connection broken: %s\n",
444                                                     (ErrStr)?ErrStr:"");
445                                             wc_backtrace();
446                                             WCC->serv_sock = (-1);
447                                             WCC->connected = 0;
448                                             WCC->logged_in = 0;
449                                             return rc;
450                                     }
451                                     else
452                                             return StrLength(Ret);
453                             }
454                             else 
455                                     return StrLength(Ret);
456                         }
457                 }
458                 else
459                         return -1;
460         }
461         else while ((WCC->serv_sock!=-1) &&
462                (bytes < total_len)) {
463                 thisblock = MaxRead;
464                 if ((total_len - bytes) < thisblock) {
465                         thisblock = total_len - bytes;
466                         if (thisblock == 0) {
467                                 FlushStrBuf(Ret); 
468                                 return -1; 
469                         }
470                 }
471                 serv_printf("READ %d|%d", (int)bytes, (int)thisblock);
472                 if (StrBuf_ServGetln(Buf) > 0)
473                 {
474                         if (GetServerStatus(Buf, NULL) == 6)
475                         {
476                             StrBufCutLeft(Buf, 4);
477                             thisblock = StrTol(Buf);
478                             if (WCC->serv_sock==-1) {
479                                     FlushStrBuf(Ret); 
480                                     return -1; 
481                             }
482                             StrBuf_ServGetBLOBBuffered(Ret, thisblock);
483                             bytes += thisblock;
484                     }
485                     else {
486                             lprintf(3, "Error: %s\n", ChrPtr(Buf) + 4);
487                             return -1;
488                     }
489                 }
490         }
491         return StrLength(Ret);
492 }
493
494
495 int ClientGetLine(ParsedHttpHdrs *Hdr, StrBuf *Target)
496 {
497         const char *Error;
498 #ifdef HAVE_OPENSSL
499         const char *pch, *pchs;
500         int rlen, len, retval = 0;
501
502         if (is_https) {
503                 int ntries = 0;
504                 if (StrLength(Hdr->ReadBuf) > 0) {
505                         pchs = ChrPtr(Hdr->ReadBuf);
506                         pch = strchr(pchs, '\n');
507                         if (pch != NULL) {
508                                 rlen = 0;
509                                 len = pch - pchs;
510                                 if (len > 0 && (*(pch - 1) == '\r') )
511                                         rlen ++;
512                                 StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
513                                 StrBufCutLeft(Hdr->ReadBuf, len + 1);
514                                 return len - rlen;
515                         }
516                 }
517
518                 while (retval == 0) { 
519                                 pch = NULL;
520                                 pchs = ChrPtr(Hdr->ReadBuf);
521                                 if (*pchs != '\0')
522                                         pch = strchr(pchs, '\n');
523                                 if (pch == NULL) {
524                                         retval = client_read_sslbuffer(Hdr->ReadBuf, SLEEPING);
525                                         pchs = ChrPtr(Hdr->ReadBuf);
526                                         pch = strchr(pchs, '\n');
527                                 }
528                                 if (retval == 0) {
529                                         sleeeeeeeeeep(1);
530                                         ntries ++;
531                                 }
532                                 if (ntries > 10)
533                                         return 0;
534                 }
535                 if ((retval > 0) && (pch != NULL)) {
536                         rlen = 0;
537                         len = pch - pchs;
538                         if (len > 0 && (*(pch - 1) == '\r') )
539                                 rlen ++;
540                         StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
541                         StrBufCutLeft(Hdr->ReadBuf, len + 1);
542                         return len - rlen;
543
544                 }
545                 else 
546                         return -1;
547         }
548         else 
549 #endif
550                 return StrBufTCP_read_buffered_line_fast(Target, 
551                                                          Hdr->ReadBuf,
552                                                          &Hdr->Pos,
553                                                          &Hdr->http_sock,
554                                                          5,
555                                                          1,
556                                                          &Error);
557 }
558
559
560 /* 
561  * This is a generic function to set up a master socket for listening on
562  * a TCP port.  The server shuts down if the bind fails.  (IPv4/IPv6 version)
563  *
564  * ip_addr      IP address to bind
565  * port_number  port number to bind
566  * queue_len    number of incoming connections to allow in the queue
567  */
568 int webcit_tcp_server(char *ip_addr, int port_number, int queue_len)
569 {
570         struct protoent *p;
571         struct sockaddr_in6 sin6;
572         struct sockaddr_in sin4;
573         int s, i, b;
574         int ip_version = 6;
575
576         memset(&sin6, 0, sizeof(sin6));
577         memset(&sin4, 0, sizeof(sin4));
578         sin6.sin6_family = AF_INET6;
579         sin4.sin_family = AF_INET;
580
581         if (    (ip_addr == NULL)                                                       /* any IPv6 */
582                 || (IsEmptyStr(ip_addr))
583                 || (!strcmp(ip_addr, "*"))
584         ) {
585                 ip_version = 6;
586                 sin6.sin6_addr = in6addr_any;
587         }
588         else if (!strcmp(ip_addr, "0.0.0.0"))                                           /* any IPv4 */
589         {
590                 ip_version = 4;
591                 sin4.sin_addr.s_addr = INADDR_ANY;
592         }
593         else if ((strchr(ip_addr, '.')) && (!strchr(ip_addr, ':')))                     /* specific IPv4 */
594         {
595                 ip_version = 4;
596                 if (inet_pton(AF_INET, ip_addr, &sin4.sin_addr) <= 0) {
597                         lprintf(1, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
598                         return (-WC_EXIT_BIND);
599                 }
600         }
601         else                                                                            /* specific IPv6 */
602         {
603                 ip_version = 6;
604                 if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) {
605                         lprintf(1, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
606                         return (-WC_EXIT_BIND);
607                 }
608         }
609
610         if (port_number == 0) {
611                 lprintf(1, "Cannot start: no port number specified.\n");
612                 return (-WC_EXIT_BIND);
613         }
614         sin6.sin6_port = htons((u_short) port_number);
615         sin4.sin_port = htons((u_short) port_number);
616
617         p = getprotobyname("tcp");
618
619         s = socket( ((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto));
620         if (s < 0) {
621                 lprintf(1, "Can't create a listening socket: %s\n", strerror(errno));
622                 return (-WC_EXIT_BIND);
623         }
624         /* Set some socket options that make sense. */
625         i = 1;
626         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
627
628         if (ip_version == 6) {
629                 b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
630         }
631         else {
632                 b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4));
633         }
634
635         if (b < 0) {
636                 lprintf(1, "Can't bind: %s\n", strerror(errno));
637                 return (-WC_EXIT_BIND);
638         }
639
640         if (listen(s, queue_len) < 0) {
641                 lprintf(1, "Can't listen: %s\n", strerror(errno));
642                 return (-WC_EXIT_BIND);
643         }
644         return (s);
645 }
646
647
648 /*
649  * Create a Unix domain socket and listen on it
650  * sockpath - file name of the unix domain socket
651  * queue_len - Number of incoming connections to allow in the queue
652  */
653 int webcit_uds_server(char *sockpath, int queue_len)
654 {
655         struct sockaddr_un addr;
656         int s;
657         int i;
658         int actual_queue_len;
659
660         actual_queue_len = queue_len;
661         if (actual_queue_len < 5) actual_queue_len = 5;
662
663         i = unlink(sockpath);
664         if ((i != 0) && (errno != ENOENT)) {
665                 lprintf(1, "webcit: can't unlink %s: %s\n",
666                         sockpath, strerror(errno));
667                 return (-WC_EXIT_BIND);
668         }
669
670         memset(&addr, 0, sizeof(addr));
671         addr.sun_family = AF_UNIX;
672         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
673
674         s = socket(AF_UNIX, SOCK_STREAM, 0);
675         if (s < 0) {
676                 lprintf(1, "webcit: Can't create a unix domain socket: %s\n", strerror(errno));
677                 return (-WC_EXIT_BIND);
678         }
679
680         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
681                 lprintf(1, "webcit: Can't bind: %s\n",
682                         strerror(errno));
683                 return (-WC_EXIT_BIND);
684         }
685
686         if (listen(s, actual_queue_len) < 0) {
687                 lprintf(1, "webcit: Can't listen: %s\n",
688                         strerror(errno));
689                 return (-WC_EXIT_BIND);
690         }
691
692         chmod(sockpath, 0777);
693         return(s);
694 }
695
696
697
698
699 /*
700  * Read data from the client socket.
701  *
702  * sock         socket fd to read from
703  * buf          buffer to read into 
704  * bytes        number of bytes to read
705  * timeout      Number of seconds to wait before timing out
706  *
707  * Possible return values:
708  *      1       Requested number of bytes has been read.
709  *      0       Request timed out.
710  *      -1      Connection is broken, or other error.
711  */
712 int client_read_to(ParsedHttpHdrs *Hdr, StrBuf *Target, int bytes, int timeout)
713 {
714         const char *Error;
715         int retval = 0;
716
717 #ifdef HAVE_OPENSSL
718         if (is_https) {
719                 long bufremain;
720                 long baselen;
721
722                 baselen = StrLength(Target);
723
724                 if (Hdr->Pos == NULL)
725                         Hdr->Pos = ChrPtr(Hdr->ReadBuf);
726                 bufremain = StrLength(Hdr->ReadBuf) - (Hdr->Pos - ChrPtr(Hdr->ReadBuf));
727
728                 if (bytes < bufremain)
729                         bufremain = bytes;
730                 StrBufAppendBufPlain(Target, Hdr->Pos, bufremain, 0);
731                 StrBufCutLeft(Hdr->ReadBuf, bufremain);
732
733                 if (bytes > bufremain) 
734                 {
735                         while ((StrLength(Hdr->ReadBuf) + StrLength(Target) < bytes + baselen) &&
736                                (retval >= 0))
737                                 retval = client_read_sslbuffer(Hdr->ReadBuf, timeout);
738                         if (retval >= 0) {
739                                 StrBufAppendBuf(Target, Hdr->ReadBuf, 0); /* todo: Buf > bytes? */
740 #ifdef HTTP_TRACING
741                                 write(2, "\033[32m", 5);
742                                 write(2, buf, bytes);
743                                 write(2, "\033[30m", 5);
744 #endif
745                                 return 1;
746                         }
747                         else {
748                                 lprintf(2, "client_read_ssl() failed\n");
749                                 return -1;
750                         }
751                 }
752                 else 
753                         return 1;
754         }
755 #endif
756
757         retval = StrBufReadBLOBBuffered(Target, 
758                                         Hdr->ReadBuf, 
759                                         &Hdr->Pos, 
760                                         &Hdr->http_sock, 
761                                         1, 
762                                         bytes,
763                                         O_TERM,
764                                         &Error);
765         if (retval < 0) {
766                 lprintf(2, "client_read() failed: %s\n",
767                         Error);
768                 wc_backtrace();
769                 return retval;
770         }
771
772 #ifdef HTTP_TRACING
773         write(2, "\033[32m", 5);
774         write(2, buf, bytes);
775         write(2, "\033[30m", 5);
776 #endif
777         return 1;
778 }
779
780
781 /*
782  * Begin buffering HTTP output so we can transmit it all in one write operation later.
783  */
784 void begin_burst(void)
785 {
786         if (WC->WBuf == NULL) {
787                 WC->WBuf = NewStrBufPlain(NULL, 32768);
788         }
789 }
790
791
792 /*
793  * Finish buffering HTTP output.  [Compress using zlib and] output with a Content-Length: header.
794  */
795 long end_burst(void)
796 {
797         wcsession *WCC = WC;
798         const char *ptr, *eptr;
799         long count;
800         ssize_t res = 0;
801         fd_set wset;
802         int fdflags;
803
804         if (!DisableGzip && (WCC->Hdr->HR.gzip_ok))
805         {
806                 if (CompressBuffer(WCC->WBuf) > 0)
807                         hprintf("Content-encoding: gzip\r\n");
808                 else {
809                         lprintf(CTDL_ALERT, "Compression failed: %d [%s] sending uncompressed\n", errno, strerror(errno));
810                         wc_backtrace();
811                 }
812         }
813
814         if (WCC->WFBuf != NULL) {
815                 WildFireSerializePayload(WCC->WFBuf, WCC->HBuf, &WCC->Hdr->nWildfireHeaders, NULL);
816                 FreeStrBuf(&WCC->WFBuf);
817         }
818
819         if (WCC->Hdr->HR.prohibit_caching)
820                 hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n");
821         hprintf("Content-length: %d\r\n\r\n", StrLength(WCC->WBuf));
822
823         ptr = ChrPtr(WCC->HBuf);
824         count = StrLength(WCC->HBuf);
825         eptr = ptr + count;
826
827 #ifdef HAVE_OPENSSL
828         if (is_https) {
829                 client_write_ssl(WCC->HBuf);
830                 client_write_ssl(WCC->WBuf);
831                 return (count);
832         }
833 #endif
834
835         
836 #ifdef HTTP_TRACING
837         
838         write(2, "\033[34m", 5);
839         write(2, ptr, StrLength(WCC->WBuf));
840         write(2, "\033[30m", 5);
841 #endif
842         if (WCC->Hdr->http_sock == -1)
843                 return -1;
844         fdflags = fcntl(WC->Hdr->http_sock, F_GETFL);
845
846         while ((ptr < eptr) && (WCC->Hdr->http_sock != -1)){
847                 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
848                         FD_ZERO(&wset);
849                         FD_SET(WCC->Hdr->http_sock, &wset);
850                         if (select(WCC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
851                                 lprintf(2, "client_write: Socket select failed (%s)\n", strerror(errno));
852                                 return -1;
853                         }
854                 }
855
856                 if ((WCC->Hdr->http_sock == -1) || 
857                     (res = write(WCC->Hdr->http_sock, 
858                                  ptr,
859                                  count)) == -1) {
860                         lprintf(2, "client_write: Socket write failed (%s)\n", strerror(errno));
861                         wc_backtrace();
862                         return res;
863                 }
864                 count -= res;
865                 ptr += res;
866         }
867
868         ptr = ChrPtr(WCC->WBuf);
869         count = StrLength(WCC->WBuf);
870         eptr = ptr + count;
871
872 #ifdef HTTP_TRACING
873         
874         write(2, "\033[34m", 5);
875         write(2, ptr, StrLength(WCC->WBuf));
876         write(2, "\033[30m", 5);
877 #endif
878
879         while ((ptr < eptr) && (WCC->Hdr->http_sock != -1)) {
880                 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
881                         FD_ZERO(&wset);
882                         FD_SET(WCC->Hdr->http_sock, &wset);
883                         if (select(WCC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
884                                 lprintf(2, "client_write: Socket select failed (%s)\n", strerror(errno));
885                                 return -1;
886                         }
887                 }
888
889                 if ((WCC->Hdr->http_sock == -1) || 
890                     (res = write(WCC->Hdr->http_sock, 
891                                  ptr,
892                                  count)) == -1) {
893                         lprintf(2, "client_write: Socket write failed (%s)\n", strerror(errno));
894                         wc_backtrace();
895                         return res;
896                 }
897                 count -= res;
898                 ptr += res;
899         }
900
901         return StrLength(WCC->WBuf);
902 }
903
904
905 /*
906  * lingering_close() a`la Apache. see
907  * http://www.apache.org/docs/misc/fin_wait_2.html for rationale
908  */
909 int lingering_close(int fd)
910 {
911         char buf[SIZ];
912         int i;
913         fd_set set;
914         struct timeval tv, start;
915
916         gettimeofday(&start, NULL);
917         if (fd == -1)
918                 return -1;
919         shutdown(fd, 1);
920         do {
921                 do {
922                         gettimeofday(&tv, NULL);
923                         tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec);
924                         tv.tv_usec = start.tv_usec - tv.tv_usec;
925                         if (tv.tv_usec < 0) {
926                                 tv.tv_sec--;
927                                 tv.tv_usec += 1000000;
928                         }
929                         FD_ZERO(&set);
930                         FD_SET(fd, &set);
931                         i = select(fd + 1, &set, NULL, NULL, &tv);
932                 } while (i == -1 && errno == EINTR);
933
934                 if (i <= 0)
935                         break;
936
937                 i = read(fd, buf, sizeof buf);
938         } while (i != 0 && (i != -1 || errno == EINTR));
939
940         return close(fd);
941 }
942
943 void
944 HttpNewModule_TCPSOCKETS
945 (ParsedHttpHdrs *httpreq)
946 {
947
948         httpreq->ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
949 }
950
951 void
952 HttpDetachModule_TCPSOCKETS
953 (ParsedHttpHdrs *httpreq)
954 {
955
956         FlushStrBuf(httpreq->ReadBuf);
957         ReAdjustEmptyBuf(httpreq->ReadBuf, 4 * SIZ, SIZ);
958 }
959
960 void
961 HttpDestroyModule_TCPSOCKETS
962 (ParsedHttpHdrs *httpreq)
963 {
964
965         FreeStrBuf(&httpreq->ReadBuf);
966 }
967
968
969 void
970 SessionNewModule_TCPSOCKETS
971 (wcsession *sess)
972 {
973         sess->CLineBuf = NewStrBuf();
974         sess->MigrateReadLineBuf = NewStrBuf();
975 }
976
977 void 
978 SessionDestroyModule_TCPSOCKETS
979 (wcsession *sess)
980 {
981         FreeStrBuf(&sess->CLineBuf);
982         FreeStrBuf(&sess->ReadBuf);
983         sess->ReadPos = NULL;
984         FreeStrBuf(&sess->MigrateReadLineBuf);
985         if (sess->serv_sock > 0)
986                 close(sess->serv_sock);
987 }