From 6e4be1c4803bdf42ee1243d7cb3e8b7f56bdc405 Mon Sep 17 00:00:00 2001
From: Ludovic Pouzenc <ludovic@pouzenc.fr>
Date: Fri, 17 Jun 2016 12:09:05 +0200
Subject: Initial import of needed libraries.w

---
 libraries/WebServer/WebServer.h  | 1159 ++++++++++++++++++++++++++++++++++++++
 libraries/WebServer/keywords.txt |   34 ++
 libraries/WebServer/readme.md    |  103 ++++
 3 files changed, 1296 insertions(+)
 create mode 100644 libraries/WebServer/WebServer.h
 create mode 100644 libraries/WebServer/keywords.txt
 create mode 100644 libraries/WebServer/readme.md

diff --git a/libraries/WebServer/WebServer.h b/libraries/WebServer/WebServer.h
new file mode 100644
index 0000000..0bbbf35
--- /dev/null
+++ b/libraries/WebServer/WebServer.h
@@ -0,0 +1,1159 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil;  c-file-style: "k&r"; c-basic-offset: 2; -*-
+
+   Webduino, a simple Arduino web server
+   Copyright 2009-2012 Ben Combee, Ran Talbott, Christopher Lee, Martin Lormes
+
+   Permission is hereby granted, free of charge, to any person obtaining a copy
+   of this software and associated documentation files (the "Software"), to deal
+   in the Software without restriction, including without limitation the rights
+   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+   copies of the Software, and to permit persons to whom the Software is
+   furnished to do so, subject to the following conditions:
+
+   The above copyright notice and this permission notice shall be included in
+   all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+   THE SOFTWARE.
+*/
+
+#ifndef WEBDUINO_H_
+#define WEBDUINO_H_
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <EthernetClient.h>
+#include <EthernetServer.h>
+
+/********************************************************************
+ * CONFIGURATION
+ ********************************************************************/
+
+#define WEBDUINO_VERSION 1007
+#define WEBDUINO_VERSION_STRING "1.7"
+
+#if WEBDUINO_SUPRESS_SERVER_HEADER
+#define WEBDUINO_SERVER_HEADER ""
+#else
+#define WEBDUINO_SERVER_HEADER "Server: Webduino/" WEBDUINO_VERSION_STRING CRLF
+#endif
+
+// standard END-OF-LINE marker in HTTP
+#define CRLF "\r\n"
+
+// If processConnection is called without a buffer, it allocates one
+// of 32 bytes
+#define WEBDUINO_DEFAULT_REQUEST_LENGTH 32
+
+// How long to wait before considering a connection as dead when
+// reading the HTTP request.  Used to avoid DOS attacks.
+#ifndef WEBDUINO_READ_TIMEOUT_IN_MS
+#define WEBDUINO_READ_TIMEOUT_IN_MS 1000
+#endif
+
+#ifndef WEBDUINO_FAIL_MESSAGE
+#define WEBDUINO_FAIL_MESSAGE "<h1>EPIC FAIL</h1>"
+#endif
+
+#ifndef WEBDUINO_AUTH_REALM
+#define WEBDUINO_AUTH_REALM "Webduino"
+#endif // #ifndef WEBDUINO_AUTH_REALM
+
+#ifndef WEBDUINO_AUTH_MESSAGE
+#define WEBDUINO_AUTH_MESSAGE "<h1>401 Unauthorized</h1>"
+#endif // #ifndef WEBDUINO_AUTH_MESSAGE
+
+#ifndef WEBDUINO_SERVER_ERROR_MESSAGE
+#define WEBDUINO_SERVER_ERROR_MESSAGE "<h1>500 Internal Server Error</h1>"
+#endif // WEBDUINO_SERVER_ERROR_MESSAGE
+
+// add '#define WEBDUINO_FAVICON_DATA ""' to your application
+// before including WebServer.h to send a null file as the favicon.ico file
+// otherwise this defaults to a 16x16 px black diode on blue ground
+// (or include your own icon if you like)
+#ifndef WEBDUINO_FAVICON_DATA
+#define WEBDUINO_FAVICON_DATA { 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, \
+                                0x10, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, \
+                                0xb0, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, \
+                                0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, \
+                                0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, \
+                                0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, \
+                                0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, \
+                                0x00, 0xff, 0xff, 0x00, 0x00, 0xcf, 0xbf, \
+                                0x00, 0x00, 0xc7, 0xbf, 0x00, 0x00, 0xc3, \
+                                0xbf, 0x00, 0x00, 0xc1, 0xbf, 0x00, 0x00, \
+                                0xc0, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0xc0, 0xbf, 0x00, 0x00, 0xc1, 0xbf, \
+                                0x00, 0x00, 0xc3, 0xbf, 0x00, 0x00, 0xc7, \
+                                0xbf, 0x00, 0x00, 0xcf, 0xbf, 0x00, 0x00, \
+                                0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                                0x00, 0x00 }
+#endif // #ifndef WEBDUINO_FAVICON_DATA
+
+// add "#define WEBDUINO_SERIAL_DEBUGGING 1" to your application
+// before including WebServer.h to have incoming requests logged to
+// the serial port.
+#ifndef WEBDUINO_SERIAL_DEBUGGING
+#define WEBDUINO_SERIAL_DEBUGGING 0
+#endif
+#if WEBDUINO_SERIAL_DEBUGGING
+#include <HardwareSerial.h>
+#endif
+
+// declared in wiring.h
+extern "C" unsigned long millis(void);
+
+// declare a static string
+#define P(name)   static const prog_uchar name[] PROGMEM
+
+// returns the number of elements in the array
+#define SIZE(array) (sizeof(array) / sizeof(*array))
+
+/********************************************************************
+ * DECLARATIONS
+ ********************************************************************/
+
+/* Return codes from nextURLparam.  NOTE: URLPARAM_EOS is returned
+ * when you call nextURLparam AFTER the last parameter is read.  The
+ * last actual parameter gets an "OK" return code. */
+
+typedef enum URLPARAM_RESULT { URLPARAM_OK,
+                               URLPARAM_NAME_OFLO,
+                               URLPARAM_VALUE_OFLO,
+                               URLPARAM_BOTH_OFLO,
+                               URLPARAM_EOS         // No params left
+};
+
+class WebServer: public Print
+{
+public:
+  // passed to a command to indicate what kind of request was received
+  enum ConnectionType { INVALID, GET, HEAD, POST, PUT, DELETE, PATCH };
+
+  // any commands registered with the web server have to follow
+  // this prototype.
+  // url_tail contains the part of the URL that wasn't matched against
+  //          the registered command table.
+  // tail_complete is true if the complete URL fit in url_tail,  false if
+  //          part of it was lost because the buffer was too small.
+  typedef void Command(WebServer &server, ConnectionType type,
+                       char *url_tail, bool tail_complete);
+
+  // constructor for webserver object
+  WebServer(const char *urlPrefix = "", int port = 80);
+
+  // start listening for connections
+  void begin();
+
+  // check for an incoming connection, and if it exists, process it
+  // by reading its request and calling the appropriate command
+  // handler.  This version is for compatibility with apps written for
+  // version 1.1,  and allocates the URL "tail" buffer internally.
+  void processConnection();
+
+  // check for an incoming connection, and if it exists, process it
+  // by reading its request and calling the appropriate command
+  // handler.  This version saves the "tail" of the URL in buff.
+  void processConnection(char *buff, int *bufflen);
+
+  // set command that's run when you access the root of the server
+  void setDefaultCommand(Command *cmd);
+
+  // set command run for undefined pages
+  void setFailureCommand(Command *cmd);
+
+  // add a new command to be run at the URL specified by verb
+  void addCommand(const char *verb, Command *cmd);
+
+  // utility function to output CRLF pair
+  void printCRLF();
+
+  // output a string stored in program memory, usually one defined
+  // with the P macro
+  void printP(const prog_uchar *str);
+
+  // inline overload for printP to handle signed char strings
+  void printP(const prog_char *str) { printP((prog_uchar*)str); }
+
+  // output raw data stored in program memory
+  void writeP(const prog_uchar *data, size_t length);
+
+  // output HTML for a radio button
+  void radioButton(const char *name, const char *val,
+                   const char *label, bool selected);
+
+  // output HTML for a checkbox
+  void checkBox(const char *name, const char *val,
+                const char *label, bool selected);
+
+  // returns next character or -1 if we're at end-of-stream
+  int read();
+
+  // put a character that's been read back into the input pool
+  void push(int ch);
+
+  // returns true if the string is next in the stream.  Doesn't
+  // consume any character if false, so can be used to try out
+  // different expected values.
+  bool expect(const char *expectedStr);
+
+  // returns true if a number, with possible whitespace in front, was
+  // read from the server stream.  number will be set with the new
+  // value or 0 if nothing was read.
+  bool readInt(int &number);
+
+  // reads a header value, stripped of possible whitespace in front,
+  // from the server stream
+  void readHeader(char *value, int valueLen);
+
+  // Read the next keyword parameter from the socket.  Assumes that other
+  // code has already skipped over the headers,  and the next thing to
+  // be read will be the start of a keyword.
+  //
+  // returns true if we're not at end-of-stream
+  bool readPOSTparam(char *name, int nameLen, char *value, int valueLen);
+
+  // Read the next keyword parameter from the buffer filled by getRequest.
+  //
+  // returns 0 if everything weent okay,  non-zero if not
+  // (see the typedef for codes)
+  URLPARAM_RESULT nextURLparam(char **tail, char *name, int nameLen,
+                               char *value, int valueLen);
+
+  // compare string against credentials in current request
+  //
+  // authCredentials must be Base64 encoded outside of Webduino
+  // (I wanted to be easy on the resources)
+  //
+  // returns true if strings match, false otherwise
+  bool checkCredentials(const char authCredentials[45]);
+
+  // output headers and a message indicating a server error
+  void httpFail();
+  
+  // output headers and a message indicating "401 Unauthorized"
+  void httpUnauthorized();
+
+  // output headers and a message indicating "500 Internal Server Error"
+  void httpServerError();
+
+  // output standard headers indicating "200 Success".  You can change the
+  // type of the data you're outputting or also add extra headers like
+  // "Refresh: 1".  Extra headers should each be terminated with CRLF.
+  void httpSuccess(const char *contentType = "text/html; charset=utf-8",
+                   const char *extraHeaders = NULL);
+
+  // used with POST to output a redirect to another URL.  This is
+  // preferable to outputting HTML from a post because you can then
+  // refresh the page without getting a "resubmit form" dialog.
+  void httpSeeOther(const char *otherURL);
+
+  // implementation of write used to implement Print interface
+  virtual size_t write(uint8_t);
+  virtual size_t write(const char *str);
+  virtual size_t write(const uint8_t *buffer, size_t size);
+  size_t write(const char *data, size_t length);
+
+private:
+  EthernetServer m_server;
+  EthernetClient m_client;
+  const char *m_urlPrefix;
+
+  unsigned char m_pushback[32];
+  char m_pushbackDepth;
+
+  int m_contentLength;
+  char m_authCredentials[51];
+  bool m_readingContent;
+
+  Command *m_failureCmd;
+  Command *m_defaultCmd;
+  struct CommandMap
+  {
+    const char *verb;
+    Command *cmd;
+  } m_commands[8];
+  char m_cmdCount;
+
+  void reset();
+  void getRequest(WebServer::ConnectionType &type, char *request, int *length);
+  bool dispatchCommand(ConnectionType requestType, char *verb,
+                       bool tail_complete);
+  void processHeaders();
+  void outputCheckboxOrRadio(const char *element, const char *name,
+                             const char *val, const char *label,
+                             bool selected);
+
+  static void defaultFailCmd(WebServer &server, ConnectionType type,
+                             char *url_tail, bool tail_complete);
+  void noRobots(ConnectionType type);
+  void favicon(ConnectionType type);
+};
+
+/* define this macro if you want to include the header in a sketch source
+   file but not define any of the implementation. This is useful if
+   multiple source files are using the Webduino class. */
+#ifndef WEBDUINO_NO_IMPLEMENTATION
+
+/********************************************************************
+ * IMPLEMENTATION
+ ********************************************************************/
+
+WebServer::WebServer(const char *urlPrefix, int port) :
+  m_server(port),
+  m_client(255),
+  m_urlPrefix(urlPrefix),
+  m_pushbackDepth(0),
+  m_cmdCount(0),
+  m_contentLength(0),
+  m_failureCmd(&defaultFailCmd),
+  m_defaultCmd(&defaultFailCmd)
+{
+}
+
+void WebServer::begin()
+{
+  m_server.begin();
+}
+
+void WebServer::setDefaultCommand(Command *cmd)
+{
+  m_defaultCmd = cmd;
+}
+
+void WebServer::setFailureCommand(Command *cmd)
+{
+  m_failureCmd = cmd;
+}
+
+void WebServer::addCommand(const char *verb, Command *cmd)
+{
+  if (m_cmdCount < SIZE(m_commands))
+  {
+    m_commands[m_cmdCount].verb = verb;
+    m_commands[m_cmdCount++].cmd = cmd;
+  }
+}
+
+size_t WebServer::write(uint8_t ch)
+{
+  return m_client.write(ch);
+}
+
+size_t WebServer::write(const char *str)
+{
+  return m_client.write(str);
+}
+
+size_t WebServer::write(const uint8_t *buffer, size_t size)
+{
+  return m_client.write(buffer, size);
+}
+
+size_t WebServer::write(const char *buffer, size_t length)
+{
+  return m_client.write((const uint8_t *)buffer, length);
+}
+
+void WebServer::writeP(const prog_uchar *data, size_t length)
+{
+  // copy data out of program memory into local storage, write out in
+  // chunks of 32 bytes to avoid extra short TCP/IP packets
+  uint8_t buffer[32];
+  size_t bufferEnd = 0;
+
+  while (length--)
+  {
+    if (bufferEnd == 32)
+    {
+      m_client.write(buffer, 32);
+      bufferEnd = 0;
+    }
+
+    buffer[bufferEnd++] = pgm_read_byte(data++);
+  }
+
+  if (bufferEnd > 0)
+    m_client.write(buffer, bufferEnd);
+}
+
+void WebServer::printP(const prog_uchar *str)
+{
+  // copy data out of program memory into local storage, write out in
+  // chunks of 32 bytes to avoid extra short TCP/IP packets
+  uint8_t buffer[32];
+  size_t bufferEnd = 0;
+  
+  while (buffer[bufferEnd++] = pgm_read_byte(str++))
+  {
+    if (bufferEnd == 32)
+    {
+      m_client.write(buffer, 32);
+      bufferEnd = 0;
+    }
+  }
+
+  // write out everything left but trailing NUL
+  if (bufferEnd > 1)
+    m_client.write(buffer, bufferEnd - 1);
+}
+
+void WebServer::printCRLF()
+{
+  m_client.write((const uint8_t *)"\r\n", 2);
+}
+
+bool WebServer::dispatchCommand(ConnectionType requestType, char *verb,
+        bool tail_complete)
+{
+  // if there is no URL, i.e. we have a prefix and it's requested without a 
+  // trailing slash or if the URL is just the slash
+  if ((verb[0] == 0) || ((verb[0] == '/') && (verb[1] == 0)))
+  {
+    m_defaultCmd(*this, requestType, "", tail_complete);
+    return true;
+  }
+  // if the URL is just a slash followed by a question mark
+  // we're looking at the default command with GET parameters passed
+  if ((verb[0] == '/') && (verb[1] == '?'))
+  {
+    verb+=2; // skip over the "/?" part of the url
+    m_defaultCmd(*this, requestType, verb, tail_complete);
+    return true;
+  }
+  // We now know that the URL contains at least one character.  And,
+  // if the first character is a slash,  there's more after it.
+  if (verb[0] == '/')
+  {
+    char i;
+    char *qm_loc;
+    int verb_len;
+    int qm_offset;
+    // Skip over the leading "/",  because it makes the code more
+    // efficient and easier to understand.
+    verb++;
+    // Look for a "?" separating the filename part of the URL from the
+    // parameters.  If it's not there, compare to the whole URL.
+    qm_loc = strchr(verb, '?');
+    verb_len = (qm_loc == NULL) ? strlen(verb) : (qm_loc - verb);
+    qm_offset = (qm_loc == NULL) ? 0 : 1;
+    for (i = 0; i < m_cmdCount; ++i)
+    {
+      if ((verb_len == strlen(m_commands[i].verb))
+          && (strncmp(verb, m_commands[i].verb, verb_len) == 0))
+      {
+        // Skip over the "verb" part of the URL (and the question
+        // mark, if present) when passing it to the "action" routine
+        m_commands[i].cmd(*this, requestType,
+        verb + verb_len + qm_offset,
+        tail_complete);
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+// processConnection with a default buffer
+void WebServer::processConnection()
+{
+  char request[WEBDUINO_DEFAULT_REQUEST_LENGTH];
+  int  request_len = WEBDUINO_DEFAULT_REQUEST_LENGTH;
+  processConnection(request, &request_len);
+}
+
+void WebServer::processConnection(char *buff, int *bufflen)
+{
+  int urlPrefixLen = strlen(m_urlPrefix);
+
+  m_client = m_server.available();
+
+  if (m_client) {
+    m_readingContent = false;
+    buff[0] = 0;
+    ConnectionType requestType = INVALID;
+#if WEBDUINO_SERIAL_DEBUGGING > 1
+    Serial.println("*** checking request ***");
+#endif
+    getRequest(requestType, buff, bufflen);
+#if WEBDUINO_SERIAL_DEBUGGING > 1
+    Serial.print("*** requestType = ");
+    Serial.print((int)requestType);
+    Serial.print(", request = \"");
+    Serial.print(buff);
+    Serial.println("\" ***");
+#endif
+
+    // don't even look further at invalid requests.
+    // this is done to prevent Webduino from hanging
+    // - when there are illegal requests,
+    // - when someone contacts it through telnet rather than proper HTTP,
+    // - etc.
+    if (requestType != INVALID)
+    {
+      processHeaders();
+#if WEBDUINO_SERIAL_DEBUGGING > 1
+      Serial.println("*** headers complete ***");
+#endif
+
+      if (strcmp(buff, "/robots.txt") == 0)
+      {
+        noRobots(requestType);
+      }
+      else if (strcmp(buff, "/favicon.ico") == 0)
+      {
+        favicon(requestType);
+      }
+    }
+    if      (requestType == INVALID ||
+             strncmp(buff, m_urlPrefix, urlPrefixLen) != 0 ||
+             !dispatchCommand(requestType, buff + urlPrefixLen,
+                              (*bufflen) >= 0))
+    {
+      m_failureCmd(*this, requestType, buff, (*bufflen) >= 0);
+    }
+
+#if WEBDUINO_SERIAL_DEBUGGING > 1
+    Serial.println("*** stopping connection ***");
+#endif
+    reset();
+  }
+}
+
+bool WebServer::checkCredentials(const char authCredentials[45])
+{
+  char basic[7] = "Basic ";
+  if((0 == strncmp(m_authCredentials,basic,6)) && 
+     (0 == strcmp(authCredentials, m_authCredentials + 6))) return true;
+  return false;
+}
+
+void WebServer::httpFail()
+{
+  P(failMsg) =
+    "HTTP/1.0 400 Bad Request" CRLF
+    WEBDUINO_SERVER_HEADER
+    "Content-Type: text/html" CRLF
+    CRLF
+    WEBDUINO_FAIL_MESSAGE;
+
+  printP(failMsg);
+}
+
+void WebServer::defaultFailCmd(WebServer &server,
+                               WebServer::ConnectionType type,
+                               char *url_tail,
+                               bool tail_complete)
+{
+  server.httpFail();
+}
+
+void WebServer::noRobots(ConnectionType type)
+{
+  httpSuccess("text/plain");
+  if (type != HEAD)
+  {
+    P(allowNoneMsg) = "User-agent: *" CRLF "Disallow: /" CRLF;
+    printP(allowNoneMsg);
+  }
+}
+
+void WebServer::favicon(ConnectionType type)
+{
+  httpSuccess("image/x-icon","Cache-Control: max-age=31536000\r\n");
+  if (type != HEAD)
+  {
+    P(faviconIco) = WEBDUINO_FAVICON_DATA;
+    writeP(faviconIco, sizeof(faviconIco));
+  }
+}
+
+void WebServer::httpUnauthorized()
+{
+  P(failMsg) =
+    "HTTP/1.0 401 Authorization Required" CRLF
+    WEBDUINO_SERVER_HEADER
+    "Content-Type: text/html" CRLF
+    "WWW-Authenticate: Basic realm=\"" WEBDUINO_AUTH_REALM "\"" CRLF
+    CRLF
+    WEBDUINO_AUTH_MESSAGE;
+
+  printP(failMsg);
+}
+
+void WebServer::httpServerError()
+{
+  P(failMsg) =
+    "HTTP/1.0 500 Internal Server Error" CRLF
+    WEBDUINO_SERVER_HEADER
+    "Content-Type: text/html" CRLF
+    CRLF
+    WEBDUINO_SERVER_ERROR_MESSAGE;
+
+  printP(failMsg);
+}
+
+void WebServer::httpSuccess(const char *contentType,
+                            const char *extraHeaders)
+{
+  P(successMsg1) =
+    "HTTP/1.0 200 OK" CRLF
+    WEBDUINO_SERVER_HEADER
+    "Access-Control-Allow-Origin: *" CRLF
+    "Content-Type: ";
+
+  printP(successMsg1);
+  print(contentType);
+  printCRLF();
+  if (extraHeaders)
+    print(extraHeaders);
+  printCRLF();
+}
+
+void WebServer::httpSeeOther(const char *otherURL)
+{
+  P(seeOtherMsg) =
+    "HTTP/1.0 303 See Other" CRLF
+    WEBDUINO_SERVER_HEADER
+    "Location: ";
+
+  printP(seeOtherMsg);
+  print(otherURL);
+  printCRLF();
+  printCRLF();
+}
+
+int WebServer::read()
+{
+  if (m_client == NULL)
+    return -1;
+
+  if (m_pushbackDepth == 0)
+  {
+    unsigned long timeoutTime = millis() + WEBDUINO_READ_TIMEOUT_IN_MS;
+
+    while (m_client.connected())
+    {
+      // stop reading the socket early if we get to content-length
+      // characters in the POST.  This is because some clients leave
+      // the socket open because they assume HTTP keep-alive.
+      if (m_readingContent)
+      {
+        if (m_contentLength == 0)
+        {
+#if WEBDUINO_SERIAL_DEBUGGING > 1
+          Serial.println("\n*** End of content, terminating connection");
+#endif
+          return -1;
+        }
+        --m_contentLength;
+      }
+
+      int ch = m_client.read();
+
+      // if we get a character, return it, otherwise continue in while
+      // loop, checking connection status
+      if (ch != -1)
+      {
+#if WEBDUINO_SERIAL_DEBUGGING
+        if (ch == '\r')
+          Serial.print("<CR>");
+        else if (ch == '\n')
+          Serial.println("<LF>");
+        else
+          Serial.print((char)ch);
+#endif
+        return ch;
+      }
+      else
+      {
+        unsigned long now = millis();
+        if (now > timeoutTime)
+        {
+          // connection timed out, destroy client, return EOF
+#if WEBDUINO_SERIAL_DEBUGGING
+          Serial.println("*** Connection timed out");
+#endif
+          reset();
+          return -1;
+        }
+      }
+    }
+
+    // connection lost, return EOF
+#if WEBDUINO_SERIAL_DEBUGGING
+    Serial.println("*** Connection lost");
+#endif
+    return -1;
+  }
+  else
+    return m_pushback[--m_pushbackDepth];
+}
+
+void WebServer::push(int ch)
+{
+  // don't allow pushing EOF
+  if (ch == -1)
+    return;
+
+  m_pushback[m_pushbackDepth++] = ch;
+  // can't raise error here, so just replace last char over and over
+  if (m_pushbackDepth == SIZE(m_pushback))
+    m_pushbackDepth = SIZE(m_pushback) - 1;
+}
+
+void WebServer::reset()
+{
+  m_pushbackDepth = 0;
+  m_client.flush();
+  m_client.stop();
+}
+
+bool WebServer::expect(const char *str)
+{
+  const char *curr = str;
+  while (*curr != 0)
+  {
+    int ch = read();
+    if (ch != *curr++)
+    {
+      // push back ch and the characters we accepted
+      push(ch);
+      while (--curr != str)
+        push(curr[-1]);
+      return false;
+    }
+  }
+  return true;
+}
+
+bool WebServer::readInt(int &number)
+{
+  bool negate = false;
+  bool gotNumber = false;
+  int ch;
+  number = 0;
+
+  // absorb whitespace
+  do
+  {
+    ch = read();
+  } while (ch == ' ' || ch == '\t');
+
+  // check for leading minus sign
+  if (ch == '-')
+  {
+    negate = true;
+    ch = read();
+  }
+
+  // read digits to update number, exit when we find non-digit
+  while (ch >= '0' && ch <= '9')
+  {
+    gotNumber = true;
+    number = number * 10 + ch - '0';
+    ch = read();
+  }
+
+  push(ch);
+  if (negate)
+    number = -number;
+  return gotNumber;
+}
+
+void WebServer::readHeader(char *value, int valueLen)
+{
+  int ch;
+  memset(value, 0, valueLen);
+  --valueLen;
+
+  // absorb whitespace
+  do
+  {
+    ch = read();
+  } while (ch == ' ' || ch == '\t');
+
+  // read rest of line
+  do
+  {
+    if (valueLen > 1)
+    {
+      *value++=ch;
+      --valueLen;
+      ch = read();
+    }
+  } while (ch != '\r');
+  push(ch);
+}
+
+bool WebServer::readPOSTparam(char *name, int nameLen,
+                              char *value, int valueLen)
+{
+  // assume name is at current place in stream
+  int ch;
+
+  // clear out name and value so they'll be NUL terminated
+  memset(name, 0, nameLen);
+  memset(value, 0, valueLen);
+
+  // decrement length so we don't write into NUL terminator
+  --nameLen;
+  --valueLen;
+
+  while ((ch = read()) != -1)
+  {
+    if (ch == '+')
+    {
+      ch = ' ';
+    }
+    else if (ch == '=')
+    {
+      /* that's end of name, so switch to storing in value */
+      nameLen = 0;
+      continue;
+    }
+    else if (ch == '&')
+    {
+      /* that's end of pair, go away */
+      return true;
+    }
+    else if (ch == '%')
+    {
+      /* handle URL encoded characters by converting back to original form */
+      int ch1 = read();
+      int ch2 = read();
+      if (ch1 == -1 || ch2 == -1)
+        return false;
+      char hex[3] = { ch1, ch2, 0 };
+      ch = strtoul(hex, NULL, 16);
+    }
+
+    // output the new character into the appropriate buffer or drop it if
+    // there's no room in either one.  This code will malfunction in the
+    // case where the parameter name is too long to fit into the name buffer,
+    // but in that case, it will just overflow into the value buffer so
+    // there's no harm.
+    if (nameLen > 0)
+    {
+      *name++ = ch;
+      --nameLen;
+    }
+    else if (valueLen > 0)
+    {
+      *value++ = ch;
+      --valueLen;
+    }
+  }
+
+  // if we get here, we hit the end-of-file, so POST is over and there
+  // are no more parameters
+  return false;
+}
+
+/* Retrieve a parameter that was encoded as part of the URL, stored in
+ * the buffer pointed to by *tail.  tail is updated to point just past
+ * the last character read from the buffer. */
+URLPARAM_RESULT WebServer::nextURLparam(char **tail, char *name, int nameLen,
+                                        char *value, int valueLen)
+{
+  // assume name is at current place in stream
+  char ch, hex[3];
+  URLPARAM_RESULT result = URLPARAM_OK;
+  char *s = *tail;
+  bool keep_scanning = true;
+  bool need_value = true;
+
+  // clear out name and value so they'll be NUL terminated
+  memset(name, 0, nameLen);
+  memset(value, 0, valueLen);
+
+  if (*s == 0)
+    return URLPARAM_EOS;
+  // Read the keyword name
+  while (keep_scanning)
+  {
+    ch = *s++;
+    switch (ch)
+    {
+    case 0:
+      s--;  // Back up to point to terminating NUL
+      // Fall through to "stop the scan" code
+    case '&':
+      /* that's end of pair, go away */
+      keep_scanning = false;
+      need_value = false;
+      break;
+    case '+':
+      ch = ' ';
+      break;
+    case '%':
+      /* handle URL encoded characters by converting back
+       * to original form */
+      if ((hex[0] = *s++) == 0)
+      {
+        s--;        // Back up to NUL
+        keep_scanning = false;
+        need_value = false;
+      }
+      else
+      {
+        if ((hex[1] = *s++) == 0)
+        {
+          s--;  // Back up to NUL
+          keep_scanning = false;
+          need_value = false;
+        }
+        else
+        {
+          hex[2] = 0;
+          ch = strtoul(hex, NULL, 16);
+        }
+      }
+      break;
+    case '=':
+      /* that's end of name, so switch to storing in value */
+      keep_scanning = false;
+      break;
+    }
+
+
+    // check against 1 so we don't overwrite the final NUL
+    if (keep_scanning && (nameLen > 1))
+    {
+      *name++ = ch;
+      --nameLen;
+    }
+    else
+      result = URLPARAM_NAME_OFLO;
+  }
+
+  if (need_value && (*s != 0))
+  {
+    keep_scanning = true;
+    while (keep_scanning)
+    {
+      ch = *s++;
+      switch (ch)
+      {
+      case 0:
+        s--;  // Back up to point to terminating NUL
+              // Fall through to "stop the scan" code
+      case '&':
+        /* that's end of pair, go away */
+        keep_scanning = false;
+        need_value = false;
+        break;
+      case '+':
+        ch = ' ';
+        break;
+      case '%':
+        /* handle URL encoded characters by converting back to original form */
+        if ((hex[0] = *s++) == 0)
+        {
+          s--;  // Back up to NUL
+          keep_scanning = false;
+          need_value = false;
+        }
+        else
+        {
+          if ((hex[1] = *s++) == 0)
+          {
+            s--;  // Back up to NUL
+            keep_scanning = false;
+            need_value = false;
+          }
+          else
+          {
+            hex[2] = 0;
+            ch = strtoul(hex, NULL, 16);
+          }
+
+        }
+        break;
+      }
+
+
+      // check against 1 so we don't overwrite the final NUL
+      if (keep_scanning && (valueLen > 1))
+      {
+        *value++ = ch;
+        --valueLen;
+      }
+      else
+        result = (result == URLPARAM_OK) ?
+          URLPARAM_VALUE_OFLO :
+          URLPARAM_BOTH_OFLO;
+    }
+  }
+  *tail = s;
+  return result;
+}
+
+
+
+// Read and parse the first line of the request header.
+// The "command" (GET/HEAD/POST) is translated into a numeric value in type.
+// The URL is stored in request,  up to the length passed in length
+// NOTE 1: length must include one byte for the terminating NUL.
+// NOTE 2: request is NOT checked for NULL,  nor length for a value < 1.
+// Reading stops when the code encounters a space, CR, or LF.  If the HTTP
+// version was supplied by the client,  it will still be waiting in the input
+// stream when we exit.
+//
+// On return, length contains the amount of space left in request.  If it's
+// less than 0,  the URL was longer than the buffer,  and part of it had to
+// be discarded.
+
+void WebServer::getRequest(WebServer::ConnectionType &type,
+                           char *request, int *length)
+{
+  --*length; // save room for NUL
+
+  type = INVALID;
+
+  // store the HTTP method line of the request
+  if (expect("GET "))
+    type = GET;
+  else if (expect("HEAD "))
+    type = HEAD;
+  else if (expect("POST "))
+    type = POST;
+  else if (expect("PUT "))
+    type = PUT;
+  else if (expect("DELETE "))
+    type = DELETE;
+  else if (expect("PATCH "))
+    type = PATCH;
+
+  // if it doesn't start with any of those, we have an unknown method
+  // so just get out of here
+  else
+    return;
+
+  int ch;
+  while ((ch = read()) != -1)
+  {
+    // stop storing at first space or end of line
+    if (ch == ' ' || ch == '\n' || ch == '\r')
+    {
+      break;
+    }
+    if (*length > 0)
+    {
+      *request = ch;
+      ++request;
+    }
+    --*length;
+  }
+  // NUL terminate
+  *request = 0;
+}
+
+void WebServer::processHeaders()
+{
+  // look for three things: the Content-Length header, the Authorization 
+  // header, and the double-CRLF that ends the headers.
+
+  // empty the m_authCredentials before every run of this function.
+  // otherwise users who don't send an Authorization header would be treated
+  // like the last user who tried to authenticate (possibly successful)
+  m_authCredentials[0]=0;
+
+  while (1)
+  {
+    if (expect("Content-Length:"))
+    {
+      readInt(m_contentLength);
+#if WEBDUINO_SERIAL_DEBUGGING > 1
+      Serial.print("\n*** got Content-Length of ");
+      Serial.print(m_contentLength);
+      Serial.print(" ***");
+#endif
+      continue;
+    }
+
+    if (expect("Authorization:"))
+    {
+      readHeader(m_authCredentials,51);
+#if WEBDUINO_SERIAL_DEBUGGING > 1
+      Serial.print("\n*** got Authorization: of ");
+      Serial.print(m_authCredentials);
+      Serial.print(" ***");
+#endif
+      continue;
+    }
+
+    if (expect(CRLF CRLF))
+    {
+      m_readingContent = true;
+      return;
+    }
+
+    // no expect checks hit, so just absorb a character and try again
+    if (read() == -1)
+    {
+      return;
+    }
+  }
+}
+
+void WebServer::outputCheckboxOrRadio(const char *element, const char *name,
+                                      const char *val, const char *label,
+                                      bool selected)
+{
+  P(cbPart1a) = "<label><input type='";
+  P(cbPart1b) = "' name='";
+  P(cbPart2) = "' value='";
+  P(cbPart3) = "' ";
+  P(cbChecked) = "checked ";
+  P(cbPart4) = "/> ";
+  P(cbPart5) = "</label>";
+
+  printP(cbPart1a);
+  print(element);
+  printP(cbPart1b);
+  print(name);
+  printP(cbPart2);
+  print(val);
+  printP(cbPart3);
+  if (selected)
+    printP(cbChecked);
+  printP(cbPart4);
+  print(label);
+  printP(cbPart5);
+}
+
+void WebServer::checkBox(const char *name, const char *val,
+                         const char *label, bool selected)
+{
+  outputCheckboxOrRadio("checkbox", name, val, label, selected);
+}
+
+void WebServer::radioButton(const char *name, const char *val,
+                            const char *label, bool selected)
+{
+  outputCheckboxOrRadio("radio", name, val, label, selected);
+}
+
+#endif // WEBDUINO_NO_IMPLEMENTATION
+
+#endif // WEBDUINO_H_
diff --git a/libraries/WebServer/keywords.txt b/libraries/WebServer/keywords.txt
new file mode 100644
index 0000000..f72c5a9
--- /dev/null
+++ b/libraries/WebServer/keywords.txt
@@ -0,0 +1,34 @@
+WebServer	KEYWORD1
+ConnectionType	KEYWORD1
+INVALID	KEYWORD2
+GET	KEYWORD2
+HEAD	KEYWORD2
+POST	KEYWORD2
+PUT	KEYWORD2
+DELETE	KEYWORD2
+PATCH	KEYWORD2
+begin	KEYWORD2
+processConnection	KEYWORD2
+setDefaultCommand	KEYWORD2
+setFailureCommand	KEYWORD2
+addCommand	KEYWORD2
+printCRLF	KEYWORD2
+printP	KEYWORD2
+writeP	KEYWORD2
+radioButton	KEYWORD2
+checkBox	KEYWORD2
+read	KEYWORD2
+push	KEYWORD2
+expect	KEYWORD2
+readInt	KEYWORD2
+readHeader	KEYWORD2
+readPOSTparam	KEYWORD2
+nextURLparam	KEYWORD2
+checkCredentials	KEYWORD2
+httpFail	KEYWORD2
+httpUnauthorized	KEYWORD2
+httpServerError	KEYWORD2
+httpSuccess	KEYWORD2
+httpSeeOther	KEYWORD2
+write	KEYWORD2
+P	KEYWORD2
diff --git a/libraries/WebServer/readme.md b/libraries/WebServer/readme.md
new file mode 100644
index 0000000..6e4c6a1
--- /dev/null
+++ b/libraries/WebServer/readme.md
@@ -0,0 +1,103 @@
+# Webduino
+
+This is an Arduino-based Web Server library, originally developed for a class at NYC Resistor. It's called Webduino, and it's an extensible web server library for the Arduino using the Wiznet-based Ethernet shields. It's released under the MIT license allowing all sorts of reuse.
+
+## Features
+
+- URL parameter parsing
+- Handle the following HTTP Methods: GET, HEAD, POST, PUT, DELETE, PATCH
+- Web Forms
+- Images
+- JSON/RESTful interface
+- HTTP Basic Authentication
+
+## Installation Notes
+
+With Arduino 1.0, add the Webduino folder to the "libraries" folder of your sketchbook directory.
+
+You can put the examples in your own sketchbook directory, or in hardware/libraries/webduino/examples, as you prefer.
+
+If you get an error message when building the examples similar to "WebServer.h not found", it's a problem with where you put the Webduino folder. The server won't work if the header is directly in the libraries folder.
+
+## Presentation
+
+[Wedbuino Presentation on Google Docs](http://docs.google.com/present/view?id=dd8gqxt8_5c8w9qfg3)
+
+## Compatible Ethernet Shields
+
+These have all been tested with the library successfully:
+
+- [Freetronics Etherten](http://www.freetronics.com/products/etherten)
+- [Freetronics Ethernet Shield](http://www.freetronics.com/products/ethernet-shield-with-poe)
+- [Arduino Ethernet](http://arduino.cc/en/Main/ArduinoBoardEthernet)
+- [Arduino Ethernet Shield, both original and updated microSD version](http://arduino.cc/en/Main/ArduinoEthernetShield)
+- [Adafruit Ethernet Shield w/ Wiznet 811MJ module](http://www.ladyada.net/make/eshield/)
+- [NKC Electronics Ethernet Shield DIY Kit](http://store.nkcelectronics.com/nkc-ethernet-shield-diy-kit-without-wiz812mj-mod812.html)
+
+Shields using the Microchip ENC28J60 chip won't work with the library as that requires more software support for implementating 
+the TCP/IP stack.
+
+## Version history
+
+### 1.7 released in Jan 2012
+
+- fixed Google Code issue [4](http://code.google.com/p/webduino/issues/detail?id=4) where expect fails with high-ASCII characters due to sign issues
+- fixed Google Code issue [8](http://code.google.com/p/webduino/issues/detail?id=8) by adding WEBDUINO_NO_IMPLEMENTATION macro that allows including the class definition without the implementation code
+- fixed Google Code issue [9](http://code.google.com/p/webduino/issues/detail?id=9): allowing prog_char* strings for printP
+- added httpServerError() method to output 500 Internal Server Error message
+- added support for HTTP PUT, DELETE, and PATCH methods (see Google Code issue [11](http://code.google.com/p/webduino/issues/detail?id=11)
+- fixed Google Code issue [12](http://code.google.com/p/webduino/issues/detail?id=12): off-by-one error in name/value parser (readPOSTparam) where the buffer wouldn't ever be completely filled
+- updated copyright string for 2012 and major authors
+- GitHub fork now the official version; all open issues on Google Code site fixed or closed and moved to GitHub
+
+### 1.6 released in Jan 2012
+
+- added [checkCredentials](http://ten-fingers-and-a-brain.com/arduino-projects/webduino/checkcredentials/) and [httpUnauthorized](http://ten-fingers-and-a-brain.com/arduino-projects/webduino/httpunauthorized/) methods as well as readHeader method for HTTP Basic Authentication; currently users will have to do the Base64 encoding outside of Webduino and I'm uncertain whether I ever want this inside the library or not...
+- fixed the request parser: previously the command dispatcher would always pass true for tail_complete, even if the tail was incomplete
+- fixed the command dispatcher: previously the default command could not have a tail, but the EPIC FAIL was returned
+
+### 1.5 released in Dec 2011
+
+- added a default favicon.ico based on the led.png from the Image example to save resources on Firefox trying to load this file on every request if it can't be loaded
+- added keywords.txt for syntax highlighting in Arduino IDE
+- bumped the version number up in response headers and compiler variables
+- added version history to readme
+- fixed default value for prefix
+- fixed /index.html in Hello World example
+
+### releases between Jul 2011 and Dec 2011
+
+- project forked on GitHub by Chris Lee
+- JSON/RESTful interface
+- header Access-Control-Allow-Origin added
+- code split in .h and .cpp files
+- Arduino 1.0 changes by Dave Falkenburg and others
+- adding CRLF after extraHeaders
+
+### 1.4.1 released in Nov 2009
+
+- fix examples to use readPOSTparam method
+
+### 1.4 released in Sep 2009
+
+- bug fix for multple connections
+
+### 1.3.1 released in Aug 2009
+
+- major bug fix for 1.3 for posts where Content-Length is last header sent
+
+### 1.3 released in Aug 2009
+
+- mostly robustness fixes (beta)
+
+### 1.2.1 released in Aug 2009
+
+- fixed HelloWorld example
+
+### 1.2 released in Jul 2009
+
+- now with GET parameter handling
+
+### 1.1 released in Apr 2009
+
+### 1.0 released in Mar 2009
-- 
cgit v1.2.3