Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: SW_HTTPServer WiflyInterface mbed C12832 IniManager
Revision 38:962d71d6dd0e, committed 2015-02-02
- Comitter:
- WiredHome
- Date:
- Mon Feb 02 03:03:08 2015 +0000
- Parent:
- 36:5ca4a8b8d888
- Commit message:
- Refresh update, bug fix, mbed application board compatibility.
Changed in this revision
diff -r 5ca4a8b8d888 -r 962d71d6dd0e C12832.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C12832.lib Mon Feb 02 03:03:08 2015 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/users/WiredHome/code/C12832/#e37a0e1a97a1
diff -r 5ca4a8b8d888 -r 962d71d6dd0e Examples/DynamicFileIn.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Examples/DynamicFileIn.cpp Mon Feb 02 03:03:08 2015 +0000
@@ -0,0 +1,491 @@
+/// Demonstration of dynamic page creation using the Smartware Web Server
+///
+/// Here is a sample for file upload.
+#include "SW_HTTPServer.h"
+#include "DynamicFileIn.h"
+
+#define DEBUG "File"
+#include "Utility.h"
+
+// This defines the size of the largest file you are willing to accept.
+// Hopefully, space is also available on the file system for it.
+#define ACCEPT_MAX_BYTES (25000)
+
+// This defines the local cache, which should be at least 2 x the size of ETHER_CHUNK.
+// It can be any size above that, so long as RAM is available.
+#define PCACHE_SIZE (3000)
+
+// ETHER_CHUNK is the maximum size of a single packet handoff.
+#define ETHER_CHUNK (1500)
+#if ((2 * ETHER_CHUNK) > PCACHE_SIZE)
+#error "Configuration error, PCACHE_SIZE must be at least 2 * ETHER_CHUNK size.
+#endif
+
+static int bytesInCache;
+static char * pCache;
+static char * boundary = ""; // for pointing at the boundary marker part of multipart/form-data in pCache
+static char * contenttype = NULL; // for "text/plain", or "image/bmp"
+static char * fqfn = NULL; // for the filepath/filename
+static FILE * fp = NULL;
+static int bytesWritten;
+
+static int FileSize(const char * dir, const char * filename);
+static char _tolower(char c);
+static int _strnicmp(const char * left, const char * right, int len);
+static char * _stristr(char * pHaystack, const char * pNeedle);
+static char * _strnbin(char * pBinstack, const char *pNeedle, int len);
+static void FreeResources(void);
+
+typedef enum {
+ IDLE,
+ ACCEPTED,
+ REJECTED_TOO_LARGE
+} AcceptStatus;
+
+//
+//-----------------------------7de3ab1652086c
+//Content-Disposition: form-data; name="InFile"; filename="Len0700.txt"
+//Content-Type: text/plain
+//
+//__1 - _50 12345678901234567890123456789012345678
+//_51 - 100 12345678901234567890123456789012345678
+//101 - 150 12345678901234567890123456789012345678
+//151 - 200 12345678901234567890123456789012345678
+//201 - 250 12345678901234567890123456789012345678
+//251 - 300 12345678901234567890123456789012345678
+//301 - 350 12345678901234567890123456789012345678
+//351 - 400 12345678901234567890123456789012345678
+//401 - 450 12345678901234567890123456789012345678
+//451 - 500 12345678901234567890123456789012345678
+//501 - 550 12345678901234567890123456789012345678
+//551 - 600 12345678901234567890123456789012345678
+//601 - 650 12345678901234567890123456789012345678
+//651 - 700 12345678901234567890123456789012345678
+//
+//-----------------------------7de3ab1652086c--
+//^^ Boundary Definition ^^ Boundary Definition
+// is appended to "--" is suffixed "--" indicating the end.
+//---
+//Content-Disposition: form-data; name="InFile"; filename="somefile.txt"\r\n
+//Content-Type: image/bmp\r\n
+//\r\n
+//BM6.... (where the ... is purely binary data data)
+//\r\n------webKitFormBoundary9A3C872FAC9BE23--\r\n
+//---
+//
+HTTPServer::CallBackResults ProcessCache(HTTPServer *svr, HTTPServer::CallBackType type)
+{
+ typedef enum {
+ Seeking_Boundary_Start,
+ Seeking_Filename,
+ Seeking_ContentType,
+ Seeking_Data
+ } EF_State_E;
+ //const char * statelist[] = {
+ // "Start",
+ // "Filename",
+ // "ContentType",
+ // "Data"
+ //};
+ static EF_State_E EF_State = Seeking_Boundary_Start;
+
+ char * pDataStart = pCache; // start of data in this transaction
+ char * pDataEnd = pCache + bytesInCache; // end of data in this transaction
+ char * pDBLcrlf;
+ char *p1, *p2, *p3; // general purpose pointer for processing the results
+ HTTPServer::CallBackResults ret = HTTPServer::ACCEPT_CONTINUE;
+
+ //INFO("ProcessCache %s", statelist[EF_State]);
+ INFO("### Cache Begin\r\n%s\r\n### Cache End", pCache);
+ if (EF_State == Seeking_Boundary_Start) {
+ INFO("Start");
+ pDBLcrlf = _strnbin((char *)pDataStart, "\r\n\r\n", pDataEnd - pDataStart); // find end of preamble
+ if (pDBLcrlf) {
+ p1 = _strnbin((char *)pDataStart, boundary, pDBLcrlf - pDataStart); // is there a boundary in the cache?
+ if (p1) {
+ EF_State = Seeking_Filename;
+ }
+ }
+ }
+
+ if (EF_State == Seeking_Filename) {
+ INFO("File");
+ pDBLcrlf = _strnbin((char *)pDataStart, "\r\n\r\n", pDataEnd - pDataStart); // find end of preamble
+ if (pDBLcrlf) {
+ p2 = _strnbin((char *)pDataStart, "Content-Disposition: ", pDBLcrlf - pDataStart); // and the filename container?
+ if (p2) {
+ p2 = _strnbin((char *)p2, "filename=\"", pDBLcrlf - pDataStart);
+ if (p2) { // found filename
+ p2 += strlen("filename=\""); // start of filename
+ p3 = strchr(p2, '"'); // end of filename
+ if (p3) {
+ *p3 = '\0';
+ fqfn = (char *)malloc(p3-p2 + strlen(svr->GetWebRoot())+2);
+ if (fqfn) {
+ strcpy(fqfn, svr->GetWebRoot()); // We could put it elsewhere
+ strcat(fqfn, "/");
+ strcat(fqfn, p2);
+ *p3 = '"'; // Put this back so we can print the cache
+ EF_State = Seeking_ContentType;
+ pDataStart = p3 + 1;
+ } else {
+ ERR("Failed to allocate space for filename");
+ ret = HTTPServer::ACCEPT_ERROR;
+ EF_State = Seeking_Boundary_Start;
+ bytesInCache = 0;
+ return ret;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (EF_State == Seeking_ContentType) {
+ INFO("ContentType");
+ pDBLcrlf = _strnbin((char *)pDataStart, "\r\n\r\n", pDataEnd - pDataStart); // find end of preamble
+ if (pDBLcrlf) {
+ p2 = _strnbin((char *)pDataStart, "Content-Type: ", pDBLcrlf - pDataStart);
+ if (p2) {
+ p2 += strlen("Content-Type: ");
+ p3 = strchr(p2, '\r');
+ if (p3) {
+ *p3 = '\0';
+ contenttype = (char *)malloc(p3-p2 + 2);
+ if (contenttype) {
+ strcpy(contenttype, p2);
+ *p3 = '\r'; // put it back so we can print the cache
+ } else {
+ ERR("Failed to allocate space for content type");
+ ret = HTTPServer::ACCEPT_ERROR;
+ EF_State = Seeking_Boundary_Start;
+ bytesInCache = 0;
+ return ret;
+ }
+ pDataStart = pDBLcrlf + 4; // Advance to the actual data
+ EF_State = Seeking_Data;
+ }
+ }
+ }
+ }
+
+ if (EF_State == Seeking_Data) {
+ INFO("Data");
+ if (!fp && fqfn) {
+ INFO("Open file [%s]", fqfn);
+ fp = fopen(fqfn, "w");
+ if (!fp) {
+ ERR("failed to open [%s]", fqfn);
+ FreeResources();
+ ret = HTTPServer::ACCEPT_ERROR;
+ return ret;
+ }
+ }
+ if (fp) {
+ Timer t;
+ // The file is open, so we're writing data to it.
+ // We do this until we find the ending boundary, but we may not have the [whole]
+ // ending boundary in the cache yet.
+ p1 = _strnbin((char *)pDataStart, boundary, pDataEnd - pDataStart); // is there a boundary in the cache?
+ if (p1) { // if we find the ending boundary, we can truncate and write the data ahead of the boundary
+ p1 -= 4; // Actual ending boundary is "\r\n--" + "boundary" + "--", so back up 4.
+ *p1 = '\0';
+ pDataEnd = p1;
+ if (pDataEnd > pDataStart) {
+ bytesWritten += pDataEnd - pDataStart;
+ t.start();
+ fwrite(pDataStart, 1, pDataEnd - pDataStart, fp);
+ INFO("Writing %5d bytes, %5d total written, (reserve %d) in %d us",
+ pDataEnd - pDataStart, bytesWritten, 0, t.read_us());
+ //INFO("Write: %s***", pDataStart);
+ bytesInCache = 0;
+ *pCache = '\0'; // this for debugging in case it is text and you want to print it.
+ } else {
+ INFO("Not enough data to write (%d <= %d)", pDataEnd, pDataStart);
+ }
+ } else { // didn't find the boundary, but it might be partially there...
+ // write the data, but not the last (strlen(boundary) + 4)
+ // in case this transaction has some, but not all of the boundary
+ int reserve = strlen(boundary) + 4;
+ pDataEnd -= reserve;
+ if (pDataEnd > pDataStart) {
+ bytesWritten += pDataEnd - pDataStart;
+ t.start();
+ fwrite(pDataStart, 1, pDataEnd - pDataStart, fp);
+ INFO("Writing %5d bytes, %5d total written, (reserve %d) in %d us",
+ pDataEnd - pDataStart, bytesWritten, reserve, t.read_us());
+ //INFO("Write: %s###", pDataStart);
+ memcpy(pCache, pDataEnd, reserve+1); // copies the trailing '\0'
+ bytesInCache = reserve;
+ //*(pCache + bytesInCache) = '\0';
+ INFO("remaining: %s", pCache);
+ } else {
+ INFO("Not enough data to write (%d <= %d)", pDataEnd, pDataStart);
+ }
+ }
+ ret = HTTPServer::ACCEPT_CONTINUE;
+ }
+ }
+
+ if (type == HTTPServer::DATA_TRANSFER_END) {
+ FreeResources();
+ ret = HTTPServer::ACCEPT_COMPLETE;
+ }
+ return ret;
+}
+
+
+
+/// DynamicFileIn
+///
+/// This page lets you submit a file.
+///
+/// You can see in main how this page was registered.
+///
+HTTPServer::CallBackResults DynamicFileIn(
+ HTTPServer *svr,
+ HTTPServer::CallBackType type,
+ const char * path,
+ const HTTPServer::namevalue *params,
+ int count)
+{
+ char buf[100];
+ int clSize; // content-length size to be transferred
+ static AcceptStatus accept = IDLE;
+ HTTPServer::CallBackResults ret = HTTPServer::ACCEPT_ERROR;
+ DIR *d;
+ struct dirent *p;
+
+ switch (type) {
+ case HTTPServer::CONTENT_LENGTH_REQUEST:
+ // Here we can find out how much data is headed our way,
+ // and choose to accept or reject it.
+ clSize = atoi(svr->GetHeaderValue("Content-Length"));
+ //printf("Content Len RQST(%d)\r\n", clSize);
+ //printf("Content-Type: [%s]\r\n", svr->GetHeaderValue("Content-Type"));
+ // also, for the multipart data, we have to learn and track
+ // the boundary sequence, so we can parse the big block
+ // successfully.
+ boundary = _stristr((char *)svr->GetHeaderValue("Content-Type"), "boundary=");
+ if (boundary)
+ boundary += strlen("boundary=");
+ //INFO("C-Len: %d, boundary: [%s]", clSize, boundary);
+ //printf("InFile = [%s]\r\n", svr->GetHeaderValue("InFile"));
+ // If we're happy with the size, and we have the boundary marker we need
+ if (clSize < ACCEPT_MAX_BYTES && boundary) {
+ pCache = (char *)malloc(PCACHE_SIZE);
+ if (pCache) {
+ //INFO("pCache - allocated");
+ bytesInCache = 0;
+ accept = ACCEPTED;
+ ret = HTTPServer::ACCEPT_COMPLETE;
+ } else {
+ accept = REJECTED_TOO_LARGE; // can't get the RAM to host the stream
+ ret = HTTPServer::ACCEPT_ERROR;
+ }
+ } else {
+ accept = REJECTED_TOO_LARGE;
+ }
+ break;
+
+ case HTTPServer::DATA_TRANSFER_END:
+ //INFO("\r\n\\\\\\\\\\\\ Transfer Begin [%d, %d]\r\n%s\r\n////// Transfer End", count, bytesInCache, path);
+ INFO("TransferEnd [%d, %d, %d, %d]", count, bytesInCache, PCACHE_SIZE - ETHER_CHUNK, PCACHE_SIZE);
+ // if there is anything there, copy it in.
+ if (count) {
+ memcpy(pCache + bytesInCache, path, count);
+ bytesInCache += count;
+ pCache[bytesInCache] = '\0'; // one past the data, so ok (and good for debugging text streams)
+ }
+ ret = ProcessCache(svr, type);
+ break;
+
+ case HTTPServer::DATA_TRANSFER:
+ //INFO("\r\n\\\\\\\\\\\\ Transfer Begin [%d, %d]\r\n%s\r\n////// Transfer End", count, bytesInCache, path);
+ // We chose to accept the transfer, and it may come in chunks.
+ if ((bytesInCache + count) < (PCACHE_SIZE - ETHER_CHUNK)) { // room in the cache for more, juat accept it locally
+ INFO("Transfer A [%d, %d, %d, %d]", count, bytesInCache, PCACHE_SIZE - ETHER_CHUNK, PCACHE_SIZE);
+ memcpy(pCache + bytesInCache, path, count);
+ bytesInCache += count;
+ pCache[bytesInCache] = '\0'; // one past the data, so ok (and good for debugging text streams)
+ ret = HTTPServer::ACCEPT_CONTINUE;
+ } else if ((bytesInCache + count) < PCACHE_SIZE) { // room in the cache, now process it.
+ INFO("Transfer B [%d, %d, %d, %d]", count, bytesInCache, PCACHE_SIZE - ETHER_CHUNK, PCACHE_SIZE);
+ memcpy(pCache + bytesInCache, path, count);
+ bytesInCache += count;
+ pCache[bytesInCache] = '\0'; // one past the data, so ok (and good for debugging text streams)
+ ret = ProcessCache(svr, type); // now hand it off
+ } else {
+ INFO("Transfer C [%d, %d, %d, %d]", count, bytesInCache, PCACHE_SIZE - ETHER_CHUNK, PCACHE_SIZE);
+ ret = ProcessCache(svr, type); // panic, ah!, ok, first hand it off then cache it
+ memcpy(pCache + bytesInCache, path, count);
+ bytesInCache += count;
+ pCache[bytesInCache] = '\0'; // one past the data, so ok (and good for debugging text streams)
+ ERR("FATAL - didn't flush pCache, %d in cache.", bytesInCache);
+ break;
+ }
+ break;
+
+ case HTTPServer::SEND_PAGE:
+ svr->header(200, "OK", svr->GetSupportedType(".htm"));
+ svr->send("<html><head><title>Dynamic File Submit</title></head>\r\n");
+ svr->send("<body>\r\n");
+ svr->send("<h1>Smart WiFly Web Server</h1>\r\n");
+ sprintf(buf, "This example permits you to upload a file of not more than %d bytes. ", ACCEPT_MAX_BYTES);
+ svr->send(buf);
+ svr->send("If the file is one of the supported types, you can then download it too. ");
+ svr->send("Select the file using the form gadget and then submit it.");
+ switch(accept) {
+ case IDLE:
+ default:
+ break;
+ case ACCEPTED:
+ svr->send("<br/><b>Previous submission accepted.</b><br/>\r\n");
+ break;
+ case REJECTED_TOO_LARGE:
+ svr->send("<br/><b>Previous submission rejected.</b><br/>\r\n");
+ break;
+ }
+ // note that to accept a file, the form enctype is multipart/form-data.
+ svr->send("<blockquote>\r\n");
+ sprintf(buf, "<form method='post' enctype='multipart/form-data' action='%s'>\r\n", path);
+ svr->send(buf);
+ svr->send("<table border='0'>\r\n");
+ svr->send("<tr><td>File</td><td><input type='file' name='InFile' size='60'></td></tr>\r\n");
+ svr->send("<tr><td> </td><td><input type='submit' value='Submit'><input type='reset' value='Clear'></td></tr>\r\n");
+ svr->send("</table>\r\n");
+ svr->send("</form>\r\n");
+ svr->send("</blockquote>\r\n");
+#if 1
+ sprintf(buf, "<h2>Directory of [%s]:</h2>\r\n", svr->GetWebRoot());
+ svr->send(buf);
+ d = opendir(svr->GetWebRoot());
+ if ( d != NULL ) {
+ svr->send("<ul>\r\n");
+ while ( (p = readdir(d)) != NULL ) {
+ if (svr->GetSupportedType(p->d_name) != NULL)
+ sprintf(buf, "<li><a href='%s'>%s</a> (%d bytes)</li>\r\n", p->d_name, p->d_name, FileSize(svr->GetWebRoot(), p->d_name)); // only supported types linked
+ else
+ sprintf(buf, "<li>%s (%d bytes)</li>\r\n", p->d_name, FileSize(svr->GetWebRoot(), p->d_name)); // We could skip/hide these if that makes more sense
+ svr->send(buf);
+ }
+ svr->send("</ul>\r\n");
+ closedir(d);
+ } else {
+ svr->send("Unable to open directory!");
+ }
+#endif
+ svr->send("<a href='/'>Back to main</a></body></html>\r\n");
+ ret = HTTPServer::ACCEPT_COMPLETE;
+ break;
+
+ default:
+ printf("unknown command %d\r\n", type);
+ ret = HTTPServer::ACCEPT_ERROR;
+ break;
+ }
+ return ret;
+}
+
+static void FreeResources(void)
+{
+ if (fp) {
+ fclose(fp);
+ fp = NULL;
+ }
+ if (fqfn) {
+ free(fqfn);
+ fqfn = NULL;
+ }
+ if (contenttype) {
+ free(contenttype);
+ contenttype = NULL;
+ }
+ if (pCache) {
+ free(pCache);
+ pCache = NULL;
+ bytesInCache = 0;
+ }
+}
+
+static int FileSize(const char * dir, const char * filename)
+{
+ int size = 0;
+ char * fqfn = (char *)malloc(strlen(dir) + strlen(filename) + 2);
+ if (fqfn) {
+ strcpy(fqfn, dir);
+ strcat(fqfn, "\\");
+ strcat(fqfn, filename);
+ FILE * f = fopen(fqfn,"r");
+ if (f) {
+ fseek(f, 0, SEEK_END); // seek to end of file
+ size = ftell(f);
+ fclose(f);
+ }
+ free(fqfn);
+ }
+ return size;
+}
+
+// very simple ASCII only function to convert
+// UPPERCASE to lowercase.
+//
+static char _tolower(char c)
+{
+ if (c >= 'A' && c <= 'Z')
+ c = c - 'A' + 'a';
+ return c;
+}
+
+// case insensitive compare
+static int _strnicmp(const char * left, const char * right, int len)
+{
+ if (!left || !right)
+ return 0; // actually, can't compare so this is a small lie
+ while (len) {
+ if (_tolower(*left) < _tolower(*right))
+ return -1;
+ else if (_tolower(*left) > _tolower(*right))
+ return 1;
+ left++;
+ right++;
+ len--;
+ }
+ return 0; // match
+}
+
+// Search a haystack string for a needle string - case insensitive.
+//
+static char * _stristr(char * pHaystack, const char * pNeedle)
+{
+ if (!pHaystack || !*pHaystack || !pNeedle || !*pNeedle)
+ return NULL; // bad params
+ while (*pHaystack) {
+ if (_strnicmp(pHaystack, pNeedle, strlen(pNeedle)) == 0)
+ return pHaystack;
+ pHaystack++;
+ }
+ return NULL;
+}
+
+// Search the [possibly] binary haystack for the Needle, which is a string.
+// This is used to find the 'boundary' marker in a stream from the network
+// interface. The contents between the boundary markers could be text, or
+// it could be a binary stream. Since the haystack could be binary, we need
+// to control the search by a length parameter.
+//
+static char * _strnbin(char * pBinstack, const char *pNeedle, int len)
+{
+ char * pLast;
+ if (!pBinstack || !pNeedle || !*pNeedle || len == 0)
+ return NULL; // bad params
+ len -= strlen(pNeedle); // only search [most] of the haystack
+ pLast = pBinstack + len;
+ while (pBinstack <= pLast) {
+ if (_strnicmp(pBinstack, pNeedle, strlen(pNeedle)) == 0)
+ return pBinstack;
+ pBinstack++;
+ }
+ return NULL;
+}
+
diff -r 5ca4a8b8d888 -r 962d71d6dd0e Examples/DynamicFileIn.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Examples/DynamicFileIn.h Mon Feb 02 03:03:08 2015 +0000 @@ -0,0 +1,11 @@ + +#ifndef DYNAMICFILEIN_H +#define DYNAMICFILEIN_H +#include "mbed.h" + +#include "SW_HTTPServer.h" + +HTTPServer::CallBackResults DynamicFileIn(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount); + +#endif // DYNAMICFILEIN_H +
diff -r 5ca4a8b8d888 -r 962d71d6dd0e Examples/DynamicPages.cpp
--- a/Examples/DynamicPages.cpp Mon Sep 01 21:25:27 2014 +0000
+++ b/Examples/DynamicPages.cpp Mon Feb 02 03:03:08 2015 +0000
@@ -8,7 +8,7 @@
#include "SW_HTTPServer.h"
#include "DynamicPages.h"
-#define DEBUG "dynP" /* activate this before "Utility.h" */
+//#define DEBUG "dynP" /* define DEBUG before "Utility.h" to activate the INFO, WARN, ERR macros. */
#include "Utility.h"
@@ -71,7 +71,7 @@
HTTPServer::CallBackResults SimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
{
static unsigned int pageCounter = 0;
- char buf[100];
+ char buf[150];
HTTPServer::CallBackResults ret = HTTPServer::ACCEPT_ERROR;
HTTPServer::SW_PerformanceData perfData;
//Wifly * w = svr->GetWifly()->getInstance();
@@ -192,7 +192,7 @@
///
HTTPServer::CallBackResults SimpleDynamicForm(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
{
- char buf[100];
+ char buf[150];
const char * p1, *p2;
HTTPServer::CallBackResults ret = HTTPServer::ACCEPT_ERROR;
diff -r 5ca4a8b8d888 -r 962d71d6dd0e Examples/SecurePage.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Examples/SecurePage.cpp Mon Feb 02 03:03:08 2015 +0000
@@ -0,0 +1,87 @@
+
+#include "mbed.h"
+
+#include "SW_HTTPServer.h"
+#include "DynamicPages.h"
+#define DEBUG "Secr"
+#include "Utility.h"
+#include "Base64.h"
+
+
+Base64 bc64;
+char * converted = NULL;
+size_t convertedLen = 0;
+bool accessGranted = false;
+
+bool CredentialCheck(char * user, char * pass)
+{
+ INFO("CredentialCheck(%s,%s)", user, pass);
+ if (strcmp(user, "user1") == 0 && strcmp(pass, "pass1") == 0)
+ return true;
+ else
+ return false;
+}
+
+
+/// SimpleSecurityCheck
+HTTPServer::CallBackResults SimpleSecurityCheck(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount)
+{
+ HTTPServer::CallBackResults ret = HTTPServer::ACCEPT_ERROR;
+ char buf[150];
+ INFO("SecurityCheck: %d", type);
+ switch (type) {
+ case HTTPServer::SEND_PAGE:
+ accessGranted = false;
+ INFO("hdr: %s.", svr->GetHeaderValue("Authorization"));
+ if (svr->GetHeaderValue("Authorization") != NULL) {
+ const char * p = svr->GetHeaderValue("Authorization");
+ INFO("p: %s", p);
+ if (p && strncmp(p, "Basic ", 6) == 0) {
+ p += 6;
+ if (converted) // this should never be true, but is a nice safeguard
+ free(converted);
+ converted = bc64.Decode(p, strlen(p), &convertedLen);
+ INFO("converted{%s}", converted);
+ if (converted) {
+ // Now check the actual credentials...
+ char *colon;
+ converted[convertedLen] = '\0';
+ colon = strchr(converted, ':');
+ if (colon) {
+ *colon++ = '\0';
+ if (CredentialCheck(converted, colon))
+ accessGranted = true;
+ }
+ }
+ }
+ }
+ if (!accessGranted)
+ svr->header(401, "Access Denied", "WWW-Authenticate: Basic realm='Smart HTTPServer insecure logon'\r\n");
+ else {
+ svr->header(200, "OK", "Content-Type: text/html\r\n");
+ svr->send("<html><head><title>Security Check</title></head>\r\n");
+ svr->send("<body>\r\n");
+ sprintf(buf, "<h1>Welcome %s</h1>\r\n", converted);
+ svr->send(buf);
+ svr->send("Authorization was approved.<p/>\r\n");
+ svr->send("Your browser will now cache the credentials until you close it.");
+ svr->send("So, be sure to close it for maximum security (and don't let it save your credentials).<p/>\r\n");
+ svr->send("<a href='/'>back to main</a></body></html>\r\n");
+ }
+ if (converted)
+ free(converted); // don't leak memory
+ ret = HTTPServer::ACCEPT_COMPLETE;
+ break;
+ case HTTPServer::CONTENT_LENGTH_REQUEST:
+ ret = HTTPServer::ACCEPT_COMPLETE;
+ break;
+ case HTTPServer::DATA_TRANSFER:
+ ret = HTTPServer::ACCEPT_COMPLETE;
+ break;
+ default:
+ ret = HTTPServer::ACCEPT_ERROR;
+ break;
+ }
+ return ret;
+}
+
diff -r 5ca4a8b8d888 -r 962d71d6dd0e Examples/SecurePage.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Examples/SecurePage.h Mon Feb 02 03:03:08 2015 +0000 @@ -0,0 +1,12 @@ + +#ifndef SECUREPAGE_H +#define SECUREPAGE_H +#include "mbed.h" + +#include "SW_HTTPServer.h" + +HTTPServer::CallBackResults SimpleSecurityCheck(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount); + +#endif // SECUREPAGE_H + +
diff -r 5ca4a8b8d888 -r 962d71d6dd0e Examples/ServerConfig.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Examples/ServerConfig.cpp Mon Feb 02 03:03:08 2015 +0000
@@ -0,0 +1,125 @@
+/// Demonstration of dynamic page creation using the Smartware Web Server
+///
+/// There are a few samples here of the dynamically generated web pages.
+/// Read the header above each one for more details.
+///
+/// Status - very experimental.
+///
+/// It reads the ini file, but is not reliably managing it.
+/// have only tried on the local file system so far.
+///
+#include "mbed.h"
+
+#include "SW_HTTPServer.h"
+#include "DynamicPages.h"
+#include "Utility.h"
+#include "IniManager.h"
+
+
+
+/// ServerConfig
+///
+/// ssid
+/// pass code
+/// Security
+/// time server
+/// time zone
+///
+HTTPServer::CallBackResults ServerConfig(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount) {
+ char buf[250];
+ HTTPServer::CallBackResults ret = HTTPServer::ACCEPT_ERROR;
+ char ssid[50] = "";
+ char pass[50] = "";
+ char security[10] = "";
+ char timeserver[60] = "";
+ char timezone[4] = ""; // -12
+ //char *securityList[] =
+ // { "NONE", "WEP_128", "WPA1", "WPA", "WPA2_PSK", "n/a", "ADHOC", "n/a", "WPE_64" };
+ INI ini("/local/config.ini");
+
+ switch (type) {
+ case HTTPServer::SEND_PAGE:
+ // svr->GetParameter("SSID");
+
+ // ReadString(const char * section, const char * key, char * buffer, size_t bufferSize)
+ ini.ReadString("Wifi", "ssid", ssid, sizeof(ssid), svr->GetParameter("ssid"));
+ printf("svr ssid: %s\r\n", ssid);
+ printf("web ssid: %s\r\n", svr->GetParameter("ssid"));
+ if (svr->GetParameter("ssid") && strcmp(ssid, svr->GetParameter("ssid")) != 0)
+ {
+ strcpy(ssid, svr->GetParameter("ssid"));
+ printf("== ssid: %s\r\n", ssid);
+ ini.WriteString("Wifi", "ssid", ssid);
+ }
+ printf("svr ssid: %s\r\n", ssid);
+ ini.ReadString("Wifi", "pass", pass, sizeof(pass), svr->GetParameter("pass"));
+ if (svr->GetParameter("pass") && strcmp(pass, svr->GetParameter("pass")) != 0)
+ {
+ strcpy(pass, svr->GetParameter("pass"));
+ ini.WriteString("Wifi", "pass", pass);
+ }
+ printf("svr pass: %s\r\n", pass);
+ ini.ReadString("Wifi", "security", security, sizeof(security), svr->GetParameter("security"));
+ if (svr->GetParameter("security") && strcmp(security, svr->GetParameter("security")) != 0)
+ {
+ strcpy(security, svr->GetParameter("security"));
+ ini.WriteString("Wifi", "security", security);
+ }
+ ini.ReadString("Clock", "timeserver", timeserver, sizeof(timeserver), svr->GetParameter("timeserver"));
+ if (svr->GetParameter("timeserver") && strcmp(timeserver, svr->GetParameter("timeserver")) != 0)
+ {
+ strcpy(timeserver, svr->GetParameter("timeserver"));
+ ini.WriteString("Clock", "timeserver", timeserver);
+ }
+ ini.ReadString("Clock", "timezone", timezone, sizeof(timezone), svr->GetParameter("timezone"));
+ if (svr->GetParameter("timezone") && strcmp(timezone, svr->GetParameter("timezone")) != 0)
+ {
+ strcpy(timezone, svr->GetParameter("timezone"));
+ ini.WriteString("Clock", "timezone", timezone);
+ }
+
+ // send the header
+ svr->header(200, "OK", svr->GetSupportedType(".htm"));
+ // send some data
+ svr->send("<html><head><title>Server Config</title></head>\r\n");
+ svr->send("<body>\r\n");
+ svr->send("<h1>Smart WiFly Web Server - Server Config</h1>\r\n");
+ svr->send("Configure options here.<br/>\r\n");
+ // Create a user form for which they can post changes
+ sprintf(buf, "<form method='post' action='%s'>\r\n", path);
+ svr->send(buf);
+ // show the parameters in a nice format
+ svr->send("<table border='1'>\r\n");
+ sprintf(buf, "<tr><td>Setting</td><td>Value</td><td>Description</td></tr>\r\n");
+ svr->send(buf);
+ sprintf(buf, "<tr><td>SSID</td><td><input type='text' name='ssid' value='%s'></td><td>Name of the Access Point</td></tr>\r\n", ssid);
+ svr->send(buf);
+ sprintf(buf, "<tr><td>PassCode</td><td><input type='text' name='pass' value='%s'></td><td>Passcode</td></tr>\r\n", pass);
+ svr->send(buf);
+ sprintf(buf, "<tr><td>Security</td><td><input type='text' name='security' value='%s'></td><td>Security Setting</td></tr>\r\n", security);
+ svr->send(buf);
+ sprintf(buf, "<tr><td>Time Server</td><td><input type='text' name='timeserver' value='%s'></td><td>Time Server</td></tr>\r\n", timeserver);
+ svr->send(buf);
+ sprintf(buf, "<tr><td>Time Zone</td><td><input type='text' name='timezone' value='%s'></td><td>Time Zone</td></tr>\r\n", timezone);
+ svr->send(buf);
+ //svr->send("<tr><td> </td><td>File</td><td><input type='file' name='InFile' size='40'></td></tr>\r\n");
+ svr->send("<tr><td> </td><td colspan='2'><input type='submit' value='submit'><input type='reset' value='clear'></td></tr>\r\n");
+ svr->send("</table>\r\n");
+ svr->send("</form>\r\n");
+ // see how we're doing with free memory
+ svr->send("<br/><a href='/'>Back to main</a></body></html>\r\n");
+ ret = HTTPServer::ACCEPT_COMPLETE;
+ break;
+ case HTTPServer::CONTENT_LENGTH_REQUEST:
+ ret = HTTPServer::ACCEPT_COMPLETE;
+ break;
+ case HTTPServer::DATA_TRANSFER:
+ ret = HTTPServer::ACCEPT_COMPLETE;
+ break;
+ default:
+ ret = HTTPServer::ACCEPT_ERROR;
+ break;
+ }
+ return ret;
+}
+
diff -r 5ca4a8b8d888 -r 962d71d6dd0e Examples/ServerConfig.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Examples/ServerConfig.h Mon Feb 02 03:03:08 2015 +0000 @@ -0,0 +1,11 @@ + +#ifndef SERVERCONFIG_H +#define SERVERCONFIG_H +#include "mbed.h" + +#include "SW_HTTPServer.h" + +HTTPServer::CallBackResults ServerConfig(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, const HTTPServer::namevalue *params, int paramcount); + +#endif // SERVERCONFIG_H +
diff -r 5ca4a8b8d888 -r 962d71d6dd0e IniManager.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IniManager.lib Mon Feb 02 03:03:08 2015 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/WiredHome/code/IniManager/#4947b8c244e9
diff -r 5ca4a8b8d888 -r 962d71d6dd0e SW_HTTPServer.lib --- a/SW_HTTPServer.lib Mon Sep 01 21:25:27 2014 +0000 +++ b/SW_HTTPServer.lib Mon Feb 02 03:03:08 2015 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/WiredHome/code/SW_HTTPServer/#0427544a5c08 +http://mbed.org/users/WiredHome/code/SW_HTTPServer/#3fc773c2986e
diff -r 5ca4a8b8d888 -r 962d71d6dd0e main.cpp
--- a/main.cpp Mon Sep 01 21:25:27 2014 +0000
+++ b/main.cpp Mon Feb 02 03:03:08 2015 +0000
@@ -1,71 +1,236 @@
/** @file main.cpp contains the main program that a user would write.
* see the documentation above "main"
*/
-#include "mbed.h" // ver 83
+#include "mbed.h" // testing ver 92, last stable v89
#include "RawSerial.h"
// My components
+#include "IniManager.h"
#include "Utility.h" // a couple of simple helper functions
#include "WiflyInterface.h" // ver 53, derived from mbed official ver 4
#include "SW_HTTPServer.h" // ver 31, derived from nweb
#include "DynamicPages.h" // my dynamically generated pages
+#include "SecurePage.h" // my secure pages
+#include "ServerConfig.h" // various configuration options
+#include "DynamicFileIn.h" // Upload a file to the server
+//#include "MSCFileSystem.h" // ver 1, this and SDFileSystem
+//#include "SDFileSystem.h" // ver 1, this and MSCFileSystem
#define MBED_APP_BOARD 1 /* http://mbed.org/components/mbed-Application-Board/ */
#define SMART_BOARD 2 /* http://mbed.org/users/WiredHome/notebook/SmartBoard-baseboard/ */
#define HW_ADAPTER MBED_APP_BOARD /* Which board are we compiling against? SMART_BOARD or MBED_APP_BOARD */
+#if HW_ADAPTER == MBED_APP_BOARD
+#include "C12832.h"
+
+C12832 lcd(p5, p7, p6, p8, p11);
+#endif
+
#define HTTP_SERVER_PORT 80
RawSerial pc(USBTX, USBRX);
+LocalFileSystem local("local"); // some place to hold settings and maybe the static web pages
+//MSCFileSystem msc("msc"); // Mass Storage on USB
+//SDFileSystem sd(p5, p6, p7, p8, "sd"); // for the static web pages
+#define WEBROOT "/local"
+
+PwmOut signOfLife(LED1);
+
+Timer onceinawhile;
+
+/// ShowSignOfLife
+///
+/// Pulse an LED to indicate a sign of life of the program.
+/// also has some moderate entertainment value.
+///
+void ShowSignOfLife() {
+#define PI 3.14159265359
+ static Timer activityTimer;
+ static unsigned int activityStart;
+ static bool init;
+ //static float currentBrightness = 0.0;
+ static int degrees = 0;
+ float v;
+ //static bool rampUp = true;
+
+ if (!init) {
+ activityTimer.start();
+ activityStart = (unsigned int) activityTimer.read_ms();
+ init = true;
+ }
+ if ((unsigned int)activityTimer.read_ms() - activityStart > 20) {
+
+ v = sin(degrees * PI / 180);
+ if (v < 0)
+ v = 0;
+ signOfLife = v;
+ degrees += 2;
+ activityStart = (unsigned int) activityTimer.read_ms();
+ }
+}
+
+// A time-bound hook on startup to permit the user to access the wifly module
+//
+void WiFlyShell(Wifly & wifly, PC & pc)
+{
+ Timer userTimer;
+ const int baudrates[] = {2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400};
+ static int i = 0;
+
+ pc.printf("Pausing 5 sec for shell access to WiFly (press <enter>).\r\n");
+ userTimer.start();
+ do {
+ if (pc.readable()) {
+ bool loop = true;
+ int c = pc.getc();
+ pc.printf("Shell opened to WiFly module: <esc> to exit,\r\n ctrl-B to reboot, ctrl-C to step baud\r\n");
+ while (loop) {
+ if (pc.readable()) {
+ int c = pc.getc();
+ switch (c) {
+ case '\x1B': // <ESC>
+ loop = false;
+ break;
+ case '\x02': // Ctrl-B
+ wifly.reboot();
+ break;
+ case '\x03': // Ctrl-C
+ pc.printf("Setting to %d baud.\r\n", baudrates[i]);
+ wifly.baud(baudrates[i]);
+ i++;
+ if (i >= (sizeof(baudrates)/sizeof(baudrates[0])))
+ i = 0;
+ break;
+ default:
+ wifly.putc(c);
+ break;
+ }
+ }
+ if (wifly.readable())
+ pc.putc(wifly.getc());
+ }
+ }
+ } while (userTimer.read_ms() < 5000);
+ pc.printf(" WiFly shell closed.\r\n");
+}
+/// Smart-WiFly-WebServer is the creation of a web server using a WiFly module.
+///
+/// This is a working version, but it is not using the standardized wifly
+/// library, which would not work for me... I had to make a number
+/// of changes to get it to work well. After trying to minmimize those
+/// changes, additional improvements became more and more clumsy, so
+/// I have now been working to refactor where it makes sense, even as
+/// it further deviates from the mbed-library version.
+///
+/// I created this because I couldn't find one that worked and wanted to
+/// learn the WiFly module. There are a lot of possible improvements:
+/// @li I think I'm not using the Socket interface as fully as I should.
+/// @li I would like it to be faster (the interface from mbed to wifly is
+/// limited to 230400 baud before it drops chars. HW handshake could
+/// improve this, but the HW handshake pins on the LPC1768 are not
+/// both brought out.
+/// @li I would like to integrate this with the rtos.
+/// @li If a page has multiple components (e.g. images), it appears
+/// unreliable. It doesn't see the request for the extra component.
+/// A poor workaround, for images, is to use a javascript to post-
+/// load them. This is fundamentally a constraint of the WiFly module.
+///
+/// history:
+/// @li 20130602 added .txt to the supported types (e.g. robots.txt), so
+/// revised the credentials to .crd, which is an unsupported type
+/// therefore won't be delivered to the user.
+///
+/// @note Copyright © 2013 by Smartware Computing, all rights reserved.
+/// Individuals may use this application for evaluation or non-commercial
+/// purposes. Within this restriction, changes may be made to this application
+/// as long as this copyright notice is retained. The user shall make
+/// clear that their work is a derived work, and not the original.
+/// Users of this application and sources accept this application "as is" and
+/// shall hold harmless Smartware Computing, for any undesired results while
+/// using this application - whether real or imagined.
+///
+/// @author David Smart, Smartware Computing
+///
int main()
{
+ char ssid[60], pass[60];
+ INI ini("/local/config.ini"); // handles the credentials
+
pc.baud(460800); // I like a snappy terminal, so crank it up!
pc.printf("\r\nSmart WiFly - Build " __DATE__ " " __TIME__ "\r\n");
+ if (!ini.ReadString("Wifi", "ssid", ssid, sizeof(ssid))
+ || !ini.ReadString("Wifi", "pass", pass, sizeof(pass))) {
+ pc.printf("**** ERROR, credentials not found. ****\r\n");
+ wait(1.0);
+ error(" Waiting for user to fix this problem. \r\n"); // flash and die
+ }
+
// first signals are tx, rx, reset, status pins.
#if HW_ADAPTER == MBED_APP_BOARD
- WiflyInterface wifly( p9, p10, p30, p29, "sf1896", "Sir William Hearth", WPA);
+ WiflyInterface wifly( p9, p10, p30, p29, ssid, pass, WPA);
#elif HW_ADAPTER == SMART_BOARD
- WiflyInterface wifly(p28, p27, p23, p24, "sf1896", "Sir William Hearth", WPA);
+ WiflyInterface wifly(p28, p27, p23, p24, ssid, pass, WPA);
#endif
// Bring the WiFly interface online
do {
wifly.init(); // start it up as a client of my network using DHCP
- wifly.baud(460800); // Only 230K w/o HW flow control
+ wifly.baud(230400); // Only 230K w/o HW flow control
if (0 == wifly.connect())
break;
pc.printf(" Failed to connect, retrying...\r\n");
wait(1.0);
wifly.reset();
} while (1);
-
+
// Let the human know it is ready - if they are watching
pc.printf("Connect to this server at http://%s:%d\r\n", wifly.getIPAddress(), HTTP_SERVER_PORT);
+ #if HW_ADAPTER == MBED_APP_BOARD
+ lcd.cls();
+ lcd.locate(0,3);
+ lcd.printf("mbed application board!\r\n");
+ lcd.printf("http://%s:%d", wifly.getIPAddress(), HTTP_SERVER_PORT);
+ #endif
+
// Now let's instantiate the web server - along with a few settings:
// the Wifly object, the port of interest (typically 80),
// file system path to the static pages, if any.
// the maximum parameters per transaction (in the query string),
// the maximum number of dynamic pages that can be registered,
// the serial port back thru USB (for development/logging)
- HTTPServer svr(HTTP_SERVER_PORT, "/", 15, 30, 10, &pc);
+ HTTPServer svr(HTTP_SERVER_PORT, WEBROOT, 15, 30, 10, &pc);
// But for even more fun, I'm registering a few dynamic pages
// You see the handlers for in DynamicPages.cpp.
// Here you can see the path to place on the URL.
// ex. http://192.168.1.140/dyn
- svr.RegisterHandler("/", SuperSimpleDynamicPage);
+ //svr.RegisterHandler("/", SuperSimpleDynamicPage);
svr.RegisterHandler("/dyn", SimpleDynamicPage);
svr.RegisterHandler("/led", SimpleDynamicForm);
-
+
+ svr.RegisterHandler("/dyn1", SimpleDynamicPage);
+ svr.RegisterHandler("/dyn2", SimpleDynamicForm);
+ svr.RegisterHandler("/pass", SimpleSecurityCheck);
+ svr.RegisterHandler("/config", ServerConfig);
+ svr.RegisterHandler("/FileIn", DynamicFileIn);
+ onceinawhile.start();
while (true) {
+ if (onceinawhile.read_ms() >= 200)
+ {
+ onceinawhile.reset();
+ //pc.printf("Largest free mem block is %d\r\n", Free());
+ ShowSignOfLife();
+ }
svr.Poll(); // non-blocking, but also not deterministic
+ if (pc.readable())
+ WiFlyShell(wifly, pc); // allow shell access at runtime (if user taps a key)
}
}
diff -r 5ca4a8b8d888 -r 962d71d6dd0e mbed.bld --- a/mbed.bld Mon Sep 01 21:25:27 2014 +0000 +++ b/mbed.bld Mon Feb 02 03:03:08 2015 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/9327015d4013 \ No newline at end of file +http://mbed.org/users/mbed_official/code/mbed/builds/4fc01daae5a5 \ No newline at end of file