| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- /*
- * Copyright (C) 2012 by egnite GmbH
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- */
- /*!
- * $Id$
- */
- #if !defined(HTTPD_EXCLUDE_DATE)
- #include <pro/rfctime.h>
- #endif
- #include <pro/uhttp/utils.h>
- #include <pro/uhttp/uhttpd.h>
- #include <pro/uhttp/mediatypes.h>
- #include <stdlib.h>
- #include <string.h>
- #include <io.h>
- #include <fcntl.h>
- #include <memdebug.h>
- #ifndef HTTP_MAX_REQUEST_SIZE
- #define HTTP_MAX_REQUEST_SIZE 64
- #endif
- /*! Constant string "GET". */
- const char ct_GET[] = "GET";
- /*! Constant string "HEAD". */
- const char ct_HEAD[] = "HEAD";
- /*! Constant string "POST". */
- const char ct_POST[] = "POST";
- /*! Constant string "Content-Disposition". */
- const char ct_Content_Disposition[] = "Content-Disposition";
- /*! Constant string "Content-Type". */
- const char ct_Content_Type[] = "Content-Type";
- /*! Constant string "Accept-Encoding". */
- const char ct_Accept_Encoding[] = "Accept-Encoding";
- /*! Constant string "Authorization". */
- const char ct_Authorization[] = "Authorization";
- /*! Constant string "Connection". */
- const char ct_Connection[] = "Connection";
- /*! Constant string "close". */
- const char ct_close[] = "close";
- /*! Constant string "keep-alive". */
- const char ct_Keep_Alive[] = "keep-alive";
- /*! Constant string "Content-Length". */
- const char ct_Content_Length[] = "Content-Length";
- /*! Constant string "Cookie". */
- const char ct_Cookie[] = "Cookie";
- /*! Constant string "Host". */
- const char ct_Host[] = "Host";
- /*! Constant string "If-Modified-Since". */
- const char ct_If_Modified_Since[] = "If-Modified-Since";
- /*! Constant string "Referer". */
- const char ct_Referer[] = "Referer";
- /*! Constant string "User-Agent". */
- const char ct_User_Agent[] = "User-Agent";
- /*! Constant string "Last-Modified". */
- const char ct_Last_Modified[] = "Last-Modified";
- /*! Constant string "Expires". */
- const char ct_Expires[] = "Expires";
- /*! Constant string "Date". */
- const char ct_Date[] = "Date";
- /*! Constant string "Content-Encoding". */
- const char ct_Content_Encoding[] = "Content-Encoding";
- /*! Constant string "Location". */
- const char ct_Location[] = "Location";
- char *http_root_path;
- static int HttpAuthValidateAll(HTTPD_SESSION *req);
- HTTP_AUTH_VALIDATOR httpd_auth_validator = HttpAuthValidateAll;
- static int HttpLocationRedirNone(HTTPD_SESSION *hs);
- HTTP_LOC_REDIRECTOR httpd_loc_redirector = HttpLocationRedirNone;
- static int HttpAuthValidateAll(HTTPD_SESSION *req)
- {
- (void)req;
- return 0;
- }
- static int HttpLocationRedirNone(HTTPD_SESSION *hs)
- {
- (void)hs;
- return -1;
- }
- char *HttpArgParseFirst(HTTP_REQUEST * req)
- {
- req->req_argp = req->req_query;
- return HttpArgParseNext(req);
- }
- char *HttpArgParseNext(HTTP_REQUEST * req)
- {
- char *rp = NULL;
- char *cp;
- int len;
- if (req->req_argp) {
- cp = strchr(req->req_argp, '&');
- if (cp) {
- len = (int) (cp - req->req_argp);
- cp++;
- rp = malloc(len + 1);
- if (rp) {
- memcpy(rp, req->req_argp, len);
- *(rp + len) = '\0';
- }
- } else {
- rp = strdup(req->req_argp);
- }
- req->req_argp = cp;
- if (rp) {
- cp = strchr(rp, '=');
- if (cp) {
- *cp++ = '\0';
- HttpUrlUnescape(cp);
- }
- req->req_argv = cp;
- }
- free(req->req_argn);
- req->req_argn = rp;
- HttpUrlUnescape(rp);
- }
- return rp;
- }
- char *HttpArgReadNext(HTTPD_SESSION *hs, long *avail)
- {
- int bufsiz;
- int got;
- char *cp;
- char *rp = NULL;
- char *buf = NULL;
- if (*avail) {
- bufsiz = *avail > 128 ? 128 : *avail;
- buf = malloc(bufsiz + 1);
- if (buf) {
- got = StreamReadUntilChars(hs->s_stream, "&", NULL, buf, bufsiz);
- if (got > 0) {
- *avail -= got;
- rp = strdup(buf);
- free(hs->s_req.req_argn);
- hs->s_req.req_argn = rp;
- cp = strchr(rp, '=');
- if (cp) {
- *cp++ = '\0';
- HttpUrlUnescape(cp);
- hs->s_req.req_argv = cp;
- }
- HttpUrlUnescape(rp);
- }
- free(buf);
- }
- }
- return rp;
- }
- char *HttpArgValue(HTTP_REQUEST * req)
- {
- return req->req_argv;
- }
- const char *HttpArgValueSub(const char *str, const char *name, int *len)
- {
- const char *cp = str;
- size_t namelen = strlen(name);
- *len = 0;
- for (;;) {
- /* Move to the next disposition parameter. */
- cp = strchr(cp, ';');
- if (cp == NULL) {
- break;
- }
- /* Skip leading spaces. */
- while (*++cp == ' ');
- /* Check the parameter name. */
- if (strncasecmp(cp, name, namelen) == 0) {
- cp += namelen;
- /* Skip the name. */
- while (*cp && strchr("=\";", *cp) == NULL) {
- cp++;
- }
- /* Check if we have a name/value pair. */
- if (*cp == '=') {
- /* Skip leading spaces. */
- while (*++cp == ' ');
- /* Skip leading quote. */
- cp += *cp == '"';
- /* Determine the length of the value. */
- while (cp[*len] && strchr("\";", cp[*len]) == NULL) {
- (*len)++;
- }
- break;
- }
- }
- }
- return cp;
- }
- #if HTTP_VERSION >= 0x10
- int HttpParseMultipartHeader(HTTPD_SESSION *hs, const char *bnd, long *avail)
- {
- int rc = -1;
- int got = 0;
- char *buf;
- char **strval;
- int bndlen = strlen(bnd);
- buf = malloc(MIN(*avail, HTTP_MAX_REQUEST_SIZE) + 1);
- if (buf == NULL) {
- return -1;
- }
- free(hs->s_req.req_bnd_type);
- hs->s_req.req_bnd_type = NULL;
- free(hs->s_req.req_bnd_dispo);
- hs->s_req.req_bnd_dispo = NULL;
- while (*avail) {
- /* Read the next line, expecting a boundary. */
- got = StreamReadUntilChars(hs->s_stream, "\n", "\r", buf, MIN(*avail, HTTP_MAX_REQUEST_SIZE));
- if (got <= 0) {
- /* Broken connection. */
- break;
- }
- *avail -= got;
- if (got > bndlen && strncmp(buf, bnd, bndlen) == 0) {
- /* Found a boundary. */
- break;
- }
- }
- /* Got boundary, check if it's the last one. */
- if (got < bndlen + 2 || strncmp(buf + bndlen, "--", 2)) {
- while (*avail) {
- /* Get next header name. */
- got = StreamReadUntilChars(hs->s_stream, ":\n", "\r", buf, MIN(*avail, HTTP_MAX_REQUEST_SIZE));
- if (got <= 0) {
- /* Broken connection, stop parsing. */
- break;
- }
- *avail -= got;
- if (*buf == '\0') {
- /* Empty line marks the end of the request header. */
- rc = 0;
- break;
- }
- strval = NULL;
- if (strcasecmp(buf, ct_Content_Disposition) == 0) {
- strval = &hs->s_req.req_bnd_dispo;
- }
- else if (strcasecmp(buf, ct_Content_Type) == 0) {
- strval = &hs->s_req.req_bnd_type;
- }
- else {
- got = StreamReadUntilChars(hs->s_stream, "\n", NULL, NULL, *avail);
- if (got <= 0) {
- break;
- }
- *avail -= got;
- }
- if (strval) {
- got = StreamReadUntilChars(hs->s_stream, "\n", "\r", buf, MIN(*avail, HTTP_MAX_REQUEST_SIZE));
- if (got <= 0) {
- break;
- }
- *avail -= got;
- *strval = strdup(buf);
- }
- }
- }
- free(buf);
- return rc;
- }
- #endif
- int HttpParseHeader(HTTPD_SESSION *hs)
- {
- int got;
- char *buf;
- char *cp;
- buf = malloc(HTTP_MAX_REQUEST_SIZE + 1);
- if (buf == NULL) {
- return -1;
- }
- memset(&hs->s_req, 0, sizeof(HTTP_REQUEST));
- /* Read the first word of the request. */
- got = StreamReadUntilChars(hs->s_stream, " \n", "\r", buf, HTTP_MAX_REQUEST_SIZE);
- if (got <= 0) {
- free(buf);
- return -1;
- }
- /* Expect a valid method. */
- if (strcasecmp(buf, ct_GET) == 0) {
- hs->s_req.req_method = HTTP_METHOD_GET;
- }
- #if HTTP_VERSION >= 0x10
- else if (strcasecmp(buf, ct_HEAD) == 0) {
- hs->s_req.req_method = HTTP_METHOD_HEAD;
- }
- else if (strcasecmp(buf, ct_POST) == 0) {
- hs->s_req.req_method = HTTP_METHOD_POST;
- }
- #endif
- else {
- /* Method not implemented. */
- HttpSendError(hs, 501);
- free(buf);
- return -1;
- }
- /* Read the second word of the request. */
- got = StreamReadUntilChars(hs->s_stream, " \n", "\r", buf, HTTP_MAX_REQUEST_SIZE);
- /* Expect a valid URI. */
- cp = strchr(buf, '?');
- if (cp) {
- *cp++ = '\0';
- hs->s_req.req_query = strdup(cp);
- }
- hs->s_req.req_url = UriUnescape(strdup(buf));
- /* Read the remaining part of the request. */
- got = StreamReadUntilChars(hs->s_stream, "\n", " \r", buf, HTTP_MAX_REQUEST_SIZE);
- /* If no HTTP version is provided, then assume HTTP/0.9. */
- if (strncasecmp(buf, "HTTP/", 5)) {
- hs->s_req.req_version = 0x09;
- } else {
- hs->s_req.req_version = buf[5] - '0';
- hs->s_req.req_version <<= 4;
- hs->s_req.req_version += buf[6] - '0';
- }
- #if HTTP_VERSION >= 0x10
- #if HTTP_VERSION >= 0x11 && HTTP_KEEP_ALIVE_REQ
- hs->s_req.req_connection = HTTP_CONN_KEEP_ALIVE;
- #endif
- do {
- char **strval;
- /* Get next header name. */
- if (StreamReadUntilChars(hs->s_stream, ":\n", "\r", buf, HTTP_MAX_REQUEST_SIZE) <= 0) {
- /* Broken connection, stop parsing. */
- break;
- }
- if (*buf == '\0') {
- /* Empty line marks the end of the request header. */
- break;
- }
- strval = NULL;
- if (strcasecmp(buf, ct_Accept_Encoding) == 0) {
- strval = &hs->s_req.req_encoding;
- }
- else if (strcasecmp(buf, ct_Authorization) == 0) {
- strval = &hs->s_req.req_auth;
- }
- #if HTTP_KEEP_ALIVE_REQ
- else if (strcasecmp(buf, ct_Connection) == 0) {
- got = StreamReadUntilChars(hs->s_stream, "\n", "\r", buf, HTTP_MAX_REQUEST_SIZE);
- if (strcasecmp(buf, ct_close) == 0) {
- hs->s_req.req_connection = HTTP_CONN_CLOSE;
- }
- else if (strcasecmp(buf, ct_Keep_Alive) == 0) {
- hs->s_req.req_connection = HTTP_CONN_KEEP_ALIVE;
- }
- }
- #endif
- else if (strcasecmp(buf, ct_Content_Length) == 0) {
- got = StreamReadUntilChars(hs->s_stream, "\n", "\r", buf, HTTP_MAX_REQUEST_SIZE);
- hs->s_req.req_length = atol(buf);
- }
- else if (strcasecmp(buf, ct_Content_Type) == 0) {
- strval = &hs->s_req.req_type;
- }
- else if (strcasecmp(buf, ct_Cookie) == 0) {
- strval = &hs->s_req.req_cookie;
- }
- else if (strcasecmp(buf, ct_Host) == 0) {
- strval = &hs->s_req.req_host;
- }
- #if !defined(HTTPD_EXCLUDE_DATE)
- else if (strcasecmp(buf, ct_If_Modified_Since) == 0) {
- got = StreamReadUntilChars(hs->s_stream, "\n", "\r", buf, HTTP_MAX_REQUEST_SIZE);
- hs->s_req.req_ims = RfcTimeParse(buf);
- }
- #endif
- else if (strcasecmp(buf, ct_Referer) == 0) {
- strval = &hs->s_req.req_referer;
- }
- else if (strcasecmp(buf, ct_User_Agent) == 0) {
- strval = &hs->s_req.req_agent;
- }
- else {
- got = StreamReadUntilChars(hs->s_stream, "\n", NULL, NULL, 9999);
- }
- if (strval) {
- got = StreamReadUntilChars(hs->s_stream, "\n", "\r", buf, HTTP_MAX_REQUEST_SIZE);
- *strval = strdup(buf);
- }
- } while(1);
- #endif
- free(buf);
- return 0;
- }
- int HttpRegisterRootPath(char *path)
- {
- if (http_root_path) {
- free(http_root_path);
- }
- if (path) {
- http_root_path = strdup(path);
- if (http_root_path == NULL) {
- return -1;
- }
- } else {
- http_root_path = NULL;
- }
- return 0;
- }
- void HttpdClientHandler(HTTP_STREAM *sp)
- {
- char *filename;
- HTTPD_SESSION *hs;
- HTTP_REQUEST *req;
- MEDIA_TYPE_ENTRY *mt;
- int err = 0;
- hs = malloc(sizeof(HTTPD_SESSION));
- if (hs) {
- do {
- hs->s_stream = sp;
- req = &hs->s_req;
- if (HttpParseHeader(hs)) {
- break;
- }
- if ((*httpd_auth_validator) (hs)) {
- err = 401;
- }
- else if ((*httpd_loc_redirector) (hs)) {
- /* No redirection available. */
- filename = AllocConcatStrings(HTTP_ROOT, req->req_url, NULL);
- if (filename) {
- mt = GetMediaTypeEntry(filename);
- if (mt == NULL) {
- err = 404;
- } else {
- mt->media_handler(hs, mt, filename);
- }
- free(filename);
- } else {
- err = 404;
- }
- }
- if (err) {
- HttpSendError(hs, err);
- }
- free(req->req_url);
- free(req->req_query);
- free(req->req_argp);
- free(req->req_argn);
- #if HTTP_VERSION >= 0x10
- free(req->req_realm);
- free(req->req_type);
- free(req->req_cookie);
- free(req->req_auth);
- free(req->req_agent);
- free(req->req_referer);
- free(req->req_host);
- free(req->req_encoding);
- free(req->req_bnd_dispo);
- free(req->req_bnd_type);
- #endif
- } while(req->req_connection == HTTP_CONN_KEEP_ALIVE);
- free(hs);
- }
- }
|