/* * TOPPERS ECHONET Lite Communication Middleware * * Copyright (C) 2017 Cores Co., Ltd. Japan * * 上記著作権者は,以下の(1)~(4)の条件を満たす場合に限り,本ソフトウェ * ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改 * 変・再配布(以下,利用と呼ぶ)することを無償で許諾する. * (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作 * 権表示,この利用条件および下記の無保証規定が,そのままの形でソー * スコード中に含まれていること. * (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使 * 用できる形で再配布する場合には,再配布に伴うドキュメント(利用 * 者マニュアルなど)に,上記の著作権表示,この利用条件および下記 * の無保証規定を掲載すること. * (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使 * 用できない形で再配布する場合には,次のいずれかの条件を満たすこ * と. * (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著 * 作権表示,この利用条件および下記の無保証規定を掲載すること. * (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに * 報告すること. * (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損 * 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること. * また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理 * 由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを * 免責すること. * * 本ソフトウェアは,無保証で提供されているものである.上記著作権者お * よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的 * に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ * アの利用により直接的または間接的に生じたいかなる損害に関しても,そ * の責任を負わない. * * @(#) $Id: httpd.c 270 2017-02-09 04:03:47Z coas-nagasima $ */ #include "httpd.h" #include #include #include "syssvc/syslog.h" #include "http-strings.h" #include "netinet/in.h" #include "arduino.h" #define TRUE 1 #define FALSE 0 #ifndef _MSC_VER /* strnlen() is a POSIX.2008 addition. Can't rely on it being available so * define it ourselves. */ size_t strnlen(const char *s, size_t maxlen) { const char *p; p = memchr(s, '\0', maxlen); if (p == NULL) return maxlen; return p - s; } void strcpy_s(char *dst, int size, const char *src) { int slen = strlen(src); if (slen >= size) slen = size - 1; memcpy(dst, src, slen); dst[slen] = '\0'; } void strcat_s(char *dst, int size, const char *src) { int dlen = strlen(dst); int slen = strlen(src); if (dlen + slen >= size) slen = size - 1 - dlen; memcpy(&dst[dlen], src, slen); dst[dlen + slen] = '\0'; } #endif int httpd_strnicmp(const char *s1, const char *s2, size_t n) { int i; char c1, c2; for (i = 0; i < n; i++, s1++, s2++) { c1 = *s1; c2 = *s2; if (c1 == '\0' && c2 == '\0') return 0; if (c1 >= 'a' && c1 <= 'z') c1 += 'A' - 'a'; if (c2 >= 'a' && c2 <= 'z') c2 += 'A' - 'a'; if (c1 < c2) return -1; if (c1 > c2) return 1; } return 0; } size_t strlncat(char *dst, size_t len, const char *src, size_t n) { size_t slen; size_t dlen; size_t rlen; size_t ncpy = 0; slen = strnlen(src, n); dlen = strnlen(dst, len); if (dlen < len) { rlen = len - dlen; ncpy = slen < rlen ? slen : (rlen - 1); memcpy(dst + dlen, src, ncpy); dst[dlen + ncpy] = '\0'; } //assert(len > slen + dlen); //return slen + dlen; return ncpy; } extern struct httpd_state *uploding; int websvr_message_begin(http_parser *p) { struct httpd_state *s = get_context(p); memset(&s->message, 0, sizeof(s->message)); s->message.message_begin_cb_called = TRUE; return 0; } int websvr_request_url(http_parser *p, const char *buf, size_t len) { struct httpd_state *s = get_context(p); strlncat(s->message.request_url, sizeof(s->message.request_url), buf, len); char *ptr = strrchr(s->message.request_url, '?'); if (ptr != NULL) { ptr[0] = '\0'; s->query = &ptr[1]; } else s->query = NULL; /* ""か"/"なら"index.html"に変更 */ if ((s->message.request_url[0] == '\0') || ((s->message.request_url[0] == '/') && (s->message.request_url[1] == '\0'))) { s->filename = &s->message.filename[sizeof(s->message.filename) - 2]; strcpy_s(s->filename, sizeof(s->message.request_url) + 2, "0:"); strcat_s(s->filename, sizeof(s->message.request_url) + 2, http_index_html); s->file.redirect = 1; } /* "/~/"ならSDカードから読み込み */ else if ((s->message.request_url[0] == '/') && (s->message.request_url[1] == '~') && (s->message.request_url[2] == '/')) { s->filename = &s->message.filename[sizeof(s->message.filename) - 2 - sizeof(http_www) - 1 + 2]; memcpy(s->filename, "1:", 2); memcpy(s->filename, http_www, sizeof(http_www) - 1); } else { s->filename = &s->message.filename[sizeof(s->message.filename) - 2]; memcpy(s->filename, "0:", 2); } return 0; } int websvr_response_status(http_parser *p, const char *buf, size_t len) { struct httpd_state *s = get_context(p); strlncat(s->message.response_status, sizeof(s->message.response_status), buf, len); return 0; } int websvr_header_field(http_parser *p, const char *buf, size_t len) { struct httpd_state *s = get_context(p); struct message *m = &s->message; if (strncmp("Referer", buf, len) == 0) { m->num_headers = 1; } else if (strncmp("Host", buf, len) == 0) { m->num_headers = 2; } else if (strncmp("Upgrade", buf, len) == 0) { m->num_headers = 3; } else if (strncmp("Connection", buf, len) == 0) { m->num_headers = 4; } else if (strncmp("Sec-WebSocket-Key", buf, len) == 0) { m->num_headers = 5; } else if (strncmp("Origin", buf, len) == 0) { m->num_headers = 6; } else if (strncmp("Sec-WebSocket-Protocol", buf, len) == 0) { m->num_headers = 7; } else if (strncmp("Sec-WebSocket-Version", buf, len) == 0) { m->num_headers = 8; } else { m->num_headers = 0; } return 0; } int websvr_header_value(http_parser *p, const char *buf, size_t len) { struct httpd_state *s = get_context(p); struct message *m = &s->message; switch (m->num_headers) { case 1: strlncat(m->referer, sizeof(m->referer), buf, len); break; case 2: strlncat(m->host, sizeof(m->host), buf, len); break; case 3: strlncat(m->upgrade, sizeof(m->upgrade), buf, len); break; case 4: strlncat(m->connection, sizeof(m->connection), buf, len); break; case 5: strlncat(m->sec_websocket_key, sizeof(m->sec_websocket_key), buf, len); break; case 6: strlncat(m->origin, sizeof(m->origin), buf, len); break; case 7: strlncat(m->sec_websocket_protocol, sizeof(m->sec_websocket_protocol), buf, len); break; case 8: strlncat(m->sec_websocket_version, sizeof(m->sec_websocket_version), buf, len); break; } return 0; } int websvr_headers_complete(http_parser *p) { struct httpd_state *s = get_context(p); s->message.method = p->method; s->message.http_major = p->http_major; s->message.http_minor = p->http_minor; s->message.headers_complete_cb_called = TRUE; s->message.should_keep_alive = http_should_keep_alive(p); if ((s->message.method == HTTP_GET) && httpd_strnicmp(http_websocket, s->message.upgrade, sizeof(s->message.upgrade)) == 0) { s->in.state = IN_STATE_WEBSOCKET; s->state = STATE_WEBSOCKET; s->close_req = 0; websocket_init(&s->websocket, s->cepid); return 0; } else if (s->message.method == HTTP_GET) { s->in.state = IN_STATE_RESPONSE; s->out.state = OUT_STATE_OPEN_GET_FILE; return 1; } else if (s->message.method == HTTP_POST) { if ((s->parser.content_length > 16 * 1024) || httpd_strnicmp(s->message.request_url, http_upload, sizeof(http_upload) - 1) != 0) { goto error; } if (uploding == NULL) { uploding = s; // アップロード先はSDカード s->filename[0] = '1'; syslog(LOG_NOTICE, "create: %s.%d %s", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename); if (!httpd_fs_create(s->filename, &s->file)) { goto error; } s->in.state = IN_STATE_UPLOAD; } else if (strcmp(s->filename, uploding->filename) == 0) { syslog(LOG_NOTICE, "collision: %s.%d %s", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename); goto error; } else { s->in.state = IN_STATE_UPLOAD_WAIT; s->in.wait = true; } s->out.state = OUT_STATE_WAIT_POST_BODY; s->out.wait = true; return 0; } else { s->state = STATE_CLOSING; return 1; } error: s->filename = NULL; s->response_body = http_content_403; s->response_pos = 0; s->response_len = sizeof(http_content_403) - 1; s->out.statushdr = http_header_403; s->out.state = OUT_STATE_SEND_HEADER; return 1; } int websvr_body(http_parser *p, const char *buf, size_t len) { struct httpd_state *s = get_context(p); if (s->message.body_is_final) { syslog(LOG_ERROR, "\n\n *** Error http_body_is_final() should return 1 " "on last on_body callback call " "but it doesn't! ***\n\n"); s->state = STATE_CLOSING; return 0; } httpd_fs_write(&s->file, buf, len); s->message.body_size += len; s->message.body_is_final = http_body_is_final(p); if (s->message.body_is_final) { syslog(LOG_NOTICE, "close: %s.%d %s", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename); httpd_fs_close(&s->file); memset(&s->file, 0, sizeof(s->file)); strcpy_s(RubyFilename, sizeof(RubyFilename), s->filename); s->reset = 1; s->filename = NULL; s->response_body = http_content_200; s->response_pos = 0; s->response_len = sizeof(http_content_200) - 1; uploding = NULL; s->in.state = IN_STATE_END; s->out.state = OUT_STATE_BODY_RECEIVED; } return 0; } int websvr_message_complete(http_parser *p) { struct httpd_state *s = get_context(p); if (s->message.should_keep_alive != http_should_keep_alive(p)) { syslog(LOG_ERROR, "\n\n *** Error http_should_keep_alive() should have same " "value in both on_message_complete and on_headers_complete " "but it doesn't! ***\n\n"); assert(0); } if (s->message.body_size && http_body_is_final(p) && !s->message.body_is_final) { syslog(LOG_ERROR, "\n\n *** Error http_body_is_final() should return 1 " "on last on_body callback call " "but it doesn't! ***\n\n"); assert(0); } s->message.message_complete_cb_called = TRUE; return 0; } http_parser_settings websvr_settings = { websvr_message_begin, websvr_request_url, websvr_response_status, websvr_header_field, websvr_header_value, websvr_headers_complete, websvr_body, websvr_message_complete, };