Bug Summary

File:src/mod/xml_int/mod_xml_rpc/../../../../libs/xmlrpc-c/lib/abyss/src/server.c
Location:line 464, column 5
Description:Function call argument is an uninitialized value

Annotated Source Code

1/* Copyright information is at end of file */
2
3#define _XOPEN_SOURCE700 600 /* Make sure strdup() is in <string.h> */
4#define _BSD_SOURCE1 /* Make sure setgroups()is in <grp.h> */
5#ifndef _DEFAULT_SOURCE1
6#define _DEFAULT_SOURCE1
7#endif
8
9#include <assert.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <time.h>
14#include <errno(*__errno_location ()).h>
15#ifndef WIN32
16 #include <grp.h>
17#endif
18
19#include "xmlrpc_config.h"
20#include "bool.h"
21#include "girmath.h"
22#include "mallocvar.h"
23#include "xmlrpc-c/string_int.h"
24#include "xmlrpc-c/sleep_int.h"
25
26#include "xmlrpc-c/abyss.h"
27#include "trace.h"
28#include "session.h"
29#include "file.h"
30#include "conn.h"
31#include "chanswitch.h"
32#include "channel.h"
33#include "socket.h"
34#ifdef WIN32
35 #include "socket_win.h"
36#else
37 #include "socket_unix.h"
38#endif
39#include "http.h"
40#include "handler.h"
41
42#include "server.h"
43
44
45struct uriHandler {
46 initHandlerFn init;
47 termHandlerFn term;
48 handleReq3Fn handleReq3;
49 handleReq2Fn handleReq2;
50 URIHandler handleReq1;
51 void * userdata;
52};
53
54
55
56void
57ServerTerminate(TServer * const serverP) {
58
59 struct _TServer * const srvP = serverP->srvP;
60
61 srvP->terminationRequested = true;
62
63 if (srvP->chanSwitchP) {
64 ChanSwitchInterrupt(srvP->chanSwitchP);
65 }
66}
67
68
69
70void
71ServerResetTerminate(TServer * const serverP) {
72
73 struct _TServer * const srvP = serverP->srvP;
74
75 srvP->terminationRequested = false;
76}
77
78
79
80static void
81initUnixStuff(struct _TServer * const srvP) {
82#ifndef WIN32
83 srvP->pidfileP = NULL((void*)0);
84 srvP->uid = srvP->gid = -1;
85#endif
86}
87
88
89
90static bool
91logOpen(struct _TServer * const srvP) {
92
93 bool success;
94
95 success = FileOpenCreate(&srvP->logfileP, srvP->logfilename,
96 O_WRONLY01 | O_APPEND02000);
97 if (success) {
98 bool success;
99 success = MutexCreate(&srvP->logmutexP);
100 if (success)
101 srvP->logfileisopen = TRUE1;
102 else
103 TraceMsg("Can't create mutex for log file");
104
105 if (!success)
106 FileClose(srvP->logfileP);
107 } else
108 TraceMsg("Can't open log file '%s'", srvP->logfilename);
109
110 return success;
111}
112
113
114
115static void
116logClose(struct _TServer * const srvP) {
117
118 if (srvP->logfileisopen) {
119 FileClose(srvP->logfileP);
120 MutexDestroy(srvP->logmutexP);
121 srvP->logfileisopen = FALSE0;
122 }
123}
124
125
126
127static void
128initChanSwitchStuff(struct _TServer * const srvP,
129 bool const noAccept,
130 TChanSwitch * const chanSwitchP,
131 bool const userChanSwitch,
132 unsigned short const port,
133 const char ** const errorP) {
134
135
136 if (chanSwitchP) {
6
Taking false branch
137 *errorP = NULL((void*)0);
138 srvP->serverAcceptsConnections = TRUE1;
139 srvP->chanSwitchP = chanSwitchP;
140 srvP->weCreatedChanSwitch = !userChanSwitch;
141 } else if (noAccept) {
7
Taking true branch
142 *errorP = NULL((void*)0);
143 srvP->serverAcceptsConnections = FALSE0;
144 srvP->chanSwitchP = NULL((void*)0);
145 srvP->weCreatedChanSwitch = FALSE0;
146 } else {
147 *errorP = NULL((void*)0);
148 srvP->serverAcceptsConnections = TRUE1;
149 srvP->chanSwitchP = NULL((void*)0);
150 srvP->weCreatedChanSwitch = FALSE0;
151 srvP->port = port;
152 }
153}
154
155
156
157static void
158createServer(struct _TServer ** const srvPP,
159 bool const noAccept,
160 TChanSwitch * const chanSwitchP,
161 bool const userChanSwitch,
162 unsigned short const portNumber,
163 const char ** const errorP) {
164
165 struct _TServer * srvP;
166
167 MALLOCVAR(srvP)srvP = malloc(sizeof(*srvP));
2
Within the expansion of the macro 'MALLOCVAR':
a
Uninitialized value stored to field 'name'
168
169 if (srvP == NULL((void*)0)) {
3
Assuming 'srvP' is not equal to null
4
Taking false branch
170 xmlrpc_asprintf(errorP,
171 "Unable to allocate space for server descriptor");
172 } else {
173 srvP->terminationRequested = false;
174
175 initChanSwitchStuff(srvP, noAccept, chanSwitchP, userChanSwitch,
5
Calling 'initChanSwitchStuff'
8
Returning from 'initChanSwitchStuff'
176 portNumber, errorP);
177
178 if (!*errorP) {
9
Taking true branch
179 srvP->builtinHandlerP = HandlerCreate();
180 if (!srvP->builtinHandlerP)
10
Taking true branch
181 xmlrpc_asprintf(errorP, "Unable to allocate space for "
182 "builtin handler descriptor");
183 else {
184 srvP->defaultHandler = HandlerDefaultBuiltin;
185 srvP->defaultHandlerContext = srvP->builtinHandlerP;
186
187 srvP->name = strdup("unnamed")(__extension__ (__builtin_constant_p ("unnamed") && (
(size_t)(const void *)(("unnamed") + 1) - (size_t)(const void
*)("unnamed") == 1) ? (((const char *) ("unnamed"))[0] == '\0'
? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len
= strlen ("unnamed") + 1; char *__retval = (char *) malloc (
__len); if (__retval != ((void*)0)) __retval = (char *) memcpy
(__retval, "unnamed", __len); __retval; })) : __strdup ("unnamed"
)))
;
188 srvP->logfilename = NULL((void*)0);
189 srvP->keepalivetimeout = 15;
190 srvP->keepalivemaxconn = 30;
191 srvP->timeout = 15;
192 srvP->advertise = TRUE1;
193 srvP->useSigchld = FALSE0;
194 srvP->uriHandlerStackSize = 0;
195
196 initUnixStuff(srvP);
197
198 ListInitAutoFree(&srvP->handlers);
199
200 srvP->logfileisopen = FALSE0;
201
202 *errorP = NULL((void*)0);
203
204 if (*errorP)
205 HandlerDestroy(srvP->builtinHandlerP);
206 }
207 }
208 if (*errorP)
11
Taking false branch
209 free(srvP);
210 }
211 *srvPP = srvP;
212}
213
214
215
216static void
217setNamePathLog(TServer * const serverP,
218 const char * const name,
219 const char * const filesPath,
220 const char * const logFileName) {
221/*----------------------------------------------------------------------------
222 This odd function exists to help with backward compatibility.
223 Today, we have the expandable model where you create a server with
224 default parameters, then use ServerSet... functions to choose
225 non-default parameters. But before, you specified these three
226 parameters right in the arguments of various create functions.
227-----------------------------------------------------------------------------*/
228 if (name)
15
Assuming 'name' is non-null
16
Taking true branch
229 ServerSetName(serverP, name);
17
Calling 'ServerSetName'
230 if (filesPath)
231 ServerSetFilesPath(serverP, filesPath);
232 if (logFileName)
233 ServerSetLogFileName(serverP, logFileName);
234}
235
236
237
238abyss_bool
239ServerCreate(TServer * const serverP,
240 const char * const name,
241 xmlrpc_uint16_t const portNumber,
242 const char * const filesPath,
243 const char * const logFileName) {
244
245 bool const noAcceptFalse = FALSE0;
246 bool const userChanSwitchFalse = FALSE0;
247
248 bool success;
249 const char * error;
250
251 createServer(&serverP->srvP, noAcceptFalse,
252 NULL((void*)0), userChanSwitchFalse,
253 portNumber, &error);
254
255 if (error) {
256 TraceMsg(error);
257 xmlrpc_strfree(error);
258 success = FALSE0;
259 } else {
260 success = TRUE1;
261
262 setNamePathLog(serverP, name, filesPath, logFileName);
263 }
264
265 return success;
266}
267
268
269
270static void
271createSwitchFromOsSocket(TOsSocket const osSocket,
272 TChanSwitch ** const chanSwitchPP,
273 const char ** const errorP) {
274
275#ifdef WIN32
276 ChanSwitchWinCreateWinsock(osSocket, chanSwitchPP, errorP);
277#else
278 ChanSwitchUnixCreateFd(osSocket, chanSwitchPP, errorP);
279#endif
280}
281
282
283
284static void
285createChannelFromOsSocket(TOsSocket const osSocket,
286 TChannel ** const channelPP,
287 void ** const channelInfoPP,
288 const char ** const errorP) {
289
290#ifdef WIN32
291 ChannelWinCreateWinsock(osSocket, channelPP,
292 (struct abyss_win_chaninfo**)channelInfoPP,
293 errorP);
294#else
295 ChannelUnixCreateFd(osSocket, channelPP,
296 (struct abyss_unix_chaninfo**)channelInfoPP,
297 errorP);
298#endif
299}
300
301
302
303abyss_bool
304ServerCreateSocket(TServer * const serverP,
305 const char * const name,
306 TOsSocket const socketFd,
307 const char * const filesPath,
308 const char * const logFileName) {
309
310 bool success;
311 TChanSwitch * chanSwitchP;
312 const char * error;
313
314 createSwitchFromOsSocket(socketFd, &chanSwitchP, &error);
315
316 if (error) {
317 TraceMsg(error);
318 success = FALSE0;
319 xmlrpc_strfree(error);
320 } else {
321 bool const noAcceptFalse = FALSE0;
322 bool const userChanSwitchFalse = FALSE0;
323
324 const char * error;
325
326 createServer(&serverP->srvP, noAcceptFalse,
327 chanSwitchP, userChanSwitchFalse,
328 0, &error);
329
330 if (error) {
331 TraceMsg(error);
332 success = FALSE0;
333 xmlrpc_strfree(error);
334 } else {
335 success = TRUE1;
336
337 setNamePathLog(serverP, name, filesPath, logFileName);
338 }
339 if (!success)
340 ChanSwitchDestroy(chanSwitchP);
341 }
342
343 return success;
344}
345
346
347
348abyss_bool
349ServerCreateNoAccept(TServer * const serverP,
350 const char * const name,
351 const char * const filesPath,
352 const char * const logFileName) {
353
354 bool const noAcceptTrue = TRUE1;
355 bool const userChanSwitchFalse = FALSE0;
356
357 bool success;
358 const char * error;
359
360 createServer(&serverP->srvP, noAcceptTrue,
1
Calling 'createServer'
12
Returning from 'createServer'
361 NULL((void*)0), userChanSwitchFalse,
362 0, &error);
363
364 if (error) {
13
Taking false branch
365 TraceMsg(error);
366 success = FALSE0;
367 xmlrpc_strfree(error);
368 } else {
369 success = TRUE1;
370
371 setNamePathLog(serverP, name, filesPath, logFileName);
14
Calling 'setNamePathLog'
372 }
373 return success;
374}
375
376
377
378void
379ServerCreateSwitch(TServer * const serverP,
380 TChanSwitch * const chanSwitchP,
381 const char ** const errorP) {
382
383 bool const noAcceptFalse = FALSE0;
384 bool const userChanSwitchTrue = TRUE1;
385
386 assert(serverP)((serverP) ? (void) (0) : __assert_fail ("serverP", "../../../../libs/xmlrpc-c/lib/abyss/src/server.c"
, 386, __PRETTY_FUNCTION__))
;
387 assert(chanSwitchP)((chanSwitchP) ? (void) (0) : __assert_fail ("chanSwitchP", "../../../../libs/xmlrpc-c/lib/abyss/src/server.c"
, 387, __PRETTY_FUNCTION__))
;
388
389 createServer(&serverP->srvP, noAcceptFalse,
390 chanSwitchP, userChanSwitchTrue,
391 0, errorP);
392}
393
394
395
396void
397ServerCreateSocket2(TServer * const serverP,
398 TSocket * const socketP,
399 const char ** const errorP) {
400
401 TChanSwitch * const chanSwitchP = SocketGetChanSwitch(socketP);
402
403 assert(socketP)((socketP) ? (void) (0) : __assert_fail ("socketP", "../../../../libs/xmlrpc-c/lib/abyss/src/server.c"
, 403, __PRETTY_FUNCTION__))
;
404
405 if (!chanSwitchP)
406 xmlrpc_asprintf(
407 errorP, "Socket is not a listening socket. "
408 "You should use ServerCreateSwitch() instead, anyway.");
409 else
410 ServerCreateSwitch(serverP, chanSwitchP, errorP);
411}
412
413
414
415static void
416terminateHandlers(TList * const handlersP) {
417/*----------------------------------------------------------------------------
418 Terminate all handlers in the list '*handlersP'.
419
420 I.e. call each handler's terminate function.
421-----------------------------------------------------------------------------*/
422 if (handlersP->item) {
423 unsigned int i;
424 for (i = handlersP->size; i > 0; --i) {
425 struct uriHandler * const handlerP = handlersP->item[i-1];
426 if (handlerP->term)
427 handlerP->term(handlerP->userdata);
428 }
429 }
430}
431
432
433
434void
435ServerFree(TServer * const serverP) {
436
437 struct _TServer * const srvP = serverP->srvP;
438
439 if (srvP->weCreatedChanSwitch && srvP->chanSwitchP)
440 ChanSwitchDestroy(srvP->chanSwitchP);
441
442 xmlrpc_strfree(srvP->name);
443
444 terminateHandlers(&srvP->handlers);
445
446 ListFree(&srvP->handlers);
447
448 HandlerDestroy(srvP->builtinHandlerP);
449
450 logClose(srvP);
451
452 if (srvP->logfilename)
453 xmlrpc_strfree(srvP->logfilename);
454
455 free(srvP);
456}
457
458
459
460void
461ServerSetName(TServer * const serverP,
462 const char * const name) {
463
464 xmlrpc_strfree(serverP->srvP->name);
18
Function call argument is an uninitialized value
465
466 serverP->srvP->name = strdup(name)(__extension__ (__builtin_constant_p (name) && ((size_t
)(const void *)((name) + 1) - (size_t)(const void *)(name) ==
1) ? (((const char *) (name))[0] == '\0' ? (char *) calloc (
(size_t) 1, (size_t) 1) : ({ size_t __len = strlen (name) + 1
; char *__retval = (char *) malloc (__len); if (__retval != (
(void*)0)) __retval = (char *) memcpy (__retval, name, __len)
; __retval; })) : __strdup (name)))
;
467}
468
469
470
471void
472ServerSetFilesPath(TServer * const serverP,
473 const char * const filesPath) {
474
475 HandlerSetFilesPath(serverP->srvP->builtinHandlerP, filesPath);
476}
477
478
479
480void
481ServerSetLogFileName(TServer * const serverP,
482 const char * const logFileName) {
483
484 struct _TServer * const srvP = serverP->srvP;
485
486 if (srvP->logfilename)
487 xmlrpc_strfree(srvP->logfilename);
488
489 srvP->logfilename = strdup(logFileName)(__extension__ (__builtin_constant_p (logFileName) &&
((size_t)(const void *)((logFileName) + 1) - (size_t)(const void
*)(logFileName) == 1) ? (((const char *) (logFileName))[0] ==
'\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len
= strlen (logFileName) + 1; char *__retval = (char *) malloc
(__len); if (__retval != ((void*)0)) __retval = (char *) memcpy
(__retval, logFileName, __len); __retval; })) : __strdup (logFileName
)))
;
490}
491
492
493
494void
495ServerSetKeepaliveTimeout(TServer * const serverP,
496 xmlrpc_uint32_t const keepaliveTimeout) {
497
498 serverP->srvP->keepalivetimeout = MAX(keepaliveTimeout, 1)((keepaliveTimeout) > (1) ? (keepaliveTimeout) : (1));
499}
500
501
502
503void
504ServerSetKeepaliveMaxConn(TServer * const serverP,
505 xmlrpc_uint32_t const keepaliveMaxConn) {
506
507 serverP->srvP->keepalivemaxconn = MAX(keepaliveMaxConn, 1)((keepaliveMaxConn) > (1) ? (keepaliveMaxConn) : (1));
508}
509
510
511
512void
513ServerSetTimeout(TServer * const serverP,
514 xmlrpc_uint32_t const timeout) {
515
516 serverP->srvP->timeout = timeout;
517}
518
519
520
521void
522ServerSetAdvertise(TServer * const serverP,
523 abyss_bool const advertise) {
524
525 serverP->srvP->advertise = advertise;
526}
527
528
529
530void
531ServerSetMimeType(TServer * const serverP,
532 MIMEType * const mimeTypeP) {
533
534 HandlerSetMimeType(serverP->srvP->builtinHandlerP, mimeTypeP);
535}
536
537
538
539static URIHandler2
540makeUriHandler2(const struct uriHandler * const handlerP) {
541
542 URIHandler2 retval;
543
544 retval.init = handlerP->init;
545 retval.term = handlerP->term;
546 retval.handleReq2 = handlerP->handleReq2;
547 retval.handleReq1 = handlerP->handleReq1;
548 retval.userdata = handlerP->userdata;
549
550 return retval;
551}
552
553
554
555static void
556runUserHandler(TSession * const sessionP,
557 struct _TServer * const srvP) {
558
559 abyss_bool handled;
560 int i;
561
562 for (i = srvP->handlers.size-1, handled = FALSE0;
563 i >= 0 && !handled;
564 --i) {
565 const struct uriHandler * const handlerP = srvP->handlers.item[i];
566
567 if (handlerP->handleReq3)
568 handlerP->handleReq3(handlerP->userdata, sessionP, &handled);
569 if (handlerP->handleReq2) {
570 URIHandler2 handler2 = makeUriHandler2(handlerP);
571 handlerP->handleReq2(&handler2, sessionP, &handled);
572 } else if (handlerP->handleReq1)
573 handled = handlerP->handleReq1(sessionP);
574 }
575
576 assert(srvP->defaultHandler)((srvP->defaultHandler) ? (void) (0) : __assert_fail ("srvP->defaultHandler"
, "../../../../libs/xmlrpc-c/lib/abyss/src/server.c", 576, __PRETTY_FUNCTION__
))
;
577
578 if (!handled)
579 srvP->defaultHandler(sessionP);
580}
581
582
583
584static void
585handleReqTooNewHttpVersion(TSession * const sessionP) {
586
587 const char * msg;
588
589 ResponseStatus(sessionP, 505);
590
591 xmlrpc_asprintf(&msg, "Request is in HTTP Version %u"
592 "We understand only HTTP 1",
593 sessionP->version.major);
594
595 ResponseError2(sessionP, msg);
596
597 xmlrpc_strfree(msg);
598}
599
600
601
602static void
603handleReqInvalidURI(TSession * const sessionP) {
604
605 ResponseStatus(sessionP, 400);
606
607 ResponseError2(sessionP, "Invalid URI");
608}
609
610
611
612static void
613processRequestFromClient(TConn * const connectionP,
614 bool const lastReqOnConn,
615 uint32_t const timeout,
616 bool * const keepAliveP) {
617/*----------------------------------------------------------------------------
618 Get and execute one HTTP request from client connection *connectionP,
619 through the connection buffer. I.e. Some of the request may already be in
620 the connection buffer, and we may leave some of later requests in the
621 connection buffer.
622
623 In fact, due to timing considerations, we assume the client has begun
624 sending the request, which as a practical matter means Caller has already
625 deposited some of it in the connection buffer.
626
627 If there isn't one full request in the buffer now, we wait for one full
628 request to come through the buffer, up to 'timeout'.
629
630 We return as *keepAliveP whether Caller should keep the connection
631 alive for a while for possible future requests from the client, based
632 on 'lastReqOnConn' and the content of the HTTP request.
633
634 Executing the request consists primarily of calling the URI handlers that
635 are associated with the connection (*connectionP), passing each the request
636 information we read. Each handler can respond according to the HTTP method
637 (GET, POST, etc) and URL etc, and that response may be either to
638 execute the request and send the response or refuse the request and let
639 us call the next one in the list.
640-----------------------------------------------------------------------------*/
641 TSession session;
642 const char * error;
643 uint16_t httpErrorCode;
644
645 RequestInit(&session, connectionP);
646
647 session.serverDeniesKeepalive = lastReqOnConn;
648
649 RequestRead(&session, timeout, &error, &httpErrorCode);
650
651 if (error) {
652 ResponseStatus(&session, httpErrorCode);
653 ResponseError2(&session, error);
654 xmlrpc_strfree(error);
655 } else {
656 if (session.version.major >= 2)
657 handleReqTooNewHttpVersion(&session);
658 else if (!RequestValidURI(&session))
659 handleReqInvalidURI(&session);
660 else
661 runUserHandler(&session, connectionP->server->srvP);
662 }
663
664 assert(session.status != 0)((session.status != 0) ? (void) (0) : __assert_fail ("session.status != 0"
, "../../../../libs/xmlrpc-c/lib/abyss/src/server.c", 664, __PRETTY_FUNCTION__
))
;
665
666 if (session.responseStarted)
667 HTTPWriteEndChunk(&session);
668 else
669 ResponseError(&session);
670
671 *keepAliveP = HTTPKeepalive(&session);
672
673 SessionLog(&session);
674
675 RequestFree(&session);
676}
677
678
679
680static TThreadProc serverFunc;
681
682static void
683serverFunc(void * const userHandle) {
684/*----------------------------------------------------------------------------
685 Do server stuff on one connection. At its simplest, this means do
686 one HTTP request. But with keepalive, it can be many requests.
687-----------------------------------------------------------------------------*/
688 TConn * const connectionP = userHandle;
689 struct _TServer * const srvP = connectionP->server->srvP;
690
691 unsigned int requestCount;
692 /* Number of requests we've handled so far on this connection */
693 bool connectionDone;
694 /* No more need for this HTTP connection */
695
696 requestCount = 0;
697 connectionDone = FALSE0;
698
699 while (!connectionDone) {
700 bool timedOut, eof;
701 const char * readError;
702
703 /* Wait for and get beginning (at least ) of next request. We do
704 this separately from getting the rest of the request because we
705 treat dead time between requests differently from dead time in
706 the middle of a request.
707 */
708 ConnRead(connectionP, srvP->keepalivetimeout,
709 &timedOut, &eof, &readError);
710
711 if (readError) {
712 TraceMsg("Failed to read from Abyss connection. %s", readError);
713 xmlrpc_strfree(readError);
714 connectionDone = TRUE1;
715 } else if (timedOut) {
716 connectionDone = TRUE1;
717 } else if (eof) {
718 connectionDone = TRUE1;
719 } else if (srvP->terminationRequested) {
720 connectionDone = TRUE1;
721 } else {
722 bool const lastReqOnConn =
723 requestCount + 1 >= srvP->keepalivemaxconn;
724
725 bool keepalive;
726
727 processRequestFromClient(connectionP, lastReqOnConn, srvP->timeout,
728 &keepalive);
729
730 ++requestCount;
731
732 if (!keepalive)
733 connectionDone = TRUE1;
734
735 /**************** Must adjust the read buffer *****************/
736 ConnReadInit(connectionP);
737 }
738 }
739}
740
741
742
743/* This is the maximum amount of stack space, in bytes, serverFunc()
744 itself requires -- not counting what the user's request handler
745 (which serverFunc() calls) requires.
746*/
747#define SERVER_FUNC_STACK1024 1024
748
749
750
751static void
752createSwitchFromPortNum(unsigned short const portNumber,
753 TChanSwitch ** const chanSwitchPP,
754 const char ** const errorP) {
755
756#ifdef WIN32
757 ChanSwitchWinCreate(portNumber, chanSwitchPP, errorP);
758#else
759 ChanSwitchUnixCreate(portNumber, chanSwitchPP, errorP);
760#endif
761}
762
763
764
765static void
766createChanSwitch(struct _TServer * const srvP,
767 const char ** const errorP) {
768
769 TChanSwitch * chanSwitchP;
770 const char * error;
771
772 /* Not valid to call this when channel switch already exists: */
773 assert(srvP->chanSwitchP == NULL)((srvP->chanSwitchP == ((void*)0)) ? (void) (0) : __assert_fail
("srvP->chanSwitchP == ((void*)0)", "../../../../libs/xmlrpc-c/lib/abyss/src/server.c"
, 773, __PRETTY_FUNCTION__))
;
774
775 createSwitchFromPortNum(srvP->port, &chanSwitchP, &error);
776
777 if (error) {
778 xmlrpc_asprintf(errorP,
779 "Can't create channel switch. %s", error);
780 xmlrpc_strfree(error);
781 } else {
782 *errorP = NULL((void*)0);
783
784 srvP->weCreatedChanSwitch = TRUE1;
785 srvP->chanSwitchP = chanSwitchP;
786 }
787}
788
789
790
791int
792ServerInit(TServer * const serverP) {
793/*----------------------------------------------------------------------------
794 Initialize a server to accept connections.
795
796 Do not confuse this with creating the server -- ServerCreate().
797
798 Not necessary or valid with a server that doesn't accept connections (i.e.
799 user supplies the channels (TCP connections)).
800-----------------------------------------------------------------------------*/
801 struct _TServer * const srvP = serverP->srvP;
802 const char * retError;
803
804 if (!srvP->serverAcceptsConnections)
805 xmlrpc_asprintf(&retError,
806 "ServerInit() is not valid on a server that doesn't "
807 "accept connections "
808 "(i.e. created with ServerCreateNoAccept)");
809 else {
810 retError = NULL((void*)0); /* initial value */
811
812 if (!srvP->chanSwitchP) {
813 const char * error;
814 createChanSwitch(srvP, &error);
815
816 if (error) {
817 xmlrpc_asprintf(&retError, "Unable to create a channel switch "
818 "for the server. %s", error);
819 xmlrpc_strfree(error);
820 }
821 }
822
823 if (!retError) {
824 const char * error;
825
826 assert(srvP->chanSwitchP)((srvP->chanSwitchP) ? (void) (0) : __assert_fail ("srvP->chanSwitchP"
, "../../../../libs/xmlrpc-c/lib/abyss/src/server.c", 826, __PRETTY_FUNCTION__
))
;
827
828 ChanSwitchListen(srvP->chanSwitchP, MAX_CONN16, &error);
829
830 if (error) {
831 xmlrpc_asprintf(&retError,
832 "Failed to listen on bound socket. %s",
833 error);
834 xmlrpc_strfree(error);
835 }
836 }
837 }
838 if (retError) {
839 TraceMsg("ServerInit() failed. %s", retError);
840 xmlrpc_strfree(retError);
841 return 0;
842 }
843
844 return 1;
845}
846
847
848
849/* We don't do any locking on the outstanding connections list, so
850 we must make sure that only the master thread (the one that listens
851 for connections) ever accesses it.
852
853 That's why when a thread completes, it places the connection in
854 "finished" status, but doesn't destroy the connection.
855*/
856
857typedef struct {
858
859 TConn * firstP;
860 unsigned int count;
861 /* Redundant with 'firstP', for quick access */
862} outstandingConnList;
863
864
865
866static void
867createOutstandingConnList(outstandingConnList ** const listPP) {
868
869 outstandingConnList * listP;
870
871 MALLOCVAR_NOFAIL(listP)do {if ((listP = malloc(sizeof(*listP))) == ((void*)0)) abort
();} while(0)
;
872
873 listP->firstP = NULL((void*)0); /* empty list */
874 listP->count = 0;
875
876 *listPP = listP;
877}
878
879
880
881static void
882destroyOutstandingConnList(outstandingConnList * const listP) {
883
884 assert(listP->firstP == NULL)((listP->firstP == ((void*)0)) ? (void) (0) : __assert_fail
("listP->firstP == ((void*)0)", "../../../../libs/xmlrpc-c/lib/abyss/src/server.c"
, 884, __PRETTY_FUNCTION__))
;
885 assert(listP->count == 0)((listP->count == 0) ? (void) (0) : __assert_fail ("listP->count == 0"
, "../../../../libs/xmlrpc-c/lib/abyss/src/server.c", 885, __PRETTY_FUNCTION__
))
;
886
887 free(listP);
888}
889
890
891
892static void
893addToOutstandingConnList(outstandingConnList * const listP,
894 TConn * const connectionP) {
895
896 connectionP->nextOutstandingP = listP->firstP;
897
898 listP->firstP = connectionP;
899
900 ++listP->count;
901}
902
903
904
905static void
906freeFinishedConns(outstandingConnList * const listP) {
907/*----------------------------------------------------------------------------
908 Garbage-collect the resources associated with connections that are
909 finished with their jobs. Thread resources, connection pool
910 descriptor, etc.
911-----------------------------------------------------------------------------*/
912 TConn ** pp;
913
914 pp = &listP->firstP;
915
916 while (*pp) {
917 TConn * const connectionP = (*pp);
918
919 ThreadUpdateStatus(connectionP->threadP);
920
921 if (connectionP->finished) {
922 /* Take it out of the list */
923 *pp = connectionP->nextOutstandingP;
924 --listP->count;
925
926 ConnWaitAndRelease(connectionP);
927 } else {
928 /* Move to next connection in list */
929 pp = &connectionP->nextOutstandingP;
930 }
931 }
932}
933
934
935
936static void
937waitForConnectionFreed(outstandingConnList * const outstandingConnListP
938 ATTR_UNUSED__attribute__((__unused__))) {
939/*----------------------------------------------------------------------------
940 Wait for a connection descriptor in 'connectionPool' to be probably
941 freed.
942-----------------------------------------------------------------------------*/
943
944 /* TODO: We should do something more sophisticated here. For pthreads,
945 we can have a thread signal us by semaphore when it terminates.
946 For fork, we might be able to use the "done" handler argument
947 to ConnCreate() to get interrupted when the death of a child
948 signal happens.
949 */
950
951 xmlrpc_millisecond_sleep(2000);
952}
953
954
955
956static void
957waitForNoConnections(outstandingConnList * const outstandingConnListP) {
958
959 while (outstandingConnListP->firstP) {
960 freeFinishedConns(outstandingConnListP);
961
962 if (outstandingConnListP->firstP)
963 waitForConnectionFreed(outstandingConnListP);
964 }
965}
966
967
968
969static void
970waitForConnectionCapacity(outstandingConnList * const outstandingConnListP) {
971/*----------------------------------------------------------------------------
972 Wait until there are fewer than the maximum allowed connections in
973 progress.
974-----------------------------------------------------------------------------*/
975 /* We need to make this number configurable. Note that MAX_CONN (16) is
976 also the backlog limit on the TCP socket, and they really aren't
977 related. As it stands, we can have 16 connections in progress inside
978 Abyss plus 16 waiting in the the channel switch.
979 */
980
981 while (outstandingConnListP->count >= MAX_CONN16) {
982 freeFinishedConns(outstandingConnListP);
983 if (outstandingConnListP->firstP)
984 waitForConnectionFreed(outstandingConnListP);
985 }
986}
987
988
989
990#ifndef WIN32
991void
992ServerHandleSigchld(pid_t const pid) {
993
994 ThreadHandleSigchld(pid);
995}
996#endif
997
998
999
1000void
1001ServerUseSigchld(TServer * const serverP) {
1002
1003 struct _TServer * const srvP = serverP->srvP;
1004
1005 srvP->useSigchld = TRUE1;
1006}
1007
1008
1009
1010static TThreadDoneFn destroyChannel;
1011
1012static void
1013destroyChannel(void * const userHandle) {
1014/*----------------------------------------------------------------------------
1015 This is a "connection done" function for the connection the server
1016 serves. It gets called some time after the connection has done its
1017 thing. Its job is to clean up stuff the server created for use by
1018 the connection, but the server can't clean up because the
1019 connection might be processed asynchronously in a background
1020 thread.
1021
1022 To wit, we destroy the connection's channel and release the memory
1023 for the information about that channel.
1024-----------------------------------------------------------------------------*/
1025 TConn * const connectionP = userHandle;
1026
1027 ChannelDestroy(connectionP->channelP);
1028 free(connectionP->channelInfoP);
1029}
1030
1031
1032
1033static void
1034acceptAndProcessNextConnection(
1035 TServer * const serverP,
1036 outstandingConnList * const outstandingConnListP) {
1037
1038 struct _TServer * const srvP = serverP->srvP;
1039
1040 TConn * connectionP;
1041 const char * error;
1042 TChannel * channelP;
1043 void * channelInfoP;
1044
1045 ChanSwitchAccept(srvP->chanSwitchP, &channelP, &channelInfoP, &error);
1046
1047 if (error) {
1048 TraceMsg("Failed to accept the next connection from a client "
1049 "at the channel level. %s", error);
1050 xmlrpc_strfree(error);
1051 } else {
1052 if (channelP) {
1053 const char * error;
1054
1055 freeFinishedConns(outstandingConnListP);
1056
1057 waitForConnectionCapacity(outstandingConnListP);
1058
1059 ConnCreate(&connectionP, serverP, channelP, channelInfoP,
1060 &serverFunc,
1061 SERVER_FUNC_STACK1024 + srvP->uriHandlerStackSize,
1062 &destroyChannel, ABYSS_BACKGROUND,
1063 srvP->useSigchld,
1064 &error);
1065 if (!error) {
1066 addToOutstandingConnList(outstandingConnListP,
1067 connectionP);
1068 ConnProcess(connectionP);
1069 /* When connection is done (which could be later, courtesy
1070 of a background thread), destroyChannel() will
1071 destroy *channelP.
1072 */
1073 } else {
1074 TraceMsg("Failed to create an Abyss connection "
1075 "out of new channel %lx. %s", channelP, error);
1076 xmlrpc_strfree(error);
1077 ChannelDestroy(channelP);
1078 free(channelInfoP);
1079 }
1080 } else {
1081 /* Accept function was interrupted before it got a connection */
1082 }
1083 }
1084}
1085
1086
1087
1088static void
1089serverRun2(TServer * const serverP) {
1090
1091 struct _TServer * const srvP = serverP->srvP;
1092 outstandingConnList * outstandingConnListP;
1093
1094 createOutstandingConnList(&outstandingConnListP);
1095
1096 while (!srvP->terminationRequested)
1097 acceptAndProcessNextConnection(serverP, outstandingConnListP);
1098
1099 waitForNoConnections(outstandingConnListP);
1100
1101 destroyOutstandingConnList(outstandingConnListP);
1102}
1103
1104
1105
1106void
1107ServerRun(TServer * const serverP) {
1108
1109 struct _TServer * const srvP = serverP->srvP;
1110
1111 if (!srvP->chanSwitchP)
1112 TraceMsg("This server is not set up to accept connections "
1113 "on its own, so you can't use ServerRun(). "
1114 "Try ServerRunConn() or ServerInit()");
1115 else
1116 serverRun2(serverP);
1117}
1118
1119
1120
1121static void
1122serverRunChannel(TServer * const serverP,
1123 TChannel * const channelP,
1124 void * const channelInfoP,
1125 const char ** const errorP) {
1126/*----------------------------------------------------------------------------
1127 Do the HTTP transaction on the channel 'channelP'.
1128 (channel must be at the beginning of the HTTP request -- nothing having
1129 been read or written yet).
1130
1131 channelInfoP == NULL means no channel info supplied.
1132-----------------------------------------------------------------------------*/
1133 struct _TServer * const srvP = serverP->srvP;
1134
1135 TConn * connectionP;
1136 const char * error;
1137
1138 srvP->keepalivemaxconn = 1;
1139
1140 ConnCreate(&connectionP,
1141 serverP, channelP, channelInfoP,
1142 &serverFunc, SERVER_FUNC_STACK1024 + srvP->uriHandlerStackSize,
1143 NULL((void*)0), ABYSS_FOREGROUND, srvP->useSigchld,
1144 &error);
1145 if (error) {
1146 xmlrpc_asprintf(errorP, "Couldn't create HTTP connection out of "
1147 "channel (connected socket). %s", error);
1148 xmlrpc_strfree(error);
1149 } else {
1150 *errorP = NULL((void*)0);
1151
1152 ConnProcess(connectionP);
1153
1154 ConnWaitAndRelease(connectionP);
1155 }
1156}
1157
1158
1159
1160void
1161ServerRunChannel(TServer * const serverP,
1162 TChannel * const channelP,
1163 void * const channelInfoP,
1164 const char ** const errorP) {
1165/*----------------------------------------------------------------------------
1166 Do the HTTP transaction on the channel 'channelP'.
1167
1168 (channel must be at the beginning of the HTTP request -- nothing having
1169 been read or written yet).
1170-----------------------------------------------------------------------------*/
1171 struct _TServer * const srvP = serverP->srvP;
1172
1173 if (srvP->serverAcceptsConnections)
1174 xmlrpc_asprintf(errorP,
1175 "This server is configured to accept connections on "
1176 "its own socket. "
1177 "Try ServerRun() or ServerCreateNoAccept().");
1178 else
1179 serverRunChannel(serverP, channelP, channelInfoP, errorP);
1180}
1181
1182
1183
1184void
1185ServerRunConn2(TServer * const serverP,
1186 TSocket * const connectedSocketP,
1187 const char ** const errorP) {
1188/*----------------------------------------------------------------------------
1189 Do the HTTP transaction on the TCP connection on the socket
1190 *connectedSocketP.
1191 (socket must be connected state, with nothing having been read or
1192 written on the connection yet).
1193-----------------------------------------------------------------------------*/
1194 TChannel * const channelP = SocketGetChannel(connectedSocketP);
1195
1196 if (!channelP)
1197 xmlrpc_asprintf(errorP, "The socket supplied is not a connected "
1198 "socket. You should use ServerRunChannel() instead, "
1199 "anyway.");
1200 else
1201 ServerRunChannel(serverP,
1202 channelP, SocketGetChannelInfo(connectedSocketP),
1203 errorP);
1204}
1205
1206
1207
1208void
1209ServerRunConn(TServer * const serverP,
1210 TOsSocket const connectedOsSocket) {
1211
1212 TChannel * channelP;
1213 void * channelInfoP;
1214 const char * error;
1215
1216 createChannelFromOsSocket(connectedOsSocket,
1217 &channelP, &channelInfoP, &error);
1218 if (error) {
1219 TraceExit("Unable to use supplied socket");
1220 xmlrpc_strfree(error);
1221 } else {
1222 const char * error;
1223
1224 ServerRunChannel(serverP, channelP, channelInfoP, &error);
1225
1226 if (error) {
1227 TraceExit("Failed to run server on connection on file "
1228 "descriptor %d. %s", connectedOsSocket, error);
1229 xmlrpc_strfree(error);
1230 }
1231 ChannelDestroy(channelP);
1232 free(channelInfoP);
1233 }
1234}
1235
1236
1237
1238void
1239ServerRunOnce(TServer * const serverP) {
1240/*----------------------------------------------------------------------------
1241 Accept a connection from the channel switch and do the HTTP
1242 transaction that comes over it.
1243
1244 If no connection is presently waiting at the switch, wait for one.
1245 But return immediately if we receive a signal during the wait.
1246-----------------------------------------------------------------------------*/
1247 struct _TServer * const srvP = serverP->srvP;
1248
1249 if (!srvP->chanSwitchP)
1250 TraceMsg("This server is not set up to accept connections "
1251 "on its own, so you can't use ServerRunOnce(). "
1252 "Try ServerRunChannel() or ServerInit()");
1253 else {
1254 const char * error;
1255 TChannel * channelP;
1256 void * channelInfoP;
1257
1258 srvP->keepalivemaxconn = 1;
1259
1260 ChanSwitchAccept(srvP->chanSwitchP, &channelP, &channelInfoP, &error);
1261 if (error) {
1262 TraceMsg("Failed to accept the next connection from a client "
1263 "at the channel level. %s", error);
1264 xmlrpc_strfree(error);
1265 } else {
1266 if (channelP) {
1267 const char * error;
1268
1269 serverRunChannel(serverP, channelP, channelInfoP, &error);
1270
1271 if (error) {
1272 const char * peerDesc;
1273 ChannelFormatPeerInfo(channelP, &peerDesc);
1274 TraceExit("Got a connection from '%s', but failed to "
1275 "run server on it. %s", peerDesc, error);
1276 xmlrpc_strfree(peerDesc);
1277 xmlrpc_strfree(error);
1278 }
1279 ChannelDestroy(channelP);
1280 free(channelInfoP);
1281 }
1282 }
1283 }
1284}
1285
1286
1287
1288void
1289ServerRunOnce2(TServer * const serverP,
1290 enum abyss_foreback const foregroundBackground ATTR_UNUSED__attribute__((__unused__))) {
1291/*----------------------------------------------------------------------------
1292 This is a backward compatibility interface to ServerRunOnce().
1293
1294 'foregroundBackground' is meaningless. We always process the
1295 connection in the foreground. The parameter exists because we once
1296 thought we could do them in the background, but we really can't do
1297 that in any clean way. If Caller wants background execution, he can
1298 spin his own thread or process to call us. It makes much more sense
1299 in Caller's context.
1300-----------------------------------------------------------------------------*/
1301 ServerRunOnce(serverP);
1302}
1303
1304
1305
1306static void
1307setGroups(void) {
1308
1309#if HAVE_SETGROUPS1
1310 if (setgroups(0, NULL((void*)0)) == (-1))
1311 TraceExit("Failed to setup the group.");
1312#endif
1313}
1314
1315
1316
1317void
1318ServerDaemonize(TServer * const serverP) {
1319/*----------------------------------------------------------------------------
1320 Turn Caller into a daemon (i.e. fork a child, then exit; the child
1321 returns to Caller).
1322
1323 NOTE: It's ridiculous, but conventional, for us to do this. It's
1324 ridiculous because the task of daemonizing is not something
1325 particular to Abyss. It ought to be done by a higher level. In
1326 fact, it should be done before the Abyss server program is even
1327 execed. The user should run a "daemonize" program that creates a
1328 daemon which execs the Abyss server program.
1329-----------------------------------------------------------------------------*/
1330 struct _TServer * const srvP = serverP->srvP;
1331
1332#ifndef _WIN32
1333 /* Become a daemon */
1334 switch (fork()) {
1335 case 0:
1336 break;
1337 case -1:
1338 TraceExit("Unable to become a daemon");
1339 default:
1340 /* We are the parent */
1341 exit(0);
1342 }
1343
1344 setsid();
1345
1346 /* Change the current user if we are root */
1347 if (getuid()==0) {
1348 if (srvP->uid == (uid_t)-1)
1349 TraceExit("Can't run under root privileges. "
1350 "Please add a User option in your "
1351 "Abyss configuration file.");
1352
1353 setGroups();
1354
1355 if (srvP->gid != (gid_t)-1)
1356 if (setgid(srvP->gid)==(-1))
1357 TraceExit("Failed to change the group.");
1358
1359 if (setuid(srvP->uid) == -1)
1360 TraceExit("Failed to change the user.");
1361 }
1362
1363 if (srvP->pidfileP) {
1364 char z[16];
1365
1366 sprintf(z, "%d", getpid());
1367 FileWrite(srvP->pidfileP, z, strlen(z));
1368 FileClose(srvP->pidfileP);
1369 }
1370#endif /* _WIN32 */
1371}
1372
1373
1374
1375static void
1376serverAddHandler(TServer * const serverP,
1377 initHandlerFn init,
1378 termHandlerFn term,
1379 URIHandler handleReq1,
1380 handleReq2Fn handleReq2,
1381 handleReq3Fn handleReq3,
1382 void * const userdata,
1383 size_t const handleReqStackSizeReq,
1384 abyss_bool * const successP) {
1385
1386 struct _TServer * const srvP = serverP->srvP;
1387 size_t handleReqStackSize =
1388 handleReqStackSizeReq ? handleReqStackSizeReq : 128*1024;
1389
1390 struct uriHandler * handlerP;
1391
1392 MALLOCVAR(handlerP)handlerP = malloc(sizeof(*handlerP));
1393 if (handlerP == NULL((void*)0))
1394 *successP = FALSE0;
1395 else {
1396 handlerP->init = init;
1397 handlerP->term = term;
1398 handlerP->handleReq1 = handleReq1;
1399 handlerP->handleReq2 = handleReq2;
1400 handlerP->handleReq3 = handleReq3;
1401 handlerP->userdata = userdata;
1402
1403 srvP->uriHandlerStackSize =
1404 MAX(srvP->uriHandlerStackSize, handleReqStackSize)((srvP->uriHandlerStackSize) > (handleReqStackSize) ? (
srvP->uriHandlerStackSize) : (handleReqStackSize))
;
1405
1406 if (handlerP->init == NULL((void*)0))
1407 *successP = TRUE1;
1408 else {
1409 URIHandler2 handler2 = makeUriHandler2(handlerP);
1410 handlerP->init(&handler2, successP);
1411 }
1412 if (*successP)
1413 *successP = ListAdd(&serverP->srvP->handlers, handlerP);
1414
1415 if (!*successP)
1416 free(handlerP);
1417 }
1418}
1419
1420
1421
1422void
1423ServerAddHandler3(TServer * const serverP,
1424 const struct ServerReqHandler3 * const handlerP,
1425 abyss_bool * const successP) {
1426
1427 serverAddHandler(serverP, NULL((void*)0), handlerP->term, NULL((void*)0), NULL((void*)0),
1428 handlerP->handleReq, handlerP->userdata,
1429 handlerP->handleReqStackSize, successP);
1430}
1431
1432
1433
1434void
1435ServerAddHandler2(TServer * const serverP,
1436 URIHandler2 * const handlerArgP,
1437 abyss_bool * const successP) {
1438
1439 /* This generation of the URI handler interface is strange because
1440 it went through an unfortunate evolution. So it halfway looks like
1441 the use supplies a handler object and Abyss calls its methods, and
1442 halfway looks like the user simply describes his handler.
1443
1444 Abyss calls handleReq2 with a pointer to a URIHandler2 like the
1445 one which is our argument, but it isn't the same one. User can
1446 discard *handlerArgP as soon as we return.
1447 */
1448
1449 serverAddHandler(serverP,
1450 handlerArgP->init,
1451 handlerArgP->term,
1452 handlerArgP->handleReq1,
1453 handlerArgP->handleReq2,
1454 NULL((void*)0),
1455 handlerArgP->userdata,
1456 0,
1457 successP);
1458}
1459
1460
1461
1462abyss_bool
1463ServerAddHandler(TServer * const serverP,
1464 URIHandler const function) {
1465
1466 URIHandler2 handler;
1467 abyss_bool success;
1468
1469 handler.init = NULL((void*)0);
1470 handler.term = NULL((void*)0);
1471 handler.userdata = NULL((void*)0);
1472 handler.handleReq2 = NULL((void*)0);
1473 handler.handleReq1 = function;
1474
1475 ServerAddHandler2(serverP, &handler, &success);
1476
1477 return success;
1478}
1479
1480
1481
1482/* This is the maximum amount of stack we allow a user's default URI
1483 handler to use. (If he exceeds this, results are undefined).
1484
1485 We really ought to provide user a way to set this, as he can for
1486 his non-default URI handlers.
1487*/
1488#define USER_DEFAULT_HANDLER_STACK128*1024 128*1024
1489
1490void
1491ServerDefaultHandler(TServer * const serverP,
1492 URIHandler const handler) {
1493
1494 struct _TServer * const srvP = serverP->srvP;
1495
1496 if (handler) {
1497 srvP->defaultHandler = handler;
1498 srvP->uriHandlerStackSize =
1499 MAX(srvP->uriHandlerStackSize, USER_DEFAULT_HANDLER_STACK)((srvP->uriHandlerStackSize) > (128*1024) ? (srvP->uriHandlerStackSize
) : (128*1024))
;
1500 } else {
1501 srvP->defaultHandler = HandlerDefaultBuiltin;
1502 srvP->defaultHandlerContext = srvP->builtinHandlerP;
1503 srvP->uriHandlerStackSize =
1504 MAX(srvP->uriHandlerStackSize, HandlerDefaultBuiltinStack)((srvP->uriHandlerStackSize) > (HandlerDefaultBuiltinStack
) ? (srvP->uriHandlerStackSize) : (HandlerDefaultBuiltinStack
))
;
1505 }
1506}
1507
1508
1509
1510void
1511LogWrite(TServer * const serverP,
1512 const char * const msg) {
1513
1514 struct _TServer * const srvP = serverP->srvP;
1515
1516 if (!srvP->logfileisopen && srvP->logfilename)
1517 logOpen(srvP);
1518
1519 if (srvP->logfileisopen) {
1520 bool success;
1521 success = MutexLock(srvP->logmutexP);
1522 if (success) {
1523 const char * const lbr = "\n";
1524 FileWrite(srvP->logfileP, msg, strlen(msg));
1525 FileWrite(srvP->logfileP, lbr, strlen(lbr));
1526
1527 MutexUnlock(srvP->logmutexP);
1528 }
1529 }
1530}
1531/*******************************************************************************
1532**
1533** server.c
1534**
1535** This file is part of the ABYSS Web server project.
1536**
1537** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
1538** All rights reserved.
1539**
1540** Redistribution and use in source and binary forms, with or without
1541** modification, are permitted provided that the following conditions
1542** are met:
1543** 1. Redistributions of source code must retain the above copyright
1544** notice, this list of conditions and the following disclaimer.
1545** 2. Redistributions in binary form must reproduce the above copyright
1546** notice, this list of conditions and the following disclaimer in the
1547** documentation and/or other materials provided with the distribution.
1548** 3. The name of the author may not be used to endorse or promote products
1549** derived from this software without specific prior written permission.
1550**
1551** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1552** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1553** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1554** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1555** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1556** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1557** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1558** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1559** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1560** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1561** SUCH DAMAGE.
1562**
1563******************************************************************************/