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
--- /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
--- /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; +} +
--- /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 +
--- 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;
--- /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; +} +
--- /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 + +
--- /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; +} +
--- /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 +
--- /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
--- 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
--- 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) } }
--- 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