00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00138 #include <cfg/arch.h>
00139 #include <cfg/http.h>
00140
00141 #include <string.h>
00142 #include <io.h>
00143 #include <fcntl.h>
00144 #include <ctype.h>
00145 #include <stdlib.h>
00146 #include <sys/stat.h>
00147
00148 #include <sys/heap.h>
00149 #include <sys/version.h>
00150
00151 #include <pro/rfctime.h>
00152 #include <pro/httpd.h>
00153
00154 #include "dencode.h"
00155 #include "httpd_p.h"
00156
00157
00159 #ifndef HTTP_MAJOR_VERSION
00160 #define HTTP_MAJOR_VERSION 1
00161 #endif
00162
00164 #ifndef HTTP_MINOR_VERSION
00165 #define HTTP_MINOR_VERSION 1
00166 #endif
00167
00169 #ifndef HTTP_KEEP_ALIVE_REQ
00170 #define HTTP_KEEP_ALIVE_REQ 0
00171 #endif
00172
00174 #ifndef HTTP_MAX_REQUEST_SIZE
00175 #define HTTP_MAX_REQUEST_SIZE 256
00176 #endif
00177
00179 #ifndef HTTP_FILE_CHUNK_SIZE
00180 #define HTTP_FILE_CHUNK_SIZE 512
00181 #endif
00182
00187
00191 MIMETYPES mimeTypes[] = {
00192 {
00193 ".txt", "text/plain", NULL}, {
00194 ".html", "text/html", NULL}, {
00195 ".shtml", "text/html", NULL}, {
00196 ".asp", "text/html", NULL}, {
00197 ".htm", "text/html", NULL}, {
00198 ".gif", "image/gif", NULL}, {
00199 ".jpg", "image/jpeg", NULL}, {
00200 ".png", "image/png", NULL}, {
00201 ".pdf", "application/pdf", NULL}, {
00202 ".js", "application/x-javascript", NULL}, {
00203 ".jar", "application/x-java-archive", NULL}, {
00204 ".css", "text/css", NULL}, {
00205 ".xml", "text/xml", NULL}, {
00206 NULL, NULL, NULL}
00207 };
00208
00209 static u_long http_optflags;
00210
00222 void NutHttpSendHeaderTop(FILE * stream, REQUEST * req, int status, char *title)
00223 {
00224 static prog_char fmt_P[] = "HTTP/%d.%d %d %s\r\nServer: Ethernut %s\r\n";
00225
00226 fprintf_P(stream, fmt_P, HTTP_MAJOR_VERSION, HTTP_MINOR_VERSION, status, title, NutVersionString());
00227 #if !defined(HTTPD_EXCLUDE_DATE)
00228 if (http_optflags & HTTP_OF_USE_HOST_TIME) {
00229 time_t now = time(NULL);
00230 fprintf(stream, "Date: %s GMT\r\n", Rfc1123TimeString(gmtime(&now)));
00231 }
00232 #endif
00233 }
00234
00251 void NutHttpSendHeaderBot(FILE * stream, char *mime_type, long bytes)
00252 {
00253 NutHttpSendHeaderBottom( stream, 0, mime_type, bytes );
00254 }
00255
00270 void NutHttpSendHeaderBottom(FILE * stream, REQUEST * req, char *mime_type, long bytes)
00271 {
00272 static prog_char typ_fmt_P[] = "Content-Type: %s\r\n";
00273 static prog_char len_fmt_P[] = "Content-Length: %ld\r\n";
00274 static prog_char con_str_P[] = "Connection: ";
00275 static prog_char ccl_str_P[] = "close\r\n\r\n";
00276
00277 if (mime_type)
00278 fprintf_P(stream, typ_fmt_P, mime_type);
00279 if (bytes >= 0)
00280 fprintf_P(stream, len_fmt_P, bytes);
00281 fputs_P(con_str_P, stream);
00282 #if HTTP_KEEP_ALIVE_REQ
00283 if ( req && req->req_connection == HTTP_CONN_KEEP_ALIVE) {
00284 static prog_char cka_str_P[] = "Keep-Alive\r\n\r\n";
00285 fputs_P(cka_str_P, stream);
00286 }
00287 else {
00288 fputs_P(ccl_str_P, stream);
00289 }
00290 #else
00291 fputs_P(ccl_str_P, stream);
00292 #endif
00293 }
00294
00305 void NutHttpSendError(FILE * stream, REQUEST * req, int status)
00306 {
00307 static prog_char err_fmt_P[] = "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD><BODY>%d %s</BODY></HTML>\r\n";
00308 static prog_char auth_fmt_P[] = "WWW-Authenticate: Basic realm=\"%s\"\r\n";
00309 char *title;
00310
00311 switch (status) {
00312 case 304:
00313 title = "Not Modified";
00314 break;
00315 case 400:
00316 title = "Bad Request";
00317 break;
00318 case 401:
00319 title = "Unauthorized";
00320 break;
00321 case 404:
00322 title = "Not Found";
00323 break;
00324 case 500:
00325 title = "Internal Error";
00326 break;
00327 case 501:
00328 title = "Not Implemented";
00329 break;
00330 default:
00331 title = "Error";
00332 break;
00333 }
00334 #if HTTP_KEEP_ALIVE_REQ
00335 if (status >= 400) {
00336 req->req_connection = HTTP_CONN_CLOSE;
00337 }
00338 #endif
00339 NutHttpSendHeaderTop(stream, req, status, title);
00340 if (status == 401) {
00341 char *cp = 0;
00342 char *realm = req->req_url;
00343
00344 if ((cp = strrchr(realm, '/')) != NULL)
00345 *cp = 0;
00346 else
00347 realm = ".";
00348 fprintf_P(stream, auth_fmt_P, realm);
00349 if (cp)
00350 *cp = '/';
00351 }
00352 NutHttpSendHeaderBottom(stream, req, "text/html", -1);
00353 fprintf_P(stream, err_fmt_P, status, title, status, title);
00354 }
00355
00356 static MIMETYPES *GetMimeEntry(char *name)
00357 {
00358 MIMETYPES *rc;
00359 int fl;
00360
00361 if (name == NULL || (fl = strlen(name)) == 0) {
00362 return &mimeTypes[1];
00363 }
00364 for (rc = mimeTypes; rc->mtyp_ext; rc++) {
00365 if (strcasecmp(&(name[fl - strlen(rc->mtyp_ext)]), rc->mtyp_ext) == 0) {
00366 return rc;
00367 }
00368 }
00369 return &mimeTypes[0];
00370 }
00371
00385 char *NutGetMimeType(char *name)
00386 {
00387 return GetMimeEntry(name)->mtyp_type;
00388 }
00389
00405 void *NutGetMimeHandler(char *name)
00406 {
00407 return GetMimeEntry(name)->mtyp_handler;
00408 }
00409
00421 void NutHttpURLDecode(char *str)
00422 {
00423 register char *ptr1, *ptr2, ch;
00424 char hexstr[3] = { 0, 0, 0 };
00425 for (ptr1 = ptr2 = str; *ptr1; ptr1++) {
00426 if (*ptr1 == '+')
00427 *ptr2++ = ' ';
00428 else if (*ptr1 == '%') {
00429 hexstr[0] = ptr1[1];
00430 hexstr[1] = ptr1[2];
00431 ch = strtol(hexstr, 0, 16);
00432 *ptr2++ = ch;
00433 ptr1 += 2;
00434 } else
00435 *ptr2++ = *ptr1;
00436 }
00437 *ptr2 = 0;
00438 }
00439
00450 void NutHttpProcessQueryString(REQUEST * req)
00451 {
00452 register int i;
00453 register char *ptr;
00454
00455 if (!req->req_query)
00456 return;
00457
00458 req->req_numqptrs = 1;
00459 for (ptr = req->req_query; *ptr; ptr++)
00460 if (*ptr == '&')
00461 req->req_numqptrs++;
00462
00463 req->req_qptrs = (char **) NutHeapAlloc(sizeof(char *) * (req->req_numqptrs * 2));
00464 if (req->req_qptrs == NULL) {
00465
00466 req->req_numqptrs = 0;
00467 return;
00468 }
00469 req->req_qptrs[0] = req->req_query;
00470 req->req_qptrs[1] = NULL;
00471 for (ptr = req->req_query, i = 2; *ptr; ptr++) {
00472 if (*ptr == '&') {
00473 req->req_qptrs[i] = ptr + 1;
00474 req->req_qptrs[i + 1] = NULL;
00475 *ptr = 0;
00476 i += 2;
00477 }
00478 }
00479
00480 for (i = 0; i < req->req_numqptrs; i++) {
00481 for (ptr = req->req_qptrs[i * 2]; *ptr; ptr++) {
00482 if (*ptr == '=') {
00483 req->req_qptrs[i * 2 + 1] = ptr + 1;
00484 *ptr = 0;
00485 NutHttpURLDecode(req->req_qptrs[i * 2 + 1]);
00486 break;
00487 }
00488 }
00489 NutHttpURLDecode(req->req_qptrs[i * 2]);
00490 }
00491 }
00492
00493 static void NutHttpProcessFileRequest(FILE * stream, REQUEST * req)
00494 {
00495 int fd;
00496 int n;
00497 char *data;
00498 long file_len;
00499 void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
00500 char *mime_type;
00501 char *filename = NULL;
00502 char *modstr = NULL;
00503
00504
00505
00506
00507 if (NutHttpAuthValidate(req)) {
00508 NutHttpSendError(stream, req, 401);
00509 return;
00510 }
00511
00512
00513
00514
00515 if (NutCgiCheckRequest(stream, req)) {
00516 return;
00517 }
00518
00519 for (n = 0, fd = -1; default_files[n]; n++) {
00520 filename = CreateFilePath(req->req_url, default_files[n]);
00521 if (filename == NULL) {
00522 NutHttpSendError(stream, req, 500);
00523 return;
00524 }
00525
00526
00527
00528
00529
00530
00531 if ((fd = _open(filename, _O_BINARY | _O_RDONLY)) != -1) {
00532 if (_filelength(fd)) {
00533 break;
00534 }
00535 _close(fd);
00536 }
00537 NutHeapFree(filename);
00538 }
00539 if (fd == -1) {
00540 NutHttpSendError(stream, req, 404);
00541 return;
00542 }
00543
00544
00545 mime_type = NutGetMimeType(filename);
00546 handler = NutGetMimeHandler(filename);
00547
00548 #if !defined(HTTPD_EXCLUDE_DATE)
00549
00550
00551
00552 if (handler == NULL && (http_optflags & HTTP_OF_USE_FILE_TIME)) {
00553 struct stat s;
00554 time_t ftime;
00555 char *time_str;
00556
00557 if (stat(filename, &s) == 0) {
00558 ftime = s.st_mtime;
00559 }
00560 else {
00561
00562 ftime = RfcTimeParse("Fri " __DATE__ " " __TIME__);
00563 }
00564
00565
00566 if (req->req_ims && s.st_mtime <= req->req_ims) {
00567 _close(fd);
00568 NutHttpSendError(stream, req, 304);
00569 NutHeapFree(filename);
00570 return;
00571 }
00572
00573
00574 time_str = Rfc1123TimeString(gmtime(&ftime));
00575 if ((modstr = NutHeapAlloc(strlen(time_str) + 1)) != NULL) {
00576 strcpy(modstr, time_str);
00577 }
00578 }
00579 #endif
00580
00581
00582 NutHeapFree(filename);
00583
00584 NutHttpSendHeaderTop(stream, req, 200, "Ok");
00585 if (modstr) {
00586 fprintf(stream, "Last-Modified: %s GMT\r\n", modstr);
00587 NutHeapFree(modstr);
00588 }
00589
00590 file_len = _filelength(fd);
00591
00592 if (handler) {
00593 NutHttpSendHeaderBottom(stream, req, mime_type, -1);
00594 handler(stream, fd, file_len, http_root, req);
00595 }
00596
00597 else {
00598 NutHttpSendHeaderBottom(stream, req, mime_type, file_len);
00599 if (req->req_method != METHOD_HEAD) {
00600 size_t size = HTTP_FILE_CHUNK_SIZE;
00601
00602 if ((data = NutHeapAlloc(size)) != NULL) {
00603 while (file_len) {
00604 if (file_len < HTTP_FILE_CHUNK_SIZE)
00605 size = (size_t) file_len;
00606
00607 n = _read(fd, data, size);
00608 if (n <= 0) {
00609
00610 break;
00611 }
00612 if (fwrite(data, 1, n, stream) == 0) {
00613 break;
00614 }
00615 file_len -= (long) n;
00616 }
00617 NutHeapFree(data);
00618 }
00619 }
00620 }
00621 _close(fd);
00622 }
00623
00627 static char *NextWord(char *str)
00628 {
00629 while (*str && *str != ' ' && *str != '\t')
00630 str++;
00631 if (*str)
00632 *str++ = 0;
00633 while (*str == ' ' || *str == '\t')
00634 str++;
00635 return str;
00636 }
00637
00643 static REQUEST *CreateRequestInfo(void)
00644 {
00645 REQUEST *req;
00646
00647 if ((req = NutHeapAllocClear(sizeof(REQUEST))) != NULL) {
00648 req->req_version = HTTP_MAJOR_VERSION * 10 + HTTP_MINOR_VERSION;
00649 }
00650 return req;
00651 }
00652
00665 int NutRegisterHttpRoot(char *path)
00666 {
00667 int len;
00668
00669 if (http_root)
00670 NutHeapFree(http_root);
00671 if (path && (len = strlen(path)) != 0) {
00672 if ((http_root = NutHeapAlloc(len + 1)) != NULL)
00673 strcpy(http_root, path);
00674 else
00675 return -1;
00676 } else
00677 http_root = NULL;
00678
00679 return 0;
00680 }
00681
00690 void NutHttpSetOptionFlags(u_long flags)
00691 {
00692 http_optflags = flags;
00693 }
00694
00700 u_long NutHttpGetOptionFlags(void)
00701 {
00702 return http_optflags;
00703 }
00704
00720 static int HeaderFieldValue(char **hfvp, CONST char *str)
00721 {
00722
00723 if (*hfvp == NULL) {
00724
00725 while (*str == ' ' || *str == '\t')
00726 str++;
00727
00728 if ((*hfvp = NutHeapAlloc(strlen(str) + 1)) == NULL)
00729 return -1;
00730 strcpy(*hfvp, str);
00731 }
00732 return 0;
00733 }
00734
00744 void NutHttpProcessRequest(FILE * stream)
00745 {
00746 REQUEST *req = NULL;
00747 char *method = NULL;
00748 char *path;
00749 char *line;
00750 char *protocol;
00751 char *cp;
00752 #if HTTP_KEEP_ALIVE_REQ
00753 int keep_alive_max = HTTP_KEEP_ALIVE_REQ;
00754 #endif
00755
00756 for(;;) {
00757
00758 DestroyRequestInfo(req);
00759 if ((req = CreateRequestInfo()) == NULL)
00760 break;
00761 if (method)
00762 NutHeapFree(method);
00763
00764
00765 if ((method = NutHeapAlloc(HTTP_MAX_REQUEST_SIZE)) == NULL) {
00766 break;
00767 }
00768 if (fgets(method, HTTP_MAX_REQUEST_SIZE, stream) == NULL) {
00769 break;
00770 }
00771 if ((cp = strchr(method, '\r')) != NULL)
00772 *cp = 0;
00773 if ((cp = strchr(method, '\n')) != NULL)
00774 *cp = 0;
00775
00776
00777
00778
00779 if ((line = NutHeapAlloc(HTTP_MAX_REQUEST_SIZE)) == NULL) {
00780 break;
00781 }
00782 for (;;) {
00783
00784 if (fgets(line, HTTP_MAX_REQUEST_SIZE, stream) == NULL)
00785 break;
00786 if ((cp = strchr(line, '\r')) != 0)
00787 *cp = 0;
00788 if ((cp = strchr(line, '\n')) != 0)
00789 *cp = 0;
00790 if (*line == 0)
00791
00792 break;
00793 if (strncasecmp(line, "Authorization:", 14) == 0) {
00794 if (HeaderFieldValue(&req->req_auth, line + 14))
00795 break;
00796 } else if (strncasecmp(line, "Content-Length:", 15) == 0) {
00797 req->req_length = atol(line + 15);
00798 } else if (strncasecmp(line, "Content-Type:", 13) == 0) {
00799 if (HeaderFieldValue(&req->req_type, line + 13))
00800 break;
00801 } else if (strncasecmp(line, "Cookie:", 7) == 0) {
00802 if (HeaderFieldValue(&req->req_cookie, line + 7))
00803 break;
00804 } else if (strncasecmp(line, "User-Agent:", 11) == 0) {
00805 if (HeaderFieldValue(&req->req_agent, line + 11))
00806 break;
00807 #if !defined(HTTPD_EXCLUDE_DATE)
00808 } else if (strncasecmp(line, "If-Modified-Since:", 18) == 0) {
00809 req->req_ims = RfcTimeParse(line + 18);
00810 #endif
00811 } else if (strncasecmp(line, "Referer:", 8) == 0) {
00812 if (HeaderFieldValue(&req->req_referer, line + 8))
00813 break;
00814 } else if (strncasecmp(line, "Host:", 5) == 0) {
00815 if (HeaderFieldValue(&req->req_host, line + 5))
00816 break;
00817 }
00818 #if HTTP_KEEP_ALIVE_REQ
00819 else if (strncasecmp(line, "Connection:", 11) == 0) {
00820 if (strncasecmp(line + 12, "close", 5) == 0) {
00821 req->req_connection = HTTP_CONN_CLOSE;
00822 }
00823 else if (strncasecmp(line + 12, "Keep-Alive", 10) == 0) {
00824 req->req_connection = HTTP_CONN_KEEP_ALIVE;
00825 }
00826 }
00827 #endif
00828 }
00829 if (line) {
00830 NutHeapFree(line);
00831 }
00832 path = NextWord(method);
00833 protocol = NextWord(path);
00834 NextWord(protocol);
00835
00836
00837 if (strcasecmp(method, "GET") == 0)
00838 req->req_method = METHOD_GET;
00839 else if (strcasecmp(method, "HEAD") == 0)
00840 req->req_method = METHOD_HEAD;
00841 else if (strcasecmp(method, "POST") == 0)
00842 req->req_method = METHOD_POST;
00843 else {
00844 NutHttpSendError(stream, req, 501);
00845 break;
00846 }
00847 if (*path == 0 || *protocol == 0) {
00848 NutHttpSendError(stream, req, 400);
00849 break;
00850 }
00851
00852
00853 if (strcasecmp(protocol, "HTTP/1.0") == 0) {
00854 req->req_version = 10;
00855 #if HTTP_KEEP_ALIVE_REQ
00856 if (req->req_connection != HTTP_CONN_KEEP_ALIVE) {
00857 req->req_connection = HTTP_CONN_CLOSE;
00858 }
00859 #endif
00860 }
00861 #if HTTP_KEEP_ALIVE_REQ
00862 else if (req->req_connection != HTTP_CONN_CLOSE) {
00863 req->req_connection = HTTP_CONN_KEEP_ALIVE;
00864 }
00865
00866
00867 if (keep_alive_max > 0) {
00868 keep_alive_max--;
00869 }
00870 else {
00871 req->req_connection = HTTP_CONN_CLOSE;
00872 }
00873 #else
00874 req->req_connection = HTTP_CONN_CLOSE;
00875 #endif
00876
00877 if ((cp = strchr(path, '?')) != 0) {
00878 *cp++ = 0;
00879 if ((req->req_query = NutHeapAlloc(strlen(cp) + 1)) == NULL) {
00880 break;
00881 }
00882 strcpy(req->req_query, cp);
00883
00884 NutHttpProcessQueryString(req);
00885 }
00886
00887 if ((req->req_url = NutHeapAlloc(strlen(path) + 1)) == NULL) {
00888 break;
00889 }
00890 strcpy(req->req_url, path);
00891
00892 if (NutDecodePath(req->req_url) == 0) {
00893 NutHttpSendError(stream, req, 400);
00894 } else {
00895 NutHttpProcessFileRequest(stream, req);
00896 }
00897 fflush(stream);
00898
00899 if (req->req_connection == HTTP_CONN_CLOSE) {
00900 break;
00901 }
00902 }
00903 DestroyRequestInfo(req);
00904 if (method)
00905 NutHeapFree(method);
00906 }
00907