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.14  2009/02/13 14:43:11  haraldkipp
00037  * Memory overflow bug fixed.
00038  *
00039  * Revision 1.13  2009/02/06 15:40:29  haraldkipp
00040  * Using newly available strdup() and calloc().
00041  * Replaced NutHeap routines by standard malloc/free.
00042  * Replaced pointer value 0 by NULL.
00043  *
00044  * Revision 1.12  2008/08/22 09:23:57  haraldkipp
00045  * GCC specific implementation removed.
00046  *
00047  * Revision 1.11  2008/08/18 11:00:39  olereinhardt
00048  * Fixed a memory leak. A filename was never freed again
00049  *
00050  * Revision 1.10  2008/08/11 07:00:36  haraldkipp
00051  * BSD types replaced by stdint types (feature request #1282721).
00052  *
00053  * Revision 1.9  2008/07/26 14:09:29  haraldkipp
00054  * Fixed another problem with ICCAVR.
00055  *
00056  * Revision 1.8  2008/07/25 12:17:26  olereinhardt
00057  * Imagecraft compilers does not support alloca (to allocate memory from the
00058  * stack). So we use NutHeapAlloc / NutHeapClear instead for Imagecraft.
00059  *
00060  * Revision 1.7  2008/07/17 11:36:34  olereinhardt
00061  * - Moved some functions used in httpd.c as well as in ssi.c into httpd_p.c
00062  * - Implemeted $QUERY_STRING parameter in for CGIs included by a ssi file
00063  *
00064  * Revision 1.6  2008/05/13 21:24:55  thiagocorrea
00065  * Fix a few documentation typos.
00066  *
00067  * Revision 1.5  2008/04/21 22:16:25  olereinhardt
00068  * 2008-04-21  Ole Reinhardt <ole.reinhardt@embedded-it.de>
00069  *         * pro/ssi.c: Nicer Implement query string feature and save some
00070  *           memory
00071  *
00072  * Revision 1.4  2008/04/18 13:22:57  haraldkipp
00073  * Fixed ICCAVR compile errors. No chance to implement GCC's PSTR().
00074  *
00075  * Revision 1.3  2008/02/15 17:09:03  haraldkipp
00076  * Quick hack provided by Niels. No idea what this is for, but
00077  * according to the author it is a dirty solution. We urgently
00078  * need it to get the Elektor Radio working. Sorry in advance
00079  * for any trouble this change may cause.
00080  *
00081  * Revision 1.2  2006/03/16 15:25:39  haraldkipp
00082  * Changed human readable strings from u_char to char to stop GCC 4 from
00083  * nagging about signedness.
00084  *
00085  * Revision 1.1  2005/08/05 11:22:14  olereinhardt
00086  * Added Server side include support. Initial checkin
00087  *
00088  */  
00089 
00094 
00095 #include <cfg/arch.h>
00096 
00097 #include <io.h>
00098 #include <ctype.h>
00099 #include <stdio.h>
00100 #include <string.h>
00101 #include <stdlib.h>
00102 #include <sys/types.h>
00103 #include <unistd.h>
00104 #include <fcntl.h>
00105 #include <memdebug.h>
00106 
00107 #include <sys/heap.h>
00108 #include <sys/version.h>
00109 
00110 #include <pro/httpd.h>
00111 #include <pro/ssi.h>
00112 #include "httpd_p.h"
00113 #include "dencode.h"
00114 
00115 #define BUFSIZE 512
00116 
00117 #define MIN(a,b) (a<b?a:b)
00118 
00119 #define SSI_TYPE_FILE    0x01
00120 #define SSI_TYPE_VIRTUAL 0x02
00121 #define SSI_TYPE_EXEC    0x03
00122 
00123 static prog_char rsp_not_found_P[] = "404 Not found: %s\r\n";
00124 static prog_char rsp_intern_err_P[] = "500 Internal error\r\n";
00125 static prog_char rsp_bad_req_P[] = "400 Bad request\r\n";
00126 
00127 extern char *cgiBinPath;
00128 
00139 static void NutSsiProcessFile(FILE * stream, char *filename)
00140 {
00141     int fd;
00142     int n;
00143     char *data;
00144     int size;
00145     long file_len;
00146 
00147     fd = _open(filename, _O_BINARY | _O_RDONLY);
00148 
00149     if (fd == -1) {                     // No such file found... send a 404 string.
00150         fprintf_P(stream, rsp_not_found_P, filename);
00151         return;
00152     }
00153     
00154     file_len = _filelength(fd);
00155     
00156     size = 512;
00157     if ((data = malloc(size)) != NULL) {
00158         while (file_len) {
00159             if (file_len < 512L)
00160                 size = (int) file_len;
00161 
00162             n = _read(fd, data, size);
00163             if (fwrite(data, 1, n, stream) == 0)
00164                 break;
00165             file_len -= (long) n;
00166         }
00167         free(data);
00168     }
00169 
00170     _close(fd);
00171 }
00172 
00173 
00187 static void NutSsiProcessVirtual(FILE * stream, char *url, char* http_root, REQUEST *orig_req)
00188 {
00189     int fd;
00190     int i;
00191     int n;
00192     char *data;
00193     int size;
00194     long file_len;
00195     char *filename = NULL;
00196     void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
00197     char *cp;
00198     REQUEST * req;
00199     CONST char *cgi_bin = cgiBinPath ? cgiBinPath : "cgi-bin/";
00200     CONST char * tmp;
00201     size_t len;
00202 
00203     tmp = cgi_bin;
00204     if (NutDecodePath(url) == 0) {
00205         fprintf_P(stream, rsp_bad_req_P);
00206         return;
00207     }
00208   
00209     /*
00210      * Process CGI.
00211      */
00212     while (tmp) {
00213         /* Skip leading path separators. */
00214         while (*cgi_bin == ';')
00215             cgi_bin++;
00216         /* Determine the length of the next path component. */
00217         for (len = 0, cp = (char *)cgi_bin; *cp && *cp != ';'; len++, cp++);
00218         tmp = cgi_bin;
00219         if (len && strncasecmp(url, tmp, len) == 0) {
00220             if ((req = calloc(1, sizeof(REQUEST))) == 0) {
00221                 fprintf_P(stream, rsp_intern_err_P);
00222                 return;
00223             }
00224             req->req_method = METHOD_GET;
00225             req->req_version = orig_req->req_version;
00226             req->req_length = 0;
00227             
00228             if (orig_req->req_agent != NULL) {
00229                 if ((req->req_agent = strdup(orig_req->req_agent)) == NULL) {
00230                     fprintf_P(stream, rsp_intern_err_P);
00231                     DestroyRequestInfo(req);
00232                     return;
00233                 }
00234             }
00235             if (orig_req->req_cookie!= NULL) {
00236                 if ((req->req_cookie = strdup(orig_req->req_cookie)) == NULL) {
00237                     fprintf_P(stream, rsp_intern_err_P);
00238                     DestroyRequestInfo(req);
00239                     return;
00240                 }
00241             }
00242             if ((cp = strchr(url, '?')) != 0) {
00243                 *cp++ = 0;
00244                 if (strcmp(cp, "$QUERY_STRING") == 0) {
00245                     uint16_t size;
00246                     size = 1; /* At least 1 for empty requests. */
00247                     for (i = 0; i < orig_req->req_numqptrs*2; i ++) {
00248                         size += strlen(orig_req->req_qptrs[i]) + 1;
00249                     }
00250                     if ((req->req_query = malloc(size)) == NULL) {
00251                         fprintf_P(stream, rsp_intern_err_P);
00252                         DestroyRequestInfo(req);
00253                         return;
00254                     }
00255                     req->req_query[0] = 0;
00256                     for (i = 0; i < (orig_req->req_numqptrs * 2); i++) {
00257                         if(i) {
00258                             strcat (req->req_query, "&");
00259                         }
00260                         strcat (req->req_query, orig_req->req_qptrs[i]);
00261                         strcat (req->req_query, "=");
00262                         i++;
00263                         strcat (req->req_query, orig_req->req_qptrs[i]);
00264                     }
00265 
00266                 } else {
00267                     if ((req->req_query = strdup(cp)) == NULL) {
00268                         fprintf_P(stream, rsp_intern_err_P);
00269                         DestroyRequestInfo(req);
00270                         return;
00271                     }
00272                 }
00273                 NutHttpProcessQueryString(req);
00274             }
00275             if ((req->req_url = strdup(url)) == NULL) {
00276                 fprintf_P(stream, rsp_intern_err_P);
00277                 DestroyRequestInfo(req);
00278                 return;
00279             }
00280             NutCgiProcessRequest(stream, req, len);
00281             DestroyRequestInfo(req);
00282             return;
00283         }
00284         cgi_bin += len;
00285         if (*cgi_bin == '\0') {
00286             break;
00287         }
00288     }
00289     
00290     /*
00291      * Process file.
00292      */
00293     
00294     for (n = 0, fd = -1; default_files[n]; n++) {
00295         filename = CreateFilePath(url, default_files[n]);
00296         if (filename == NULL) {
00297             fprintf_P(stream, rsp_intern_err_P);
00298             return;
00299         }
00300         /*
00301          * Note, that simple file systems may not provide stat() or access(),
00302          * thus trying to open the file is the only way to check for existence.
00303          * Another problem is, that PHAT allows to open directories. We use
00304          * the file length to ensure, that we got a normal file.
00305          */
00306         if ((fd = _open(filename, _O_BINARY | _O_RDONLY)) != -1) {
00307             if (_filelength(fd)) {
00308                 break;
00309             }
00310             _close(fd);
00311         }
00312         free(filename);
00313     }
00314     if (fd == -1) {
00315         fprintf_P(stream, rsp_not_found_P, filename);
00316         free(filename);
00317         return;
00318     }
00319     
00320     file_len = _filelength(fd);
00321     handler = NutGetMimeHandler(filename);
00322     free(filename);
00323     
00324     if (handler == NULL) {
00325         size = 512;                 // If we have not registered a mime handler handle default.
00326         if ((data = malloc(size)) != NULL) {
00327             while (file_len) {
00328                 if (file_len < 512L) {
00329                     size = (int) file_len;
00330                 }
00331 
00332                 n = _read(fd, data, size);
00333                 if (fwrite(data, 1, n, stream) == 0) {
00334                     break;
00335                 }
00336                 file_len -= (long) n;
00337             }
00338             free(data);
00339         }
00340     } else handler(stream, fd, file_len, http_root, orig_req);
00341     _close(fd);
00342     return;
00343 }
00344 
00357 static void NutSsiSkipWhitespace(char *buffer, uint16_t *pos, uint16_t end)
00358 {
00359     while ((*pos < end) && (
00360            (buffer[*pos] == '\n') || (buffer[*pos] == '\r') ||
00361            (buffer[*pos] == '\t') || (buffer[*pos] == ' ')))
00362         (*pos) ++;
00363 }
00364 
00383 static uint8_t NutSsiCheckForSsi(FILE *stream, char *buffer, uint16_t end, char* http_root, REQUEST *req)
00384 {
00385     uint16_t pos = 4; // First character after comment start
00386     char * filename;
00387     uint8_t type;
00388 
00389     pos = 4;
00390     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after comment start
00391     if (pos == end) return 0;
00392     
00393     if (strncasecmp(&buffer[pos], "#include", 8) == 0) { // Search include directive
00394         pos += 8;
00395         type = SSI_TYPE_VIRTUAL;
00396     } else 
00397     if (strncasecmp(&buffer[pos], "#exec", 5) == 0) { // Search include or exec directive
00398         pos += 5;
00399         type = SSI_TYPE_EXEC;
00400     } else return 0;                                // No include or exec found. Skip the rest of this comment...
00401     if (pos >= end) return 0;
00402 
00403     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after #include directive
00404     if (pos == end) return 0;
00405     
00406     if (type == SSI_TYPE_VIRTUAL) {
00407         if (strncasecmp(&buffer[pos], "virtual", 7) == 0) {  // Search virtual directive
00408             pos += 7;
00409         } else                                      // No virtual found. Test for file...
00410         if (strncasecmp(&buffer[pos], "file", 4) == 0) {  // Search file directive
00411             pos += 4;
00412             type = SSI_TYPE_FILE;
00413         } else return 0;                            // No file found. Test for file...
00414     } else {
00415         if (strncasecmp(&buffer[pos], "cgi", 3) == 0) {  // Search cgi directive
00416             pos += 3;
00417         } else return 0;                            // No cgi found. return...
00418     }
00419     if (pos >= end) return 0;
00420     
00421     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after virtual, file or cgi directive
00422     if (pos == end) return 0;
00423 
00424     if (buffer[pos] != '=') return 0;               // check for assertion
00425     pos ++; 
00426 
00427     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after assertion
00428     if (pos == end) return 0;    
00429     
00430     if (buffer[pos] == '"') {                       // Search for filename and pass to output function
00431         pos ++;
00432         if (pos == end) return 0;
00433         filename = &buffer[pos];
00434         while (buffer[pos] != '"') {
00435             pos ++;
00436             if (pos == end) return 0;
00437         }
00438         buffer[pos] = '\0';
00439         switch (type) {
00440             case SSI_TYPE_FILE:
00441                 NutSsiProcessFile(stream, filename);
00442                 break;
00443             case SSI_TYPE_VIRTUAL:
00444                 NutSsiProcessVirtual(stream, filename, http_root, req);
00445                 break;
00446             case SSI_TYPE_EXEC:
00447                 NutSsiProcessVirtual(stream, filename, http_root, req);
00448                 break;
00449         }
00450     }
00451     return 1;
00452 }
00453 
00473 static void NutHttpProcessSHTML(FILE * stream, int fd, int file_len, char* http_root, REQUEST *req)
00474 {
00475     char * buffer;
00476     uint8_t in_comment;
00477     int buffsize;
00478     int fpos;
00479     int n;
00480     char *index;
00481     uint8_t found;
00482     buffsize = MIN(BUFSIZE, file_len);
00483     buffer = malloc(buffsize+1);
00484     in_comment = 0;
00485     fpos = 0;
00486     while (file_len != fpos) {
00487         memset(buffer, 0, buffsize+1);
00488         n = _read(fd, buffer, MIN(buffsize, file_len-fpos));
00489         
00490         if (!in_comment) {        
00491             
00492             index = strstr(buffer, "<!--");
00493             if (index == NULL) {                    // Nothing found. print next 412 characters, seek to next startpoint.
00494                 if (file_len > buffsize) {
00495                     fwrite(buffer, 1, MIN(buffsize-100, n), stream);
00496                     fpos += MIN(buffsize-100, n);
00497                     _seek(fd, fpos, SEEK_SET);
00498                 } else {
00499                     fwrite(buffer, 1, n, stream);
00500                     fpos += n;
00501                 }
00502                 
00503             } else {
00504                 found = (int)index - (int)buffer;   // We have found a comment initializer. Seek to the startpoint and print the beginning of the buffer.
00505                 fwrite (buffer, 1, found, stream);
00506                 fpos += found;
00507                 _seek(fd, fpos, SEEK_SET);
00508                 in_comment = 1;
00509             }
00510         } else {                                    // Ok, we assume we are "into" a comment.    
00511             index = strstr(buffer, "-->");
00512             if (index == NULL) {                    // We have not found the end of the comment in the next 512 characters. Byepass this comment.
00513                 fwrite(buffer, 1, MIN(buffsize, n), stream);
00514                 fpos += MIN(buffsize, n);
00515                 in_comment = 0;
00516             } else {                                // Ok. This seems to be a comment with maximum length of 512 bytes. We now search for ssi code.    
00517                 found = (int)index - (int)buffer;
00518                 if (!NutSsiCheckForSsi(stream, buffer, found, http_root, req)) {
00519                     fwrite(buffer, 1, found+3, stream);
00520                 }
00521                 fpos += found+3;
00522                 _seek(fd, fpos, SEEK_SET);
00523                 in_comment = 0;
00524             }
00525         }
00526     }
00527     
00528     free(buffer);
00529 }
00530 
00541 void NutRegisterSsi(void)
00542 {
00543     NutSetMimeHandler(".shtml", NutHttpProcessSHTML);
00544 }
00545 

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