HTTP2.c

Go to the documentation of this file.
00001 // ////////////////////////////////////////////////////////////////////////////
00002 // ////////////////////////////////////////////////////////////////////////////
00026 /**********************************************************************
00027  * Software License Agreement
00028  *
00029  * Copyright (C) 2002-2008 Microchip Technology Inc.  All rights 
00030  * reserved.
00031  *
00032  * Microchip licenses to you the right to use, modify, copy, and 
00033  * distribute: 
00034  * (i)  the Software when embedded on a Microchip microcontroller or 
00035  *      digital signal controller product ("Device") which is 
00036  *      integrated into Licensee's product; or
00037  * (ii) ONLY the Software driver source files ENC28J60.c and 
00038  *      ENC28J60.h ported to a non-Microchip device used in 
00039  *      conjunction with a Microchip ethernet controller for the 
00040  *      sole purpose of interfacing with the ethernet controller. 
00041  *
00042  * You should refer to the license agreement accompanying this 
00043  * Software for additional information regarding your rights and 
00044  * obligations.
00045  *
00046  * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT 
00047  * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT 
00048  * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A 
00049  * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL 
00050  * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR 
00051  * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF 
00052  * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS 
00053  * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE 
00054  * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER 
00055  * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT 
00056  * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
00057  *
00058  *
00059  * Author               Date        Comment
00060  *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00061  * Nilesh Rajbharti     8/14/01     Original
00062  * Elliott Wood         6/4/07      Complete rewrite, known as HTTP2
00063  ********************************************************************/
00064 
00065 #define __HTTP2_C
00066 
00067 #include "TCPIP.h"
00068 
00069 // MODIFIX: Added the web.h and stringtable.h file for parameter defines.
00070 #include "web.h"
00071 #include "stringtable.h"
00072 #include "HTTPPrint.h"
00073 
00074 #if defined(STACK_USE_HTTP2_SERVER)
00075 
00076 /****************************************************************************
00077   Section:
00078     String Constants
00079   ***************************************************************************/
00080     static ROM BYTE HTTP_CRLF[] = "\r\n";   // New line sequence
00081     #define HTTP_CRLF_LEN   2               // Length of above string
00082         
00083 /****************************************************************************
00084   Section:
00085     File and Content Type Settings
00086   ***************************************************************************/
00087     // File type extensions corresponding to HTTP_FILE_TYPE
00088     static ROM char *httpFileExtensions[HTTP_UNKNOWN+1] =
00089     {
00090         "txt",          // HTTP_TXT
00091         "htm",          // HTTP_HTM
00092         "html",         // HTTP_HTML
00093         "cgi",          // HTTP_CGI
00094         "xml",          // HTTP_XML
00095         "css",          // HTTP_CSS
00096         "gif",          // HTTP_GIF
00097         "png",          // HTTP_PNG
00098         "jpg",          // HTTP_JPG
00099         "cla",          // HTTP_JAVA
00100         "wav",          // HTTP_WAV
00101         "\0\0\0"        // HTTP_UNKNOWN
00102     };
00103     
00104     // Content-type strings corresponding to HTTP_FILE_TYPE
00105     static ROM char *httpContentTypes[HTTP_UNKNOWN+1] =
00106     {
00107         "text/plain",            // HTTP_TXT
00108         "text/html",             // HTTP_HTM
00109         "text/html",             // HTTP_HTML
00110         "text/html",             // HTTP_CGI
00111         "text/xml",              // HTTP_XML
00112         "text/css",              // HTTP_CSS
00113         "image/gif",             // HTTP_GIF
00114         "image/png",             // HTTP_PNG
00115         "image/jpeg",            // HTTP_JPG
00116         "application/java-vm",   // HTTP_JAVA
00117         "audio/x-wave",          // HTTP_WAV
00118         ""                       // HTTP_UNKNOWN
00119     };
00120         
00121 /****************************************************************************
00122   Section:
00123     Commands and Server Responses
00124   ***************************************************************************/
00125 
00126     // Initial response strings (Corresponding to HTTP_STATUS)
00127     static ROM char *HTTPResponseHeaders[] =
00128     {
00129         "HTTP/1.1 200 OK\r\nConnection: close\r\n",
00130         "HTTP/1.1 200 OK\r\nConnection: close\r\n",
00131         "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"Protected\"\r\nConnection: close\r\n\r\n401 Unauthorized: Password required\r\n",
00132         #if defined(HTTP_MPFS_UPLOAD)
00133         "HTTP/1.1 404 Not found\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n404: File not found<br>Use <a href=\"/" HTTP_MPFS_UPLOAD "\">MPFS Upload</a> to program web pages\r\n",
00134         #else       
00135         "HTTP/1.1 404 Not found\r\nConnection: close\r\n\r\n404: File not found\r\n",
00136         #endif
00137         "HTTP/1.1 414 Request-URI Too Long\r\nConnection: close\r\n\r\n414 Request-URI Too Long: Buffer overflow detected\r\n",
00138         "HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n500 Internal Server Error: Expected data not present\r\n",
00139         "HTTP/1.1 501 Not Implemented\r\nConnection: close\r\n\r\n501 Not Implemented: Only GET and POST supported\r\n",
00140         #if defined(HTTP_MPFS_UPLOAD)
00141         "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n<html><body style=\"margin:100px\"><form method=post action=\"/" HTTP_MPFS_UPLOAD "\" enctype=\"multipart/form-data\"><b>MPFS Image Upload</b><p><input type=file name=i size=40> &nbsp; <input type=submit value=\"Upload\"></form></body></html>",
00142         "",
00143         "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n<html><body style=\"margin:100px\"><b>MPFS Update Successful</b><p><a href=\"/\">Site main page</a></body></html>",
00144         "HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n<html><body style=\"margin:100px\"><b>MPFS Image Corrupt or Wrong Version</b><p><a href=\"/" HTTP_MPFS_UPLOAD "\">Try again?</a></body></html>",
00145         #endif
00146         "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: ",
00147         "HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n403 Forbidden: SSL Required - use HTTPS\r\n"
00148     };
00149     
00150 /****************************************************************************
00151   Section:
00152     Header Parsing Configuration
00153   ***************************************************************************/
00154     #define HTTP_NUM_HEADERS        3
00155     
00156     // Header strings for which we'd like to parse
00157     static ROM char *HTTPRequestHeaders[HTTP_NUM_HEADERS] =
00158     {
00159         "Cookie:",
00160         "Authorization:",
00161         "Content-Length:"
00162     };
00163     
00164     // Set to length of longest string above
00165     #define HTTP_MAX_HEADER_LEN     (15u)
00166 
00167 /****************************************************************************
00168   Section:
00169     HTTP Connection State Global Variables
00170   ***************************************************************************/
00171     #pragma udata HTTP_CONNECTION_STATES
00172     HTTP_CONN curHTTP;                          // Current HTTP connection state
00173     HTTP_STUB httpStubs[MAX_HTTP_CONNECTIONS];  // HTTP stubs with state machine and socket
00174     BYTE curHTTPID;                             // ID of the currently loaded HTTP_CONN
00175     #pragma udata
00176 
00177 /****************************************************************************
00178   Section:
00179     Function Prototypes
00180   ***************************************************************************/
00181     static void HTTPHeaderParseLookup(BYTE i);
00182     #if defined(HTTP_USE_COOKIES)
00183     static void HTTPHeaderParseCookie(void);
00184     #endif
00185     #if defined(HTTP_USE_AUTHENTICATION)
00186     static void HTTPHeaderParseAuthorization(void);
00187     #endif
00188     #if defined(HTTP_USE_POST)
00189     static void HTTPHeaderParseContentLength(void);
00190     static HTTP_READ_STATUS HTTPReadTo(BYTE delim, BYTE* buf, WORD len);
00191     #endif
00192     
00193     static void HTTPProcess(void);
00194     static BOOL HTTPSendFile(void);
00195     static void HTTPLoadConn(BYTE hHTTP);
00196 
00197     #if defined(HTTP_MPFS_UPLOAD)
00198     static HTTP_IO_RESULT HTTPMPFSUpload(void);
00199     #endif
00200 
00201     #define mMIN(a, b)  ((a<b)?a:b)
00202 
00203 /*****************************************************************************
00204   Function:
00205     void HTTPInit(void)
00206 
00207   Summary:
00208     Initializes the HTTP server module.
00209 
00210   Description:
00211     Sets all HTTP sockets to the listening state, and initializes the
00212     state machine and file handles for each connection.  If SSL is
00213     enabled, opens a socket on that port as well.
00214 
00215   Precondition:
00216     TCP must already be initialized.
00217 
00218   Parameters:
00219     None
00220 
00221   Returns:
00222     None
00223     
00224   Remarks:
00225     This function is called only one during lifetime of the application.
00226   ***************************************************************************/
00227 void HTTPInit(void)
00228 {
00229     WORD oldPtr;
00230 
00231     // Make sure the file handles are invalidated
00232     curHTTP.file = MPFS_INVALID_HANDLE;
00233     curHTTP.offsets = MPFS_INVALID_HANDLE;
00234         
00235     for(curHTTPID = 0; curHTTPID < MAX_HTTP_CONNECTIONS; curHTTPID++)
00236     {
00237         smHTTP = SM_HTTP_IDLE;
00238         sktHTTP = TCPOpen(0, TCP_OPEN_SERVER, HTTP_PORT, TCP_PURPOSE_HTTP_SERVER);
00239         #if defined(STACK_USE_SSL_SERVER)
00240         TCPAddSSLListener(sktHTTP, HTTPS_PORT);
00241         #endif
00242         
00243         // Save the default record (just invalid file handles)
00244         oldPtr = MACSetWritePtr(BASE_HTTPB_ADDR + curHTTPID*sizeof(HTTP_CONN));
00245         MACPutArray((BYTE*)&curHTTP, sizeof(HTTP_CONN));
00246         MACSetWritePtr(oldPtr);
00247     }
00248 }
00249 
00250 
00251 /*****************************************************************************
00252   Function:
00253     void HTTPServer(void)
00254 
00255   Summary:
00256     Performs periodic tasks for the HTTP2 module.
00257 
00258   Description:
00259     Browses through each open connection and attempts to process any
00260     pending operations.
00261 
00262   Precondition:
00263     HTTPInit() must already be called.
00264 
00265   Parameters:
00266     None
00267 
00268   Returns:
00269     None
00270     
00271   Remarks:
00272     This function acts as a task (similar to one in an RTOS).  It
00273     performs its task in a co-operative manner, and the main application
00274     must call this function repeatedly to ensure that all open or new
00275     connections are served in a timely fashion.
00276   ***************************************************************************/
00277 void HTTPServer(void)
00278 {
00279     BYTE conn;
00280 
00281     for(conn = 0; conn < MAX_HTTP_CONNECTIONS; conn++)
00282     {
00283         if(httpStubs[conn].socket == INVALID_SOCKET)
00284             continue;
00285         
00286         // If a socket is disconnected at any time 
00287         // forget about it and return to idle state.
00288         // Must do this here, otherwise we will wait until a new
00289         // connection arrives, which causes problems with Linux and with SSL
00290         if(TCPWasReset(httpStubs[conn].socket))
00291         {
00292             HTTPLoadConn(conn);
00293             smHTTP = SM_HTTP_IDLE;
00294 
00295             // Make sure any opened files are closed
00296             if(curHTTP.file != MPFS_INVALID_HANDLE)
00297             {
00298                 MPFSClose(curHTTP.file);
00299                 curHTTP.file = MPFS_INVALID_HANDLE;
00300             }
00301             if(curHTTP.offsets != MPFS_INVALID_HANDLE)
00302             {
00303                 MPFSClose(curHTTP.offsets);
00304                 curHTTP.offsets = MPFS_INVALID_HANDLE;
00305             }
00306 
00307             // Adjust FIFO sizes to half and half.  Default state must remain
00308             // here so that SSL handshakes, if required, can proceed
00309             TCPAdjustFIFOSize(sktHTTP, 1, 0, TCP_ADJUST_PRESERVE_RX);
00310         }
00311         
00312         // Determine if this connection is eligible for processing
00313         if(httpStubs[conn].sm != SM_HTTP_IDLE || TCPIsGetReady(httpStubs[conn].socket))
00314         {
00315             HTTPLoadConn(conn);
00316             HTTPProcess();
00317         }
00318     }
00319 }
00320 
00321 /*****************************************************************************
00322   Function:
00323     static void HTTPLoadConn(BYTE hHTTP)
00324 
00325   Summary:
00326     Switches the currently loaded connection for the HTTP2 module.
00327 
00328   Description:
00329     Saves the currently loaded HTTP connection back to Ethernet buffer
00330     RAM, then loads the selected connection into curHTTP in local RAM
00331     for processing.
00332 
00333   Precondition:
00334     None
00335 
00336   Parameters:
00337     hHTTP - the connection ID to load
00338 
00339   Returns:
00340     None
00341   ***************************************************************************/
00342 static void HTTPLoadConn(BYTE hHTTP)
00343 {
00344     WORD oldPtr;
00345     
00346     // Return if already loaded
00347     if(hHTTP == curHTTPID)
00348         return;
00349     
00350     // Save the old one
00351     oldPtr = MACSetWritePtr(BASE_HTTPB_ADDR + curHTTPID*sizeof(HTTP_CONN));
00352     MACPutArray((BYTE*)&curHTTP, sizeof(HTTP_CONN));
00353     MACSetWritePtr(oldPtr);
00354     
00355     // Load the new one
00356     oldPtr = MACSetReadPtr(BASE_HTTPB_ADDR + hHTTP*sizeof(HTTP_CONN));
00357     MACGetArray((BYTE*)&curHTTP, sizeof(HTTP_CONN));
00358     MACSetReadPtr(oldPtr);
00359     
00360     // Remember which one is loaded
00361     curHTTPID = hHTTP;
00362             
00363 }
00364 
00365 /*****************************************************************************
00366   Function:
00367     static void HTTPProcess(void)
00368 
00369   Description:
00370     Performs any pending operations for the currently loaded HTTP connection.
00371 
00372   Precondition:
00373     HTTPInit() and HTTPLoadConn() have been called.
00374 
00375   Parameters:
00376     None
00377 
00378   Returns:
00379     None
00380   ***************************************************************************/
00381 static void HTTPProcess(void)
00382 {
00383     WORD lenA, lenB;
00384     BYTE c, i;
00385     BOOL isDone;
00386     BYTE *ext;
00387     BYTE buffer[HTTP_MAX_HEADER_LEN+1];
00388 
00389     do
00390     {
00391         isDone = TRUE;
00392 
00393         switch(smHTTP)
00394         {
00395 
00396         case SM_HTTP_IDLE:
00397 
00398             // Check how much data is waiting
00399             lenA = TCPIsGetReady(sktHTTP);
00400 
00401             // If a connection has been made, then process the request
00402             if(lenA)
00403             {// Clear out state info and move to next state
00404                 curHTTP.ptrData = curHTTP.data;
00405                 smHTTP = SM_HTTP_PARSE_REQUEST;
00406                 curHTTP.isAuthorized = 0xff;
00407                 curHTTP.hasArgs = FALSE;
00408                 curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND;
00409                 curHTTP.callbackPos = 0xffffffff;
00410                 curHTTP.byteCount = 0;
00411                 #if defined(HTTP_USE_POST)
00412                 curHTTP.smPost = 0x00;
00413                 #endif
00414                 
00415                 // Adjust the TCP FIFOs for optimal reception of 
00416                 // the next HTTP request from the browser
00417                 TCPAdjustFIFOSize(sktHTTP, 1, 0, TCP_ADJUST_PRESERVE_RX | TCP_ADJUST_GIVE_REST_TO_RX);
00418             }
00419             else
00420                 // Don't break for new connections.  There may be 
00421                 // an entire request in the buffer already.
00422                 break;
00423 
00424         case SM_HTTP_PARSE_REQUEST:
00425 
00426             // Verify the entire first line is in the FIFO
00427             if(TCPFind(sktHTTP, '\n', 0, FALSE) == 0xffff)
00428             {// First line isn't here yet
00429                 if(TCPGetRxFIFOFree(sktHTTP) == 0)
00430                 {// If the FIFO is full, we overflowed
00431                     curHTTP.httpStatus = HTTP_OVERFLOW;
00432                     smHTTP = SM_HTTP_SERVE_HEADERS;
00433                     isDone = FALSE;
00434                 }
00435                 if((LONG)(TickGet() - curHTTP.callbackID) > (LONG)0)
00436                 {// A timeout has occurred
00437                     TCPDisconnect(sktHTTP);
00438                     smHTTP = SM_HTTP_DISCONNECT;
00439                     isDone = FALSE;
00440                 }
00441                 break;
00442             }
00443 
00444             // Reset the watchdog timer
00445             curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND;
00446 
00447             // Determine the request method
00448             lenA = TCPFind(sktHTTP, ' ', 0, FALSE);
00449             if(lenA > 5)
00450                 lenA = 5;
00451             TCPGetArray(sktHTTP, curHTTP.data, lenA+1);
00452 
00453             if ( memcmppgm2ram(curHTTP.data, (ROM void*)"GET", 3) == 0)
00454                 curHTTP.httpStatus = HTTP_GET;
00455             #if defined(HTTP_USE_POST)
00456             else if ( memcmppgm2ram(curHTTP.data, (ROM void*)"POST", 4) == 0)
00457                 curHTTP.httpStatus = HTTP_POST;
00458             #endif
00459             else
00460             {// Unrecognized method, so return not implemented
00461                 curHTTP.httpStatus = HTTP_NOT_IMPLEMENTED;
00462                 smHTTP = SM_HTTP_SERVE_HEADERS;
00463                 isDone = FALSE;
00464                 break;
00465             }
00466 
00467             // Find end of filename
00468             lenA = TCPFind(sktHTTP, ' ', 0, FALSE);
00469             lenB = TCPFindEx(sktHTTP, '?', 0, lenA, FALSE);
00470             lenA = mMIN(lenA, lenB);
00471             
00472             // If the file name is too long, then reject the request
00473             if(lenA > HTTP_MAX_DATA_LEN - HTTP_DEFAULT_LEN - 1)
00474             {
00475                 curHTTP.httpStatus = HTTP_OVERFLOW;
00476                 smHTTP = SM_HTTP_SERVE_HEADERS;
00477                 isDone = FALSE;
00478                 break;
00479             }
00480 
00481             // Read in the filename and decode
00482             lenB = TCPGetArray(sktHTTP, curHTTP.data, lenA);
00483             curHTTP.data[lenB] = '\0';
00484             HTTPURLDecode(curHTTP.data);
00485             
00486             // Check if this is an MPFS Upload
00487             #if defined(HTTP_MPFS_UPLOAD)
00488             if(memcmppgm2ram(&curHTTP.data[1], HTTP_MPFS_UPLOAD, strlenpgm(HTTP_MPFS_UPLOAD)) == 0)
00489             {// Read remainder of line, and bypass all file opening, etc.
00490                 #if defined(HTTP_USE_AUTHENTICATION)
00491                 curHTTP.isAuthorized = HTTPNeedsAuth(&curHTTP.data[1]);
00492                 #endif
00493                 if(curHTTP.httpStatus == HTTP_GET)
00494                     curHTTP.httpStatus = HTTP_MPFS_FORM;
00495                 else
00496                     curHTTP.httpStatus = HTTP_MPFS_UP;
00497 
00498                 smHTTP = SM_HTTP_PARSE_HEADERS;
00499                 isDone = FALSE;
00500                 break;
00501             }
00502             #endif
00503 
00504             // If the last character is a not a directory delimiter, then try to open the file
00505             // String starts at 2nd character, because the first is always a '/'
00506             if(curHTTP.data[lenB-1] != '/')
00507                 curHTTP.file = MPFSOpen(&curHTTP.data[1]);
00508                 
00509             // If the open fails, then add our default name and try again
00510             if(curHTTP.file == MPFS_INVALID_HANDLE)
00511             {
00512                 // Add the directory delimiter if needed
00513                 if(curHTTP.data[lenB-1] != '/')
00514                     curHTTP.data[lenB++] = '/';
00515                 
00516                 // Add our default file name            
00517                 #if defined(STACK_USE_SSL_SERVER)
00518                 if(TCPIsSSL(sktHTTP))
00519                 {
00520                     strcpypgm2ram((void*)&curHTTP.data[lenB], HTTPS_DEFAULT_FILE);
00521                     lenB += strlenpgm(HTTPS_DEFAULT_FILE);
00522                 }
00523                 else
00524                 #endif
00525                 {
00526                     strcpypgm2ram((void*)&curHTTP.data[lenB], HTTP_DEFAULT_FILE);
00527                     lenB += strlenpgm(HTTP_DEFAULT_FILE);
00528                 }
00529                     
00530                 // Try to open again
00531                 curHTTP.file = MPFSOpen(&curHTTP.data[1]);
00532             }
00533             
00534             // Find the extension in the filename
00535             for(ext = curHTTP.data + lenB-1; ext != curHTTP.data; ext--)
00536                 if(*ext == '.')
00537                     break;
00538                     
00539             // Compare to known extensions to determine Content-Type
00540             ext++;
00541             for(curHTTP.fileType = HTTP_TXT; curHTTP.fileType < HTTP_UNKNOWN; curHTTP.fileType++)
00542                 if(!stricmppgm2ram(ext, (ROM void*)httpFileExtensions[curHTTP.fileType]))
00543                     break;
00544             
00545             // Perform first round authentication (pass file name only)
00546             #if defined(HTTP_USE_AUTHENTICATION)
00547             curHTTP.isAuthorized = HTTPNeedsAuth(&curHTTP.data[1]);
00548             #endif
00549             
00550             // If the file was found, see if it has an index
00551             if(curHTTP.file != MPFS_INVALID_HANDLE &&
00552                 (MPFSGetFlags(curHTTP.file) & MPFS2_FLAG_HASINDEX) )
00553             {
00554                 curHTTP.offsets = MPFSOpenID(MPFSGetID(curHTTP.file) + 1);
00555             }
00556 
00557             // Read GET args, up to buffer size - 1
00558             lenA = TCPFind(sktHTTP, ' ', 0, FALSE);
00559             if(lenA != 0)
00560             {
00561                 curHTTP.hasArgs = TRUE;
00562                 
00563                 // Trash the '?'
00564                 TCPGet(sktHTTP, &c);
00565 
00566                 // Verify there's enough space
00567                 lenA--;
00568                 if(lenA >= HTTP_MAX_DATA_LEN - 2)
00569                 {
00570                     curHTTP.httpStatus = HTTP_OVERFLOW;
00571                     smHTTP = SM_HTTP_SERVE_HEADERS;
00572                     isDone = FALSE;
00573                     break;
00574                 }
00575 
00576                 // Read in the arguments and '&'-terminate in anticipation of cookies
00577                 curHTTP.ptrData += TCPGetArray(sktHTTP, curHTTP.data, lenA);
00578                 *(curHTTP.ptrData++) = '&';
00579 
00580             }
00581 
00582             // Clear the rest of the line
00583             lenA = TCPFind(sktHTTP, '\n', 0, FALSE);
00584             TCPGetArray(sktHTTP, NULL, lenA + 1);
00585 
00586             // Move to parsing the headers
00587             smHTTP = SM_HTTP_PARSE_HEADERS;
00588             
00589             // No break, continue to parsing headers
00590 
00591         case SM_HTTP_PARSE_HEADERS:
00592 
00593             // Loop over all the headers
00594             while(1)
00595             {
00596                 // Make sure entire line is in the FIFO
00597                 lenA = TCPFind(sktHTTP, '\n', 0, FALSE);
00598                 if(lenA == 0xffff)
00599                 {// If not, make sure we can receive more data
00600                     if(TCPGetRxFIFOFree(sktHTTP) == 0)
00601                     {// Overflow
00602                         curHTTP.httpStatus = HTTP_OVERFLOW;
00603                         smHTTP = SM_HTTP_SERVE_HEADERS;
00604                         isDone = FALSE;
00605                     }
00606                     if((LONG)(TickGet() - curHTTP.callbackID) > (LONG)0)
00607                     {// A timeout has occured
00608                         TCPDisconnect(sktHTTP);
00609                         smHTTP = SM_HTTP_DISCONNECT;
00610                         isDone = FALSE;
00611                     }
00612                     break;
00613                 }
00614                 
00615                 // Reset the watchdog timer
00616                 curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND;
00617                 
00618                 // If a CRLF is immediate, then headers are done
00619                 if(lenA == 1)
00620                 {// Remove the CRLF and move to next state
00621                     TCPGetArray(sktHTTP, NULL, 2);
00622                     smHTTP = SM_HTTP_AUTHENTICATE;
00623                     isDone = FALSE;
00624                     break;
00625                 }
00626     
00627                 // Find the header name, and use isDone as a flag to indicate a match
00628                 lenB = TCPFindEx(sktHTTP, ':', 0, lenA, FALSE) + 2;
00629                 isDone = FALSE;
00630     
00631                 // If name is too long or this line isn't a header, ignore it
00632                 if(lenB > sizeof(buffer))
00633                 {
00634                     TCPGetArray(sktHTTP, NULL, lenA+1);
00635                     continue;
00636                 }
00637                 
00638                 // Read in the header name
00639                 TCPGetArray(sktHTTP, buffer, lenB);
00640                 buffer[lenB-1] = '\0';
00641                 lenA -= lenB;
00642         
00643                 // Compare header read to ones we're interested in
00644                 for(i = 0; i < HTTP_NUM_HEADERS; i++)
00645                 {
00646                     if(strcmppgm2ram((char*)buffer, (ROM char *)HTTPRequestHeaders[i]) == 0)
00647                     {// Parse the header and stop the loop
00648                         HTTPHeaderParseLookup(i);
00649                         isDone = TRUE;
00650                         break;
00651                     }
00652                 }
00653                 
00654                 // Clear the rest of the line, and call the loop again
00655                 if(isDone)
00656                 {// We already know how much to remove unless a header was found
00657                     lenA = TCPFind(sktHTTP, '\n', 0, FALSE);
00658                 }
00659                 TCPGetArray(sktHTTP, NULL, lenA+1);
00660             }
00661             
00662             break;
00663 
00664         case SM_HTTP_AUTHENTICATE:
00665         
00666             #if defined(HTTP_USE_AUTHENTICATION)
00667             // Check current authorization state
00668             if(curHTTP.isAuthorized < 0x80)
00669             {// 401 error
00670                 curHTTP.httpStatus = HTTP_UNAUTHORIZED;
00671                 smHTTP = SM_HTTP_SERVE_HEADERS;
00672                 isDone = FALSE;
00673                 
00674                 #if defined(HTTP_NO_AUTH_WITHOUT_SSL)
00675                 if(!TCPIsSSL(sktHTTP))
00676                     curHTTP.httpStatus = HTTP_SSL_REQUIRED;
00677                 #endif
00678 
00679                 break;
00680             }
00681             #endif
00682 
00683             // Parse the args string
00684             *curHTTP.ptrData = '\0';
00685             curHTTP.ptrData = HTTPURLDecode(curHTTP.data);
00686 
00687             // If this is an MPFS upload form request, bypass to headers
00688             #if defined(HTTP_MPFS_UPLOAD)
00689             if(curHTTP.httpStatus == HTTP_MPFS_FORM)
00690             {
00691                 smHTTP = SM_HTTP_SERVE_HEADERS;
00692                 isDone = FALSE;
00693                 break;
00694             }
00695             #endif
00696             
00697             // Move on to GET args, unless there are none
00698             smHTTP = SM_HTTP_PROCESS_GET;
00699             if(!curHTTP.hasArgs)
00700                 smHTTP = SM_HTTP_PROCESS_POST;
00701             isDone = FALSE;
00702             curHTTP.hasArgs = FALSE;
00703             break;
00704 
00705         case SM_HTTP_PROCESS_GET:
00706 
00707             // Run the application callback HTTPExecuteGet()
00708             if(HTTPExecuteGet() == HTTP_IO_WAITING)
00709             {// If waiting for asynchronous process, return to main app
00710                 break;
00711             }
00712 
00713             // Move on to POST data
00714             smHTTP = SM_HTTP_PROCESS_POST;
00715 
00716         case SM_HTTP_PROCESS_POST:
00717 
00718             #if defined(HTTP_USE_POST)
00719             
00720             // See if we have any new data
00721             if(TCPIsGetReady(sktHTTP) == curHTTP.callbackPos)
00722             {
00723                 if((LONG)(TickGet() - curHTTP.callbackID) > (LONG)0)
00724                 {// If a timeout has occured, disconnect
00725                     TCPDisconnect(sktHTTP);
00726                     smHTTP = SM_HTTP_DISCONNECT;
00727                     isDone = FALSE;
00728                     break;
00729                 }
00730             }
00731             
00732             if(curHTTP.httpStatus == HTTP_POST 
00733                 #if defined(HTTP_MPFS_UPLOAD)
00734                 || (curHTTP.httpStatus >= HTTP_MPFS_UP && curHTTP.httpStatus <= HTTP_MPFS_ERROR)
00735                 #endif
00736                  )
00737             {
00738                 // Run the application callback HTTPExecutePost()
00739                 #if defined(HTTP_MPFS_UPLOAD)
00740                 if(curHTTP.httpStatus >= HTTP_MPFS_UP && curHTTP.httpStatus <= HTTP_MPFS_ERROR)
00741                 {
00742                     c = HTTPMPFSUpload();
00743                     if(c == HTTP_IO_DONE)
00744                     {
00745                         smHTTP = SM_HTTP_SERVE_HEADERS;
00746                         isDone = FALSE;
00747                         break;
00748                     }
00749                 }
00750                 else
00751                 #endif
00752                 c = HTTPExecutePost();
00753                 
00754                 // If waiting for asynchronous process, return to main app
00755                 if(c == HTTP_IO_WAITING)
00756                 {// return to main app and make sure we don't get stuck by the watchdog
00757                     curHTTP.callbackPos = TCPIsGetReady(sktHTTP) - 1;
00758                     break;
00759                 }
00760                 else if(c == HTTP_IO_NEED_DATA)
00761                 {// If waiting for more data
00762                     curHTTP.callbackPos = TCPIsGetReady(sktHTTP);
00763                     curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND;
00764                     
00765                     // If more is expected and space is available, return to main app
00766                     if(curHTTP.byteCount > curHTTP.callbackPos && TCPGetRxFIFOFree(sktHTTP) != 0)
00767                         break;
00768                     
00769                     // Handle cases where application ran out of data or buffer space
00770                     curHTTP.httpStatus = HTTP_INTERNAL_SERVER_ERROR;
00771                     smHTTP = SM_HTTP_SERVE_HEADERS;
00772                     isDone = FALSE;
00773                     break;  
00774                 }
00775             }
00776             #endif
00777 
00778             // We're done with POST
00779             smHTTP = SM_HTTP_PROCESS_REQUEST;
00780             // No break, continue to sending request
00781 
00782         case SM_HTTP_PROCESS_REQUEST:
00783 
00784             // Check for 404
00785             if(curHTTP.file == MPFS_INVALID_HANDLE)
00786             {
00787                 curHTTP.httpStatus = HTTP_NOT_FOUND;
00788                 smHTTP = SM_HTTP_SERVE_HEADERS;
00789                 isDone = FALSE;
00790                 break;
00791             }
00792 
00793             // Set up the dynamic substitutions
00794             curHTTP.byteCount = 0;
00795             if(curHTTP.offsets == MPFS_INVALID_HANDLE)
00796             {// If no index file, then set next offset to huge
00797                 curHTTP.nextCallback = 0xffffffff;
00798             }
00799             else
00800             {// Read in the next callback index
00801                 MPFSGetLong(curHTTP.offsets, &(curHTTP.nextCallback));
00802             }
00803             
00804             // Move to next state
00805             smHTTP = SM_HTTP_SERVE_HEADERS;
00806 
00807         case SM_HTTP_SERVE_HEADERS:
00808 
00809             // We're in write mode now:
00810             // Adjust the TCP FIFOs for optimal transmission of 
00811             // the HTTP response to the browser
00812             TCPAdjustFIFOSize(sktHTTP, 1, 0, TCP_ADJUST_GIVE_REST_TO_TX);
00813                 
00814             // Send headers
00815             TCPPutROMString(sktHTTP, (ROM BYTE*)HTTPResponseHeaders[curHTTP.httpStatus]);
00816             
00817             // If this is a redirect, print the rest of the Location: header               
00818             if(curHTTP.httpStatus == HTTP_REDIRECT)
00819             {
00820                 TCPPutString(sktHTTP, curHTTP.data);
00821                 TCPPutROMString(sktHTTP, (ROM BYTE*)"\r\n\r\n304 Redirect: ");
00822                 TCPPutString(sktHTTP, curHTTP.data);
00823                 TCPPutROMString(sktHTTP, (ROM BYTE*)HTTP_CRLF);
00824             }
00825 
00826             // If not GET or POST, we're done
00827             if(curHTTP.httpStatus != HTTP_GET && curHTTP.httpStatus != HTTP_POST)
00828             {// Disconnect
00829                 smHTTP = SM_HTTP_DISCONNECT;
00830                 break;
00831             }
00832 
00833             // Output the content type, if known
00834             if(curHTTP.fileType != HTTP_UNKNOWN)
00835             {
00836                 TCPPutROMString(sktHTTP, (ROM BYTE*)"Content-Type: ");
00837                 TCPPutROMString(sktHTTP, (ROM BYTE*)httpContentTypes[curHTTP.fileType]);
00838                 TCPPutROMString(sktHTTP, HTTP_CRLF);
00839             }
00840             
00841             // Output the gzip encoding header if needed
00842             if(MPFSGetFlags(curHTTP.file) & MPFS2_FLAG_ISZIPPED)
00843             {
00844                 TCPPutROMString(sktHTTP, (ROM BYTE*)"Content-Encoding: gzip\r\n");
00845             }
00846                         
00847             // Output the cache-control
00848             TCPPutROMString(sktHTTP, (ROM BYTE*)"Cache-Control: ");
00849             if(curHTTP.httpStatus == HTTP_POST || curHTTP.nextCallback != 0xffffffff)
00850             {// This is a dynamic page or a POST request, so no cache
00851                 TCPPutROMString(sktHTTP, (ROM BYTE*)"no-cache");
00852             }
00853             else
00854             {// This is a static page, so save it for the specified amount of time
00855                 TCPPutROMString(sktHTTP, (ROM BYTE*)"max-age=");
00856                 TCPPutROMString(sktHTTP, (ROM BYTE*)HTTP_CACHE_LEN);
00857             }
00858             TCPPutROMString(sktHTTP, HTTP_CRLF);
00859             
00860             // Check if we should output cookies
00861             if(curHTTP.hasArgs)
00862                 smHTTP = SM_HTTP_SERVE_COOKIES;
00863             else
00864             {// Terminate the headers
00865                 TCPPutROMString(sktHTTP, HTTP_CRLF);
00866                 smHTTP = SM_HTTP_SERVE_BODY;
00867             }
00868     
00869             // Move to next stage
00870             isDone = FALSE;
00871             break;
00872 
00873         case SM_HTTP_SERVE_COOKIES:
00874 
00875             #if defined(HTTP_USE_COOKIES)
00876             // If the TX FIFO runs out of space, the client will never get CRLFCRLF
00877             // Avoid writing huge cookies - keep it under a hundred bytes max
00878 
00879             // Write cookies one at a time as space permits
00880             for(curHTTP.ptrRead = curHTTP.data; curHTTP.hasArgs != 0; curHTTP.hasArgs--)
00881             {
00882                 // Write the header
00883                 TCPPutROMString(sktHTTP, (ROM BYTE*)"Set-Cookie: ");
00884 
00885                 // Write the name, URL encoded, one character at a time
00886                 while((c = *(curHTTP.ptrRead++)))
00887                 {
00888                     if(c == ' ')
00889                         TCPPut(sktHTTP, '+');
00890                     else if(c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z')
00891                     {
00892                         TCPPut(sktHTTP, '%');
00893                         TCPPut(sktHTTP, btohexa_high(c));
00894                         TCPPut(sktHTTP, btohexa_low(c));
00895                     }
00896                     else
00897                         TCPPut(sktHTTP, c);
00898                 }
00899                 
00900                 TCPPut(sktHTTP, '=');
00901                 
00902                 // Write the value, URL encoded, one character at a time
00903                 while((c = *(curHTTP.ptrRead++)))
00904                 {
00905                     if(c == ' ')
00906                         TCPPut(sktHTTP, '+');
00907                     else if(c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z')
00908                     {
00909                         TCPPut(sktHTTP, '%');
00910                         TCPPut(sktHTTP, btohexa_high(c));
00911                         TCPPut(sktHTTP, btohexa_low(c));
00912                     }
00913                     else
00914                         TCPPut(sktHTTP, c);
00915                 }
00916                 
00917                 // Finish the line
00918                 TCPPutROMString(sktHTTP, HTTP_CRLF);
00919 
00920             }
00921             #endif
00922 
00923             // We're done, move to next state
00924             TCPPutROMString(sktHTTP, HTTP_CRLF);
00925             smHTTP = SM_HTTP_SERVE_BODY;
00926 
00927         case SM_HTTP_SERVE_BODY:
00928 
00929             isDone = FALSE;
00930 
00931             // Try to send next packet
00932             if(HTTPSendFile())
00933             {// If EOF, then we're done so close and disconnect
00934                 MPFSClose(curHTTP.file);
00935                 curHTTP.file = MPFS_INVALID_HANDLE;
00936                 smHTTP = SM_HTTP_DISCONNECT;
00937                 isDone = TRUE;
00938             }
00939             
00940             // If the TX FIFO is full, then return to main app loop
00941             if(TCPIsPutReady(sktHTTP) == 0)
00942                 isDone = TRUE;
00943             break;
00944 
00945         case SM_HTTP_SEND_FROM_CALLBACK:
00946 
00947             isDone = TRUE;
00948 
00949             // Check that at least the minimum bytes are free
00950             if(TCPIsPutReady(sktHTTP) < HTTP_MIN_CALLBACK_FREE)
00951                 break;
00952 
00953             // Fill TX FIFO from callback
00954             HTTPPrint(curHTTP.callbackID);
00955             
00956             if(curHTTP.callbackPos == 0)
00957             {// Callback finished its output, so move on
00958                 isDone = FALSE;
00959                 smHTTP = SM_HTTP_SERVE_BODY;
00960             }// Otherwise, callback needs more buffer space, so return and wait
00961             
00962             break;
00963 
00964         case SM_HTTP_DISCONNECT:
00965             // Make sure any opened files are closed
00966             if(curHTTP.file != MPFS_INVALID_HANDLE)
00967             {
00968                 MPFSClose(curHTTP.file);
00969                 curHTTP.file = MPFS_INVALID_HANDLE;
00970             }
00971             if(curHTTP.offsets != MPFS_INVALID_HANDLE)
00972             {
00973                 MPFSClose(curHTTP.offsets);
00974                 curHTTP.offsets = MPFS_INVALID_HANDLE;
00975             }
00976 
00977             TCPDisconnect(sktHTTP);
00978             smHTTP = SM_HTTP_IDLE;
00979             break;
00980         }
00981     } while(!isDone);
00982 
00983 }
00984 
00985 
00986 /*****************************************************************************
00987   Function:
00988     static BOOL HTTPSendFile(void)
00989 
00990   Description:
00991     Serves up the next chunk of curHTTP's file, up to a) available TX FIFO
00992     space or b) the next callback index, whichever comes first.
00993 
00994   Precondition:
00995     curHTTP.file and curHTTP.offsets have both been opened for reading.
00996 
00997   Parameters:
00998     None
00999 
01000   Return Values:
01001     TRUE - the end of the file was reached and reading is done
01002     FALSE - more data remains to be read
01003   ***************************************************************************/
01004 static BOOL HTTPSendFile(void)
01005 {
01006     WORD numBytes, len;
01007     BYTE c, data[64];
01008     
01009     // Determine how many bytes we can read right now
01010     len = TCPIsPutReady(sktHTTP);
01011     numBytes = mMIN(len, curHTTP.nextCallback - curHTTP.byteCount);
01012     
01013     // Get/put as many bytes as possible
01014     curHTTP.byteCount += numBytes;
01015     while(numBytes > 0)
01016     {
01017         len = MPFSGetArray(curHTTP.file, data, mMIN(numBytes, 64));
01018         if(len == 0)
01019             return TRUE;
01020         else
01021             TCPPutArray(sktHTTP, data, len);
01022         numBytes -= len;
01023     }
01024     
01025     // Check if a callback index was reached
01026     if(curHTTP.byteCount == curHTTP.nextCallback)
01027     {
01028         // Update the state machine
01029         smHTTP = SM_HTTP_SEND_FROM_CALLBACK;
01030         curHTTP.callbackPos = 0;
01031 
01032         // Read past the variable name and close the MPFS
01033 /* MODIFIX: Commented out, because in the changed MPFS utility this variables
01034 are removed from the destination image to save memory space.
01035         MPFSGet(curHTTP.file, NULL);
01036         do
01037         {
01038             if(!MPFSGet(curHTTP.file, &c))
01039                 break;
01040             curHTTP.byteCount++;
01041         } while(c != '~');
01042         curHTTP.byteCount++;
01043 */      
01044         // Read in the callback address and next offset
01045         MPFSGetLong(curHTTP.offsets, &(curHTTP.callbackID));
01046         if(!MPFSGetLong(curHTTP.offsets, &(curHTTP.nextCallback)))
01047         {
01048             curHTTP.nextCallback = 0xffffffff;
01049             MPFSClose(curHTTP.offsets);
01050             curHTTP.offsets = MPFS_INVALID_HANDLE;
01051         }
01052     }
01053 
01054     // We are not done sending a file yet...
01055     return FALSE;
01056 }
01057 
01058 /*****************************************************************************
01059   Function:
01060     static void HTTPHeaderParseLookup(BYTE i)
01061 
01062   Description:
01063     Calls the appropriate header parser based on the index of the header
01064     that was read from the request.
01065 
01066   Precondition:
01067     None
01068 
01069   Parameters:
01070     i - the index of the string found in HTTPRequestHeaders
01071 
01072   Return Values:
01073     TRUE - the end of the file was reached and reading is done
01074     FALSE - more data remains to be read
01075   ***************************************************************************/
01076 static void HTTPHeaderParseLookup(BYTE i)
01077 {
01078     // i corresponds to an index in HTTPRequestHeaders
01079     
01080     #if defined(HTTP_USE_COOKIES)
01081     if(i == 0u)
01082     {
01083         HTTPHeaderParseCookie();
01084         return;
01085     }
01086     #endif
01087     
01088     #if defined(HTTP_USE_AUTHENTICATION)    
01089     if(i == 1u)
01090     {
01091         HTTPHeaderParseAuthorization();
01092         return;
01093     }
01094     #endif
01095     
01096     #if defined(HTTP_USE_POST)
01097     if(i == 2u)
01098     {
01099         HTTPHeaderParseContentLength();
01100         return;
01101     }
01102     #endif
01103 }
01104 
01105 /*****************************************************************************
01106   Function:
01107     static void HTTPHeaderParseAuthorization(void)
01108 
01109   Summary:
01110     Parses the "Authorization:" header for a request and verifies the
01111     credentials.
01112 
01113   Description:
01114     Parses the "Authorization:" header for a request.  For example, 
01115     "BASIC YWRtaW46cGFzc3dvcmQ=" is decoded to a user name of "admin" and
01116     a password of "password".  Once read, HTTPCheckAuth is called from
01117     CustomHTTPApp.c to determine if the credentials are acceptable.
01118 
01119     The return value of HTTPCheckAuth is saved in curHTTP.isAuthorized for
01120     later use by the application.
01121 
01122   Precondition:
01123     None
01124 
01125   Parameters:
01126     None
01127 
01128   Returns:
01129     None
01130 
01131   Remarks:
01132     This function is ony available when HTTP_USE_AUTHENTICATION is defined.
01133   ***************************************************************************/
01134 #if defined(HTTP_USE_AUTHENTICATION)
01135 static void HTTPHeaderParseAuthorization(void)
01136 {
01137     WORD len;
01138     BYTE buf[40];
01139     BYTE *ptrBuf;
01140     
01141     // If auth processing is not required, return
01142     if(curHTTP.isAuthorized & 0x80)
01143         return;
01144 
01145     // Clear the auth type ("BASIC ")
01146     TCPGetArray(sktHTTP, NULL, 6);
01147 
01148     // Find the terminating CRLF and make sure it's a multiple of four
01149     len = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE);
01150     len += 3;
01151     len &= 0xfc;
01152     len = mMIN(len, sizeof(buf)-4);
01153     
01154     // Read in 4 bytes at a time and decode (slower, but saves RAM)
01155     for(ptrBuf = buf; len > 0; len-=4, ptrBuf+=3)
01156     {
01157         TCPGetArray(sktHTTP, ptrBuf, 4);
01158         Base64Decode(ptrBuf, 4, ptrBuf, 3);
01159     }
01160 
01161     // Null terminate both, and make sure there's at least two terminators
01162     *ptrBuf = '\0';
01163     for(len = 0, ptrBuf = buf; len < sizeof(buf); len++, ptrBuf++)
01164         if(*ptrBuf == ':')
01165             break;
01166     *(ptrBuf++) = '\0';
01167     
01168     // Verify credentials
01169     curHTTP.isAuthorized = HTTPCheckAuth(buf, ptrBuf);
01170 
01171     return;
01172 }
01173 #endif
01174 
01175 /*****************************************************************************
01176   Function:
01177     static void HTTPHeaderParseCookie(void)
01178 
01179   Summary:
01180     Parses the "Cookie:" headers for a request and stores them as GET
01181     variables.
01182 
01183   Description:
01184     Parses the "Cookie:" headers for a request.  For example, 
01185     "Cookie: name=Wile+E.+Coyote; order=ROCKET_LAUNCHER" is decoded to 
01186     "name=Wile+E.+Coyote&order=ROCKET_LAUNCHER&" and stored as any other 
01187     GET variable in curHTTP.data.
01188 
01189     The user application can easily access these values later using the
01190     HTTPGetArg() and HTTPGetROMArg() functions.
01191 
01192   Precondition:
01193     None
01194 
01195   Parameters:
01196     None
01197 
01198   Returns:
01199     None
01200 
01201   Remarks:
01202     This function is ony available when HTTP_USE_COOKIES is defined.
01203   ***************************************************************************/
01204 #if defined(HTTP_USE_COOKIES)
01205 static void HTTPHeaderParseCookie(void)
01206 {
01207     WORD lenA, lenB;
01208 
01209     // Verify there's enough space
01210     lenB = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE);
01211     if(lenB >= curHTTP.data + HTTP_MAX_DATA_LEN - curHTTP.ptrData - 2)
01212     {// If not, overflow
01213         curHTTP.httpStatus = HTTP_OVERFLOW;
01214         smHTTP = SM_HTTP_SERVE_HEADERS;
01215         return;
01216     }
01217 
01218     // While a CRLF is not immediate, grab a cookie value
01219     while(lenB != 0)
01220     {
01221         // Look for a ';' and use the shorter of that or a CRLF
01222         lenA = TCPFind(sktHTTP, ';', 0, FALSE);
01223         
01224         // Read to the terminator
01225         curHTTP.ptrData += TCPGetArray(sktHTTP, curHTTP.ptrData, mMIN(lenA, lenB));
01226         
01227         // Insert an & to anticipate another cookie
01228         *(curHTTP.ptrData++) = '&';
01229         
01230         // If semicolon, trash it and whitespace
01231         if(lenA < lenB)
01232         {
01233             TCPGet(sktHTTP, NULL);
01234             while(TCPFind(sktHTTP, ' ', 0, FALSE) == 0)
01235                 TCPGet(sktHTTP, NULL);
01236         }
01237         
01238         // Find the new distance to the CRLF
01239         lenB = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE);
01240     }
01241 
01242     return;
01243 
01244 }
01245 #endif
01246 
01247 /*****************************************************************************
01248   Function:
01249     static void HTTPHeaderParseContentLength(void)
01250 
01251   Summary:
01252     Parses the "Content-Length:" header for a request.
01253 
01254   Description:
01255     Parses the "Content-Length:" header to determine how many bytes of
01256     POST data to expect after the request.  This value is stored in 
01257     curHTTP.byteCount.
01258 
01259   Precondition:
01260     None
01261 
01262   Parameters:
01263     None
01264 
01265   Returns:
01266     None
01267 
01268   Remarks:
01269     This function is ony available when HTTP_USE_POST is defined.
01270   ***************************************************************************/
01271 #if defined(HTTP_USE_POST)
01272 static void HTTPHeaderParseContentLength(void)
01273 {
01274     WORD len;
01275     BYTE buf[10];
01276 
01277     // Read up to the CRLF (max 9 bytes or ~1GB)
01278     len = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE);
01279     len = TCPGetArray(sktHTTP, buf, len);
01280     buf[len] = '\0';
01281     
01282     curHTTP.byteCount = atol((char*)buf);
01283     
01284     return;
01285 
01286 }
01287 #endif
01288 
01289 /*****************************************************************************
01290   Function:
01291     BYTE* HTTPURLDecode(BYTE* cData)
01292 
01293   Summary:
01294     Parses a string from URL encoding to plain-text.
01295 
01296   Description:
01297     Parses a string from URL encoding to plain-text.  The following
01298     conversions are made: ‘=’ to ‘\0’, ‘&’ to ‘\0’, ‘+’ to ‘ ‘, and
01299     “%xx” to a single hex byte.
01300  
01301     After completion, the data has been decoded and a null terminator
01302     signifies the end of a name or value.  A second null terminator (or a
01303     null name parameter) indicates the end of all the data.
01304 
01305   Precondition:
01306     The data parameter is null terminated and has at least one extra
01307     byte free.
01308 
01309   Parameters:
01310     cData - The string which is to be decoded in place.
01311 
01312   Returns:
01313     A pointer to the last null terminator in data, which is also the
01314     first free byte for new data.
01315 
01316   Remarks:
01317     This function is called by the stack to parse GET arguments and 
01318     cookie data.  User applications can use this function to decode POST
01319     data, but first need to verify that the string is null-terminated.
01320   ***************************************************************************/
01321 BYTE* HTTPURLDecode(BYTE* cData)
01322 {
01323     BYTE *pRead, *pWrite;
01324     WORD wLen;
01325     BYTE c;
01326     WORD_VAL hex;
01327      
01328     // Determine length of input
01329     wLen = strlen((char*)cData);
01330      
01331     // Read all characters in the string
01332     for(pRead = pWrite = cData; wLen != 0; )
01333     {
01334         c = *pRead++;
01335         wLen--;
01336         
01337         if(c == '=' || c == '&')
01338             *pWrite++ = '\0';
01339         else if(c == '+')
01340             *pWrite++ = ' ';
01341         else if(c == '%')
01342         {
01343             if(wLen < 2)
01344                 wLen = 0;
01345             else
01346             {
01347                 hex.v[1] = *pRead++;
01348                 hex.v[0] = *pRead++;
01349                 wLen--;
01350                 wLen--;
01351                 *pWrite++ = hexatob(hex);
01352             }
01353         }
01354         else
01355             *pWrite++ = c;
01356     }
01357     
01358     // Double null terminate the last value
01359     *pWrite++ = '\0';
01360     *pWrite = '\0';
01361     
01362     return pWrite;
01363 }
01364 
01365 /*****************************************************************************
01366   Function:
01367     BYTE* HTTPGetArg(BYTE* cData, BYTE* cArg)
01368 
01369   Summary:
01370     Locates a form field value in a given data array.
01371 
01372   Description:
01373     Searches through a data array to find the value associated with a
01374     given argument.  It can be used to find form field values in data
01375     received over GET or POST.
01376     
01377     The end of data is assumed to be reached when a null name parameter is
01378     encountered.  This requires the string to have an even number of 
01379     null-terminated strings, followed by an additional null terminator.
01380 
01381   Precondition:
01382     The data array has a valid series of null terminated name/value pairs.
01383 
01384   Parameters:
01385     data - the buffer to search
01386     arg - the name of the argument to find
01387 
01388   Returns:
01389     A pointer to the argument value, or NULL if not found.
01390   ***************************************************************************/
01391 BYTE* HTTPGetArg(BYTE* cData, BYTE* cArg)
01392 {
01393     // Search through the array while bytes remain
01394     while(*cData != '\0')
01395     { 
01396         // Look for arg at current position
01397         if(!strcmp((char*)cArg, (char*)cData))
01398         {// Found it, so return parameter
01399             return cData + strlen((char*)cArg) + 1;
01400         }
01401         
01402         // Skip past two strings (NUL bytes)
01403         cData += strlen((char*)cData) + 1;
01404         cData += strlen((char*)cData) + 1;
01405     }
01406         
01407     // Return NULL if not found
01408     return NULL;
01409 }
01410 
01411 /*****************************************************************************
01412   Function:
01413     BYTE* HTTPGetROMArg(BYTE* cData, ROM BYTE* cArg)
01414 
01415   Summary:
01416     Locates a form field value in a given data array.
01417 
01418   Description:
01419     Searches through a data array to find the value associated with a
01420     given argument.  It can be used to find form field values in data
01421     received over GET or POST.
01422     
01423     The end of data is assumed to be reached when a null name parameter is
01424     encountered.  This requires the string to have an even number of 
01425     null-terminated strings, followed by an additional null terminator.
01426 
01427   Precondition:
01428     The data array has a valid series of null terminated name/value pairs.
01429 
01430   Parameters:
01431     data - the buffer to search
01432     arg - the name of the argument to find
01433 
01434   Returns:
01435     A pointer to the argument value, or NULL if not found.
01436 
01437   Remarks:
01438     This function is aliased to HTTPGetArg on non-PIC18 platforms.
01439   ***************************************************************************/
01440 #if defined(__18CXX)
01441 BYTE* HTTPGetROMArg(BYTE* cData, ROM BYTE* cArg)
01442 {
01443     // Search through the array while bytes remain
01444     while(*cData != '\0')
01445     {
01446         // Look for arg at current position
01447         if(!memcmppgm2ram(cData, (ROM void*)cArg, strlenpgm((ROM char*)cArg) + 1))
01448         {// Found it, so skip to next string
01449             return cData + strlenpgm((ROM char*)cArg) + 1;
01450         }
01451         
01452         // Skip past two strings (NUL bytes)
01453         cData += strlen((char*)cData) + 1;
01454         cData += strlen((char*)cData) + 1;
01455     }
01456         
01457     // Return NULL if not found
01458     return NULL;
01459 }
01460 #endif
01461 
01462 /*****************************************************************************
01463   Function:
01464     HTTP_READ_STATUS HTTPReadPostName(BYTE* cData, WORD wLen)
01465 
01466   Summary:
01467     Reads a name from a URL encoded string in the TCP buffer.
01468 
01469   Description:
01470     Reads a name from a URL encoded string in the TCP buffer.  This function
01471     is meant to be called from an HTTPExecutePost callback to facilitate
01472     easier parsing of incoming data.  This function also prevents buffer
01473     overflows by forcing the programmer to indicate how many bytes are
01474     expected.  At least 2 extra bytes are needed in cData over the maximum
01475     length of data expected to be read.
01476     
01477     This function will read until the next '=' character, which indicates the
01478     end of a name parameter.  It assumes that the front of the buffer is
01479     the beginning of the name paramter to be read.
01480     
01481     This function properly updates curHTTP.byteCount by decrementing it
01482     by the number of bytes read.  It also removes the delimiting '=' from
01483     the buffer.
01484 
01485   Precondition:
01486     Front of TCP buffer is the beginning of a name parameter, and the rest of
01487     the TCP buffer contains a URL-encoded string with a name parameter 
01488     terminated by a '=' character.
01489 
01490   Parameters:
01491     cData - where to store the name once it is read
01492     wLen - how many bytes can be written to cData
01493 
01494   Return Values:
01495     HTTP_READ_OK - name was successfully read
01496     HTTP_READ_TRUNCTATED - entire name could not fit in the buffer, so the
01497                             value was truncated and data has been lost
01498     HTTP_READ_INCOMPLETE - entire name was not yet in the buffer, so call
01499                             this function again later to retrieve
01500   ***************************************************************************/
01501 #if defined(HTTP_USE_POST)
01502 HTTP_READ_STATUS HTTPReadPostName(BYTE* cData, WORD wLen)
01503 {
01504     HTTP_READ_STATUS status;
01505     
01506     status = HTTPReadTo('=', cData, wLen);
01507 
01508     // Decode the data (if not reading to null or blank) and return
01509     if(cData && !*cData)
01510         HTTPURLDecode(cData);
01511     return status;
01512 }   
01513 #endif
01514 
01515 /*****************************************************************************
01516   Function:
01517     HTTP_READ_STATUS HTTPReadPostValue(BYTE* cData, WORD wLen)
01518 
01519   Summary:
01520     Reads a value from a URL encoded string in the TCP buffer.
01521 
01522   Description:
01523     Reads a value from a URL encoded string in the TCP buffer.  This function
01524     is meant to be called from an HTTPExecutePost callback to facilitate
01525     easier parsing of incoming data.  This function also prevents buffer
01526     overflows by forcing the programmer to indicate how many bytes are
01527     expected.  At least 2 extra bytes are needed in cData above the maximum
01528     length of data expected to be read.
01529     
01530     This function will read until the next '&' character, which indicates the
01531     end of a value parameter.  It assumes that the front of the buffer is
01532     the beginning of the value paramter to be read.  If curHTTP.byteCount
01533     indicates that all expected bytes are in the buffer, it assumes that 
01534     all remaining data is the value and acts accordingly.
01535     
01536     This function properly updates curHTTP.byteCount by decrementing it
01537     by the number of bytes read.  The terminating '&' character is also 
01538     removed from the buffer.
01539     
01540   Precondition:
01541     Front of TCP buffer is the beginning of a name parameter, and the rest of
01542     the TCP buffer contains a URL-encoded string with a name parameter 
01543     terminated by a '=' character.
01544 
01545   Parameters:
01546     cData - where to store the value once it is read
01547     wLen - how many bytes can be written to cData
01548 
01549   Return Values:
01550     HTTP_READ_OK - value was successfully read
01551     HTTP_READ_TRUNCTATED - entire value could not fit in the buffer, so the
01552                             value was truncated and data has been lost
01553     HTTP_READ_INCOMPLETE - entire value was not yet in the buffer, so call
01554                             this function again later to retrieve
01555   ***************************************************************************/
01556 #if defined(HTTP_USE_POST)
01557 HTTP_READ_STATUS HTTPReadPostValue(BYTE* cData, WORD wLen)
01558 {
01559     HTTP_READ_STATUS status;
01560     
01561     // Try to read the value
01562     status = HTTPReadTo('&', cData, wLen);
01563     
01564     // If read was incomplete, check if we're at the end
01565     if(status == HTTP_READ_INCOMPLETE)
01566     {
01567         // If all data has arrived, read all remaining data
01568         if(curHTTP.byteCount == TCPIsGetReady(sktHTTP))
01569             status = HTTPReadTo('\0', cData, wLen);
01570     }
01571         
01572     // Decode the data (if not reading to null or blank) and return
01573     if(cData && *cData)
01574         HTTPURLDecode(cData);
01575     return status;
01576 }   
01577 #endif
01578 
01579 /*****************************************************************************
01580   Function:
01581     static HTTP_READ_STATUS HTTPReadTo(BYTE cDelim, BYTE* cData, WORD wLen)
01582 
01583   Summary:
01584     Reads to a buffer until a specified delimiter character.
01585 
01586   Description:
01587     Reads from the TCP buffer to cData until either cDelim is reached, or
01588     until wLen - 2 bytes have been read.  The value read is saved to cData and 
01589     null terminated.  (wLen - 2 is used so that the value can be passed to
01590     HTTPURLDecode later, which requires a null terminator plus one extra free
01591     byte.)
01592     
01593     The delimiter character is removed from the buffer, but not saved to 
01594     cData. If all data cannot fit into cData, it will still be removed from 
01595     the buffer but will not be saved anywhere.
01596 
01597     This function properly updates curHTTP.byteCount by decrementing it
01598     by the number of bytes read. 
01599 
01600   Precondition:
01601     None
01602 
01603   Parameters:
01604     cDelim - the character at which to stop reading, or NULL to read to
01605              the end of the buffer
01606     cData - where to store the data being read
01607     wLen - how many bytes can be written to cData
01608 
01609   Return Values:
01610     HTTP_READ_OK - data was successfully read
01611     HTTP_READ_TRUNCTATED - entire data could not fit in the buffer, so the
01612                             data was truncated and data has been lost
01613     HTTP_READ_INCOMPLETE - delimiter character was not found
01614   ***************************************************************************/
01615 #if defined(HTTP_USE_POST)
01616 static HTTP_READ_STATUS HTTPReadTo(BYTE cDelim, BYTE* cData, WORD wLen)
01617 {
01618     HTTP_READ_STATUS status;
01619     WORD wPos;
01620     
01621     // Either look for delimiter, or read all available data
01622     if(cDelim)
01623         wPos = TCPFind(sktHTTP, cDelim, 0, FALSE);
01624     else
01625         wPos = TCPIsGetReady(sktHTTP);
01626     
01627     // If not found, return incomplete
01628     if(wPos == 0xffff)
01629         return HTTP_READ_INCOMPLETE;
01630     
01631     // Read the value
01632     if(wLen < 2 && cData != NULL)
01633     {// Buffer is too small, so read to NULL instead
01634         curHTTP.byteCount -= TCPGetArray(sktHTTP, NULL, wPos);
01635         status = HTTP_READ_TRUNCATED;
01636     }
01637     else if(cData == NULL)
01638     {// Just remove the data
01639         curHTTP.byteCount -= TCPGetArray(sktHTTP, NULL, wPos);
01640         status = HTTP_READ_OK;
01641     }
01642     else if(wPos > wLen - 2)
01643     {// Read data, but truncate at max length
01644         curHTTP.byteCount -= TCPGetArray(sktHTTP, cData, wLen - 2);
01645         curHTTP.byteCount -= TCPGetArray(sktHTTP, NULL, wPos - (wLen - 2));
01646         cData[wLen - 2] = '\0';
01647         status = HTTP_READ_TRUNCATED;
01648     }
01649     else
01650     {// Read the data normally
01651         curHTTP.byteCount -= TCPGetArray(sktHTTP, cData, wPos);
01652         cData[wPos] = '\0';
01653         status = HTTP_READ_OK;
01654     }
01655     
01656     // Remove the delimiter
01657     if(cDelim)
01658         curHTTP.byteCount -= TCPGet(sktHTTP, NULL);
01659     
01660     return status;
01661 }   
01662 #endif
01663 
01664 /*****************************************************************************
01665   Function:
01666     HTTP_IO_RESULT HTTPMPFSUpload(void)
01667 
01668   Summary:
01669     Saves a file uploaded via POST as the new MPFS image in EEPROM or 
01670     external Flash.
01671 
01672   Description:
01673     Allows the MPFS image in EEPROM or external Flash to be updated via a 
01674     web page by accepting a file upload and storing it to the external memory.
01675 
01676   Precondition:
01677     MPFSFormat() has been called.
01678 
01679   Parameters:
01680     None
01681 
01682   Return Values:
01683     HTTP_IO_DONE - on success
01684     HTTP_IO_NEED_DATA - if more data is still expected
01685 
01686   Remarks:
01687     This function is only available when MPFS uploads are enabled and
01688     the MPFS image is stored in EEPROM.
01689 
01690   Internal:
01691     After the headers, the first line from the form will be the MIME
01692     separator.  Following that is more headers about the file, which
01693     are discarded.  After another CRLFCRLF pair the file data begins,
01694     which is read 16 bytes at a time and written to external memory.
01695   ***************************************************************************/
01696 #if defined(HTTP_MPFS_UPLOAD)
01697 static HTTP_IO_RESULT HTTPMPFSUpload(void)
01698 {
01699     BYTE c[16];
01700     WORD lenA, lenB;
01701     
01702     switch(curHTTP.httpStatus)
01703     {
01704         // New upload, so look for the CRLFCRLF
01705         case HTTP_MPFS_UP:
01706         
01707             lenA = TCPFindROMArray(sktHTTP, (ROM BYTE*)"\r\n\r\n", 4, 0, FALSE);
01708         
01709             if(lenA != 0xffff)
01710             {// Found it, so remove all data up to and including
01711                 lenA = TCPGetArray(sktHTTP, NULL, lenA);
01712                 curHTTP.byteCount -= lenA;
01713                 
01714                 // Make sure first 6 bytes are also in
01715                 if(TCPIsGetReady(sktHTTP) < (4 + 6) )
01716                 {
01717                     lenA++;
01718                     return HTTP_IO_NEED_DATA;
01719                 }
01720                 
01721                 // Make sure it's an MPFS of the correct version
01722                 lenA = TCPGetArray(sktHTTP, c, 10);
01723                 curHTTP.byteCount -= lenA;
01724                 if(memcmppgm2ram(c, (ROM void*)"\r\n\r\nMPFS\x02\x01", 10) == 0)
01725                 {// Read as Ver 2.1
01726                     curHTTP.httpStatus = HTTP_MPFS_OK;
01727                     
01728                     // Format MPFS storage and put 6 byte tag
01729                     curHTTP.file = MPFSFormat();
01730                     MPFSPutArray(curHTTP.file, &c[4], 6);
01731                 }
01732                 else
01733                 {// Version is wrong
01734                     curHTTP.httpStatus = HTTP_MPFS_ERROR;
01735                 }
01736             }
01737             else
01738             {// Otherwise, remove as much as possible
01739                 lenA = TCPGetArray(sktHTTP, NULL, TCPIsGetReady(sktHTTP) - 4);
01740                 curHTTP.byteCount -= lenA;
01741             }
01742             
01743             break;
01744         
01745         // Received file is invalid
01746         case HTTP_MPFS_ERROR:
01747             curHTTP.byteCount -= TCPIsGetReady(sktHTTP);
01748             TCPDiscard(sktHTTP);
01749             if(curHTTP.byteCount < 100 || curHTTP.byteCount > 0x80000000)
01750             {// If almost all data was read, or if we overflowed, then return
01751                 smHTTP = SM_HTTP_SERVE_HEADERS;
01752                 return HTTP_IO_DONE;
01753             }
01754             break;
01755         
01756         // File is verified, so write the data
01757         case HTTP_MPFS_OK:
01758             // Determine how much to read
01759             lenA = TCPIsGetReady(sktHTTP);
01760             if(lenA > curHTTP.byteCount)
01761                 lenA = curHTTP.byteCount;
01762                 
01763             while(lenA > 0)
01764             {
01765                 lenB = TCPGetArray(sktHTTP, c, mMIN(lenA,16));
01766                 curHTTP.byteCount -= lenB;
01767                 lenA -= lenB;
01768                 MPFSPutArray(curHTTP.file, c, lenB);
01769             }
01770                 
01771             // If we've read all the data
01772             if(curHTTP.byteCount == 0)
01773             {
01774                 MPFSPutEnd(TRUE);
01775                 smHTTP = SM_HTTP_SERVE_HEADERS;
01776                 return HTTP_IO_DONE;
01777             }
01778             
01779         // Other states are not valid here
01780         default:
01781             break;
01782     }
01783         
01784     // Ask for more data
01785     return HTTP_IO_NEED_DATA;
01786     
01787 }
01788 #endif
01789 
01790 /*****************************************************************************
01791   Function:
01792     void HTTPIncFile(ROM BYTE* cFile)
01793 
01794   Summary:
01795     Writes a file byte-for-byte to the currently loaded TCP socket.
01796 
01797   Description:
01798     Allows an entire file to be included as a dynamic variable, providing
01799     a basic templating system for HTML web pages.  This reduces unneeded
01800     duplication of visual elements such as headers, menus, etc.
01801 
01802     When curHTTP.callbackPos is 0, the file is opened and as many bytes
01803     as possible are written.  The current position is then saved to 
01804     curHTTP.callbackPos and the file is closed.  On subsequent calls, 
01805     reading begins at the saved location and continues.  Once the end of
01806     the input file is reached, curHTTP.callbackPos is set back to 0 to 
01807     indicate completion.
01808 
01809   Precondition:
01810     None
01811 
01812   Parameters:
01813     cFile - the name of the file to be sent
01814 
01815   Returns:
01816     None
01817     
01818   Remarks:
01819     Users should not call this function directly, but should instead add
01820     dynamic variables in the form of ~inc:filename.ext~ in their HTML code
01821     to include (for example) the file "filename.ext" at that specified
01822     location.  The MPFS2 Generator utility will handle the rest.
01823   ***************************************************************************/
01824 void HTTPIncFile(ROM BYTE* cFile)
01825 {
01826     WORD wCount, wLen;
01827     BYTE data[64];
01828     MPFS_HANDLE fp;
01829     
01830     // Check if this is a first round call
01831     if(curHTTP.callbackPos == 0x00)
01832     {// On initial call, open the file and save its ID
01833         fp = MPFSOpenROM(cFile);
01834         if(fp == MPFS_INVALID_HANDLE)
01835         {// File not found, so abort
01836             return;
01837         }
01838         ((DWORD_VAL*)&curHTTP.callbackPos)->w[0] = MPFSGetID(fp);
01839     }
01840     else
01841     {// The file was already opened, so load up its ID and seek
01842         fp = MPFSOpenID(((DWORD_VAL*)&curHTTP.callbackPos)->w[0]);
01843         if(fp == MPFS_INVALID_HANDLE)
01844         {// No file handles available, so wait for now
01845             return;
01846         }
01847         MPFSSeek(fp, ((DWORD_VAL*)&curHTTP.callbackPos)->w[1], MPFS_SEEK_FORWARD);
01848     }
01849     
01850     // Get/put as many bytes as possible
01851     wCount = TCPIsPutReady(sktHTTP);
01852     while(wCount > 0)
01853     {
01854         wLen = MPFSGetArray(fp, data, mMIN(wCount, 64));
01855         if(wLen == 0)
01856         {// If no bytes were read, an EOF was reached
01857             MPFSClose(fp);
01858             curHTTP.callbackPos = 0x00;
01859             return;
01860         }
01861         else
01862         {// Write the bytes to the socket
01863             TCPPutArray(sktHTTP, data, wLen);
01864             wCount -= wLen;
01865         }
01866     }
01867     
01868     // Save the new address and close the file
01869     ((DWORD_VAL*)&curHTTP.callbackPos)->w[1] = MPFSTell(fp);
01870     MPFSClose(fp);
01871     
01872     return;
01873 }
01874 
01875 
01876 #endif
01877 
01878 // MODIFIX: Added this function to check for busy HTTP connection in case of
01879 // shut down.
01880 BOOL HTTPCheckForIdle(void)
01881 {
01882     BYTE conn;
01883 
01884     for(conn = 0; conn < MAX_HTTP_CONNECTIONS; conn++)
01885     {
01886         if((httpStubs[conn].sm != SM_HTTP_IDLE) || TCPIsGetReady(httpStubs[conn].socket))
01887         {
01888             return FALSE;
01889         }
01890     }
01891 
01892     return TRUE;
01893 }
01894 

Generated on Sun Nov 27 20:02:38 2011 for eWicht by  doxygen 1.5.5