Done with doxygenizing
[citadel.git] / webcit / webserver.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup Webserver This contains a simple multithreaded TCP server manager.  It sits around
6  * waiting on the specified port for incoming HTTP connections.  When a
7  * connection is established, it calls context_loop() from context_loop.c.
8  *
9  */
10
11 /*@{*/
12 #include "webserver.h"
13
14 #ifndef HAVE_SNPRINTF
15 int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp);
16 #endif
17
18 int verbosity = 9;              /**< Logging level */
19 int msock;                          /**< master listening socket */
20 int is_https = 0;               /**< Nonzero if I am an HTTPS service */
21 int follow_xff = 0;             /**< Follow X-Forwarded-For: header */
22 extern void *context_loop(int);
23 extern void *housekeeping_loop(void);
24 extern pthread_mutex_t SessionListMutex;
25 extern pthread_key_t MyConKey;
26
27
28 char *server_cookie = NULL; /**< our Cookie connection to the client */
29
30
31 char *ctdlhost = DEFAULT_HOST; /**< our name */
32 char *ctdlport = DEFAULT_PORT; /**< our Port */
33 int setup_wizard = 0;          /**< should we run the setup wizard? \todo */
34 char wizard_filename[PATH_MAX];/**< where's the setup wizard? */
35
36 /** 
37  * \brief This is a generic function to set up a master socket for listening on
38  * a TCP port.  The server shuts down if the bind fails.
39  * \param ip_addr ip to bind to
40  * \param port_number the port to bind to 
41  * \param queue_len the size of the input queue ????
42  */
43 int ig_tcp_server(char *ip_addr, int port_number, int queue_len)
44 {
45         struct sockaddr_in sin;
46         int s, i;
47
48         memset(&sin, 0, sizeof(sin));
49         sin.sin_family = AF_INET;
50         if (ip_addr == NULL) {
51                 sin.sin_addr.s_addr = INADDR_ANY;
52         } else {
53                 sin.sin_addr.s_addr = inet_addr(ip_addr);
54         }
55
56         if (sin.sin_addr.s_addr == INADDR_NONE) {
57                 sin.sin_addr.s_addr = INADDR_ANY;
58         }
59
60         if (port_number == 0) {
61                 lprintf(1, "Cannot start: no port number specified.\n");
62                 exit(1);
63         }
64         sin.sin_port = htons((u_short) port_number);
65
66         s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto));
67         if (s < 0) {
68                 lprintf(1, "Can't create a socket: %s\n", strerror(errno));
69                 exit(errno);
70         }
71         /** Set some socket options that make sense. */
72         i = 1;
73         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
74
75         if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
76                 lprintf(1, "Can't bind: %s\n", strerror(errno));
77                 exit(errno);
78         }
79         if (listen(s, queue_len) < 0) {
80                 lprintf(1, "Can't listen: %s\n", strerror(errno));
81                 exit(errno);
82         }
83         return (s);
84 }
85
86
87
88 /**
89  * \brief Create a Unix domain socket and listen on it
90  * \param sockpath file name of the unix domain socket
91  * \param queue_len queue size of the kernel fifo????
92  */
93 int ig_uds_server(char *sockpath, int queue_len)
94 {
95         struct sockaddr_un addr;
96         int s;
97         int i;
98         int actual_queue_len;
99
100         actual_queue_len = queue_len;
101         if (actual_queue_len < 5) actual_queue_len = 5;
102
103         i = unlink(sockpath);
104         if (i != 0) if (errno != ENOENT) {
105                 lprintf(1, "citserver: can't unlink %s: %s\n",
106                         sockpath, strerror(errno));
107                 exit(errno);
108         }
109
110         memset(&addr, 0, sizeof(addr));
111         addr.sun_family = AF_UNIX;
112         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
113
114         s = socket(AF_UNIX, SOCK_STREAM, 0);
115         if (s < 0) {
116                 lprintf(1, "citserver: Can't create a socket: %s\n",
117                         strerror(errno));
118                 exit(errno);
119         }
120
121         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
122                 lprintf(1, "citserver: Can't bind: %s\n",
123                         strerror(errno));
124                 exit(errno);
125         }
126
127         if (listen(s, actual_queue_len) < 0) {
128                 lprintf(1, "citserver: Can't listen: %s\n",
129                         strerror(errno));
130                 exit(errno);
131         }
132
133         chmod(sockpath, 0777);
134         return(s);
135 }
136
137
138
139
140 /**
141  * \brief Read data from the client socket.
142  * \param sock socket fd to read from ???
143  * \param buf buffer to read into 
144  * \param bytes how large is the read buffer?
145  * \param timeout how long should we wait for input?
146  * \return values are\
147  *      1       Requested number of bytes has been read.\
148  *      0       Request timed out.\
149  *         -1           Connection is broken, or other error.
150  */
151 int client_read_to(int sock, char *buf, int bytes, int timeout)
152 {
153         int len, rlen;
154         fd_set rfds;
155         struct timeval tv;
156         int retval;
157
158
159 #ifdef HAVE_OPENSSL
160         if (is_https) {
161                 return (client_read_ssl(buf, bytes, timeout));
162         }
163 #endif
164
165         len = 0;
166         while (len < bytes) {
167                 FD_ZERO(&rfds);
168                 FD_SET(sock, &rfds);
169                 tv.tv_sec = timeout;
170                 tv.tv_usec = 0;
171
172                 retval = select((sock) + 1, &rfds, NULL, NULL, &tv);
173                 if (FD_ISSET(sock, &rfds) == 0) {
174                         return (0);
175                 }
176
177                 rlen = read(sock, &buf[len], bytes - len);
178
179                 if (rlen < 1) {
180                         lprintf(2, "client_read() failed: %s\n",
181                                 strerror(errno));
182                         return (-1);
183                 }
184                 len = len + rlen;
185         }
186
187 #ifdef HTTP_TRACING
188         write(2, "\033[32m", 5);
189         write(2, buf, bytes);
190         write(2, "\033[30m", 5);
191 #endif
192         return (1);
193 }
194
195 /**
196  * \brief write data to the client
197  * \param buf data to write to the client
198  * \param count size of buffer
199  */
200 ssize_t client_write(const void *buf, size_t count)
201 {
202
203         if (WC->burst != NULL) {
204                 WC->burst =
205                     realloc(WC->burst, (WC->burst_len + count + 2));
206                 memcpy(&WC->burst[WC->burst_len], buf, count);
207                 WC->burst_len += count;
208                 return (count);
209         }
210 #ifdef HAVE_OPENSSL
211         if (is_https) {
212                 client_write_ssl((char *) buf, count);
213                 return (count);
214         }
215 #endif
216 #ifdef HTTP_TRACING
217         write(2, "\033[34m", 5);
218         write(2, buf, count);
219         write(2, "\033[30m", 5);
220 #endif
221         return (write(WC->http_sock, buf, count));
222 }
223
224 /**
225  * \brief what burst???
226  */
227 void begin_burst(void)
228 {
229         if (WC->burst != NULL) {
230                 free(WC->burst);
231                 WC->burst = NULL;
232         }
233         WC->burst_len = 0;
234         WC->burst = malloc(SIZ);
235 }
236
237
238 /**
239  * \brief uses the same calling syntax as compress2(), but it
240  * creates a stream compatible with HTTP "Content-encoding: gzip"
241  */
242 #ifdef HAVE_ZLIB
243 #define DEF_MEM_LEVEL 8 /**< memlevel??? */
244 #define OS_CODE 0x03    /**< unix */
245 int ZEXPORT compress_gzip(Bytef * dest,         /**< compressed buffer*/
246                                                   uLongf * destLen,     /**< length of the compresed data */
247                                                   const Bytef * source, /**< source to encode */
248                                                   uLong sourceLen,      /**< length of the source to encode */
249                                                   int level)            /**< what level??? */
250 {
251         const int gz_magic[2] = { 0x1f, 0x8b }; /** gzip magic header */
252
253         /** write gzip header */
254         sprintf((char *) dest, "%c%c%c%c%c%c%c%c%c%c",
255                 gz_magic[0], gz_magic[1], Z_DEFLATED,
256                 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /** xflags */ ,
257                 OS_CODE);
258
259         /* normal deflate */
260         z_stream stream;
261         int err;
262         stream.next_in = (Bytef *) source;
263         stream.avail_in = (uInt) sourceLen;
264         stream.next_out = dest + 10L;   // after header
265         stream.avail_out = (uInt) * destLen;
266         if ((uLong) stream.avail_out != *destLen)
267                 return Z_BUF_ERROR;
268
269         stream.zalloc = (alloc_func) 0;
270         stream.zfree = (free_func) 0;
271         stream.opaque = (voidpf) 0;
272
273         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
274                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
275         if (err != Z_OK)
276                 return err;
277
278         err = deflate(&stream, Z_FINISH);
279         if (err != Z_STREAM_END) {
280                 deflateEnd(&stream);
281                 return err == Z_OK ? Z_BUF_ERROR : err;
282         }
283         *destLen = stream.total_out + 10L;
284
285         /* write CRC and Length */
286         uLong crc = crc32(0L, source, sourceLen);
287         int n;
288         for (n = 0; n < 4; ++n, ++*destLen) {
289                 dest[*destLen] = (int) (crc & 0xff);
290                 crc >>= 8;
291         }
292         uLong len = stream.total_in;
293         for (n = 0; n < 4; ++n, ++*destLen) {
294                 dest[*destLen] = (int) (len & 0xff);
295                 len >>= 8;
296         }
297         err = deflateEnd(&stream);
298         return err;
299 }
300 #endif
301
302 /**
303  * \brief what burst???
304  */
305 void end_burst(void)
306 {
307         size_t the_len;
308         char *the_data;
309
310         if (WC->burst == NULL)
311                 return;
312
313         the_len = WC->burst_len;
314         the_data = WC->burst;
315
316         WC->burst_len = 0;
317         WC->burst = NULL;
318
319 #ifdef HAVE_ZLIB
320         /* Handle gzip compression */
321         if (WC->gzip_ok) {
322                 char *compressed_data = NULL;
323                 uLongf compressed_len;
324
325                 compressed_len = (uLongf) ((the_len * 101) / 100) + 100;
326                 compressed_data = malloc(compressed_len);
327
328                 if (compress_gzip((Bytef *) compressed_data,
329                                   &compressed_len,
330                                   (Bytef *) the_data,
331                                   (uLongf) the_len, Z_BEST_SPEED) == Z_OK) {
332                         wprintf("Content-encoding: gzip\r\n");
333                         free(the_data);
334                         the_data = compressed_data;
335                         the_len = compressed_len;
336                 } else {
337                         free(compressed_data);
338                 }
339         }
340 #endif                          /* HAVE_ZLIB */
341
342         wprintf("Content-length: %d\r\n\r\n", the_len);
343         client_write(the_data, the_len);
344         free(the_data);
345         return;
346 }
347
348
349
350 /**
351  * \brief Read data from the client socket with default timeout.
352  * (This is implemented in terms of client_read_to() and could be
353  * justifiably moved out of sysdep.c)
354  * \param sock the socket fd to read from???
355  * \param buf the buffer to write to
356  * \param bytes how large is the buffer
357  */
358 int client_read(int sock, char *buf, int bytes)
359 {
360         return (client_read_to(sock, buf, bytes, SLEEPING));
361 }
362
363
364 /**
365  * \brief Get a LF-terminated line of text from the client.
366  * (This is implemented in terms of client_read() and could be
367  * justifiably moved out of sysdep.c)
368  * \param sock socket fd to get client line from???
369  * \param buf buffer to write read data to
370  * \param bufsiz how many bytes to read
371  * \return  numer of bytes read???
372  */
373 int client_getln(int sock, char *buf, int bufsiz)
374 {
375         int i, retval;
376
377         /** Read one character at a time.*/
378         for (i = 0;; i++) {
379                 retval = client_read(sock, &buf[i], 1);
380                 if (retval != 1 || buf[i] == '\n' || i == (bufsiz-1))
381                         break;
382         }
383
384         /** If we got a long line, discard characters until the newline.         */
385         if (i == (bufsiz-1))
386                 while (buf[i] != '\n' && retval == 1)
387                         retval = client_read(sock, &buf[i], 1);
388
389         /**
390          * Strip any trailing non-printable characters.
391          */
392         buf[i] = 0;
393         while ((strlen(buf) > 0) && (!isprint(buf[strlen(buf) - 1]))) {
394                 buf[strlen(buf) - 1] = 0;
395         }
396         return (retval);
397 }
398
399
400 /**
401  * \brief Start running as a daemon.  
402  * param do_close_stdio Only close stdio if set.
403  */
404 void start_daemon(int do_close_stdio)
405 {
406         if (do_close_stdio) {
407                 /* close(0); */
408                 close(1);
409                 close(2);
410         }
411         signal(SIGHUP, SIG_IGN);
412         signal(SIGINT, SIG_IGN);
413         signal(SIGQUIT, SIG_IGN);
414         if (fork() != 0)
415                 exit(0);
416 }
417
418 void spawn_another_worker_thread()
419 {
420         pthread_t SessThread;   /**< Thread descriptor */
421         pthread_attr_t attr;    /**< Thread attributes */
422         int ret;
423
424         lprintf(3, "Creating a new thread\n");
425
426         /** set attributes for the new thread */
427         pthread_attr_init(&attr);
428         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
429
430         /**
431          * Our per-thread stacks need to be bigger than the default size, otherwise
432          * the MIME parser crashes on FreeBSD, and the IMAP service crashes on
433          * 64-bit Linux.
434          */
435         if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) {
436                 lprintf(1, "pthread_attr_setstacksize: %s\n",
437                         strerror(ret));
438                 pthread_attr_destroy(&attr);
439         }
440
441         /** now create the thread */
442         if (pthread_create(&SessThread, &attr,
443                            (void *(*)(void *)) worker_entry, NULL)
444             != 0) {
445                 lprintf(1, "Can't create thread: %s\n", strerror(errno));
446         }
447
448         /** free up the attributes */
449         pthread_attr_destroy(&attr);
450 }
451
452 /**
453  * \brief Here's where it all begins.
454  * \param argc number of commandline args
455  * \param argv the commandline arguments
456  */
457 int main(int argc, char **argv)
458 {
459         pthread_t SessThread;   /**< Thread descriptor */
460         pthread_attr_t attr;    /**< Thread attributes */
461         int a, i;                       /**< General-purpose variables */
462         int port = PORT_NUM;    /**< Port to listen on */
463         char tracefile[PATH_MAX];
464         char ip_addr[256];
465         char *webcitdir = WEBCITDIR;
466 #ifdef ENABLE_NLS
467         char *locale = NULL;
468         char *mo = NULL;
469 #endif /* ENABLE_NLS */
470         char uds_listen_path[PATH_MAX]; /**< listen on a unix domain socket? */
471
472         strcpy(uds_listen_path, "");
473
474         /** Parse command line */
475 #ifdef HAVE_OPENSSL
476         while ((a = getopt(argc, argv, "h:i:p:t:x:cfs")) != EOF)
477 #else
478         while ((a = getopt(argc, argv, "h:i:p:t:x:cf")) != EOF)
479 #endif
480                 switch (a) {
481                 case 'h':
482                         webcitdir = strdup(optarg);
483                         break;
484                 case 'i':
485                         safestrncpy(ip_addr, optarg, sizeof ip_addr);
486                         break;
487                 case 'p':
488                         port = atoi(optarg);
489                         if (port == 0) {
490                                 safestrncpy(uds_listen_path, optarg, sizeof uds_listen_path);
491                         }
492                         break;
493                 case 't':
494                         safestrncpy(tracefile, optarg, sizeof tracefile);
495                         freopen(tracefile, "w", stdout);
496                         freopen(tracefile, "w", stderr);
497                         freopen(tracefile, "r", stdin);
498                         break;
499                 case 'x':
500                         verbosity = atoi(optarg);
501                         break;
502                 case 'f':
503                         follow_xff = 1;
504                         break;
505                 case 'c':
506                         server_cookie = malloc(256);
507                         if (server_cookie != NULL) {
508                                 safestrncpy(server_cookie,
509                                        "Set-cookie: wcserver=",
510                                         256);
511                                 if (gethostname
512                                     (&server_cookie[strlen(server_cookie)],
513                                      200) != 0) {
514                                         lprintf(2, "gethostname: %s\n",
515                                                 strerror(errno));
516                                         free(server_cookie);
517                                 }
518                         }
519                         break;
520                 case 's':
521                         is_https = 1;
522                         break;
523                 default:
524                         fprintf(stderr, "usage: webserver "
525                                 "[-i ip_addr] [-p http_port] "
526                                 "[-t tracefile] [-c] [-f] "
527 #ifdef HAVE_OPENSSL
528                                 "[-s] "
529 #endif
530                                 "[remotehost [remoteport]]\n");
531                         return 1;
532                 }
533
534         if (optind < argc) {
535                 ctdlhost = argv[optind];
536                 if (++optind < argc)
537                         ctdlport = argv[optind];
538         }
539         /** Tell 'em who's in da house */
540         lprintf(1, SERVER "\n");
541         lprintf(1, "Copyright (C) 1996-2005 by the Citadel development team.\n"
542                 "This software is distributed under the terms of the "
543                 "GNU General Public License.\n\n"
544         );
545
546         lprintf(9, "Changing directory to %s\n", webcitdir);
547         if (chdir(webcitdir) != 0) {
548                 perror("chdir");
549         }
550
551         /** initialize the International Bright Young Thing */
552 #ifdef ENABLE_NLS
553
554         initialize_locales();
555
556         locale = setlocale(LC_ALL, "");
557
558         mo = malloc(strlen(webcitdir) + 20);
559         sprintf(mo, "%s/locale", webcitdir);
560         lprintf(9, "Message catalog directory: %s\n",
561                 bindtextdomain("webcit", mo)
562         );
563         free(mo);
564         lprintf(9, "Text domain: %s\n",
565                 textdomain("webcit")
566         );
567         lprintf(9, "Text domain Charset: %s\n",
568                         bind_textdomain_codeset("webcit","UTF8")
569         );
570 #endif
571
572         initialize_viewdefs();
573         initialize_axdefs();
574
575         /**
576          * Set up a place to put thread-specific data.
577          * We only need a single pointer per thread - it points to the
578          * wcsession struct to which the thread is currently bound.
579          */
580         if (pthread_key_create(&MyConKey, NULL) != 0) {
581                 lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
582         }
583
584         /**
585          * Set up a place to put thread-specific SSL data.
586          * We don't stick this in the wcsession struct because SSL starts
587          * up before the session is bound, and it gets torn down between
588          * transactions.
589          */
590 #ifdef HAVE_OPENSSL
591         if (pthread_key_create(&ThreadSSL, NULL) != 0) {
592                 lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
593         }
594 #endif
595
596         /**
597          * Bind the server to our favorite port.
598          * There is no need to check for errors, because ig_tcp_server()
599          * exits if it doesn't succeed.
600          */
601
602         if (strlen(uds_listen_path) > 0) {
603                 lprintf(2, "Attempting to create listener socket at %s...\n", uds_listen_path);
604                 msock = ig_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH);
605         }
606         else {
607                 lprintf(2, "Attempting to bind to port %d...\n", port);
608                 msock = ig_tcp_server(ip_addr, port, LISTEN_QUEUE_LENGTH);
609         }
610
611         lprintf(2, "Listening on socket %d\n", msock);
612         signal(SIGPIPE, SIG_IGN);
613
614         pthread_mutex_init(&SessionListMutex, NULL);
615
616         /**
617          * Start up the housekeeping thread
618          */
619         pthread_attr_init(&attr);
620         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
621         pthread_create(&SessThread, &attr,
622                        (void *(*)(void *)) housekeeping_loop, NULL);
623
624
625         /**
626          * If this is an HTTPS server, fire up SSL
627          */
628 #ifdef HAVE_OPENSSL
629         if (is_https) {
630                 init_ssl();
631         }
632 #endif
633
634         /** Start a few initial worker threads */
635         for (i = 0; i < (MIN_WORKER_THREADS); ++i) {
636                 spawn_another_worker_thread();
637         }
638
639         /* now the original thread becomes another worker */
640         worker_entry();
641         return 0;
642 }
643
644
645 /**
646  * Entry point for worker threads
647  */
648 void worker_entry(void)
649 {
650         int ssock;
651         int i = 0;
652         int time_to_die = 0;
653         int fail_this_transaction = 0;
654
655         do {
656                 /** Only one thread can accept at a time */
657                 fail_this_transaction = 0;
658                 ssock = accept(msock, NULL, 0);
659                 if (ssock < 0) {
660                         lprintf(2, "accept() failed: %s\n",
661                                 strerror(errno));
662                 } else {
663                         /** Set the SO_REUSEADDR socket option */
664                         i = 1;
665                         setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR,
666                                    &i, sizeof(i));
667
668                         /** If we are an HTTPS server, go crypto now. */
669 #ifdef HAVE_OPENSSL
670                         if (is_https) {
671                                 if (starttls(ssock) != 0) {
672                                         fail_this_transaction = 1;
673                                         close(ssock);
674                                 }
675                         }
676 #endif
677
678                         if (fail_this_transaction == 0) {
679                                 /** Perform an HTTP transaction... */
680                                 context_loop(ssock);
681                                 /** ...and close the socket. */
682                                 lingering_close(ssock);
683                         }
684
685                 }
686
687         } while (!time_to_die);
688
689         pthread_exit(NULL);
690 }
691
692 /**
693  * \brief logprintf. log messages 
694  * logs to stderr if loglevel is lower than the verbosity set at startup
695  * \param loglevel level of the message
696  * \param format the printf like format string
697  * \param ... the strings to put into format
698  */
699 int lprintf(int loglevel, const char *format, ...)
700 {
701         va_list ap;
702
703         if (loglevel <= verbosity) {
704                 va_start(ap, format);
705                 vfprintf(stderr, format, ap);
706                 va_end(ap);
707                 fflush(stderr);
708         }
709         return 1;
710 }
711
712
713 /*@}*/