Bug Summary

File:src/mod/xml_int/mod_xml_rpc/../../../../libs/xmlrpc-c/lib/abyss/src/http.c
Location:line 456, column 9
Description:Branch condition evaluates to a garbage value

Annotated Source Code

1/* Copyright information is at the end of the file */
2
3#define _XOPEN_SOURCE700 600 /* For strdup() */
4#define _BSD_SOURCE1 /* For xmlrpc_strcaseeq() */
5#ifndef _DEFAULT_SOURCE1
6#define _DEFAULT_SOURCE1
7#endif
8
9#include <ctype.h>
10#include <assert.h>
11#include <stdlib.h>
12#include <stdio.h>
13#include <string.h>
14#include <errno(*__errno_location ()).h>
15#include <time.h>
16
17#include "xmlrpc_config.h"
18#include "bool.h"
19#include "mallocvar.h"
20#include "xmlrpc-c/util.h"
21#include "xmlrpc-c/string_int.h"
22#include "xmlrpc-c/base64_int.h"
23#include "xmlrpc-c/abyss.h"
24
25#include "server.h"
26#include "session.h"
27#include "conn.h"
28#include "token.h"
29#include "date.h"
30#include "data.h"
31
32#include "http.h"
33
34/*********************************************************************
35** Request Parser
36*********************************************************************/
37
38/*********************************************************************
39** Request
40*********************************************************************/
41
42static void
43initRequestInfo(TRequestInfo * const requestInfoP,
44 httpVersion const httpVersion,
45 const char * const requestLine,
46 TMethod const httpMethod,
47 const char * const host,
48 unsigned int const port,
49 const char * const path,
50 const char * const query) {
51/*----------------------------------------------------------------------------
52 Set up the request info structure. For information that is
53 controlled by the header, use the defaults -- I.e. the value that
54 applies if the request contains no applicable header field.
55-----------------------------------------------------------------------------*/
56 XMLRPC_ASSERT_PTR_OK(requestLine)do if (!((requestLine) != ((void*)0))) xmlrpc_assertion_failed
("../../../../libs/xmlrpc-c/lib/abyss/src/http.c", 56); while
(0)
;
57 XMLRPC_ASSERT_PTR_OK(path)do if (!((path) != ((void*)0))) xmlrpc_assertion_failed("../../../../libs/xmlrpc-c/lib/abyss/src/http.c"
, 57); while (0)
;
58
59 requestInfoP->requestline = strdup(requestLine)(__extension__ (__builtin_constant_p (requestLine) &&
((size_t)(const void *)((requestLine) + 1) - (size_t)(const void
*)(requestLine) == 1) ? (((const char *) (requestLine))[0] ==
'\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len
= strlen (requestLine) + 1; char *__retval = (char *) malloc
(__len); if (__retval != ((void*)0)) __retval = (char *) memcpy
(__retval, requestLine, __len); __retval; })) : __strdup (requestLine
)))
;
60 requestInfoP->method = httpMethod;
61 requestInfoP->host = xmlrpc_strdupnull(host);
62 requestInfoP->port = port;
63 requestInfoP->uri = strdup(path)(__extension__ (__builtin_constant_p (path) && ((size_t
)(const void *)((path) + 1) - (size_t)(const void *)(path) ==
1) ? (((const char *) (path))[0] == '\0' ? (char *) calloc (
(size_t) 1, (size_t) 1) : ({ size_t __len = strlen (path) + 1
; char *__retval = (char *) malloc (__len); if (__retval != (
(void*)0)) __retval = (char *) memcpy (__retval, path, __len)
; __retval; })) : __strdup (path)))
;
64 requestInfoP->query = xmlrpc_strdupnull(query);
65 requestInfoP->from = NULL((void*)0);
66 requestInfoP->useragent = NULL((void*)0);
67 requestInfoP->referer = NULL((void*)0);
68 requestInfoP->user = NULL((void*)0);
69
70 if (httpVersion.major > 1 ||
71 (httpVersion.major == 1 && httpVersion.minor >= 1))
72 requestInfoP->keepalive = TRUE1;
73 else
74 requestInfoP->keepalive = FALSE0;
75}
76
77
78
79static void
80freeRequestInfo(TRequestInfo * const requestInfoP) {
81
82 xmlrpc_strfreenull(requestInfoP->host);
83
84 xmlrpc_strfreenull(requestInfoP->user);
85
86 xmlrpc_strfree(requestInfoP->uri);
87
88 xmlrpc_strfree(requestInfoP->requestline);
89
90 xmlrpc_strfree(requestInfoP->query);
91
92}
93
94
95
96void
97RequestInit(TSession * const sessionP,
98 TConn * const connectionP) {
99
100 sessionP->validRequest = false; /* Don't have valid request yet */
101
102 time(&sessionP->date);
103
104 sessionP->connP = connectionP;
105
106 sessionP->responseStarted = FALSE0;
107
108 sessionP->chunkedwrite = FALSE0;
109 sessionP->chunkedwritemode = FALSE0;
110
111 sessionP->continueRequired = FALSE0;
112
113 ListInit(&sessionP->cookies);
114 ListInit(&sessionP->ranges);
115 TableInit(&sessionP->requestHeaderFields);
116 TableInit(&sessionP->responseHeaderFields);
117
118 sessionP->status = 0; /* No status from handler yet */
119
120 StringAlloc(&(sessionP->header));
121}
122
123
124
125void
126RequestFree(TSession * const sessionP) {
127
128 if (sessionP->validRequest)
129 freeRequestInfo(&sessionP->requestInfo);
130
131 ListFree(&sessionP->cookies);
132 ListFree(&sessionP->ranges);
133 TableFree(&sessionP->requestHeaderFields);
134 TableFree(&sessionP->responseHeaderFields);
135 StringFree(&(sessionP->header));
136}
137
138
139
140static char *
141firstLfPos(TConn * const connectionP,
142 char * const lineStart) {
143/*----------------------------------------------------------------------------
144 Return a pointer in the connection's receive buffer to the first
145 LF (linefeed aka newline) character in the buffer at or after 'lineStart'.
146
147 If there is no LF in the buffer at or after 'lineStart', return NULL.
148-----------------------------------------------------------------------------*/
149 const char * const bufferEnd =
150 connectionP->buffer.t + connectionP->buffersize;
151
152 char * p;
153
154 for (p = lineStart; p < bufferEnd && *p != LF'\n'; ++p);
155
156 if (p < bufferEnd)
157 return p;
158 else
159 return NULL((void*)0);
160}
161
162
163
164static void
165getLineInBuffer(TConn * const connectionP,
166 char * const lineStart,
167 time_t const deadline,
168 char ** const lineEndP,
169 bool * const errorP) {
170/*----------------------------------------------------------------------------
171 Get a line into the connection's read buffer, starting at position
172 'lineStart', if there isn't one already there. 'lineStart' is either
173 within the buffer or just after it.
174
175 Read the channel until we get a full line, except fail if we don't get
176 one by 'deadline'.
177-----------------------------------------------------------------------------*/
178 bool error;
179 char * lfPos;
180
181 assert(lineStart <= connectionP->buffer.t + connectionP->buffersize)((lineStart <= connectionP->buffer.t + connectionP->
buffersize) ? (void) (0) : __assert_fail ("lineStart <= connectionP->buffer.t + connectionP->buffersize"
, "../../../../libs/xmlrpc-c/lib/abyss/src/http.c", 181, __PRETTY_FUNCTION__
))
;
182
183 error = FALSE0; /* initial value */
184 lfPos = NULL((void*)0); /* initial value */
185
186 while (!error && !lfPos) {
187 int const timeLeft = (int)(deadline - time(NULL((void*)0)));
188 if (timeLeft <= 0)
189 error = TRUE1;
190 else {
191 lfPos = firstLfPos(connectionP, lineStart);
192 if (!lfPos) {
193 const char * readError;
194 ConnRead(connectionP, timeLeft, NULL((void*)0), NULL((void*)0), &readError);
195 if (readError) {
196 error = TRUE1;
197 xmlrpc_strfree(readError);
198 }
199 }
200 }
201 }
202 *errorP = error;
203 *lineEndP = lfPos + 1;
204}
205
206
207
208static bool
209isContinuationLine(const char * const line) {
210
211 return (line[0] == ' ' || line[0] == '\t');
212}
213
214
215
216static bool
217isEmptyLine(const char * const line) {
218
219 return (line[0] == '\n' || (line[0] == '\r' && line[1] == '\n'));
220}
221
222
223
224static void
225convertLineEnd(char * const lineStart,
226 char * const prevLineStart,
227 char const newVal) {
228/*----------------------------------------------------------------------------
229 Assuming a line begins at 'lineStart' and the line before it (the
230 "previous line") begins at 'prevLineStart', replace the line
231 delimiter at the end of the previous line with the character 'newVal'.
232
233 The line delimiter is either CRLF or LF. In the CRLF case, we replace
234 both CR and LF with 'newVal'.
235-----------------------------------------------------------------------------*/
236 assert(lineStart >= prevLineStart + 1)((lineStart >= prevLineStart + 1) ? (void) (0) : __assert_fail
("lineStart >= prevLineStart + 1", "../../../../libs/xmlrpc-c/lib/abyss/src/http.c"
, 236, __PRETTY_FUNCTION__))
;
237 *(lineStart-1) = newVal;
238 if (prevLineStart + 1 < lineStart &&
239 *(lineStart-2) == CR'\r')
240 *(lineStart-2) = newVal;
241}
242
243
244
245static void
246getRestOfField(TConn * const connectionP,
247 char * const lineEnd,
248 time_t const deadline,
249 const char ** const fieldEndP,
250 bool * const errorP) {
251/*----------------------------------------------------------------------------
252 Given that the read buffer for connection *connectionP contains (at
253 its current read position) the first line of an HTTP header field, which
254 ends at position 'lineEnd', find the rest of it.
255
256 Some or all of the rest of the field may be in the buffer already;
257 we read more from the connection as necessary, but not if it takes past
258 'deadline'. In the latter case, we fail.
259
260 We return the location of the end of the whole field as *headerEndP.
261 We do not remove the field from the buffer, but we do modify the
262 buffer so as to join the multiple lines of the field into a single
263 line, and to NUL-terminate the field.
264-----------------------------------------------------------------------------*/
265 char * const fieldStart = connectionP->buffer.t + connectionP->bufferpos;
266
267 char * fieldEnd;
268 /* End of the field lines we've seen at so far */
269 bool gotWholeField;
270 bool error;
271
272 fieldEnd = lineEnd; /* initial value - end of 1st line */
273
274 for (gotWholeField = FALSE0, error = FALSE0;
275 !gotWholeField && !error;) {
276
277 char * nextLineEnd;
278
279 /* Note that we are guaranteed, assuming the HTTP stream is
280 valid, that there is at least one more line in it. Worst
281 case, it's the empty line that marks the end of the headers.
282 */
283 getLineInBuffer(connectionP, fieldEnd, deadline,
284 &nextLineEnd, &error);
285 if (!error) {
286 if (isContinuationLine(fieldEnd)) {
287 /* Join previous line to this one */
288 convertLineEnd(fieldEnd, fieldStart, ' ');
289 /* Add this line to the header */
290 fieldEnd = nextLineEnd;
291 } else {
292 gotWholeField = TRUE1;
293
294 /* NUL-terminate the whole field */
295 convertLineEnd(fieldEnd, fieldStart, '\0');
296 }
297 }
298 }
299 *fieldEndP = fieldEnd;
300 *errorP = error;
301}
302
303
304
305static void
306readField(TConn * const connectionP,
307 time_t const deadline,
308 bool * const endOfHeaderP,
309 char ** const fieldP,
310 bool * const errorP) {
311/*----------------------------------------------------------------------------
312 Read an HTTP header field, or the end of header empty line, on connection
313 *connectionP.
314
315 An HTTP header field is basically a line, except that if a line starts
316 with white space, it's a continuation of the previous line. A line
317 is delimited by either LF or CRLF.
318
319 The first line of an HTTP header field is never empty; an empty line
320 signals the end of the HTTP header and beginning of the HTTP body. We
321 call that empty line the EOH mark.
322
323 We assume the connection is positioned to a header or EOH mark.
324
325 In the course of reading, we read at least one character past the
326 line delimiter at the end of the field or EOH mark; we may read
327 much more. But we leave everything after the field or EOH (and
328 its line delimiter) in the internal buffer, with the buffer pointer
329 pointing to it.
330
331 We use stuff already in the internal buffer (perhaps left by a
332 previous call to this subroutine) before reading any more from from
333 the channel.
334
335 We return as *fieldP the next field as an ASCIIZ string, with no
336 line delimiter. That string is stored in the "unused" portion of
337 the connection's internal buffer. Iff there is no next field, we
338 return *endOfHeaderP == true and nothing meaningful as *fieldP.
339-----------------------------------------------------------------------------*/
340 char * const bufferStart = connectionP->buffer.t + connectionP->bufferpos;
341
342 bool error;
343 char * lineEnd;
344
345 getLineInBuffer(connectionP, bufferStart, deadline, &lineEnd, &error);
346
347 if (!error) {
5
Assuming 'error' is not equal to 0
6
Taking false branch
348 if (isContinuationLine(bufferStart))
349 error = TRUE1;
350 else if (isEmptyLine(bufferStart)) {
351 /* Consume the EOH mark from the buffer */
352 connectionP->bufferpos = lineEnd - connectionP->buffer.t;
353 *endOfHeaderP = TRUE1;
354 } else {
355 /* We have the first line of a field; there may be more. */
356
357 const char * fieldEnd;
358
359 *endOfHeaderP = FALSE0;
360
361 getRestOfField(connectionP, lineEnd, deadline,
362 &fieldEnd, &error);
363
364 if (!error) {
365 *fieldP = bufferStart;
366
367 /* Consume the header from the buffer (but be careful --
368 you can't reuse that part of the buffer because the
369 string we will return is in it!
370 */
371 connectionP->bufferpos = fieldEnd - connectionP->buffer.t;
372 }
373 }
374 }
375 *errorP = error;
376}
377
378
379
380static void
381skipToNonemptyLine(TConn * const connectionP,
382 time_t const deadline,
383 bool * const errorP) {
384
385 char * const bufferStart = connectionP->buffer.t + connectionP->bufferpos;
386
387 bool gotNonEmptyLine;
388 bool error;
389 char * lineStart;
390
391 lineStart = bufferStart; /* initial value */
392 gotNonEmptyLine = FALSE0; /* initial value */
393 error = FALSE0; /* initial value */
394
395 while (!gotNonEmptyLine && !error) {
396 char * lineEnd;
397
398 getLineInBuffer(connectionP, lineStart, deadline, &lineEnd, &error);
399
400 if (!error) {
401 if (!isEmptyLine(lineStart))
402 gotNonEmptyLine = TRUE1;
403 else
404 lineStart = lineEnd;
405 }
406 }
407 if (!error) {
408 /* Consume all the empty lines; advance buffer pointer to first
409 non-empty line.
410 */
411 connectionP->bufferpos = lineStart - connectionP->buffer.t;
412 }
413 *errorP = error;
414}
415
416
417
418static void
419readRequestField(TSession * const sessionP,
420 time_t const deadline,
421 char ** const requestLineP,
422 uint16_t * const httpErrorCodeP) {
423/*----------------------------------------------------------------------------
424 Read the HTTP request header field from session 'sessionP'. We read
425 through the session's internal buffer; i.e. we may get data that was
426 previously read from the network, or we may read more from the network.
427
428 We assume the connection is presently positioned to the beginning of
429 the HTTP document. We leave it positioned after the request field.
430
431 We ignore any empty lines at the beginning of the stream, per
432 RFC2616 Section 4.1.
433
434 Fail if we can't get the field before 'deadline'.
435
436 Return as *requestLineP the request field read. This ASCIIZ string is
437 in the session's internal buffer.
438
439 Return as *httpErrorCodeP the HTTP error code that describes how we
440 are not able to read the request field, or 0 if we can.
441 If we can't, *requestLineP is meaningless.
442-----------------------------------------------------------------------------*/
443 char * line;
444 bool error;
445 bool endOfHeader;
2
'endOfHeader' declared without an initial value
446
447 skipToNonemptyLine(sessionP->connP, deadline, &error);
448
449 if (!error) {
3
Taking true branch
450 readField(sessionP->connP, deadline, &endOfHeader, &line, &error);
4
Calling 'readField'
7
Returning from 'readField'
451
452 /* End of header is delimited by an empty line, and we skipped all
453 the empty lines above, so readField() could not have encountered
454 EOH:
455 */
456 assert(!endOfHeader)((!endOfHeader) ? (void) (0) : __assert_fail ("!endOfHeader",
"../../../../libs/xmlrpc-c/lib/abyss/src/http.c", 456, __PRETTY_FUNCTION__
))
;
8
Within the expansion of the macro 'assert':
a
Branch condition evaluates to a garbage value
457 }
458 if (error)
459 *httpErrorCodeP = 408; /* Request Timeout */
460 else {
461 *httpErrorCodeP = 0;
462 *requestLineP = line;
463 }
464}
465
466
467
468static void
469unescapeUri(char * const uri,
470 bool * const errorP) {
471
472 char * x;
473 char * y;
474
475 x = y = uri;
476
477 *errorP = FALSE0;
478
479 while (*x && !*errorP) {
480 switch (*x) {
481 case '%': {
482 char c;
483 ++x;
484 c = tolower(*x++)(__extension__ ({ int __res; if (sizeof (*x++) > 1) { if (
__builtin_constant_p (*x++)) { int __c = (*x++); __res = __c <
-128 || __c > 255 ? __c : (*__ctype_tolower_loc ())[__c];
} else __res = tolower (*x++); } else __res = (*__ctype_tolower_loc
())[(int) (*x++)]; __res; }))
;
485 if ((c >= '0') && (c <= '9'))
486 c -= '0';
487 else if ((c >= 'a') && (c <= 'f'))
488 c -= 'a' - 10;
489 else
490 *errorP = TRUE1;
491
492 if (!*errorP) {
493 char d;
494 d = tolower(*x++)(__extension__ ({ int __res; if (sizeof (*x++) > 1) { if (
__builtin_constant_p (*x++)) { int __c = (*x++); __res = __c <
-128 || __c > 255 ? __c : (*__ctype_tolower_loc ())[__c];
} else __res = tolower (*x++); } else __res = (*__ctype_tolower_loc
())[(int) (*x++)]; __res; }))
;
495 if ((d >= '0') && (d <= '9'))
496 d -= '0';
497 else if ((d >= 'a') && (d <= 'f'))
498 d -= 'a' - 10;
499 else
500 *errorP = TRUE1;
501
502 if (!*errorP)
503 *y++ = ((c << 4) | d);
504 }
505 } break;
506
507
508 default:
509 *y++ = *x++;
510 break;
511 }
512 }
513 *y = '\0';
514}
515
516
517
518static void
519parseHostPort(const char * const hostport,
520 const char ** const hostP,
521 unsigned short * const portP,
522 const char ** const errorP,
523 uint16_t * const httpErrorCodeP) {
524/*----------------------------------------------------------------------------
525 Parse a 'hostport', a string in the form www.acme.com:8080 .
526
527 Return the host name part (www.acme.com) as *hostP (in newly
528 malloced storage), and the port part (8080) as *portP.
529
530 Default the port to 80 if 'hostport' doesn't have the port part.
531-----------------------------------------------------------------------------*/
532 char * buffer;
533 char * colonPos;
534
535 buffer = strdup(hostport)(__extension__ (__builtin_constant_p (hostport) && ((
size_t)(const void *)((hostport) + 1) - (size_t)(const void *
)(hostport) == 1) ? (((const char *) (hostport))[0] == '\0' ?
(char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len =
strlen (hostport) + 1; char *__retval = (char *) malloc (__len
); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval
, hostport, __len); __retval; })) : __strdup (hostport)))
;
536
537 colonPos = strrchr(buffer, ':');
538 if (colonPos) {
539 const char * p;
540 uint32_t port;
541
542 *colonPos = '\0'; /* Split hostport at the colon */
543
544
545 for (p = colonPos + 1, port = 0;
546 isdigit(*p)((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short int)
_ISdigit)
&& port < 65535;
547 (port = port * 10 + (*p - '0')), ++p);
548
549 if (*p || port == 0) {
550 xmlrpc_asprintf(errorP, "There is nothing, or something "
551 "non-numeric for the port number after the "
552 "colon in '%s'", hostport);
553 *httpErrorCodeP = 400; /* Bad Request */
554 } else {
555 *hostP = strdup(buffer)(__extension__ (__builtin_constant_p (buffer) && ((size_t
)(const void *)((buffer) + 1) - (size_t)(const void *)(buffer
) == 1) ? (((const char *) (buffer))[0] == '\0' ? (char *) calloc
((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (buffer)
+ 1; char *__retval = (char *) malloc (__len); if (__retval !=
((void*)0)) __retval = (char *) memcpy (__retval, buffer, __len
); __retval; })) : __strdup (buffer)))
;
556 *portP = port;
557 *errorP = NULL((void*)0);
558 }
559 } else {
560 *hostP = strdup(buffer)(__extension__ (__builtin_constant_p (buffer) && ((size_t
)(const void *)((buffer) + 1) - (size_t)(const void *)(buffer
) == 1) ? (((const char *) (buffer))[0] == '\0' ? (char *) calloc
((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (buffer)
+ 1; char *__retval = (char *) malloc (__len); if (__retval !=
((void*)0)) __retval = (char *) memcpy (__retval, buffer, __len
); __retval; })) : __strdup (buffer)))
;
561 *portP = 80;
562 *errorP = NULL((void*)0);
563 }
564 free(buffer);
565}
566
567
568
569static void
570parseRequestUri(char * const requestUri,
571 const char ** const hostP,
572 unsigned short * const portP,
573 const char ** const pathP,
574 const char ** const queryP,
575 uint16_t * const httpErrorCodeP) {
576/*----------------------------------------------------------------------------
577 Parse the request URI (in the request line
578 "GET http://www.myserver.com:8080/myfile.cgi?parm HTTP/1.1",
579 "http://www.myserver.com:8080/myfile.cgi?parm" is the request URI).
580
581 Return as *hostP the "www.myserver.com" in the above example. If
582 that part of the URI doesn't exist, return *hostP == NULL.
583
584 Return as *portP the 8080 in the above example. If it doesn't exist,
585 return 80.
586
587 Return as *pathP the "/myfile.cgi" in the above example. If it
588 doesn't exist, return "*".
589
590 Return as *queryP the "parm" in the above example. If it doesn't
591 exist, return *queryP == NULL.
592
593 Return strings in newly malloc'ed storage.
594
595 Return as *httpErrorCodeP the HTTP error code that describes how the
596 URI is invalid, or 0 if it is valid. If it's invalid, other return
597 values are meaningless.
598
599 This destroys 'requestUri'. We should fix that.
600-----------------------------------------------------------------------------*/
601 bool error;
602
603 unescapeUri(requestUri, &error);
604
605 if (error)
606 *httpErrorCodeP = 400; /* Bad Request */
607 else {
608 char * requestUriNoQuery;
609 /* The request URI with any query (the stuff marked by a question
610 mark at the end of a request URI) chopped off.
611 */
612 {
613 /* Split requestUri at the question mark */
614 char * const qmark = strchr(requestUri, '?')(__extension__ (__builtin_constant_p ('?') && !__builtin_constant_p
(requestUri) && ('?') == '\0' ? (char *) __rawmemchr
(requestUri, '?') : __builtin_strchr (requestUri, '?')))
;
615
616 if (qmark) {
617 *qmark = '\0';
618 *queryP = strdup(qmark + 1)(__extension__ (__builtin_constant_p (qmark + 1) && (
(size_t)(const void *)((qmark + 1) + 1) - (size_t)(const void
*)(qmark + 1) == 1) ? (((const char *) (qmark + 1))[0] == '\0'
? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len
= strlen (qmark + 1) + 1; char *__retval = (char *) malloc (
__len); if (__retval != ((void*)0)) __retval = (char *) memcpy
(__retval, qmark + 1, __len); __retval; })) : __strdup (qmark
+ 1)))
;
619 } else
620 *queryP = NULL((void*)0);
621
622 requestUriNoQuery = requestUri;
623 }
624
625
626 if (requestUriNoQuery[0] == '/') {
627 *hostP = NULL((void*)0);
628 *pathP = strdup(requestUriNoQuery)(__extension__ (__builtin_constant_p (requestUriNoQuery) &&
((size_t)(const void *)((requestUriNoQuery) + 1) - (size_t)(
const void *)(requestUriNoQuery) == 1) ? (((const char *) (requestUriNoQuery
))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({
size_t __len = strlen (requestUriNoQuery) + 1; char *__retval
= (char *) malloc (__len); if (__retval != ((void*)0)) __retval
= (char *) memcpy (__retval, requestUriNoQuery, __len); __retval
; })) : __strdup (requestUriNoQuery)))
;
629 *portP = 80;
630 *httpErrorCodeP = 0;
631 } else {
632 if (!xmlrpc_strneq(requestUriNoQuery, "http://", 7))
633 *httpErrorCodeP = 400; /* Bad Request */
634 else {
635 char * const hostportpath = &requestUriNoQuery[7];
636 char * const slashPos = strchr(hostportpath, '/')(__extension__ (__builtin_constant_p ('/') && !__builtin_constant_p
(hostportpath) && ('/') == '\0' ? (char *) __rawmemchr
(hostportpath, '/') : __builtin_strchr (hostportpath, '/')))
;
637
638 const char * host;
639 const char * path;
640 unsigned short port;
641
642 char * hostport;
643
644 if (slashPos) {
645 char * p;
646 path = strdup(slashPos)(__extension__ (__builtin_constant_p (slashPos) && ((
size_t)(const void *)((slashPos) + 1) - (size_t)(const void *
)(slashPos) == 1) ? (((const char *) (slashPos))[0] == '\0' ?
(char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len =
strlen (slashPos) + 1; char *__retval = (char *) malloc (__len
); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval
, slashPos, __len); __retval; })) : __strdup (slashPos)))
;
647
648 /* Nul-terminate the host name. To make space for
649 it, slide the whole name back one character.
650 This moves it into the space now occupied by
651 the end of "http://", which we don't need.
652 */
653 for (p = hostportpath; *p != '/'; ++p)
654 *(p-1) = *p;
655 *(p-1) = '\0';
656
657 hostport = hostportpath - 1;
658 *httpErrorCodeP = 0;
659 } else {
660 path = strdup("*")(__extension__ (__builtin_constant_p ("*") && ((size_t
)(const void *)(("*") + 1) - (size_t)(const void *)("*") == 1
) ? (((const char *) ("*"))[0] == '\0' ? (char *) calloc ((size_t
) 1, (size_t) 1) : ({ size_t __len = strlen ("*") + 1; char *
__retval = (char *) malloc (__len); if (__retval != ((void*)0
)) __retval = (char *) memcpy (__retval, "*", __len); __retval
; })) : __strdup ("*")))
;
661 hostport = hostportpath;
662 *httpErrorCodeP = 0;
663 }
664 if (!*httpErrorCodeP) {
665 const char * error;
666 parseHostPort(hostport, &host, &port,
667 &error, httpErrorCodeP);
668 if (error)
669 xmlrpc_strfree(error);
670 else
671 *httpErrorCodeP = 0;
672 }
673 if (*httpErrorCodeP)
674 xmlrpc_strfree(path);
675
676 *hostP = host;
677 *portP = port;
678 *pathP = path;
679 }
680 }
681 }
682}
683
684
685
686static void
687parseRequestLine(char * const requestLine,
688 TMethod * const httpMethodP,
689 httpVersion * const httpVersionP,
690 const char ** const hostP,
691 unsigned short * const portP,
692 const char ** const pathP,
693 const char ** const queryP,
694 bool * const moreLinesP,
695 uint16_t * const httpErrorCodeP) {
696/*----------------------------------------------------------------------------
697 Modifies *requestLine!
698-----------------------------------------------------------------------------*/
699 const char * httpMethodName;
700 char * p;
701
702 p = requestLine;
703
704 /* Jump over spaces */
705 NextToken((const char **)&p);
706
707 httpMethodName = GetToken(&p);
708 if (!httpMethodName)
709 *httpErrorCodeP = 400; /* Bad Request */
710 else {
711 char * requestUri;
712
713 if (xmlrpc_streq(httpMethodName, "GET"))
714 *httpMethodP = m_get;
715 else if (xmlrpc_streq(httpMethodName, "PUT"))
716 *httpMethodP = m_put;
717 else if (xmlrpc_streq(httpMethodName, "OPTIONS"))
718 *httpMethodP = m_options;
719 else if (xmlrpc_streq(httpMethodName, "DELETE"))
720 *httpMethodP = m_delete;
721 else if (xmlrpc_streq(httpMethodName, "POST"))
722 *httpMethodP = m_post;
723 else if (xmlrpc_streq(httpMethodName, "TRACE"))
724 *httpMethodP = m_trace;
725 else if (xmlrpc_streq(httpMethodName, "HEAD"))
726 *httpMethodP = m_head;
727 else
728 *httpMethodP = m_unknown;
729
730 /* URI and Query Decoding */
731 NextToken((const char **)&p);
732
733 requestUri = GetToken(&p);
734 if (!requestUri)
735 *httpErrorCodeP = 400; /* Bad Request */
736 else {
737 const char * host;
738 unsigned short port;
739 const char * path;
740 const char * query;
741
742 parseRequestUri(requestUri, &host, &port, &path, &query,
743 httpErrorCodeP);
744
745 if (!*httpErrorCodeP) {
746 const char * httpVersion;
747
748 NextToken((const char **)&p);
749
750 /* HTTP Version Decoding */
751
752 httpVersion = GetToken(&p);
753 if (httpVersion) {
754 uint32_t vmin, vmaj;
755 if (sscanf(httpVersion, "HTTP/%d.%d", &vmaj, &vmin) != 2)
756 *httpErrorCodeP = 400; /* Bad Request */
757 else {
758 httpVersionP->major = vmaj;
759 httpVersionP->minor = vmin;
760 *httpErrorCodeP = 0; /* no error */
761 }
762 *moreLinesP = TRUE1;
763 } else {
764 /* There is no HTTP version, so this is a single
765 line request.
766 */
767 *httpErrorCodeP = 0; /* no error */
768 *moreLinesP = FALSE0;
769 }
770 if (*httpErrorCodeP) {
771 xmlrpc_strfree(host);
772 xmlrpc_strfree(path);
773 xmlrpc_strfree(query);
774 }
775 *hostP = host;
776 *portP = port;
777 *pathP = path;
778 *queryP = query;
779 }
780 }
781 }
782}
783
784
785
786static void
787strtolower(char * const s) {
788
789 char * t;
790
791 t = &s[0];
792 while (*t) {
793 *t = tolower(*t)(__extension__ ({ int __res; if (sizeof (*t) > 1) { if (__builtin_constant_p
(*t)) { int __c = (*t); __res = __c < -128 || __c > 255
? __c : (*__ctype_tolower_loc ())[__c]; } else __res = tolower
(*t); } else __res = (*__ctype_tolower_loc ())[(int) (*t)]; __res
; }))
;
794 ++t;
795 }
796}
797
798
799
800static void
801getFieldNameToken(char ** const pP,
802 char ** const fieldNameP,
803 const char ** const errorP,
804 uint16_t * const httpErrorCodeP) {
805/*----------------------------------------------------------------------------
806 Assuming that *pP points to the place in an HTTP header where the field
807 name belongs, return the field name and advance *pP past that token.
808
809 The field name is the lower case representation of the value of the
810 field name token.
811-----------------------------------------------------------------------------*/
812 char * fieldName;
813
814 NextToken((const char **)pP);
815
816 fieldName = GetToken(pP);
817 if (!fieldName) {
818 xmlrpc_asprintf(errorP, "The header has no field name token");
819 *httpErrorCodeP = 400; /* Bad Request */
820 } else {
821 if (fieldName[strlen(fieldName)-1] != ':') {
822 /* Not a valid field name */
823 xmlrpc_asprintf(errorP, "The field name token '%s' "
824 "does not end with a colon (:)", fieldName);
825 *httpErrorCodeP = 400; /* Bad Request */
826 } else {
827 fieldName[strlen(fieldName)-1] = '\0'; /* remove trailing colon */
828
829 strtolower(fieldName);
830
831 *errorP = NULL((void*)0);
832 }
833 }
834 *fieldNameP = fieldName;
835}
836
837
838
839static void
840processField(const char * const fieldName,
841 char * const fieldValue,
842 TSession * const sessionP,
843 const char ** const errorP,
844 uint16_t * const httpErrorCodeP) {
845/*----------------------------------------------------------------------------
846 We may modify *fieldValue, and we put pointers to *fieldValue and
847 *fieldName into *sessionP.
848
849 We must fix this some day. *sessionP should point to individual
850 malloc'ed strings.
851-----------------------------------------------------------------------------*/
852 *errorP = NULL((void*)0); /* initial assumption */
853
854 if (xmlrpc_streq(fieldName, "connection")) {
855 if (xmlrpc_strcaseeq(fieldValue, "keep-alive"))
856 sessionP->requestInfo.keepalive = TRUE1;
857 else
858 sessionP->requestInfo.keepalive = FALSE0;
859 } else if (xmlrpc_streq(fieldName, "host")) {
860 if (sessionP->requestInfo.host) {
861 xmlrpc_strfree(sessionP->requestInfo.host);
862 sessionP->requestInfo.host = NULL((void*)0);
863 }
864 parseHostPort(fieldValue, &sessionP->requestInfo.host,
865 &sessionP->requestInfo.port, errorP, httpErrorCodeP);
866 } else if (xmlrpc_streq(fieldName, "from"))
867 sessionP->requestInfo.from = fieldValue;
868 else if (xmlrpc_streq(fieldName, "user-agent"))
869 sessionP->requestInfo.useragent = fieldValue;
870 else if (xmlrpc_streq(fieldName, "referer"))
871 sessionP->requestInfo.referer = fieldValue;
872 else if (xmlrpc_streq(fieldName, "range")) {
873 if (xmlrpc_strneq(fieldValue, "bytes=", 6)) {
874 bool succeeded;
875 succeeded = ListAddFromString(&sessionP->ranges, &fieldValue[6]);
876 if (!succeeded) {
877 xmlrpc_asprintf(errorP, "ListAddFromString() failed for "
878 "\"range: bytes=...\" header value '%s'",
879 &fieldValue[6]);
880 *httpErrorCodeP = 400;
881 }
882 }
883 } else if (xmlrpc_streq(fieldName, "cookies")) {
884 bool succeeded;
885 succeeded = ListAddFromString(&sessionP->cookies, fieldValue);
886 if (!succeeded) {
887 xmlrpc_asprintf(errorP, "ListAddFromString() failed for "
888 "cookies: header value '%s'", fieldValue);
889 *httpErrorCodeP = 400;
890 }
891 } else if (xmlrpc_streq(fieldName, "expect")) {
892 if (xmlrpc_strcaseeq(fieldValue, "100-continue"))
893 sessionP->continueRequired = TRUE1;
894 }
895}
896
897
898
899static void
900readAndProcessHeaderFields(TSession * const sessionP,
901 time_t const deadline,
902 const char ** const errorP,
903 uint16_t * const httpErrorCodeP) {
904/*----------------------------------------------------------------------------
905 Read all the HTTP header fields from the session *sessionP, which has at
906 least one field coming. Update *sessionP to reflect the information in the
907 fields.
908
909 If we find an error in the fields or while trying to read them, we return
910 a text explanation of the problem as *errorP and an appropriate HTTP error
911 code as *httpErrorCodeP. Otherwise, we return *errorP = NULL and nothing
912 as *httpErrorCodeP.
913-----------------------------------------------------------------------------*/
914 bool endOfHeader;
915
916 assert(!sessionP->validRequest)((!sessionP->validRequest) ? (void) (0) : __assert_fail ("!sessionP->validRequest"
, "../../../../libs/xmlrpc-c/lib/abyss/src/http.c", 916, __PRETTY_FUNCTION__
))
;
917 /* Calling us doesn't make sense if there is already a valid request */
918
919 *errorP = NULL((void*)0); /* initial assumption */
920 endOfHeader = false; /* Caller assures us there is at least one header */
921
922 while (!endOfHeader && !*errorP) {
923 char * field;
924 bool error;
925 readField(sessionP->connP, deadline, &endOfHeader, &field, &error);
926 if (error) {
927 xmlrpc_asprintf(errorP, "Failed to read header from "
928 "client connection.");
929 *httpErrorCodeP = 408; /* Request Timeout */
930 } else {
931 if (!endOfHeader) {
932 char * p;
933 char * fieldName;
934
935 p = &field[0];
936 getFieldNameToken(&p, &fieldName, errorP, httpErrorCodeP);
937 if (!*errorP) {
938 char * fieldValue;
939
940 NextToken((const char **)&p);
941
942 fieldValue = p;
943
944 TableAdd(&sessionP->requestHeaderFields,
945 fieldName, fieldValue);
946
947 processField(fieldName, fieldValue, sessionP, errorP,
948 httpErrorCodeP);
949 }
950 }
951 }
952 }
953}
954
955
956
957void
958RequestRead(TSession * const sessionP,
959 uint32_t const timeout,
960 const char ** const errorP,
961 uint16_t * const httpErrorCodeP) {
962/*----------------------------------------------------------------------------
963 Read the headers of a new HTTP request (assuming nothing has yet been
964 read on the session).
965
966 Update *sessionP with the information from the headers.
967
968 Leave the connection positioned to the body of the request, ready
969 to be read by an HTTP request handler (via SessionRefillBuffer() and
970 SessionGetReadData()).
971-----------------------------------------------------------------------------*/
972 time_t const deadline = time(NULL((void*)0)) + timeout;
973
974 uint16_t httpErrorCode; /* zero for no error */
975 char * requestLine; /* In connection;s internal buffer */
976
977 readRequestField(sessionP, deadline, &requestLine, &httpErrorCode);
1
Calling 'readRequestField'
978 if (httpErrorCode) {
979 xmlrpc_asprintf(errorP, "Problem getting the request header");
980 *httpErrorCodeP = httpErrorCode;
981 } else {
982 TMethod httpMethod;
983 const char * host;
984 const char * path;
985 const char * query;
986 unsigned short port;
987 bool moreFields;
988
989 parseRequestLine(requestLine, &httpMethod, &sessionP->version,
990 &host, &port, &path, &query,
991 &moreFields, &httpErrorCode);
992
993 if (httpErrorCode) {
994 xmlrpc_asprintf(errorP, "Unable to parse the request header "
995 "'%s'", requestLine);
996 *httpErrorCodeP = httpErrorCode;
997 } else {
998 initRequestInfo(&sessionP->requestInfo, sessionP->version,
999 requestLine,
1000 httpMethod, host, port, path, query);
1001
1002 if (moreFields) {
1003 readAndProcessHeaderFields(sessionP, deadline,
1004 errorP, httpErrorCodeP);
1005 } else
1006 *errorP = NULL((void*)0);
1007
1008 if (!*errorP)
1009 sessionP->validRequest = true;
1010
1011 xmlrpc_strfreenull(host);
1012 xmlrpc_strfree(path);
1013 xmlrpc_strfreenull(query);
1014 }
1015 }
1016}
1017
1018
1019
1020char *
1021RequestHeaderValue(TSession * const sessionP,
1022 const char * const name) {
1023
1024 return (TableFind(&sessionP->requestHeaderFields, name));
1025}
1026
1027
1028
1029bool
1030RequestValidURI(TSession * const sessionP) {
1031
1032 if (!sessionP->requestInfo.uri)
1033 return FALSE0;
1034
1035 if (xmlrpc_streq(sessionP->requestInfo.uri, "*"))
1036 return (sessionP->requestInfo.method != m_options);
1037
1038 if (strchr(sessionP->requestInfo.uri, '*')(__extension__ (__builtin_constant_p ('*') && !__builtin_constant_p
(sessionP->requestInfo.uri) && ('*') == '\0' ? (char
*) __rawmemchr (sessionP->requestInfo.uri, '*') : __builtin_strchr
(sessionP->requestInfo.uri, '*')))
)
1039 return FALSE0;
1040
1041 return TRUE1;
1042}
1043
1044
1045
1046bool
1047RequestValidURIPath(TSession * const sessionP) {
1048
1049 uint32_t i;
1050 const char * p;
1051
1052 p = sessionP->requestInfo.uri;
1053
1054 i = 0;
1055
1056 if (*p == '/') {
1057 i = 1;
1058 while (*p)
1059 if (*(p++) == '/') {
1060 if (*p == '/')
1061 break;
1062 else if ((xmlrpc_strneq(p,"./", 2)) || (xmlrpc_streq(p, ".")))
1063 ++p;
1064 else if ((xmlrpc_strneq(p, "../", 2)) ||
1065 (xmlrpc_streq(p, ".."))) {
1066 p += 2;
1067 --i;
1068 if (i == 0)
1069 break;
1070 }
1071 /* Prevent accessing hidden files (starting with .) */
1072 else if (*p == '.')
1073 return FALSE0;
1074 else
1075 if (*p)
1076 ++i;
1077 }
1078 }
1079 return (*p == 0 && i > 0);
1080}
1081
1082
1083
1084bool
1085RequestAuth(TSession * const sessionP,
1086 const char * const credential,
1087 const char * const user,
1088 const char * const pass) {
1089/*----------------------------------------------------------------------------
1090 Authenticate requester, in a very simplistic fashion.
1091
1092 If the request executing on session *sessionP specifies basic
1093 authentication (via Authorization header) with username 'user', password
1094 'pass', then return TRUE. Else, return FALSE and set up an authorization
1095 failure response (HTTP response status 401) that says user must supply an
1096 identity in the 'credential' domain.
1097
1098 When we return TRUE, we also set the username in the request info for the
1099 session to 'user' so that a future SessionGetRequestInfo can get it.
1100-----------------------------------------------------------------------------*/
1101 bool authorized;
1102 char * authHdrPtr;
1103
1104 authHdrPtr = RequestHeaderValue(sessionP, "authorization");
1105 if (authHdrPtr) {
1106 const char * authType;
1107 NextToken((const char **)&authHdrPtr);
1108 GetTokenConst(&authHdrPtr, &authType);
1109 authType = GetToken(&authHdrPtr);
1110 if (authType) {
1111 if (xmlrpc_strcaseeq(authType, "basic")) {
1112 const char * userPass;
1113 char userPassEncoded[80];
1114
1115 NextToken((const char **)&authHdrPtr);
1116
1117 xmlrpc_asprintf(&userPass, "%s:%s", user, pass);
1118 xmlrpc_base64Encode(userPass, userPassEncoded);
1119 xmlrpc_strfree(userPass);
1120
1121 if (xmlrpc_streq(authHdrPtr, userPassEncoded)) {
1122 sessionP->requestInfo.user = strdup(user)(__extension__ (__builtin_constant_p (user) && ((size_t
)(const void *)((user) + 1) - (size_t)(const void *)(user) ==
1) ? (((const char *) (user))[0] == '\0' ? (char *) calloc (
(size_t) 1, (size_t) 1) : ({ size_t __len = strlen (user) + 1
; char *__retval = (char *) malloc (__len); if (__retval != (
(void*)0)) __retval = (char *) memcpy (__retval, user, __len)
; __retval; })) : __strdup (user)))
;
1123 authorized = TRUE1;
1124 } else
1125 authorized = FALSE0;
1126 } else
1127 authorized = FALSE0;
1128 } else
1129 authorized = FALSE0;
1130 } else
1131 authorized = FALSE0;
1132
1133 if (!authorized) {
1134 const char * hdrValue;
1135 xmlrpc_asprintf(&hdrValue, "Basic realm=\"%s\"", credential);
1136 ResponseAddField(sessionP, "WWW-Authenticate", hdrValue);
1137
1138 xmlrpc_strfree(hdrValue);
1139
1140 ResponseStatus(sessionP, 401);
1141 }
1142 return authorized;
1143}
1144
1145
1146
1147/*********************************************************************
1148** Range
1149*********************************************************************/
1150
1151abyss_bool
1152RangeDecode(char * const strArg,
1153 xmlrpc_uint64_t const filesize,
1154 xmlrpc_uint64_t * const start,
1155 xmlrpc_uint64_t * const end) {
1156
1157 char *str;
1158 char *ss;
1159
1160 str = strArg; /* initial value */
1161
1162 *start=0;
1163 *end=filesize-1;
1164
1165 if (*str=='-')
1166 {
1167 *start=filesize-strtol(str+1,&ss,10);
1168 return ((ss!=str) && (!*ss));
1169 };
1170
1171 *start=strtol(str,&ss,10);
1172
1173 if ((ss==str) || (*ss!='-'))
1174 return FALSE0;
1175
1176 str=ss+1;
1177
1178 if (!*str)
1179 return TRUE1;
1180
1181 *end=strtol(str,&ss,10);
1182
1183 if ((ss==str) || (*ss) || (*end<*start))
1184 return FALSE0;
1185
1186 return TRUE1;
1187}
1188
1189/*********************************************************************
1190** HTTP
1191*********************************************************************/
1192
1193const char *
1194HTTPReasonByStatus(uint16_t const code) {
1195
1196 struct _HTTPReasons {
1197 uint16_t status;
1198 const char * reason;
1199 };
1200
1201 static struct _HTTPReasons const reasons[] = {
1202 { 100,"Continue" },
1203 { 101,"Switching Protocols" },
1204 { 200,"OK" },
1205 { 201,"Created" },
1206 { 202,"Accepted" },
1207 { 203,"Non-Authoritative Information" },
1208 { 204,"No Content" },
1209 { 205,"Reset Content" },
1210 { 206,"Partial Content" },
1211 { 300,"Multiple Choices" },
1212 { 301,"Moved Permanently" },
1213 { 302,"Moved Temporarily" },
1214 { 303,"See Other" },
1215 { 304,"Not Modified" },
1216 { 305,"Use Proxy" },
1217 { 400,"Bad Request" },
1218 { 401,"Unauthorized" },
1219 { 402,"Payment Required" },
1220 { 403,"Forbidden" },
1221 { 404,"Not Found" },
1222 { 405,"Method Not Allowed" },
1223 { 406,"Not Acceptable" },
1224 { 407,"Proxy Authentication Required" },
1225 { 408,"Request Timeout" },
1226 { 409,"Conflict" },
1227 { 410,"Gone" },
1228 { 411,"Length Required" },
1229 { 412,"Precondition Failed" },
1230 { 413,"Request Entity Too Large" },
1231 { 414,"Request-URI Too Long" },
1232 { 415,"Unsupported Media Type" },
1233 { 500,"Internal Server Error" },
1234 { 501,"Not Implemented" },
1235 { 502,"Bad Gateway" },
1236 { 503,"Service Unavailable" },
1237 { 504,"Gateway Timeout" },
1238 { 505,"HTTP Version Not Supported" },
1239 { 000, NULL((void*)0) }
1240 };
1241 const struct _HTTPReasons * reasonP;
1242
1243 reasonP = &reasons[0];
1244
1245 while (reasonP->status <= code)
1246 if (reasonP->status == code)
1247 return reasonP->reason;
1248 else
1249 ++reasonP;
1250
1251 return "No Reason";
1252}
1253
1254
1255
1256int32_t
1257HTTPRead(TSession * const s ATTR_UNUSED__attribute__((__unused__)),
1258 const char * const buffer ATTR_UNUSED__attribute__((__unused__)),
1259 uint32_t const len ATTR_UNUSED__attribute__((__unused__))) {
1260
1261 return 0;
1262}
1263
1264
1265
1266bool
1267HTTPWriteBodyChunk(TSession * const sessionP,
1268 const char * const buffer,
1269 uint32_t const len) {
1270
1271 if (sessionP->chunkedwrite && sessionP->chunkedwritemode) {
1272 char chunkHeader[16];
1273
1274 sprintf(chunkHeader, "%x\r\n", len);
1275
1276 if (ConnWrite(sessionP->connP, chunkHeader, strlen(chunkHeader)))
1277 if (ConnWrite(sessionP->connP, buffer, len))
1278 return ConnWrite(sessionP->connP, "\r\n", 2);
1279
1280 return FALSE0;
1281 } else
1282 return ConnWrite(sessionP->connP, buffer, len);
1283
1284}
1285
1286
1287
1288bool
1289HTTPWriteEndChunk(TSession * const sessionP) {
1290
1291 bool retval = TRUE1;
1292
1293 if (sessionP->chunkedwritemode && sessionP->chunkedwrite) {
1294 /* May be one day trailer dumping will be added */
1295 sessionP->chunkedwritemode = FALSE0;
1296 retval = ConnWrite(sessionP->connP, "0\r\n\r\n", 5);
1297 }
1298
1299 return retval;
1300}
1301
1302
1303
1304bool
1305HTTPKeepalive(TSession * const sessionP) {
1306/*----------------------------------------------------------------------------
1307 Return value: the connection should be kept alive after the session
1308 *sessionP is over.
1309-----------------------------------------------------------------------------*/
1310 return (sessionP->requestInfo.keepalive &&
1311 !sessionP->serverDeniesKeepalive &&
1312 sessionP->status < 400);
1313}
1314
1315
1316
1317bool
1318HTTPWriteContinue(TSession * const sessionP) {
1319
1320 char const continueStatus[] = "HTTP/1.1 100 continue\r\n\r\n";
1321 /* This is a status line plus an end-of-headers empty line */
1322
1323 return ConnWrite(sessionP->connP, continueStatus, strlen(continueStatus));
1324}
1325
1326
1327
1328/******************************************************************************
1329**
1330** http.c
1331**
1332** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
1333** All rights reserved.
1334**
1335** Redistribution and use in source and binary forms, with or without
1336** modification, are permitted provided that the following conditions
1337** are met:
1338** 1. Redistributions of source code must retain the above copyright
1339** notice, this list of conditions and the following disclaimer.
1340** 2. Redistributions in binary form must reproduce the above copyright
1341** notice, this list of conditions and the following disclaimer in the
1342** documentation and/or other materials provided with the distribution.
1343** 3. The name of the author may not be used to endorse or promote products
1344** derived from this software without specific prior written permission.
1345**
1346** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1347** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1348** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1349** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1350** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1351** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1352** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1353** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1354** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1355** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1356** SUCH DAMAGE.
1357**
1358******************************************************************************/