initial load of upstream version 1.06.32
[xmlrpc-c] / lib / abyss / src / server.c
1 /* Copyright information is at end of file */
2 #include <assert.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <time.h>
7 #include <errno.h>
8 #ifdef WIN32
9   #include <io.h>
10 #else
11   #include <unistd.h>
12   #include <grp.h>
13 #endif
14 #include <fcntl.h>
15
16 #include "xmlrpc_config.h"
17 #include "mallocvar.h"
18 #include "xmlrpc-c/string_int.h"
19 #include "xmlrpc-c/sleep_int.h"
20
21 #include "xmlrpc-c/abyss.h"
22 #include "trace.h"
23 #include "session.h"
24 #include "conn.h"
25 #include "socket.h"
26 #ifdef WIN32
27   #include "socket_win.h"
28 #else
29   #include "socket_unix.h"
30 #endif
31 #include "http.h"
32 #include "date.h"
33 #include "abyss_info.h"
34
35 #include "server.h"
36
37
38 void
39 ServerTerminate(TServer * const serverP) {
40
41     struct _TServer * const srvP = serverP->srvP;
42
43     srvP->terminationRequested = true;
44 }
45
46
47
48 void
49 ServerResetTerminate(TServer * const serverP) {
50
51     struct _TServer * const srvP = serverP->srvP;
52
53     srvP->terminationRequested = false;
54 }
55
56
57
58 typedef int (*TQSortProc)(const void *, const void *);
59
60 static int
61 cmpfilenames(const TFileInfo **f1,const TFileInfo **f2) {
62     if (((*f1)->attrib & A_SUBDIR) && !((*f2)->attrib & A_SUBDIR))
63         return (-1);
64     if (!((*f1)->attrib & A_SUBDIR) && ((*f2)->attrib & A_SUBDIR))
65         return 1;
66
67     return strcmp((*f1)->name,(*f2)->name);
68 }
69
70 static int
71 cmpfiledates(const TFileInfo **f1,const TFileInfo **f2) {
72     if (((*f1)->attrib & A_SUBDIR) && !((*f2)->attrib & A_SUBDIR))
73         return (-1);
74     if (!((*f1)->attrib & A_SUBDIR) && ((*f2)->attrib & A_SUBDIR))
75         return 1;
76
77     return ((*f1)->time_write-(*f2)->time_write);
78 }
79
80
81
82 static void
83 determineSortType(const char *  const query,
84                   abyss_bool *  const ascendingP,
85                   uint16_t *    const sortP,
86                   abyss_bool *  const textP,
87                   const char ** const errorP) {
88
89     *ascendingP = TRUE;
90     *sortP = 1;
91     *textP = FALSE;
92     *errorP = NULL;
93     
94     if (query) {
95         if (xmlrpc_streq(query, "plain"))
96             *textP = TRUE;
97         else if (xmlrpc_streq(query, "name-up")) {
98             *sortP = 1;
99             *ascendingP = TRUE;
100         } else if (xmlrpc_streq(query, "name-down")) {
101             *sortP = 1;
102             *ascendingP = FALSE;
103         } else if (xmlrpc_streq(query, "date-up")) {
104             *sortP = 2;
105             *ascendingP = TRUE;
106         } else if (xmlrpc_streq(query, "date-down")) {
107             *sortP = 2;
108             *ascendingP = FALSE;
109         } else  {
110             xmlrpc_asprintf(errorP, "invalid query value '%s'", query);
111         }
112     }
113 }
114
115
116
117 static void
118 generateListing(TList *       const listP,
119                 char *        const z,
120                 const char *  const uri,
121                 TPool *       const poolP,
122                 const char ** const errorP,
123                 uint16_t *    const responseStatusP) {
124     
125     TFileInfo fileinfo;
126     TFileFind findhandle;
127
128     *errorP = NULL;
129
130     if (!FileFindFirst(&findhandle, z, &fileinfo)) {
131         *responseStatusP = ResponseStatusFromErrno(errno);
132         xmlrpc_asprintf(errorP, "Can't read first entry in directory");
133     } else {
134         ListInit(listP);
135
136         do {
137             TFileInfo * fi;
138             /* Files whose names start with a dot are ignored */
139             /* This includes implicitly the ./ and ../ */
140             if (*fileinfo.name == '.') {
141                 if (xmlrpc_streq(fileinfo.name, "..")) {
142                     if (xmlrpc_streq(uri, "/"))
143                         continue;
144                 } else
145                     continue;
146             }
147             fi = (TFileInfo *)PoolAlloc(poolP, sizeof(fileinfo));
148             if (fi) {
149                 abyss_bool success;
150                 memcpy(fi, &fileinfo, sizeof(fileinfo));
151                 success =  ListAdd(listP, fi);
152                 if (!success)
153                     xmlrpc_asprintf(errorP, "ListAdd() failed");
154             } else
155                 xmlrpc_asprintf(errorP, "PoolAlloc() failed.");
156         } while (!*errorP && FileFindNext(&findhandle, &fileinfo));
157
158         if (*errorP) {
159             *responseStatusP = 500;
160             ListFree(listP);
161         }            
162         FileFindClose(&findhandle);
163     }
164 }
165
166
167
168 static void
169 sendDirectoryDocument(TList *      const listP,
170                       abyss_bool   const ascending,
171                       uint16_t     const sort,
172                       abyss_bool   const text,
173                       const char * const uri,
174                       MIMEType *   const mimeTypeP,
175                       TSession *   const sessionP,
176                       char *       const z) {
177
178     char *p,z1[26],z2[20],z3[9],u;
179     const char * z4;
180     int16_t i;
181     uint32_t k;
182
183     if (text) {
184         sprintf(z, "Index of %s" CRLF, uri);
185         i = strlen(z)-2;
186         p = z + i + 2;
187
188         while (i > 0) {
189             *(p++) = '-';
190             --i;
191         }
192
193         *p = '\0';
194         strcat(z, CRLF CRLF
195                "Name                      Size      "
196                "Date-Time             Type" CRLF
197                "------------------------------------"
198                "--------------------------------------------"CRLF);
199     } else {
200         sprintf(z, "<HTML><HEAD><TITLE>Index of %s</TITLE></HEAD><BODY>"
201                 "<H1>Index of %s</H1><PRE>",
202                 uri, uri);
203         strcat(z, "Name                      Size      "
204                "Date-Time             Type<HR WIDTH=100%>"CRLF);
205     }
206
207     HTTPWriteBodyChunk(sessionP, z, strlen(z));
208
209     /* Sort the files */
210     qsort(listP->item, listP->size, sizeof(void *),
211           (TQSortProc)(sort == 1 ? cmpfilenames : cmpfiledates));
212     
213     /* Write the listing */
214     if (ascending)
215         i = 0;
216     else
217         i = listP->size - 1;
218
219     while ((i < listP->size) && (i >= 0)) {
220         TFileInfo * fi;
221         struct tm ftm;
222
223         fi = listP->item[i];
224
225         if (ascending)
226             ++i;
227         else
228             --i;
229             
230         strcpy(z, fi->name);
231
232         k = strlen(z);
233
234         if (fi->attrib & A_SUBDIR) {
235             z[k++] = '/';
236             z[k] = '\0';
237         }
238
239         if (k > 24) {
240             z[10] = '\0';
241             strcpy(z1, z);
242             strcat(z1, "...");
243             strcat(z1, z + k - 11);
244             k = 24;
245             p = z1 + 24;
246         } else {
247             strcpy(z1, z);
248             
249             ++k;
250             p = z1 + k;
251             while (k < 25)
252                 z1[k++] = ' ';
253             
254             z1[25] = '\0';
255         }
256
257         ftm = *gmtime(&fi->time_write);
258         sprintf(z2, "%02u/%02u/%04u %02u:%02u:%02u",ftm.tm_mday,ftm.tm_mon+1,
259                 ftm.tm_year+1900,ftm.tm_hour,ftm.tm_min,ftm.tm_sec);
260
261         if (fi->attrib & A_SUBDIR) {
262             strcpy(z3, "   --  ");
263             z4 = "Directory";
264         } else {
265             if (fi->size < 9999)
266                 u = 'b';
267             else {
268                 fi->size /= 1024;
269                 if (fi->size < 9999)
270                     u = 'K';
271                 else {
272                     fi->size /= 1024;
273                     if (fi->size < 9999)
274                         u = 'M';
275                     else
276                         u = 'G';
277                 }
278             }
279                 
280             sprintf(z3, "%5llu %c", fi->size, u);
281             
282             if (xmlrpc_streq(fi->name, ".."))
283                 z4 = "";
284             else
285                 z4 = MIMETypeFromFileName2(mimeTypeP, fi->name);
286
287             if (!z4)
288                 z4 = "Unknown";
289         }
290
291         if (text)
292             sprintf(z, "%s%s %s    %s   %s"CRLF, z1, p, z3, z2, z4);
293         else
294             sprintf(z, "<A HREF=\"%s%s\">%s</A>%s %s    %s   %s"CRLF,
295                     fi->name, fi->attrib & A_SUBDIR ? "/" : "",
296                     z1, p, z3, z2, z4);
297
298         HTTPWriteBodyChunk(sessionP, z, strlen(z));
299     }
300         
301     /* Write the tail of the file */
302     if (text)
303         strcpy(z, SERVER_PLAIN_INFO);
304     else
305         strcpy(z, "</PRE>" SERVER_HTML_INFO "</BODY></HTML>" CRLF CRLF);
306     
307     HTTPWriteBodyChunk(sessionP, z, strlen(z));
308 }
309
310
311
312 static void
313 fileDate(TSession * const sessionP,
314          time_t     const statFilemodTime,
315          TDate *    const fileDateP) {
316
317     abyss_bool haveDate;
318     TDate filemodDate;
319
320     haveDate = DateFromLocal(&filemodDate, statFilemodTime);
321
322     if (haveDate) {
323         if (DateCompare(&sessionP->date, &filemodDate) < 0)
324             *fileDateP = sessionP->date;
325         else
326             *fileDateP = filemodDate;
327     } else
328         *fileDateP = sessionP->date;
329 }
330
331
332
333 static abyss_bool
334 ServerDirectoryHandler(TSession * const r,
335                        char *     const z,
336                        time_t     const fileModTime,
337                        MIMEType * const mimeTypeP) {
338
339     TList list;
340     abyss_bool text;
341     abyss_bool ascending;
342     uint16_t sort;    /* 1=by name, 2=by date */
343     TPool pool;
344     TDate date;
345     const char * error;
346     uint16_t responseStatus;
347     TDate dirdate;
348     const char * imsHdr;
349     
350     determineSortType(r->request_info.query, &ascending, &sort, &text, &error);
351
352     if (error) {
353         ResponseStatus(r,400);
354         xmlrpc_strfree(error);
355         return TRUE;
356     }
357
358     fileDate(r, fileModTime, &dirdate);
359
360     imsHdr = RequestHeaderValue(r, "If-Modified-Since");
361     if (imsHdr) {
362         if (DateDecode(imsHdr, &date)) {
363             if (DateCompare(&dirdate, &date) <= 0) {
364                 ResponseStatus(r, 304);
365                 ResponseWrite(r);
366                 return TRUE;
367             }
368         }
369     }
370
371     if (!PoolCreate(&pool, 1024)) {
372         ResponseStatus(r, 500);
373         return TRUE;
374     }
375
376     generateListing(&list, z, r->request_info.uri,
377                     &pool, &error, &responseStatus);
378     if (error) {
379         ResponseStatus(r, responseStatus);
380         xmlrpc_strfree(error);
381         PoolFree(&pool);
382         return TRUE;
383     }
384
385     /* Send something to the user to show that we are still alive */
386     ResponseStatus(r, 200);
387     ResponseContentType(r, (text ? "text/plain" : "text/html"));
388
389     if (DateToString(&dirdate, z))
390         ResponseAddField(r, "Last-Modified", z);
391     
392     ResponseChunked(r);
393     ResponseWrite(r);
394
395     if (r->request_info.method!=m_head)
396         sendDirectoryDocument(&list, ascending, sort, text,
397                               r->request_info.uri, mimeTypeP, r, z);
398
399     HTTPWriteEndChunk(r);
400
401     /* Free memory and exit */
402     ListFree(&list);
403     PoolFree(&pool);
404
405     return TRUE;
406 }
407
408
409
410 #define BOUNDARY    "##123456789###BOUNDARY"
411
412 static void
413 sendBody(TSession *   const sessionP,
414          TFile *      const fileP,
415          uint64_t     const filesize,
416          const char * const mediatype,
417          uint64_t     const start0,
418          uint64_t     const end0,
419          char *       const z) {
420
421     if (sessionP->ranges.size == 0)
422         ConnWriteFromFile(sessionP->conn, fileP, 0, filesize - 1, z, 4096, 0);
423     else if (sessionP->ranges.size == 1)
424         ConnWriteFromFile(sessionP->conn, fileP, start0, end0, z, 4096, 0);
425     else {
426         uint64_t i;
427         for (i = 0; i <= sessionP->ranges.size; ++i) {
428             ConnWrite(sessionP->conn,"--", 2);
429             ConnWrite(sessionP->conn, BOUNDARY, strlen(BOUNDARY));
430             ConnWrite(sessionP->conn, CRLF, 2);
431
432             if (i < sessionP->ranges.size) {
433                 uint64_t start;
434                 uint64_t end;
435                 abyss_bool decoded;
436                     
437                 decoded = RangeDecode((char *)(sessionP->ranges.item[i]),
438                                       filesize,
439                                       &start, &end);
440                 if (decoded) {
441                     /* Entity header, not response header */
442                     sprintf(z, "Content-type: %s" CRLF
443                             "Content-range: bytes %llu-%llu/%llu" CRLF
444                             "Content-length: %llu" CRLF
445                             CRLF, mediatype, start, end,
446                             filesize, end-start+1);
447
448                     ConnWrite(sessionP->conn, z, strlen(z));
449
450                     ConnWriteFromFile(sessionP->conn, fileP, start, end, z,
451                                       4096, 0);
452                 }
453             }
454         }
455     }
456 }
457
458
459
460 static abyss_bool
461 ServerFileHandler(TSession * const r,
462                   char *     const z,
463                   time_t     const fileModTime,
464                   MIMEType * const mimeTypeP) {
465
466     const char * mediatype;
467     TFile file;
468     uint64_t filesize;
469     uint64_t start;
470     uint64_t end;
471     TDate date;
472     char * p;
473     TDate filedate;
474     
475     mediatype = MIMETypeGuessFromFile2(mimeTypeP, z);
476
477     if (!FileOpen(&file,z,O_BINARY | O_RDONLY)) {
478         ResponseStatusErrno(r);
479         return TRUE;
480     }
481
482     fileDate(r, fileModTime, &filedate);
483
484     p = RequestHeaderValue(r, "if-modified-since");
485     if (p) {
486         if (DateDecode(p,&date)) {
487             if (DateCompare(&filedate, &date) <= 0) {
488                 ResponseStatus(r, 304);
489                 ResponseWrite(r);
490                 return TRUE;
491             } else
492                 r->ranges.size = 0;
493         }
494     }
495     filesize = FileSize(&file);
496
497     switch (r->ranges.size) {
498     case 0:
499         ResponseStatus(r, 200);
500         break;
501
502     case 1: {
503         abyss_bool decoded;
504         decoded = RangeDecode((char *)(r->ranges.item[0]), filesize,
505                               &start, &end);
506         if (!decoded) {
507             ListFree(&(r->ranges));
508             ResponseStatus(r, 200);
509             break;
510         }
511         
512         sprintf(z, "bytes %llu-%llu/%llu", start, end, filesize);
513
514         ResponseAddField(r, "Content-range", z);
515         ResponseContentLength(r, end - start + 1);
516         ResponseStatus(r, 206);
517     } break;
518
519     default:
520         ResponseContentType(r, "multipart/ranges; boundary=" BOUNDARY);
521         ResponseStatus(r, 206);
522         break;
523     }
524     
525     if (r->ranges.size == 0) {
526         ResponseContentLength(r, filesize);
527         ResponseContentType(r, mediatype);
528     }
529     
530     if (DateToString(&filedate, z))
531         ResponseAddField(r, "Last-Modified", z);
532
533     ResponseWrite(r);
534
535     if (r->request_info.method != m_head)
536         sendBody(r, &file, filesize, mediatype, start, end, z);
537
538     FileClose(&file);
539
540     return TRUE;
541 }
542
543
544
545 static abyss_bool
546 ServerDefaultHandlerFunc(TSession * const sessionP) {
547
548     struct _TServer * const srvP = ConnServer(sessionP->conn)->srvP;
549
550     char *p;
551     char z[4096];
552     TFileStat fs;
553     unsigned int i;
554     abyss_bool endingslash=FALSE;
555
556     if (!RequestValidURIPath(sessionP)) {
557         ResponseStatus(sessionP, 400);
558         return TRUE;
559     }
560
561     /* Must check for * (asterisk uri) in the future */
562     if (sessionP->request_info.method == m_options) {
563         ResponseAddField(sessionP, "Allow", "GET, HEAD");
564         ResponseContentLength(sessionP, 0);
565         ResponseStatus(sessionP, 200);
566         return TRUE;
567     }
568
569     if ((sessionP->request_info.method != m_get) &&
570         (sessionP->request_info.method != m_head)) {
571         ResponseAddField(sessionP, "Allow", "GET, HEAD");
572         ResponseStatus(sessionP, 405);
573         return TRUE;
574     }
575
576     strcpy(z, srvP->filespath);
577     strcat(z, sessionP->request_info.uri);
578
579     p = z + strlen(z) - 1;
580     if (*p == '/') {
581         endingslash = TRUE;
582         *p = '\0';
583     }
584
585 #ifdef WIN32
586     p = z;
587     while (*p) {
588         if ((*p) == '/')
589             *p= '\\';
590
591         ++p;
592     }
593 #endif  /* WIN32 */
594
595     if (!FileStat(z, &fs)) {
596         ResponseStatusErrno(sessionP);
597         return TRUE;
598     }
599
600     if (fs.st_mode & S_IFDIR) {
601         /* Redirect to the same directory but with the ending slash
602         ** to avoid problems with some browsers (IE for examples) when
603         ** they generate relative urls */
604         if (!endingslash) {
605             strcpy(z, sessionP->request_info.uri);
606             p = z+strlen(z);
607             *p = '/';
608             *(p+1) = '\0';
609             ResponseAddField(sessionP, "Location", z);
610             ResponseStatus(sessionP, 302);
611             ResponseWrite(sessionP);
612             return TRUE;
613         }
614
615         *p = DIRECTORY_SEPARATOR[0];
616         ++p;
617
618         i = srvP->defaultfilenames.size;
619         while (i-- > 0) {
620             *p = '\0';        
621             strcat(z, (srvP->defaultfilenames.item[i]));
622             if (FileStat(z, &fs)) {
623                 if (!(fs.st_mode & S_IFDIR))
624                     return ServerFileHandler(sessionP, z, fs.st_mtime,
625                                              srvP->mimeTypeP);
626             }
627         }
628
629         *(p-1) = '\0';
630         
631         if (!FileStat(z, &fs)) {
632             ResponseStatusErrno(sessionP);
633             return TRUE;
634         }
635         return ServerDirectoryHandler(sessionP, z, fs.st_mtime,
636                                       srvP->mimeTypeP);
637     } else
638         return ServerFileHandler(sessionP, z, fs.st_mtime,
639                                  srvP->mimeTypeP);
640 }
641
642
643
644 static void
645 initUnixStuff(struct _TServer * const srvP) {
646 #ifndef WIN32
647     srvP->pidfile = srvP->uid = srvP->gid = -1;
648 #endif
649 }
650
651
652
653 static abyss_bool
654 logOpen(struct _TServer * const srvP) {
655
656     abyss_bool success;
657
658     success = FileOpenCreate(&srvP->logfile, srvP->logfilename,
659                              O_WRONLY | O_APPEND);
660     if (success) {
661         abyss_bool success;
662         success = MutexCreate(&srvP->logmutex);
663         if (success)
664             srvP->logfileisopen = TRUE;
665         else
666             TraceMsg("Can't create mutex for log file");
667
668         if (!success)
669             FileClose(&srvP->logfile);
670     } else
671         TraceMsg("Can't open log file '%s'", srvP->logfilename);
672
673     return success;
674 }
675
676
677
678 static void
679 logClose(struct _TServer * const srvP) {
680
681     if (srvP->logfileisopen) {
682         FileClose(&srvP->logfile);
683         MutexFree(&srvP->logmutex);
684         srvP->logfileisopen = FALSE;
685     }
686 }
687
688
689
690 static void
691 initSocketStuff(struct _TServer * const srvP,
692                 abyss_bool        const noAccept,
693                 TSocket *         const userSocketP,
694                 uint16_t          const port,
695                 const char **     const errorP) {
696
697     if (userSocketP) {
698         *errorP = NULL;
699         srvP->serverAcceptsConnections = TRUE;
700         srvP->socketBound = TRUE;
701         srvP->listenSocketP = userSocketP;
702     } else if (noAccept) {
703         *errorP = NULL;
704         srvP->serverAcceptsConnections = FALSE;
705         srvP->socketBound = FALSE;
706     } else {
707         *errorP = NULL;
708         srvP->serverAcceptsConnections = TRUE;
709         srvP->socketBound = FALSE;
710         srvP->port = port;
711     }
712     srvP->weCreatedListenSocket = FALSE;
713 }
714
715
716
717 static void
718 createServer(struct _TServer ** const srvPP,
719              abyss_bool         const noAccept,
720              TSocket *          const userSocketP,
721              uint16_t           const portNumber,             
722              const char **      const errorP) {
723
724     struct _TServer * srvP;
725
726     MALLOCVAR(srvP);
727
728     if (srvP == NULL) {
729         xmlrpc_asprintf(errorP,
730                         "Unable to allocate space for server descriptor");
731     } else {
732         srvP->terminationRequested = false;
733
734         initSocketStuff(srvP, noAccept, userSocketP, portNumber, errorP);
735
736         if (!*errorP) {
737             srvP->defaulthandler = ServerDefaultHandlerFunc;
738
739             srvP->name             = strdup("unnamed");
740             srvP->filespath        = strdup(DEFAULT_DOCS);
741             srvP->logfilename      = NULL;
742             srvP->keepalivetimeout = 15;
743             srvP->keepalivemaxconn = 30;
744             srvP->timeout          = 15;
745             srvP->advertise        = TRUE;
746             srvP->mimeTypeP        = NULL;
747             srvP->useSigchld       = FALSE;
748             
749             initUnixStuff(srvP);
750
751             ListInitAutoFree(&srvP->handlers);
752             ListInitAutoFree(&srvP->defaultfilenames);
753
754             srvP->logfileisopen = FALSE;
755
756             *errorP = NULL;
757         }        
758         if (*errorP)
759             free(srvP);
760     }
761     *srvPP = srvP;
762 }
763
764
765
766 static void
767 setNamePathLog(TServer *    const serverP,
768                const char * const name,
769                const char * const filesPath,
770                const char * const logFileName) {
771 /*----------------------------------------------------------------------------
772    This odd function exists to help with backward compatibility.
773    Today, we have the expandable model where you create a server with
774    default parameters, then use ServerSet... functions to choose
775    non-default parameters.  But before, you specified these three
776    parameters right in the arguments of various create functions.
777 -----------------------------------------------------------------------------*/
778     if (name)
779         ServerSetName(serverP, name);
780     if (filesPath)
781         ServerSetFilesPath(serverP, filesPath);
782     if (logFileName)
783         ServerSetLogFileName(serverP, logFileName);
784 }
785
786
787
788 abyss_bool
789 ServerCreate(TServer *    const serverP,
790              const char * const name,
791              uint16_t     const portNumber,
792              const char * const filesPath,
793              const char * const logFileName) {
794
795     abyss_bool const noAcceptFalse = FALSE;
796
797     abyss_bool success;
798     const char * error;
799
800     createServer(&serverP->srvP, noAcceptFalse, NULL, portNumber, &error);
801
802     if (error) {
803         TraceMsg(error);
804         xmlrpc_strfree(error);
805         success = FALSE;
806     } else {
807         success = TRUE;
808     
809         setNamePathLog(serverP, name, filesPath, logFileName);
810     }
811
812     return success;
813 }
814
815
816
817 static void
818 createSocketFromOsSocket(TOsSocket    const osSocket,
819                          TSocket **   const socketPP) {
820
821 #ifdef WIN32
822     SocketWinCreateWinsock(osSocket, socketPP);
823 #else
824     SocketUnixCreateFd(osSocket, socketPP);
825 #endif
826 }
827
828
829
830 abyss_bool
831 ServerCreateSocket(TServer *    const serverP,
832                    const char * const name,
833                    TOsSocket    const socketFd,
834                    const char * const filesPath,
835                    const char * const logFileName) {
836
837     abyss_bool success;
838     TSocket * socketP;
839
840     createSocketFromOsSocket(socketFd, &socketP);
841
842     if (socketP) {
843         abyss_bool const noAcceptFalse = FALSE;
844
845         const char * error;
846
847         createServer(&serverP->srvP, noAcceptFalse, socketP, 0, &error);
848
849         if (error) {
850             TraceMsg(error);
851             success = FALSE;
852             xmlrpc_strfree(error);
853         } else {
854             success = TRUE;
855             
856             setNamePathLog(serverP, name, filesPath, logFileName);
857         }
858     } else
859         success = FALSE;
860
861     return success;
862 }
863
864
865
866 abyss_bool
867 ServerCreateNoAccept(TServer *    const serverP,
868                      const char * const name,
869                      const char * const filesPath,
870                      const char * const logFileName) {
871
872     abyss_bool const noAcceptTrue = TRUE;
873
874     abyss_bool success;
875     const char * error;
876
877     createServer(&serverP->srvP, noAcceptTrue, NULL, 0, &error);
878
879     if (error) {
880         TraceMsg(error);
881         success = FALSE;
882         xmlrpc_strfree(error);
883     } else {
884         success = TRUE;
885         
886         setNamePathLog(serverP, name, filesPath, logFileName);
887     }
888     return success;
889 }
890
891
892
893 void
894 ServerCreateSocket2(TServer *     const serverP,
895                     TSocket *     const socketP,
896                     const char ** const errorP) {
897     
898     abyss_bool const noAcceptFalse = FALSE;
899
900     assert(socketP);
901
902     createServer(&serverP->srvP, noAcceptFalse, socketP, 0, errorP);
903 }
904
905
906
907 static void
908 terminateHandlers(TList * const handlersP) {
909 /*----------------------------------------------------------------------------
910    Terminate all handlers in the list '*handlersP'.
911
912    I.e. call each handler's terminate function.
913 -----------------------------------------------------------------------------*/
914     if (handlersP->item) {
915         unsigned int i;
916         for (i = handlersP->size; i > 0; --i) {
917             URIHandler2 * const handlerP = handlersP->item[i-1];
918             if (handlerP->term)
919                 handlerP->term(handlerP->userdata);
920         }
921     }
922 }
923
924
925
926 void
927 ServerFree(TServer * const serverP) {
928
929     struct _TServer * const srvP = serverP->srvP;
930
931     if (srvP->weCreatedListenSocket)
932         SocketDestroy(srvP->listenSocketP);
933
934     xmlrpc_strfree(srvP->name);
935
936     xmlrpc_strfree(srvP->filespath);
937     
938     ListFree(&srvP->defaultfilenames);
939
940     terminateHandlers(&srvP->handlers);
941
942     ListFree(&srvP->handlers);
943
944     logClose(srvP);
945
946     if (srvP->logfilename)
947         xmlrpc_strfree(srvP->logfilename);
948
949     free(srvP);
950 }
951
952
953
954 void
955 ServerSetName(TServer *    const serverP,
956               const char * const name) {
957
958     xmlrpc_strfree(serverP->srvP->name);
959     
960     serverP->srvP->name = strdup(name);
961 }
962
963
964
965 void
966 ServerSetFilesPath(TServer *    const serverP,
967                    const char * const filesPath) {
968
969     xmlrpc_strfree(serverP->srvP->filespath);
970     
971     serverP->srvP->filespath = strdup(filesPath);
972 }
973
974
975
976 void
977 ServerSetLogFileName(TServer *    const serverP,
978                      const char * const logFileName) {
979     
980     struct _TServer * const srvP = serverP->srvP;
981
982     if (srvP->logfilename)
983         xmlrpc_strfree(srvP->logfilename);
984     
985     srvP->logfilename = strdup(logFileName);
986 }
987
988
989
990 void
991 ServerSetKeepaliveTimeout(TServer * const serverP,
992                           uint32_t  const keepaliveTimeout) {
993
994     serverP->srvP->keepalivetimeout = keepaliveTimeout;
995 }
996
997
998
999 void
1000 ServerSetKeepaliveMaxConn(TServer * const serverP,
1001                           uint32_t  const keepaliveMaxConn) {
1002
1003     serverP->srvP->keepalivemaxconn = keepaliveMaxConn;
1004 }
1005
1006
1007
1008 void
1009 ServerSetTimeout(TServer * const serverP,
1010                  uint32_t  const timeout) {
1011
1012     serverP->srvP->timeout = timeout;
1013 }
1014
1015
1016
1017 void
1018 ServerSetAdvertise(TServer *  const serverP,
1019                    abyss_bool const advertise) {
1020
1021     serverP->srvP->advertise = advertise;
1022 }
1023
1024
1025
1026 void
1027 ServerSetMimeType(TServer *  const serverP,
1028                   MIMEType * const MIMETypeP) {
1029
1030     serverP->srvP->mimeTypeP = MIMETypeP;
1031 }
1032
1033
1034
1035 static void
1036 runUserHandler(TSession *        const sessionP,
1037                struct _TServer * const srvP) {
1038
1039     abyss_bool handled;
1040     int i;
1041     
1042     for (i = srvP->handlers.size-1, handled = FALSE;
1043          i >= 0 && !handled;
1044          --i) {
1045         URIHandler2 * const handlerP = srvP->handlers.item[i];
1046         
1047         if (handlerP->handleReq2)
1048             handlerP->handleReq2(handlerP, sessionP, &handled);
1049         else if (handlerP->handleReq1)
1050             handled = handlerP->handleReq1(sessionP);
1051     }
1052     
1053     if (!handled)
1054         ((URIHandler)(srvP->defaulthandler))(sessionP);
1055 }
1056
1057
1058
1059 static void
1060 processDataFromClient(TConn *      const connectionP,
1061                       abyss_bool   const lastReqOnConn,
1062                       abyss_bool * const keepAliveP) {
1063
1064     TSession session;
1065
1066     RequestInit(&session, connectionP);
1067
1068     session.serverDeniesKeepalive = lastReqOnConn;
1069         
1070     RequestRead(&session);
1071     if (session.status == 0) {
1072         if (session.version.major >= 2)
1073             ResponseStatus(&session, 505);
1074         else if (!RequestValidURI(&session))
1075             ResponseStatus(&session, 400);
1076         else
1077             runUserHandler(&session, connectionP->server->srvP);
1078     }
1079     assert(session.status != 0);
1080     
1081     if (session.responseStarted)
1082         HTTPWriteEndChunk(&session);
1083     else
1084         ResponseError(&session);
1085
1086     *keepAliveP = HTTPKeepalive(&session);
1087     
1088     SessionLog(&session);
1089
1090     RequestFree(&session);
1091 }
1092
1093
1094 static TThreadProc serverFunc;
1095
1096 static void
1097 serverFunc(void * const userHandle) {
1098 /*----------------------------------------------------------------------------
1099    Do server stuff on one connection.  At its simplest, this means do
1100    one HTTP request.  But with keepalive, it can be many requests.
1101 -----------------------------------------------------------------------------*/
1102     TConn *           const connectionP = userHandle;
1103     struct _TServer * const srvP = connectionP->server->srvP;
1104
1105     unsigned int requestCount;
1106         /* Number of requests we've handled so far on this connection */
1107     abyss_bool connectionDone;
1108         /* No more need for this HTTP connection */
1109
1110     requestCount = 0;
1111     connectionDone = FALSE;
1112
1113     while (!connectionDone) {
1114         abyss_bool success;
1115         
1116         /* Wait to read until timeout */
1117         success = ConnRead(connectionP, srvP->keepalivetimeout);
1118
1119         if (!success)
1120             connectionDone = TRUE;
1121         else {
1122             abyss_bool const lastReqOnConn =
1123                 requestCount + 1 >= srvP->keepalivemaxconn;
1124
1125             abyss_bool keepalive;
1126             
1127             processDataFromClient(connectionP, lastReqOnConn, &keepalive);
1128             
1129             ++requestCount;
1130
1131             if (!keepalive)
1132                 connectionDone = TRUE;
1133             
1134             /**************** Must adjust the read buffer *****************/
1135             ConnReadInit(connectionP);
1136         }
1137     }
1138 }
1139
1140
1141
1142 static void
1143 createAndBindSocket(struct _TServer * const srvP) {
1144
1145     abyss_bool success;
1146
1147     success = SocketInit();
1148     if (!success)
1149         TraceMsg("Can't initialize TCP sockets");
1150     else {
1151         TSocket * socketP;
1152         
1153         SocketUnixCreate(&socketP);
1154         
1155         if (!socketP)
1156             TraceMsg("Can't create a socket");
1157         else {
1158             abyss_bool success;
1159             
1160             success = SocketBind(socketP, NULL, srvP->port);
1161             
1162             if (!success)
1163                 TraceMsg("Failed to bind listening socket to port number %u",
1164                          srvP->port);
1165             else {
1166                 srvP->weCreatedListenSocket = TRUE;
1167                 srvP->socketBound = TRUE;
1168                 srvP->listenSocketP = socketP;
1169             }
1170             if (!success)
1171                 SocketDestroy(socketP);
1172         }
1173     }
1174 }
1175
1176
1177
1178 void
1179 ServerInit(TServer * const serverP) {
1180 /*----------------------------------------------------------------------------
1181    Initialize a server to accept connections.
1182
1183    Do not confuse this with creating the server -- ServerCreate().
1184
1185    Not necessary or valid with a server that doesn't accept connections (i.e.
1186    user supplies the TCP connections).
1187 -----------------------------------------------------------------------------*/
1188     struct _TServer * const srvP = serverP->srvP;
1189     abyss_bool success;
1190     
1191     if (!srvP->serverAcceptsConnections) {
1192         TraceMsg("ServerInit() is not valid on a server that doesn't "
1193                  "accept connections "
1194                  "(i.e. created with ServerCreateNoAccept)");
1195         success = FALSE;
1196     } else {
1197         if (!srvP->socketBound)
1198             createAndBindSocket(srvP);
1199
1200         if (srvP->socketBound) {
1201             success = SocketListen(srvP->listenSocketP, MAX_CONN);
1202
1203             if (!success)
1204                 TraceMsg("Failed to listen on bound socket.");
1205         } else
1206             success = FALSE;
1207     }
1208     if (!success)
1209         exit(1);
1210 }
1211
1212
1213
1214 /* We don't do any locking on the outstanding connections list, so 
1215    we must make sure that only the master thread (the one that listens
1216    for connections) ever accesses it.
1217
1218    That's why when a thread completes, it places the connection in
1219    "finished" status, but doesn't destroy the connection.
1220 */
1221
1222 typedef struct {
1223
1224     TConn * firstP;
1225     unsigned int count;
1226         /* Redundant with 'firstP', for quick access */
1227 } outstandingConnList;
1228
1229
1230
1231 static void
1232 createOutstandingConnList(outstandingConnList ** const listPP) {
1233
1234     outstandingConnList * listP;
1235
1236     MALLOCVAR_NOFAIL(listP);
1237
1238     listP->firstP = NULL;  /* empty list */
1239     listP->count = 0;
1240
1241     *listPP = listP;
1242 }
1243
1244
1245
1246 static void
1247 destroyOutstandingConnList(outstandingConnList * const listP) {
1248
1249     assert(listP->firstP == NULL);
1250     assert(listP->count == 0);
1251
1252     free(listP);
1253 }
1254
1255
1256
1257 static void
1258 addToOutstandingConnList(outstandingConnList * const listP,
1259                          TConn *               const connectionP) {
1260
1261     connectionP->nextOutstandingP = listP->firstP;
1262
1263     listP->firstP = connectionP;
1264
1265     ++listP->count;
1266 }
1267
1268
1269
1270 static void
1271 freeFinishedConns(outstandingConnList * const listP) {
1272 /*----------------------------------------------------------------------------
1273    Garbage-collect the resources associated with connections that are
1274    finished with their jobs.  Thread resources, connection pool
1275    descriptor, etc.
1276 -----------------------------------------------------------------------------*/
1277     TConn ** pp;
1278
1279     pp = &listP->firstP;
1280
1281     while (*pp) {
1282         TConn * const connectionP = (*pp);
1283
1284         ThreadUpdateStatus(connectionP->threadP);
1285         
1286         if (connectionP->finished) {
1287             /* Take it out of the list */
1288             *pp = connectionP->nextOutstandingP;
1289             --listP->count;
1290             
1291             ConnWaitAndRelease(connectionP);
1292         } else {
1293             /* Move to next connection in list */
1294             pp = &connectionP->nextOutstandingP;
1295         }
1296     }
1297 }
1298
1299
1300
1301 static void
1302 waitForConnectionFreed(outstandingConnList * const outstandingConnListP
1303                        ATTR_UNUSED) {
1304 /*----------------------------------------------------------------------------
1305   Wait for a connection descriptor in 'connectionPool' to be probably
1306   freed.
1307 -----------------------------------------------------------------------------*/
1308
1309     /* TODO: We should do something more sophisticated here.  For pthreads,
1310        we can have a thread signal us by semaphore when it terminates.
1311        For fork, we might be able to use the "done" handler argument
1312        to ConnCreate() to get interrupted when the death of a child
1313        signal happens.
1314     */
1315
1316     xmlrpc_millisecond_sleep(2000);
1317 }
1318
1319
1320
1321 static void
1322 waitForNoConnections(outstandingConnList * const outstandingConnListP) {
1323
1324     while (outstandingConnListP->firstP) {
1325         freeFinishedConns(outstandingConnListP);
1326     
1327         if (outstandingConnListP->firstP)
1328             waitForConnectionFreed(outstandingConnListP);
1329     }
1330 }
1331
1332
1333
1334 static void
1335 waitForConnectionCapacity(outstandingConnList * const outstandingConnListP) {
1336 /*----------------------------------------------------------------------------
1337    Wait until there are fewer than the maximum allowed connections in
1338    progress.
1339 -----------------------------------------------------------------------------*/
1340     while (outstandingConnListP->count >= MAX_CONN) {
1341         freeFinishedConns(outstandingConnListP);
1342         if (outstandingConnListP->firstP)
1343             waitForConnectionFreed(outstandingConnListP);
1344     }
1345 }
1346
1347
1348
1349 #ifndef WIN32
1350 void
1351 ServerHandleSigchld(pid_t const pid) {
1352
1353     ThreadHandleSigchld(pid);
1354 }
1355 #endif
1356
1357
1358
1359 void
1360 ServerUseSigchld(TServer * const serverP) {
1361
1362     struct _TServer * const srvP = serverP->srvP;
1363     
1364     srvP->useSigchld = TRUE;
1365 }
1366
1367
1368
1369 TThreadDoneFn destroySocket;
1370
1371 static void
1372 destroyConnSocket(void * const userHandle) {
1373 /*----------------------------------------------------------------------------
1374    This is a "connection done" function for the connection the server
1375    serves.  It gets called some time after the connection has done its
1376    thing.  Its job is to clean up stuff the server created for use by
1377    the connection, but the server can't clean up because the
1378    connection might be processed asynchronously in a background
1379    thread.
1380
1381    To wit, we destroy the connection's socket.
1382 -----------------------------------------------------------------------------*/
1383     TConn * const connectionP = userHandle;
1384
1385     SocketDestroy(connectionP->socketP);
1386 }
1387
1388
1389
1390 static void 
1391 serverRun2(TServer * const serverP) {
1392
1393     struct _TServer * const srvP = serverP->srvP;
1394     outstandingConnList * outstandingConnListP;
1395
1396     createOutstandingConnList(&outstandingConnListP);
1397
1398     while (!srvP->terminationRequested) {
1399         TConn * connectionP;
1400
1401         abyss_bool connected;
1402         abyss_bool failed;
1403         TSocket * connectedSocketP;
1404         TIPAddr peerIpAddr;
1405
1406         SocketAccept(srvP->listenSocketP,
1407                      &connected, &failed,
1408                      &connectedSocketP, &peerIpAddr);
1409         
1410         if (connected) {
1411             const char * error;
1412
1413             freeFinishedConns(outstandingConnListP);
1414
1415             waitForConnectionCapacity(outstandingConnListP);
1416
1417             ConnCreate(&connectionP, serverP, connectedSocketP,
1418                        &serverFunc, &destroyConnSocket, ABYSS_BACKGROUND,
1419                        srvP->useSigchld,
1420                        &error);
1421             if (!error) {
1422                 addToOutstandingConnList(outstandingConnListP, connectionP);
1423                 ConnProcess(connectionP);
1424                 /* When connection is done (which could be later, courtesy
1425                    of a background thread), destroyConnSocket() will
1426                    destroy *connectedSocketP.
1427                 */
1428             } else {
1429                 xmlrpc_strfree(error);
1430                 SocketDestroy(connectedSocketP);
1431             }
1432         } else if (failed)
1433             TraceMsg("Socket Error=%d", SocketError(srvP->listenSocketP));
1434     }
1435     waitForNoConnections(outstandingConnListP);
1436
1437     destroyOutstandingConnList(outstandingConnListP);
1438 }
1439
1440
1441
1442 void 
1443 ServerRun(TServer * const serverP) {
1444
1445     struct _TServer * const srvP = serverP->srvP;
1446
1447     if (!srvP->socketBound)
1448         TraceMsg("This server is not set up to accept connections "
1449                  "on its own, so you can't use ServerRun().  "
1450                  "Try ServerRunConn() or ServerInit()");
1451     else
1452         serverRun2(serverP);
1453 }
1454
1455
1456
1457 static void
1458 serverRunConn(TServer * const serverP,
1459               TSocket * const connectedSocketP) {
1460 /*----------------------------------------------------------------------------
1461    Do the HTTP transaction on the TCP connection on the socket
1462    'connectedSocketP'.
1463    (socket must be in connected state, with nothing having been read or
1464    written on the connection yet).
1465 -----------------------------------------------------------------------------*/
1466     struct _TServer * const srvP = serverP->srvP;
1467
1468     TConn * connectionP;
1469     const char * error;
1470
1471     srvP->keepalivemaxconn = 1;
1472
1473     ConnCreate(&connectionP, 
1474                serverP, connectedSocketP,
1475                &serverFunc, NULL, ABYSS_FOREGROUND, srvP->useSigchld,
1476                &error);
1477     if (error) {
1478         TraceMsg("Couldn't create HTTP connection out of "
1479                  "connected socket.  %s", error);
1480         xmlrpc_strfree(error);
1481     } else {
1482         ConnProcess(connectionP);
1483
1484         ConnWaitAndRelease(connectionP);
1485     }
1486 }
1487
1488
1489
1490 void
1491 ServerRunConn2(TServer *     const serverP,
1492                TSocket *     const connectedSocketP,
1493                const char ** const errorP) {
1494 /*----------------------------------------------------------------------------
1495    Do the HTTP transaction on the TCP connection on the socket
1496    'connectedOsSocket'.
1497    (socket must be connected state, with nothing having been read or
1498    written on the connection yet).
1499 -----------------------------------------------------------------------------*/
1500     struct _TServer * const srvP = serverP->srvP;
1501
1502     if (srvP->serverAcceptsConnections)
1503         xmlrpc_asprintf(errorP,
1504                         "This server is configured to accept connections on "
1505                         "its own socket.  "
1506                         "Try ServerRun() or ServerCreateNoAccept().");
1507     else {
1508         serverRunConn(serverP, connectedSocketP);
1509         *errorP = NULL;
1510     }
1511 }
1512
1513
1514
1515 void
1516 ServerRunConn(TServer * const serverP,
1517               TOsSocket const connectedOsSocket) {
1518
1519     TSocket * socketP;
1520     createSocketFromOsSocket(connectedOsSocket, &socketP);
1521     if (!socketP)
1522         TraceExit("Unable to use supplied socket");
1523     else {
1524         const char * error;
1525
1526         ServerRunConn2(serverP, socketP, &error);
1527
1528         if (error) {
1529             TraceExit("Failed to run server on connection on file "
1530                       "descriptor %d.  %s", connectedOsSocket, error);
1531             xmlrpc_strfree(error);
1532         }
1533         SocketDestroy(socketP);
1534     }
1535 }
1536
1537
1538
1539 void
1540 ServerRunOnce(TServer * const serverP) {
1541 /*----------------------------------------------------------------------------
1542    Accept a connection from the listening socket and do the HTTP
1543    transaction that comes over it.
1544
1545    If no connection is presently waiting on the listening socket, wait
1546    for one.  But return immediately if we receive a signal during the
1547    wait.
1548 -----------------------------------------------------------------------------*/
1549     struct _TServer * const srvP = serverP->srvP;
1550
1551     if (!srvP->socketBound)
1552         TraceMsg("This server is not set up to accept connections "
1553                  "on its own, so you can't use ServerRunOnce().  "
1554                  "Try ServerRunConn() or ServerInit()");
1555     else {
1556         abyss_bool connected;
1557         abyss_bool failed;
1558         TSocket *  connectedSocketP;
1559         TIPAddr    remoteAddr;
1560     
1561         srvP->keepalivemaxconn = 1;
1562
1563         SocketAccept(srvP->listenSocketP,
1564                      &connected, &failed,
1565                      &connectedSocketP, &remoteAddr);
1566         if (connected) {
1567             serverRunConn(serverP, connectedSocketP);
1568             SocketDestroy(connectedSocketP);
1569         } else if (failed)
1570             TraceMsg("Socket Error=%d", SocketError(srvP->listenSocketP));
1571     }
1572 }
1573
1574
1575
1576 void
1577 ServerRunOnce2(TServer *           const serverP,
1578                enum abyss_foreback const foregroundBackground ATTR_UNUSED) {
1579 /*----------------------------------------------------------------------------
1580    This is a backward compatibility interface to ServerRunOnce().
1581
1582    'foregroundBackground' is meaningless.  We always process the
1583    connection in the foreground.  The parameter exists because we once
1584    thought we could do them in the background, but we really can't do
1585    that in any clean way.  If Caller wants background execution, he can
1586    spin his own thread or process to call us.  It makes much more sense
1587    in Caller's context.
1588 -----------------------------------------------------------------------------*/
1589     ServerRunOnce(serverP);
1590 }
1591
1592
1593
1594 static void
1595 setGroups(void) {
1596
1597 #ifdef HAVE_SETGROUPS   
1598     if (setgroups(0, NULL) == (-1))
1599         TraceExit("Failed to setup the group.");
1600 #endif
1601 }
1602
1603
1604
1605 void
1606 ServerDaemonize(TServer * const serverP) {
1607 /*----------------------------------------------------------------------------
1608    Turn Caller into a daemon (i.e. fork a child, then exit; the child
1609    returns to Caller).
1610
1611    NOTE: It's ridiculous, but conventional, for us to do this.  It's
1612    ridiculous because the task of daemonizing is not something
1613    particular to Abyss.  It ought to be done by a higher level.  In
1614    fact, it should be done before the Abyss server program is even
1615    execed.  The user should run a "daemonize" program that creates a
1616    daemon which execs the Abyss server program.
1617 -----------------------------------------------------------------------------*/
1618     struct _TServer * const srvP = serverP->srvP;
1619
1620 #ifndef _WIN32
1621     /* Become a daemon */
1622     switch (fork()) {
1623     case 0:
1624         break;
1625     case -1:
1626         TraceExit("Unable to become a daemon");
1627     default:
1628         /* We are the parent */
1629         exit(0);
1630     }
1631     
1632     setsid();
1633
1634     /* Change the current user if we are root */
1635     if (getuid()==0) {
1636         if (srvP->uid == (uid_t)-1)
1637             TraceExit("Can't run under root privileges.  "
1638                       "Please add a User option in your "
1639                       "Abyss configuration file.");
1640
1641         setGroups();
1642
1643         if (srvP->gid != (gid_t)-1)
1644             if (setgid(srvP->gid)==(-1))
1645                 TraceExit("Failed to change the group.");
1646         
1647         if (setuid(srvP->uid) == -1)
1648             TraceExit("Failed to change the user.");
1649     }
1650     
1651     if (srvP->pidfile != -1) {
1652         char z[16];
1653     
1654         sprintf(z, "%d", getpid());
1655         FileWrite(&srvP->pidfile, z, strlen(z));
1656         FileClose(&srvP->pidfile);
1657     }
1658 #endif  /* _WIN32 */
1659 }
1660
1661
1662
1663 void
1664 ServerAddHandler2(TServer *     const serverP,
1665                   URIHandler2 * const handlerArgP,
1666                   abyss_bool *  const successP) {
1667
1668     URIHandler2 * handlerP;
1669
1670     MALLOCVAR(handlerP);
1671     if (handlerP == NULL)
1672         *successP = FALSE;
1673     else {
1674         *handlerP = *handlerArgP;
1675
1676         if (handlerP->init == NULL)
1677             *successP = TRUE;
1678         else
1679             handlerP->init(handlerP, successP);
1680
1681         if (*successP)
1682             *successP = ListAdd(&serverP->srvP->handlers, handlerP);
1683
1684         if (!*successP)
1685             free(handlerP);
1686     }
1687 }
1688
1689
1690
1691 static URIHandler2 *
1692 createHandler(URIHandler const function) {
1693
1694     URIHandler2 * handlerP;
1695
1696     MALLOCVAR(handlerP);
1697     if (handlerP != NULL) {
1698         handlerP->init       = NULL;
1699         handlerP->term       = NULL;
1700         handlerP->userdata   = NULL;
1701         handlerP->handleReq2 = NULL;
1702         handlerP->handleReq1 = function;
1703     }
1704     return handlerP;
1705 }
1706
1707
1708
1709 abyss_bool
1710 ServerAddHandler(TServer *  const serverP,
1711                  URIHandler const function) {
1712
1713     URIHandler2 * handlerP;
1714     abyss_bool success;
1715
1716     handlerP = createHandler(function);
1717
1718     if (handlerP == NULL)
1719         success = FALSE;
1720     else {
1721         success = ListAdd(&serverP->srvP->handlers, handlerP);
1722
1723         if (!success)
1724             free(handlerP);
1725     }
1726     return success;
1727 }
1728
1729
1730
1731 void
1732 ServerDefaultHandler(TServer *  const serverP,
1733                      URIHandler const handler) {
1734
1735     serverP->srvP->defaulthandler =
1736         handler ? handler : ServerDefaultHandlerFunc;
1737 }
1738
1739
1740
1741 void
1742 LogWrite(TServer *    const serverP,
1743          const char * const msg) {
1744
1745     struct _TServer * const srvP = serverP->srvP;
1746
1747     if (!srvP->logfileisopen && srvP->logfilename)
1748         logOpen(srvP);
1749
1750     if (srvP->logfileisopen) {
1751         abyss_bool success;
1752         success = MutexLock(&srvP->logmutex);
1753         if (success) {
1754             const char * const lbr = "\n";
1755             FileWrite(&srvP->logfile, msg, strlen(msg));
1756             FileWrite(&srvP->logfile, lbr, strlen(lbr));
1757         
1758             MutexUnlock(&srvP->logmutex);
1759         }
1760     }
1761 }
1762 /*******************************************************************************
1763 **
1764 ** server.c
1765 **
1766 ** This file is part of the ABYSS Web server project.
1767 **
1768 ** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
1769 ** All rights reserved.
1770 **
1771 ** Redistribution and use in source and binary forms, with or without
1772 ** modification, are permitted provided that the following conditions
1773 ** are met:
1774 ** 1. Redistributions of source code must retain the above copyright
1775 **    notice, this list of conditions and the following disclaimer.
1776 ** 2. Redistributions in binary form must reproduce the above copyright
1777 **    notice, this list of conditions and the following disclaimer in the
1778 **    documentation and/or other materials provided with the distribution.
1779 ** 3. The name of the author may not be used to endorse or promote products
1780 **    derived from this software without specific prior written permission.
1781 ** 
1782 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1783 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1784 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1785 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1786 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1787 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1788 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1789 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1790 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1791 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1792 ** SUCH DAMAGE.
1793 **
1794 ******************************************************************************/