httpread.c

00001 
00043 #include "includes.h"
00044 
00045 #include "common.h"
00046 #include "eloop.h"
00047 #include "httpread.h"
00048 
00049 
00050 /* Tunable parameters */
00051 #define HTTPREAD_READBUF_SIZE 1024      /* read in chunks of this size */
00052 #define HTTPREAD_HEADER_MAX_SIZE 4096   /* max allowed for headers */
00053 #define HTTPREAD_BODYBUF_DELTA 4096     /* increase allocation by this */
00054 
00055 #if 0
00056 /* httpread_debug -- set this global variable > 0 e.g. from debugger
00057  * to enable debugs (larger numbers for more debugs)
00058  * Make this a #define of 0 to eliminate the debugging code.
00059  */
00060 int httpread_debug = 99;
00061 #else
00062 #define httpread_debug 0        /* eliminates even the debugging code */
00063 #endif
00064 
00065 
00066 /* control instance -- actual definition (opaque to application)
00067  */
00068 struct httpread {
00069         /* information from creation */
00070         int sd;         /* descriptor of TCP socket to read from */
00071         void (*cb)(struct httpread *handle, void *cookie,
00072                     enum httpread_event e);  /* call on event */
00073         void *cookie;   /* pass to callback */
00074         int max_bytes;          /* maximum file size else abort it */
00075         int timeout_seconds;            /* 0 or total duration timeout period */
00076 
00077         /* dynamically used information follows */
00078         int sd_registered;      /* nonzero if we need to unregister socket */
00079         int to_registered;      /* nonzero if we need to unregister timeout */
00080 
00081         int got_hdr;            /* nonzero when header is finalized */
00082         char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */
00083         int hdr_nbytes;
00084 
00085         enum httpread_hdr_type hdr_type;
00086         int version;            /* 1 if we've seen 1.1 */
00087         int reply_code;         /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
00088         int got_content_length; /* true if we know content length for sure */
00089         int content_length;     /* body length,  iff got_content_length */
00090         int chunked;            /* nonzero for chunked data */
00091         char *uri;
00092 
00093         int got_body;           /* nonzero when body is finalized */
00094         char *body;
00095         int body_nbytes;
00096         int body_alloc_nbytes;  /* amount allocated */
00097 
00098         int got_file;           /* here when we are done */
00099 
00100         /* The following apply if data is chunked: */
00101         int in_chunk_data;      /* 0=in/at header, 1=in the data or tail*/
00102         int chunk_start;        /* offset in body of chunk hdr or data */
00103         int chunk_size;         /* data of chunk (not hdr or ending CRLF)*/
00104         int in_trailer;         /* in header fields after data (chunked only)*/
00105         enum trailer_state {
00106                 trailer_line_begin = 0,
00107                 trailer_empty_cr,       /* empty line + CR */
00108                 trailer_nonempty,
00109                 trailer_nonempty_cr,
00110         } trailer_state;
00111 };
00112 
00113 
00114 /* Check words for equality, where words consist of graphical characters
00115  * delimited by whitespace
00116  * Returns nonzero if "equal" doing case insensitive comparison.
00117  */
00118 static int word_eq(char *s1, char *s2)
00119 {
00120         int c1;
00121         int c2;
00122         int end1 = 0;
00123         int end2 = 0;
00124         for (;;) {
00125                 c1 = *s1++;
00126                 c2 = *s2++;
00127                 if (isalpha(c1) && isupper(c1))
00128                         c1 = tolower(c1);
00129                 if (isalpha(c2) && isupper(c2))
00130                         c2 = tolower(c2);
00131                 end1 = !isgraph(c1);
00132                 end2 = !isgraph(c2);
00133                 if (end1 || end2 || c1 != c2)
00134                         break;
00135         }
00136         return end1 && end2;  /* reached end of both words? */
00137 }
00138 
00139 
00140 /* convert hex to binary
00141  * Requires that c have been previously tested true with isxdigit().
00142  */
00143 static int hex_value(int c)
00144 {
00145         if (isdigit(c))
00146                 return c - '0';
00147         if (islower(c))
00148                 return 10 + c - 'a';
00149         return 10 + c - 'A';
00150 }
00151 
00152 
00153 static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
00154 
00155 /* httpread_destroy -- if h is non-NULL, clean up
00156  * This must eventually be called by the application following
00157  * call of the application's callback and may be called
00158  * earlier if desired.
00159  */
00160 void httpread_destroy(struct httpread *h)
00161 {
00162         if (httpread_debug >= 10)
00163                 wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
00164         if (!h)
00165                 return;
00166 
00167         if (h->to_registered)
00168                 eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
00169         h->to_registered = 0;
00170         if (h->sd_registered)
00171                 eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
00172         h->sd_registered = 0;
00173         os_free(h->body);
00174         os_free(h->uri);
00175         os_memset(h, 0, sizeof(*h));  /* aid debugging */
00176         h->sd = -1;     /* aid debugging */
00177         os_free(h);
00178 }
00179 
00180 
00181 /* httpread_timeout_handler -- called on excessive total duration
00182  */
00183 static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
00184 {
00185         struct httpread *h = user_ctx;
00186         wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
00187         h->to_registered = 0;   /* is self-cancelling */
00188         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
00189 }
00190 
00191 
00192 /* Analyze options only so far as is needed to correctly obtain the file.
00193  * The application can look at the raw header to find other options.
00194  */
00195 static int httpread_hdr_option_analyze(
00196         struct httpread *h,
00197         char *hbp       /* pointer to current line in header buffer */
00198         )
00199 {
00200         if (word_eq(hbp, "CONTENT-LENGTH:")) {
00201                 while (isgraph(*hbp))
00202                         hbp++;
00203                 while (*hbp == ' ' || *hbp == '\t')
00204                         hbp++;
00205                 if (!isdigit(*hbp))
00206                         return -1;
00207                 h->content_length = atol(hbp);
00208                 h->got_content_length = 1;
00209                 return 0;
00210         }
00211         if (word_eq(hbp, "TRANSFER_ENCODING:") ||
00212             word_eq(hbp, "TRANSFER-ENCODING:")) {
00213                 while (isgraph(*hbp))
00214                         hbp++;
00215                 while (*hbp == ' ' || *hbp == '\t')
00216                         hbp++;
00217                 /* There should (?) be no encodings of interest
00218                  * other than chunked...
00219                  */
00220                 if (word_eq(hbp, "CHUNKED")) {
00221                         h->chunked = 1;
00222                         h->in_chunk_data = 0;
00223                         /* ignore possible ;<parameters> */
00224                 }
00225                 return 0;
00226         }
00227         /* skip anything we don't know, which is a lot */
00228         return 0;
00229 }
00230 
00231 
00232 static int httpread_hdr_analyze(struct httpread *h)
00233 {
00234         char *hbp = h->hdr;      /* pointer into h->hdr */
00235         int standard_first_line = 1;
00236 
00237         /* First line is special */
00238         h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
00239         if (!isgraph(*hbp))
00240                 goto bad;
00241         if (os_strncmp(hbp, "HTTP/", 5) == 0) {
00242                 h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
00243                 standard_first_line = 0;
00244                 hbp += 5;
00245                 if (hbp[0] == '1' && hbp[1] == '.' &&
00246                     isdigit(hbp[2]) && hbp[2] != '0')
00247                         h->version = 1;
00248                 while (isgraph(*hbp))
00249                         hbp++;
00250                 while (*hbp == ' ' || *hbp == '\t')
00251                         hbp++;
00252                 if (!isdigit(*hbp))
00253                         goto bad;
00254                 h->reply_code = atol(hbp);
00255         } else if (word_eq(hbp, "GET"))
00256                 h->hdr_type = HTTPREAD_HDR_TYPE_GET;
00257         else if (word_eq(hbp, "HEAD"))
00258                 h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
00259         else if (word_eq(hbp, "POST"))
00260                 h->hdr_type = HTTPREAD_HDR_TYPE_POST;
00261         else if (word_eq(hbp, "PUT"))
00262                 h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
00263         else if (word_eq(hbp, "DELETE"))
00264                 h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
00265         else if (word_eq(hbp, "TRACE"))
00266                 h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
00267         else if (word_eq(hbp, "CONNECT"))
00268                 h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
00269         else if (word_eq(hbp, "NOTIFY"))
00270                 h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
00271         else if (word_eq(hbp, "M-SEARCH"))
00272                 h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
00273         else if (word_eq(hbp, "M-POST"))
00274                 h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
00275         else if (word_eq(hbp, "SUBSCRIBE"))
00276                 h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
00277         else if (word_eq(hbp, "UNSUBSCRIBE"))
00278                 h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
00279         else {
00280         }
00281 
00282         if (standard_first_line) {
00283                 char *rawuri;
00284                 char *uri;
00285                 /* skip type */
00286                 while (isgraph(*hbp))
00287                         hbp++;
00288                 while (*hbp == ' ' || *hbp == '\t')
00289                         hbp++;
00290                 /* parse uri.
00291                  * Find length, allocate memory for translated
00292                  * copy, then translate by changing %<hex><hex>
00293                  * into represented value.
00294                  */
00295                 rawuri = hbp;
00296                 while (isgraph(*hbp))
00297                         hbp++;
00298                 h->uri = os_malloc((hbp - rawuri) + 1);
00299                 if (h->uri == NULL)
00300                         goto bad;
00301                 uri = h->uri;
00302                 while (rawuri < hbp) {
00303                         int c = *rawuri;
00304                         if (c == '%' &&
00305                             isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
00306                                 *uri++ = (hex_value(rawuri[1]) << 4) |
00307                                         hex_value(rawuri[2]);
00308                                 rawuri += 3;
00309                         } else {
00310                                 *uri++ = c;
00311                                 rawuri++;
00312                         }
00313                 }
00314                 *uri = 0;       /* null terminate */
00315                 while (isgraph(*hbp))
00316                         hbp++;
00317                 while (*hbp == ' ' || *hbp == '\t')
00318                         hbp++;
00319                 /* get version */
00320                 if (0 == strncmp(hbp, "HTTP/", 5)) {
00321                         hbp += 5;
00322                         if (hbp[0] == '1' && hbp[1] == '.' &&
00323                             isdigit(hbp[2]) && hbp[2] != '0')
00324                                 h->version = 1;
00325                 }
00326         }
00327         /* skip rest of line */
00328         while (*hbp)
00329                 if (*hbp++ == '\n')
00330                         break;
00331 
00332         /* Remainder of lines are options, in any order;
00333          * or empty line to terminate
00334          */
00335         for (;;) {
00336                 /* Empty line to terminate */
00337                 if (hbp[0] == '\n' ||
00338                     (hbp[0] == '\r' && hbp[1] == '\n'))
00339                         break;
00340                 if (!isgraph(*hbp))
00341                         goto bad;
00342                 if (httpread_hdr_option_analyze(h, hbp))
00343                         goto bad;
00344                 /* skip line */
00345                 while (*hbp)
00346                         if (*hbp++ == '\n')
00347                                 break;
00348         }
00349 
00350         /* chunked overrides content-length always */
00351         if (h->chunked)
00352                 h->got_content_length = 0;
00353 
00354         /* For some types, we should not try to read a body
00355          * This is in addition to the application determining
00356          * that we should not read a body.
00357          */
00358         switch (h->hdr_type) {
00359         case HTTPREAD_HDR_TYPE_REPLY:
00360                 /* Some codes can have a body and some not.
00361                  * For now, just assume that any other than 200
00362                  * do not...
00363                  */
00364                 if (h->reply_code != 200)
00365                         h->max_bytes = 0;
00366                 break;
00367         case HTTPREAD_HDR_TYPE_GET:
00368         case HTTPREAD_HDR_TYPE_HEAD:
00369                 /* in practice it appears that it is assumed
00370                  * that GETs have a body length of 0... ?
00371                  */
00372                 if (h->chunked == 0 && h->got_content_length == 0)
00373                         h->max_bytes = 0;
00374                 break;
00375         case HTTPREAD_HDR_TYPE_POST:
00376         case HTTPREAD_HDR_TYPE_PUT:
00377         case HTTPREAD_HDR_TYPE_DELETE:
00378         case HTTPREAD_HDR_TYPE_TRACE:
00379         case HTTPREAD_HDR_TYPE_CONNECT:
00380         case HTTPREAD_HDR_TYPE_NOTIFY:
00381         case HTTPREAD_HDR_TYPE_M_SEARCH:
00382         case HTTPREAD_HDR_TYPE_M_POST:
00383         case HTTPREAD_HDR_TYPE_SUBSCRIBE:
00384         case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
00385         default:
00386                 break;
00387         }
00388 
00389         return 0;
00390 
00391 bad:
00392         /* Error */
00393         return -1;
00394 }
00395 
00396 
00397 /* httpread_read_handler -- called when socket ready to read
00398  *
00399  * Note: any extra data we read past end of transmitted file is ignored;
00400  * if we were to support keeping connections open for multiple files then
00401  * this would have to be addressed.
00402  */
00403 static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
00404 {
00405         struct httpread *h = sock_ctx;
00406         int nread;
00407         char *rbp;      /* pointer into read buffer */
00408         char *hbp;      /* pointer into header buffer */
00409         char *bbp;      /* pointer into body buffer */
00410         char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */
00411 
00412         if (httpread_debug >= 20)
00413                 wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
00414 
00415         /* read some at a time, then search for the interal
00416          * boundaries between header and data and etc.
00417          */
00418         nread = read(h->sd, readbuf, sizeof(readbuf));
00419         if (nread < 0)
00420                 goto bad;
00421         if (nread == 0) {
00422                 /* end of transmission... this may be normal
00423                  * or may be an error... in some cases we can't
00424                  * tell which so we must assume it is normal then.
00425                  */
00426                 if (!h->got_hdr) {
00427                         /* Must at least have completed header */
00428                         wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
00429                         goto bad;
00430                 }
00431                 if (h->chunked || h->got_content_length) {
00432                         /* Premature EOF; e.g. dropped connection */
00433                         wpa_printf(MSG_DEBUG,
00434                                    "httpread premature eof(%p) %d/%d",
00435                                    h, h->body_nbytes,
00436                                    h->content_length);
00437                         goto bad;
00438                 }
00439                 /* No explicit length, hopefully we have all the data
00440                  * although dropped connections can cause false
00441                  * end
00442                  */
00443                 if (httpread_debug >= 10)
00444                         wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
00445                         h->got_body = 1;
00446                         goto got_file;
00447         }
00448         rbp = readbuf;
00449 
00450         /* Header consists of text lines (terminated by both CR and LF)
00451          * and an empty line (CR LF only).
00452          */
00453         if (!h->got_hdr) {
00454                 hbp = h->hdr + h->hdr_nbytes;
00455                 /* add to headers until:
00456                  *      -- we run out of data in read buffer
00457                  *      -- or, we run out of header buffer room
00458                  *      -- or, we get double CRLF in headers
00459                  */
00460                 for (;;) {
00461                         if (nread == 0)
00462                                 goto get_more;
00463                         if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
00464                                 goto bad;
00465                         }
00466                         *hbp++ = *rbp++;
00467                         nread--;
00468                         h->hdr_nbytes++;
00469                         if (h->hdr_nbytes >= 4 &&
00470                             hbp[-1] == '\n' &&
00471                             hbp[-2] == '\r' &&
00472                             hbp[-3] == '\n' &&
00473                             hbp[-4] == '\r' ) {
00474                                 h->got_hdr = 1;
00475                                 *hbp = 0;       /* null terminate */
00476                                 break;
00477                         }
00478                 }
00479                 /* here we've just finished reading the header */
00480                 if (httpread_hdr_analyze(h)) {
00481                         wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
00482                         goto bad;
00483                 }
00484                 if (h->max_bytes == 0) {
00485                         if (httpread_debug >= 10)
00486                                 wpa_printf(MSG_DEBUG,
00487                                            "httpread no body hdr end(%p)", h);
00488                         goto got_file;
00489                 }
00490                 if (h->got_content_length && h->content_length == 0) {
00491                         if (httpread_debug >= 10)
00492                                 wpa_printf(MSG_DEBUG,
00493                                            "httpread zero content length(%p)",
00494                                            h);
00495                         goto got_file;
00496                 }
00497         }
00498 
00499         /* Certain types of requests never have data and so
00500          * must be specially recognized.
00501          */
00502         if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
00503             !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
00504             !os_strncasecmp(h->hdr, "HEAD", 4) ||
00505             !os_strncasecmp(h->hdr, "GET", 3)) {
00506                 if (!h->got_body) {
00507                         if (httpread_debug >= 10)
00508                                 wpa_printf(MSG_DEBUG,
00509                                            "httpread NO BODY for sp. type");
00510                 }
00511                 h->got_body = 1;
00512                 goto got_file;
00513         }
00514 
00515         /* Data can be just plain binary data, or if "chunked"
00516          * consists of chunks each with a header, ending with
00517          * an ending header.
00518          */
00519         if (nread == 0)
00520                 goto get_more;
00521         if (!h->got_body) {
00522                 /* Here to get (more of) body */
00523                 /* ensure we have enough room for worst case for body
00524                  * plus a null termination character
00525                  */
00526                 if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
00527                         char *new_body;
00528                         int new_alloc_nbytes;
00529 
00530                         if (h->body_nbytes >= h->max_bytes)
00531                                 goto bad;
00532                         new_alloc_nbytes = h->body_alloc_nbytes +
00533                                 HTTPREAD_BODYBUF_DELTA;
00534                         /* For content-length case, the first time
00535                          * through we allocate the whole amount
00536                          * we need.
00537                          */
00538                         if (h->got_content_length &&
00539                             new_alloc_nbytes < (h->content_length + 1))
00540                                 new_alloc_nbytes = h->content_length + 1;
00541                         if ((new_body = os_realloc(h->body, new_alloc_nbytes))
00542                             == NULL)
00543                                 goto bad;
00544 
00545                         h->body = new_body;
00546                         h->body_alloc_nbytes = new_alloc_nbytes;
00547                 }
00548                 /* add bytes */
00549                 bbp = h->body + h->body_nbytes;
00550                 for (;;) {
00551                         int ncopy;
00552                         /* See if we need to stop */
00553                         if (h->chunked && h->in_chunk_data == 0) {
00554                                 /* in chunk header */
00555                                 char *cbp = h->body + h->chunk_start;
00556                                 if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
00557                                     bbp[-1] == '\n') {
00558                                         /* end of chunk hdr line */
00559                                         /* hdr line consists solely
00560                                          * of a hex numeral and CFLF
00561                                          */
00562                                         if (!isxdigit(*cbp))
00563                                                 goto bad;
00564                                         h->chunk_size = strtoul(cbp, NULL, 16);
00565                                         /* throw away chunk header
00566                                          * so we have only real data
00567                                          */
00568                                         h->body_nbytes = h->chunk_start;
00569                                         bbp = cbp;
00570                                         if (h->chunk_size == 0) {
00571                                                 /* end of chunking */
00572                                                 /* trailer follows */
00573                                                 h->in_trailer = 1;
00574                                                 if (httpread_debug >= 20)
00575                                                         wpa_printf(
00576                                                                 MSG_DEBUG,
00577                                                                 "httpread end chunks(%p)", h);
00578                                                 break;
00579                                         }
00580                                         h->in_chunk_data = 1;
00581                                         /* leave chunk_start alone */
00582                                 }
00583                         } else if (h->chunked) {
00584                                 /* in chunk data */
00585                                 if ((h->body_nbytes - h->chunk_start) ==
00586                                     (h->chunk_size + 2)) {
00587                                         /* end of chunk reached,
00588                                          * new chunk starts
00589                                          */
00590                                         /* check chunk ended w/ CRLF
00591                                          * which we'll throw away
00592                                          */
00593                                         if (bbp[-1] == '\n' &&
00594                                             bbp[-2] == '\r') {
00595                                         } else
00596                                                 goto bad;
00597                                         h->body_nbytes -= 2;
00598                                         bbp -= 2;
00599                                         h->chunk_start = h->body_nbytes;
00600                                         h->in_chunk_data = 0;
00601                                         h->chunk_size = 0; /* just in case */
00602                                 }
00603                         } else if (h->got_content_length &&
00604                                    h->body_nbytes >= h->content_length) {
00605                                 h->got_body = 1;
00606                                 if (httpread_debug >= 10)
00607                                         wpa_printf(
00608                                                 MSG_DEBUG,
00609                                                 "httpread got content(%p)", h);
00610                                 goto got_file;
00611                         }
00612                         if (nread <= 0)
00613                                 break;
00614                         /* Now transfer. Optimize using memcpy where we can. */
00615                         if (h->chunked && h->in_chunk_data) {
00616                                 /* copy up to remainder of chunk data
00617                                  * plus the required CR+LF at end
00618                                  */
00619                                 ncopy = (h->chunk_start + h->chunk_size + 2) -
00620                                         h->body_nbytes;
00621                         } else if (h->chunked) {
00622                                 /*in chunk header -- don't optimize */
00623                                 *bbp++ = *rbp++;
00624                                 nread--;
00625                                 h->body_nbytes++;
00626                                 continue;
00627                         } else if (h->got_content_length) {
00628                                 ncopy = h->content_length - h->body_nbytes;
00629                         } else {
00630                                 ncopy = nread;
00631                         }
00632                         /* Note: should never be 0 */
00633                         if (ncopy > nread)
00634                                 ncopy = nread;
00635                         os_memcpy(bbp, rbp, ncopy);
00636                         bbp += ncopy;
00637                         h->body_nbytes += ncopy;
00638                         rbp += ncopy;
00639                         nread -= ncopy;
00640                 }       /* body copy loop */
00641         }       /* !got_body */
00642         if (h->chunked && h->in_trailer) {
00643                 /* If "chunked" then there is always a trailer,
00644                  * consisting of zero or more non-empty lines
00645                  * ending with CR LF and then an empty line w/ CR LF.
00646                  * We do NOT support trailers except to skip them --
00647                  * this is supported (generally) by the http spec.
00648                  */
00649                 bbp = h->body + h->body_nbytes;
00650                 for (;;) {
00651                         int c;
00652                         if (nread <= 0)
00653                                 break;
00654                         c = *rbp++;
00655                         nread--;
00656                         switch (h->trailer_state) {
00657                         case trailer_line_begin:
00658                                 if (c == '\r')
00659                                         h->trailer_state = trailer_empty_cr;
00660                                 else
00661                                         h->trailer_state = trailer_nonempty;
00662                                 break;
00663                         case trailer_empty_cr:
00664                                 /* end empty line */
00665                                 if (c == '\n') {
00666                                         h->trailer_state = trailer_line_begin;
00667                                         h->in_trailer = 0;
00668                                         if (httpread_debug >= 10)
00669                                                 wpa_printf(
00670                                                         MSG_DEBUG,
00671                                                         "httpread got content(%p)", h);
00672                                         h->got_body = 1;
00673                                         goto got_file;
00674                                 }
00675                                 h->trailer_state = trailer_nonempty;
00676                                 break;
00677                         case trailer_nonempty:
00678                                 if (c == '\r')
00679                                         h->trailer_state = trailer_nonempty_cr;
00680                                 break;
00681                         case trailer_nonempty_cr:
00682                                 if (c == '\n')
00683                                         h->trailer_state = trailer_line_begin;
00684                                 else
00685                                         h->trailer_state = trailer_nonempty;
00686                                 break;
00687                         }
00688                 }
00689         }
00690         goto get_more;
00691 
00692 bad:
00693         /* Error */
00694         wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
00695         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
00696         return;
00697 
00698 get_more:
00699         return;
00700 
00701 got_file:
00702         if (httpread_debug >= 10)
00703                 wpa_printf(MSG_DEBUG,
00704                            "httpread got file %d bytes type %d",
00705                            h->body_nbytes, h->hdr_type);
00706         /* Null terminate for convenience of some applications */
00707         if (h->body)
00708                 h->body[h->body_nbytes] = 0; /* null terminate */
00709         h->got_file = 1;
00710         /* Assume that we do NOT support keeping connection alive,
00711          * and just in case somehow we don't get destroyed right away,
00712          * unregister now.
00713          */
00714         if (h->sd_registered)
00715                 eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
00716         h->sd_registered = 0;
00717         /* The application can destroy us whenever they feel like...
00718          * cancel timeout.
00719          */
00720         if (h->to_registered)
00721                 eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
00722         h->to_registered = 0;
00723         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
00724 }
00725 
00726 
00727 /* httpread_create -- start a new reading session making use of eloop.
00728  * The new instance will use the socket descriptor for reading (until
00729  * it gets a file and not after) but will not close the socket, even
00730  * when the instance is destroyed (the application must do that).
00731  * Return NULL on error.
00732  *
00733  * Provided that httpread_create successfully returns a handle,
00734  * the callback fnc is called to handle httpread_event events.
00735  * The caller should do destroy on any errors or unknown events.
00736  *
00737  * Pass max_bytes == 0 to not read body at all (required for e.g.
00738  * reply to HEAD request).
00739  */
00740 struct httpread * httpread_create(
00741         int sd,  /* descriptor of TCP socket to read from */
00742         void (*cb)(struct httpread *handle, void *cookie,
00743                    enum httpread_event e),  /* call on event */
00744         void *cookie,    /* pass to callback */
00745         int max_bytes,    /* maximum body size else abort it */
00746         int timeout_seconds     /* 0; or total duration timeout period */
00747         )
00748 {
00749         struct httpread *h = NULL;
00750 
00751         h = os_zalloc(sizeof(*h));
00752         if (h == NULL)
00753                 goto fail;
00754         h->sd = sd;
00755         h->cb = cb;
00756         h->cookie = cookie;
00757         h->max_bytes = max_bytes;
00758         h->timeout_seconds = timeout_seconds;
00759 
00760         if (timeout_seconds > 0) {
00761                 if (eloop_register_timeout(timeout_seconds, 0,
00762                                            httpread_timeout_handler,
00763                                            NULL, h)) {
00764                         /* No way to recover (from malloc failure) */
00765                         goto fail;
00766                 }
00767                 h->to_registered = 1;
00768         }
00769         if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
00770                                 NULL, h)) {
00771                 /* No way to recover (from malloc failure) */
00772                 goto fail;
00773         }
00774         h->sd_registered = 1;
00775         return h;
00776 
00777 fail:
00778 
00779         /* Error */
00780         httpread_destroy(h);
00781         return NULL;
00782 }
00783 
00784 
00785 /* httpread_hdr_type_get -- When file is ready, returns header type. */
00786 enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
00787 {
00788         return h->hdr_type;
00789 }
00790 
00791 
00792 /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
00793  * or possibly NULL (which would be an error).
00794  */
00795 char * httpread_uri_get(struct httpread *h)
00796 {
00797         return h->uri;
00798 }
00799 
00800 
00801 /* httpread_reply_code_get -- When reply is ready, returns reply code */
00802 int httpread_reply_code_get(struct httpread *h)
00803 {
00804         return h->reply_code;
00805 }
00806 
00807 
00808 /* httpread_length_get -- When file is ready, returns file length. */
00809 int httpread_length_get(struct httpread *h)
00810 {
00811         return h->body_nbytes;
00812 }
00813 
00814 
00815 /* httpread_data_get -- When file is ready, returns file content
00816  * with null byte appened.
00817  * Might return NULL in some error condition.
00818  */
00819 void * httpread_data_get(struct httpread *h)
00820 {
00821         return h->body ? h->body : "";
00822 }
00823 
00824 
00825 /* httpread_hdr_get -- When file is ready, returns header content
00826  * with null byte appended.
00827  * Might return NULL in some error condition.
00828  */
00829 char * httpread_hdr_get(struct httpread *h)
00830 {
00831         return h->hdr;
00832 }
00833 
00834 
00835 /* httpread_hdr_line_get -- When file is ready, returns pointer
00836  * to line within header content matching the given tag
00837  * (after the tag itself and any spaces/tabs).
00838  *
00839  * The tag should end with a colon for reliable matching.
00840  *
00841  * If not found, returns NULL;
00842  */
00843 char * httpread_hdr_line_get(struct httpread *h, const char *tag)
00844 {
00845         int tag_len = os_strlen(tag);
00846         char *hdr = h->hdr;
00847         hdr = os_strchr(hdr, '\n');
00848         if (hdr == NULL)
00849                 return NULL;
00850         hdr++;
00851         for (;;) {
00852                 if (!os_strncasecmp(hdr, tag, tag_len)) {
00853                         hdr += tag_len;
00854                         while (*hdr == ' ' || *hdr == '\t')
00855                                 hdr++;
00856                         return hdr;
00857                 }
00858                 hdr = os_strchr(hdr, '\n');
00859                 if (hdr == NULL)
00860                         return NULL;
00861                 hdr++;
00862         }
00863 }
00864 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines

Generated on Sat Nov 21 23:16:54 2009 for hostapd by  doxygen 1.6.1