ssi.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2004 by Ole Reinhardt <ole.reinhardt@embedded-it.de>. 
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 3. Neither the name of the copyright holders nor the names of
00015  *    contributors may be used to endorse or promote products derived
00016  *    from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00019  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00021  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00022  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00023  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00024  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00025  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00026  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00027  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00028  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00029  * SUCH DAMAGE.
00030  *
00031  * For additional information see http://www.ethernut.de/
00032  */
00033  
00034 /*
00035  * $Log: ssi.c,v $
00036  * Revision 1.3  2008/02/15 17:09:03  haraldkipp
00037  * Quick hack provided by Niels. No idea what this is for, but
00038  * according to the author it is a dirty solution. We urgently
00039  * need it to get the Elektor Radio working. Sorry in advance
00040  * for any trouble this change may cause.
00041  *
00042  * Revision 1.2  2006/03/16 15:25:39  haraldkipp
00043  * Changed human readable strings from u_char to char to stop GCC 4 from
00044  * nagging about signedness.
00045  *
00046  * Revision 1.1  2005/08/05 11:22:14  olereinhardt
00047  * Added Server side include support. Initial checkin
00048  *
00049  */  
00050 
00055 
00056 #include <cfg/arch.h>
00057 
00058 #include <io.h>
00059 #include <ctype.h>
00060 #include <stdio.h>
00061 #include <string.h>
00062 #include <stdlib.h>
00063 #include <sys/types.h>
00064 #include <unistd.h>
00065 #include <fcntl.h>
00066 
00067 
00068 #include <sys/heap.h>
00069 #include <sys/version.h>
00070 
00071 #include <pro/httpd.h>
00072 #include <pro/ssi.h>
00073 #include "dencode.h"
00074 
00075 #define BUFSIZE 512
00076 
00077 #define MIN(a,b) (a<b?a:b)
00078 
00079 #define SSI_TYPE_FILE    0x01
00080 #define SSI_TYPE_VIRTUAL 0x02
00081 #define SSI_TYPE_EXEC    0x03
00082 
00083 
00094 static void NutSsiProcessFile(FILE * stream, char *filename)
00095 {
00096     int fd;
00097     int n;
00098     char *data;
00099     int size;
00100     long file_len;
00101 
00102     fd = _open(filename, _O_BINARY | _O_RDONLY);
00103 
00104     if (fd == -1) {                     // No such file found... send a 404 string.
00105         fprintf_P(stream, PSTR("404 Not found: %s\n"), filename);
00106         return;
00107     }
00108     
00109     file_len = _filelength(fd);
00110     
00111     size = 512;
00112     if ((data = NutHeapAlloc(size)) != 0) {
00113         while (file_len) {
00114             if (file_len < 512L)
00115                 size = (int) file_len;
00116 
00117             n = _read(fd, data, size);
00118             if (fwrite(data, 1, n, stream) == 0)
00119                 break;
00120             file_len -= (long) n;
00121         }
00122         NutHeapFree(data);
00123     }
00124 
00125     _close(fd);
00126 }
00127 
00128 static void DestroyRequestInfo(REQUEST * req)
00129 {
00130     if (req->req_url)
00131         NutHeapFree(req->req_url);
00132     if (req->req_query)
00133         NutHeapFree(req->req_query);
00134     if (req->req_type)
00135         NutHeapFree(req->req_type);
00136     if (req->req_cookie)
00137         NutHeapFree(req->req_cookie);
00138     if (req->req_auth)
00139         NutHeapFree(req->req_auth);
00140     if (req->req_agent)
00141         NutHeapFree(req->req_agent);
00142     if (req->req_qptrs)
00143         NutHeapFree(req->req_qptrs);
00144     NutHeapFree(req);
00145 }
00146 
00160 static void NutSsiProcessVirtual(FILE * stream, char *url, char* http_root, REQUEST *orig_req)
00161 {
00162     int fd;
00163     int i;
00164     int n;
00165     char *data;
00166     int size;
00167     long file_len;
00168     char *filename = NULL;
00169     void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
00170     
00171     
00172     char *cp;
00173     REQUEST * req;
00174 
00175     if (NutDecodePath(url) == 0) {
00176         fprintf_P(stream, PSTR("400 Bead request\n"));
00177         return;
00178     }
00179     
00180     /*
00181      * Process CGI.
00182      */
00183     if (strncasecmp(url, "cgi-bin/", 8) == 0) {
00184         if ((req = NutHeapAllocClear(sizeof(REQUEST))) == 0) {
00185             fprintf_P(stream, PSTR("500 Internal error\n"));
00186             return;
00187         }
00188         req->req_method = METHOD_GET;
00189         req->req_version = orig_req->req_version;
00190         req->req_length = 0;
00191         
00192         if (orig_req->req_agent != NULL) {
00193             if ((req->req_agent = NutHeapAlloc((strlen(orig_req->req_agent) + 1))) == 0) {
00194                 fprintf_P(stream, PSTR("500 Internal error\n"));
00195                 DestroyRequestInfo(req);
00196                 return;
00197             }
00198             strcpy(req->req_agent, orig_req->req_agent);
00199         }
00200         if (orig_req->req_cookie!= NULL) {
00201             if ((req->req_cookie = NutHeapAlloc((strlen(orig_req->req_cookie) + 1))) == 0) {
00202                 fprintf_P(stream, PSTR("500 Internal error\n"));
00203                 DestroyRequestInfo(req);
00204                 return;
00205             }
00206             strcpy(req->req_cookie, orig_req->req_cookie);
00207         }
00208         if ((cp = strchr(url, '?')) != 0) {
00209             *cp++ = 0;
00210             if ((req->req_query = NutHeapAlloc(512)) == 0) {
00211                 fprintf_P(stream, PSTR("500 Internal error\n"));
00212                 DestroyRequestInfo(req);
00213                 return;
00214             }
00215             strcpy(req->req_query, cp);
00216             if (strcmp(req->req_query, "$QUERY_STRING") == 0){
00217                 req->req_query[0] = 0;
00218                 for (i = 0; i < (orig_req->req_numqptrs*2); i++) {
00219                     if(i)
00220                         strcat (req->req_query, "&");
00221                     strcat (req->req_query, orig_req->req_qptrs[i]);
00222                     strcat (req->req_query, "=");
00223                     i++;
00224                     strcat (req->req_query, orig_req->req_qptrs[i]);
00225                 }
00226             }
00227             NutHttpProcessQueryString(req);
00228         }
00229         if ((req->req_url = NutHeapAlloc(strlen(url) + 1)) == 0) {
00230             fprintf_P(stream, PSTR("500 Internal error\n"));
00231             DestroyRequestInfo(req);
00232             return;
00233         }
00234         strcpy(req->req_url, url);
00235 
00236     
00237         NutCgiProcessRequest(stream, req);
00238         DestroyRequestInfo(req);
00239         return;
00240     }
00241     
00242     /*
00243      * Process file.
00244      */
00245     if (http_root) {
00246         filename = NutHeapAlloc(strlen(http_root) + strlen(url) + 1);
00247         strcpy(filename, http_root);
00248     } else {
00249         filename = NutHeapAlloc(strlen(url) + 6);
00250         strcpy(filename, "UROM:");
00251     }
00252     
00253     strcat(filename, url);
00254 
00255     handler = NutGetMimeHandler(filename);
00256 
00257     fd = _open(filename, _O_BINARY | _O_RDONLY);
00258     NutHeapFree(filename);
00259     if (fd == -1) {                     // Search for index.html
00260         char *index;
00261         u_short urll;
00262 
00263 
00264         urll = strlen(url);
00265         if ((index = NutHeapAllocClear(urll + 12)) == 0) {
00266             fprintf_P(stream, PSTR("500 Internal error\n"));
00267             return;
00268         }
00269         if (urll)
00270             strcpy(index, url);
00271         if (urll && index[urll - 1] != '/')
00272             strcat(index, "/");
00273         strcat(index, "index.html");
00274 
00275         if (http_root) {
00276             filename = NutHeapAlloc(strlen(http_root) + strlen(index) + 1);
00277             strcpy(filename, http_root);
00278         } else {
00279             filename = NutHeapAlloc(strlen(index) + 6);
00280             strcpy(filename, "UROM:");
00281         }
00282         strcat(filename, index);
00283 
00284         NutHeapFree(index);
00285 
00286         handler = NutGetMimeHandler(filename);
00287 
00288         fd = _open(filename, _O_BINARY | _O_RDONLY);
00289         NutHeapFree(filename);
00290         if (fd == -1) {                 // We have no index.html. But perhaps an index.shtml?
00291             urll = strlen(url);
00292             if ((index = NutHeapAllocClear(urll + 13)) == 0) {
00293                 fprintf_P(stream, PSTR("500 Internal error\n"));
00294                 return;
00295             }
00296             if (urll)
00297                 strcpy(index, url);
00298             if (urll && index[urll - 1] != '/')
00299                 strcat(index, "/");
00300             strcat(index, "index.shtml");
00301     
00302             if (http_root) {
00303                 filename = NutHeapAlloc(strlen(http_root) + strlen(index) + 1);
00304                 strcpy(filename, http_root);
00305             } else {
00306                 filename = NutHeapAlloc(strlen(index) + 6);
00307                 strcpy(filename, "UROM:");
00308             }
00309             strcat(filename, index);
00310     
00311             NutHeapFree(index);
00312 
00313             handler = NutGetMimeHandler(filename);
00314 
00315             fd = _open(filename, _O_BINARY | _O_RDONLY);
00316             NutHeapFree(filename);
00317             if (fd == -1) {            // Non of both found... send a 404
00318                 fprintf_P(stream, PSTR("404 Not found: %s\n"), filename);
00319                 return;
00320             }
00321         }
00322     }
00323     
00324     file_len = _filelength(fd);
00325     
00326     if (handler == NULL) {
00327         size = 512;                 // If we have not registered a mime handler handle default.
00328         if ((data = NutHeapAlloc(size)) != 0) {
00329             while (file_len) {
00330                 if (file_len < 512L)
00331                     size = (int) file_len;
00332 
00333                 n = _read(fd, data, size);
00334                 if (fwrite(data, 1, n, stream) == 0)
00335                     break;
00336                 file_len -= (long) n;
00337             }
00338             NutHeapFree(data);
00339         }
00340     } else handler(stream, fd, file_len, http_root, orig_req);
00341     _close(fd);
00342 }
00343 
00356 static void NutSsiSkipWhitespace(char *buffer, u_short *pos, u_short end)
00357 {
00358     while ((*pos < end) && (
00359            (buffer[*pos] == '\n') || (buffer[*pos] == '\r') ||
00360            (buffer[*pos] == '\t') || (buffer[*pos] == ' ')))
00361         (*pos) ++;
00362 }
00363 
00382 static u_char NutSsiCheckForSsi(FILE *stream, char *buffer, u_short end, char* http_root, REQUEST *req)
00383 {
00384     u_short pos = 4; // First character after comment start
00385     char * filename;
00386     u_char type;
00387 
00388     pos = 4;
00389     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after comment start
00390     if (pos == end) return 0;
00391     
00392     if (strncasecmp(&buffer[pos], "#include", 8) == 0) { // Search include directive
00393         pos += 8;
00394         type = SSI_TYPE_VIRTUAL;
00395     } else 
00396     if (strncasecmp(&buffer[pos], "#exec", 5) == 0) { // Search include or exec directive
00397         pos += 5;
00398         type = SSI_TYPE_EXEC;
00399     } else return 0;                                // No include or exec found. Skip the rest of this comment...
00400     if (pos >= end) return 0;
00401 
00402     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after #include directive
00403     if (pos == end) return 0;
00404     
00405     if (type == SSI_TYPE_VIRTUAL) {
00406         if (strncasecmp(&buffer[pos], "virtual", 7) == 0) {  // Search virtual directive
00407             pos += 7;
00408         } else                                      // No virtual found. Test for file...
00409         if (strncasecmp(&buffer[pos], "file", 4) == 0) {  // Search file directive
00410             pos += 4;
00411             type = SSI_TYPE_FILE;
00412         } else return 0;                            // No file found. Test for file...
00413     } else {
00414         if (strncasecmp(&buffer[pos], "cgi", 3) == 0) {  // Search cgi directive
00415             pos += 3;
00416         } else return 0;                            // No cgi found. return...
00417     }
00418     if (pos >= end) return 0;
00419     
00420     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after virtual, file or cgi directive
00421     if (pos == end) return 0;
00422 
00423     if (buffer[pos] != '=') return 0;               // check for assertion
00424     pos ++; 
00425 
00426     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after assertion
00427     if (pos == end) return 0;    
00428     
00429     if (buffer[pos] == '"') {                       // Search for filename and pass to output function
00430         pos ++;
00431         if (pos == end) return 0;
00432         filename = &buffer[pos];
00433         while (buffer[pos] != '"') {
00434             pos ++;
00435             if (pos == end) return 0;
00436         }
00437         buffer[pos] = '\0';
00438         switch (type) {
00439             case SSI_TYPE_FILE:
00440                 NutSsiProcessFile(stream, filename);
00441                 break;
00442             case SSI_TYPE_VIRTUAL:
00443                 NutSsiProcessVirtual(stream, filename, http_root, req);
00444                 break;
00445             case SSI_TYPE_EXEC:
00446                 NutSsiProcessVirtual(stream, filename, http_root, req);
00447                 break;
00448         }
00449     }
00450     return 1;
00451 }
00452 
00472 static void NutHttpProcessSHTML(FILE * stream, int fd, int file_len, char* http_root, REQUEST *req)
00473 {
00474     char * buffer;
00475     u_char in_comment;
00476     int buffsize;
00477     int fpos;
00478     int n;
00479     char *index;
00480     u_char found;
00481     buffsize = MIN(BUFSIZE, file_len);
00482     buffer = NutHeapAlloc(buffsize+1);
00483     in_comment = 0;
00484     fpos = 0;
00485     while (file_len != fpos) {
00486         memset(buffer, 0, buffsize+1);
00487         n = _read(fd, buffer, MIN(buffsize, file_len-fpos));
00488         
00489         if (!in_comment) {        
00490             
00491             index = strstr(buffer, "<!--");
00492             if (index == NULL) {                    // Nothing found. print next 412 characters, seek to next startpoint.
00493                 if (file_len > buffsize) {
00494                     fwrite(buffer, 1, MIN(buffsize-100, n), stream);
00495                     fpos += MIN(buffsize-100, n);
00496                     _seek(fd, fpos, SEEK_SET);
00497                 } else {
00498                     fwrite(buffer, 1, n, stream);
00499                     fpos += n;
00500                 }
00501                 
00502             } else {
00503                 found = (int)index - (int)buffer;   // We have found a comment initializer. Seek to the startpoint and print the beginning of the buffer.
00504                 fwrite (buffer, 1, found, stream);
00505                 fpos += found;
00506                 _seek(fd, fpos, SEEK_SET);
00507                 in_comment = 1;
00508             }
00509         } else {                                    // Ok, we assume we are "into" a comment.    
00510             index = strstr(buffer, "-->");
00511             if (index == NULL) {                    // We have not found the end of the comment in the next 512 characters. Byepass this comment.
00512                 fwrite(buffer, 1, MIN(buffsize, n), stream);
00513                 fpos += MIN(buffsize, n);
00514                 in_comment = 0;
00515             } else {                                // Ok. This seems to be a comment with maximum length of 512 bytes. We now search for ssi code.    
00516                 found = (int)index - (int)buffer;
00517                 if (!NutSsiCheckForSsi(stream, buffer, found, http_root, req)) {
00518                     fwrite(buffer, 1, found+3, stream);
00519                 }
00520                 fpos += found+3;
00521                 _seek(fd, fpos, SEEK_SET);
00522                 in_comment = 0;
00523             }
00524         }
00525     }
00526     
00527     NutHeapFree(buffer);
00528 }
00529 
00540 void NutRegisterSsi(void)
00541 {
00542     NutSetMimeHandler(".shtml", NutHttpProcessSHTML);
00543 }
00544 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/