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$
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 <string.h>
00098 #include <stdlib.h>
00099 #include <memdebug.h>
00100 
00101 #include <io.h>
00102 #include <ctype.h>
00103 #include <stdio.h>
00104 #include <sys/types.h>
00105 #include <unistd.h>
00106 #include <fcntl.h>
00107 #include <memdebug.h>
00108 
00109 #include <sys/heap.h>
00110 #include <sys/version.h>
00111 
00112 #include <pro/httpd.h>
00113 #include <pro/ssi.h>
00114 #include "httpd_p.h"
00115 #include "dencode.h"
00116 
00117 #define BUFSIZE 512
00118 
00119 #define MIN(a,b) (a<b?a:b)
00120 
00121 #define SSI_TYPE_FILE    0x01
00122 #define SSI_TYPE_VIRTUAL 0x02
00123 #define SSI_TYPE_EXEC    0x03
00124 
00125 static prog_char rsp_not_found_P[] = "404 Not found: %s\r\n";
00126 static prog_char rsp_intern_err_P[] = "500 Internal error\r\n";
00127 static prog_char rsp_bad_req_P[] = "400 Bad request\r\n";
00128 
00129 extern char *cgiBinPath;
00130 
00141 static void NutSsiProcessFile(FILE * stream, char *filename)
00142 {
00143     int fd;
00144     int n;
00145     char *data;
00146     int size;
00147     long file_len;
00148 
00149     fd = _open(filename, _O_BINARY | _O_RDONLY);
00150 
00151     if (fd == -1) {                     // No such file found... send a 404 string.
00152         fprintf_P(stream, rsp_not_found_P, filename);
00153         return;
00154     }
00155     
00156     file_len = _filelength(fd);
00157     
00158     size = 512;
00159     if ((data = malloc(size)) != NULL) {
00160         while (file_len) {
00161             if (file_len < 512L)
00162                 size = (int) file_len;
00163 
00164             n = _read(fd, data, size);
00165             if (fwrite(data, 1, n, stream) == 0)
00166                 break;
00167             file_len -= (long) n;
00168         }
00169         free(data);
00170     }
00171 
00172     _close(fd);
00173 }
00174 
00175 
00189 static void NutSsiProcessVirtual(FILE * stream, char *url, char* http_root, REQUEST *orig_req)
00190 {
00191     int fd;
00192     int i;
00193     int n;
00194     char *data;
00195     int size;
00196     long file_len;
00197     char *filename = NULL;
00198     void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
00199     char *cp;
00200     REQUEST * req;
00201     CONST char *cgi_bin = cgiBinPath ? cgiBinPath : "cgi-bin/";
00202     CONST char * tmp;
00203     size_t len;
00204 
00205     tmp = cgi_bin;
00206     if (NutDecodePath(url) == 0) {
00207         fprintf_P(stream, rsp_bad_req_P);
00208         return;
00209     }
00210   
00211     /*
00212      * Process CGI.
00213      */
00214     while (tmp) {
00215         /* Skip leading path separators. */
00216         while (*cgi_bin == ';')
00217             cgi_bin++;
00218         /* Determine the length of the next path component. */
00219         for (len = 0, cp = (char *)cgi_bin; *cp && *cp != ';'; len++, cp++);
00220         tmp = cgi_bin;
00221         if (len && strncasecmp(url, tmp, len) == 0) {
00222             if ((req = calloc(1, sizeof(REQUEST))) == 0) {
00223                 fprintf_P(stream, rsp_intern_err_P);
00224                 return;
00225             }
00226             req->req_method = METHOD_GET;
00227             req->req_version = orig_req->req_version;
00228             req->req_length = 0;
00229             
00230             if (orig_req->req_agent != NULL) {
00231                 if ((req->req_agent = strdup(orig_req->req_agent)) == NULL) {
00232                     fprintf_P(stream, rsp_intern_err_P);
00233                     DestroyRequestInfo(req);
00234                     return;
00235                 }
00236             }
00237             if (orig_req->req_cookie!= NULL) {
00238                 if ((req->req_cookie = strdup(orig_req->req_cookie)) == NULL) {
00239                     fprintf_P(stream, rsp_intern_err_P);
00240                     DestroyRequestInfo(req);
00241                     return;
00242                 }
00243             }
00244             if ((cp = strchr(url, '?')) != 0) {
00245                 *cp++ = 0;
00246                 if (strcmp(cp, "$QUERY_STRING") == 0) {
00247                     uint16_t size;
00248                     size = 1; /* At least 1 for empty requests. */
00249                     for (i = 0; i < orig_req->req_numqptrs*2; i ++) {
00250                         size += strlen(orig_req->req_qptrs[i]) + 1;
00251                     }
00252                     if ((req->req_query = malloc(size)) == NULL) {
00253                         fprintf_P(stream, rsp_intern_err_P);
00254                         DestroyRequestInfo(req);
00255                         return;
00256                     }
00257                     req->req_query[0] = 0;
00258                     for (i = 0; i < (orig_req->req_numqptrs * 2); i++) {
00259                         if(i) {
00260                             strcat (req->req_query, "&");
00261                         }
00262                         strcat (req->req_query, orig_req->req_qptrs[i]);
00263                         strcat (req->req_query, "=");
00264                         i++;
00265                         strcat (req->req_query, orig_req->req_qptrs[i]);
00266                     }
00267 
00268                 } else {
00269                     if ((req->req_query = strdup(cp)) == NULL) {
00270                         fprintf_P(stream, rsp_intern_err_P);
00271                         DestroyRequestInfo(req);
00272                         return;
00273                     }
00274                 }
00275                 NutHttpProcessQueryString(req);
00276             }
00277             if ((req->req_url = strdup(url)) == NULL) {
00278                 fprintf_P(stream, rsp_intern_err_P);
00279                 DestroyRequestInfo(req);
00280                 return;
00281             }
00282             NutCgiProcessRequest(stream, req, len);
00283             DestroyRequestInfo(req);
00284             return;
00285         }
00286         cgi_bin += len;
00287         if (*cgi_bin == '\0') {
00288             break;
00289         }
00290     }
00291     
00292     /*
00293      * Process file.
00294      */
00295     
00296     for (n = 0, fd = -1; default_files[n]; n++) {
00297         filename = CreateFilePath(url, default_files[n]);
00298         if (filename == NULL) {
00299             fprintf_P(stream, rsp_intern_err_P);
00300             return;
00301         }
00302         /*
00303          * Note, that simple file systems may not provide stat() or access(),
00304          * thus trying to open the file is the only way to check for existence.
00305          * Another problem is, that PHAT allows to open directories. We use
00306          * the file length to ensure, that we got a normal file.
00307          */
00308         if ((fd = _open(filename, _O_BINARY | _O_RDONLY)) != -1) {
00309             if (_filelength(fd)) {
00310                 break;
00311             }
00312             _close(fd);
00313         }
00314         free(filename);
00315     }
00316     if (fd == -1) {
00317         fprintf_P(stream, rsp_not_found_P, filename);
00318         return;
00319     }
00320     
00321     file_len = _filelength(fd);
00322     handler = NutGetMimeHandler(filename);
00323     free(filename);
00324     
00325     if (handler == NULL) {
00326         size = 512;                 // If we have not registered a mime handler handle default.
00327         if ((data = malloc(size)) != NULL) {
00328             while (file_len) {
00329                 if (file_len < 512L) {
00330                     size = (int) file_len;
00331                 }
00332 
00333                 n = _read(fd, data, size);
00334                 if (fwrite(data, 1, n, stream) == 0) {
00335                     break;
00336                 }
00337                 file_len -= (long) n;
00338             }
00339             free(data);
00340         }
00341     } else handler(stream, fd, file_len, http_root, orig_req);
00342     _close(fd);
00343     return;
00344 }
00345 
00358 static void NutSsiSkipWhitespace(char *buffer, uint16_t *pos, uint16_t end)
00359 {
00360     while ((*pos < end) && (
00361            (buffer[*pos] == '\n') || (buffer[*pos] == '\r') ||
00362            (buffer[*pos] == '\t') || (buffer[*pos] == ' ')))
00363         (*pos) ++;
00364 }
00365 
00384 static uint8_t NutSsiCheckForSsi(FILE *stream, char *buffer, uint16_t end, char* http_root, REQUEST *req)
00385 {
00386     uint16_t pos = 4; // First character after comment start
00387     char * filename;
00388     uint8_t type;
00389 
00390     pos = 4;
00391     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after comment start
00392     if (pos == end) return 0;
00393     
00394     if (strncasecmp(&buffer[pos], "#include", 8) == 0) { // Search include directive
00395         pos += 8;
00396         type = SSI_TYPE_VIRTUAL;
00397     } else 
00398     if (strncasecmp(&buffer[pos], "#exec", 5) == 0) { // Search include or exec directive
00399         pos += 5;
00400         type = SSI_TYPE_EXEC;
00401     } else return 0;                                // No include or exec found. Skip the rest of this comment...
00402     if (pos >= end) return 0;
00403 
00404     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after #include directive
00405     if (pos == end) return 0;
00406     
00407     if (type == SSI_TYPE_VIRTUAL) {
00408         if (strncasecmp(&buffer[pos], "virtual", 7) == 0) {  // Search virtual directive
00409             pos += 7;
00410         } else                                      // No virtual found. Test for file...
00411         if (strncasecmp(&buffer[pos], "file", 4) == 0) {  // Search file directive
00412             pos += 4;
00413             type = SSI_TYPE_FILE;
00414         } else return 0;                            // No file found. Test for file...
00415     } else {
00416         if (strncasecmp(&buffer[pos], "cgi", 3) == 0) {  // Search cgi directive
00417             pos += 3;
00418         } else return 0;                            // No cgi found. return...
00419     }
00420     if (pos >= end) return 0;
00421     
00422     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after virtual, file or cgi directive
00423     if (pos == end) return 0;
00424 
00425     if (buffer[pos] != '=') return 0;               // check for assertion
00426     pos ++; 
00427 
00428     NutSsiSkipWhitespace(buffer, &pos, end);        // Skip whitespaces after assertion
00429     if (pos == end) return 0;    
00430     
00431     if (buffer[pos] == '"') {                       // Search for filename and pass to output function
00432         pos ++;
00433         if (pos == end) return 0;
00434         filename = &buffer[pos];
00435         while (buffer[pos] != '"') {
00436             pos ++;
00437             if (pos == end) return 0;
00438         }
00439         buffer[pos] = '\0';
00440         switch (type) {
00441             case SSI_TYPE_FILE:
00442                 NutSsiProcessFile(stream, filename);
00443                 break;
00444             case SSI_TYPE_VIRTUAL:
00445                 NutSsiProcessVirtual(stream, filename, http_root, req);
00446                 break;
00447             case SSI_TYPE_EXEC:
00448                 NutSsiProcessVirtual(stream, filename, http_root, req);
00449                 break;
00450         }
00451     }
00452     return 1;
00453 }
00454 
00474 static void NutHttpProcessSHTML(FILE * stream, int fd, int file_len, char* http_root, REQUEST *req)
00475 {
00476     char * buffer;
00477     uint8_t in_comment;
00478     int buffsize;
00479     int fpos;
00480     int n;
00481     char *index;
00482     uint8_t found;
00483     buffsize = MIN(BUFSIZE, file_len);
00484     buffer = malloc(buffsize+1);
00485     in_comment = 0;
00486     fpos = 0;
00487     while (file_len != fpos) {
00488         memset(buffer, 0, buffsize+1);
00489         n = _read(fd, buffer, MIN(buffsize, file_len-fpos));
00490         
00491         if (!in_comment) {        
00492             
00493             index = strstr(buffer, "<!--");
00494             if (index == NULL) {                    // Nothing found. print next 412 characters, seek to next startpoint.
00495                 if (file_len > buffsize) {
00496                     fwrite(buffer, 1, MIN(buffsize-100, n), stream);
00497                     fpos += MIN(buffsize-100, n);
00498                     _seek(fd, fpos, SEEK_SET);
00499                 } else {
00500                     fwrite(buffer, 1, n, stream);
00501                     fpos += n;
00502                 }
00503                 
00504             } else {
00505                 found = (int)index - (int)buffer;   // We have found a comment initializer. Seek to the startpoint and print the beginning of the buffer.
00506                 fwrite (buffer, 1, found, stream);
00507                 fpos += found;
00508                 _seek(fd, fpos, SEEK_SET);
00509                 in_comment = 1;
00510             }
00511         } else {                                    // Ok, we assume we are "into" a comment.    
00512             index = strstr(buffer, "-->");
00513             if (index == NULL) {                    // We have not found the end of the comment in the next 512 characters. Byepass this comment.
00514                 fwrite(buffer, 1, MIN(buffsize, n), stream);
00515                 fpos += MIN(buffsize, n);
00516                 in_comment = 0;
00517             } else {                                // Ok. This seems to be a comment with maximum length of 512 bytes. We now search for ssi code.    
00518                 found = (int)index - (int)buffer;
00519                 if (!NutSsiCheckForSsi(stream, buffer, found, http_root, req)) {
00520                     fwrite(buffer, 1, found+3, stream);
00521                 }
00522                 fpos += found+3;
00523                 _seek(fd, fpos, SEEK_SET);
00524                 in_comment = 0;
00525             }
00526         }
00527     }
00528     
00529     free(buffer);
00530 }
00531 
00542 void NutRegisterSsi(void)
00543 {
00544     NutSetMimeHandler(".shtml", NutHttpProcessSHTML);
00545 }
00546 

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