--- /dev/null
+/* =================================================================
+ *
+ * http.c
+ *
+ * Handles the client requests.
+ *
+ * =================================================================
+ * ####ECOSGPLCOPYRIGHTBEGIN####
+ * -------------------------------------------
+ * This file is part of eCos, the Embedded Configurable Operating
+ * System.
+ * Copyright (C) 2005, 2007 eCosCentric Ltd.
+ *
+ * eCos is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 or (at your option)
+ * any later version.
+ *
+ * eCos is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with eCos; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * As a special exception, if other files instantiate templates or
+ * use macros or inline functions from this file, or you compile this
+ * file and link it with other works to produce a work based on this
+ * file, this file does not by itself cause the resulting work to be
+ * covered by the GNU General Public License. However the source code
+ * for this file must still be made available in accordance with
+ * section (3) of the GNU General Public License.
+ *
+ * This exception does not invalidate any other reasons why a work
+ * based on this file might be covered by the GNU General Public
+ * License.
+ *
+ * -------------------------------------------
+ * ####ECOSGPLCOPYRIGHTEND####
+ * =================================================================
+ * #####DESCRIPTIONBEGIN####
+ *
+ * Author(s): Anthony Tonizzo (atonizzo@gmail.com)
+ * Contributors: Sergei Gavrikov (w3sg@SoftHome.net)
+ * Lars Povlsen (lpovlsen@vitesse.com)
+ * Date: 2006-06-12
+ * Purpose:
+ * Description:
+ *
+ * ####DESCRIPTIONEND####
+ *
+ * =================================================================
+ */
+
+#include <pkgconf/hal.h>
+#include <pkgconf/kernel.h>
+#include <cyg/kernel/kapi.h> // Kernel API.
+#include <cyg/infra/diag.h> // For diagnostic printing.
+#include <network.h>
+#include <time.h>
+
+#include <cyg/hal/hal_tables.h>
+#include <cyg/fileio/fileio.h>
+#include <stdio.h> // sprintf().
+#include <stdlib.h>
+
+#ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER
+#include <cyg/objloader/elf.h>
+#include <cyg/objloader/objelf.h>
+#endif
+
+#include <cyg/athttpd/http.h>
+#include <cyg/athttpd/socket.h>
+#include <cyg/athttpd/handler.h>
+#include <cyg/athttpd/forms.h>
+
+cyg_int32 debug_print = 0;
+
+const char *day_of_week[7] = {"Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat"};
+const char *month_of_year[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+const char *home_pages[] = {"index.html", "index.htm",
+ "default.html", "home.html"};
+CYG_HAL_TABLE_BEGIN(cyg_httpd_mime_table, httpd_mime_table);
+CYG_HAL_TABLE_END(cyg_httpd_mime_table_end, httpd_mime_table);
+
+__externC cyg_httpd_mime_table_entry cyg_httpd_mime_table[];
+__externC cyg_httpd_mime_table_entry cyg_httpd_mime_table_end[];
+
+// Standard handlers added by default. Correspond to the most used extensions.
+// The user can add his/her own later.
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_htm_entry, "htm",
+ "text/html; charset=iso-8859-1");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_html_entry, "html",
+ "text/html; charset=iso-8859-1");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_gif_entry, "gif", "image/gif");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_jpg_entry, "jpg", "image/jpg");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_png_entry, "png", "image/png");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_css_entry, "css", "text/css");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_js_entry, "js", "application/x-javascript");
+
+void
+cyg_httpd_send_error(cyg_int32 err_type)
+{
+ httpstate.status_code = err_type;
+
+ // Errors pages close the socket and are never cached.
+ httpstate.mode |= CYG_HTTPD_MODE_NO_CACHE;
+
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+ diag_printf("Sending error: %d\n", err_type);
+#endif
+
+#ifdef CYGOPT_NET_ATHTTPD_USE_FS
+ // Check if the user has defines his own error pages.
+ struct stat sp;
+ char file_name[CYG_HTTPD_MAXPATH];
+ strcpy(file_name, CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR);
+ if (file_name[strlen(file_name)-1] != '/')
+ strcat(file_name, "/");
+ strcat(file_name, CYGDAT_NET_ATHTTPD_SERVEROPT_ERRORDIR);
+ if (file_name[strlen(file_name)-1] != '/')
+ strcat(file_name, "/");
+ sprintf(file_name + strlen(file_name), "error_%d.html", err_type);
+ cyg_httpd_cleanup_filename(file_name);
+ cyg_int32 rc = stat(file_name, &sp);
+ if (rc == 0)
+ {
+ char *extension = rindex(file_name, '.');
+ if (extension == NULL)
+ // No extension in the file name.
+ httpstate.mime_type = 0;
+ else
+ httpstate.mime_type = cyg_httpd_find_mime_string(++extension);
+
+ httpstate.payload_len = sp.st_size;
+ cyg_int32 header_length = cyg_httpd_format_header();
+ cyg_httpd_write(httpstate.outbuffer, header_length);
+
+ // File found.
+ FILE *fp = fopen(file_name, "r");
+ if(fp == NULL)
+ return;
+
+ ssize_t payload_size = fread(httpstate.outbuffer,
+ 1,
+ CYG_HTTPD_MAXOUTBUFFER,
+ fp);
+ while (payload_size > 0)
+ {
+ ssize_t bytes_written = cyg_httpd_write_chunked(httpstate.outbuffer,
+ payload_size);
+ if (bytes_written != payload_size)
+ break;
+
+ payload_size = fread(httpstate.outbuffer,
+ 1,
+ CYG_HTTPD_MAXOUTBUFFER,
+ fp);
+ }
+
+ fclose(fp);
+ return;
+ }
+#endif
+
+ // Because the size of the frame is not known upfront (every error message
+ // is different and thus has different length) we use chunked frames to
+ // send the message out.
+#if defined(CYGOPT_NET_ATHTTPD_CLOSE_CHUNKED_CONNECTIONS)
+ httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
+#endif
+
+ httpstate.mode |= CYG_HTTPD_MODE_TRANSFER_CHUNKED;
+ httpstate.status_code = err_type;
+ httpstate.last_modified = -1;
+ httpstate.mime_type = "text/html";
+ cyg_int32 header_length = cyg_httpd_format_header();
+ cyg_httpd_write(httpstate.outbuffer, header_length);
+
+ // If no file has been defined, send a simple notification. We must use
+ // chunked frames, because we do not know upfron the length of the
+ // packet we have to send.
+ strcpy(httpstate.outbuffer,
+ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n");
+ switch (err_type)
+ {
+ case CYG_HTTPD_STATUS_MOVED_PERMANENTLY:
+ strcat(httpstate.outbuffer,
+ "<html><head><title>301 Moved Permanently</title></head>\r\n"
+ "<body><h1>Moved Permanently</h1>\r\n"
+ "<p>The document has moved <a href=\"");
+ strcat(httpstate.outbuffer, httpstate.url);
+ strcat(httpstate.outbuffer, "\">here</a>.\r\n");
+ break;
+ case CYG_HTTPD_STATUS_MOVED_TEMPORARILY:
+ strcat(httpstate.outbuffer,
+ "<html><head><title>302 Found</title></head>\r\n"
+ "<body><h1>Redirect</h1>\r\n"
+ "<p>Please continue <a href=\"");
+ strcat(httpstate.outbuffer, httpstate.url);
+ strcat(httpstate.outbuffer, "\">here</a>.\r\n");
+ break;
+ case CYG_HTTPD_STATUS_NOT_AUTHORIZED:
+ strcat(httpstate.outbuffer,
+ "<html><head><title>401 Not Authorized</title></head>\r\n");
+ strcat(httpstate.outbuffer,
+ "<body><p>Authorization required to access this URL.</p>\r\n");
+ break;
+ case CYG_HTTPD_STATUS_NOT_MODIFIED:
+ cyg_httpd_end_chunked();
+ return;
+ case CYG_HTTPD_STATUS_NOT_FOUND:
+ strcat(httpstate.outbuffer,
+ "<html><head><title>404 Not Found</title></head>\r\n");
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "<p>The requested URL: %s was not found on this server</p>\r\n",
+ httpstate.url);
+ break;
+ case CYG_HTTPD_STATUS_SYSTEM_ERROR:
+ strcat(httpstate.outbuffer,
+ "<html><head><title>500 Server Error</title></head>\r\n");
+ strcat(httpstate.outbuffer,
+ "<p>The server encountered an unexpected condition that "
+ "prevented it from fulfilling the request"
+ " by the client</p>\r\n");
+ break;
+ case CYG_HTTPD_STATUS_NOT_IMPLEMENTED:
+ strcat(httpstate.outbuffer,
+ "<html><head><title>501 Not Implemented</title></head>\r\n");
+ strcat(httpstate.outbuffer,
+ "<p>The method requested is not implemented</p>\r\n");
+ break;
+ default:
+ strcat(httpstate.outbuffer,
+ "<html><head><title>400 Bad Request</title></head>\r\n");
+ strcat(httpstate.outbuffer,
+ "<p>Bad request</p>\r\n");
+ break;
+ }
+
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "<hr>%s at %d.%d.%d.%d Port %d\r\n</body></html>\r\n",
+ CYGDAT_NET_ATHTTPD_SERVEROPT_SERVERID,
+ httpstate.host[0],
+ httpstate.host[1],
+ httpstate.host[2],
+ httpstate.host[3],
+ CYGNUM_NET_ATHTTPD_SERVEROPT_PORT);
+
+ cyg_httpd_write_chunked(httpstate.outbuffer, strlen(httpstate.outbuffer));
+ cyg_httpd_end_chunked();
+}
+
+// Return a time_t that is always UTC (aka GMT).
+time_t
+cyg_httpd_parse_date(char *time)
+{
+ int i;
+ char month[4];
+ struct tm tm_mod;
+
+ // We are going to get rid of the day of the week. This is always the first
+ // part of the string, separated by a blank.
+ time = strchr( time, ' ');
+ if ( time == NULL)
+ return 0;
+ time++;
+
+ /// RFC1123. The date is in the format: Sun, 06 Nov 1994 08:49:37 GMT.
+ cyg_int32 rc = sscanf(time,
+ "%2d %3s %4d %2d:%2d:%2d GMT",
+ &tm_mod.tm_mday,
+ month,
+ &tm_mod.tm_year,
+ &tm_mod.tm_hour,
+ &tm_mod.tm_min,
+ &tm_mod.tm_sec);
+ if (rc != 6)
+ {
+ // RFC1036. The date is in the format: Sunday, 06-Nov-94 08:49:37 GMT.
+ rc = sscanf(time,
+ "%2d-%3s-%2d %2d:%2d:%2d GMT",
+ &tm_mod.tm_mday,
+ month,
+ &tm_mod.tm_year,
+ &tm_mod.tm_hour,
+ &tm_mod.tm_min,
+ &tm_mod.tm_sec);
+ if (rc != 6)
+ {
+ // asctime().
+ rc = sscanf(time,"%3s %2d %2d:%2d:%2d %4d",
+ month,
+ &tm_mod.tm_mday,
+ &tm_mod.tm_hour,
+ &tm_mod.tm_min,
+ &tm_mod.tm_sec,
+ &tm_mod.tm_year);
+ if (rc != 6)
+ return 0;
+ }
+ }
+
+ for (i = 0; i < sizeof(month_of_year); i++)
+ if (strcmp(month, month_of_year[i]) == 0)
+ {
+ tm_mod.tm_mon = i;
+ if (tm_mod.tm_year > 1900)
+ tm_mod.tm_year -= 1900;
+ return mktime(&tm_mod);
+ }
+
+ return 0;
+}
+
+// Finds the mime string into the mime_table associated with a specific
+// extension. Returns the MIME type to send in the header, or NULL if the
+// extension is not in the table.
+char*
+cyg_httpd_find_mime_string(char *ext)
+{
+ cyg_httpd_mime_table_entry *entry = cyg_httpd_mime_table;
+
+ while (entry != cyg_httpd_mime_table_end)
+ {
+ if (!strcmp((const char*)ext, entry->extension))
+ return entry->mime_string;
+ entry++;
+ }
+
+ return (char*)0;
+}
+
+void
+cyg_httpd_cleanup_filename(char *filename)
+{
+ char *src = strstr(filename, "//");
+ while (src != 0)
+ {
+ strcpy(src + 1, src + 2);
+ src = strstr(filename, "//");
+ }
+
+ src = strstr(filename, "/./");
+ while (src != 0)
+ {
+ strcpy(src + 1, src + 3);
+ src = strstr(filename, "/./");
+ }
+
+ src = strstr(filename, "/../");
+ while (src != 0)
+ {
+ char *comp1 = filename, *comp2 = filename;
+
+ // Search the path component before this redirection.
+ while ((comp1 = strchr(comp1, '/')) != src)
+ comp2 = ++comp1;
+
+ strcpy(comp2, src + 4);
+ src = strstr(filename, "/../");
+ }
+}
+
+cyg_int32
+cyg_httpd_initialize(void)
+{
+ httpstate.post_data = NULL;
+ httpstate.needs_auth = (cyg_httpd_auth_table_entry *)0;
+ return 0;
+}
+
+void
+cyg_httpd_append_homepage(char *root)
+{
+#ifdef CYGOPT_NET_ATHTTPD_USE_FS
+ struct stat sp;
+ cyg_int32 i;
+
+ cyg_int32 root_len = strlen(root);
+ for (i = 0; i < sizeof(home_pages)/sizeof(char*); i++)
+ {
+ root[root_len] = '\0';
+ sprintf(root + root_len, "%s", home_pages[i]);
+ cyg_int32 rc = stat(root, &sp);
+ if (rc == 0)
+ return;
+ }
+ root[root_len] = 0;
+#endif
+
+#ifdef CYGDAT_NET_ATHTTPD_ALTERNATE_HOME
+ if (strcmp(root, "/") == 0)
+ // The client is trying to open the main index file.
+ strcat(root, CYGDAT_NET_ATHTTPD_ALTERNATE_HOME);
+#endif
+}
+
+#ifdef CYGOPT_NET_ATHTTPD_USE_FS
+void
+cyg_httpd_send_file(char *name)
+{
+ cyg_int32 err;
+ FILE *fp;
+ struct stat sp;
+ char file_name[CYG_HTTPD_MAXPATH];
+
+ strcpy(file_name, CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR);
+ if (file_name[strlen(file_name)-1] != '/')
+ strcat(file_name, "/");
+ strcat(file_name, name);
+ cyg_httpd_cleanup_filename(file_name);
+
+ // Check if the file is in the file system. This will also give us the
+ // size of the file, to be used in the HTTP header.
+ cyg_int32 rc = stat(file_name, &sp);
+ if (rc < 0)
+ {
+ // Before giving up, we make a last ditch attempt at finding a file
+ // within the internal resources of the server. The user can add
+ // his/her own files to the table.
+ cyg_httpd_ires_table_entry *p = cyg_httpd_find_ires(name);
+ if (p != NULL)
+ {
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+ diag_printf("Sending Internal Resource: %s\n", name);
+#endif
+ cyg_httpd_send_ires(p);
+ }
+ else
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
+ return;
+ }
+
+ if (S_ISDIR(sp.st_mode))
+ {
+ char tmp_url[CYG_HTTPD_MAXURL];
+ strcpy(tmp_url, httpstate.url);
+ // Directories need a trialing slash, and if missing, we'll redirect
+ // the client to the right URL. This is called (appropriately
+ // enough) "Trailing-Slash Redirection".
+ if (name[strlen(name)-1] != '/')
+ {
+ sprintf(httpstate.url,
+ "http://%d.%d.%d.%d:%d%s/",
+ httpstate.host[0],
+ httpstate.host[1],
+ httpstate.host[2],
+ httpstate.host[3],
+ CYGNUM_NET_ATHTTPD_SERVEROPT_PORT,
+ tmp_url);
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_MOVED_PERMANENTLY);
+ return;
+ }
+
+ // We are going to try to locate an index page in the directory we got
+ // in the URL.
+ cyg_httpd_append_homepage(file_name);
+ if (file_name[strlen(file_name)-1] == '/')
+ {
+#ifdef CYGOPT_NET_ATHTTPD_USE_DIRLIST
+ // No home page found, we are sending a directory listing.
+ cyg_httpd_send_directory_listing(name);
+#else
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
+#endif
+ return;
+ }
+ stat(file_name, &sp);
+ }
+
+ httpstate.last_modified = sp.st_mtime;
+
+ // Let's see if we luck out and can send a 304.
+ if ((httpstate.modified_since != -1) &&
+ (httpstate.modified_since >= httpstate.last_modified))
+ {
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_MODIFIED);
+ return;
+ }
+ else
+ httpstate.status_code = CYG_HTTPD_STATUS_OK;
+
+ // Here we'll look for an extension to the file. Consider the case where
+ // there might be more than one dot in the file name. We'll look for just
+ // the last one, then we'll check the extension.
+ char *extension = rindex(file_name, '.');
+ if (extension == NULL)
+ httpstate.mime_type = 0;
+ else
+ httpstate.mime_type = cyg_httpd_find_mime_string(++extension);
+
+ httpstate.payload_len = sp.st_size;
+ httpstate.mode &= ~CYG_HTTPD_MODE_NO_CACHE;
+ cyg_int32 payload_size = cyg_httpd_format_header();
+ if ((httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) != 0)
+ {
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+ diag_printf("Sending header only for URL: %s\n", file_name);
+#endif
+ send(httpstate.sockets[httpstate.client_index].descriptor,
+ httpstate.outbuffer,
+ payload_size,
+ 0);
+ return;
+ }
+
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+ diag_printf("Sending file: %s\n", file_name);
+#endif
+ fp = fopen(file_name, "r");
+ if (fp == NULL)
+ {
+ // We should really read errno and send messages accordingly...
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
+ return;
+ }
+
+ // Fill up the rest of the buffer and send it out.
+ cyg_int32 bread = fread(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ 1,
+ CYG_HTTPD_MAXOUTBUFFER - payload_size,
+ fp);
+ cyg_httpd_write(httpstate.outbuffer, payload_size + bread);
+
+ ssize_t bytes_written = 0;
+ sp.st_size -= bread;
+ while (bytes_written < sp.st_size)
+ {
+ bread = fread(httpstate.outbuffer, 1, CYG_HTTPD_MAXOUTBUFFER, fp);
+ bytes_written += cyg_httpd_write(httpstate.outbuffer, bread);
+ }
+
+ err = fclose(fp);
+ if (err < 0)
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
+}
+#endif
+
+cyg_int32
+cyg_httpd_format_header(void)
+{
+ sprintf(httpstate.outbuffer, "HTTP/1.1 %d", httpstate.status_code);
+ time_t time_val = time(NULL);
+
+ // Error messages (i.e. with status other than OK, automatically add
+ // the no-cache header.
+ switch (httpstate.status_code)
+ {
+ case CYG_HTTPD_STATUS_MOVED_PERMANENTLY:
+ strcat(httpstate.outbuffer, " Moved Permanently\r\n");
+ strcat(httpstate.outbuffer, "Location: ");
+ strcat(httpstate.outbuffer, httpstate.url);
+ strcat(httpstate.outbuffer, "\r\n");
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "Content-Length: %d\r\n",
+ httpstate.payload_len);
+ break;
+ case CYG_HTTPD_STATUS_MOVED_TEMPORARILY:
+ strcat(httpstate.outbuffer, " Found\r\n");
+ strcat(httpstate.outbuffer, "Location: ");
+ strcat(httpstate.outbuffer, httpstate.url);
+ strcat(httpstate.outbuffer, "\r\n");
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "Content-Length: %d\r\n",
+ httpstate.payload_len);
+ break;
+#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
+ case CYG_HTTPD_STATUS_NOT_AUTHORIZED:
+ // A 401 error closes the connection right away.
+ httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
+ strcat(httpstate.outbuffer, " Not Authorized\r\n");
+
+ // Here we should set the proper header based on the authentication
+ // required (httpstate.needs_authMode) but for now, with only
+ // Basic Authentication supported, there is no need to do so.
+ if (httpstate.needs_auth->auth_mode == CYG_HTTPD_AUTH_BASIC)
+ {
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "WWW-Authenticate: Basic realm=\"%s\"\r\n",
+ httpstate.needs_auth->auth_domainname);
+ }
+ else
+ {
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "WWW-Authenticate: Digest realm=\"%s\", ",
+ httpstate.needs_auth->auth_domainname);
+ strftime(cyg_httpd_md5_nonce,
+ 33,
+ TIME_FORMAT_RFC1123,
+ gmtime(&time_val));
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "nonce=\"%s\", ", cyg_httpd_md5_nonce);
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "opaque=\"%s\", ",
+ CYG_HTTPD_MD5_AUTH_OPAQUE);
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "stale=false, algorithm=%s, qop=\"%s\"\r\n",
+ CYG_HTTPD_MD5_AUTH_NAME,
+ CYG_HTTPD_MD5_AUTH_QOP);
+ }
+ break;
+#endif
+ case CYG_HTTPD_STATUS_NOT_MODIFIED:
+ strcat(httpstate.outbuffer, " Not Modified\r\n");
+ break;
+ case CYG_HTTPD_STATUS_NOT_FOUND:
+ strcat(httpstate.outbuffer, " Not Found\r\n");
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "Content-Length: %d\r\n",
+ httpstate.payload_len);
+ break;
+ case CYG_HTTPD_STATUS_METHOD_NOT_ALLOWED:
+ strcat(httpstate.outbuffer, " Method Not Allowed\r\n");
+ break;
+ default:
+ strcat(httpstate.outbuffer, " OK\r\n");
+ if ((httpstate.mode & CYG_HTTPD_MODE_TRANSFER_CHUNKED) == 0)
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "Content-Length: %d\r\n",
+ httpstate.payload_len);
+ break;
+ }
+
+ strcat(httpstate.outbuffer, "Date: ");
+ strftime(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
+ TIME_FORMAT_RFC1123,
+ gmtime(&time_val));
+ strcat(httpstate.outbuffer, "\r\n");
+
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "Server: %s\r\n",
+ CYGDAT_NET_ATHTTPD_SERVEROPT_SERVERID);
+
+ if (httpstate.mode & CYG_HTTPD_MODE_CLOSE_CONN)
+ strcat(httpstate.outbuffer, "Connection: close\r\n");
+ else
+ strcat(httpstate.outbuffer, "Connection: keep-alive\r\n");
+
+ // When we cannot find the appropriate MIME type, we'll send a default type.
+ if (httpstate.mime_type == 0)
+ httpstate.mime_type = CYGDAT_NET_ATHTTPD_DEFAULT_MIME_TYPE;
+ sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ "Content-Type: %s\r\n",
+ httpstate.mime_type);
+
+ if (httpstate.mode & CYG_HTTPD_MODE_TRANSFER_CHUNKED)
+ strcat(httpstate.outbuffer, "Transfer-Encoding: chunked\r\n");
+
+ if (httpstate.mode & CYG_HTTPD_MODE_NO_CACHE)
+ strcat(httpstate.outbuffer, "Cache-Control: no-cache\r\n");
+
+ if (httpstate.last_modified != -1)
+ {
+ time_val = httpstate.last_modified;
+ strcat(httpstate.outbuffer, "Last-Modified: ");
+ strftime(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
+ TIME_FORMAT_RFC1123,
+ gmtime(&time_val));
+ strcat(httpstate.outbuffer, "\r\n");
+
+#if (CYGOPT_NET_ATHTTPD_DOCUMENT_EXPIRATION_TIME != 0)
+ time_val += CYGOPT_NET_ATHTTPD_DOCUMENT_EXPIRATION_TIME;
+ strcat(httpstate.outbuffer, "Expires: ");
+ strftime(httpstate.outbuffer + strlen(httpstate.outbuffer),
+ CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
+ TIME_FORMAT_RFC1123,
+ gmtime(&time_val));
+ strcat(httpstate.outbuffer, "\r\n");
+#endif
+ }
+
+ // There must be 2 carriage returns between the header and the body,
+ // so if you modify this function make sure that there is another
+ // CRLF already terminating the buffer thus far.
+ strcat(httpstate.outbuffer, "\r\n");
+ return strlen(httpstate.outbuffer);
+}
+
+void
+cyg_httpd_handle_method_GET(void)
+{
+#if defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER) ||\
+ defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL)
+ // If the URL is a CGI script, there is a different directory...
+ if (httpstate.url[0] == '/' &&
+ !strncmp(httpstate.url + 1,
+ CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR,
+ strlen(CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR)))
+ {
+ cyg_httpd_exec_cgi();
+ return;
+ }
+ // If the OBJLOADER package is not loaded, then the request for a library
+ // will likely generate a 404.
+#endif
+
+ // User defined handlers take precedence over other forms of response.
+ handler h = cyg_httpd_find_handler();
+ if (h != 0)
+ {
+ h(&httpstate);
+ return;
+ }
+
+
+#ifdef CYGOPT_NET_ATHTTPD_USE_FS
+ // No handler, we'll redirect to the file system.
+ cyg_httpd_send_file(httpstate.url);
+#else
+ // If we do not have a file system, we look for the file within the
+ // internal resources of the server. The user can add his/her own files
+ // to the table.
+ if (strcmp(httpstate.url, "/") == 0)
+ {
+ int i;
+ cyg_httpd_ires_table_entry *p;
+ for (i = 0; i < sizeof(home_pages)/sizeof(char*); i++)
+ {
+ httpstate.url[1] = '\0';
+ strcat(httpstate.url, home_pages[i]);
+ p = cyg_httpd_find_ires(httpstate.url);
+ if (p != NULL)
+ {
+ cyg_httpd_send_ires(p);
+ return;
+ }
+ }
+ }
+ else
+ {
+ cyg_httpd_ires_table_entry *p = cyg_httpd_find_ires(httpstate.url);
+ if (p != NULL)
+ {
+ cyg_httpd_send_ires(p);
+ return;
+ }
+ }
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
+#endif
+}
+
+char*
+cyg_httpd_get_URL(char* p)
+{
+ char* dest = httpstate.url;
+
+ // First get rid of multiple leading slashes.
+ while ((p[0] == '/') && (p[1] == '/'))
+ p++;
+
+ // Store the url, and check if there is a form result in it.
+ while ((*p != ' ') && (*p != '?') &&
+ ((dest - httpstate.url) <= CYG_HTTPD_MAXURL))
+ {
+ // Look for encoded characters in the URL.
+ if (*p == '%')
+ {
+ p++;
+ cyg_int8 ch = cyg_httpd_from_hex(*p++);
+ if (ch == -1)
+ {
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
+ return (char*)0;
+ }
+ *dest = ch << 4;
+ ch = cyg_httpd_from_hex(*p++);
+ if (ch == -1)
+ {
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
+ return (char*)0;
+ }
+ *dest += ch;
+ dest++;
+ }
+ else
+ *dest++ = *p++;
+ }
+
+ // Terminate the file name...
+ *dest = '\0';
+
+ // The URL must start with a leading slash.
+ if (httpstate.url[0] != '/')
+ {
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
+ return (char*)0;
+ }
+ return p;
+}
+
+char*
+cyg_httpd_parse_POST(char* p)
+{
+ httpstate.method = CYG_HTTPD_METHOD_POST;
+ char *cp = cyg_httpd_get_URL(p);
+ if (cp == 0)
+ return (char*)0;
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+ diag_printf("POST Request URL: %s\n", httpstate.url);
+#endif
+
+ while (*cp++ != '\n');
+ return cp;
+}
+
+char*
+cyg_httpd_parse_GET(char* p)
+{
+ char *cp = cyg_httpd_get_URL(p);
+ if (cp == 0)
+ return 0;
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+ if ( httpstate.method == CYG_HTTPD_METHOD_GET)
+ diag_printf("GET Request URL: %s\n", httpstate.url);
+ else
+ diag_printf("HEAD Request URL: %s\n", httpstate.url);
+#endif
+
+ if (*cp == '?')
+ // If we have a GET header with form variables we'll get the
+ // variables out of it and store them in the variable table.
+ // Can we assume that HEAD request can have form variables?
+ // That will be a yes until I learn otherwise.
+ cp = cyg_httpd_store_form_data(++cp);
+
+ // Run to end of line.
+ while (*cp++ != '\n');
+ return cp;
+}
+
+char*
+cyg_httpd_process_header(char *p)
+{
+#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
+ // Clear the previous request's response. The client properly authenticated
+ // will always reinitialize this variable during the header parsing
+ // process. This variable is also commandeered to hold the hashed
+ // username:password duo in the basic authentication.
+ cyg_httpd_md5_response[0] = '\0';
+#endif
+
+ // The deafult for HTTP 1.1 is keep-alive connections, unless specifically
+ // closed by the far end.
+ httpstate.mode &= ~(CYG_HTTPD_MODE_CLOSE_CONN | CYG_HTTPD_MODE_FORM_DATA |\
+ CYG_HTTPD_MODE_SEND_HEADER_ONLY);
+ httpstate.modified_since = -1;
+ httpstate.content_len = 0;
+ while (p < httpstate.request_end)
+ {
+ if (strncasecmp("GET ", p, 4) == 0)
+ {
+ // We need separate flags for HEAD and SEND_HEADERS_ONLY since
+ // we can send a header only even in the case of a GET request
+ // (as a 304 response.)
+ httpstate.method = CYG_HTTPD_METHOD_GET;
+ httpstate.mode &= ~CYG_HTTPD_MODE_SEND_HEADER_ONLY;
+ p = cyg_httpd_parse_GET(p + 4);
+ if (p ==0)
+ return (char*)0;
+ }
+ else if (strncasecmp("POST ", p, 5) == 0)
+ {
+ p = cyg_httpd_parse_POST(p + 5);
+ if (p ==0)
+ return (char*)0;
+ }
+ else if (strncasecmp("HEAD ", p, 5) == 0)
+ {
+ httpstate.method = CYG_HTTPD_METHOD_HEAD;
+ httpstate.mode |= CYG_HTTPD_MODE_SEND_HEADER_ONLY;
+ p = cyg_httpd_parse_GET(p + 5);
+ if (p ==0)
+ return (char*)0;
+ }
+ else if (strncasecmp(p, "Content-Length: ", 16) == 0)
+ {
+ p = strchr(p, ':') + 2;
+ if (p)
+ // In the case of a POST request, this is the total length of
+ // the payload, which might be spread across several frames.
+ httpstate.content_len = atoi(p);
+ while (*p++ != '\n');
+ }
+ else if (strncasecmp(p, "Content-Type: ", 14) == 0)
+ {
+ p = strchr(p, ':') + 2;
+ if (p)
+ // In the case of a POST request, this is the total length of
+ // the payload, which might be spread across several frames.
+ if (strncasecmp(p,
+ "application/x-www-form-urlencoded",
+ 33) == 0)
+ httpstate.mode |= CYG_HTTPD_MODE_FORM_DATA;
+ while (*p++ != '\n');
+ }
+ else if (strncasecmp("Host:", p, 5) == 0)
+ {
+ p += 5;
+ if (*p == ' ')
+ p++;
+ sscanf(p,
+ "%d.%d.%d.%d",
+ &httpstate.host[0],
+ &httpstate.host[1],
+ &httpstate.host[2],
+ &httpstate.host[3]);
+ while (*p++ != '\n');
+ }
+ else if (strncasecmp("If-Modified-Since:", p, 18) == 0)
+ {
+ p += 18;
+ if ( *p == ' ')
+ p++;
+ httpstate.modified_since = cyg_httpd_parse_date(p);
+ while (*p++ != '\n');
+ }
+#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
+ else if (strncasecmp("Authorization:", p, 14) == 0)
+ {
+ p += 14;
+ while (*p == ' ')
+ p++;
+ if (strncasecmp("Basic", p, 5) == 0)
+ {
+ p += 5;
+ while (*p == ' ')
+ p++;
+ cyg_int32 auth_data_length = 0;
+ while (*p != '\n')
+ {
+ // We are going to copy only up to
+ // AUTH_STORAGE_BUFFER_LENGTH characters to prevent
+ // overflow of the cyg_httpd_md5_response variable.
+ if (auth_data_length < AUTH_STORAGE_BUFFER_LENGTH)
+ if ((*p != '\r') && (*p != ' '))
+ cyg_httpd_md5_response[auth_data_length++] = *p;
+ p++;
+ }
+ p++;
+ cyg_httpd_md5_response[auth_data_length] = '\0';
+ }
+ else if (strncasecmp(p, "Digest", 6) == 0)
+ {
+ p += 6;
+ while (*p == ' ')
+ p++;
+ while (*p != '\n')
+ {
+ if (strncasecmp(p, "realm=", 6) == 0)
+ p = cyg_httpd_digest_skip(p + 6);
+ else if (strncasecmp(p, "username=", 9) == 0)
+ p = cyg_httpd_digest_skip(p + 9);
+ else if (strncasecmp(p, "nonce=", 6) == 0)
+ p = cyg_httpd_digest_skip(p + 6);
+ else if (strncasecmp(p, "response=", 9) == 0)
+ p = cyg_httpd_digest_data(cyg_httpd_md5_response,
+ p + 9);
+ else if (strncasecmp(p, "cnonce=", 7) == 0)
+ p = cyg_httpd_digest_data(cyg_httpd_md5_cnonce, p + 7);
+ else if (strncasecmp(p, "qop=", 4) == 0)
+ p = cyg_httpd_digest_skip(p + 4);
+ else if (strncasecmp(p, "nc=", 3) == 0)
+ p = cyg_httpd_digest_data(cyg_httpd_md5_noncecount,
+ p + 3);
+ else if (strncasecmp(p, "algorithm=", 10) == 0)
+ p = cyg_httpd_digest_skip(p + 10);
+ else if (strncasecmp(p, "opaque=", 7) == 0)
+ p = cyg_httpd_digest_skip(p + 7);
+ else if (strncasecmp(p, "uri=", 4) == 0)
+ p = cyg_httpd_digest_skip(p + 4);
+ else
+ p++;
+ }
+ p++;
+ }
+ else
+ while (*p++ != '\n');
+ }
+#endif // CYGOPT_NET_ATHTTPD_USE_AUTH
+ else if (strncasecmp(p, "Connection:", 11) == 0)
+ {
+ p += 11;
+ while (*p == ' ')
+ p++;
+ if (strncasecmp(p, "close", 5) == 0)
+ httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
+ while (*p++ != '\n');
+ }
+ else
+ // We'll just dump the rest of the line and move on to the next.
+ while (*p++ != '\n');
+ }
+ return p;
+}
+
+void
+cyg_httpd_process_method(void)
+{
+ char* p = httpstate.inbuffer;
+
+ // Some browsers send an extra '\r\n' after the POST data that is not
+ // accounted in the "Content-Length:" field. We are going to junk all
+ // the leading returns and line carriages we find.
+ while ((*p == '\r') || (*p =='\n'))
+ p++;
+
+ while (*p != '\0')
+ {
+ p = cyg_httpd_process_header(p);
+ if (p == 0)
+ return;
+
+#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
+ // Let's check that the requested URL is not inside some directory that
+ // needs authentication.
+ cyg_httpd_auth_table_entry* auth =
+ cyg_httpd_is_authenticated(httpstate.url);
+ if (auth != 0)
+ {
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_AUTHORIZED);
+ return;
+ }
+#endif
+ switch (httpstate.method)
+ {
+ case CYG_HTTPD_METHOD_GET:
+ case CYG_HTTPD_METHOD_HEAD:
+ cyg_httpd_handle_method_GET();
+ break;
+ case CYG_HTTPD_METHOD_POST:
+ cyg_httpd_handle_method_POST();
+ return;
+ break;
+ default:
+ cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_IMPLEMENTED);
+ return;
+ break;
+ }
+ }
+}
+