busybox/networking/httpd_ssi.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2009 Denys Vlasenko <vda.linux@googlemail.com>
   3 *
   4 * Licensed under GPLv2, see file LICENSE in this source tree.
   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