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
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101 #include <cfg/arch.h>
00102
00103 #include <string.h>
00104 #include <io.h>
00105 #include <fcntl.h>
00106 #include <ctype.h>
00107 #include <stdlib.h>
00108
00109 #include <sys/heap.h>
00110 #include <sys/version.h>
00111
00112 #include "dencode.h"
00113
00114 #include <pro/httpd.h>
00115
00120
00121 static struct {
00122 char *ext;
00123 char *type;
00124 void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
00125 } mimeTypes[] = {
00126 {
00127 ".txt", "text/plain", NULL}, {
00128 ".html", "text/html", NULL}, {
00129 ".shtml", "text/html", NULL}, {
00130 ".asp", "text/html", NULL}, {
00131 ".htm", "text/html", NULL}, {
00132 ".gif", "image/gif", NULL}, {
00133 ".jpg", "image/jpeg", NULL}, {
00134 ".png", "image/png", NULL}, {
00135 ".pdf", "application/pdf", NULL}, {
00136 ".js", "application/x-javascript", NULL}, {
00137 ".css", "text/css", NULL}, {
00138 ".xml", "text/xml", NULL}
00139 };
00140
00141 static char *http_root;
00142
00154 void NutHttpSendHeaderTop(FILE * stream, REQUEST * req, int status, char *title)
00155 {
00156 static prog_char fmt_P[] = "HTTP/%d.%d %d %s\r\nServer: Ethernut %s\r\n";
00157
00158 fprintf_P(stream, fmt_P, req->req_version / 10, req->req_version % 10, status, title, NutVersionString());
00159 }
00160
00175 void NutHttpSendHeaderBot(FILE * stream, char *mime_type, long bytes)
00176 {
00177 static prog_char typ_fmt_P[] = "Content-Type: %s\r\n";
00178 static prog_char len_fmt_P[] = "Content-Length: %ld\r\n";
00179 static prog_char ccl_str_P[] = "Connection: close\r\n\r\n";
00180
00181 if (mime_type)
00182 fprintf_P(stream, typ_fmt_P, mime_type);
00183 if (bytes >= 0)
00184 fprintf_P(stream, len_fmt_P, bytes);
00185 fputs_P(ccl_str_P, stream);
00186 }
00187
00198 void NutHttpSendError(FILE * stream, REQUEST * req, int status)
00199 {
00200 static prog_char err_fmt_P[] = "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD><BODY>%d %s</BODY></HTML>";
00201 static prog_char auth_fmt_P[] = "WWW-Authenticate: Basic realm=\"%s\"\r\n";
00202 char *title;
00203
00204 switch (status) {
00205 case 400:
00206 title = "Bad Request";
00207 break;
00208 case 401:
00209 title = "Unauthorized";
00210 break;
00211 case 404:
00212 title = "Not Found";
00213 break;
00214 case 500:
00215 title = "Internal Error";
00216 break;
00217 case 501:
00218 title = "Not Implemented";
00219 break;
00220 default:
00221 title = "Error";
00222 break;
00223 }
00224
00225 NutHttpSendHeaderTop(stream, req, status, title);
00226 if (status == 401) {
00227 char *cp = 0;
00228 char *realm = req->req_url;
00229
00230 if ((cp = strrchr(realm, '/')) != 0)
00231 *cp = 0;
00232 else
00233 realm = ".";
00234 fprintf_P(stream, auth_fmt_P, realm);
00235 if (cp)
00236 *cp = '/';
00237 }
00238 NutHttpSendHeaderBot(stream, "text/html", -1);
00239 fprintf_P(stream, err_fmt_P, status, title, status, title);
00240 }
00241
00256 char *NutGetMimeType(char *name)
00257 {
00258 size_t i;
00259 int fl;
00260
00261 if (name == 0 || (fl = strlen(name)) == 0)
00262 return mimeTypes[1].type;
00263 for (i = 0; i < sizeof(mimeTypes) / sizeof(*mimeTypes); i++)
00264 if (strcasecmp(&(name[fl - strlen(mimeTypes[i].ext)]), mimeTypes[i].ext) == 0)
00265 return mimeTypes[i].type;
00266 return mimeTypes[0].type;
00267 }
00268
00284 void *NutGetMimeHandler(char *name)
00285 {
00286 size_t i;
00287 int fl;
00288
00289 if (name == 0 || (fl = strlen(name)) == 0)
00290 return mimeTypes[1].handler;
00291 for (i = 0; i < sizeof(mimeTypes) / sizeof(*mimeTypes); i++)
00292 if (strcasecmp(&(name[fl - strlen(mimeTypes[i].ext)]), mimeTypes[i].ext) == 0)
00293 return mimeTypes[i].handler;
00294 return mimeTypes[0].handler;
00295 }
00296
00308 u_char NutSetMimeHandler(char *extension, void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req))
00309 {
00310 size_t i;
00311
00312 if (extension == NULL)
00313 return 1;
00314 for (i = 0; i < sizeof(mimeTypes) / sizeof(*mimeTypes); i++)
00315 if (strcasecmp(extension, mimeTypes[i].ext) == 0) {
00316 mimeTypes[i].handler = handler;
00317 return 0;
00318 }
00319 return 1;
00320 }
00321
00332 static char *hexdigits = "0123456789ABCDEF";
00333
00334 char *NutHttpURLEncode(char *str)
00335 {
00336 register char *ptr1, *ptr2;
00337 char *encstring;
00338 int numEncs = 0;
00339
00340 if (!str)
00341 return 0;
00342
00343
00344 for (ptr1 = str; *ptr1; ptr1++) {
00345 if (!isalnum(*ptr1) || *ptr1 == '%' || *ptr1 == '&'|| *ptr1 == '+' ||
00346 *ptr1 == ',' || *ptr1 == ':' || *ptr1 == ';'|| *ptr1 == '='|| *ptr1 == '?'|| *ptr1 == '@')
00347 numEncs++;
00348 }
00349
00350 encstring = (char *) NutHeapAlloc(strlen(str) + 2 * numEncs + 1);
00351
00352
00353
00354 ptr2 = encstring;
00355 for (ptr1 = str; *ptr1; ptr1++) {
00356 if (isalnum(*ptr1) || *ptr1 == '%' || *ptr1 == '&'|| *ptr1 == '+' ||
00357 *ptr1 == ',' || *ptr1 == ':' || *ptr1 == ';'|| *ptr1 == '='|| *ptr1 == '?'|| *ptr1 == '@')
00358 *ptr2++ = *ptr1;
00359 else {
00360 *ptr2++ = '%';
00361 *ptr2++ = hexdigits[(*ptr1 >> 4)];
00362 *ptr2++ = hexdigits[*ptr1 & 0x0F];
00363 }
00364 }
00365 *ptr2++ = 0;
00366 return encstring;
00367 }
00368
00380 void NutHttpURLDecode(char *str)
00381 {
00382 register char *ptr1, *ptr2, ch;
00383 char hexstr[3] = { 0, 0, 0 };
00384 for (ptr1 = ptr2 = str; *ptr1; ptr1++) {
00385 if (*ptr1 == '+')
00386 *ptr2++ = ' ';
00387 else if (*ptr1 == '%') {
00388 hexstr[0] = ptr1[1];
00389 hexstr[1] = ptr1[2];
00390 ch = strtol(hexstr, 0, 16);
00391 *ptr2++ = ch;
00392 ptr1 += 2;
00393 } else
00394 *ptr2++ = *ptr1;
00395 }
00396 *ptr2 = 0;
00397 }
00398
00409 void NutHttpProcessQueryString(REQUEST * req)
00410 {
00411 register int i;
00412 register char *ptr;
00413
00414 if (!req->req_query)
00415 return;
00416
00417 req->req_numqptrs = 1;
00418 for (ptr = req->req_query; *ptr; ptr++)
00419 if (*ptr == '&')
00420 req->req_numqptrs++;
00421
00422 req->req_qptrs = (char **) NutHeapAlloc(sizeof(char *) * (req->req_numqptrs * 2));
00423 if (!req->req_qptrs) {
00424
00425 req->req_numqptrs = 0;
00426 return;
00427 }
00428 req->req_qptrs[0] = req->req_query;
00429 req->req_qptrs[1] = 0;
00430 for (ptr = req->req_query, i = 2; *ptr; ptr++) {
00431 if (*ptr == '&') {
00432 req->req_qptrs[i] = ptr + 1;
00433 req->req_qptrs[i + 1] = 0;
00434 *ptr = 0;
00435 i += 2;
00436 }
00437 }
00438
00439 for (i = 0; i < req->req_numqptrs; i++) {
00440 for (ptr = req->req_qptrs[i * 2]; *ptr; ptr++) {
00441 if (*ptr == '=') {
00442 req->req_qptrs[i * 2 + 1] = ptr + 1;
00443 *ptr = 0;
00444 NutHttpURLDecode(req->req_qptrs[i * 2 + 1]);
00445 break;
00446 }
00447 }
00448 NutHttpURLDecode(req->req_qptrs[i * 2]);
00449 }
00450 }
00451
00464 void NutHttpProcessPostQuery(FILE *stream, REQUEST * req)
00465 {
00466 register int i;
00467 register char *ptr;
00468
00469 if (req->req_query != NULL)
00470 return;
00471
00472 if (!stream)
00473 return;
00474
00475 if (req->req_method == METHOD_POST) {
00476 req->req_query = NutHeapAllocClear(req->req_length+1);
00477 if (req->req_query == NULL) {
00478
00479 req->req_numqptrs = 0;
00480 NutHeapFree(req->req_query);
00481 return;
00482 }
00483 i = 0;
00484 while (i < req->req_length) {
00485 i += fread(&req->req_query[i], 1, req->req_length-i, stream);
00486 }
00487 } else return;
00488
00489 req->req_numqptrs = 1;
00490 for (ptr = req->req_query; *ptr; ptr++)
00491 if (*ptr == '&')
00492 req->req_numqptrs++;
00493
00494 req->req_qptrs = (char **) NutHeapAlloc(sizeof(char *) * (req->req_numqptrs * 2));
00495 if (!req->req_qptrs) {
00496
00497 req->req_numqptrs = 0;
00498 NutHeapFree(req->req_query);
00499 return;
00500 }
00501 req->req_qptrs[0] = req->req_query;
00502 req->req_qptrs[1] = 0;
00503 for (ptr = req->req_query, i = 2; *ptr; ptr++) {
00504 if (*ptr == '&') {
00505 req->req_qptrs[i] = ptr + 1;
00506 req->req_qptrs[i + 1] = 0;
00507 *ptr = 0;
00508 i += 2;
00509 }
00510 }
00511
00512 for (i = 0; i < req->req_numqptrs; i++) {
00513 for (ptr = req->req_qptrs[i * 2]; *ptr; ptr++) {
00514 if (*ptr == '=') {
00515 req->req_qptrs[i * 2 + 1] = ptr + 1;
00516 *ptr = 0;
00517 NutHttpURLDecode(req->req_qptrs[i * 2 + 1]);
00518 break;
00519 }
00520 }
00521 NutHttpURLDecode(req->req_qptrs[i * 2]);
00522 }
00523 }
00524
00533 char *NutHttpGetParameter(REQUEST * req, char *name)
00534 {
00535 int i;
00536 for (i = 0; i < req->req_numqptrs; i++)
00537 if (strcmp(req->req_qptrs[i * 2], name) == 0)
00538 return req->req_qptrs[i * 2 + 1];
00539 return NULL;
00540 }
00541
00549 int NutHttpGetParameterCount(REQUEST * req)
00550 {
00551 return req->req_numqptrs;
00552 }
00553
00563 char *NutHttpGetParameterName(REQUEST * req, int index)
00564 {
00565 if (index < 0 || index >= NutHttpGetParameterCount(req))
00566 return NULL;
00567 return req->req_qptrs[index * 2];
00568 }
00569
00579 char *NutHttpGetParameterValue(REQUEST * req, int index)
00580 {
00581 if (index < 0 || index >= NutHttpGetParameterCount(req))
00582 return NULL;
00583 return req->req_qptrs[index * 2 + 1];
00584 }
00585
00586 static void NutHttpProcessFileRequest(FILE * stream, REQUEST * req)
00587 {
00588 int fd;
00589 int n;
00590 char *data;
00591 int size;
00592 long file_len;
00593 char *filename = NULL;
00594 void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
00595
00596
00597
00598
00599 if (NutHttpAuthValidate(req)) {
00600 NutHttpSendError(stream, req, 401);
00601 return;
00602 }
00603
00604
00605
00606
00607 if (strncasecmp(req->req_url, "cgi-bin/", 8) == 0) {
00608 NutCgiProcessRequest(stream, req);
00609 return;
00610 }
00611
00612
00613
00614
00615 if (http_root) {
00616 filename = NutHeapAlloc(strlen(http_root) + strlen(req->req_url) + 1);
00617 strcpy(filename, http_root);
00618 } else {
00619 filename = NutHeapAlloc(strlen(req->req_url) + 6);
00620 strcpy(filename, "UROM:");
00621 }
00622
00623 strcat(filename, req->req_url);
00624
00625 handler = NutGetMimeHandler(filename);
00626
00627 fd = _open(filename, _O_BINARY | _O_RDONLY);
00628 NutHeapFree(filename);
00629 if (fd == -1) {
00630 char *index;
00631 u_short urll;
00632
00633
00634 urll = strlen(req->req_url);
00635 if ((index = NutHeapAllocClear(urll + 12)) == 0) {
00636 NutHttpSendError(stream, req, 500);
00637 return;
00638 }
00639 if (urll)
00640 strcpy(index, req->req_url);
00641 if (urll && index[urll - 1] != '/')
00642 strcat(index, "/");
00643 strcat(index, "index.html");
00644
00645 if (http_root) {
00646 filename = NutHeapAlloc(strlen(http_root) + strlen(index) + 1);
00647 strcpy(filename, http_root);
00648 } else {
00649 filename = NutHeapAlloc(strlen(index) + 6);
00650 strcpy(filename, "UROM:");
00651 }
00652 strcat(filename, index);
00653
00654 NutHeapFree(index);
00655
00656 handler = NutGetMimeHandler(filename);
00657
00658 fd = _open(filename, _O_BINARY | _O_RDONLY);
00659 NutHeapFree(filename);
00660 if (fd == -1) {
00661 urll = strlen(req->req_url);
00662 if ((index = NutHeapAllocClear(urll + 13)) == 0) {
00663 NutHttpSendError(stream, req, 500);
00664 return;
00665 }
00666 if (urll)
00667 strcpy(index, req->req_url);
00668 if (urll && index[urll - 1] != '/')
00669 strcat(index, "/");
00670 strcat(index, "index.shtml");
00671
00672 if (http_root) {
00673 filename = NutHeapAlloc(strlen(http_root) + strlen(index) + 1);
00674 strcpy(filename, http_root);
00675 } else {
00676 filename = NutHeapAlloc(strlen(index) + 6);
00677 strcpy(filename, "UROM:");
00678 }
00679 strcat(filename, index);
00680
00681 NutHeapFree(index);
00682
00683 handler = NutGetMimeHandler(filename);
00684
00685 fd = _open(filename, _O_BINARY | _O_RDONLY);
00686 NutHeapFree(filename);
00687 if (fd == -1) {
00688 NutHttpSendError(stream, req, 404);
00689 return;
00690 }
00691 }
00692 }
00693
00694 file_len = _filelength(fd);
00695 if (handler == NULL) {
00696 NutHttpSendHeaderTop(stream, req, 200, "Ok");
00697 NutHttpSendHeaderBot(stream, NutGetMimeType(req->req_url), file_len);
00698 if (req->req_method != METHOD_HEAD) {
00699 size = 512;
00700 if ((data = NutHeapAlloc(size)) != 0) {
00701 while (file_len) {
00702 if (file_len < 512L)
00703 size = (int) file_len;
00704
00705 n = _read(fd, data, size);
00706 if (fwrite(data, 1, n, stream) == 0)
00707 break;
00708 file_len -= (long) n;
00709 }
00710 NutHeapFree(data);
00711 }
00712 }
00713 } else {
00714 NutHttpSendHeaderTop(stream, req, 200, "Ok");
00715 NutHttpSendHeaderBot(stream, NutGetMimeType(req->req_url), -1);
00716 handler(stream, fd, file_len, http_root, req);
00717 }
00718 _close(fd);
00719 }
00720
00724 static char *NextWord(char *str)
00725 {
00726 while (*str && *str != ' ' && *str != '\t')
00727 str++;
00728 if (*str)
00729 *str++ = 0;
00730 while (*str == ' ' || *str == '\t')
00731 str++;
00732 return str;
00733 }
00734
00738 static REQUEST *CreateRequestInfo(void)
00739 {
00740 return NutHeapAllocClear(sizeof(REQUEST));
00741 }
00742
00746 static void DestroyRequestInfo(REQUEST * req)
00747 {
00748 if (req->req_url)
00749 NutHeapFree(req->req_url);
00750 if (req->req_query)
00751 NutHeapFree(req->req_query);
00752 if (req->req_type)
00753 NutHeapFree(req->req_type);
00754 if (req->req_cookie)
00755 NutHeapFree(req->req_cookie);
00756 if (req->req_auth)
00757 NutHeapFree(req->req_auth);
00758 if (req->req_agent)
00759 NutHeapFree(req->req_agent);
00760 if (req->req_qptrs)
00761 NutHeapFree(req->req_qptrs);
00762 NutHeapFree(req);
00763 }
00764
00777 int NutRegisterHttpRoot(char *path)
00778 {
00779 int len;
00780
00781 if (http_root)
00782 NutHeapFree(http_root);
00783 if (path && (len = strlen(path)) != 0) {
00784 if ((http_root = NutHeapAlloc(len + 1)) != 0)
00785 strcpy(http_root, path);
00786 } else
00787 http_root = 0;
00788
00789 return 0;
00790 }
00791
00801 void NutHttpProcessRequest(FILE * stream)
00802 {
00803 REQUEST *req;
00804 char *method;
00805 char *path;
00806 char *line;
00807 char *protocol;
00808 char *cp;
00809
00810
00811
00812
00813
00814 if ((req = CreateRequestInfo()) == 0)
00815 return;
00816 if ((method = NutHeapAlloc(256)) == 0) {
00817 DestroyRequestInfo(req);
00818 return;
00819 }
00820 fgets(method, 256, stream);
00821 if ((cp = strchr(method, '\r')) != 0)
00822 *cp = 0;
00823 if ((cp = strchr(method, '\n')) != 0)
00824 *cp = 0;
00825
00826
00827
00828
00829 if ((line = NutHeapAlloc(256)) == 0) {
00830 NutHeapFree(method);
00831 DestroyRequestInfo(req);
00832 return;
00833 }
00834 for (;;) {
00835 if (fgets(line, 256, stream) == 0)
00836 break;
00837 if ((cp = strchr(line, '\r')) != 0)
00838 *cp = 0;
00839 if ((cp = strchr(line, '\n')) != 0)
00840 *cp = 0;
00841 if (*line == 0)
00842 break;
00843 if (strncasecmp(line, "Authorization:", 14) == 0) {
00844 if (req->req_auth == 0) {
00845 cp = &line[14];
00846 while (*cp == ' ' || *cp == '\t')
00847 cp++;
00848 if ((req->req_auth = NutHeapAlloc(strlen(cp) + 1)) == 0)
00849 break;
00850 strcpy(req->req_auth, cp);
00851 }
00852 } else if (strncasecmp(line, "Content-Length:", 15) == 0) {
00853 cp = &line[15];
00854 while (*cp == ' ' || *cp == '\t')
00855 cp++;
00856 req->req_length = atoi(cp);
00857 } else if (strncasecmp(line, "Content-Type:", 13) == 0) {
00858 if (req->req_type == 0) {
00859 cp = &line[13];
00860 while (*cp == ' ' || *cp == '\t')
00861 cp++;
00862 if ((req->req_type = NutHeapAlloc(strlen(cp) + 1)) == 0)
00863 break;
00864 strcpy(req->req_type, cp);
00865 }
00866 } else if (strncasecmp(line, "Cookie:", 7) == 0) {
00867 if (req->req_cookie == 0) {
00868 cp = &line[7];
00869 while (*cp == ' ' || *cp == '\t')
00870 cp++;
00871 if ((req->req_cookie = NutHeapAlloc(strlen(cp) + 1)) == 0)
00872 break;
00873 strcpy(req->req_cookie, cp);
00874 }
00875 } else if (strncasecmp(line, "User-Agent:", 11) == 0) {
00876 if (req->req_agent == 0) {
00877 cp = &line[11];
00878 while (*cp == ' ' || *cp == '\t')
00879 cp++;
00880 if ((req->req_agent = NutHeapAlloc(strlen(cp) + 1)) == 0)
00881 break;
00882 strcpy(req->req_agent, cp);
00883 }
00884 }
00885 }
00886 NutHeapFree(line);
00887
00888 path = NextWord(method);
00889 protocol = NextWord(path);
00890 NextWord(protocol);
00891
00892 if (strcasecmp(method, "GET") == 0)
00893 req->req_method = METHOD_GET;
00894 else if (strcasecmp(method, "HEAD") == 0)
00895 req->req_method = METHOD_HEAD;
00896 else if (strcasecmp(method, "POST") == 0)
00897 req->req_method = METHOD_POST;
00898 else {
00899 NutHeapFree(method);
00900 NutHttpSendError(stream, req, 501);
00901 DestroyRequestInfo(req);
00902 return;
00903 }
00904 if (*path == 0 || *protocol == 0) {
00905 NutHeapFree(method);
00906 NutHttpSendError(stream, req, 400);
00907 DestroyRequestInfo(req);
00908 return;
00909 }
00910
00911 req->req_version = 10;
00912 if (strcasecmp(protocol, "HTTP/0.9") == 0)
00913 req->req_version = 9;
00914
00915 if ((cp = strchr(path, '?')) != 0) {
00916 *cp++ = 0;
00917 if ((req->req_query = NutHeapAlloc(strlen(cp) + 1)) == 0) {
00918 NutHeapFree(method);
00919 DestroyRequestInfo(req);
00920 return;
00921 }
00922 strcpy(req->req_query, cp);
00923
00924 NutHttpProcessQueryString(req);
00925 }
00926
00927 if ((req->req_url = NutHeapAlloc(strlen(path) + 1)) == 0) {
00928 NutHeapFree(method);
00929 DestroyRequestInfo(req);
00930 return;
00931 }
00932 strcpy(req->req_url, path);
00933
00934 NutHeapFree(method);
00935
00936 if (NutDecodePath(req->req_url) == 0) {
00937 NutHttpSendError(stream, req, 400);
00938 } else {
00939 NutHttpProcessFileRequest(stream, req);
00940 }
00941 DestroyRequestInfo(req);
00942 }
00943