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

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