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