* do linebuffered/non-blocking reads from http requests
[citadel.git] / webcit / webserver.c
1 /*
2  * $Id$
3  *
4  * This contains a simple multithreaded TCP server manager.  It sits around
5  * waiting on the specified port for incoming HTTP connections.  When a
6  * connection is established, it calls context_loop() from context_loop.c.
7  *
8  * Copyright (c) 1996-2008 by the citadel.org developers.
9  * This program is released under the terms of the GNU General Public License v3.
10  *
11  */
12
13 #include "webcit.h"
14 #include "webserver.h"
15
16 #if HAVE_BACKTRACE
17 #include <execinfo.h>
18 #endif
19 #include "modules_init.h"
20 #ifndef HAVE_SNPRINTF
21 int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp);
22 #endif
23
24 int verbosity = 9;              /* Logging level */
25 int msock;                      /* master listening socket */
26 int is_https = 0;               /* Nonzero if I am an HTTPS service */
27 int follow_xff = 0;             /* Follow X-Forwarded-For: header */
28 int home_specified = 0;         /* did the user specify a homedir? */
29 int time_to_die = 0;            /* Nonzero if server is shutting down */
30 extern void *context_loop(int*);
31 extern void *housekeeping_loop(void);
32 extern pthread_mutex_t SessionListMutex;
33 extern pthread_key_t MyConKey;
34
35
36 char ctdl_key_dir[PATH_MAX]=SSL_DIR;
37 char file_crpt_file_key[PATH_MAX]="";
38 char file_crpt_file_csr[PATH_MAX]="";
39 char file_crpt_file_cer[PATH_MAX]="";
40
41 char socket_dir[PATH_MAX];                      /* where to talk to our citadel server */
42 static const char editor_absolut_dir[PATH_MAX]=EDITORDIR;       /* nailed to what configure gives us. */
43 static char static_dir[PATH_MAX];               /* calculated on startup */
44 static char static_local_dir[PATH_MAX];         /* calculated on startup */
45 static char static_icon_dir[PATH_MAX];          /* where should we find our mime icons? */
46 char  *static_dirs[]={                          /* needs same sort order as the web mapping */
47         (char*)static_dir,                      /* our templates on disk */
48         (char*)static_local_dir,                /* user provided templates disk */
49         (char*)editor_absolut_dir,              /* the editor on disk */
50         (char*)static_icon_dir                  /* our icons... */
51 };
52
53 /*
54  * Subdirectories from which the client may request static content
55  *
56  * (If you add more, remember to increment 'ndirs' below)
57  */
58 char *static_content_dirs[] = {
59         "static",                     /* static templates */
60         "static.local",               /* site local static templates */
61         "tiny_mce"                    /* rich text editor */
62 };
63
64 int ndirs=3;
65
66
67 char *server_cookie = NULL;     /* our Cookie connection to the client */
68 int http_port = PORT_NUM;       /* Port to listen on */
69 char *ctdlhost = DEFAULT_HOST;  /* our name */
70 char *ctdlport = DEFAULT_PORT;  /* our Port */
71 int setup_wizard = 0;           /* should we run the setup wizard? \todo */
72 char wizard_filename[PATH_MAX]; /* where's the setup wizard? */
73 int running_as_daemon = 0;      /* should we deamonize on startup? */
74
75
76 /* 
77  * This is a generic function to set up a master socket for listening on
78  * a TCP port.  The server shuts down if the bind fails.
79  *
80  * ip_addr      IP address to bind
81  * port_number  port number to bind
82  * queue_len    number of incoming connections to allow in the queue
83  */
84 int ig_tcp_server(char *ip_addr, int port_number, int queue_len)
85 {
86         struct sockaddr_in sin;
87         int s, i;
88
89         memset(&sin, 0, sizeof(sin));
90         sin.sin_family = AF_INET;
91         if (ip_addr == NULL) {
92                 sin.sin_addr.s_addr = INADDR_ANY;
93         } else {
94                 sin.sin_addr.s_addr = inet_addr(ip_addr);
95         }
96
97         if (sin.sin_addr.s_addr == INADDR_NONE) {
98                 sin.sin_addr.s_addr = INADDR_ANY;
99         }
100
101         if (port_number == 0) {
102                 lprintf(1, "Cannot start: no port number specified.\n");
103                 exit(WC_EXIT_BIND);
104         }
105         sin.sin_port = htons((u_short) port_number);
106
107         s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto));
108         if (s < 0) {
109                 lprintf(1, "Can't create a socket: %s\n", strerror(errno));
110                 exit(WC_EXIT_BIND);
111         }
112         /* Set some socket options that make sense. */
113         i = 1;
114         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
115
116         fcntl(s, F_SETFL, O_NONBLOCK); /* maide: this statement is incorrect
117                                           there should be a preceding F_GETFL
118                                           and a bitwise OR with the previous
119                                           fd flags */
120         
121         if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
122                 lprintf(1, "Can't bind: %s\n", strerror(errno));
123                 exit(WC_EXIT_BIND);
124         }
125         if (listen(s, queue_len) < 0) {
126                 lprintf(1, "Can't listen: %s\n", strerror(errno));
127                 exit(WC_EXIT_BIND);
128         }
129         return (s);
130 }
131
132
133
134 /*
135  * Create a Unix domain socket and listen on it
136  * sockpath - file name of the unix domain socket
137  * queue_len - Number of incoming connections to allow in the queue
138  */
139 int ig_uds_server(char *sockpath, int queue_len)
140 {
141         struct sockaddr_un addr;
142         int s;
143         int i;
144         int actual_queue_len;
145
146         actual_queue_len = queue_len;
147         if (actual_queue_len < 5) actual_queue_len = 5;
148
149         i = unlink(sockpath);
150         if (i != 0) if (errno != ENOENT) {
151                 lprintf(1, "webcit: can't unlink %s: %s\n",
152                         sockpath, strerror(errno));
153                 exit(WC_EXIT_BIND);
154         }
155
156         memset(&addr, 0, sizeof(addr));
157         addr.sun_family = AF_UNIX;
158         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
159
160         s = socket(AF_UNIX, SOCK_STREAM, 0);
161         if (s < 0) {
162                 lprintf(1, "webcit: Can't create a socket: %s\n",
163                         strerror(errno));
164                 exit(WC_EXIT_BIND);
165         }
166
167         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
168                 lprintf(1, "webcit: Can't bind: %s\n",
169                         strerror(errno));
170                 exit(WC_EXIT_BIND);
171         }
172
173         if (listen(s, actual_queue_len) < 0) {
174                 lprintf(1, "webcit: Can't listen: %s\n",
175                         strerror(errno));
176                 exit(WC_EXIT_BIND);
177         }
178
179         chmod(sockpath, 0777);
180         return(s);
181 }
182
183
184
185
186 /*
187  * Read data from the client socket.
188  *
189  * sock         socket fd to read from
190  * buf          buffer to read into 
191  * bytes        number of bytes to read
192  * timeout      Number of seconds to wait before timing out
193  *
194  * Possible return values:
195  *      1       Requested number of bytes has been read.
196  *      0       Request timed out.
197  *      -1      Connection is broken, or other error.
198  */
199 int client_read_to(int *sock, StrBuf *Target, StrBuf *Buf, int bytes, int timeout)
200 {
201         const char *Error;
202         int retval = 0;
203
204 #ifdef HAVE_OPENSSL
205         if (is_https) {
206                 while ((retval >= 0) && (StrLength(Buf) < bytes))
207                         retval = client_read_sslbuffer(Buf, timeout);
208                 if (retval >= 0) {
209                         StrBufAppendBuf(Target, Buf, 0); /// todo: Buf > bytes?
210
211                 }
212                 else {
213                         lprintf(2, "client_read_ssl() failed\n");
214                         return -1;
215                 }
216         }
217 #endif
218
219         if (StrLength(Buf) > 0) {//// todo: what if Buf > bytes?
220                 StrBufAppendBuf(Target, Buf, 0);
221         }
222         retval = StrBufReadBLOB(Target, 
223                            sock, 
224                            (StrLength(Target) > 0), 
225                            bytes - StrLength(Target), 
226                                 &Error);
227         if (retval < 0) {
228                 lprintf(2, "client_read() failed: %s\n",
229                         Error);
230                 return retval;
231         }
232
233 #ifdef HTTP_TRACING
234         write(2, "\033[32m", 5);
235         write(2, buf, bytes);
236         write(2, "\033[30m", 5);
237 #endif
238         return (1);
239 }
240
241
242 /*
243  * Begin buffering HTTP output so we can transmit it all in one write operation later.
244  */
245 void begin_burst(void)
246 {
247         if (WC->WBuf == NULL)
248                 WC->WBuf = NewStrBufPlain(NULL, 32768);
249 }
250
251
252 /*
253  * Finish buffering HTTP output.  [Compress using zlib and] output with a Content-Length: header.
254  */
255 long end_burst(void)
256 {
257         struct wcsession *WCC = WC;
258         const char *ptr, *eptr;
259         long count;
260         ssize_t res;
261         fd_set wset;
262         int fdflags;
263
264 #ifdef HAVE_ZLIB
265         /* Perform gzip compression, if enabled and supported by client */
266         if ((WCC->gzip_ok) && CompressBuffer(WCC->WBuf))
267         {
268                 hprintf("Content-encoding: gzip\r\n");
269         }
270 #endif  /* HAVE_ZLIB */
271
272         hprintf("Content-length: %d\r\n\r\n", StrLength(WCC->WBuf));
273
274         ptr = ChrPtr(WCC->HBuf);
275         count = StrLength(WCC->HBuf);
276         eptr = ptr + count;
277
278 #ifdef HAVE_OPENSSL
279         if (is_https) {
280                 client_write_ssl(WCC->HBuf);
281                 client_write_ssl(WCC->WBuf);
282                 return (count);
283         }
284 #endif
285
286         
287 #ifdef HTTP_TRACING
288         
289         write(2, "\033[34m", 5);
290         write(2, ptr, StrLength(WCC->WBuf));
291         write(2, "\033[30m", 5);
292 #endif
293         fdflags = fcntl(WC->http_sock, F_GETFL);
294
295         while (ptr < eptr) {
296                 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
297                         FD_ZERO(&wset);
298                         FD_SET(WCC->http_sock, &wset);
299                         if (select(WCC->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
300                                 lprintf(2, "client_write: Socket select failed (%s)\n", strerror(errno));
301                                 return -1;
302                         }
303                 }
304
305                 if ((res = write(WCC->http_sock, 
306                                  ptr,
307                                  count)) == -1) {
308                         lprintf(2, "client_write: Socket write failed (%s)\n", strerror(errno));
309                         wc_backtrace();
310                         return res;
311                 }
312                 count -= res;
313                 ptr += res;
314         }
315
316         ptr = ChrPtr(WCC->WBuf);
317         count = StrLength(WCC->WBuf);
318         eptr = ptr + count;
319
320 #ifdef HTTP_TRACING
321         
322         write(2, "\033[34m", 5);
323         write(2, ptr, StrLength(WCC->WBuf));
324         write(2, "\033[30m", 5);
325 #endif
326
327         while (ptr < eptr) {
328                 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
329                         FD_ZERO(&wset);
330                         FD_SET(WCC->http_sock, &wset);
331                         if (select(WCC->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
332                                 lprintf(2, "client_write: Socket select failed (%s)\n", strerror(errno));
333                                 return -1;
334                         }
335                 }
336
337                 if ((res = write(WCC->http_sock, 
338                                  ptr,
339                                  count)) == -1) {
340                         lprintf(2, "client_write: Socket write failed (%s)\n", strerror(errno));
341                         wc_backtrace();
342                         return res;
343                 }
344                 count -= res;
345                 ptr += res;
346         }
347
348         return StrLength(WCC->WBuf);
349 }
350
351
352
353 /*
354  * Read data from the client socket with default timeout.
355  * (This is implemented in terms of client_read_to() and could be
356  * justifiably moved out of sysdep.c)
357  *
358  * sock         the socket fd to read from
359  * buf          the buffer to write to
360  * bytes        Number of bytes to read
361  */
362 int client_read(int *sock, StrBuf *Target, StrBuf *buf, int bytes)
363 {
364         return (client_read_to(sock, Target, buf, bytes, SLEEPING));
365 }
366
367
368 /*
369  * Get a LF-terminated line of text from the client.
370  * (This is implemented in terms of client_read() and could be
371  * justifiably moved out of sysdep.c)
372  *
373  * sock         socket fd to get client line from
374  * buf          buffer to write read data to
375  * bufsiz       how many bytes to read
376  *
377  * returns the number of bytes read
378  */
379 /////int client_getln(int *sock, char *buf, int bufsiz)
380 /////{
381 /////   int i, retval;
382 /////
383 /////   /* Read one character at a time.*/
384 /////   for (i = 0; *sock > 0; i++) {
385 /////           retval = client_read(sock, &buf[i], 1);
386 /////           if (retval < 0)
387 /////                   return retval;
388 /////           if (retval != 1 || buf[i] == '\n' || i == (bufsiz-1))
389 /////                   break;
390 /////           if ( (!isspace(buf[i])) && (!isprint(buf[i])) ) {
391 /////                   /* Non printable character recieved from client */
392 /////                   return(-1);
393 /////           }
394 /////   }
395 /////
396 /////   /* If we got a long line, discard characters until the newline. */
397 /////   if (i == (bufsiz-1))
398 /////           while (buf[i] != '\n' && retval == 1)
399 /////                   retval = client_read(sock, &buf[i], 1);
400 /////
401 /////   /*
402 /////    * Strip any trailing non-printable characters.
403 /////    */
404 /////   buf[i] = 0;
405 /////   while ((i > 0) && (!isprint(buf[i - 1]))) {
406 /////           buf[--i] = 0;
407 /////   }
408 /////   return (retval);
409 /////}
410
411 /*
412  * Shut us down the regular way.
413  * signum is the signal we want to forward
414  */
415 pid_t current_child;
416 void graceful_shutdown_watcher(int signum) {
417         lprintf (1, "bye; shutting down watcher.");
418         kill(current_child, signum);
419         if (signum != SIGHUP)
420                 exit(0);
421 }
422
423
424 int ClientGetLine(int *sock, StrBuf *Target, StrBuf *CLineBuf)
425 {
426         const char *Error, *pch, *pchs;
427         int rlen, len, retval = 0;
428
429         if (is_https) {
430                 if (StrLength(CLineBuf) > 0) {
431                         pchs = ChrPtr(CLineBuf);
432                         pch = strchr(pchs, '\n');
433                         if (pch != NULL) {
434                                 rlen = 0;
435                                 len = pch - pchs;
436                                 if (len > 0 && (*(pch - 1) == '\r') )
437                                         rlen ++;
438                                 StrBufSub(Target, CLineBuf, 0, len - rlen);
439                                 StrBufCutLeft(CLineBuf, len + 1);
440                                 return len - rlen;
441                         }
442                 }
443
444                 while ((retval >= 0) && 
445                        (pchs = ChrPtr(CLineBuf),
446                         pch = strchr(pchs, '\n'), 
447                         pch == NULL))
448                         retval = client_read_sslbuffer(CLineBuf, SLEEPING);
449                 if ((retval > 0) && (pch != NULL)) {
450                         rlen = 0;
451                         len = pch - pchs;
452                         if (len > 0 && (*(pch - 1) == '\r') )
453                                 rlen ++;
454                         StrBufSub(Target, CLineBuf, 0, len - rlen);
455                         StrBufCutLeft(CLineBuf, len + 1);
456                         return len - rlen;
457
458                 }
459                 else 
460                         return -1;
461         }
462         else 
463                 return StrBufTCP_read_buffered_line(Target, 
464                                                     CLineBuf,
465                                                     sock,
466                                                     5,
467                                                     1,
468                                                     &Error);
469 }
470
471
472
473 /*
474  * Shut us down the regular way.
475  * signum is the signal we want to forward
476  */
477 pid_t current_child;
478 void graceful_shutdown(int signum) {
479 //      kill(current_child, signum);
480         char wd[SIZ];
481         FILE *FD;
482         int fd;
483         getcwd(wd, SIZ);
484         lprintf (1, "bye going down gracefull.[%d][%s]\n", signum, wd);
485         fd = msock;
486         msock = -1;
487         time_to_die = 1;
488         FD=fdopen(fd, "a+");
489         fflush (FD);
490         fclose (FD);
491         close(fd);
492 }
493
494
495 /*
496  * Start running as a daemon.
497  */
498 void start_daemon(char *pid_file) 
499 {
500         int status = 0;
501         pid_t child = 0;
502         FILE *fp;
503         int do_restart = 0;
504
505         current_child = 0;
506
507         /* Close stdin/stdout/stderr and replace them with /dev/null.
508          * We don't just call close() because we don't want these fd's
509          * to be reused for other files.
510          */
511         chdir("/");
512
513         signal(SIGHUP, SIG_IGN);
514         signal(SIGINT, SIG_IGN);
515         signal(SIGQUIT, SIG_IGN);
516
517         child = fork();
518         if (child != 0) {
519                 exit(0);
520         }
521
522         setsid();
523         umask(0);
524         freopen("/dev/null", "r", stdin);
525         freopen("/dev/null", "w", stdout);
526         freopen("/dev/null", "w", stderr);
527         signal(SIGTERM, graceful_shutdown_watcher);
528         signal(SIGHUP, graceful_shutdown_watcher);
529
530         do {
531                 current_child = fork();
532
533         
534                 if (current_child < 0) {
535                         perror("fork");
536                         ShutDownLibCitadel ();
537                         exit(errno);
538                 }
539         
540                 else if (current_child == 0) {  // child process
541 //                      signal(SIGTERM, graceful_shutdown);
542                         signal(SIGHUP, graceful_shutdown);
543
544                         return; /* continue starting webcit. */
545                 }
546         
547                 else { // watcher process
548 //                      signal(SIGTERM, SIG_IGN);
549 //                      signal(SIGHUP, SIG_IGN);
550                         if (pid_file) {
551                                 fp = fopen(pid_file, "w");
552                                 if (fp != NULL) {
553                                         fprintf(fp, "%d\n", getpid());
554                                         fclose(fp);
555                                 }
556                         }
557                         waitpid(current_child, &status, 0);
558                 }
559
560                 do_restart = 0;
561
562                 /* Did the main process exit with an actual exit code? */
563                 if (WIFEXITED(status)) {
564
565                         /* Exit code 0 means the watcher should exit */
566                         if (WEXITSTATUS(status) == 0) {
567                                 do_restart = 0;
568                         }
569
570                         /* Exit code 101-109 means the watcher should exit */
571                         else if ( (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109) ) {
572                                 do_restart = 0;
573                         }
574
575                         /* Any other exit code means we should restart. */
576                         else {
577                                 do_restart = 1;
578                         }
579                 }
580
581                 /* Any other type of termination (signals, etc.) should also restart. */
582                 else {
583                         do_restart = 1;
584                 }
585
586         } while (do_restart);
587
588         if (pid_file) {
589                 unlink(pid_file);
590         }
591         ShutDownLibCitadel ();
592         exit(WEXITSTATUS(status));
593 }
594
595 /*
596  * Spawn an additional worker thread into the pool.
597  */
598 void spawn_another_worker_thread()
599 {
600         pthread_t SessThread;   /* Thread descriptor */
601         pthread_attr_t attr;    /* Thread attributes */
602         int ret;
603
604         lprintf(3, "Creating a new thread\n");
605
606         /* set attributes for the new thread */
607         pthread_attr_init(&attr);
608         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
609
610         /*
611          * Our per-thread stacks need to be bigger than the default size, otherwise
612          * the MIME parser crashes on FreeBSD, and the IMAP service crashes on
613          * 64-bit Linux.
614          */
615         if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) {
616                 lprintf(1, "pthread_attr_setstacksize: %s\n",
617                         strerror(ret));
618                 pthread_attr_destroy(&attr);
619         }
620
621         /* now create the thread */
622         if (pthread_create(&SessThread, &attr,
623                            (void *(*)(void *)) worker_entry, NULL)
624             != 0) {
625                 lprintf(1, "Can't create thread: %s\n", strerror(errno));
626         }
627
628         /* free up the attributes */
629         pthread_attr_destroy(&attr);
630 }
631
632 //#define DBG_PRINNT_HOOKS_AT_START
633 #ifdef DBG_PRINNT_HOOKS_AT_START
634 const char foobuf[32];
635 const char *nix(void *vptr) {snprintf(foobuf, 32, "%0x", (long) vptr); return foobuf;}
636 #endif 
637 void InitTemplateCache(void);
638 extern int LoadTemplates;
639 extern void LoadZoneFiles(void);
640
641 /*
642  * Here's where it all begins.
643  */
644 int main(int argc, char **argv)
645 {
646         pthread_t SessThread;           /* Thread descriptor */
647         pthread_attr_t attr;            /* Thread attributes */
648         int a, i;                       /* General-purpose variables */
649         char tracefile[PATH_MAX];
650         char ip_addr[256]="0.0.0.0";
651         char dirbuffer[PATH_MAX]="";
652         int relh=0;
653         int home=0;
654         int home_specified=0;
655         char relhome[PATH_MAX]="";
656         char webcitdir[PATH_MAX] = DATADIR;
657         char *pidfile = NULL;
658         char *hdir;
659         const char *basedir;
660 #ifdef ENABLE_NLS
661         char *locale = NULL;
662         char *mo = NULL;
663 #endif /* ENABLE_NLS */
664         char uds_listen_path[PATH_MAX]; /* listen on a unix domain socket? */
665
666         HandlerHash = NewHash(1, NULL);
667         PreferenceHooks = NewHash(1, NULL);
668         WirelessTemplateCache = NewHash(1, NULL);
669         WirelessLocalTemplateCache = NewHash(1, NULL);
670         LocalTemplateCache = NewHash(1, NULL);
671         TemplateCache = NewHash(1, NULL);
672         GlobalNS = NewHash(1, NULL);
673         Iterators = NewHash(1, NULL);
674         Contitionals = NewHash(1, NULL);
675         LoadZoneFiles();
676
677
678 #ifdef DBG_PRINNT_HOOKS_AT_START
679         dbg_PrintHash(HandlerHash, nix, NULL);
680 #endif
681
682         /* Ensure that we are linked to the correct version of libcitadel */
683         if (libcitadel_version_number() < LIBCITADEL_VERSION_NUMBER) {
684                 fprintf(stderr, " You are running libcitadel version %d.%02d\n",
685                         (libcitadel_version_number() / 100), (libcitadel_version_number() % 100));
686                 fprintf(stderr, "WebCit was compiled against version %d.%02d\n",
687                         (LIBCITADEL_VERSION_NUMBER / 100), (LIBCITADEL_VERSION_NUMBER % 100));
688                 return(1);
689         }
690
691         strcpy(uds_listen_path, "");
692
693         /* Parse command line */
694 #ifdef HAVE_OPENSSL
695         while ((a = getopt(argc, argv, "h:i:p:t:T:x:dD:cfs")) != EOF)
696 #else
697         while ((a = getopt(argc, argv, "h:i:p:t:T:x:dD:cf")) != EOF)
698 #endif
699                 switch (a) {
700                 case 'h':
701                         hdir = strdup(optarg);
702                         relh=hdir[0]!='/';
703                         if (!relh) safestrncpy(webcitdir, hdir,
704                                                                    sizeof webcitdir);
705                         else
706                                 safestrncpy(relhome, relhome,
707                                                         sizeof relhome);
708                         /* free(hdir); TODO: SHOULD WE DO THIS? */
709                         home_specified = 1;
710                         home=1;
711                         break;
712                 case 'd':
713                         running_as_daemon = 1;
714                         break;
715                 case 'D':
716                         pidfile = strdup(optarg);
717                         running_as_daemon = 1;
718                         break;
719                 case 'i':
720                         safestrncpy(ip_addr, optarg, sizeof ip_addr);
721                         break;
722                 case 'p':
723                         http_port = atoi(optarg);
724                         if (http_port == 0) {
725                                 safestrncpy(uds_listen_path, optarg, sizeof uds_listen_path);
726                         }
727                         break;
728                 case 't':
729                         safestrncpy(tracefile, optarg, sizeof tracefile);
730                         freopen(tracefile, "w", stdout);
731                         freopen(tracefile, "w", stderr);
732                         freopen(tracefile, "r", stdin);
733                         break;
734                 case 'T':
735                         LoadTemplates = atoi(optarg);
736                         break;
737                 case 'x':
738                         verbosity = atoi(optarg);
739                         break;
740                 case 'f':
741                         follow_xff = 1;
742                         break;
743                 case 'c':
744                         server_cookie = malloc(256);
745                         if (server_cookie != NULL) {
746                                 safestrncpy(server_cookie,
747                                        "Set-cookie: wcserver=",
748                                         256);
749                                 if (gethostname
750                                     (&server_cookie[strlen(server_cookie)],
751                                      200) != 0) {
752                                         lprintf(2, "gethostname: %s\n",
753                                                 strerror(errno));
754                                         free(server_cookie);
755                                 }
756                         }
757                         break;
758                 case 's':
759                         is_https = 1;
760                         break;
761                 default:
762                         fprintf(stderr, "usage: webcit "
763                                 "[-i ip_addr] [-p http_port] "
764                                 "[-t tracefile] [-c] [-f] "
765                                 "[-T Templatedebuglevel] "
766                                 "[-d] "
767 #ifdef HAVE_OPENSSL
768                                 "[-s] "
769 #endif
770                                 "[remotehost [remoteport]]\n");
771                         return 1;
772                 }
773
774         if (optind < argc) {
775                 ctdlhost = argv[optind];
776                 if (++optind < argc)
777                         ctdlport = argv[optind];
778         }
779
780         /* daemonize, if we were asked to */
781         if (running_as_daemon) {
782                 start_daemon(pidfile);
783         }
784         else {
785 ///             signal(SIGTERM, graceful_shutdown);
786                 signal(SIGHUP, graceful_shutdown);
787         }
788
789         /* Tell 'em who's in da house */
790         lprintf(1, PACKAGE_STRING "\n");
791         lprintf(1, "Copyright (C) 1996-2008 by the Citadel development team.\n"
792                 "This software is distributed under the terms of the "
793                 "GNU General Public License.\n\n"
794         );
795
796
797         /* initialize the International Bright Young Thing */
798 #ifdef ENABLE_NLS
799         initialize_locales();
800
801         locale = setlocale(LC_ALL, "");
802
803         mo = malloc(strlen(webcitdir) + 20);
804         lprintf(9, "Message catalog directory: %s\n", bindtextdomain("webcit", LOCALEDIR"/locale"));
805         free(mo);
806         lprintf(9, "Text domain: %s\n", textdomain("webcit"));
807         lprintf(9, "Text domain Charset: %s\n", bind_textdomain_codeset("webcit","UTF8"));
808         preset_locale();
809 #endif
810
811
812         /* calculate all our path on a central place */
813     /* where to keep our config */
814         
815 #define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
816         snprintf(SUBDIR,sizeof SUBDIR,  "%s%s%s%s%s%s%s", \
817                          (home&!relh)?webcitdir:basedir, \
818              ((basedir!=webcitdir)&(home&!relh))?basedir:"/", \
819              ((basedir!=webcitdir)&(home&!relh))?"/":"", \
820                          relhome, \
821              (relhome[0]!='\0')?"/":"",\
822                          dirbuffer,\
823                          (dirbuffer[0]!='\0')?"/":"");
824         basedir=RUNDIR;
825         COMPUTE_DIRECTORY(socket_dir);
826         basedir=WWWDIR "/static";
827         COMPUTE_DIRECTORY(static_dir);
828         basedir=WWWDIR "/static/icons";
829         COMPUTE_DIRECTORY(static_icon_dir);
830         basedir=WWWDIR "/static.local";
831         COMPUTE_DIRECTORY(static_local_dir);
832
833         snprintf(file_crpt_file_key,
834                  sizeof file_crpt_file_key, 
835                  "%s/citadel.key",
836                  ctdl_key_dir);
837         snprintf(file_crpt_file_csr,
838                  sizeof file_crpt_file_csr, 
839                  "%s/citadel.csr",
840                  ctdl_key_dir);
841         snprintf(file_crpt_file_cer,
842                  sizeof file_crpt_file_cer, 
843                  "%s/citadel.cer",
844                  ctdl_key_dir);
845
846         /* we should go somewhere we can leave our coredump, if enabled... */
847         lprintf(9, "Changing directory to %s\n", socket_dir);
848         if (chdir(webcitdir) != 0) {
849                 perror("chdir");
850         }
851         LoadIconDir(static_icon_dir);
852         InitTemplateCache();
853
854         initialise_modules();
855         initialize_viewdefs();
856         initialize_axdefs();
857
858         /* Tell libical to return an error instead of aborting if it sees badly formed iCalendar data. */
859         icalerror_errors_are_fatal = 0;
860
861         /*
862          * Set up a place to put thread-specific data.
863          * We only need a single pointer per thread - it points to the
864          * wcsession struct to which the thread is currently bound.
865          */
866         if (pthread_key_create(&MyConKey, NULL) != 0) {
867                 lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
868         }
869         InitialiseSemaphores ();
870
871         /*
872          * Set up a place to put thread-specific SSL data.
873          * We don't stick this in the wcsession struct because SSL starts
874          * up before the session is bound, and it gets torn down between
875          * transactions.
876          */
877 #ifdef HAVE_OPENSSL
878         if (pthread_key_create(&ThreadSSL, NULL) != 0) {
879                 lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
880         }
881 #endif
882
883         /*
884          * Bind the server to our favorite port.
885          * There is no need to check for errors, because ig_tcp_server()
886          * exits if it doesn't succeed.
887          */
888
889         if (!IsEmptyStr(uds_listen_path)) {
890                 lprintf(2, "Attempting to create listener socket at %s...\n", uds_listen_path);
891                 msock = ig_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH);
892         }
893         else {
894                 lprintf(2, "Attempting to bind to port %d...\n", http_port);
895                 msock = ig_tcp_server(ip_addr, http_port, LISTEN_QUEUE_LENGTH);
896         }
897
898         lprintf(2, "Listening on socket %d\n", msock);
899         signal(SIGPIPE, SIG_IGN);
900
901         pthread_mutex_init(&SessionListMutex, NULL);
902
903         /*
904          * Start up the housekeeping thread
905          */
906         pthread_attr_init(&attr);
907         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
908         pthread_create(&SessThread, &attr,
909                        (void *(*)(void *)) housekeeping_loop, NULL);
910
911
912         /*
913          * If this is an HTTPS server, fire up SSL
914          */
915 #ifdef HAVE_OPENSSL
916         if (is_https) {
917                 init_ssl();
918         }
919 #endif
920
921         /* Start a few initial worker threads */
922         for (i = 0; i < (MIN_WORKER_THREADS); ++i) {
923                 spawn_another_worker_thread();
924         }
925
926         /* now the original thread becomes another worker */
927         worker_entry();
928         ShutDownLibCitadel ();
929         DeleteHash(&HandlerHash);
930         DeleteHash(&PreferenceHooks);
931         return 0;
932 }
933
934
935 void ShutDownWebcit(void)
936 {
937         DeleteHash(&ZoneHash);
938         free_zone_directory ();
939         icaltimezone_release_zone_tab ();
940         icalmemory_free_ring ();
941         ShutDownLibCitadel ();
942         DeleteHash(&HandlerHash);
943         DeleteHash(&PreferenceHooks);
944         DeleteHash(&GlobalNS);
945         DeleteHash(&WirelessTemplateCache);
946         DeleteHash(&WirelessLocalTemplateCache);
947         DeleteHash(&TemplateCache);
948         DeleteHash(&LocalTemplateCache);
949         DeleteHash(&Iterators);
950         DeleteHash(&Contitionals);
951 #ifdef ENABLE_NLS
952         ShutdownLocale();
953 #endif
954 #ifdef HAVE_OPENSSL
955         if (is_https) {
956                 shutdown_ssl();
957         }
958 #endif
959 }
960
961 /*
962  * Entry point for worker threads
963  */
964 void worker_entry(void)
965 {
966         int ssock;
967         int i = 0;
968         int fail_this_transaction = 0;
969         int ret;
970         struct timeval tv;
971         fd_set readset, tempset;
972
973         tv.tv_sec = 0;
974         tv.tv_usec = 10000;
975         FD_ZERO(&readset);
976         FD_SET(msock, &readset);
977
978         do {
979                 /* Only one thread can accept at a time */
980                 fail_this_transaction = 0;
981                 ssock = -1; 
982                 errno = EAGAIN;
983                 do {
984                         ret = -1; /* just one at once should select... */
985                         begin_critical_section(S_SELECT);
986
987                         FD_ZERO(&tempset);
988                         if (msock > 0) FD_SET(msock, &tempset);
989                         tv.tv_sec = 0;
990                         tv.tv_usec = 10000;
991                         if (msock > 0)  ret = select(msock+1, &tempset, NULL, NULL,  &tv);
992                         end_critical_section(S_SELECT);
993                         if ((ret < 0) && (errno != EINTR) && (errno != EAGAIN))
994                         {// EINTR and EAGAIN are thrown but not of interest.
995                                 lprintf(2, "accept() failed:%d %s\n",
996                                         errno, strerror(errno));
997                         }
998                         else if ((ret > 0) && (msock > 0) && FD_ISSET(msock, &tempset))
999                         {// Successfully selected, and still not shutting down? Accept!
1000                                 ssock = accept(msock, NULL, 0);
1001                         }
1002                         
1003                 } while ((msock > 0) && (ssock < 0)  && (time_to_die == 0));
1004
1005                 if ((msock == -1)||(time_to_die))
1006                 {// ok, we're going down.
1007                         int shutdown = 0;
1008
1009                         /* the first to come here will have to do the cleanup.
1010                          * make shure its realy just one.
1011                          */
1012                         begin_critical_section(S_SHUTDOWN);
1013                         if (msock == -1)
1014                         {
1015                                 msock = -2;
1016                                 shutdown = 1;
1017                         }
1018                         end_critical_section(S_SHUTDOWN);
1019                         if (shutdown == 1)
1020                         {// we're the one to cleanup the mess.
1021                                 lprintf(2, "I'm master shutdown: tagging sessions to be killed.\n");
1022                                 shutdown_sessions();
1023                                 lprintf(2, "master shutdown: waiting for others\n");
1024                                 sleeeeeeeeeep(1); // wait so some others might finish...
1025                                 lprintf(2, "master shutdown: cleaning up sessions\n");
1026                                 do_housekeeping();
1027                                 lprintf(2, "master shutdown: cleaning up libical\n");
1028
1029                                 ShutDownWebcit();
1030
1031                                 lprintf(2, "master shutdown exiting!.\n");                              
1032                                 exit(0);
1033                         }
1034                         break;
1035                 }
1036                 if (ssock < 0 ) continue;
1037
1038                 if (msock < 0) {
1039                         if (ssock > 0) close (ssock);
1040                         lprintf(2, "inbetween.");
1041                         pthread_exit(NULL);
1042                 } else { // Got it? do some real work!
1043                         /* Set the SO_REUSEADDR socket option */
1044                         i = 1;
1045                         setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR,
1046                                    &i, sizeof(i));
1047
1048                         /* If we are an HTTPS server, go crypto now. */
1049 #ifdef HAVE_OPENSSL
1050                         if (is_https) {
1051                                 if (starttls(ssock) != 0) {
1052                                         fail_this_transaction = 1;
1053                                         close(ssock);
1054                                 }
1055                         }
1056 #endif
1057
1058                         if (fail_this_transaction == 0) {
1059
1060                                 /* Perform an HTTP transaction... */
1061                                 context_loop(&ssock);
1062
1063                                 /* Shut down SSL/TLS if required... */
1064 #ifdef HAVE_OPENSSL
1065                                 if (is_https) {
1066                                         endtls();
1067                                 }
1068 #endif
1069
1070                                 /* ...and close the socket. */
1071                                 if (ssock > 0)
1072                                         lingering_close(ssock);
1073                         }
1074
1075                 }
1076
1077         } while (!time_to_die);
1078
1079         lprintf (1, "bye\n");
1080         pthread_exit(NULL);
1081 }
1082
1083 /*
1084  * print log messages 
1085  * logs to stderr if loglevel is lower than the verbosity set at startup
1086  *
1087  * loglevel     level of the message
1088  * format       the printf like format string
1089  * ...          the strings to put into format
1090  */
1091 int lprintf(int loglevel, const char *format, ...)
1092 {
1093         va_list ap;
1094
1095         if (loglevel <= verbosity) {
1096                 va_start(ap, format);
1097                 vfprintf(stderr, format, ap);
1098                 va_end(ap);
1099                 fflush(stderr);
1100         }
1101         return 1;
1102 }
1103
1104
1105 /*
1106  * print the actual stack frame.
1107  */
1108 void wc_backtrace(void)
1109 {
1110 #ifdef HAVE_BACKTRACE
1111         void *stack_frames[50];
1112         size_t size, i;
1113         char **strings;
1114
1115
1116         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
1117         strings = backtrace_symbols(stack_frames, size);
1118         for (i = 0; i < size; i++) {
1119                 if (strings != NULL)
1120                         lprintf(1, "%s\n", strings[i]);
1121                 else
1122                         lprintf(1, "%p\n", stack_frames[i]);
1123         }
1124         free(strings);
1125 #endif
1126 }
1127