]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/net/athttpd/v2_0/src/http.c
Initial revision
[karo-tx-redboot.git] / packages / net / athttpd / v2_0 / src / http.c
1 /* =================================================================
2  *
3  *      http.c
4  *
5  *      Handles the client requests.
6  *
7  * ================================================================= 
8  * ####ECOSGPLCOPYRIGHTBEGIN####
9  * -------------------------------------------
10  * This file is part of eCos, the Embedded Configurable Operating
11  * System.
12  * Copyright (C) 2005, 2007 eCosCentric Ltd.
13  * 
14  * eCos is free software; you can redistribute it and/or modify it
15  * under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 or (at your option)
17  * any later version.
18  * 
19  * eCos is distributed in the hope that it will be useful, but
20  * WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with eCos; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
27  * 
28  * As a special exception, if other files instantiate templates or
29  * use macros or inline functions from this file, or you compile this
30  * file and link it with other works to produce a work based on this
31  * file, this file does not by itself cause the resulting work to be
32  * covered by the GNU General Public License. However the source code
33  * for this file must still be made available in accordance with
34  * section (3) of the GNU General Public License.
35  * 
36  * This exception does not invalidate any other reasons why a work
37  * based on this file might be covered by the GNU General Public
38  * License.
39  *
40  * -------------------------------------------
41  * ####ECOSGPLCOPYRIGHTEND####
42  * =================================================================
43  * #####DESCRIPTIONBEGIN####
44  * 
45  *  Author(s):    Anthony Tonizzo (atonizzo@gmail.com)
46  *  Contributors: Sergei Gavrikov (w3sg@SoftHome.net)
47  *                Lars Povlsen    (lpovlsen@vitesse.com)
48  *  Date:         2006-06-12
49  *  Purpose:      
50  *  Description:  
51  *               
52  * ####DESCRIPTIONEND####
53  * 
54  * =================================================================
55  */
56
57 #include <pkgconf/hal.h>
58 #include <pkgconf/kernel.h>
59 #include <cyg/kernel/kapi.h>           // Kernel API.
60 #include <cyg/infra/diag.h>            // For diagnostic printing.
61 #include <network.h>
62 #include <time.h>
63
64 #include <cyg/hal/hal_tables.h>
65 #include <cyg/fileio/fileio.h>
66 #include <stdio.h>                     // sprintf().
67 #include <stdlib.h>
68
69 #ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER
70 #include <cyg/objloader/elf.h>
71 #include <cyg/objloader/objelf.h>
72 #endif
73
74 #include <cyg/athttpd/http.h>
75 #include <cyg/athttpd/socket.h>
76 #include <cyg/athttpd/handler.h>
77 #include <cyg/athttpd/forms.h>
78
79 cyg_int32 debug_print = 0;
80
81 const char *day_of_week[7] = {"Sun", "Mon", "Tue", "Wed", 
82                                  "Thu", "Fri", "Sat"};
83 const char *month_of_year[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 
84                                     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
85 const char *home_pages[] = {"index.html",   "index.htm",
86                             "default.html", "home.html"};
87 CYG_HAL_TABLE_BEGIN(cyg_httpd_mime_table, httpd_mime_table);
88 CYG_HAL_TABLE_END(cyg_httpd_mime_table_end, httpd_mime_table);
89
90 __externC cyg_httpd_mime_table_entry cyg_httpd_mime_table[];
91 __externC cyg_httpd_mime_table_entry cyg_httpd_mime_table_end[];
92
93 // Standard handlers added by default. Correspond to the most used extensions.
94 // The user can add his/her own later.
95 CYG_HTTPD_MIME_TABLE_ENTRY(hal_htm_entry, "htm",
96                                        "text/html; charset=iso-8859-1");
97 CYG_HTTPD_MIME_TABLE_ENTRY(hal_html_entry, "html", 
98                                        "text/html; charset=iso-8859-1");
99 CYG_HTTPD_MIME_TABLE_ENTRY(hal_gif_entry, "gif", "image/gif");
100 CYG_HTTPD_MIME_TABLE_ENTRY(hal_jpg_entry, "jpg", "image/jpg");
101 CYG_HTTPD_MIME_TABLE_ENTRY(hal_png_entry, "png", "image/png");
102 CYG_HTTPD_MIME_TABLE_ENTRY(hal_css_entry, "css", "text/css");
103 CYG_HTTPD_MIME_TABLE_ENTRY(hal_js_entry, "js", "application/x-javascript");
104
105 void 
106 cyg_httpd_send_error(cyg_int32 err_type)
107 {
108     httpstate.status_code = err_type;
109     
110     // Errors pages close the socket and are never cached.
111     httpstate.mode |= CYG_HTTPD_MODE_NO_CACHE;
112
113 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
114     diag_printf("Sending error: %d\n", err_type);
115 #endif    
116
117 #ifdef CYGOPT_NET_ATHTTPD_USE_FS
118     // Check if the user has defines his own error pages.
119     struct stat sp;
120     char file_name[CYG_HTTPD_MAXPATH];
121     strcpy(file_name, CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR);
122     if (file_name[strlen(file_name)-1] != '/')
123         strcat(file_name, "/");
124     strcat(file_name, CYGDAT_NET_ATHTTPD_SERVEROPT_ERRORDIR);
125     if (file_name[strlen(file_name)-1] != '/')
126         strcat(file_name, "/");
127     sprintf(file_name + strlen(file_name), "error_%d.html", err_type);
128     cyg_httpd_cleanup_filename(file_name);
129     cyg_int32 rc = stat(file_name, &sp);
130     if (rc == 0)
131     {
132         char *extension = rindex(file_name, '.');
133         if (extension == NULL)
134             // No extension in the file name.
135             httpstate.mime_type = 0;
136         else
137             httpstate.mime_type = cyg_httpd_find_mime_string(++extension);
138
139         httpstate.payload_len  = sp.st_size;
140         cyg_int32 header_length = cyg_httpd_format_header();
141         cyg_httpd_write(httpstate.outbuffer, header_length);
142     
143         // File found.
144         FILE *fp = fopen(file_name, "r");
145         if(fp == NULL)
146             return;
147
148         ssize_t payload_size = fread(httpstate.outbuffer, 
149                                      1, 
150                                      CYG_HTTPD_MAXOUTBUFFER, 
151                                      fp);
152         while (payload_size > 0)
153         {
154             ssize_t bytes_written = cyg_httpd_write_chunked(httpstate.outbuffer, 
155                                                             payload_size);
156             if (bytes_written != payload_size)
157                 break;
158
159             payload_size = fread(httpstate.outbuffer, 
160                                  1, 
161                                  CYG_HTTPD_MAXOUTBUFFER, 
162                                  fp);
163         }
164
165         fclose(fp);
166         return;
167     }
168 #endif    
169
170     // Because the size of the frame is not known upfront (every error message
171     //  is different and thus has different length) we use chunked frames to
172     //  send the message out.
173 #if defined(CYGOPT_NET_ATHTTPD_CLOSE_CHUNKED_CONNECTIONS)
174     httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
175 #endif
176     
177     httpstate.mode |= CYG_HTTPD_MODE_TRANSFER_CHUNKED;
178     httpstate.status_code = err_type;
179     httpstate.last_modified = -1;
180     httpstate.mime_type = "text/html";
181     cyg_int32 header_length = cyg_httpd_format_header();
182     cyg_httpd_write(httpstate.outbuffer, header_length);
183     
184     // If no file has been defined, send a simple notification. We must use
185     //  chunked frames, because we do not know upfron the length of the
186     //  packet we have to send.
187     strcpy(httpstate.outbuffer,
188            "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n");
189     switch (err_type)
190     {
191     case CYG_HTTPD_STATUS_MOVED_PERMANENTLY:
192         strcat(httpstate.outbuffer,
193                "<html><head><title>301 Moved Permanently</title></head>\r\n"
194                "<body><h1>Moved Permanently</h1>\r\n"
195                "<p>The document has moved <a href=\"");
196         strcat(httpstate.outbuffer, httpstate.url);
197         strcat(httpstate.outbuffer, "\">here</a>.\r\n");
198         break;
199     case CYG_HTTPD_STATUS_MOVED_TEMPORARILY:
200         strcat(httpstate.outbuffer, 
201                "<html><head><title>302 Found</title></head>\r\n"
202                "<body><h1>Redirect</h1>\r\n"
203                "<p>Please continue <a href=\"");
204         strcat(httpstate.outbuffer, httpstate.url);
205         strcat(httpstate.outbuffer, "\">here</a>.\r\n");
206         break;
207     case CYG_HTTPD_STATUS_NOT_AUTHORIZED:
208         strcat(httpstate.outbuffer, 
209                "<html><head><title>401 Not Authorized</title></head>\r\n");
210         strcat(httpstate.outbuffer, 
211                "<body><p>Authorization required to access this URL.</p>\r\n");
212         break;    
213     case CYG_HTTPD_STATUS_NOT_MODIFIED:
214         cyg_httpd_end_chunked();
215         return;
216     case CYG_HTTPD_STATUS_NOT_FOUND:
217         strcat(httpstate.outbuffer, 
218                "<html><head><title>404 Not Found</title></head>\r\n");
219         sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
220                 "<p>The requested URL: %s was not found on this server</p>\r\n",
221                 httpstate.url);
222         break;
223     case CYG_HTTPD_STATUS_SYSTEM_ERROR:
224         strcat(httpstate.outbuffer, 
225                "<html><head><title>500 Server Error</title></head>\r\n");
226         strcat(httpstate.outbuffer, 
227                "<p>The server encountered an unexpected condition that "
228                "prevented it from fulfilling the request"
229                " by the client</p>\r\n");
230         break;
231     case CYG_HTTPD_STATUS_NOT_IMPLEMENTED:
232         strcat(httpstate.outbuffer, 
233                "<html><head><title>501 Not Implemented</title></head>\r\n");
234         strcat(httpstate.outbuffer, 
235                "<p>The method requested is not implemented</p>\r\n");
236         break;
237     default:
238         strcat(httpstate.outbuffer, 
239                "<html><head><title>400 Bad Request</title></head>\r\n");
240         strcat(httpstate.outbuffer, 
241                "<p>Bad request</p>\r\n");
242         break;
243     }
244     
245     sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
246             "<hr>%s at %d.%d.%d.%d Port %d\r\n</body></html>\r\n",
247             CYGDAT_NET_ATHTTPD_SERVEROPT_SERVERID,
248             httpstate.host[0],
249             httpstate.host[1],
250             httpstate.host[2],
251             httpstate.host[3],
252             CYGNUM_NET_ATHTTPD_SERVEROPT_PORT);
253     
254     cyg_httpd_write_chunked(httpstate.outbuffer, strlen(httpstate.outbuffer));
255     cyg_httpd_end_chunked();
256 }
257
258 // Return a time_t that is always UTC (aka GMT).
259 time_t
260 cyg_httpd_parse_date(char *time)
261 {
262     int    i;
263     char   month[4];
264     struct tm tm_mod;
265
266     // We are going to get rid of the day of the week. This is always the first
267     //  part of the string, separated by a blank.
268     time = strchr( time, ' ');
269     if ( time == NULL)
270         return 0;
271     time++;
272
273     /// RFC1123. The date is in the format: Sun, 06 Nov 1994 08:49:37 GMT.
274     cyg_int32 rc = sscanf(time,
275                           "%2d %3s %4d %2d:%2d:%2d GMT",
276                           &tm_mod.tm_mday,
277                           month,
278                           &tm_mod.tm_year,
279                           &tm_mod.tm_hour,
280                           &tm_mod.tm_min,
281                           &tm_mod.tm_sec);
282     if (rc != 6)
283     {
284         // RFC1036. The date is in the format: Sunday, 06-Nov-94 08:49:37 GMT.
285         rc = sscanf(time,
286                     "%2d-%3s-%2d %2d:%2d:%2d GMT",
287                     &tm_mod.tm_mday,
288                     month,
289                     &tm_mod.tm_year,
290                     &tm_mod.tm_hour,
291                     &tm_mod.tm_min,
292                     &tm_mod.tm_sec);
293         if (rc != 6)
294         {
295             // asctime().
296             rc = sscanf(time,"%3s %2d %2d:%2d:%2d %4d",
297                         month,
298                         &tm_mod.tm_mday,
299                         &tm_mod.tm_hour,
300                         &tm_mod.tm_min,
301                         &tm_mod.tm_sec,
302                         &tm_mod.tm_year);
303             if (rc != 6)
304                 return 0;
305         }
306     }
307
308     for (i = 0; i < sizeof(month_of_year); i++)
309         if (strcmp(month, month_of_year[i]) == 0)
310         {
311             tm_mod.tm_mon = i;
312             if (tm_mod.tm_year > 1900)
313                 tm_mod.tm_year -= 1900;
314             return mktime(&tm_mod);
315         }
316
317     return 0;
318 }
319
320 // Finds the mime string into the mime_table associated with a specific 
321 //  extension. Returns the MIME type to send in the header, or NULL if the
322 //  extension is not in the table.
323 char*
324 cyg_httpd_find_mime_string(char *ext)
325 {
326     cyg_httpd_mime_table_entry *entry = cyg_httpd_mime_table;
327
328     while (entry != cyg_httpd_mime_table_end)
329     {
330         if (!strcmp((const char*)ext, entry->extension))
331             return entry->mime_string;
332         entry++;
333     }
334             
335     return (char*)0;
336 }
337
338 void
339 cyg_httpd_cleanup_filename(char *filename)
340 {
341     char *src = strstr(filename, "//");
342     while (src != 0)
343     {
344         strcpy(src + 1, src + 2);
345         src = strstr(filename, "//");
346     }
347
348     src = strstr(filename, "/./");
349     while (src != 0)
350     {
351         strcpy(src + 1, src + 3);
352         src = strstr(filename, "/./");
353     }
354
355     src = strstr(filename, "/../");
356     while (src != 0)
357     {
358         char *comp1 = filename, *comp2 = filename;
359
360         // Search the path component before this redirection.
361         while ((comp1 = strchr(comp1, '/')) != src)
362             comp2 = ++comp1;
363
364         strcpy(comp2, src + 4);
365         src = strstr(filename, "/../");
366     }
367 }
368
369 cyg_int32
370 cyg_httpd_initialize(void)
371 {
372     httpstate.post_data = NULL;
373     httpstate.needs_auth = (cyg_httpd_auth_table_entry *)0;
374     return 0;
375 }
376
377 void
378 cyg_httpd_append_homepage(char *root)
379 {
380 #ifdef CYGOPT_NET_ATHTTPD_USE_FS
381     struct stat sp;
382     cyg_int32 i;
383
384     cyg_int32 root_len = strlen(root);
385     for (i = 0; i < sizeof(home_pages)/sizeof(char*); i++)
386     {
387         root[root_len] = '\0';
388         sprintf(root + root_len, "%s", home_pages[i]);
389         cyg_int32 rc = stat(root, &sp);
390         if (rc == 0)
391             return;
392     }
393     root[root_len] = 0;
394 #endif    
395     
396 #ifdef CYGDAT_NET_ATHTTPD_ALTERNATE_HOME    
397     if (strcmp(root, "/") == 0)
398         // The client is trying to open the main index file.
399         strcat(root, CYGDAT_NET_ATHTTPD_ALTERNATE_HOME);
400 #endif    
401 }
402
403 #ifdef CYGOPT_NET_ATHTTPD_USE_FS
404 void
405 cyg_httpd_send_file(char *name)
406 {
407     cyg_int32  err;
408     FILE      *fp;
409     struct stat sp;
410     char       file_name[CYG_HTTPD_MAXPATH];
411
412     strcpy(file_name, CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR);
413     if (file_name[strlen(file_name)-1] != '/')
414         strcat(file_name, "/");
415     strcat(file_name, name);
416     cyg_httpd_cleanup_filename(file_name);
417         
418     // Check if the file is in the file system. This will also give us the
419     //  size of the file, to be used in the HTTP header.
420     cyg_int32 rc = stat(file_name, &sp);
421     if (rc < 0)
422     {
423         // Before giving up, we make a last ditch attempt at finding a file
424         //  within the internal resources of the server. The user can add
425         //  his/her own files to the table.
426         cyg_httpd_ires_table_entry *p = cyg_httpd_find_ires(name);
427         if (p != NULL)
428         {
429 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
430             diag_printf("Sending Internal Resource: %s\n", name);
431 #endif    
432             cyg_httpd_send_ires(p);
433         }    
434         else    
435             cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
436         return;
437     }
438     
439     if (S_ISDIR(sp.st_mode))
440     {
441         char tmp_url[CYG_HTTPD_MAXURL];
442         strcpy(tmp_url, httpstate.url);
443         // Directories need a trialing slash, and if missing, we'll redirect
444         //  the client to the right URL. This is called (appropriately
445         //  enough) "Trailing-Slash Redirection". 
446         if (name[strlen(name)-1] != '/')
447         {
448             sprintf(httpstate.url,
449                     "http://%d.%d.%d.%d:%d%s/",
450                     httpstate.host[0],
451                     httpstate.host[1],
452                     httpstate.host[2],
453                     httpstate.host[3],
454                     CYGNUM_NET_ATHTTPD_SERVEROPT_PORT,
455                     tmp_url);
456             cyg_httpd_send_error(CYG_HTTPD_STATUS_MOVED_PERMANENTLY);
457             return;
458         }
459
460         // We are going to try to locate an index page in the directory we got
461         //  in the URL. 
462         cyg_httpd_append_homepage(file_name);
463         if (file_name[strlen(file_name)-1] == '/')
464         {
465 #ifdef CYGOPT_NET_ATHTTPD_USE_DIRLIST
466             // No home page found, we are sending a directory listing.
467             cyg_httpd_send_directory_listing(name);
468 #else
469             cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
470 #endif
471             return;
472         }
473         stat(file_name, &sp);
474     }
475     
476     httpstate.last_modified = sp.st_mtime;
477
478     // Let's see if we luck out and can send a 304.
479     if ((httpstate.modified_since != -1) && 
480                    (httpstate.modified_since >= httpstate.last_modified))
481     {                   
482         cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_MODIFIED);
483         return;
484     }    
485     else    
486         httpstate.status_code = CYG_HTTPD_STATUS_OK;
487
488     // Here we'll look for an extension to the file. Consider the case where
489     //  there might be more than one dot in the file name. We'll look for just
490     //  the last one, then we'll check the extension.
491     char *extension = rindex(file_name, '.');
492     if (extension == NULL)
493         httpstate.mime_type = 0;
494     else    
495         httpstate.mime_type = cyg_httpd_find_mime_string(++extension);
496
497     httpstate.payload_len  = sp.st_size;
498     httpstate.mode &= ~CYG_HTTPD_MODE_NO_CACHE;
499     cyg_int32 payload_size = cyg_httpd_format_header();
500     if ((httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) != 0)
501     {                 
502 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
503         diag_printf("Sending header only for URL: %s\n", file_name);
504 #endif    
505         send(httpstate.sockets[httpstate.client_index].descriptor, 
506              httpstate.outbuffer, 
507              payload_size,
508              0);
509         return;
510     }
511
512 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
513     diag_printf("Sending file: %s\n", file_name);
514 #endif    
515     fp = fopen(file_name, "r");
516     if (fp == NULL)
517     {
518         // We should really read errno and send messages accordingly...
519         cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
520         return;
521     }
522     
523     // Fill up the rest of the buffer and send it out.
524     cyg_int32 bread = fread(httpstate.outbuffer + strlen(httpstate.outbuffer),
525                             1, 
526                             CYG_HTTPD_MAXOUTBUFFER - payload_size,
527                             fp);
528     cyg_httpd_write(httpstate.outbuffer, payload_size + bread);
529
530     ssize_t bytes_written = 0;
531     sp.st_size -= bread;
532     while (bytes_written < sp.st_size)
533     {
534         bread = fread(httpstate.outbuffer, 1, CYG_HTTPD_MAXOUTBUFFER, fp);
535         bytes_written += cyg_httpd_write(httpstate.outbuffer, bread);
536     }    
537     
538     err = fclose(fp);
539     if (err < 0)
540         cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
541 }
542 #endif
543
544 cyg_int32
545 cyg_httpd_format_header(void)
546 {
547     sprintf(httpstate.outbuffer, "HTTP/1.1 %d", httpstate.status_code);
548     time_t time_val = time(NULL);
549     
550     // Error messages (i.e. with status other than OK, automatically add
551     //  the no-cache header.
552     switch (httpstate.status_code)
553     {
554     case CYG_HTTPD_STATUS_MOVED_PERMANENTLY:
555         strcat(httpstate.outbuffer, " Moved Permanently\r\n");
556         strcat(httpstate.outbuffer, "Location: ");
557         strcat(httpstate.outbuffer, httpstate.url);
558         strcat(httpstate.outbuffer, "\r\n");
559         sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
560                 "Content-Length: %d\r\n",
561                 httpstate.payload_len);
562         break;
563     case CYG_HTTPD_STATUS_MOVED_TEMPORARILY:
564         strcat(httpstate.outbuffer, " Found\r\n");
565         strcat(httpstate.outbuffer, "Location: ");
566         strcat(httpstate.outbuffer, httpstate.url);
567         strcat(httpstate.outbuffer, "\r\n");
568         sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
569                 "Content-Length: %d\r\n",
570                 httpstate.payload_len);
571         break;
572 #ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
573     case CYG_HTTPD_STATUS_NOT_AUTHORIZED:
574         // A 401 error closes the connection right away.
575         httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
576         strcat(httpstate.outbuffer, " Not Authorized\r\n");
577         
578         // Here we should set the proper header based on the authentication
579         //  required (httpstate.needs_authMode) but for now, with only
580         //  Basic Authentication supported, there is no need to do so.
581         if (httpstate.needs_auth->auth_mode == CYG_HTTPD_AUTH_BASIC)
582         {
583             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
584                     "WWW-Authenticate: Basic realm=\"%s\"\r\n",
585                     httpstate.needs_auth->auth_domainname);
586         }
587         else             
588         {
589             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
590                      "WWW-Authenticate: Digest realm=\"%s\", ",
591                      httpstate.needs_auth->auth_domainname);
592             strftime(cyg_httpd_md5_nonce, 
593                      33,
594                      TIME_FORMAT_RFC1123,
595                      gmtime(&time_val));
596             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
597                     "nonce=\"%s\", ", cyg_httpd_md5_nonce);
598             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
599                     "opaque=\"%s\", ", 
600                     CYG_HTTPD_MD5_AUTH_OPAQUE);
601             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
602                     "stale=false, algorithm=%s, qop=\"%s\"\r\n",
603                     CYG_HTTPD_MD5_AUTH_NAME,
604                     CYG_HTTPD_MD5_AUTH_QOP);
605         }
606         break;
607 #endif
608     case CYG_HTTPD_STATUS_NOT_MODIFIED:
609         strcat(httpstate.outbuffer, " Not Modified\r\n");
610         break;
611     case CYG_HTTPD_STATUS_NOT_FOUND:
612         strcat(httpstate.outbuffer, " Not Found\r\n");
613         sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
614                 "Content-Length: %d\r\n", 
615                 httpstate.payload_len);
616         break;
617     case CYG_HTTPD_STATUS_METHOD_NOT_ALLOWED:
618         strcat(httpstate.outbuffer, " Method Not Allowed\r\n");
619         break;
620     default:
621         strcat(httpstate.outbuffer, " OK\r\n");
622         if ((httpstate.mode & CYG_HTTPD_MODE_TRANSFER_CHUNKED) == 0)
623             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
624                     "Content-Length: %d\r\n", 
625                     httpstate.payload_len);
626         break;
627     }
628
629     strcat(httpstate.outbuffer, "Date: ");
630     strftime(httpstate.outbuffer + strlen(httpstate.outbuffer), 
631              CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
632              TIME_FORMAT_RFC1123,
633              gmtime(&time_val));
634     strcat(httpstate.outbuffer, "\r\n");
635     
636     sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer), 
637             "Server: %s\r\n", 
638             CYGDAT_NET_ATHTTPD_SERVEROPT_SERVERID);
639     
640     if (httpstate.mode & CYG_HTTPD_MODE_CLOSE_CONN)
641         strcat(httpstate.outbuffer, "Connection: close\r\n");
642     else
643         strcat(httpstate.outbuffer, "Connection: keep-alive\r\n");
644
645     // When we cannot find the appropriate MIME type, we'll send a default type.
646     if (httpstate.mime_type == 0)
647         httpstate.mime_type = CYGDAT_NET_ATHTTPD_DEFAULT_MIME_TYPE;
648     sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
649             "Content-Type: %s\r\n", 
650             httpstate.mime_type);
651
652     if (httpstate.mode & CYG_HTTPD_MODE_TRANSFER_CHUNKED)
653         strcat(httpstate.outbuffer, "Transfer-Encoding: chunked\r\n");
654
655     if (httpstate.mode & CYG_HTTPD_MODE_NO_CACHE)
656         strcat(httpstate.outbuffer, "Cache-Control: no-cache\r\n");
657         
658     if (httpstate.last_modified != -1)
659     {
660         time_val = httpstate.last_modified;
661         strcat(httpstate.outbuffer, "Last-Modified: "); 
662         strftime(httpstate.outbuffer + strlen(httpstate.outbuffer), 
663                  CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
664                  TIME_FORMAT_RFC1123,
665                  gmtime(&time_val));
666         strcat(httpstate.outbuffer, "\r\n");
667
668 #if (CYGOPT_NET_ATHTTPD_DOCUMENT_EXPIRATION_TIME != 0)                 
669         time_val += CYGOPT_NET_ATHTTPD_DOCUMENT_EXPIRATION_TIME;
670         strcat(httpstate.outbuffer, "Expires: "); 
671         strftime(httpstate.outbuffer + strlen(httpstate.outbuffer), 
672                  CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
673                  TIME_FORMAT_RFC1123,
674                  gmtime(&time_val));
675         strcat(httpstate.outbuffer, "\r\n");
676 #endif
677     }        
678                  
679     // There must be 2 carriage returns between the header and the body, 
680     //  so if you modify this function make sure that there is another 
681     //  CRLF already terminating the buffer thus far.
682     strcat(httpstate.outbuffer, "\r\n");
683     return strlen(httpstate.outbuffer);
684 }
685
686 void
687 cyg_httpd_handle_method_GET(void)
688 {
689 #if defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER) ||\
690                              defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL)
691     // If the URL is a CGI script, there is a different directory...
692     if (httpstate.url[0] == '/' &&
693                     !strncmp(httpstate.url + 1, 
694                               CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR, 
695                               strlen(CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR)))
696     {                              
697         cyg_httpd_exec_cgi();
698         return;
699     }
700     // If the OBJLOADER package is not loaded, then the request for a library
701     //  will likely generate a 404.
702 #endif    
703
704     // User defined handlers take precedence over other forms of response.
705     handler h = cyg_httpd_find_handler();
706     if (h != 0)
707     {
708         h(&httpstate);
709         return;
710     }
711     
712
713 #ifdef CYGOPT_NET_ATHTTPD_USE_FS
714     // No handler, we'll redirect to the file system.
715     cyg_httpd_send_file(httpstate.url);
716 #else
717     // If we do not have a file system, we look for the file within the 
718     //  internal resources of the server. The user can add his/her own files
719     //  to the table.
720     if (strcmp(httpstate.url, "/") == 0)
721     {
722         int i;
723         cyg_httpd_ires_table_entry *p;
724         for (i = 0; i < sizeof(home_pages)/sizeof(char*); i++)
725         {
726             httpstate.url[1] = '\0';
727             strcat(httpstate.url, home_pages[i]);
728             p = cyg_httpd_find_ires(httpstate.url);
729             if (p != NULL)
730             {
731                 cyg_httpd_send_ires(p);
732                 return;
733             }    
734         }        
735     }
736     else
737     {
738         cyg_httpd_ires_table_entry *p = cyg_httpd_find_ires(httpstate.url);
739         if (p != NULL)
740         {
741             cyg_httpd_send_ires(p);
742             return;
743         }    
744     }        
745     cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
746 #endif    
747 }
748
749 char*
750 cyg_httpd_get_URL(char* p)
751 {
752     char* dest = httpstate.url;
753
754     // First get rid of multiple leading slashes.
755     while ((p[0] == '/') && (p[1] == '/'))
756        p++;
757
758     // Store the url, and check if there is a form result in it.
759     while ((*p != ' ') && (*p != '?') &&
760             ((dest - httpstate.url) <= CYG_HTTPD_MAXURL))
761     {
762         // Look for encoded characters in the URL.
763         if (*p == '%') 
764         {
765             p++;
766             cyg_int8 ch = cyg_httpd_from_hex(*p++);
767             if (ch == -1)
768             {
769                 cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
770                 return (char*)0;
771             }
772             *dest = ch << 4;
773             ch = cyg_httpd_from_hex(*p++);
774             if (ch == -1)
775             {
776                 cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
777                 return (char*)0;
778             }
779             *dest += ch;
780             dest++;
781         }
782         else 
783             *dest++ = *p++;
784     }
785
786     // Terminate the file name...
787     *dest = '\0';
788
789     // The URL must start with a leading slash.
790     if (httpstate.url[0] != '/') 
791     {
792         cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
793         return (char*)0;
794     }
795     return p;
796 }
797
798 char*
799 cyg_httpd_parse_POST(char* p)
800 {
801     httpstate.method = CYG_HTTPD_METHOD_POST;
802     char *cp = cyg_httpd_get_URL(p);
803     if (cp == 0)
804         return (char*)0;
805 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
806     diag_printf("POST Request URL: %s\n", httpstate.url);
807 #endif    
808
809     while (*cp++ != '\n');
810     return cp;
811 }
812
813 char*
814 cyg_httpd_parse_GET(char* p)
815 {
816     char *cp = cyg_httpd_get_URL(p);
817     if (cp == 0)
818         return 0;
819 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
820     if ( httpstate.method == CYG_HTTPD_METHOD_GET)
821         diag_printf("GET Request URL: %s\n", httpstate.url);
822     else    
823         diag_printf("HEAD Request URL: %s\n", httpstate.url);
824 #endif    
825
826     if (*cp == '?')
827         // If we have a GET header with form variables we'll get the
828         //  variables out of it and store them in the variable table.
829         // Can we assume that HEAD request can have form variables?
830         // That will be a yes until I learn otherwise.
831         cp = cyg_httpd_store_form_data(++cp);
832
833     // Run to end of line.
834     while (*cp++ != '\n');
835     return cp;
836 }
837
838 char*
839 cyg_httpd_process_header(char *p)
840 {
841 #ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
842     // Clear the previous request's response. The client properly authenticated
843     //  will always reinitialize this variable during the header parsing
844     //  process. This variable is also commandeered to hold the hashed
845     //  username:password duo in the basic authentication.
846     cyg_httpd_md5_response[0] = '\0';
847 #endif
848
849     // The deafult for HTTP 1.1 is keep-alive connections, unless specifically
850     //  closed by the far end.
851     httpstate.mode &= ~(CYG_HTTPD_MODE_CLOSE_CONN | CYG_HTTPD_MODE_FORM_DATA |\
852                                         CYG_HTTPD_MODE_SEND_HEADER_ONLY);
853     httpstate.modified_since = -1;
854     httpstate.content_len = 0;
855     while (p < httpstate.request_end)
856     {
857         if (strncasecmp("GET ", p, 4) == 0)
858         {
859             // We need separate flags for HEAD and SEND_HEADERS_ONLY since
860             //  we can send a header only even in the case of a GET request
861             //  (as a 304 response.)
862             httpstate.method = CYG_HTTPD_METHOD_GET;
863             httpstate.mode &= ~CYG_HTTPD_MODE_SEND_HEADER_ONLY;
864             p = cyg_httpd_parse_GET(p + 4);
865             if (p ==0)
866                 return (char*)0;
867         }
868         else if (strncasecmp("POST ", p, 5) == 0)
869         {
870             p = cyg_httpd_parse_POST(p + 5);
871             if (p ==0)
872                 return (char*)0;
873         }
874         else if (strncasecmp("HEAD ", p, 5) == 0)
875         {
876             httpstate.method = CYG_HTTPD_METHOD_HEAD;
877             httpstate.mode |= CYG_HTTPD_MODE_SEND_HEADER_ONLY;
878             p = cyg_httpd_parse_GET(p + 5);
879             if (p ==0)
880                 return (char*)0;
881         }
882         else if (strncasecmp(p, "Content-Length: ", 16) == 0)
883         {
884             p = strchr(p, ':') + 2;
885             if (p)
886                 // In the case of a POST request, this is the total length of
887                 //  the payload, which might be spread across several frames.
888                 httpstate.content_len = atoi(p);
889             while (*p++ != '\n');
890         }
891         else if (strncasecmp(p, "Content-Type: ", 14) == 0)
892         {
893             p = strchr(p, ':') + 2;
894             if (p)
895                 // In the case of a POST request, this is the total length of
896                 //  the payload, which might be spread across several frames.
897                 if (strncasecmp(p,
898                                 "application/x-www-form-urlencoded",
899                                 33) == 0)
900                     httpstate.mode |= CYG_HTTPD_MODE_FORM_DATA;
901             while (*p++ != '\n');
902         }
903         else if (strncasecmp("Host:", p, 5) == 0)
904         {
905             p += 5;
906             if (*p == ' ')
907                 p++;
908             sscanf(p,
909                    "%d.%d.%d.%d",
910                    &httpstate.host[0],
911                    &httpstate.host[1],
912                    &httpstate.host[2],
913                    &httpstate.host[3]);
914             while (*p++ != '\n');
915         }
916         else if (strncasecmp("If-Modified-Since:", p, 18) == 0)
917         {
918             p += 18;
919             if ( *p == ' ')
920                 p++;
921             httpstate.modified_since = cyg_httpd_parse_date(p);
922             while (*p++ != '\n');
923         }
924 #ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
925         else if (strncasecmp("Authorization:", p, 14) == 0)
926         {
927             p += 14;
928             while (*p == ' ')
929                 p++;
930             if (strncasecmp("Basic", p, 5) == 0)
931             {
932                 p += 5;
933                 while (*p == ' ')
934                     p++;
935                 cyg_int32 auth_data_length = 0;    
936                 while (*p != '\n') 
937                 {
938                     // We are going to copy only up to 
939                     //  AUTH_STORAGE_BUFFER_LENGTH characters to prevent
940                     //  overflow of the cyg_httpd_md5_response variable.
941                     if (auth_data_length < AUTH_STORAGE_BUFFER_LENGTH)
942                         if ((*p != '\r') && (*p != ' '))
943                             cyg_httpd_md5_response[auth_data_length++] = *p;
944                     p++;
945                 }    
946                 p++;        
947                 cyg_httpd_md5_response[auth_data_length] = '\0';
948             }
949             else if (strncasecmp(p, "Digest", 6) == 0)
950             {
951                 p += 6;
952                 while (*p == ' ')
953                    p++;
954                 while (*p != '\n')
955                 {
956                     if (strncasecmp(p, "realm=", 6) == 0)
957                         p = cyg_httpd_digest_skip(p + 6);
958                     else if (strncasecmp(p, "username=", 9) == 0)
959                         p = cyg_httpd_digest_skip(p + 9);
960                     else if (strncasecmp(p, "nonce=", 6) == 0)
961                         p = cyg_httpd_digest_skip(p + 6);
962                     else if (strncasecmp(p, "response=", 9) == 0)
963                         p = cyg_httpd_digest_data(cyg_httpd_md5_response, 
964                                                   p + 9);
965                     else if (strncasecmp(p, "cnonce=", 7) == 0)
966                         p = cyg_httpd_digest_data(cyg_httpd_md5_cnonce, p + 7);
967                     else if (strncasecmp(p, "qop=", 4) == 0)
968                         p = cyg_httpd_digest_skip(p + 4);
969                     else if (strncasecmp(p, "nc=", 3) == 0)
970                         p = cyg_httpd_digest_data(cyg_httpd_md5_noncecount, 
971                                                   p + 3);
972                     else if (strncasecmp(p, "algorithm=", 10) == 0)
973                         p = cyg_httpd_digest_skip(p + 10);
974                     else if (strncasecmp(p, "opaque=", 7) == 0)
975                         p = cyg_httpd_digest_skip(p + 7);
976                     else if (strncasecmp(p, "uri=", 4) == 0)
977                         p = cyg_httpd_digest_skip(p + 4);
978                     else
979                         p++;    
980                 }
981                 p++;
982             }    
983             else
984                 while (*p++ != '\n');
985         }   
986 #endif // CYGOPT_NET_ATHTTPD_USE_AUTH
987         else if (strncasecmp(p, "Connection:", 11) == 0)
988         {
989             p += 11;
990             while (*p == ' ')
991                p++;
992             if (strncasecmp(p, "close", 5) == 0)
993                 httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
994             while (*p++ != '\n');
995         }
996         else
997             // We'll just dump the rest of the line and move on to the next.
998             while (*p++ != '\n');
999     }
1000     return p;
1001 }
1002
1003 void
1004 cyg_httpd_process_method(void)
1005 {
1006     char* p = httpstate.inbuffer;
1007     
1008     // Some browsers send an extra '\r\n' after the POST data that is not
1009     //  accounted in the "Content-Length:" field. We are going to junk all
1010     //  the leading returns and line carriages we find.
1011     while ((*p == '\r') || (*p =='\n'))
1012         p++;
1013
1014     while (*p != '\0')
1015     {
1016         p = cyg_httpd_process_header(p);
1017         if (p == 0)
1018             return;
1019
1020 #ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
1021         // Let's check that the requested URL is not inside some directory that 
1022         //  needs authentication.
1023         cyg_httpd_auth_table_entry* auth = 
1024                                   cyg_httpd_is_authenticated(httpstate.url);
1025         if (auth != 0)
1026         {
1027             cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_AUTHORIZED);
1028             return;
1029         }
1030 #endif
1031         switch (httpstate.method)
1032         {
1033             case CYG_HTTPD_METHOD_GET:
1034             case CYG_HTTPD_METHOD_HEAD:
1035                 cyg_httpd_handle_method_GET();
1036                 break;
1037             case CYG_HTTPD_METHOD_POST:
1038                 cyg_httpd_handle_method_POST();
1039                 return;
1040                 break;
1041             default:
1042                 cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_IMPLEMENTED);
1043                 return;
1044             break;
1045         }
1046     }    
1047 }
1048