| 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 | 
| 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 | ||||||
| 42 | static void | |||||
| 43 | initRequestInfo(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 | ||||||
| 79 | static void | |||||
| 80 | freeRequestInfo(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 | ||||||
| 96 | void | |||||
| 97 | RequestInit(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 | ||||||
| 125 | void | |||||
| 126 | RequestFree(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 | ||||||
| 140 | static char * | |||||
| 141 | firstLfPos(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 | ||||||
| 164 | static void | |||||
| 165 | getLineInBuffer(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 | ||||||
| 208 | static bool | |||||
| 209 | isContinuationLine(const char * const line) { | |||||
| 210 | ||||||
| 211 | return (line[0] == ' ' || line[0] == '\t'); | |||||
| 212 | } | |||||
| 213 | ||||||
| 214 | ||||||
| 215 | ||||||
| 216 | static bool | |||||
| 217 | isEmptyLine(const char * const line) { | |||||
| 218 | ||||||
| 219 | return (line[0] == '\n' || (line[0] == '\r' && line[1] == '\n')); | |||||
| 220 | } | |||||
| 221 | ||||||
| 222 | ||||||
| 223 | ||||||
| 224 | static void | |||||
| 225 | convertLineEnd(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 | ||||||
| 245 | static void | |||||
| 246 | getRestOfField(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 | ||||||
| 305 | static void | |||||
| 306 | readField(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) { | |||||
| 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 | ||||||
| 380 | static void | |||||
| 381 | skipToNonemptyLine(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 | ||||||
| 418 | static void | |||||
| 419 | readRequestField(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; | |||||
| 446 | ||||||
| 447 | skipToNonemptyLine(sessionP->connP, deadline, &error); | |||||
| 448 | ||||||
| 449 | if (!error) { | |||||
| 450 | readField(sessionP->connP, deadline, &endOfHeader, &line, &error); | |||||
| 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__ ));  | |||||
 
  | ||||||
| 457 | } | |||||
| 458 | if (error) | |||||
| 459 | *httpErrorCodeP = 408; /* Request Timeout */ | |||||
| 460 | else { | |||||
| 461 | *httpErrorCodeP = 0; | |||||
| 462 | *requestLineP = line; | |||||
| 463 | } | |||||
| 464 | } | |||||
| 465 | ||||||
| 466 | ||||||
| 467 | ||||||
| 468 | static void | |||||
| 469 | unescapeUri(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 | ||||||
| 518 | static void | |||||
| 519 | parseHostPort(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 | ||||||
| 569 | static void | |||||
| 570 | parseRequestUri(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 | ||||||
| 686 | static void | |||||
| 687 | parseRequestLine(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 | ||||||
| 786 | static void | |||||
| 787 | strtolower(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 | ||||||
| 800 | static void | |||||
| 801 | getFieldNameToken(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 | ||||||
| 839 | static void | |||||
| 840 | processField(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 | ||||||
| 899 | static void | |||||
| 900 | readAndProcessHeaderFields(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 | ||||||
| 957 | void | |||||
| 958 | RequestRead(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); | |||||
  | ||||||
| 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 | ||||||
| 1020 | char * | |||||
| 1021 | RequestHeaderValue(TSession * const sessionP, | |||||
| 1022 | const char * const name) { | |||||
| 1023 | ||||||
| 1024 | return (TableFind(&sessionP->requestHeaderFields, name)); | |||||
| 1025 | } | |||||
| 1026 | ||||||
| 1027 | ||||||
| 1028 | ||||||
| 1029 | bool | |||||
| 1030 | RequestValidURI(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 | ||||||
| 1046 | bool | |||||
| 1047 | RequestValidURIPath(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 | ||||||
| 1084 | bool | |||||
| 1085 | RequestAuth(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 | ||||||
| 1151 | abyss_bool | |||||
| 1152 | RangeDecode(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 | ||||||
| 1193 | const char * | |||||
| 1194 | HTTPReasonByStatus(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 | ||||||
| 1256 | int32_t | |||||
| 1257 | HTTPRead(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 | ||||||
| 1266 | bool | |||||
| 1267 | HTTPWriteBodyChunk(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 | ||||||
| 1288 | bool | |||||
| 1289 | HTTPWriteEndChunk(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 | ||||||
| 1304 | bool | |||||
| 1305 | HTTPKeepalive(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 | ||||||
| 1317 | bool | |||||
| 1318 | HTTPWriteContinue(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 | ******************************************************************************/ |