1/* 2 * Copyright (c) 2009 Denys Vlasenko <vda.linux@googlemail.com> 3 * 4 * Licensed under GPLv2, see file LICENSE in this tarball for details. 5 */ 6 7/* 8 * This program is a CGI application. It processes server-side includes: 9 * <!--#include file="file.html" --> 10 * 11 * Usage: put these lines in httpd.conf: 12 * 13 * *.html:/bin/httpd_ssi 14 * *.htm:/bin/httpd_ssi 15 */ 16 17/* Build a-la 18i486-linux-uclibc-gcc \ 19-static -static-libgcc \ 20-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \ 21-Wall -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes -Werror \ 22-Wold-style-definition -Wdeclaration-after-statement -Wno-pointer-sign \ 23-Wmissing-prototypes -Wmissing-declarations \ 24-Os -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer \ 25-ffunction-sections -fdata-sections -fno-guess-branch-probability \ 26-funsigned-char \ 27-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 \ 28-march=i386 -mpreferred-stack-boundary=2 \ 29-Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \ 30httpd_ssi.c -o httpd_ssi 31*/ 32 33/* Size (i386, static uclibc, approximate): 34 * text data bss dec hex filename 35 * 9487 160 68552 78199 13177 httpd_ssi 36 * 37 * Note: it wouldn't be too hard to get rid of stdio and strdup, 38 * (especially that fgets() mangles NULs...) 39 */ 40 41#include <sys/types.h> 42#include <sys/stat.h> 43#include <errno.h> 44#include <fcntl.h> 45#include <stdint.h> 46#include <stdlib.h> 47#include <string.h> 48#include <unistd.h> 49#include <stdio.h> 50#include <dirent.h> 51#include <time.h> 52 53static char* skip_whitespace(char *s) 54{ 55 while (*s == ' ' || *s == '\t') ++s; 56 57 return s; 58} 59 60static char line[64 * 1024]; 61 62static void process_includes(const char *filename) 63{ 64 int curdir_fd; 65 char *end; 66 FILE *fp = fopen(filename, "r"); 67 if (!fp) 68 exit(1); 69 70 /* Ensure that nested includes are relative: 71 * if we include a/1.htm and it includes b/2.htm, 72 * we need to include a/b/2.htm, not b/2.htm 73 */ 74 curdir_fd = -1; 75 end = strrchr(filename, '/'); 76 if (end) { 77 curdir_fd = open(".", O_RDONLY); 78 /* *end = '\0' would mishandle "/file.htm" */ 79 end[1] = '\0'; 80 chdir(filename); 81 } 82 83#define INCLUDE "<!--#include" 84 while (fgets(line, sizeof(line), fp)) { 85 unsigned preceding_len; 86 char *include_directive; 87 88 include_directive = strstr(line, INCLUDE); 89 if (!include_directive) { 90 fputs(line, stdout); 91 continue; 92 } 93 preceding_len = include_directive - line; 94 if (memchr(line, '\"', preceding_len) 95 || memchr(line, '\'', preceding_len) 96 ) { 97 /* INCLUDE string may be inside "str" or 'str', 98 * ignore it */ 99 fputs(line, stdout); 100 continue; 101 } 102 /* Small bug: we accept #includefile="file" too */ 103 include_directive = skip_whitespace(include_directive + sizeof(INCLUDE)-1); 104 if (strncmp(include_directive, "file=\"", 6) != 0) { 105 /* "<!--#include virtual=..."? - not supported */ 106 fputs(line, stdout); 107 continue; 108 } 109 include_directive += 6; /* now it points to file name */ 110 end = strchr(include_directive, '\"'); 111 if (!end) { 112 fputs(line, stdout); 113 continue; 114 } 115 /* We checked that this is a valid include directive */ 116 117 /* Print everything before directive */ 118 if (preceding_len) { 119 line[preceding_len] = '\0'; 120 fputs(line, stdout); 121 } 122 /* Save everything after directive */ 123 *end++ = '\0'; 124 end = strchr(end, '>'); 125 if (end) 126 end = strdup(end + 1); 127 128 /* FIXME: 129 * (1) are relative paths with /../ etc ok? 130 * (2) what to do with absolute paths? 131 * are they relative to doc root or to real root? 132 */ 133 process_includes(include_directive); 134 135 /* Print everything after directive */ 136 if (end) { 137 fputs(end, stdout); 138 free(end); 139 } 140 } 141 if (curdir_fd >= 0) 142 fchdir(curdir_fd); 143 fclose(fp); 144} 145 146int main(int argc, char *argv[]) 147{ 148 if (!argv[1]) 149 return 1; 150 151 /* Seen from busybox.net's Apache: 152 * HTTP/1.1 200 OK 153 * Date: Thu, 10 Sep 2009 18:23:28 GMT 154 * Server: Apache 155 * Accept-Ranges: bytes 156 * Connection: close 157 * Content-Type: text/html 158 */ 159 fputs( 160 /* "Date: Thu, 10 Sep 2009 18:23:28 GMT\r\n" */ 161 /* "Server: Apache\r\n" */ 162 /* "Accept-Ranges: bytes\r\n" - do we really accept bytes?! */ 163 "Connection: close\r\n" 164 "Content-Type: text/html\r\n" 165 "\r\n", 166 stdout 167 ); 168 process_includes(argv[1]); 169 return 0; 170} 171