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

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