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

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