File: | src/mod/xml_int/mod_xml_rpc/../../../../libs/xmlrpc-c/lib/abyss/src/conn.c |
Location: | line 493, column 17 |
Description: | Function call argument is an uninitialized value |
1 | /* Copyright information is at the end of the file. */ | |||
2 | ||||
3 | #include <time.h> | |||
4 | #include <string.h> | |||
5 | #include <stdlib.h> | |||
6 | #include <stdio.h> | |||
7 | #include <ctype.h> | |||
8 | #include <assert.h> | |||
9 | ||||
10 | #include "bool.h" | |||
11 | #include "mallocvar.h" | |||
12 | #include "xmlrpc-c/util_int.h" | |||
13 | #include "xmlrpc-c/string_int.h" | |||
14 | #include "xmlrpc-c/sleep_int.h" | |||
15 | #include "xmlrpc-c/abyss.h" | |||
16 | #include "channel.h" | |||
17 | #include "server.h" | |||
18 | #include "thread.h" | |||
19 | #include "file.h" | |||
20 | ||||
21 | #include "conn.h" | |||
22 | ||||
23 | /********************************************************************* | |||
24 | ** Conn | |||
25 | *********************************************************************/ | |||
26 | ||||
27 | static TThreadProc connJob; | |||
28 | ||||
29 | static void | |||
30 | connJob(void * const userHandle) { | |||
31 | /*---------------------------------------------------------------------------- | |||
32 | This is the root function for a thread that processes a connection | |||
33 | (performs HTTP transactions). | |||
34 | ||||
35 | We never return. We ultimately exit the thread. | |||
36 | -----------------------------------------------------------------------------*/ | |||
37 | TConn * const connectionP = userHandle; | |||
38 | ||||
39 | (connectionP->job)(connectionP); | |||
40 | ||||
41 | connectionP->finished = TRUE1; | |||
42 | /* Note that if we are running in a forked process, setting | |||
43 | connectionP->finished has no effect, because it's just our own | |||
44 | copy of *connectionP. In this case, Parent must update his own | |||
45 | copy based on a SIGCHLD signal that the OS will generate right | |||
46 | after we exit. | |||
47 | */ | |||
48 | ||||
49 | ||||
50 | /* Note that ThreadExit() runs a cleanup function, which in our | |||
51 | case is connDone(). | |||
52 | */ | |||
53 | ThreadExit(connectionP->threadP, 0); | |||
54 | } | |||
55 | ||||
56 | ||||
57 | ||||
58 | /* This is the maximum amount of stack that 'connJob' itself uses -- | |||
59 | does not count what user's connection job function uses. | |||
60 | */ | |||
61 | #define CONNJOB_STACK1024 1024 | |||
62 | ||||
63 | ||||
64 | static void | |||
65 | connDone(TConn * const connectionP) { | |||
66 | ||||
67 | /* In the forked case, this is designed to run in the parent | |||
68 | process after the child has terminated. | |||
69 | */ | |||
70 | connectionP->finished = TRUE1; | |||
71 | ||||
72 | if (connectionP->done) | |||
73 | connectionP->done(connectionP); | |||
74 | } | |||
75 | ||||
76 | ||||
77 | ||||
78 | static TThreadDoneFn threadDone; | |||
79 | ||||
80 | static void | |||
81 | threadDone(void * const userHandle) { | |||
82 | ||||
83 | TConn * const connectionP = userHandle; | |||
84 | ||||
85 | connDone(connectionP); | |||
86 | } | |||
87 | ||||
88 | ||||
89 | ||||
90 | static void | |||
91 | makeThread(TConn * const connectionP, | |||
92 | enum abyss_foreback const foregroundBackground, | |||
93 | bool const useSigchld, | |||
94 | size_t const jobStackSize, | |||
95 | const char ** const errorP) { | |||
96 | ||||
97 | switch (foregroundBackground) { | |||
98 | case ABYSS_FOREGROUND: | |||
99 | connectionP->hasOwnThread = FALSE0; | |||
100 | *errorP = NULL((void*)0); | |||
101 | break; | |||
102 | case ABYSS_BACKGROUND: { | |||
103 | const char * error; | |||
104 | connectionP->hasOwnThread = TRUE1; | |||
105 | ThreadCreate(&connectionP->threadP, connectionP, | |||
106 | &connJob, &threadDone, useSigchld, | |||
107 | CONNJOB_STACK1024 + jobStackSize, | |||
108 | &error); | |||
109 | if (error) { | |||
110 | xmlrpc_asprintf(errorP, "Unable to create thread to " | |||
111 | "process connection. %s", error); | |||
112 | xmlrpc_strfree(error); | |||
113 | } else | |||
114 | *errorP = NULL((void*)0); | |||
115 | } break; | |||
116 | } /* switch */ | |||
117 | } | |||
118 | ||||
119 | ||||
120 | ||||
121 | void | |||
122 | ConnCreate(TConn ** const connectionPP, | |||
123 | TServer * const serverP, | |||
124 | TChannel * const channelP, | |||
125 | void * const channelInfoP, | |||
126 | TThreadProc * const job, | |||
127 | size_t const jobStackSize, | |||
128 | TThreadDoneFn * const done, | |||
129 | enum abyss_foreback const foregroundBackground, | |||
130 | bool const useSigchld, | |||
131 | const char ** const errorP) { | |||
132 | /*---------------------------------------------------------------------------- | |||
133 | Create an HTTP connection. | |||
134 | ||||
135 | A connection carries one or more HTTP transactions (request/response). | |||
136 | ||||
137 | *channelP transports the requests and responses. | |||
138 | ||||
139 | The connection handles those HTTP requests. | |||
140 | ||||
141 | The connection handles the requests primarily by running the | |||
142 | function 'job' once. Some connections can do that autonomously, as | |||
143 | soon as the connection is created. Others don't until Caller | |||
144 | subsequently calls ConnProcess. Some connections complete the | |||
145 | processing before ConnProcess return, while others may run the | |||
146 | connection asynchronously to the creator, in the background, via a | |||
147 | TThread thread. 'foregroundBackground' determines which. | |||
148 | ||||
149 | 'job' calls methods of the connection to get requests and send | |||
150 | responses. | |||
151 | ||||
152 | Some time after the HTTP transactions are all done, 'done' gets | |||
153 | called in some context. | |||
154 | ||||
155 | 'channelInfoP' == NULL means no channel info supplied. | |||
156 | -----------------------------------------------------------------------------*/ | |||
157 | TConn * connectionP; | |||
158 | ||||
159 | MALLOCVAR(connectionP)connectionP = malloc(sizeof(*connectionP)); | |||
160 | ||||
161 | if (connectionP == NULL((void*)0)) | |||
162 | xmlrpc_asprintf(errorP, "Unable to allocate memory for a connection " | |||
163 | "descriptor."); | |||
164 | else { | |||
165 | connectionP->server = serverP; | |||
166 | connectionP->channelP = channelP; | |||
167 | connectionP->channelInfoP = channelInfoP; | |||
168 | connectionP->buffer.b[0] = '\0'; | |||
169 | connectionP->buffersize = 0; | |||
170 | connectionP->bufferpos = 0; | |||
171 | connectionP->finished = FALSE0; | |||
172 | connectionP->job = job; | |||
173 | connectionP->done = done; | |||
174 | connectionP->inbytes = 0; | |||
175 | connectionP->outbytes = 0; | |||
176 | connectionP->trace = getenv("ABYSS_TRACE_CONN"); | |||
177 | ||||
178 | makeThread(connectionP, foregroundBackground, useSigchld, | |||
179 | jobStackSize, errorP); | |||
180 | } | |||
181 | *connectionPP = connectionP; | |||
182 | } | |||
183 | ||||
184 | ||||
185 | ||||
186 | bool | |||
187 | ConnProcess(TConn * const connectionP) { | |||
188 | /*---------------------------------------------------------------------------- | |||
189 | Drive the main processing of a connection -- run the connection's | |||
190 | "job" function, which should read HTTP requests from the connection | |||
191 | and send HTTP responses. | |||
192 | ||||
193 | If we succeed, we guarantee the connection's "done" function will get | |||
194 | called some time after all processing is complete. It might be before | |||
195 | we return or some time after. If we fail, we guarantee the "done" | |||
196 | function will not be called. | |||
197 | -----------------------------------------------------------------------------*/ | |||
198 | bool retval; | |||
199 | ||||
200 | if (connectionP->hasOwnThread) { | |||
201 | /* There's a background thread to handle this connection. Set | |||
202 | it running. | |||
203 | */ | |||
204 | assert(connectionP->threadP)((connectionP->threadP) ? (void) (0) : __assert_fail ("connectionP->threadP" , "../../../../libs/xmlrpc-c/lib/abyss/src/conn.c", 204, __PRETTY_FUNCTION__ )); | |||
205 | retval = ThreadRun(connectionP->threadP); | |||
206 | } else { | |||
207 | /* No background thread. We just handle it here while Caller waits. */ | |||
208 | (connectionP->job)(connectionP); | |||
209 | connDone(connectionP); | |||
210 | retval = TRUE1; | |||
211 | } | |||
212 | return retval; | |||
213 | } | |||
214 | ||||
215 | ||||
216 | ||||
217 | void | |||
218 | ConnWaitAndRelease(TConn * const connectionP) { | |||
219 | ||||
220 | if (connectionP->hasOwnThread) { | |||
221 | assert(connectionP->threadP)((connectionP->threadP) ? (void) (0) : __assert_fail ("connectionP->threadP" , "../../../../libs/xmlrpc-c/lib/abyss/src/conn.c", 221, __PRETTY_FUNCTION__ )); | |||
222 | ThreadWaitAndRelease(connectionP->threadP); | |||
223 | } | |||
224 | free(connectionP); | |||
225 | } | |||
226 | ||||
227 | ||||
228 | ||||
229 | bool | |||
230 | ConnKill(TConn * const connectionP) { | |||
231 | connectionP->finished = TRUE1; | |||
232 | return ThreadKill(connectionP->threadP); | |||
233 | } | |||
234 | ||||
235 | ||||
236 | ||||
237 | void | |||
238 | ConnReadInit(TConn * const connectionP) { | |||
239 | ||||
240 | if (connectionP->buffersize > connectionP->bufferpos) { | |||
241 | connectionP->buffersize -= connectionP->bufferpos; | |||
242 | memmove(connectionP->buffer.b, | |||
243 | connectionP->buffer.b + connectionP->bufferpos, | |||
244 | connectionP->buffersize); | |||
245 | connectionP->bufferpos = 0; | |||
246 | } else | |||
247 | connectionP->buffersize = connectionP->bufferpos = 0; | |||
248 | ||||
249 | connectionP->buffer.b[connectionP->buffersize] = '\0'; | |||
250 | ||||
251 | connectionP->inbytes = connectionP->outbytes = 0; | |||
252 | } | |||
253 | ||||
254 | ||||
255 | ||||
256 | static void | |||
257 | traceReadTimeout(TConn * const connectionP, | |||
258 | uint32_t const timeout) { | |||
259 | ||||
260 | if (connectionP->trace) | |||
261 | fprintf(stderrstderr, "TIMED OUT waiting over %u seconds " | |||
262 | "for data from client.\n", timeout); | |||
263 | } | |||
264 | ||||
265 | ||||
266 | ||||
267 | static size_t | |||
268 | nextLineSize(const char * const string, | |||
269 | size_t const startPos, | |||
270 | size_t const stringSize) { | |||
271 | /*---------------------------------------------------------------------------- | |||
272 | Return the length of the line that starts at offset 'startPos' in the | |||
273 | string 'string', which is 'stringSize' characters long. | |||
274 | ||||
275 | 'string' in not NUL-terminated. | |||
276 | ||||
277 | A line begins at beginning of string or after a newline character and | |||
278 | runs through the next newline character or end of string. The line | |||
279 | includes the newline character at the end, if any. | |||
280 | -----------------------------------------------------------------------------*/ | |||
281 | size_t i; | |||
282 | ||||
283 | for (i = startPos; i < stringSize && string[i] != '\n'; ++i); | |||
284 | ||||
285 | if (i < stringSize) | |||
286 | ++i; /* Include the newline */ | |||
287 | ||||
288 | return i - startPos; | |||
289 | } | |||
290 | ||||
291 | ||||
292 | ||||
293 | static void | |||
294 | traceBuffer(const char * const label, | |||
295 | const unsigned char * const buffer, | |||
296 | unsigned int const size) { | |||
297 | ||||
298 | const char * const buffer_t = (const char *)buffer; | |||
299 | ||||
300 | size_t cursor; /* Index into buffer[] */ | |||
301 | ||||
302 | fprintf(stderrstderr, "%s:\n\n", label); | |||
303 | ||||
304 | for (cursor = 0; cursor < size; ) { | |||
305 | /* Print one line of buffer */ | |||
306 | ||||
307 | size_t const lineSize = nextLineSize(buffer_t, cursor, size); | |||
308 | const char * const printableLine = | |||
309 | xmlrpc_makePrintable_lp(&buffer_t[cursor], lineSize); | |||
310 | ||||
311 | fprintf(stderrstderr, "%s\n", printableLine); | |||
312 | ||||
313 | cursor += lineSize; | |||
314 | ||||
315 | xmlrpc_strfree(printableLine); | |||
316 | } | |||
317 | fprintf(stderrstderr, "\n"); | |||
318 | } | |||
319 | ||||
320 | ||||
321 | ||||
322 | static void | |||
323 | traceBufferText(const char * const label, | |||
324 | const char * const buffer, | |||
325 | unsigned int const size) { | |||
326 | ||||
327 | traceBuffer(label, (const unsigned char *)buffer, size); | |||
328 | } | |||
329 | ||||
330 | ||||
331 | ||||
332 | static void | |||
333 | traceChannelRead(TConn * const connectionP, | |||
334 | unsigned int const size) { | |||
335 | ||||
336 | if (connectionP->trace) | |||
337 | traceBuffer("READ FROM CHANNEL", | |||
338 | connectionP->buffer.b + connectionP->buffersize, size); | |||
339 | } | |||
340 | ||||
341 | ||||
342 | ||||
343 | static void | |||
344 | traceChannelWrite(TConn * const connectionP, | |||
345 | const char * const buffer, | |||
346 | unsigned int const size, | |||
347 | bool const failed) { | |||
348 | ||||
349 | if (connectionP->trace) { | |||
350 | const char * const label = | |||
351 | failed ? "FAILED TO WRITE TO CHANNEL" : "WROTE TO CHANNEL"; | |||
352 | traceBufferText(label, buffer, size); | |||
353 | } | |||
354 | } | |||
355 | ||||
356 | ||||
357 | ||||
358 | static uint32_t | |||
359 | bufferSpace(TConn * const connectionP) { | |||
360 | ||||
361 | return BUFFER_SIZE4096 - connectionP->buffersize; | |||
362 | } | |||
363 | ||||
364 | ||||
365 | ||||
366 | static void | |||
367 | readFromChannel(TConn * const connectionP, | |||
368 | bool * const eofP, | |||
369 | const char ** const errorP) { | |||
370 | /*---------------------------------------------------------------------------- | |||
371 | Read some data from the channel of Connection *connectionP. | |||
372 | ||||
373 | Iff there is none available to read, return *eofP == true. | |||
374 | -----------------------------------------------------------------------------*/ | |||
375 | uint32_t bytesRead; | |||
376 | bool readError; | |||
377 | ||||
378 | ChannelRead(connectionP->channelP, | |||
379 | connectionP->buffer.b + connectionP->buffersize, | |||
380 | bufferSpace(connectionP) - 1, | |||
381 | &bytesRead, &readError); | |||
382 | ||||
383 | if (readError) | |||
384 | xmlrpc_asprintf(errorP, "Error reading from channel"); | |||
385 | else { | |||
386 | *errorP = NULL((void*)0); | |||
387 | if (bytesRead > 0) { | |||
388 | *eofP = FALSE0; | |||
389 | traceChannelRead(connectionP, bytesRead); | |||
390 | connectionP->inbytes += bytesRead; | |||
391 | connectionP->buffersize += bytesRead; | |||
392 | connectionP->buffer.t[connectionP->buffersize] = '\0'; | |||
393 | } else | |||
394 | *eofP = TRUE1; | |||
395 | } | |||
396 | } | |||
397 | ||||
398 | ||||
399 | ||||
400 | static void | |||
401 | dealWithReadTimeout(bool * const timedOutP, | |||
402 | bool const timedOut, | |||
403 | uint32_t const timeout, | |||
404 | const char ** const errorP) { | |||
405 | ||||
406 | if (timedOutP) | |||
407 | *timedOutP = timedOut; | |||
408 | else { | |||
409 | if (timedOut) | |||
410 | xmlrpc_asprintf(errorP, "Read from Abyss client " | |||
411 | "connection timed out after %u seconds " | |||
412 | "or was interrupted", | |||
413 | timeout); | |||
414 | } | |||
415 | } | |||
416 | ||||
417 | ||||
418 | ||||
419 | static void | |||
420 | dealWithReadEof(bool * const eofP, | |||
421 | bool const eof, | |||
422 | const char ** const errorP) { | |||
423 | ||||
424 | if (eofP) | |||
425 | *eofP = eof; | |||
426 | else { | |||
427 | if (eof) | |||
428 | xmlrpc_asprintf(errorP, "Read from Abyss client " | |||
429 | "connection failed because client closed the " | |||
430 | "connection"); | |||
431 | } | |||
432 | } | |||
433 | ||||
434 | ||||
435 | ||||
436 | void | |||
437 | ConnRead(TConn * const connectionP, | |||
438 | uint32_t const timeout, | |||
439 | bool * const eofP, | |||
440 | bool * const timedOutP, | |||
441 | const char ** const errorP) { | |||
442 | /*---------------------------------------------------------------------------- | |||
443 | Read some stuff on connection *connectionP from the channel. Read it into | |||
444 | the connection's buffer. | |||
445 | ||||
446 | Don't wait more than 'timeout' seconds for data to arrive. If no data has | |||
447 | arrived by then and 'timedOutP' is null, fail. If 'timedOut' is non-null, | |||
448 | return as *timedOutP whether 'timeout' seconds passed without any data | |||
449 | arriving. | |||
450 | ||||
451 | Also, stop waiting upon any interruption and treat it the same as a | |||
452 | timeout. An interruption is either a signal received (and caught) at | |||
453 | an appropriate time or a ChannelInterrupt() call before or during the | |||
454 | wait. | |||
455 | ||||
456 | If 'eofP' is non-null, return *eofP == true, without reading anything, iff | |||
457 | there will no more data forthcoming on the connection because client has | |||
458 | closed the connection. If 'eofP' is null, fail in that case. | |||
459 | -----------------------------------------------------------------------------*/ | |||
460 | uint32_t const timeoutMs = timeout * 1000; | |||
461 | ||||
462 | if (timeoutMs < timeout) | |||
| ||||
463 | /* Arithmetic overflow */ | |||
464 | xmlrpc_asprintf(errorP, "Timeout value is too large"); | |||
465 | else { | |||
466 | bool const waitForRead = TRUE1; | |||
467 | bool const waitForWrite = FALSE0; | |||
468 | ||||
469 | bool readyForRead; | |||
470 | bool failed; | |||
471 | ||||
472 | ChannelWait(connectionP->channelP, waitForRead, waitForWrite, | |||
473 | timeoutMs, &readyForRead, NULL((void*)0), &failed); | |||
474 | ||||
475 | if (failed) | |||
476 | xmlrpc_asprintf(errorP, | |||
477 | "Wait for stuff to arrive from client failed."); | |||
478 | else { | |||
479 | bool eof; | |||
480 | if (readyForRead) { | |||
481 | readFromChannel(connectionP, &eof, errorP); | |||
482 | } else { | |||
483 | /* Wait was interrupted, either by our requested timeout, | |||
484 | a (caught) signal, or a ChannelInterrupt(). | |||
485 | */ | |||
486 | traceReadTimeout(connectionP, timeout); | |||
487 | *errorP = NULL((void*)0); | |||
488 | eof = FALSE0; | |||
489 | } | |||
490 | if (!*errorP) | |||
491 | dealWithReadTimeout(timedOutP, !readyForRead, timeout, errorP); | |||
492 | if (!*errorP) | |||
493 | dealWithReadEof(eofP, eof, errorP); | |||
| ||||
494 | } | |||
495 | } | |||
496 | } | |||
497 | ||||
498 | ||||
499 | ||||
500 | bool | |||
501 | ConnWrite(TConn * const connectionP, | |||
502 | const void * const buffer, | |||
503 | uint32_t const size) { | |||
504 | ||||
505 | bool failed; | |||
506 | ||||
507 | ChannelWrite(connectionP->channelP, buffer, size, &failed); | |||
508 | ||||
509 | traceChannelWrite(connectionP, buffer, size, failed); | |||
510 | ||||
511 | if (!failed) | |||
512 | connectionP->outbytes += size; | |||
513 | ||||
514 | return !failed; | |||
515 | } | |||
516 | ||||
517 | ||||
518 | ||||
519 | bool | |||
520 | ConnWriteFromFile(TConn * const connectionP, | |||
521 | const TFile * const fileP, | |||
522 | uint64_t const start, | |||
523 | uint64_t const last, | |||
524 | void * const buffer, | |||
525 | uint32_t const buffersize, | |||
526 | uint32_t const rate) { | |||
527 | /*---------------------------------------------------------------------------- | |||
528 | Write the contents of the file stream *fileP, from offset 'start' | |||
529 | up through 'last', to the HTTP connection *connectionP. | |||
530 | ||||
531 | Meter the reading so as not to read more than 'rate' bytes per second. | |||
532 | ||||
533 | Use the 'bufferSize' bytes at 'buffer' as an internal buffer for this. | |||
534 | -----------------------------------------------------------------------------*/ | |||
535 | bool retval; | |||
536 | uint32_t waittime; | |||
537 | bool success; | |||
538 | uint32_t readChunkSize; | |||
539 | uint32_t ChunkSize = 4096 * 2; /* read buffer size */ | |||
540 | ||||
541 | if (rate > 0) { | |||
542 | readChunkSize = MIN(buffersize, rate)((buffersize) < (rate) ? (buffersize) : (rate)); /* One second's worth */ | |||
543 | waittime = (1000 * buffersize) / rate; | |||
544 | } else { | |||
545 | readChunkSize = ChunkSize; | |||
546 | waittime = 0; | |||
547 | } | |||
548 | ||||
549 | success = FileSeek(fileP, start, SEEK_SET0); | |||
550 | if (!success) | |||
551 | retval = FALSE0; | |||
552 | else { | |||
553 | uint64_t const totalBytesToRead = last - start + 1; | |||
554 | uint64_t bytesread = 0; | |||
555 | ||||
556 | int32_t bytesReadThisTime = 0; | |||
557 | char * chunk = (char *) buffer; /* the beginning */ | |||
558 | do { | |||
559 | ||||
560 | if ((bytesReadThisTime = FileRead(fileP, chunk, readChunkSize)) <= 0 ) | |||
561 | break; | |||
562 | ||||
563 | bytesread += bytesReadThisTime; | |||
564 | chunk += bytesReadThisTime; | |||
565 | ||||
566 | /* fix bug in ms ie as it doesn't render text/plain properly */ | |||
567 | /* if CRLFs are split between reassembled tcp packets, */ | |||
568 | /* ie "might" undeterministically render extra empty lines */ | |||
569 | /* if it ends in CR or LF, read an extra chunk until the buffer is full */ | |||
570 | /* or end of file is reached. You may still have bad luck, complaints go to MS) */ | |||
571 | ||||
572 | /* if (bytesReadThisTime == readChunkSize && chunk - (char *) buffer + readChunkSize < buffersize) { | |||
573 | * char * end = chunk - 1; | |||
574 | * if (*end == CR || *end == LF) { | |||
575 | * continue; | |||
576 | * } | |||
577 | * } | |||
578 | */ | |||
579 | if (!bytesReadThisTime || !ConnWrite(connectionP, buffer, chunk - (char *) buffer)) { | |||
580 | break; | |||
581 | } | |||
582 | ||||
583 | chunk = (char *) buffer; /* a new beginning */ | |||
584 | ||||
585 | if (waittime > 0) | |||
586 | xmlrpc_millisecond_sleep(waittime); | |||
587 | ||||
588 | } while (bytesReadThisTime == readChunkSize); | |||
589 | ||||
590 | retval = (bytesread >= totalBytesToRead); | |||
591 | } | |||
592 | return retval; | |||
593 | } | |||
594 | ||||
595 | ||||
596 | ||||
597 | TServer * | |||
598 | ConnServer(TConn * const connectionP) { | |||
599 | return connectionP->server; | |||
600 | } | |||
601 | ||||
602 | ||||
603 | ||||
604 | void | |||
605 | ConnFormatClientAddr(TConn * const connectionP, | |||
606 | const char ** const clientAddrPP) { | |||
607 | ||||
608 | ChannelFormatPeerInfo(connectionP->channelP, clientAddrPP); | |||
609 | } | |||
610 | ||||
611 | ||||
612 | ||||
613 | /******************************************************************************* | |||
614 | ** | |||
615 | ** conn.c | |||
616 | ** | |||
617 | ** This file is part of the ABYSS Web server project. | |||
618 | ** | |||
619 | ** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>. | |||
620 | ** All rights reserved. | |||
621 | ** | |||
622 | ** Redistribution and use in source and binary forms, with or without | |||
623 | ** modification, are permitted provided that the following conditions | |||
624 | ** are met: | |||
625 | ** 1. Redistributions of source code must retain the above copyright | |||
626 | ** notice, this list of conditions and the following disclaimer. | |||
627 | ** 2. Redistributions in binary form must reproduce the above copyright | |||
628 | ** notice, this list of conditions and the following disclaimer in the | |||
629 | ** documentation and/or other materials provided with the distribution. | |||
630 | ** 3. The name of the author may not be used to endorse or promote products | |||
631 | ** derived from this software without specific prior written permission. | |||
632 | ** | |||
633 | ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||
634 | ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
635 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
636 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||
637 | ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
638 | ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||
639 | ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
640 | ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
641 | ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
642 | ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
643 | ** SUCH DAMAGE. | |||
644 | ** | |||
645 | ******************************************************************************/ |