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.2  2006/03/16 15:25:39  haraldkipp
00037  * Changed human readable strings from u_char to char to stop GCC 4 from
00038  * nagging about signedness.
00039  *
00040  * Revision 1.1  2005/08/05 11:22:14  olereinhardt
00041  * Added Server side include support. Initial checkin
00042  *
00043  */  
00044 
00049 
00050 #include <cfg/arch.h>
00051 
00052 #include <io.h>
00053 #include <ctype.h>
00054 #include <stdio.h>
00055 #include <string.h>
00056 #include <stdlib.h>
00057 #include <sys/types.h>
00058 #include <unistd.h>
00059 #include <fcntl.h>
00060 
00061 
00062 #include <sys/heap.h>
00063 #include <sys/version.h>
00064 
00065 #include <pro/httpd.h>
00066 #include <pro/ssi.h>
00067 #include "dencode.h"
00068 
00069 #define BUFSIZE 512
00070 
00071 #define MIN(a,b) (a<b?a:b)
00072 
00073 #define SSI_TYPE_FILE    0x01
00074 #define SSI_TYPE_VIRTUAL 0x02
00075 #define SSI_TYPE_EXEC    0x03
00076 
00077 
00088 static void NutSsiProcessFile(FILE * stream, char *filename)
00089 {
00090     int fd;
00091     int n;
00092     char *data;
00093     int size;
00094     long file_len;
00095 
00096     fd = _open(filename, _O_BINARY | _O_RDONLY);
00097 
00098     if (fd == -1) {                     // No such file found... send a 404 string.
00099         fprintf_P(stream, PSTR("404 Not found: %s\n"), filename);
00100         return;
00101     }
00102     
00103     file_len = _filelength(fd);
00104     
00105     size = 512;
00106     if ((data = NutHeapAlloc(size)) != 0) {
00107         while (file_len) {
00108             if (file_len < 512L)
00109                 size = (int) file_len;
00110 
00111             n = _read(fd, data, size);
00112             if (fwrite(data, 1, n, stream) == 0)
00113                 break;
00114             file_len -= (long) n;
00115         }
00116         NutHeapFree(data);
00117     }
00118 
00119     _close(fd);
00120 }
00121 
00122 static void DestroyRequestInfo(REQUEST * req)
00123 {
00124     if (req->req_url)
00125         NutHeapFree(req->req_url);
00126     if (req->req_query)
00127         NutHeapFree(req->req_query);
00128     if (req->req_type)
00129         NutHeapFree(req->req_type);
00130     if (req->req_cookie)
00131         NutHeapFree(req->req_cookie);
00132     if (req->req_auth)
00133         NutHeapFree(req->req_auth);
00134     if (req->req_agent)
00135         NutHeapFree(req->req_agent);
00136     if (req->req_qptrs)
00137         NutHeapFree(req->req_qptrs);
00138     NutHeapFree(req);
00139 }
00140 
00154 static void NutSsiProcessVirtual(FILE * stream, char *url, char* http_root, REQUEST *orig_req)
00155 {
00156     int fd;
00157     int n;
00158     char *data;
00159     int size;
00160     long file_len;
00161     char *filename = NULL;
00162     void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
00163     
00164     
00165     char *cp;
00166     REQUEST * req;
00167 
00168     if (NutDecodePath(url) == 0) {
00169         fprintf_P(stream, PSTR("400 Bead request\n"));
00170         return;
00171     }
00172     
00173     /*
00174      * Process CGI.
00175      */
00176     if (strncasecmp(url, "cgi-bin/", 8) == 0) {
00177         if ((req = NutHeapAllocClear(sizeof(REQUEST))) == 0) {
00178             fprintf_P(stream, PSTR("500 Internal error\n"));
00179             return;
00180         }
00181         req->req_method = METHOD_GET;
00182         req->req_version = orig_req->req_version;
00183         req->req_length = 0;
00184         
00185         if (orig_req->req_agent != NULL) {
00186             if ((req->req_agent = NutHeapAlloc((strlen(orig_req->req_agent) + 1))) == 0) {
00187                 fprintf_P(stream, PSTR("500 Internal error\n"));
00188                 DestroyRequestInfo(req);
00189                 return;
00190             }
00191             strcpy(req->req_agent, orig_req->req_agent);
00192         }
00193         if (orig_req->req_cookie!= NULL) {
00194             if ((req->req_cookie = NutHeapAlloc((strlen(orig_req->req_cookie) + 1))) == 0) {
00195                 fprintf_P(stream, PSTR("500 Internal error\n"));
00196                 DestroyRequestInfo(req);
00197                 return;
00198             }
00199             strcpy(req->req_cookie, orig_req->req_cookie);
00200         }
00201         if ((cp = strchr(url, '?')) != 0) {
00202             *cp++ = 0;
00203             if ((req->req_query = NutHeapAlloc(strlen(cp) + 1)) == 0) {
00204                 fprintf_P(stream, PSTR("500 Internal error\n"));
00205                 DestroyRequestInfo(req);
00206                 return;
00207             }
00208             strcpy(req->req_query, cp);
00209     
00210             NutHttpProcessQueryString(req);
00211         }
00212         if ((req->req_url = NutHeapAlloc(strlen(url) + 1)) == 0) {
00213             fprintf_P(stream, PSTR("500 Internal error\n"));
00214             DestroyRequestInfo(req);
00215             return;
00216         }
00217         strcpy(req->req_url, url);
00218 
00219         NutCgiProcessRequest(stream, req);
00220         DestroyRequestInfo(req);
00221         return;
00222     }
00223     
00224     /*
00225      * Process file.
00226      */
00227     if (http_root) {
00228         filename = NutHeapAlloc(strlen(http_root) + strlen(url) + 1);
00229         strcpy(filename, http_root);
00230     } else {
00231         filename = NutHeapAlloc(strlen(url) + 6);
00232         strcpy(filename, "UROM:");
00233     }
00234     
00235     strcat(filename, url);
00236 
00237     handler = NutGetMimeHandler(filename);
00238 
00239     fd = _open(filename, _O_BINARY | _O_RDONLY);
00240     NutHeapFree(filename);
00241     if (fd == -1) {                     // Search for index.html
00242         char *index;
00243         u_short urll;
00244 
00245 
00246         urll = strlen(url);
00247         if ((index = NutHeapAllocClear(urll + 12)) == 0) {
00248             fprintf_P(stream, PSTR("500 Internal error\n"));
00249             return;
00250         }
00251         if (urll)
00252             strcpy(index, url);
00253         if (urll && index[urll - 1] != '/')
00254             strcat(index, "/");
00255         strcat(index, "index.html");
00256 
00257         if (http_root) {
00258             filename = NutHeapAlloc(strlen(http_root) + strlen(index) + 1);
00259             strcpy(filename, http_root);
00260         } else {
00261             filename = NutHeapAlloc(strlen(index) + 6);
00262             strcpy(filename, "UROM:");
00263         }
00264         strcat(filename, index);
00265 
00266         NutHeapFree(index);
00267 
00268         handler = NutGetMimeHandler(filename);
00269 
00270         fd = _open(filename, _O_BINARY | _O_RDONLY);
00271         NutHeapFree(filename);
00272         if (fd == -1) {                 // We have no index.html. But perhaps an index.shtml?
00273             urll = strlen(url);
00274             if ((index = NutHeapAllocClear(urll + 13)) == 0) {
00275                 fprintf_P(stream, PSTR("500 Internal error\n"));
00276                 return;
00277             }
00278             if (urll)
00279                 strcpy(index, url);
00280             if (urll && index[urll - 1] != '/')
00281                 strcat(index, "/");
00282             strcat(index, "index.shtml");
00283     
00284             if (http_root) {
00285                 filename = NutHeapAlloc(strlen(http_root) + strlen(index) + 1);
00286                 strcpy(filename, http_root);
00287             } else {
00288                 filename = NutHeapAlloc(strlen(index) + 6);
00289                 strcpy(filename, "UROM:");
00290             }
00291             strcat(filename, index);
00292     
00293             NutHeapFree(index);
00294 
00295             handler = NutGetMimeHandler(filename);
00296 
00297             fd = _open(filename, _O_BINARY | _O_RDONLY);
00298             NutHeapFree(filename);
00299             if (fd == -1) {            // Non of both found... send a 404
00300                 fprintf_P(stream, PSTR("404 Not found: %s\n"), filename);
00301                 return;
00302             }
00303         }
00304     }
00305     
00306     file_len = _filelength(fd);
00307     
00308     if (handler == NULL) {
00309         size = 512;                 // If we have not registered a mime handler handle default.
00310         if ((data = NutHeapAlloc(size)) != 0) {
00311             while (file_len) {
00312                 if (file_len < 512L)
00313                     size = (int) file_len;
00314 
00315                 n = _read(fd, data, size);
00316                 if (fwrite(data, 1, n, stream) == 0)
00317                     break;
00318                 file_len -= (long) n;
00319             }
00320             NutHeapFree(data);
00321         }
00322     } else handler(stream, fd, file_len, http_root, orig_req);
00323     _close(fd);
00324 }
00325 
00338 static void NutSsiSkipWhitespace(char *buffer, u_short *pos, u_short end)
00339 {
00340     while ((*pos < end) && (
00341            (buffer[*pos] == '\n') || (buffer[*pos] == '\r') ||
00342            (buffer[*pos] == '\t') || (buffer[*pos] == ' ')))
00343         (*pos) ++;
00344 }
00345 
00364 static u_char NutSsiCheckForSsi(FILE *stream, char *buffer, u_short end, char* http_root, REQUEST *req)
00365 {
00366     u_short pos = 4; // First character after comment start
00367     char * filename;
00368     u_char type;
00369 
00370     pos = 4;
00371     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after comment start
00372     if (pos == end) return 0;
00373     
00374     if (strncasecmp(&buffer[pos], "#include", 8) == 0) { // Search include directive
00375         pos += 8;
00376         type = SSI_TYPE_VIRTUAL;
00377     } else 
00378     if (strncasecmp(&buffer[pos], "#exec", 5) == 0) { // Search include or exec directive
00379         pos += 5;
00380         type = SSI_TYPE_EXEC;
00381     } else return 0;                                // No include or exec found. Skip the rest of this comment...
00382     if (pos >= end) return 0;
00383 
00384     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after #include directive
00385     if (pos == end) return 0;
00386     
00387     if (type == SSI_TYPE_VIRTUAL) {
00388         if (strncasecmp(&buffer[pos], "virtual", 7) == 0) {  // Search virtual directive
00389             pos += 7;
00390         } else                                      // No virtual found. Test for file...
00391         if (strncasecmp(&buffer[pos], "file", 4) == 0) {  // Search file directive
00392             pos += 4;
00393             type = SSI_TYPE_FILE;
00394         } else return 0;                            // No file found. Test for file...
00395     } else {
00396         if (strncasecmp(&buffer[pos], "cgi", 3) == 0) {  // Search cgi directive
00397             pos += 3;
00398         } else return 0;                            // No cgi found. return...
00399     }
00400     if (pos >= end) return 0;
00401     
00402     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after virtual, file or cgi directive
00403     if (pos == end) return 0;
00404 
00405     if (buffer[pos] != '=') return 0;               // check for assertion
00406     pos ++; 
00407 
00408     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after assertion
00409     if (pos == end) return 0;    
00410     
00411     if (buffer[pos] == '"') {                       // Search for filename and pass to output function
00412         pos ++;
00413         if (pos == end) return 0;
00414         filename = &buffer[pos];
00415         while (buffer[pos] != '"') {
00416             pos ++;
00417             if (pos == end) return 0;
00418         }
00419         buffer[pos] = '\0';
00420         switch (type) {
00421             case SSI_TYPE_FILE:
00422                 NutSsiProcessFile(stream, filename);
00423                 break;
00424             case SSI_TYPE_VIRTUAL:
00425                 NutSsiProcessVirtual(stream, filename, http_root, req);
00426                 break;
00427             case SSI_TYPE_EXEC:
00428                 NutSsiProcessVirtual(stream, filename, http_root, req);
00429                 break;
00430         }
00431     }
00432     return 1;
00433 }
00434 
00454 static void NutHttpProcessSHTML(FILE * stream, int fd, int file_len, char* http_root, REQUEST *req)
00455 {
00456     char * buffer;
00457     u_char in_comment;
00458     int buffsize;
00459     int fpos;
00460     int n;
00461     char *index;
00462     u_char found;
00463     buffsize = MIN(BUFSIZE, file_len);
00464     buffer = NutHeapAlloc(buffsize+1);
00465     in_comment = 0;
00466     fpos = 0;
00467     while (file_len != fpos) {
00468         memset(buffer, 0, buffsize+1);
00469         n = _read(fd, buffer, MIN(buffsize, file_len-fpos));
00470         
00471         if (!in_comment) {        
00472             
00473             index = strstr(buffer, "<!--");
00474             if (index == NULL) {                    // Nothing found. print next 412 characters, seek to next startpoint.
00475                 if (file_len > buffsize) {
00476                     fwrite(buffer, 1, MIN(buffsize-100, n), stream);
00477                     fpos += MIN(buffsize-100, n);
00478                     _seek(fd, fpos, SEEK_SET);
00479                 } else {
00480                     fwrite(buffer, 1, n, stream);
00481                     fpos += n;
00482                 }
00483                 
00484             } else {
00485                 found = (int)index - (int)buffer;   // We have found a comment initializer. Seek to the startpoint and print the beginning of the buffer.
00486                 fwrite (buffer, 1, found, stream);
00487                 fpos += found;
00488                 _seek(fd, fpos, SEEK_SET);
00489                 in_comment = 1;
00490             }
00491         } else {                                    // Ok, we assume we are "into" a comment.    
00492             index = strstr(buffer, "-->");
00493             if (index == NULL) {                    // We have not found the end of the comment in the next 512 characters. Byepass this comment.
00494                 fwrite(buffer, 1, MIN(buffsize, n), stream);
00495                 fpos += MIN(buffsize, n);
00496                 in_comment = 0;
00497             } else {                                // Ok. This seems to be a comment with maximum length of 512 bytes. We now search for ssi code.    
00498                 found = (int)index - (int)buffer;
00499                 if (!NutSsiCheckForSsi(stream, buffer, found, http_root, req)) {
00500                     fwrite(buffer, 1, found+3, stream);
00501                 }
00502                 fpos += found+3;
00503                 _seek(fd, fpos, SEEK_SET);
00504                 in_comment = 0;
00505             }
00506         }
00507     }
00508     
00509     NutHeapFree(buffer);
00510 }
00511 
00522 void NutRegisterSsi(void)
00523 {
00524     NutSetMimeHandler(".shtml", NutHttpProcessSHTML);
00525 }
00526 

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