?lang_form? ?lang_select? ?lang_submit? ?lang_endform?
{HEADER END}
{BLAME START}

library

?curdirlinks? -

Blame information for rev 32

Line No. Rev Author Line
1 32 kaklik /*********************************************************************
2 *
3 * HyperText Transfer Protocol (HTTP) Server
4 * Module for Microchip TCP/IP Stack
5 * -Serves dynamic pages to web browsers such as Microsoft Internet
6 * Explorer, Mozilla Firefox, etc.
7 * -Reference: RFC 2616
8 *
9 **********************************************************************
10 * FileName: HTTP2.c
11 * Dependencies: TCP, MPFS2, Tick, CustomHTTPApp.c callbacks
12 * Processor: PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F, PIC32
13 * Compiler: Microchip C32 v1.05 or higher
14 * Microchip C30 v3.12 or higher
15 * Microchip C18 v3.30 or higher
16 * HI-TECH PICC-18 PRO 9.63PL2 or higher
17 * Company: Microchip Technology, Inc.
18 *
19 * Software License Agreement
20 *
21 * Copyright (C) 2002-2009 Microchip Technology Inc. All rights
22 * reserved.
23 *
24 * Microchip licenses to you the right to use, modify, copy, and
25 * distribute:
26 * (i) the Software when embedded on a Microchip microcontroller or
27 * digital signal controller product ("Device") which is
28 * integrated into Licensee's product; or
29 * (ii) ONLY the Software driver source files ENC28J60.c, ENC28J60.h,
30 * ENCX24J600.c and ENCX24J600.h ported to a non-Microchip device
31 * used in conjunction with a Microchip ethernet controller for
32 * the sole purpose of interfacing with the ethernet controller.
33 *
34 * You should refer to the license agreement accompanying this
35 * Software for additional information regarding your rights and
36 * obligations.
37 *
38 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
39 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
40 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A
41 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
42 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR
43 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
44 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
45 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE
46 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER
47 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT
48 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
49 *
50 *
51 * Author Date Comment
52 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
53 * Nilesh Rajbharti 8/14/01 Original
54 * Elliott Wood 6/4/07 Complete rewrite, known as HTTP2
55 ********************************************************************/
56  
57 #define __HTTP2_C
58  
59 #include "TCPIP Stack/TCPIP.h"
60  
61 #if defined(STACK_USE_HTTP2_SERVER)
62  
63 #include "HTTPPrint.h"
64  
65 /****************************************************************************
66 Section:
67 String Constants
68 ***************************************************************************/
69 static ROM BYTE HTTP_CRLF[] = "\r\n"; // New line sequence
70 #define HTTP_CRLF_LEN 2 // Length of above string
71  
72 /****************************************************************************
73 Section:
74 File and Content Type Settings
75 ***************************************************************************/
76 // File type extensions corresponding to HTTP_FILE_TYPE
77 static ROM char * ROM httpFileExtensions[HTTP_UNKNOWN+1] =
78 {
79 "txt", // HTTP_TXT
80 "htm", // HTTP_HTM
81 "html", // HTTP_HTML
82 "cgi", // HTTP_CGI
83 "xml", // HTTP_XML
84 "css", // HTTP_CSS
85 "gif", // HTTP_GIF
86 "png", // HTTP_PNG
87 "jpg", // HTTP_JPG
88 "cla", // HTTP_JAVA
89 "wav", // HTTP_WAV
90 "\0\0\0" // HTTP_UNKNOWN
91 };
92  
93 // Content-type strings corresponding to HTTP_FILE_TYPE
94 static ROM char * ROM httpContentTypes[HTTP_UNKNOWN+1] =
95 {
96 "text/plain", // HTTP_TXT
97 "text/html", // HTTP_HTM
98 "text/html", // HTTP_HTML
99 "text/html", // HTTP_CGI
100 "text/xml", // HTTP_XML
101 "text/css", // HTTP_CSS
102 "image/gif", // HTTP_GIF
103 "image/png", // HTTP_PNG
104 "image/jpeg", // HTTP_JPG
105 "application/java-vm", // HTTP_JAVA
106 "audio/x-wave", // HTTP_WAV
107 "" // HTTP_UNKNOWN
108 };
109  
110 /****************************************************************************
111 Section:
112 Commands and Server Responses
113 ***************************************************************************/
114  
115 // Initial response strings (Corresponding to HTTP_STATUS)
116 static ROM char * ROM HTTPResponseHeaders[] =
117 {
118 "HTTP/1.1 200 OK\r\nConnection: close\r\n",
119 "HTTP/1.1 200 OK\r\nConnection: close\r\n",
120 "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n400 Bad Request: can't handle Content-Length\r\n",
121 "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"Protected\"\r\nConnection: close\r\n\r\n401 Unauthorized: Password required\r\n",
122 #if defined(HTTP_MPFS_UPLOAD)
123 "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",
124 #else
125 "HTTP/1.1 404 Not found\r\nConnection: close\r\n\r\n404: File not found\r\n",
126 #endif
127 "HTTP/1.1 414 Request-URI Too Long\r\nConnection: close\r\n\r\n414 Request-URI Too Long: Buffer overflow detected\r\n",
128 "HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n500 Internal Server Error: Expected data not present\r\n",
129 "HTTP/1.1 501 Not Implemented\r\nConnection: close\r\n\r\n501 Not Implemented: Only GET and POST supported\r\n",
130 #if defined(HTTP_MPFS_UPLOAD)
131 "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>",
132 "",
133 "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>",
134 "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>",
135 #endif
136 "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: ",
137 "HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n403 Forbidden: SSL Required - use HTTPS\r\n"
138 };
139  
140 /****************************************************************************
141 Section:
142 Header Parsing Configuration
143 ***************************************************************************/
144  
145 // Header strings for which we'd like to parse
146 static ROM char * ROM HTTPRequestHeaders[] =
147 {
148 "Cookie:",
149 "Authorization:",
150 "Content-Length:"
151 };
152  
153 // Set to length of longest string above
154 #define HTTP_MAX_HEADER_LEN (15u)
155  
156 /****************************************************************************
157 Section:
158 HTTP Connection State Global Variables
159 ***************************************************************************/
160 #if defined(__18CXX) && !defined(HI_TECH_C)
161 #pragma udata HTTP_CONNECTION_STATES
162 #endif
163 HTTP_CONN curHTTP; // Current HTTP connection state
164 HTTP_STUB httpStubs[MAX_HTTP_CONNECTIONS]; // HTTP stubs with state machine and socket
165 BYTE curHTTPID; // ID of the currently loaded HTTP_CONN
166 #if defined(__18CXX) && !defined(HI_TECH_C)
167 #pragma udata
168 #endif
169  
170 /****************************************************************************
171 Section:
172 Function Prototypes
173 ***************************************************************************/
174 static void HTTPHeaderParseLookup(BYTE i);
175 #if defined(HTTP_USE_COOKIES)
176 static void HTTPHeaderParseCookie(void);
177 #endif
178 #if defined(HTTP_USE_AUTHENTICATION)
179 static void HTTPHeaderParseAuthorization(void);
180 #endif
181 #if defined(HTTP_USE_POST)
182 static void HTTPHeaderParseContentLength(void);
183 static HTTP_READ_STATUS HTTPReadTo(BYTE delim, BYTE* buf, WORD len);
184 #endif
185  
186 static void HTTPProcess(void);
187 static BOOL HTTPSendFile(void);
188 static void HTTPLoadConn(BYTE hHTTP);
189  
190 #if defined(HTTP_MPFS_UPLOAD)
191 static HTTP_IO_RESULT HTTPMPFSUpload(void);
192 #endif
193  
194 #define mMIN(a, b) ((a<b)?a:b)
195 #define smHTTP httpStubs[curHTTPID].sm // Access the current state machine
196  
197 /*****************************************************************************
198 Function:
199 void HTTPInit(void)
200  
201 Summary:
202 Initializes the HTTP server module.
203  
204 Description:
205 Sets all HTTP sockets to the listening state, and initializes the
206 state machine and file handles for each connection. If SSL is
207 enabled, opens a socket on that port as well.
208  
209 Precondition:
210 TCP must already be initialized.
211  
212 Parameters:
213 None
214  
215 Returns:
216 None
217  
218 Remarks:
219 This function is called only one during lifetime of the application.
220 ***************************************************************************/
221 void HTTPInit(void)
222 {
223 PTR_BASE oldPtr;
224  
225 // Make sure the file handles are invalidated
226 curHTTP.file = MPFS_INVALID_HANDLE;
227 curHTTP.offsets = MPFS_INVALID_HANDLE;
228  
229 for(curHTTPID = 0; curHTTPID < MAX_HTTP_CONNECTIONS; curHTTPID++)
230 {
231 smHTTP = SM_HTTP_IDLE;
232 sktHTTP = TCPOpen(0, TCP_OPEN_SERVER, HTTP_PORT, TCP_PURPOSE_HTTP_SERVER);
233 #if defined(STACK_USE_SSL_SERVER)
234 TCPAddSSLListener(sktHTTP, HTTPS_PORT);
235 #endif
236  
237 // Save the default record (just invalid file handles)
238 oldPtr = MACSetWritePtr(BASE_HTTPB_ADDR + curHTTPID*sizeof(HTTP_CONN));
239 MACPutArray((BYTE*)&curHTTP, sizeof(HTTP_CONN));
240 MACSetWritePtr(oldPtr);
241 }
242  
243 // Set curHTTPID to zero so that first call to HTTPLoadConn() doesn't write
244 // dummy data outside reserved HTTP memory.
245 curHTTPID = 0;
246 }
247  
248  
249 /*****************************************************************************
250 Function:
251 void HTTPServer(void)
252  
253 Summary:
254 Performs periodic tasks for the HTTP2 module.
255  
256 Description:
257 Browses through each open connection and attempts to process any
258 pending operations.
259  
260 Precondition:
261 HTTPInit() must already be called.
262  
263 Parameters:
264 None
265  
266 Returns:
267 None
268  
269 Remarks:
270 This function acts as a task (similar to one in an RTOS). It
271 performs its task in a co-operative manner, and the main application
272 must call this function repeatedly to ensure that all open or new
273 connections are served in a timely fashion.
274 ***************************************************************************/
275 void HTTPServer(void)
276 {
277 BYTE conn;
278  
279 for(conn = 0; conn < MAX_HTTP_CONNECTIONS; conn++)
280 {
281 if(httpStubs[conn].socket == INVALID_SOCKET)
282 continue;
283  
284 // If a socket is disconnected at any time
285 // forget about it and return to idle state.
286 // Must do this here, otherwise we will wait until a new
287 // connection arrives, which causes problems with Linux and with SSL
288 if(TCPWasReset(httpStubs[conn].socket))
289 {
290 HTTPLoadConn(conn);
291 smHTTP = SM_HTTP_IDLE;
292  
293 // Make sure any opened files are closed
294 if(curHTTP.file != MPFS_INVALID_HANDLE)
295 {
296 MPFSClose(curHTTP.file);
297 curHTTP.file = MPFS_INVALID_HANDLE;
298 }
299 if(curHTTP.offsets != MPFS_INVALID_HANDLE)
300 {
301 MPFSClose(curHTTP.offsets);
302 curHTTP.offsets = MPFS_INVALID_HANDLE;
303 }
304  
305 // Adjust FIFO sizes to half and half. Default state must remain
306 // here so that SSL handshakes, if required, can proceed
307 TCPAdjustFIFOSize(sktHTTP, 1, 0, TCP_ADJUST_PRESERVE_RX);
308 }
309  
310 // Determine if this connection is eligible for processing
311 if(httpStubs[conn].sm != SM_HTTP_IDLE || TCPIsGetReady(httpStubs[conn].socket))
312 {
313 HTTPLoadConn(conn);
314 HTTPProcess();
315 }
316 }
317 }
318  
319 /*****************************************************************************
320 Function:
321 static void HTTPLoadConn(BYTE hHTTP)
322  
323 Summary:
324 Switches the currently loaded connection for the HTTP2 module.
325  
326 Description:
327 Saves the currently loaded HTTP connection back to Ethernet buffer
328 RAM, then loads the selected connection into curHTTP in local RAM
329 for processing.
330  
331 Precondition:
332 None
333  
334 Parameters:
335 hHTTP - the connection ID to load
336  
337 Returns:
338 None
339 ***************************************************************************/
340 static void HTTPLoadConn(BYTE hHTTP)
341 {
342 WORD oldPtr;
343  
344 // Return if already loaded
345 if(hHTTP == curHTTPID)
346 return;
347  
348 // Save the old one
349 oldPtr = MACSetWritePtr(BASE_HTTPB_ADDR + curHTTPID*sizeof(HTTP_CONN));
350 MACPutArray((BYTE*)&curHTTP, sizeof(HTTP_CONN));
351 MACSetWritePtr(oldPtr);
352  
353 // Load the new one
354 oldPtr = MACSetReadPtr(BASE_HTTPB_ADDR + hHTTP*sizeof(HTTP_CONN));
355 MACGetArray((BYTE*)&curHTTP, sizeof(HTTP_CONN));
356 MACSetReadPtr(oldPtr);
357  
358 // Remember which one is loaded
359 curHTTPID = hHTTP;
360  
361 }
362  
363 /*****************************************************************************
364 Function:
365 static void HTTPProcess(void)
366  
367 Description:
368 Performs any pending operations for the currently loaded HTTP connection.
369  
370 Precondition:
371 HTTPInit() and HTTPLoadConn() have been called.
372  
373 Parameters:
374 None
375  
376 Returns:
377 None
378 ***************************************************************************/
379 static void HTTPProcess(void)
380 {
381 WORD lenA, lenB;
382 BYTE c, i;
383 BOOL isDone;
384 BYTE *ext;
385 BYTE buffer[HTTP_MAX_HEADER_LEN+1];
386  
387 do
388 {
389 isDone = TRUE;
390  
391 switch(smHTTP)
392 {
393  
394 case SM_HTTP_IDLE:
395  
396 // Check how much data is waiting
397 lenA = TCPIsGetReady(sktHTTP);
398  
399 // If a connection has been made, then process the request
400 if(lenA)
401 {// Clear out state info and move to next state
402 curHTTP.ptrData = curHTTP.data;
403 smHTTP = SM_HTTP_PARSE_REQUEST;
404 curHTTP.isAuthorized = 0xff;
405 curHTTP.hasArgs = FALSE;
406 curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND;
407 curHTTP.callbackPos = 0xffffffff;
408 curHTTP.byteCount = 0;
409 #if defined(HTTP_USE_POST)
410 curHTTP.smPost = 0x00;
411 #endif
412  
413 // Adjust the TCP FIFOs for optimal reception of
414 // the next HTTP request from the browser
415 TCPAdjustFIFOSize(sktHTTP, 1, 0, TCP_ADJUST_PRESERVE_RX | TCP_ADJUST_GIVE_REST_TO_RX);
416 }
417 else
418 // Don't break for new connections. There may be
419 // an entire request in the buffer already.
420 break;
421  
422 case SM_HTTP_PARSE_REQUEST:
423  
424 // Verify the entire first line is in the FIFO
425 if(TCPFind(sktHTTP, '\n', 0, FALSE) == 0xffff)
426 {// First line isn't here yet
427 if(TCPGetRxFIFOFree(sktHTTP) == 0u)
428 {// If the FIFO is full, we overflowed
429 curHTTP.httpStatus = HTTP_OVERFLOW;
430 smHTTP = SM_HTTP_SERVE_HEADERS;
431 isDone = FALSE;
432 }
433 if((LONG)(TickGet() - curHTTP.callbackID) > (LONG)0)
434 {// A timeout has occurred
435 TCPDisconnect(sktHTTP);
436 smHTTP = SM_HTTP_DISCONNECT;
437 isDone = FALSE;
438 }
439 break;
440 }
441  
442 // Reset the watchdog timer
443 curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND;
444  
445 // Determine the request method
446 lenA = TCPFind(sktHTTP, ' ', 0, FALSE);
447 if(lenA > 5u)
448 lenA = 5;
449 TCPGetArray(sktHTTP, curHTTP.data, lenA+1);
450  
451 if ( memcmppgm2ram(curHTTP.data, (ROM void*)"GET", 3) == 0)
452 curHTTP.httpStatus = HTTP_GET;
453 #if defined(HTTP_USE_POST)
454 else if ( memcmppgm2ram(curHTTP.data, (ROM void*)"POST", 4) == 0)
455 curHTTP.httpStatus = HTTP_POST;
456 #endif
457 else
458 {// Unrecognized method, so return not implemented
459 curHTTP.httpStatus = HTTP_NOT_IMPLEMENTED;
460 smHTTP = SM_HTTP_SERVE_HEADERS;
461 isDone = FALSE;
462 break;
463 }
464  
465 // Find end of filename
466 lenA = TCPFind(sktHTTP, ' ', 0, FALSE);
467 lenB = TCPFindEx(sktHTTP, '?', 0, lenA, FALSE);
468 lenA = mMIN(lenA, lenB);
469  
470 // If the file name is too long, then reject the request
471 if(lenA > HTTP_MAX_DATA_LEN - HTTP_DEFAULT_LEN - 1)
472 {
473 curHTTP.httpStatus = HTTP_OVERFLOW;
474 smHTTP = SM_HTTP_SERVE_HEADERS;
475 isDone = FALSE;
476 break;
477 }
478  
479 // Read in the filename and decode
480 lenB = TCPGetArray(sktHTTP, curHTTP.data, lenA);
481 curHTTP.data[lenB] = '\0';
482 HTTPURLDecode(curHTTP.data);
483  
484 // Decode may have changed the string length - update it here
485 lenB = strlen((char*)curHTTP.data);
486  
487 // Check if this is an MPFS Upload
488 #if defined(HTTP_MPFS_UPLOAD)
489 if(memcmppgm2ram(&curHTTP.data[1], HTTP_MPFS_UPLOAD, sizeof(HTTP_MPFS_UPLOAD)) == 0)
490 {// Read remainder of line, and bypass all file opening, etc.
491 #if defined(HTTP_USE_AUTHENTICATION)
492 curHTTP.isAuthorized = HTTPNeedsAuth(&curHTTP.data[1]);
493 #endif
494 if(curHTTP.httpStatus == HTTP_GET)
495 curHTTP.httpStatus = HTTP_MPFS_FORM;
496 else
497 curHTTP.httpStatus = HTTP_MPFS_UP;
498  
499 smHTTP = SM_HTTP_PARSE_HEADERS;
500 isDone = FALSE;
501 break;
502 }
503 #endif
504  
505 // If the last character is a not a directory delimiter, then try to open the file
506 // String starts at 2nd character, because the first is always a '/'
507 if(curHTTP.data[lenB-1] != '/')
508 curHTTP.file = MPFSOpen(&curHTTP.data[1]);
509  
510 // If the open fails, then add our default name and try again
511 if(curHTTP.file == MPFS_INVALID_HANDLE)
512 {
513 // Add the directory delimiter if needed
514 if(curHTTP.data[lenB-1] != '/')
515 curHTTP.data[lenB++] = '/';
516  
517 // Add our default file name
518 #if defined(STACK_USE_SSL_SERVER)
519 if(TCPIsSSL(sktHTTP))
520 {
521 strcpypgm2ram((void*)&curHTTP.data[lenB], HTTPS_DEFAULT_FILE);
522 lenB += strlenpgm(HTTPS_DEFAULT_FILE);
523 }
524 else
525 #endif
526 {
527 strcpypgm2ram((void*)&curHTTP.data[lenB], HTTP_DEFAULT_FILE);
528 lenB += strlenpgm(HTTP_DEFAULT_FILE);
529 }
530  
531 // Try to open again
532 curHTTP.file = MPFSOpen(&curHTTP.data[1]);
533 }
534  
535 // Find the extension in the filename
536 for(ext = curHTTP.data + lenB-1; ext != curHTTP.data; ext--)
537 if(*ext == '.')
538 break;
539  
540 // Compare to known extensions to determine Content-Type
541 ext++;
542 for(curHTTP.fileType = HTTP_TXT; curHTTP.fileType < HTTP_UNKNOWN; curHTTP.fileType++)
543 if(!stricmppgm2ram(ext, (ROM void*)httpFileExtensions[curHTTP.fileType]))
544 break;
545  
546 // Perform first round authentication (pass file name only)
547 #if defined(HTTP_USE_AUTHENTICATION)
548 curHTTP.isAuthorized = HTTPNeedsAuth(&curHTTP.data[1]);
549 #endif
550  
551 // If the file was found, see if it has an index
552 if(curHTTP.file != MPFS_INVALID_HANDLE &&
553 (MPFSGetFlags(curHTTP.file) & MPFS2_FLAG_HASINDEX) )
554 {
555 curHTTP.offsets = MPFSOpenID(MPFSGetID(curHTTP.file) + 1);
556 }
557  
558 // Read GET args, up to buffer size - 1
559 lenA = TCPFind(sktHTTP, ' ', 0, FALSE);
560 if(lenA != 0u)
561 {
562 curHTTP.hasArgs = TRUE;
563  
564 // Trash the '?'
565 TCPGet(sktHTTP, &c);
566  
567 // Verify there's enough space
568 lenA--;
569 if(lenA >= HTTP_MAX_DATA_LEN - 2)
570 {
571 curHTTP.httpStatus = HTTP_OVERFLOW;
572 smHTTP = SM_HTTP_SERVE_HEADERS;
573 isDone = FALSE;
574 break;
575 }
576  
577 // Read in the arguments and '&'-terminate in anticipation of cookies
578 curHTTP.ptrData += TCPGetArray(sktHTTP, curHTTP.data, lenA);
579 *(curHTTP.ptrData++) = '&';
580  
581 }
582  
583 // Clear the rest of the line
584 lenA = TCPFind(sktHTTP, '\n', 0, FALSE);
585 TCPGetArray(sktHTTP, NULL, lenA + 1);
586  
587 // Move to parsing the headers
588 smHTTP = SM_HTTP_PARSE_HEADERS;
589  
590 // No break, continue to parsing headers
591  
592 case SM_HTTP_PARSE_HEADERS:
593  
594 // Loop over all the headers
595 while(1)
596 {
597 // Make sure entire line is in the FIFO
598 lenA = TCPFind(sktHTTP, '\n', 0, FALSE);
599 if(lenA == 0xffff)
600 {// If not, make sure we can receive more data
601 if(TCPGetRxFIFOFree(sktHTTP) == 0u)
602 {// Overflow
603 curHTTP.httpStatus = HTTP_OVERFLOW;
604 smHTTP = SM_HTTP_SERVE_HEADERS;
605 isDone = FALSE;
606 }
607 if((LONG)(TickGet() - curHTTP.callbackID) > (LONG)0)
608 {// A timeout has occured
609 TCPDisconnect(sktHTTP);
610 smHTTP = SM_HTTP_DISCONNECT;
611 isDone = FALSE;
612 }
613 break;
614 }
615  
616 // Reset the watchdog timer
617 curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND;
618  
619 // If a CRLF is immediate, then headers are done
620 if(lenA == 1u)
621 {// Remove the CRLF and move to next state
622 TCPGetArray(sktHTTP, NULL, 2);
623 smHTTP = SM_HTTP_AUTHENTICATE;
624 isDone = FALSE;
625 break;
626 }
627  
628 // Find the header name, and use isDone as a flag to indicate a match
629 lenB = TCPFindEx(sktHTTP, ':', 0, lenA, FALSE) + 2;
630 isDone = FALSE;
631  
632 // If name is too long or this line isn't a header, ignore it
633 if(lenB > sizeof(buffer))
634 {
635 TCPGetArray(sktHTTP, NULL, lenA+1);
636 continue;
637 }
638  
639 // Read in the header name
640 TCPGetArray(sktHTTP, buffer, lenB);
641 buffer[lenB-1] = '\0';
642 lenA -= lenB;
643  
644 // Compare header read to ones we're interested in
645 for(i = 0; i < sizeof(HTTPRequestHeaders)/sizeof(HTTPRequestHeaders[0]); i++)
646 {
647 if(strcmppgm2ram((char*)buffer, (ROM char *)HTTPRequestHeaders[i]) == 0)
648 {// Parse the header and stop the loop
649 HTTPHeaderParseLookup(i);
650 isDone = TRUE;
651 break;
652 }
653 }
654  
655 // Clear the rest of the line, and call the loop again
656 if(isDone)
657 {// We already know how much to remove unless a header was found
658 lenA = TCPFind(sktHTTP, '\n', 0, FALSE);
659 }
660 TCPGetArray(sktHTTP, NULL, lenA+1);
661 }
662  
663 break;
664  
665 case SM_HTTP_AUTHENTICATE:
666  
667 #if defined(HTTP_USE_AUTHENTICATION)
668 // Check current authorization state
669 if(curHTTP.isAuthorized < 0x80)
670 {// 401 error
671 curHTTP.httpStatus = HTTP_UNAUTHORIZED;
672 smHTTP = SM_HTTP_SERVE_HEADERS;
673 isDone = FALSE;
674  
675 #if defined(HTTP_NO_AUTH_WITHOUT_SSL)
676 if(!TCPIsSSL(sktHTTP))
677 curHTTP.httpStatus = HTTP_SSL_REQUIRED;
678 #endif
679  
680 break;
681 }
682 #endif
683  
684 // Parse the args string
685 *curHTTP.ptrData = '\0';
686 curHTTP.ptrData = HTTPURLDecode(curHTTP.data);
687  
688 // If this is an MPFS upload form request, bypass to headers
689 #if defined(HTTP_MPFS_UPLOAD)
690 if(curHTTP.httpStatus == HTTP_MPFS_FORM)
691 {
692 smHTTP = SM_HTTP_SERVE_HEADERS;
693 isDone = FALSE;
694 break;
695 }
696 #endif
697  
698 // Move on to GET args, unless there are none
699 smHTTP = SM_HTTP_PROCESS_GET;
700 if(!curHTTP.hasArgs)
701 smHTTP = SM_HTTP_PROCESS_POST;
702 isDone = FALSE;
703 curHTTP.hasArgs = FALSE;
704 break;
705  
706 case SM_HTTP_PROCESS_GET:
707  
708 // Run the application callback HTTPExecuteGet()
709 if(HTTPExecuteGet() == HTTP_IO_WAITING)
710 {// If waiting for asynchronous process, return to main app
711 break;
712 }
713  
714 // Move on to POST data
715 smHTTP = SM_HTTP_PROCESS_POST;
716  
717 case SM_HTTP_PROCESS_POST:
718  
719 #if defined(HTTP_USE_POST)
720  
721 // See if we have any new data
722 if(TCPIsGetReady(sktHTTP) == curHTTP.callbackPos)
723 {
724 if((LONG)(TickGet() - curHTTP.callbackID) > (LONG)0)
725 {// If a timeout has occured, disconnect
726 TCPDisconnect(sktHTTP);
727 smHTTP = SM_HTTP_DISCONNECT;
728 isDone = FALSE;
729 break;
730 }
731 }
732  
733 if(curHTTP.httpStatus == HTTP_POST
734 #if defined(HTTP_MPFS_UPLOAD)
735 || (curHTTP.httpStatus >= HTTP_MPFS_UP && curHTTP.httpStatus <= HTTP_MPFS_ERROR)
736 #endif
737 )
738 {
739 // Run the application callback HTTPExecutePost()
740 #if defined(HTTP_MPFS_UPLOAD)
741 if(curHTTP.httpStatus >= HTTP_MPFS_UP && curHTTP.httpStatus <= HTTP_MPFS_ERROR)
742 {
743 c = HTTPMPFSUpload();
744 if(c == (BYTE)HTTP_IO_DONE)
745 {
746 smHTTP = SM_HTTP_SERVE_HEADERS;
747 isDone = FALSE;
748 break;
749 }
750 }
751 else
752 #endif
753 c = HTTPExecutePost();
754  
755 // If waiting for asynchronous process, return to main app
756 if(c == (BYTE)HTTP_IO_WAITING)
757 {// return to main app and make sure we don't get stuck by the watchdog
758 curHTTP.callbackPos = TCPIsGetReady(sktHTTP) - 1;
759 break;
760 }
761 else if(c == (BYTE)HTTP_IO_NEED_DATA)
762 {// If waiting for more data
763 curHTTP.callbackPos = TCPIsGetReady(sktHTTP);
764 curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND;
765  
766 // If more is expected and space is available, return to main app
767 if(curHTTP.byteCount > curHTTP.callbackPos && TCPGetRxFIFOFree(sktHTTP) != 0u)
768 break;
769  
770 // Handle cases where application ran out of data or buffer space
771 curHTTP.httpStatus = HTTP_INTERNAL_SERVER_ERROR;
772 smHTTP = SM_HTTP_SERVE_HEADERS;
773 isDone = FALSE;
774 break;
775 }
776 }
777 #endif
778  
779 // We're done with POST
780 smHTTP = SM_HTTP_PROCESS_REQUEST;
781 // No break, continue to sending request
782  
783 case SM_HTTP_PROCESS_REQUEST:
784  
785 // Check for 404
786 if(curHTTP.file == MPFS_INVALID_HANDLE)
787 {
788 curHTTP.httpStatus = HTTP_NOT_FOUND;
789 smHTTP = SM_HTTP_SERVE_HEADERS;
790 isDone = FALSE;
791 break;
792 }
793  
794 // Set up the dynamic substitutions
795 curHTTP.byteCount = 0;
796 if(curHTTP.offsets == MPFS_INVALID_HANDLE)
797 {// If no index file, then set next offset to huge
798 curHTTP.nextCallback = 0xffffffff;
799 }
800 else
801 {// Read in the next callback index
802 MPFSGetLong(curHTTP.offsets, &(curHTTP.nextCallback));
803 }
804  
805 // Move to next state
806 smHTTP = SM_HTTP_SERVE_HEADERS;
807  
808 case SM_HTTP_SERVE_HEADERS:
809  
810 // We're in write mode now:
811 // Adjust the TCP FIFOs for optimal transmission of
812 // the HTTP response to the browser
813 TCPAdjustFIFOSize(sktHTTP, 1, 0, TCP_ADJUST_GIVE_REST_TO_TX);
814  
815 // Send headers
816 TCPPutROMString(sktHTTP, (ROM BYTE*)HTTPResponseHeaders[curHTTP.httpStatus]);
817  
818 // If this is a redirect, print the rest of the Location: header
819 if(curHTTP.httpStatus == HTTP_REDIRECT)
820 {
821 TCPPutString(sktHTTP, curHTTP.data);
822 TCPPutROMString(sktHTTP, (ROM BYTE*)"\r\n\r\n304 Redirect: ");
823 TCPPutString(sktHTTP, curHTTP.data);
824 TCPPutROMString(sktHTTP, (ROM BYTE*)HTTP_CRLF);
825 }
826  
827 // If not GET or POST, we're done
828 if(curHTTP.httpStatus != HTTP_GET && curHTTP.httpStatus != HTTP_POST)
829 {// Disconnect
830 smHTTP = SM_HTTP_DISCONNECT;
831 break;
832 }
833  
834 // Output the content type, if known
835 if(curHTTP.fileType != HTTP_UNKNOWN)
836 {
837 TCPPutROMString(sktHTTP, (ROM BYTE*)"Content-Type: ");
838 TCPPutROMString(sktHTTP, (ROM BYTE*)httpContentTypes[curHTTP.fileType]);
839 TCPPutROMString(sktHTTP, HTTP_CRLF);
840 }
841  
842 // Output the gzip encoding header if needed
843 if(MPFSGetFlags(curHTTP.file) & MPFS2_FLAG_ISZIPPED)
844 {
845 TCPPutROMString(sktHTTP, (ROM BYTE*)"Content-Encoding: gzip\r\n");
846 }
847  
848 // Output the cache-control
849 TCPPutROMString(sktHTTP, (ROM BYTE*)"Cache-Control: ");
850 if(curHTTP.httpStatus == HTTP_POST || curHTTP.nextCallback != 0xffffffff)
851 {// This is a dynamic page or a POST request, so no cache
852 TCPPutROMString(sktHTTP, (ROM BYTE*)"no-cache");
853 }
854 else
855 {// This is a static page, so save it for the specified amount of time
856 TCPPutROMString(sktHTTP, (ROM BYTE*)"max-age=");
857 TCPPutROMString(sktHTTP, (ROM BYTE*)HTTP_CACHE_LEN);
858 }
859 TCPPutROMString(sktHTTP, HTTP_CRLF);
860  
861 // Check if we should output cookies
862 if(curHTTP.hasArgs)
863 smHTTP = SM_HTTP_SERVE_COOKIES;
864 else
865 {// Terminate the headers
866 TCPPutROMString(sktHTTP, HTTP_CRLF);
867 smHTTP = SM_HTTP_SERVE_BODY;
868 }
869  
870 // Move to next stage
871 isDone = FALSE;
872 break;
873  
874 case SM_HTTP_SERVE_COOKIES:
875  
876 #if defined(HTTP_USE_COOKIES)
877 // If the TX FIFO runs out of space, the client will never get CRLFCRLF
878 // Avoid writing huge cookies - keep it under a hundred bytes max
879  
880 // Write cookies one at a time as space permits
881 for(curHTTP.ptrRead = curHTTP.data; curHTTP.hasArgs != 0u; curHTTP.hasArgs--)
882 {
883 // Write the header
884 TCPPutROMString(sktHTTP, (ROM BYTE*)"Set-Cookie: ");
885  
886 // Write the name, URL encoded, one character at a time
887 while((c = *(curHTTP.ptrRead++)))
888 {
889 if(c == ' ')
890 TCPPut(sktHTTP, '+');
891 else if(c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z')
892 {
893 TCPPut(sktHTTP, '%');
894 TCPPut(sktHTTP, btohexa_high(c));
895 TCPPut(sktHTTP, btohexa_low(c));
896 }
897 else
898 TCPPut(sktHTTP, c);
899 }
900  
901 TCPPut(sktHTTP, '=');
902  
903 // Write the value, URL encoded, one character at a time
904 while((c = *(curHTTP.ptrRead++)))
905 {
906 if(c == ' ')
907 TCPPut(sktHTTP, '+');
908 else if(c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z')
909 {
910 TCPPut(sktHTTP, '%');
911 TCPPut(sktHTTP, btohexa_high(c));
912 TCPPut(sktHTTP, btohexa_low(c));
913 }
914 else
915 TCPPut(sktHTTP, c);
916 }
917  
918 // Finish the line
919 TCPPutROMString(sktHTTP, HTTP_CRLF);
920  
921 }
922 #endif
923  
924 // We're done, move to next state
925 TCPPutROMString(sktHTTP, HTTP_CRLF);
926 smHTTP = SM_HTTP_SERVE_BODY;
927  
928 case SM_HTTP_SERVE_BODY:
929  
930 isDone = FALSE;
931  
932 // Try to send next packet
933 if(HTTPSendFile())
934 {// If EOF, then we're done so close and disconnect
935 MPFSClose(curHTTP.file);
936 curHTTP.file = MPFS_INVALID_HANDLE;
937 smHTTP = SM_HTTP_DISCONNECT;
938 isDone = TRUE;
939 }
940  
941 // If the TX FIFO is full, then return to main app loop
942 if(TCPIsPutReady(sktHTTP) == 0u)
943 isDone = TRUE;
944 break;
945  
946 case SM_HTTP_SEND_FROM_CALLBACK:
947  
948 isDone = TRUE;
949  
950 // Check that at least the minimum bytes are free
951 if(TCPIsPutReady(sktHTTP) < HTTP_MIN_CALLBACK_FREE)
952 break;
953  
954 // Fill TX FIFO from callback
955 HTTPPrint(curHTTP.callbackID);
956  
957 if(curHTTP.callbackPos == 0u)
958 {// Callback finished its output, so move on
959 isDone = FALSE;
960 smHTTP = SM_HTTP_SERVE_BODY;
961 }// Otherwise, callback needs more buffer space, so return and wait
962  
963 break;
964  
965 case SM_HTTP_DISCONNECT:
966 // Make sure any opened files are closed
967 if(curHTTP.file != MPFS_INVALID_HANDLE)
968 {
969 MPFSClose(curHTTP.file);
970 curHTTP.file = MPFS_INVALID_HANDLE;
971 }
972 if(curHTTP.offsets != MPFS_INVALID_HANDLE)
973 {
974 MPFSClose(curHTTP.offsets);
975 curHTTP.offsets = MPFS_INVALID_HANDLE;
976 }
977  
978 TCPDisconnect(sktHTTP);
979 smHTTP = SM_HTTP_IDLE;
980 break;
981 }
982 } while(!isDone);
983  
984 }
985  
986  
987 /*****************************************************************************
988 Function:
989 static BOOL HTTPSendFile(void)
990  
991 Description:
992 Serves up the next chunk of curHTTP's file, up to a) available TX FIFO
993 space or b) the next callback index, whichever comes first.
994  
995 Precondition:
996 curHTTP.file and curHTTP.offsets have both been opened for reading.
997  
998 Parameters:
999 None
1000  
1001 Return Values:
1002 TRUE - the end of the file was reached and reading is done
1003 FALSE - more data remains to be read
1004 ***************************************************************************/
1005 static BOOL HTTPSendFile(void)
1006 {
1007 WORD numBytes, len;
1008 BYTE c, data[64];
1009  
1010 // Determine how many bytes we can read right now
1011 len = TCPIsPutReady(sktHTTP);
1012 numBytes = mMIN(len, curHTTP.nextCallback - curHTTP.byteCount);
1013  
1014 // Get/put as many bytes as possible
1015 curHTTP.byteCount += numBytes;
1016 while(numBytes > 0u)
1017 {
1018 len = MPFSGetArray(curHTTP.file, data, mMIN(numBytes, 64u));
1019 if(len == 0u)
1020 return TRUE;
1021 else
1022 TCPPutArray(sktHTTP, data, len);
1023 numBytes -= len;
1024 }
1025  
1026 // Check if a callback index was reached
1027 if(curHTTP.byteCount == curHTTP.nextCallback)
1028 {
1029 // Update the state machine
1030 smHTTP = SM_HTTP_SEND_FROM_CALLBACK;
1031 curHTTP.callbackPos = 0;
1032  
1033 // Read past the variable name and close the MPFS
1034 MPFSGet(curHTTP.file, NULL);
1035 do
1036 {
1037 if(!MPFSGet(curHTTP.file, &c))
1038 break;
1039 curHTTP.byteCount++;
1040 } while(c != '~');
1041 curHTTP.byteCount++;
1042  
1043 // Read in the callback address and next offset
1044 MPFSGetLong(curHTTP.offsets, &(curHTTP.callbackID));
1045 if(!MPFSGetLong(curHTTP.offsets, &(curHTTP.nextCallback)))
1046 {
1047 curHTTP.nextCallback = 0xffffffff;
1048 MPFSClose(curHTTP.offsets);
1049 curHTTP.offsets = MPFS_INVALID_HANDLE;
1050 }
1051 }
1052  
1053 // We are not done sending a file yet...
1054 return FALSE;
1055 }
1056  
1057 /*****************************************************************************
1058 Function:
1059 static void HTTPHeaderParseLookup(BYTE i)
1060  
1061 Description:
1062 Calls the appropriate header parser based on the index of the header
1063 that was read from the request.
1064  
1065 Precondition:
1066 None
1067  
1068 Parameters:
1069 i - the index of the string found in HTTPRequestHeaders
1070  
1071 Return Values:
1072 TRUE - the end of the file was reached and reading is done
1073 FALSE - more data remains to be read
1074 ***************************************************************************/
1075 static void HTTPHeaderParseLookup(BYTE i)
1076 {
1077 // i corresponds to an index in HTTPRequestHeaders
1078  
1079 #if defined(HTTP_USE_COOKIES)
1080 if(i == 0u)
1081 {
1082 HTTPHeaderParseCookie();
1083 return;
1084 }
1085 #endif
1086  
1087 #if defined(HTTP_USE_AUTHENTICATION)
1088 if(i == 1u)
1089 {
1090 HTTPHeaderParseAuthorization();
1091 return;
1092 }
1093 #endif
1094  
1095 #if defined(HTTP_USE_POST)
1096 if(i == 2u)
1097 {
1098 HTTPHeaderParseContentLength();
1099 return;
1100 }
1101 #endif
1102 }
1103  
1104 /*****************************************************************************
1105 Function:
1106 static void HTTPHeaderParseAuthorization(void)
1107  
1108 Summary:
1109 Parses the "Authorization:" header for a request and verifies the
1110 credentials.
1111  
1112 Description:
1113 Parses the "Authorization:" header for a request. For example,
1114 "BASIC YWRtaW46cGFzc3dvcmQ=" is decoded to a user name of "admin" and
1115 a password of "password". Once read, HTTPCheckAuth is called from
1116 CustomHTTPApp.c to determine if the credentials are acceptable.
1117  
1118 The return value of HTTPCheckAuth is saved in curHTTP.isAuthorized for
1119 later use by the application.
1120  
1121 Precondition:
1122 None
1123  
1124 Parameters:
1125 None
1126  
1127 Returns:
1128 None
1129  
1130 Remarks:
1131 This function is ony available when HTTP_USE_AUTHENTICATION is defined.
1132 ***************************************************************************/
1133 #if defined(HTTP_USE_AUTHENTICATION)
1134 static void HTTPHeaderParseAuthorization(void)
1135 {
1136 WORD len;
1137 BYTE buf[40];
1138 BYTE *ptrBuf;
1139  
1140 // If auth processing is not required, return
1141 if(curHTTP.isAuthorized & 0x80)
1142 return;
1143  
1144 // Clear the auth type ("BASIC ")
1145 TCPGetArray(sktHTTP, NULL, 6);
1146  
1147 // Find the terminating CRLF and make sure it's a multiple of four
1148 len = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE);
1149 len += 3;
1150 len &= 0xfc;
1151 len = mMIN(len, sizeof(buf)-4);
1152  
1153 // Read in 4 bytes at a time and decode (slower, but saves RAM)
1154 for(ptrBuf = buf; len > 0u; len-=4, ptrBuf+=3)
1155 {
1156 TCPGetArray(sktHTTP, ptrBuf, 4);
1157 Base64Decode(ptrBuf, 4, ptrBuf, 3);
1158 }
1159  
1160 // Null terminate both, and make sure there's at least two terminators
1161 *ptrBuf = '\0';
1162 for(len = 0, ptrBuf = buf; len < sizeof(buf); len++, ptrBuf++)
1163 if(*ptrBuf == ':')
1164 break;
1165 *(ptrBuf++) = '\0';
1166  
1167 // Verify credentials
1168 curHTTP.isAuthorized = HTTPCheckAuth(buf, ptrBuf);
1169  
1170 return;
1171 }
1172 #endif
1173  
1174 /*****************************************************************************
1175 Function:
1176 static void HTTPHeaderParseCookie(void)
1177  
1178 Summary:
1179 Parses the "Cookie:" headers for a request and stores them as GET
1180 variables.
1181  
1182 Description:
1183 Parses the "Cookie:" headers for a request. For example,
1184 "Cookie: name=Wile+E.+Coyote; order=ROCKET_LAUNCHER" is decoded to
1185 "name=Wile+E.+Coyote&order=ROCKET_LAUNCHER&" and stored as any other
1186 GET variable in curHTTP.data.
1187  
1188 The user application can easily access these values later using the
1189 HTTPGetArg() and HTTPGetROMArg() functions.
1190  
1191 Precondition:
1192 None
1193  
1194 Parameters:
1195 None
1196  
1197 Returns:
1198 None
1199  
1200 Remarks:
1201 This function is ony available when HTTP_USE_COOKIES is defined.
1202 ***************************************************************************/
1203 #if defined(HTTP_USE_COOKIES)
1204 static void HTTPHeaderParseCookie(void)
1205 {
1206 WORD lenA, lenB;
1207  
1208 // Verify there's enough space
1209 lenB = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE);
1210 if(lenB >= (WORD)(curHTTP.data + HTTP_MAX_DATA_LEN - curHTTP.ptrData - 2))
1211 {// If not, overflow
1212 curHTTP.httpStatus = HTTP_OVERFLOW;
1213 smHTTP = SM_HTTP_SERVE_HEADERS;
1214 return;
1215 }
1216  
1217 // While a CRLF is not immediate, grab a cookie value
1218 while(lenB != 0u)
1219 {
1220 // Look for a ';' and use the shorter of that or a CRLF
1221 lenA = TCPFind(sktHTTP, ';', 0, FALSE);
1222  
1223 // Read to the terminator
1224 curHTTP.ptrData += TCPGetArray(sktHTTP, curHTTP.ptrData, mMIN(lenA, lenB));
1225  
1226 // Insert an & to anticipate another cookie
1227 *(curHTTP.ptrData++) = '&';
1228  
1229 // If semicolon, trash it and whitespace
1230 if(lenA < lenB)
1231 {
1232 TCPGet(sktHTTP, NULL);
1233 while(TCPFind(sktHTTP, ' ', 0, FALSE) == 0u)
1234 TCPGet(sktHTTP, NULL);
1235 }
1236  
1237 // Find the new distance to the CRLF
1238 lenB = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE);
1239 }
1240  
1241 return;
1242  
1243 }
1244 #endif
1245  
1246 /*****************************************************************************
1247 Function:
1248 static void HTTPHeaderParseContentLength(void)
1249  
1250 Summary:
1251 Parses the "Content-Length:" header for a request.
1252  
1253 Description:
1254 Parses the "Content-Length:" header to determine how many bytes of
1255 POST data to expect after the request. This value is stored in
1256 curHTTP.byteCount.
1257  
1258 Precondition:
1259 None
1260  
1261 Parameters:
1262 None
1263  
1264 Returns:
1265 None
1266  
1267 Remarks:
1268 This function is ony available when HTTP_USE_POST is defined.
1269 ***************************************************************************/
1270 #if defined(HTTP_USE_POST)
1271 static void HTTPHeaderParseContentLength(void)
1272 {
1273 WORD len;
1274 BYTE buf[10];
1275  
1276 // Read up to the CRLF (max 9 bytes or ~1GB)
1277 len = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE);
1278 if(len >= sizeof(buf))
1279 {
1280 curHTTP.httpStatus = HTTP_BAD_REQUEST;
1281 curHTTP.byteCount = 0;
1282 return;
1283 }
1284 len = TCPGetArray(sktHTTP, buf, len);
1285 buf[len] = '\0';
1286  
1287 curHTTP.byteCount = atol((char*)buf);
1288 }
1289 #endif
1290  
1291 /*****************************************************************************
1292 Function:
1293 BYTE* HTTPURLDecode(BYTE* cData)
1294  
1295 Summary:
1296 Parses a string from URL encoding to plain-text.
1297  
1298 Description:
1299 Parses a string from URL encoding to plain-text. The following
1300 conversions are made: ‘=’ to ‘\0’, ‘&’ to ‘\0’, ‘+’ to ‘ ‘, and
1301 “%xx” to a single hex byte.
1302  
1303 After completion, the data has been decoded and a null terminator
1304 signifies the end of a name or value. A second null terminator (or a
1305 null name parameter) indicates the end of all the data.
1306  
1307 Precondition:
1308 The data parameter is null terminated and has at least one extra
1309 byte free.
1310  
1311 Parameters:
1312 cData - The string which is to be decoded in place.
1313  
1314 Returns:
1315 A pointer to the last null terminator in data, which is also the
1316 first free byte for new data.
1317  
1318 Remarks:
1319 This function is called by the stack to parse GET arguments and
1320 cookie data. User applications can use this function to decode POST
1321 data, but first need to verify that the string is null-terminated.
1322 ***************************************************************************/
1323 BYTE* HTTPURLDecode(BYTE* cData)
1324 {
1325 BYTE *pRead, *pWrite;
1326 WORD wLen;
1327 BYTE c;
1328 WORD hex;
1329  
1330 // Determine length of input
1331 wLen = strlen((char*)cData);
1332  
1333 // Read all characters in the string
1334 for(pRead = pWrite = cData; wLen != 0u; )
1335 {
1336 c = *pRead++;
1337 wLen--;
1338  
1339 if(c == '=' || c == '&')
1340 *pWrite++ = '\0';
1341 else if(c == '+')
1342 *pWrite++ = ' ';
1343 else if(c == '%')
1344 {
1345 if(wLen < 2u)
1346 wLen = 0;
1347 else
1348 {
1349 ((BYTE*)&hex)[1] = *pRead++;
1350 ((BYTE*)&hex)[0] = *pRead++;
1351 wLen--;
1352 wLen--;
1353 *pWrite++ = hexatob(*((WORD_VAL*)&hex));
1354 }
1355 }
1356 else
1357 *pWrite++ = c;
1358 }
1359  
1360 // Double null terminate the last value
1361 *pWrite++ = '\0';
1362 *pWrite = '\0';
1363  
1364 return pWrite;
1365 }
1366  
1367 /*****************************************************************************
1368 Function:
1369 BYTE* HTTPGetArg(BYTE* cData, BYTE* cArg)
1370  
1371 Summary:
1372 Locates a form field value in a given data array.
1373  
1374 Description:
1375 Searches through a data array to find the value associated with a
1376 given argument. It can be used to find form field values in data
1377 received over GET or POST.
1378  
1379 The end of data is assumed to be reached when a null name parameter is
1380 encountered. This requires the string to have an even number of
1381 null-terminated strings, followed by an additional null terminator.
1382  
1383 Precondition:
1384 The data array has a valid series of null terminated name/value pairs.
1385  
1386 Parameters:
1387 data - the buffer to search
1388 arg - the name of the argument to find
1389  
1390 Returns:
1391 A pointer to the argument value, or NULL if not found.
1392 ***************************************************************************/
1393 BYTE* HTTPGetArg(BYTE* cData, BYTE* cArg)
1394 {
1395 // Search through the array while bytes remain
1396 while(*cData != '\0')
1397 {
1398 // Look for arg at current position
1399 if(!strcmp((char*)cArg, (char*)cData))
1400 {// Found it, so return parameter
1401 return cData + strlen((char*)cArg) + 1;
1402 }
1403  
1404 // Skip past two strings (NUL bytes)
1405 cData += strlen((char*)cData) + 1;
1406 cData += strlen((char*)cData) + 1;
1407 }
1408  
1409 // Return NULL if not found
1410 return NULL;
1411 }
1412  
1413 /*****************************************************************************
1414 Function:
1415 BYTE* HTTPGetROMArg(BYTE* cData, ROM BYTE* cArg)
1416  
1417 Summary:
1418 Locates a form field value in a given data array.
1419  
1420 Description:
1421 Searches through a data array to find the value associated with a
1422 given argument. It can be used to find form field values in data
1423 received over GET or POST.
1424  
1425 The end of data is assumed to be reached when a null name parameter is
1426 encountered. This requires the string to have an even number of
1427 null-terminated strings, followed by an additional null terminator.
1428  
1429 Precondition:
1430 The data array has a valid series of null terminated name/value pairs.
1431  
1432 Parameters:
1433 data - the buffer to search
1434 arg - the name of the argument to find
1435  
1436 Returns:
1437 A pointer to the argument value, or NULL if not found.
1438  
1439 Remarks:
1440 This function is aliased to HTTPGetArg on non-PIC18 platforms.
1441 ***************************************************************************/
1442 #if defined(__18CXX)
1443 BYTE* HTTPGetROMArg(BYTE* cData, ROM BYTE* cArg)
1444 {
1445 // Search through the array while bytes remain
1446 while(*cData != '\0')
1447 {
1448 // Look for arg at current position
1449 if(!memcmppgm2ram(cData, (ROM void*)cArg, strlenpgm((ROM char*)cArg) + 1))
1450 {// Found it, so skip to next string
1451 return cData + strlenpgm((ROM char*)cArg) + 1;
1452 }
1453  
1454 // Skip past two strings (NUL bytes)
1455 cData += strlen((char*)cData) + 1;
1456 cData += strlen((char*)cData) + 1;
1457 }
1458  
1459 // Return NULL if not found
1460 return NULL;
1461 }
1462 #endif
1463  
1464 /*****************************************************************************
1465 Function:
1466 HTTP_READ_STATUS HTTPReadPostName(BYTE* cData, WORD wLen)
1467  
1468 Summary:
1469 Reads a name from a URL encoded string in the TCP buffer.
1470  
1471 Description:
1472 Reads a name from a URL encoded string in the TCP buffer. This function
1473 is meant to be called from an HTTPExecutePost callback to facilitate
1474 easier parsing of incoming data. This function also prevents buffer
1475 overflows by forcing the programmer to indicate how many bytes are
1476 expected. At least 2 extra bytes are needed in cData over the maximum
1477 length of data expected to be read.
1478  
1479 This function will read until the next '=' character, which indicates the
1480 end of a name parameter. It assumes that the front of the buffer is
1481 the beginning of the name paramter to be read.
1482  
1483 This function properly updates curHTTP.byteCount by decrementing it
1484 by the number of bytes read. It also removes the delimiting '=' from
1485 the buffer.
1486  
1487 Precondition:
1488 Front of TCP buffer is the beginning of a name parameter, and the rest of
1489 the TCP buffer contains a URL-encoded string with a name parameter
1490 terminated by a '=' character.
1491  
1492 Parameters:
1493 cData - where to store the name once it is read
1494 wLen - how many bytes can be written to cData
1495  
1496 Return Values:
1497 HTTP_READ_OK - name was successfully read
1498 HTTP_READ_TRUNCTATED - entire name could not fit in the buffer, so the
1499 value was truncated and data has been lost
1500 HTTP_READ_INCOMPLETE - entire name was not yet in the buffer, so call
1501 this function again later to retrieve
1502 ***************************************************************************/
1503 #if defined(HTTP_USE_POST)
1504 HTTP_READ_STATUS HTTPReadPostName(BYTE* cData, WORD wLen)
1505 {
1506 HTTP_READ_STATUS status;
1507  
1508 status = HTTPReadTo('=', cData, wLen);
1509  
1510 // Decode the data (if not reading to null or blank) and return
1511 if(cData && *cData)
1512 HTTPURLDecode(cData);
1513 return status;
1514 }
1515 #endif
1516  
1517 /*****************************************************************************
1518 Function:
1519 HTTP_READ_STATUS HTTPReadPostValue(BYTE* cData, WORD wLen)
1520  
1521 Summary:
1522 Reads a value from a URL encoded string in the TCP buffer.
1523  
1524 Description:
1525 Reads a value from a URL encoded string in the TCP buffer. This function
1526 is meant to be called from an HTTPExecutePost callback to facilitate
1527 easier parsing of incoming data. This function also prevents buffer
1528 overflows by forcing the programmer to indicate how many bytes are
1529 expected. At least 2 extra bytes are needed in cData above the maximum
1530 length of data expected to be read.
1531  
1532 This function will read until the next '&' character, which indicates the
1533 end of a value parameter. It assumes that the front of the buffer is
1534 the beginning of the value paramter to be read. If curHTTP.byteCount
1535 indicates that all expected bytes are in the buffer, it assumes that
1536 all remaining data is the value and acts accordingly.
1537  
1538 This function properly updates curHTTP.byteCount by decrementing it
1539 by the number of bytes read. The terminating '&' character is also
1540 removed from the buffer.
1541  
1542 Precondition:
1543 Front of TCP buffer is the beginning of a name parameter, and the rest of
1544 the TCP buffer contains a URL-encoded string with a name parameter
1545 terminated by a '=' character.
1546  
1547 Parameters:
1548 cData - where to store the value once it is read
1549 wLen - how many bytes can be written to cData
1550  
1551 Return Values:
1552 HTTP_READ_OK - value was successfully read
1553 HTTP_READ_TRUNCTATED - entire value could not fit in the buffer, so the
1554 value was truncated and data has been lost
1555 HTTP_READ_INCOMPLETE - entire value was not yet in the buffer, so call
1556 this function again later to retrieve
1557 ***************************************************************************/
1558 #if defined(HTTP_USE_POST)
1559 HTTP_READ_STATUS HTTPReadPostValue(BYTE* cData, WORD wLen)
1560 {
1561 HTTP_READ_STATUS status;
1562  
1563 // Try to read the value
1564 status = HTTPReadTo('&', cData, wLen);
1565  
1566 // If read was incomplete, check if we're at the end
1567 if(status == HTTP_READ_INCOMPLETE)
1568 {
1569 // If all data has arrived, read all remaining data
1570 if(curHTTP.byteCount == TCPIsGetReady(sktHTTP))
1571 status = HTTPReadTo('\0', cData, wLen);
1572 }
1573  
1574 // Decode the data (if not reading to null or blank) and return
1575 if(cData && *cData)
1576 HTTPURLDecode(cData);
1577 return status;
1578 }
1579 #endif
1580  
1581 /*****************************************************************************
1582 Function:
1583 static HTTP_READ_STATUS HTTPReadTo(BYTE cDelim, BYTE* cData, WORD wLen)
1584  
1585 Summary:
1586 Reads to a buffer until a specified delimiter character.
1587  
1588 Description:
1589 Reads from the TCP buffer to cData until either cDelim is reached, or
1590 until wLen - 2 bytes have been read. The value read is saved to cData and
1591 null terminated. (wLen - 2 is used so that the value can be passed to
1592 HTTPURLDecode later, which requires a null terminator plus one extra free
1593 byte.)
1594  
1595 The delimiter character is removed from the buffer, but not saved to
1596 cData. If all data cannot fit into cData, it will still be removed from
1597 the buffer but will not be saved anywhere.
1598  
1599 This function properly updates curHTTP.byteCount by decrementing it
1600 by the number of bytes read.
1601  
1602 Precondition:
1603 None
1604  
1605 Parameters:
1606 cDelim - the character at which to stop reading, or NULL to read to
1607 the end of the buffer
1608 cData - where to store the data being read
1609 wLen - how many bytes can be written to cData
1610  
1611 Return Values:
1612 HTTP_READ_OK - data was successfully read
1613 HTTP_READ_TRUNCTATED - entire data could not fit in the buffer, so the
1614 data was truncated and data has been lost
1615 HTTP_READ_INCOMPLETE - delimiter character was not found
1616 ***************************************************************************/
1617 #if defined(HTTP_USE_POST)
1618 static HTTP_READ_STATUS HTTPReadTo(BYTE cDelim, BYTE* cData, WORD wLen)
1619 {
1620 HTTP_READ_STATUS status;
1621 WORD wPos;
1622  
1623 // Either look for delimiter, or read all available data
1624 if(cDelim)
1625 wPos = TCPFind(sktHTTP, cDelim, 0, FALSE);
1626 else
1627 wPos = TCPIsGetReady(sktHTTP);
1628  
1629 // If not found, return incomplete
1630 if(wPos == 0xffff)
1631 return HTTP_READ_INCOMPLETE;
1632  
1633 // Read the value
1634 if(wLen < 2u && cData != NULL)
1635 {// Buffer is too small, so read to NULL instead
1636 curHTTP.byteCount -= TCPGetArray(sktHTTP, NULL, wPos);
1637 status = HTTP_READ_TRUNCATED;
1638 }
1639 else if(cData == NULL)
1640 {// Just remove the data
1641 curHTTP.byteCount -= TCPGetArray(sktHTTP, NULL, wPos);
1642 status = HTTP_READ_OK;
1643 }
1644 else if(wPos > wLen - 2)
1645 {// Read data, but truncate at max length
1646 curHTTP.byteCount -= TCPGetArray(sktHTTP, cData, wLen - 2);
1647 curHTTP.byteCount -= TCPGetArray(sktHTTP, NULL, wPos - (wLen - 2));
1648 cData[wLen - 2] = '\0';
1649 status = HTTP_READ_TRUNCATED;
1650 }
1651 else
1652 {// Read the data normally
1653 curHTTP.byteCount -= TCPGetArray(sktHTTP, cData, wPos);
1654 cData[wPos] = '\0';
1655 status = HTTP_READ_OK;
1656 }
1657  
1658 // Remove the delimiter
1659 if(cDelim)
1660 curHTTP.byteCount -= TCPGet(sktHTTP, NULL);
1661  
1662 return status;
1663 }
1664 #endif
1665  
1666 /*****************************************************************************
1667 Function:
1668 HTTP_IO_RESULT HTTPMPFSUpload(void)
1669  
1670 Summary:
1671 Saves a file uploaded via POST as the new MPFS image in EEPROM or
1672 external Flash.
1673  
1674 Description:
1675 Allows the MPFS image in EEPROM or external Flash to be updated via a
1676 web page by accepting a file upload and storing it to the external memory.
1677  
1678 Precondition:
1679 MPFSFormat() has been called.
1680  
1681 Parameters:
1682 None
1683  
1684 Return Values:
1685 HTTP_IO_DONE - on success
1686 HTTP_IO_NEED_DATA - if more data is still expected
1687  
1688 Remarks:
1689 This function is only available when MPFS uploads are enabled and
1690 the MPFS image is stored in EEPROM.
1691  
1692 Internal:
1693 After the headers, the first line from the form will be the MIME
1694 separator. Following that is more headers about the file, which
1695 are discarded. After another CRLFCRLF pair the file data begins,
1696 which is read 16 bytes at a time and written to external memory.
1697 ***************************************************************************/
1698 #if defined(HTTP_MPFS_UPLOAD)
1699 static HTTP_IO_RESULT HTTPMPFSUpload(void)
1700 {
1701 BYTE c[16];
1702 WORD lenA, lenB;
1703  
1704 switch(curHTTP.httpStatus)
1705 {
1706 // New upload, so look for the CRLFCRLF
1707 case HTTP_MPFS_UP:
1708  
1709 lenA = TCPFindROMArray(sktHTTP, (ROM BYTE*)"\r\n\r\n", 4, 0, FALSE);
1710  
1711 if(lenA != 0xffff)
1712 {// Found it, so remove all data up to and including
1713 lenA = TCPGetArray(sktHTTP, NULL, lenA);
1714 curHTTP.byteCount -= lenA;
1715  
1716 // Make sure first 6 bytes are also in
1717 if(TCPIsGetReady(sktHTTP) < (4u + 6u) )
1718 {
1719 lenA++;
1720 return HTTP_IO_NEED_DATA;
1721 }
1722  
1723 // Make sure it's an MPFS of the correct version
1724 lenA = TCPGetArray(sktHTTP, c, 10);
1725 curHTTP.byteCount -= lenA;
1726 if(memcmppgm2ram(c, (ROM void*)"\r\n\r\nMPFS\x02\x01", 10) == 0)
1727 {// Read as Ver 2.1
1728 curHTTP.httpStatus = HTTP_MPFS_OK;
1729  
1730 // Format MPFS storage and put 6 byte tag
1731 curHTTP.file = MPFSFormat();
1732 MPFSPutArray(curHTTP.file, &c[4], 6);
1733 }
1734 else
1735 {// Version is wrong
1736 curHTTP.httpStatus = HTTP_MPFS_ERROR;
1737 }
1738  
1739 return HTTP_IO_WAITING;
1740 }
1741 else
1742 {// Otherwise, remove as much as possible
1743 lenA = TCPGetArray(sktHTTP, NULL, TCPIsGetReady(sktHTTP) - 4);
1744 curHTTP.byteCount -= lenA;
1745 }
1746  
1747 break;
1748  
1749 // Received file is invalid
1750 case HTTP_MPFS_ERROR:
1751 curHTTP.byteCount -= TCPIsGetReady(sktHTTP);
1752 TCPDiscard(sktHTTP);
1753 if(curHTTP.byteCount < 100u || curHTTP.byteCount > 0x80000000u)
1754 {// If almost all data was read, or if we overflowed, then return
1755 smHTTP = SM_HTTP_SERVE_HEADERS;
1756 return HTTP_IO_DONE;
1757 }
1758 break;
1759  
1760 // File is verified, so write the data
1761 case HTTP_MPFS_OK:
1762 // Determine how much to read
1763 lenA = TCPIsGetReady(sktHTTP);
1764 if(lenA > curHTTP.byteCount)
1765 lenA = curHTTP.byteCount;
1766  
1767 while(lenA > 0u)
1768 {
1769 lenB = TCPGetArray(sktHTTP, c, mMIN(lenA,16u));
1770 curHTTP.byteCount -= lenB;
1771 lenA -= lenB;
1772 MPFSPutArray(curHTTP.file, c, lenB);
1773 }
1774  
1775 // If we've read all the data
1776 if(curHTTP.byteCount == 0u)
1777 {
1778 MPFSPutEnd(TRUE);
1779 smHTTP = SM_HTTP_SERVE_HEADERS;
1780 return HTTP_IO_DONE;
1781 }
1782  
1783 // Other states are not valid here
1784 default:
1785 break;
1786 }
1787  
1788 // Ask for more data
1789 return HTTP_IO_NEED_DATA;
1790  
1791 }
1792 #endif
1793  
1794 /*****************************************************************************
1795 Function:
1796 void HTTPIncFile(ROM BYTE* cFile)
1797  
1798 Summary:
1799 Writes a file byte-for-byte to the currently loaded TCP socket.
1800  
1801 Description:
1802 Allows an entire file to be included as a dynamic variable, providing
1803 a basic templating system for HTML web pages. This reduces unneeded
1804 duplication of visual elements such as headers, menus, etc.
1805  
1806 When curHTTP.callbackPos is 0, the file is opened and as many bytes
1807 as possible are written. The current position is then saved to
1808 curHTTP.callbackPos and the file is closed. On subsequent calls,
1809 reading begins at the saved location and continues. Once the end of
1810 the input file is reached, curHTTP.callbackPos is set back to 0 to
1811 indicate completion.
1812  
1813 Precondition:
1814 None
1815  
1816 Parameters:
1817 cFile - the name of the file to be sent
1818  
1819 Returns:
1820 None
1821  
1822 Remarks:
1823 Users should not call this function directly, but should instead add
1824 dynamic variables in the form of ~inc:filename.ext~ in their HTML code
1825 to include (for example) the file "filename.ext" at that specified
1826 location. The MPFS2 Generator utility will handle the rest.
1827 ***************************************************************************/
1828 void HTTPIncFile(ROM BYTE* cFile)
1829 {
1830 WORD wCount, wLen;
1831 BYTE data[64];
1832 MPFS_HANDLE fp;
1833  
1834 // Check if this is a first round call
1835 if(curHTTP.callbackPos == 0x00u)
1836 {// On initial call, open the file and save its ID
1837 fp = MPFSOpenROM(cFile);
1838 if(fp == MPFS_INVALID_HANDLE)
1839 {// File not found, so abort
1840 return;
1841 }
1842 ((DWORD_VAL*)&curHTTP.callbackPos)->w[0] = MPFSGetID(fp);
1843 }
1844 else
1845 {// The file was already opened, so load up its ID and seek
1846 fp = MPFSOpenID(((DWORD_VAL*)&curHTTP.callbackPos)->w[0]);
1847 if(fp == MPFS_INVALID_HANDLE)
1848 {// No file handles available, so wait for now
1849 return;
1850 }
1851 MPFSSeek(fp, ((DWORD_VAL*)&curHTTP.callbackPos)->w[1], MPFS_SEEK_FORWARD);
1852 }
1853  
1854 // Get/put as many bytes as possible
1855 wCount = TCPIsPutReady(sktHTTP);
1856 while(wCount > 0u)
1857 {
1858 wLen = MPFSGetArray(fp, data, mMIN(wCount, 64u));
1859 if(wLen == 0u)
1860 {// If no bytes were read, an EOF was reached
1861 MPFSClose(fp);
1862 curHTTP.callbackPos = 0x00;
1863 return;
1864 }
1865 else
1866 {// Write the bytes to the socket
1867 TCPPutArray(sktHTTP, data, wLen);
1868 wCount -= wLen;
1869 }
1870 }
1871  
1872 // Save the new address and close the file
1873 ((DWORD_VAL*)&curHTTP.callbackPos)->w[1] = MPFSTell(fp);
1874 MPFSClose(fp);
1875  
1876 return;
1877 }
1878  
1879  
1880 #endif
{BLAME END}
{FOOTER START}

Powered by WebSVN v2.8.3