summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNathan Angelacos <nangel@alpinelinux.org>2007-09-22 09:12:55 +0000
committerNathan Angelacos <nangel@alpinelinux.org>2007-09-22 09:12:55 +0000
commit2b2c0a4a34173fefd9cf254860252a203c189361 (patch)
tree640ce922a56d8e9eabe39b35b4e4f5313bdbd2bc /src
downloadhaserl-2b2c0a4a34173fefd9cf254860252a203c189361.tar.bz2
haserl-2b2c0a4a34173fefd9cf254860252a203c189361.tar.xz
move to trunk/tags/branches arch
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am55
-rw-r--r--src/common.c370
-rw-r--r--src/common.h62
-rw-r--r--src/config.h.in103
-rw-r--r--src/h_bash.c206
-rw-r--r--src/h_bash.h23
-rw-r--r--src/h_error.c103
-rw-r--r--src/h_error.h19
-rw-r--r--src/h_lua.c155
-rw-r--r--src/h_lua.h13
-rw-r--r--src/h_lua_common.c114
-rw-r--r--src/h_lua_common.h7
-rw-r--r--src/h_luac.c55
-rw-r--r--src/h_luac.h7
-rw-r--r--src/h_script.c347
-rw-r--r--src/h_script.h48
-rw-r--r--src/haserl.c823
-rw-r--r--src/haserl.h54
-rw-r--r--src/haserl_lualib.inc51
-rw-r--r--src/haserl_lualib.lua54
-rw-r--r--src/lua2c.c97
-rw-r--r--src/rfc2388.c514
-rw-r--r--src/rfc2388.h28
-rw-r--r--src/sliding_buffer.c170
-rw-r--r--src/sliding_buffer.h25
-rw-r--r--src/stamp-h.in1
26 files changed, 3504 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..66277a8
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,55 @@
+PACKAGE=@PACKAGE_TARNAME@
+
+EXTRA_DIST = haserl_lualib.lua haserl_lualib.inc lua2c.c
+
+datarootdir=@datarootdir@
+
+if USE_LUA
+LUASOURCE = haserl_lualib.inc h_lua_common.c h_lua_common.h
+if INCLUDE_LUASHELL
+LUASOURCE += h_lua.c h_lua.h
+endif
+if INCLUDE_LUACSHELL
+LUASOURCE += h_luac.c h_luac.h
+endif
+endif
+
+if INCLUDE_BASHSHELL
+BASHSOURCE = h_bash.c h_bash.h
+endif
+
+if USE_LUA
+
+haserl_lualib.inc : haserl_lualib.lua
+ @echo '-----------------------------------------------------'
+ @echo 'Whoops. haserl_lualib.inc is old. You will need'
+ @echo 'to compile lua2c by hand, or help the maintainer'
+ @echo 'get automake to do it for you.'
+ @echo ''
+ @echo 'For now, to compile lua2c:'
+ @echo ''
+ @echo 'gcc -I<luaheaderdir> -Wl,-E -L<lualibdir> -o lua2c lua2c.c -llua -ldl -lm'
+ @echo ''
+ @echo ''
+ @echo 'Then follow the instructions in lua2c.c to create a'
+ @echo 'new haserl_lualib.inc'
+ @echo ''
+ @echo 'Sorry.'
+ @echo '-----------------------------------------------------'
+ @exit 1
+
+h_lua_common.c : haserl_lualib.inc
+
+endif
+
+bin_PROGRAMS = haserl
+
+haserl_SOURCES = common.c common.h sliding_buffer.c sliding_buffer.h \
+ h_error.c h_error.h h_script.c h_script.h rfc2388.c rfc2388.h \
+ $(BASHSOURCE) $(LUASOURCE) haserl.c haserl.h
+
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..8f5ad6b
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,370 @@
+/*
+ * --------------------------------------------------------------------------
+ * Common library functions for the haserl suite
+ * Copyright (c) 2005-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 2, as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "common.h"
+#include "h_error.h"
+
+
+/* we define this here, but should use the header files instead. */
+void *xrealloc (void *buf, size_t size);
+
+
+/*
+ * split a string into an argv[] array, and return the number of elements.
+ * Warning: Overwrites instr with nulls (to make ASCIIZ strings) and mallocs
+ * space for the argv array. The argv array will point to the offsets in
+ * instr where the elements occur. The calling function must free the argv
+ * array, and should do so before freeing instr memory.
+ *
+ * If comment points to a non-null string, then any character in the string
+ * will mark the beginning of a comment until the end-of-line:
+ *
+ * comment="#;"
+ * foo bar # This is a comment
+ * foo baz ; This is a comment as well
+ *
+ * Example of use:
+ *
+ * int argc, count; argv_t *argv; char string[2000];
+ *
+ * strcpy (string, "This\\ string will be \"separated into\" '6' elements.", "");
+ * argc = argc_argv (string, &argv);
+ * for (count = 0; count < argc; count++) {
+ * printf ("%03d: %s\n", count, argv[count].string);
+ * }
+ * free (argv);
+ *
+ */
+
+
+int
+argc_argv (char *instr, argv_t ** argv, char *commentstr)
+{
+ char quote = '\0';
+ int arg_count = 0;
+ enum state_t
+ {
+ WHITESPACE, WORDSPACE, TOKENSTART
+ } state = WHITESPACE;
+ argv_t *argv_array = NULL;
+ int argc_slots = 0;
+ size_t len, pos;
+
+ len = strlen (instr);
+ pos = 0;
+
+ while (pos < len)
+ {
+ // printf ("%3d of %3d: %s\n", pos, len, instr);
+
+ /* Comments are really, really special */
+
+ if ((state == WHITESPACE) && (strchr (commentstr, *instr)))
+ {
+ while ((*instr != '\n') && (*instr != '\0'))
+ {
+ instr++;
+ pos++;
+ }
+ }
+
+ switch (*instr)
+ {
+
+ /* quoting */
+ case '"':
+ case '\'':
+ /* Begin quoting */
+ if (state == WHITESPACE)
+ {
+ quote = *instr;
+ state = TOKENSTART;
+ if (*(instr + 1) == quote)
+ { /* special case for NULL quote */
+ quote = '\0';
+ *instr = '\0';
+ argv_array[arg_count].quoted = -1;
+ }
+ else
+ {
+ instr++;
+ pos++;
+ }
+ }
+ else
+ { /* WORDSPACE, so quotes end or quotes within quotes */
+
+ /* Is it the same kind of quote? */
+ if (*instr == quote)
+ {
+ argv_array[arg_count - 1].quoted = -1;
+ quote = '\0';
+ *instr = '\0';
+ state = WHITESPACE;
+ }
+ }
+ break;
+
+ /* backslash - if escaping a quote within a quote */
+ case '\\':
+ if ((quote) && (*(instr + 1) == quote))
+ {
+ memmove (instr, instr + 1, strlen (instr));
+ len--;
+ }
+ /* otherwise, its just a normal character */
+ else
+ {
+ if (state == WHITESPACE)
+ {
+ state = TOKENSTART;
+ }
+ }
+ break;
+
+
+ /* whitepsace */
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ if ((state == WORDSPACE) && (quote == '\0'))
+ {
+ state = WHITESPACE;
+ *instr = '\0';
+ }
+ break;
+
+ case '\0':
+ break;
+
+ default:
+ if (state == WHITESPACE)
+ {
+ state = TOKENSTART;
+ }
+
+ } /* end switch */
+
+ if (state == TOKENSTART)
+ {
+ arg_count++;
+ if (arg_count > argc_slots)
+ {
+ argc_slots += ALLOC_CHUNK;
+ argv_array =
+ (argv_t *) xrealloc (argv_array,
+ sizeof (argv_t) * (argc_slots +
+ ALLOC_CHUNK));
+ }
+
+ if (argv_array == (char) NULL)
+ {
+ return (-1);
+ }
+ argv_array[arg_count - 1].string = instr;
+ argv_array[arg_count].quoted = (char) NULL;
+ state = WORDSPACE;
+ }
+
+ instr++;
+ pos++;
+ }
+
+ argv_array[arg_count].string = NULL;
+ *argv = argv_array;
+ return (arg_count);
+}
+
+/* Expandable Buffer is a reimplementation based on buffer.c in GCC
+ originally by Per Bother */
+
+void
+buffer_init (buffer_t * buf)
+{
+ buf->data = NULL;
+ buf->ptr = NULL;
+ buf->limit = NULL;
+}
+
+void
+buffer_destroy (buffer_t * buf)
+{
+ if (buf->data)
+ {
+ free (buf->data);
+ }
+ buffer_init (buf);
+}
+
+/* don't reallocate - just forget about the current contents */
+void
+buffer_reset (buffer_t * buf)
+{
+ if (buf->data)
+ {
+ buf->ptr = buf->data;
+ }
+ else
+ {
+ buf->ptr = NULL;
+ }
+}
+
+
+void
+buffer_add (buffer_t * buf, const void *data, unsigned long size)
+{
+ unsigned long newsize;
+ unsigned long index;
+
+ /* if we need to grow the buffer, do so now */
+ if ((buf->ptr + size) >= buf->limit)
+ {
+ index = (buf->limit - buf->data);
+ newsize = index;
+ while (newsize <= index + size)
+ {
+ newsize += 1024;
+ }
+ index = buf->ptr - buf->data;
+ buf->data = realloc (buf->data, newsize);
+ buf->limit = buf->data + newsize;
+ buf->ptr = buf->data + index;
+ }
+
+ memcpy (buf->ptr, data, size);
+ buf->ptr += size;
+}
+
+#ifndef JUST_LUACSHELL
+
+/* uppercase an entire string, using toupper */
+void
+uppercase (char *instr)
+{
+ while (*instr != '\0')
+ {
+ *instr = toupper (*instr);
+ instr++;
+ }
+}
+
+
+/* lowercase an entire string, using tolower */
+void
+lowercase (char *instr)
+{
+ while (*instr != '\0')
+ {
+ *instr = tolower (*instr);
+ instr++;
+ }
+}
+
+/* return ptr to first non-whitespace character */
+char *
+skip_whitespace (char *instr)
+{
+ while (isspace (*instr) && *instr)
+ instr++;
+ return instr;
+}
+
+
+/* return ptr to first whitespace character */
+char *
+find_whitespace (char *instr)
+{
+ while (!isspace (*instr) && *instr)
+ instr++;
+ return instr;
+}
+
+
+
+/* Counts the number of newlines in a buffer */
+int
+count_lines (char *instr, size_t len, char *where)
+{
+ size_t line = 1;
+ while ((where > instr) && (len))
+ {
+ if (*instr == '\n')
+ line++;
+ len--;
+ instr++;
+ }
+ return line;
+}
+
+#endif
+
+#ifdef TEST_FRAMEWORK
+
+main ()
+{
+
+ int argc, count;
+ argv_t *argv;
+ char string[2000];
+
+
+ strcpy (string,
+ "\\This\\ string will be '' \"separated into\" \"'\\\"'\" ' 15 ' elements.\n"
+ "' including a multi-line\n"
+ "element' with a comment. # This should not be parsed\n"
+ ";Nor should this\n" "The End.");
+
+ printf ("%s\n", string);
+
+ argc = argc_argv (string, &argv, "#;");
+ for (count = 0; count < argc; count++)
+ {
+ printf ("%03d: [%s] ", count, argv[count].string, "");
+ if (argv[count].quoted)
+ {
+ printf ("(it was quoted)");
+ }
+ printf ("\n");
+ }
+ if (argc != 15)
+ {
+ puts ("Test FAILED");
+ }
+ else
+ {
+ puts ("Test PASSED");
+ }
+ free (argv);
+}
+
+#endif
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..dbf33aa
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,62 @@
+/*
+ * $Id: common.h,v 1.5 2005/11/21 22:05:34 nangel Exp $
+ */
+#ifndef _COMMON_H
+#define _COMMON_H 1
+
+
+/* how many argv slots to allocate at once */
+#define ALLOC_CHUNK 10
+
+
+#define STDIN 0
+#define STDOUT 1
+#define STDERR 2
+
+
+#define TRUE -1
+#define FALSE 0
+#define NONE 1
+
+/* linked list */
+typedef struct
+{
+ char *buf;
+ void *next;
+} list_t;
+
+/* name/value pairs */
+typedef struct
+{
+ char *string; /* the string */
+ unsigned char quoted; /* non-zero if the string was quoted */
+} argv_t;
+
+/* expandable buffer structure */
+typedef struct
+{
+ unsigned char *data; /* the data */
+ unsigned char *ptr; /* where to write to next */
+ unsigned char *limit; /* maximal allocated buffer pos */
+} buffer_t;
+
+
+/* common.c */
+
+int argc_argv (char *instr, argv_t ** argv, char *commentstr);
+void buffer_init (buffer_t * buf);
+void buffer_reset (buffer_t * buf);
+void buffer_destroy (buffer_t * buf);
+void buffer_add (buffer_t * buf, const void *data, unsigned long size);
+
+#ifndef JUST_LUACSHELL
+
+void uppercase (char *instr);
+void lowercase (char *instr);
+char *skip_whitespace (char *instr);
+char *find_whitespace (char *instr);
+int count_lines (char *instr, size_t len, char *where);
+
+#endif
+
+#endif /* !_COMMON_H */
diff --git a/src/config.h.in b/src/config.h.in
new file mode 100644
index 0000000..f3ff098
--- /dev/null
+++ b/src/config.h.in
@@ -0,0 +1,103 @@
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+/* prefix for user supplied data */
+#undef HASERL_VAR_PREFIX
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define to 1 if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#undef HAVE_STRNCASECMP
+
+/* Define to 1 if you have the `strstr' function. */
+#undef HAVE_STRSTR
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Include Bash/Linux shell */
+#undef INCLUDE_BASHSHELL
+
+/* Include shell for precompiled Haserl/Lua */
+#undef INCLUDE_LUACSHELL
+
+/* Include ordinary Lua shell */
+#undef INCLUDE_LUASHELL
+
+/* Include just the compiled Lua shell */
+#undef JUST_LUACSHELL
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* the subshell to start up */
+#undef SUBSHELL_CMD
+
+/* Enable Lua */
+#undef USE_LUA
+
+/* Version number of package */
+#undef VERSION
+
+/* Enable GNU Extensions */
+#undef _GNU_SOURCE
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
diff --git a/src/h_bash.c b/src/h_bash.c
new file mode 100644
index 0000000..7f6d149
--- /dev/null
+++ b/src/h_bash.c
@@ -0,0 +1,206 @@
+/*-----------------------------------------------------------------
+ * haserl functions specific to a bash/ash/dash shell
+ * Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 2, as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#include "common.h"
+#include "h_error.h"
+#include "h_bash.h"
+#include "h_script.h"
+#include "haserl.h"
+
+#ifdef DEBUG
+#include <syslog.h>
+#endif
+
+/* Local subshell variables */
+static int subshell_pipe[2];
+static int subshell_pid;
+
+
+void
+bash_open (char *shell)
+{
+ int retcode = 0;
+ int count;
+ argv_t *argv;
+ char *av[20];
+
+ if (shell == NULL)
+ return;
+
+ retcode = pipe (&subshell_pipe[PARENT_IN]);
+ if (retcode == 0)
+ {
+ subshell_pid = fork ();
+ if (subshell_pid == -1)
+ {
+ die_with_message (NULL, NULL, g_err_msg[E_SUBSHELL_FAIL]);
+ }
+
+ if (subshell_pid == 0)
+ {
+ /* I'm the child, connect stdin to the parent */
+ dup2 (subshell_pipe[PARENT_IN], STDIN_FILENO);
+ close (subshell_pipe[PARENT_IN]);
+ count = argc_argv (shell, &argv, "");
+ if (count > 19)
+ {
+ /* over 20 command line args, silently truncate */
+ av[19] = "\0";
+ count = 18;
+ }
+ while (count >= 0)
+ {
+ av[count] = argv[count].string;
+ count--;
+ }
+ execv (argv[0].string, av);
+ free (argv);
+
+ /* if we get here, we had a failure */
+ die_with_message (NULL, NULL, g_err_msg[E_SUBSHELL_FAIL]);
+ }
+ else
+ {
+ /* I'm parent, move along please */
+ }
+ }
+
+ /* control should get to this point only in the parent.
+ */
+}
+
+void
+bash_destroy (void)
+{
+ int status;
+ waitpid (subshell_pid, &status, 0);
+}
+
+
+void
+bash_exec (buffer_t * buf, char *str)
+{
+ buffer_add (buf, str, strlen (str));
+ return;
+}
+
+/* Run the echo command in a subshell */
+void
+bash_echo (buffer_t * buf, char *str, size_t len)
+{
+/* limits.h would tell us the ARG_MAX characters we COULD send to the echo command, but
+ * we will take the (ancient) POSIX1 standard of 4K, subtract 1K from it and use that
+ * as the maxmimum. The Linux limit appears to be 128K, so 3K will fit. */
+
+ static char echo_start[] = "echo -n '";
+ static char echo_quote[] = "'\\''";
+ static char echo_end[] = "'\n";
+ const size_t maxlen = 3096;
+ size_t pos;
+
+ if (len == 0)
+ return;
+ pos = 0;
+
+ buffer_add (buf, echo_start, strlen (echo_start));
+ while (pos < len)
+ {
+ if (str[pos] == '\'')
+ buffer_add (buf, echo_quote, strlen (echo_quote));
+ else
+ buffer_add (buf, str + pos, 1);
+ pos++;
+ if ((pos % maxlen) == 0)
+ {
+ buffer_add (buf, echo_end, strlen (echo_end));
+ buffer_add (buf, echo_start, strlen (echo_start));
+ }
+ }
+ buffer_add (buf, echo_end, strlen (echo_end));
+}
+
+
+/* do an evaluation in a subshell */
+void
+bash_eval (buffer_t * buf, char *str, size_t len)
+{
+ static char echo_start[] = "echo -n ";
+ static char echo_end[] = "\n";
+ if (len == 0)
+ return;
+
+ buffer_add (buf, echo_start, strlen (echo_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, echo_end, strlen (echo_end));
+}
+
+
+void
+bash_setup (char *shell, list_t * env)
+{
+ list_t *next;
+
+ /* populate the environment */
+ while (env)
+ {
+ next = env->next;
+ putenv (env->buf);
+ env = next;
+ }
+
+ /* start the subshell */
+ bash_open (shell);
+}
+
+
+void
+bash_doscript (buffer_t * script, char *name)
+{
+ static char postfix[] = "\nexit\n";
+
+ /* dump the script to the subshell */
+ write (subshell_pipe[PARENT_OUT], script->data, script->ptr - script->data);
+
+ /* write the postfix */
+ write (subshell_pipe[PARENT_OUT], postfix, strlen (postfix));
+
+
+ return;
+
+}
diff --git a/src/h_bash.h b/src/h_bash.h
new file mode 100644
index 0000000..d92a493
--- /dev/null
+++ b/src/h_bash.h
@@ -0,0 +1,23 @@
+/*
+ * $Id: haserl.h,v 1.14 2005/11/18 14:43:10 nangel Exp $
+ */
+#ifndef H_SUBSHELL_H
+#define H_SUBSHELL_H 1
+
+
+/* the "names" for the pipes to the subshell */
+enum pipe_t { PARENT_IN, PARENT_OUT };
+
+/* h_bash.c */
+void bash_open(char *shell);
+void bash_destroy(void);
+void bash_exec(buffer_t *buf, char *str);
+void bash_wait(buffer_t *buf, char *str);
+void bash_echo(buffer_t *buf, char *str, size_t len);
+void bash_eval(buffer_t *buf, char *str, size_t len);
+void bash_setup(char *shell, list_t *env);
+void bash_doscript(buffer_t *script, char *name);
+
+
+
+#endif /* !H_SUBSHELL_H */
diff --git a/src/h_error.c b/src/h_error.c
new file mode 100644
index 0000000..46bd6e1
--- /dev/null
+++ b/src/h_error.c
@@ -0,0 +1,103 @@
+/*
+ * --------------------------------------------------------------------------
+ * Error functions for haserl
+ * Copyright (c) 2003-2006 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 2, as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "common.h"
+#include "h_script.h"
+#include "haserl.h"
+#include "h_error.h"
+
+char *g_err_msg[] = {
+ "",
+ "Memory Allocation Failure",
+ "Unable to open file %s",
+ "%c&gt; before &lt;%c",
+ "Missing %c&gt;",
+ "Unknown operation",
+ "Unable to start subshell",
+ "Unspecified Error",
+};
+
+
+
+/*
+ * abort the program
+ */
+void
+die_with_error (char *msg)
+{
+ fprintf (stderr, "Error: %s\n", msg);
+ exit (-1);
+}
+
+
+/* print an error message and die. If sp or where are non-null pointers, then
+ a line is added saying where in the script buffer the error occured. If
+ there's a request method, then http headers are added.
+ */
+void
+die_with_message (void *sp, char *where, const char *s, ...)
+{
+ #ifndef JUST_LUACSHELL
+ script_t *script = sp;
+ #endif
+ va_list p;
+ FILE *fo = stderr;
+
+ if (global.silent == FALSE)
+ {
+ if (getenv ("REQUEST_METHOD"))
+ {
+ fo = stdout;
+ fprintf (fo, "HTTP/1.0 500 Server Error\n"
+ "Content-Type: text/html\n\n"
+ "<html><body><b><font color=#CC0000>" PACKAGE_NAME
+ " CGI Error</font></b><br><pre>\n");
+ }
+ va_start (p, s);
+ vfprintf (fo, s, p);
+ va_end (p);
+ #ifndef JUST_LUACSHELL
+ if (where && sp)
+ {
+ fprintf (fo, " near line %d of %s\n",
+ count_lines (script->buf, script->size, where),
+ script->name);
+ }
+ #endif
+ printf ("\n");
+
+ if (getenv ("REQUEST_METHOD"))
+ fprintf (fo, "</pre></body></html>\n");
+ }
+ exit (-1);
+
+}
diff --git a/src/h_error.h b/src/h_error.h
new file mode 100644
index 0000000..af8d9aa
--- /dev/null
+++ b/src/h_error.h
@@ -0,0 +1,19 @@
+/*
+ * $Id: haserl.h,v 1.14 2005/11/18 14:43:10 nangel Exp $
+ */
+#ifndef H_ERROR_H
+#define H_ERROR_H 1
+
+
+enum error_types { E_NO_ERROR, E_MALLOC_FAIL, E_FILE_OPEN_FAIL,
+ E_END_BEFORE_BEGIN, E_NO_END_MARKER ,
+ E_NO_OP, E_SUBSHELL_FAIL, E_WHATEVER };
+
+extern char *g_err_msg[];
+
+/* h_error.c */
+void die_with_error(char *msg);
+void die_with_syntax(void *script, char *where, int error);
+void
+die_with_message ( void *sp, char *where, const char *s, ...);
+#endif /* !H_ERROR_H */
diff --git a/src/h_lua.c b/src/h_lua.c
new file mode 100644
index 0000000..6bee6e2
--- /dev/null
+++ b/src/h_lua.c
@@ -0,0 +1,155 @@
+/* --------------------------------------------------------------------------
+ * lua language specific functions
+ * Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "common.h"
+#include "h_error.h"
+#include "h_lua.h"
+#include "h_script.h"
+#include "haserl.h"
+
+extern lua_State *lua_vm;
+
+/* attempts to open a file, tokenize and then process it as a haserl script */
+int
+h_lua_loadfile (lua_State * L)
+{
+ script_t *scriptchain;
+ token_t *tokenchain;
+ buffer_t script_text;
+ int status;
+
+ /* get the filename */
+ const char *filename = luaL_checkstring (L, 1);
+
+
+ scriptchain = load_script ((char *) filename, NULL);
+ tokenchain = build_token_list (scriptchain, NULL);
+ preprocess_token_list (tokenchain);
+ process_token_list (&script_text, tokenchain);
+ free_token_list (tokenchain);
+ free_script_list (scriptchain);
+
+ /* script_text has the include file */
+ status = luaL_loadbuffer (L, (char *) script_text.data,
+ script_text.ptr - script_text.data, filename);
+ buffer_destroy (&script_text);
+ if (status)
+ {
+ lua_error (L);
+ }
+ return (1); /* we return one value, the buffer, as a function */
+}
+
+void
+lua_exec (buffer_t * buf, char *str)
+{
+ buffer_add (buf, str, strlen (str));
+}
+
+void
+lua_doscript (buffer_t * script, char *name)
+{
+ int status;
+ /* force the string to be null terminated */
+
+ buffer_add (script, "\0", 1);
+
+ status = luaL_loadbuffer (lua_vm, (char *) script->data,
+ strlen ((char *) script->data), name) ||
+ lua_pcall (lua_vm, 0, LUA_MULTRET, 0);
+
+ if (status && !lua_isnil (lua_vm, -1))
+ {
+ const char *msg = lua_tostring (lua_vm, -1);
+ if (msg == NULL)
+ msg = "(error object is not a string)";
+ die_with_message (NULL, NULL, msg);
+ }
+}
+
+
+/* Run the echo command in a subshell */
+void
+lua_echo (buffer_t * buf, char *str, size_t len)
+{
+ static char echo_start[] = " io.write ";
+ char quote[200] = "]=]"; /* 197 nested comments is a problem */
+
+ if (len == 0)
+ return;
+
+ /* figure out if the string uses ]] ]=] ]==] etc in it */
+ while ((strstr (str, quote)) && (strlen (quote) < 198))
+ {
+ memmove (quote + strlen (quote) - 1, quote + strlen (quote) - 2, 3);
+ }
+
+ /* As of 5.1, nested comments are depreciated... sigh */
+ quote[0] = '[';
+ quote[strlen (quote) - 1] = quote[0];
+ while ((strstr (str, quote)) && (strlen (quote) < 198))
+ {
+ memmove (quote + strlen (quote) - 1, quote + strlen (quote) - 2, 3);
+ }
+
+ buffer_add (buf, echo_start, strlen (echo_start));
+ buffer_add (buf, quote, strlen (quote));
+ buffer_add (buf, str, len);
+ quote[0] = ']';
+ quote[strlen (quote) - 1] = quote[0];
+ buffer_add (buf, quote, strlen (quote));
+ buffer_add (buf, "\n", 1);
+
+}
+
+
+/* do an evaluation */
+void
+lua_eval (buffer_t * buf, char *str, size_t len)
+{
+ static char start[] = " io.write(tostring(";
+ static char end[] = "))\n";
+ if (len == 0)
+ return;
+
+ buffer_add (buf, start, strlen (start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, end, strlen (end));
+}
+
diff --git a/src/h_lua.h b/src/h_lua.h
new file mode 100644
index 0000000..c0eeb20
--- /dev/null
+++ b/src/h_lua.h
@@ -0,0 +1,13 @@
+#ifndef H_LUA_H
+#define H_LUA_H 1
+
+
+void bash_open(char *shell);
+void lua_exec(buffer_t *buf, char *str);
+void lua_echo(buffer_t *buf, char *str, size_t len);
+void lua_eval(buffer_t *buf, char *str, size_t len);
+void lua_doscript(buffer_t *script, char *name);
+int h_lua_loadfile(lua_State *L);
+
+
+#endif
diff --git a/src/h_lua_common.c b/src/h_lua_common.c
new file mode 100644
index 0000000..11a598e
--- /dev/null
+++ b/src/h_lua_common.c
@@ -0,0 +1,114 @@
+/* --------------------------------------------------------------------------
+ * functions shared among both Lua shells
+ * Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "common.h"
+#include "h_lua_common.h"
+#ifdef INCLUDE_LUASHELL
+#include "h_lua.h"
+#endif
+#ifdef INCLUDE_LUACSHELL
+#include "h_luac.h"
+#endif
+#include "h_error.h"
+
+/* this is not a mistake. We are including the
+ * definition of the lualib here
+ */
+#include "haserl_lualib.inc"
+
+lua_State *lua_vm = NULL;
+
+void
+lua_common_putenv (char *str)
+{
+ char *value;
+ value = index (str, '=');
+ if (value)
+ {
+ *value = (char) NULL;
+ value++;
+ }
+ else
+ {
+ value = str + strlen (str);
+ }
+
+
+ lua_getglobal (lua_vm, "haserl");
+ lua_pushstring (lua_vm, "myputenv");
+ lua_gettable (lua_vm, -2);
+ lua_pushstring (lua_vm, str);
+ lua_pushstring (lua_vm, value);
+ lua_call (lua_vm, 2, 0);
+ return;
+}
+
+void
+lua_common_setup (char *shell, list_t * env)
+{
+ /* create a lua instance */
+ lua_vm = luaL_newstate ();
+ luaL_openlibs (lua_vm);
+
+ /* and load our haserl library */
+ if (luaL_loadbuffer (lua_vm, (const char *) &haserl_lualib, sizeof(haserl_lualib), "luascript.lua") ||
+ lua_pcall (lua_vm, 0, 0, 0))
+ {
+ die_with_message (NULL, NULL,
+ "Error passing the lua library to the lua vm: %s", lua_tostring (lua_vm, -1));
+ }
+
+ /* and put the vars in the vm */
+ while (env)
+ {
+ lua_common_putenv (env->buf);
+ env = env->next;
+ }
+
+
+ /* register our open function in the haserl table */
+ lua_getglobal (lua_vm, "haserl");
+ lua_pushstring (lua_vm, "loadfile");
+ #if defined(INCLUDE_LUASHELL) && defined(INCLUDE_LUACSHELL)
+ lua_pushcfunction(lua_vm, shell[3] == 'c' ? h_luac_loadfile : h_lua_loadfile);
+ #elif defined(INCLUDE_LUASHELL)
+ lua_pushcfunction (lua_vm, h_lua_loadfile);
+ #else /* INCLUDE_LUACSHELL */
+ lua_pushcfunction(lua_vm, h_luac_loadfile);
+ #endif
+ lua_settable (lua_vm, -3);
+
+}
+
+void
+lua_common_destroy (void)
+{
+ /* close the lua instance */
+ lua_close (lua_vm);
+}
diff --git a/src/h_lua_common.h b/src/h_lua_common.h
new file mode 100644
index 0000000..18b0e68
--- /dev/null
+++ b/src/h_lua_common.h
@@ -0,0 +1,7 @@
+#ifndef _H_LUA_COMMON_H
+#define _H_LUA_COMMON_H
+
+void lua_common_setup(char *shell, list_t *env);
+void lua_common_destroy(void);
+
+#endif
diff --git a/src/h_luac.c b/src/h_luac.c
new file mode 100644
index 0000000..55e4083
--- /dev/null
+++ b/src/h_luac.c
@@ -0,0 +1,55 @@
+/* --------------------------------------------------------------------------
+ * lua language specific functions (for compiled Lua chunks)
+ * Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "common.h"
+#include "h_luac.h"
+#include "h_error.h"
+
+extern lua_State *lua_vm;
+
+ /* tokenizer was not used, so script is NULL, but name is the filename to process */
+void
+luac_doscript (buffer_t *script, char *name)
+{
+ if (luaL_loadfile (lua_vm, name) || lua_pcall(lua_vm, 0, LUA_MULTRET, 0))
+ {
+ die_with_message (NULL, NULL, "Cannot load lua and execute chunk: %s", lua_tostring(lua_vm, -1));
+ }
+}
+
+int
+h_luac_loadfile (lua_State *L)
+{
+ const char *filename = luaL_checkstring (L, 1);
+
+ if (luaL_loadfile (L, filename))
+ {
+ die_with_message (NULL, NULL, "Cannot load file '%s': %s", filename, lua_tostring(L, -1));
+ } /* no error: function is on the stack */
+
+ return 1;
+}
diff --git a/src/h_luac.h b/src/h_luac.h
new file mode 100644
index 0000000..37c6536
--- /dev/null
+++ b/src/h_luac.h
@@ -0,0 +1,7 @@
+#ifndef _H_LUAC_H
+#define _H_LUAC_H
+
+void luac_doscript(buffer_t *script, char *name);
+int h_luac_loadfile(lua_State * L);
+
+#endif
diff --git a/src/h_script.c b/src/h_script.c
new file mode 100644
index 0000000..f719953
--- /dev/null
+++ b/src/h_script.c
@@ -0,0 +1,347 @@
+/*--------------------------------------------------------------------------
+ * functions related to opening, tokenizing and parsing a haserl script
+ * Copyright (c) 2006-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "common.h"
+#include "h_error.h"
+#include "h_script.h"
+#include "h_bash.h"
+#include "haserl.h"
+
+/* HTML, RUN, INCLUDE, EVAL NOOP }; */
+
+const char *g_tag[] = {
+ "",
+ "",
+ "in",
+ "=",
+ ""
+};
+
+/* the open and close tags */
+char open_tag[3] = "<%";
+char close_tag[3] = "%>";
+
+
+/* Open a script and return a populated script_t structure
+ */
+script_t *
+load_script (char *filename, script_t * scriptlist)
+{
+ script_t *scriptbuf;
+ int scriptfp;
+ struct stat filestat;
+
+ scriptfp = open (filename, O_NONBLOCK + O_RDONLY);
+ if (scriptfp == -1)
+ { /* open failed */
+ die_with_message (NULL, NULL, g_err_msg[E_FILE_OPEN_FAIL], filename);
+ }
+
+ fstat (scriptfp, &filestat);
+ scriptbuf = (script_t *) xmalloc (sizeof (script_t));
+ scriptbuf->name = (char *) xmalloc (strlen (filename) + 1);
+ scriptbuf->buf = (char *) xmalloc (filestat.st_size + 1);
+
+ memset (scriptbuf->name, 0, strlen (filename) + 1);
+ memcpy (scriptbuf->name, filename, strlen (filename));
+ memset (scriptbuf->buf, 0, filestat.st_size + 1);
+ read (scriptfp, scriptbuf->buf, filestat.st_size);
+
+ scriptbuf->size = filestat.st_size;
+ scriptbuf->uid = filestat.st_uid;
+ scriptbuf->gid = filestat.st_gid;
+ scriptbuf->curpos = 0;
+ scriptbuf->next = NULL;
+
+ /* if we already have scripts, add this one to the end */
+ if (scriptlist != NULL)
+ {
+ while (scriptlist->next)
+ scriptlist = scriptlist->next;
+ scriptlist->next = scriptbuf;
+ }
+
+ /* skip over the first line, if the file starts with a #!
+ * This means that offset will start at the beginning of
+ * the second line in most cases.
+ */
+ if (memcmp (scriptbuf->buf, "#!", 2) == 0)
+ {
+ while ((scriptbuf->curpos < scriptbuf->size) &&
+ ((char) scriptbuf->buf[scriptbuf->curpos] != '\n'))
+ {
+ (scriptbuf->curpos)++;
+ }
+ (scriptbuf->curpos)++;
+ }
+
+ /* If this is the first script, switch to <? ?> mode only
+ * if <% is not used anywhere else in the script. Otherwise
+ * don't change the tagging method
+ */
+ if (scriptlist == NULL)
+ {
+ if (strstr (scriptbuf->buf + scriptbuf->curpos, open_tag) == NULL)
+ {
+ open_tag[1] = '?';
+ close_tag[0] = '?';
+ }
+ }
+ close (scriptfp);
+ return (scriptbuf);
+}
+
+/* Free the script structures */
+void
+free_script_list (script_t * script)
+{
+ script_t *next;
+ while (script)
+ {
+ next = script->next;
+ if (script->name)
+ free (script->name);
+ if (script->buf)
+ free (script->buf);
+ free (script);
+ script = next;
+ }
+}
+
+/* Insert this token into the token chain.
+ * If tokenlist is null, create a new one
+ */
+token_t *
+push_token_on_list (token_t * tokenlist, script_t * scriptbuf,
+ char *start, size_t len)
+{
+ token_t *me, *next;
+ if (len == 0)
+ return (tokenlist);
+
+ me = (token_t *) xmalloc (sizeof (token_t));
+
+ if (tokenlist == NULL)
+ {
+ next = NULL;
+ }
+ else
+ {
+ next = tokenlist->next;
+ tokenlist->next = me;
+ }
+
+ me->next = next;
+ me->script = scriptbuf;
+ me->buf = start;
+ me->len = len;
+
+ return (me);
+}
+
+/* Free a token chain */
+void
+free_token_list (token_t * tokenlist)
+{
+ token_t *next;
+
+ while (tokenlist)
+ {
+ next = tokenlist->next;
+ free (tokenlist);
+ tokenlist = next;
+ }
+}
+
+#ifndef JUST_LUACSHELL
+
+/* build a tokenchain from a script. This step just
+ * splits a script buf into parts that are separated
+ * by <? ?>. If the <? ?> are out of order, then it
+ * flags that, but doesn't try to do anything else.
+ */
+token_t *
+build_token_list (script_t * scriptbuf, token_t * tokenlist)
+{
+
+ char *start, *end, *curpos, *endpos;
+ token_t *curtoken, *firsttoken;
+
+
+ curtoken = tokenlist;
+ firsttoken = tokenlist;
+
+ curpos = scriptbuf->buf + scriptbuf->curpos;
+ endpos = scriptbuf->buf + scriptbuf->size;
+
+ while (curpos < endpos)
+ {
+ start = strstr (curpos, open_tag);
+ end = strstr (curpos, close_tag);
+
+ if (start && !end)
+ die_with_message (scriptbuf, start, g_err_msg[E_NO_END_MARKER],
+ open_tag[1]);
+
+ if ((start > end) || (!start && end))
+ die_with_message (scriptbuf, end, g_err_msg[E_END_BEFORE_BEGIN],
+ open_tag[1], open_tag[1]);
+
+ if (start && (strstr (start + 1, open_tag)
+ && (strstr (start + 1, open_tag) < end)))
+ die_with_message (scriptbuf, start, g_err_msg[E_NO_END_MARKER],
+ open_tag[1]);
+
+ if (end)
+ {
+ /* push curpos to the start of the token */
+ curtoken = push_token_on_list (curtoken, scriptbuf, curpos,
+ start - curpos);
+ if (firsttoken == NULL)
+ firsttoken = curtoken;
+ /* push start of token to end of token */
+ curtoken =
+ push_token_on_list (curtoken, scriptbuf, start, end - start);
+ if (firsttoken == NULL)
+ firsttoken = curtoken;
+ /* push start of token to end of token */
+ curpos = end + 2;
+ }
+ else
+ {
+ /* push curpos to end of script */
+ curtoken =
+ push_token_on_list (curtoken, scriptbuf, curpos, endpos - curpos);
+ if (firsttoken == NULL)
+ firsttoken = curtoken;
+ curpos = endpos;
+ }
+ }
+
+ return (firsttoken);
+}
+
+/* syntax check a script */
+void
+preprocess_token_list (token_t * tokenlist)
+{
+ script_t *newscript;
+ token_t *me;
+ char *cp;
+
+ me = tokenlist;
+ /* walk the chain to fill in the tags */
+ while (me)
+ {
+ if (memcmp (me->buf, open_tag, 2))
+ {
+ me->tag = HTML;
+ }
+ else
+ {
+ me->tag = NOOP;
+ me->buf[me->len] = '\0';
+ cp = me->buf + 2; /* skip <? */
+ if (memcmp (cp, g_tag[INCLUDE], 2) == 0)
+ {
+ me->tag = INCLUDE;
+ /* skip the first word - in, include, include-file, etc */
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ cp = find_whitespace (me->buf);
+ *cp = '\0';
+ me->len = strlen (me->buf) + 1;
+ newscript = load_script (me->buf, me->script);
+ build_token_list (newscript, me);
+ }
+ if (memcmp (cp, g_tag[EVAL], 1) == 0)
+ {
+ me->tag = EVAL;
+ me->buf = find_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ if (isspace (*cp))
+ {
+ me->tag = RUN;
+ me->buf = cp;
+ }
+ if (me->tag == NOOP)
+ {
+ die_with_message (me->script, cp, g_err_msg[E_NO_OP]);
+ }
+ me->len = strlen (me->buf);
+ }
+ me = me->next;
+ }
+}
+
+
+token_t *
+process_token_list (buffer_t * buf, token_t * token)
+{
+ char *c;
+
+ buffer_init (buf);
+ shell_exec (buf, "\n"); /* try to get the error reporting match the line number */
+
+ while (token)
+ {
+ switch (token->tag)
+ {
+ case HTML:
+ /* Change from 0.8.0 - if the whole thing is just
+ whitespace, don't print it */
+ c = token->buf;
+ while ((c < (token->buf + token->len)) && (isspace (*c)))
+ c++;
+ if (c != token->buf + token->len)
+ shell_echo (buf, token->buf, token->len);
+ break;
+ case RUN:
+ shell_exec (buf, token->buf);
+ shell_exec (buf, "\n");
+ break;
+ case EVAL:
+ shell_eval (buf, token->buf, token->len);
+ break;
+ default:
+ break;
+ }
+ token = token->next;
+ }
+
+ return (token);
+
+}
+
+#endif
diff --git a/src/h_script.h b/src/h_script.h
new file mode 100644
index 0000000..97c9ed2
--- /dev/null
+++ b/src/h_script.h
@@ -0,0 +1,48 @@
+/*
+ * $Id: haserl.h,v 1.14 2005/11/18 14:43:10 nangel Exp $
+ */
+#ifndef H_SCRIPT_H
+#define H_SCRIPT_H 1
+
+
+/* Everything we care to know about a script */
+typedef struct {
+ char *name; /* pointer to name of script */
+ int size; /* size of script in bytes */
+ uid_t uid; /* user owner */
+ gid_t gid; /* group owner */
+ char *buf; /* pointer to malloc'ed buffer */
+ size_t curpos; /* current position in buffer */
+ void *next; /* next script in our chain */
+ } script_t;
+
+/* tag types */
+enum tag_t { HTML, RUN, INCLUDE, EVAL, NOOP };
+
+
+/* token structure */
+typedef struct {
+ script_t *script; /* the parent script */
+ enum tag_t tag; /* the token type */
+ size_t len; /* length of token */
+ char *buf; /* pointer to start of token */
+ void *next; /* the next token in the chain */
+ } token_t;
+
+
+
+/* h_script.c */
+script_t *load_script(char *filename, script_t *scriptlist);
+void free_script_list(script_t *script);
+token_t *push_token_on_list(token_t *tokenlist, script_t *scriptbuf, char *start, size_t len);
+void free_token_list(token_t *tokenlist);
+
+#ifndef JUST_LUACSHELL
+
+token_t *build_token_list(script_t *scriptbuf, token_t *tokenlist);
+void preprocess_token_list(token_t *tokenlist);
+token_t *process_token_list(buffer_t *buf, token_t *tokenlist);
+
+#endif
+
+#endif /* !H_SCRIPT_H */
diff --git a/src/haserl.c b/src/haserl.c
new file mode 100644
index 0000000..96ae420
--- /dev/null
+++ b/src/haserl.c
@@ -0,0 +1,823 @@
+/* --------------------------------------------------------------------------
+ * core of haserl.cgi - a poor-man's php for embedded/lightweight environments
+ * Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 2, as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * -----
+ * The x2c() and unescape_url() routines were taken from
+ * http://www.jmarshall.com/easy/cgi/getcgi.c.txt
+ *
+ * The comments in that text file state:
+ *
+ *** Written in 1996 by James Marshall, james@jmarshall.com, except
+ *** that the x2c() and unescape_url() routines were lifted directly
+ *** from NCSA's sample program util.c, packaged with their HTTPD.
+ *** For the latest, see http://www.jmarshall.com/easy/cgi/
+ * -----
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <grp.h>
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#include "common.h"
+#include "h_error.h"
+#include "h_script.h"
+#include "sliding_buffer.h"
+#include "rfc2388.h"
+#ifdef INCLUDE_BASHSHELL
+#include "h_bash.h"
+#endif
+
+#ifdef USE_LUA
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#include "h_lua_common.h"
+#endif
+#ifdef INCLUDE_LUASHELL
+#include "h_lua.h"
+#endif
+#ifdef INCLUDE_LUACSHELL
+#include "h_luac.h"
+#endif
+
+#ifdef DEBUG
+#include <syslog.h>
+#endif
+
+#include "haserl.h"
+
+#ifndef TEMPDIR
+#define TEMPDIR "/tmp"
+#endif
+
+#ifndef MAX_UPLOAD_KB
+#define MAX_UPLOAD_KB 2048
+#endif
+
+/* Refuse to disable the subshell */
+#ifndef SUBSHELL_CMD
+#define SUBSHELL_CMD "/bin/sh"
+#endif
+
+haserl_t global;
+
+
+/* declare the shell_ function pointers here */
+void (*shell_exec)(buffer_t *buf, char *str);
+void (*shell_echo)(buffer_t *buf, char *str, size_t len);
+void (*shell_eval)(buffer_t *buf, char *str, size_t len);
+void (*shell_setup)( char *, list_t *);
+void (*shell_doscript)( buffer_t *, char *);
+void (*shell_destroy) (void);
+
+
+
+/* global shell execution function pointers. These point to the actual functions
+ that do the job, based on the language */
+
+/*
+ * Command line / Config file directives When adding a long option, make sure
+ * to update the short_options as well
+ */
+
+struct option ga_long_options[] = {
+ {"version", no_argument, 0, 'v'},
+ {"help", no_argument, 0, 'h'},
+ {"debug", no_argument, 0, 'D'},
+ {"upload-limit", required_argument, 0, 'u'},
+ {"upload-dir", required_argument, 0, 'U'},
+ {"upload-handler", required_argument, 0, 'H' },
+ {"accept-all", no_argument, 0, 'a'},
+ {"accept-none", no_argument, 0, 'n'},
+ {"shell", required_argument, 0, 's'},
+ {"silent", no_argument, 0, 'S'},
+ {0, 0, 0, 0}
+};
+
+const char *gs_short_options = "vhDu:U:H:ans:S";
+
+/*
+ * Convert 2 char hex string into char it represents
+ * (from http://www.jmarshall.com/easy/cgi)
+ */
+char
+x2c (char *what)
+{
+ char digit;
+
+ digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
+ digit *= 16;
+ digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
+ return (digit);
+}
+
+/*
+ * unsescape %xx to the characters they represent
+ */
+/* Modified by Juris Feb 2007 */
+void
+unescape_url (char *url)
+{
+ int i, j;
+ for (i = 0, j = 0; url[j]; ++i, ++j)
+ {
+ if ((url[i] = url[j]) != '%')
+ continue;
+ if (!url[j + 1] || !url[j + 2])
+ break;
+ url[i] = x2c (&url[j + 1]);
+ j += 2;
+ }
+ url[i] = '\0';
+}
+
+
+
+
+/*
+ * allocate memory or die, busybox style.
+ */
+void *
+xmalloc (size_t size)
+{
+ void *buf;
+ if ((buf = malloc (size)) == NULL)
+ {
+ die_with_message (NULL, NULL, g_err_msg[E_MALLOC_FAIL]);
+ }
+ memset (buf, 0, size);
+ return buf;
+}
+
+
+/*
+ * realloc memory, or die xmalloc style.
+ */
+void *
+xrealloc (void *buf, size_t size)
+{
+ if ((buf = realloc (buf, size)) == NULL)
+ {
+ die_with_message (NULL, NULL, g_err_msg[E_MALLOC_FAIL]);
+ }
+ return buf;
+}
+
+
+/*
+ * adds or replaces the "key=value" value in the env_list chain
+ * prefix is appended to the key (e.g. FORM_key=value)
+ */
+list_t *
+myputenv (list_t * cur, char *str, char *prefix)
+{
+ list_t *prev = NULL;
+ size_t keylen;
+ char *entry = NULL;
+
+ entry = xmalloc (strlen (str) + strlen (prefix) + 1);
+ if (strlen (prefix))
+ {
+ memcpy (entry, prefix, strlen (prefix));
+ }
+ memcpy ((char *) (entry + strlen (prefix)), str, strlen (str));
+
+ keylen = (size_t) (index (entry, '=') - entry);
+
+ if (keylen <= 0)
+ {
+ free (entry);
+ return (NULL);
+ }
+
+ /* does the value already exist? */
+ while (cur != NULL)
+ {
+ if (memcmp (cur->buf, entry, keylen + 1) == 0)
+ {
+ entry[keylen] = '\0';
+ // unsetenv (entry);
+ entry[keylen] = '=';
+ free (cur->buf);
+ if (prev != NULL)
+ prev->next = cur->next;
+ free (cur);
+ cur = prev;
+ } /* end if found a matching key */
+ prev = cur;
+ if ( cur ) {
+ cur = (list_t *) cur->next;
+ }
+ } /* end if matching key */
+
+ /* add the value to the end of the chain */
+ cur = xmalloc (sizeof (list_t));
+ cur->buf = entry;
+ // putenv (cur->buf);
+ if (prev != NULL)
+ prev->next = cur;
+
+ return (cur);
+}
+
+/* free list_t chain */
+void
+free_list_chain (list_t * list)
+{
+ list_t *next;
+
+ while (list)
+ {
+ next = list->next;
+ free (list->buf);
+ free (list);
+ list = next;
+ }
+}
+
+
+
+/* readenv
+ * reads the current environment and popluates our environment chain
+ */
+
+void
+readenv (list_t * env)
+{
+ extern char **environ;
+ int count = 0;
+
+ while (environ[count] != NULL)
+ {
+ myputenv (env, environ[count], global.nul_prefix);
+ count++;
+ }
+}
+
+
+/* CookieVars ()
+ * if HTTP_COOKIE is passed as an environment variable,
+ * attempt to parse its values into environment variables
+ */
+void
+CookieVars (list_t * env)
+{
+ char *qs;
+ char *token;
+
+ if (getenv ("HTTP_COOKIE") != NULL)
+ {
+ qs = strdup (getenv ("HTTP_COOKIE"));
+ }
+ else
+ {
+ return;
+ }
+
+ /** split on; to extract name value pairs */
+ token = strtok (qs, ";");
+ while (token)
+ {
+ // skip leading spaces
+ while (token[0] == ' ')
+ {
+ token++;
+ }
+ myputenv (env, token, global.var_prefix);
+ token = strtok (NULL, ";");
+ }
+ free (qs);
+}
+
+
+/* SessionID
+ * Makes a uniqe SESSIONID environment variable for this script
+ */
+
+void
+sessionid (list_t * env)
+{
+ char session[29];
+
+ sprintf (session, "SESSIONID=%x%x", getpid (), (int) time (NULL));
+ myputenv (env, session, global.nul_prefix);
+}
+
+list_t *
+wcversion (list_t * env)
+{
+ char version[200];
+ sprintf (version, "HASERLVER=%s", PACKAGE_VERSION);
+ return (myputenv (env, version, global.nul_prefix));
+}
+
+
+void
+haserlflags (list_t * env)
+{
+ char buf[200];
+
+ snprintf (buf, 200, "HASERL_UPLOAD_DIR=%s", global.uploaddir);
+ myputenv (env, buf, global.nul_prefix);
+
+ snprintf (buf, 200, "HASERL_UPLOAD_LIMIT=%lu", global.uploadkb);
+ myputenv (env, buf, global.nul_prefix);
+
+ snprintf (buf, 200, "HASERL_ACCEPT_ALL=%d", global.acceptall);
+ myputenv (env, buf, global.nul_prefix);
+
+ snprintf (buf, 200, "HASERL_SHELL=%s", global.shell);
+ myputenv (env, buf, global.nul_prefix);
+
+}
+
+/*
+ * Read cgi variables from query string, and put in environment
+ */
+
+int
+ReadCGIQueryString (list_t * env)
+{
+ char *qs;
+ char *token;
+ int i;
+
+ if (getenv ("QUERY_STRING") != NULL)
+ {
+ qs = strdup (getenv ("QUERY_STRING"));
+ }
+ else
+ {
+ return (0);
+ }
+
+ /* change plusses into spaces */
+ for (i = 0; qs[i]; i++)
+ {
+ if (qs[i] == '+')
+ {
+ qs[i] = ' ';
+ }
+ };
+
+ /** split on & and ; to extract name value pairs */
+
+ token = strtok (qs, "&;");
+ while (token)
+ {
+ unescape_url (token);
+ myputenv (env, token, global.var_prefix);
+ token = strtok (NULL, "&;");
+ }
+ free (qs);
+ return (0);
+}
+
+
+/*
+ * Read cgi variables from stdin (for POST queries)
+ */
+
+int
+ReadCGIPOSTValues (list_t * env)
+{
+ int content_length = 0;
+ int max_len;
+ int i, x;
+ sliding_buffer_t sbuf;
+ buffer_t token;
+ unsigned char *data;
+
+ if (getenv ("CONTENT_LENGTH" ) == NULL )
+ return (0);
+
+ if (getenv ("CONTENT_TYPE"))
+ {
+ if (strncasecmp (getenv ("CONTENT_TYPE"), "multipart/form-data", 19)
+ == 0)
+ {
+ /* This is a mime request, we need to go to the mime handler */
+ i = rfc2388_handler (env);
+ return (i);
+ }
+ }
+
+ s_buffer_init (&sbuf, 32768);
+ sbuf.fh = STDIN;
+ buffer_init (&token);
+
+
+ /* Allow 2MB content, unless they have a global upload set */
+ max_len = ((global.uploadkb == 0) ? 2048 : global.uploadkb) *1024;
+
+ do
+ {
+ /* x is true if this token ends with a matchstr or is at the end of stream */
+ x = s_buffer_read (&sbuf, "&");
+ content_length += sbuf.len;
+ if (content_length > max_len)
+ {
+ die_with_message (NULL, NULL,
+ "Attempted to send content larger than allowed limits.");
+ }
+
+ if ((x == 0) || (token.data))
+ {
+ buffer_add (&token, (char *) sbuf.segment, sbuf.len);
+ }
+
+ if (x)
+ {
+ data = sbuf.segment;
+ sbuf.segment[sbuf.len] = '\0';
+ if (token.data)
+ {
+ /* add the ASCIIZ */
+ buffer_add (&token, sbuf.segment + sbuf.len, 1);
+ data = token.data;
+ }
+
+ /* change plusses into spaces */
+ for (i = 0; i < strlen ((char *) data); i++)
+ {
+ if (data[i] == '+')
+ {
+ data[i] = ' ';
+ }
+ }
+ unescape_url ((char *) data);
+ myputenv (env, (char *) data, global.var_prefix);
+ if (token.data)
+ {
+ buffer_reset (&token);
+ }
+ }
+ }
+ while (!sbuf.eof);
+ s_buffer_destroy (&sbuf);
+ buffer_destroy (&token);
+ return (0);
+}
+
+
+int
+parseCommandLine (int argc, char *argv[])
+{
+ int c;
+ int option_index = 0;
+
+ /* set optopt and optind to 0 to reset getopt_long -
+ * we may call it multiple times
+ */
+ optopt = 0;
+ optind = 0;
+
+ while ((c = getopt_long (argc, argv, gs_short_options,
+ ga_long_options, &option_index)) != -1)
+ {
+ switch (c)
+ {
+ case 'D':
+ global.debug = TRUE;
+ break;
+ case 's':
+ global.shell = optarg;
+ break;
+ case 'S':
+ global.silent = TRUE;
+ break;
+ case 'u':
+ if (optarg)
+ {
+ global.uploadkb = atoi (optarg);
+ }
+ else
+ {
+ global.uploadkb = MAX_UPLOAD_KB;
+ }
+ break;
+ case 'a':
+ global.acceptall = TRUE;
+ break;
+ case 'n':
+ global.acceptall = NONE;
+ break;
+ case 'U':
+ global.uploaddir = optarg;
+ break;
+ case 'H' :
+ global.uploadhandler = optarg;
+ break;
+ case 'v':
+ case 'h':
+ printf ("This is " PACKAGE_NAME " version " PACKAGE_VERSION ""
+ "(http://haserl.sourceforge.net)\n");
+ exit (0);
+ break;
+ }
+ }
+ return (optind);
+}
+
+int
+BecomeUser (uid_t uid, gid_t gid)
+{
+ /* This silently fails if it doesn't work */
+ /* Following is from Timo Teras */
+ if (getuid() == 0)
+ setgroups(1, &gid);
+
+ setgid (gid);
+ setgid(getgid());
+
+ setuid (uid);
+ setuid(getuid());
+
+ return (0);
+}
+
+/*
+ * Assign default values to the global structure
+ */
+
+void
+assignGlobalStartupValues ()
+{
+ global.uploadkb = 0; /* how big an upload do we allow (0 for none) */
+ global.shell = SUBSHELL_CMD; /* The shell we use */
+ global.silent = FALSE; /* We do print errors if we find them */
+ global.uploaddir = TEMPDIR; /* where to upload to */
+ global.uploadhandler = NULL; /* the upload handler */
+ global.debug = FALSE; /* Not in debug mode. */
+ global.acceptall = FALSE; /* don't allow POST data for GET method */
+ global.uploadlist = NULL; /* we don't have any uploaded files */
+ global.var_prefix = HASERL_VAR_PREFIX;
+ global.nul_prefix = "";
+
+}
+
+
+void
+unlink_uploadlist ()
+{
+ token_t *me;
+ me = global.uploadlist;
+
+ while (me)
+ {
+ unlink (me->buf);
+ free (me->buf);
+ me = me->next;
+ }
+
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Main
+ *
+ *------------------------------------------------------------------------*/
+
+int
+main (int argc, char *argv[])
+{
+ #ifndef JUST_LUACSHELL
+ token_t *tokenchain = NULL;
+ buffer_t script_text;
+ #endif
+ script_t *scriptchain;
+
+ int retval = 0;
+ char *filename = NULL;
+
+ argv_t *av = NULL;
+ char **av2;
+
+ int command;
+ int count;
+
+ list_t *env = NULL;
+
+ #ifdef DEBUG
+ openlog("haserl", LOG_NDELAY, LOG_LOCAL0);
+ syslog (LOG_ERR, "Haserl starting...");
+ #endif
+
+ assignGlobalStartupValues();
+ #ifndef JUST_LUACSHELL
+ buffer_init (&script_text);
+ #endif
+
+ /* if more than argv[1] and argv[1] is not a file */
+ switch (argc)
+ {
+ case 1:
+ /* we were run, instead of called as a shell script */
+ puts ("This is " PACKAGE_NAME " version " PACKAGE_VERSION "\n"
+ "This program runs as a cgi interpeter, not interactively\n"
+ "Please see: http://haserl.sourceforge.net");
+ return (0);
+ break;
+ case 2:
+ filename = argv[1];
+ break;
+ default: /* more than two */
+ command = argc_argv (argv[1], &av, "");
+
+ /* and we need to add the original argv[0] command for optarg to work */
+ av2 = xmalloc (sizeof (char *) * (command + 2));
+ av2[0] = argv[0];
+ for (count = 0; count < command; count++)
+ {
+ av2[count + 1] = av[count].string;
+ }
+ parseCommandLine (command + 1, av2);
+ argv[1] = av[1].string;
+ free (av);
+ free (av2);
+ filename = argv[2];
+ break;
+ /* we silently ignore 3,4,etc. */
+ }
+
+
+ scriptchain = load_script (filename, NULL);
+/* drop permissions */
+ BecomeUser (scriptchain->uid, scriptchain->gid);
+
+ /* populate the function pointers based on the shell selected */
+ if (strcmp(global.shell, "lua") && strcmp(global.shell, "luac")) /* default to "bash" */
+ {
+ #ifdef INCLUDE_BASHSHELL
+ shell_exec = &bash_exec;
+ shell_echo = &bash_echo;
+ shell_eval = &bash_eval;
+ shell_setup = &bash_setup;
+ shell_doscript = &bash_doscript;
+ shell_destroy = &bash_destroy;
+ #else
+ die_with_message (NULL, NULL, "Bash shell is not enabled.");
+ #endif
+ }
+ else
+ {
+ #ifdef USE_LUA
+ shell_setup = &lua_common_setup;
+ shell_destroy = &lua_common_destroy;
+ global.var_prefix = "FORM.";
+ global.nul_prefix = "ENV.";
+
+ if(global.shell[3] == 'c') /* compiled Lua specific function ptr... */
+ #ifdef INCLUDE_LUACSHELL
+ shell_doscript = &luac_doscript;
+ #else
+ die_with_message (NULL, NULL, "Compiled Lua shell is not enabled.");
+ #endif
+ else
+ {
+ #ifdef INCLUDE_LUASHELL
+ shell_exec = &lua_exec;
+ shell_echo = &lua_echo;
+ shell_eval = &lua_eval;
+ shell_doscript = &lua_doscript;
+ #else
+ die_with_message (NULL, NULL, "Standard Lua shell is not enabled.");
+ #endif
+ }
+ #else
+ die_with_message (NULL, NULL, "Lua shells are not enabled.");
+ #endif
+ }
+
+/* Read the current environment into our chain */
+ env = wcversion (env);
+ readenv (env);
+ sessionid (env);
+ haserlflags (env);
+
+ #ifndef JUST_LUACSHELL
+ if (strcmp (global.shell, "luac"))
+ {
+ tokenchain = build_token_list (scriptchain, NULL);
+ preprocess_token_list (tokenchain);
+ }
+ #endif
+
+/* Read the request data */
+ if (global.acceptall != NONE)
+ {
+ /* If we have a request method, and we were run as a #! style script */
+ CookieVars (env);
+ if (getenv ("REQUEST_METHOD"))
+ {
+ if (strcasecmp (getenv ("REQUEST_METHOD"), "GET") == 0)
+ {
+ if (global.acceptall == TRUE)
+ ReadCGIPOSTValues (env);
+ ReadCGIQueryString (env);
+ }
+
+ if (strcasecmp (getenv ("REQUEST_METHOD"), "POST") == 0)
+ {
+ if (global.acceptall == TRUE)
+ retval = ReadCGIQueryString (env);
+ retval = ReadCGIPOSTValues (env);
+ }
+ }
+ }
+
+ #ifdef DEBUG
+ syslog (LOG_ERR, "The script is going to start.");
+ #endif
+
+ /* build a copy of the script to send to the shell */
+ #ifndef JUST_LUACSHELL
+ if (strcmp (global.shell, "luac"))
+ {
+ process_token_list (&script_text, tokenchain);
+ }
+ #endif
+
+ /* run the script */
+ if (global.debug == TRUE)
+ {
+ #ifndef JUST_LUACSHELL
+ if (getenv ("REQUEST_METHOD"))
+ {
+ write (1, "Content-Type: text/plain\n\n", 26);
+ }
+ write (1, script_text.data, script_text.ptr - script_text.data);
+ #else
+ die_with_message (NULL, NULL, "Debugging output doesn't work with the compiled Lua shell.");
+ #endif
+ }
+ else
+ {
+ shell_setup (global.shell, env);
+ #ifdef JUST_LUACSHELL
+ shell_doscript (NULL, scriptchain->name);
+ #else
+ shell_doscript (&script_text, scriptchain->name);
+ #endif
+ shell_destroy ();
+ }
+
+ #ifdef DEBUG
+ syslog (LOG_ERR, "The script has finished.");
+ #endif
+
+ if (global.uploadlist)
+ {
+ unlink_uploadlist ();
+ free_token_list (global.uploadlist);
+ }
+
+ #ifndef JUST_LUACSHELL
+ /* destroy the script */
+ buffer_destroy (&script_text);
+ free_token_list (tokenchain);
+ #endif
+
+ free_list_chain (env);
+ free_script_list (scriptchain);
+
+ #ifdef DEBUG
+ closelog();
+ #endif
+ return (0);
+
+}
diff --git a/src/haserl.h b/src/haserl.h
new file mode 100644
index 0000000..8cd5a9e
--- /dev/null
+++ b/src/haserl.h
@@ -0,0 +1,54 @@
+#ifndef _HASERL_H
+#define _HASERL_H 1
+
+
+/* Just a silly construct to contain global variables */
+typedef struct
+{
+ unsigned long uploadkb; /* how big an upload do we allow (0 for none) */
+ char *shell; /* The shell we use */
+ char *uploaddir; /* where we upload to */
+ char *uploadhandler; /* a handler for uploads */
+ char *var_prefix; /* what name we give to FORM variables */
+ char *nul_prefix; /* what name we give to environment variables */
+ token_t *uploadlist; /* a linked list of pathspecs */
+ int debug; /* true if in "debug" mode */
+ int acceptall; /* true if we'll accept POST data on GETs and vice versa */
+ int silent; /* true if we never print errors */
+} haserl_t;
+
+extern haserl_t global;
+
+char x2c(char *what);
+void unescape_url(char *url);
+void *xmalloc (size_t size);
+void *xrealloc (void *buf, size_t size);
+list_t *myputenv(list_t *cur, char *str, char *prefix);
+void free_list_chain ( list_t *);
+void readenv(list_t *env);
+void CookieVars(list_t *env);
+void sessionid(list_t *env);
+list_t *wcversion(list_t *env);
+void haserlflags(list_t *env);
+int ReadCGIQueryString(list_t *env);
+int ReadCGIPOSTValues(list_t *env);
+int LineToStr(char *string, size_t max);
+int ReadMimeEncodedInput(list_t *env);
+void PrintParseError(char *error, int linenum);
+int parseCommandLine(int argc, char *argv[]);
+int BecomeUser(uid_t uid, gid_t gid);
+void assignGlobalStartupValues(void);
+void unlink_uploadlist (void);
+int main(int argc, char *argv[]);
+
+
+extern void (*shell_exec)(buffer_t *buf, char *str);
+extern void (*shell_echo)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_eval)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_setup)( char *, list_t *);
+extern void (*shell_doscript)( buffer_t *, char *);
+extern void (*shell_destroy) (void);
+
+#endif /* !_HASERL_H */
+
+
diff --git a/src/haserl_lualib.inc b/src/haserl_lualib.inc
new file mode 100644
index 0000000..608c57f
--- /dev/null
+++ b/src/haserl_lualib.inc
@@ -0,0 +1,51 @@
+/* This file was automatically generated from foo. DO NOT EDIT */
+
+static const unsigned char haserl_lualib[] = {
+ 27, 76,117, 97, 81, 0, 1, 4, 4, 4, 8, 0, 3, 0, 0, 0,
+ 61, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 16,
+ 0, 0, 0, 10, 0, 0, 0, 74, 0, 0, 0,138, 0, 0, 0,135,
+ 128, 0, 0, 71, 64, 0, 0, 7, 0, 0, 0, 5, 0, 0, 0,100,
+ 0, 0, 0, 9, 64,128,129, 5, 0, 0, 0,100, 64, 0, 0, 9,
+ 64, 0,130, 5, 0, 0, 0,100,128, 0, 0, 9, 64,128,130, 30,
+ 0,128, 0, 6, 0, 0, 0, 4, 7, 0, 0, 0,104, 97,115,101,
+ 114,108, 0, 4, 5, 0, 0, 0, 70, 79, 82, 77, 0, 4, 4, 0,
+ 0, 0, 69, 78, 86, 0, 4, 9, 0, 0, 0,115,101,116,102,105,
+ 101,108,100, 0, 4, 9, 0, 0, 0,103,101,116,102,105,101,108,
+ 100, 0, 4, 9, 0, 0, 0,109,121,112,117,116,101,110,118, 0,
+ 3, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 37, 0, 0, 0,
+ 0, 2, 0, 10, 29, 0, 0, 0,133, 0, 0, 0,197, 64, 0, 0,
+ 198,128,192, 1, 0, 1, 0, 0, 65,193, 0, 0,220, 0,129, 1,
+ 22,128, 4,128, 5, 2, 1, 0, 64, 2, 0, 3, 28,130, 0, 1,
+ 26, 2, 0, 0, 22,192, 0,128, 5, 2, 1, 0, 64, 2, 0, 3,
+ 28,130, 0, 1,128, 1, 0, 4, 23, 64,193, 3, 22,128, 1,128,
+ 6,130, 1, 1, 26, 66, 0, 0, 22, 0, 0,128, 10, 2, 0, 0,
+ 137, 0, 2, 3,134,128, 1, 1, 22, 0, 0,128,137, 64, 0, 3,
+ 225,128, 0, 0, 22,128,250,127, 30, 0,128, 0, 6, 0, 0, 0,
+ 4, 3, 0, 0, 0, 95, 71, 0, 4, 7, 0, 0, 0,115,116,114,
+ 105,110,103, 0, 4, 6, 0, 0, 0,103,102,105,110,100, 0, 4,
+ 13, 0, 0, 0, 40, 91, 37,119, 95, 93, 43, 41, 40, 46, 63, 41,
+ 0, 4, 9, 0, 0, 0,116,111,110,117,109, 98,101,114, 0, 4,
+ 2, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, 45, 0,
+ 0, 0, 0, 1, 0, 8, 12, 0, 0, 0, 69, 0, 0, 0,133, 64,
+ 0, 0,134,128, 64, 1,192, 0, 0, 0, 1,193, 0, 0,156, 0,
+ 129, 1, 22, 0, 0,128, 70, 64,129, 0,161, 64, 0, 0, 22, 0,
+ 255,127, 94, 0, 0, 1, 30, 0,128, 0, 4, 0, 0, 0, 4, 3,
+ 0, 0, 0, 95, 71, 0, 4, 7, 0, 0, 0,115,116,114,105,110,
+ 103, 0, 4, 6, 0, 0, 0,103,102,105,110,100, 0, 4, 7, 0,
+ 0, 0, 91, 37,119, 95, 93, 43, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0,
+ 0, 53, 0, 0, 0, 0, 2, 0, 6, 20, 0, 0, 0,133, 0, 0,
+ 0,134, 64, 64, 1,192, 0, 0, 0, 1,129, 0, 0, 65,193, 0,
+ 0,156,128, 0, 2, 0, 0, 0, 1,133, 0, 0, 0,134, 64, 64,
+ 1,192, 0, 0, 0, 1, 1, 1, 0, 65,193, 0, 0,156,128, 0,
+ 2, 0, 0, 0, 1,133, 64, 1, 0,134,128, 65, 1,192, 0, 0,
+ 0, 0, 1,128, 0,156, 64,128, 1, 30, 0,128, 0, 7, 0, 0,
+ 0, 4, 7, 0, 0, 0,115,116,114,105,110,103, 0, 4, 5, 0,
+ 0, 0,103,115,117, 98, 0, 4, 7, 0, 0, 0, 91, 92, 93, 92,
+ 91, 93, 0, 4, 2, 0, 0, 0, 46, 0, 4, 6, 0, 0, 0, 91,
+ 92, 46, 93, 43, 0, 4, 7, 0, 0, 0,104, 97,115,101,114,108,
+ 0, 4, 9, 0, 0, 0,115,101,116,102,105,101,108,100, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
diff --git a/src/haserl_lualib.lua b/src/haserl_lualib.lua
new file mode 100644
index 0000000..045dbd3
--- /dev/null
+++ b/src/haserl_lualib.lua
@@ -0,0 +1,54 @@
+-- --------------------------------------------------------------------------
+-- haserl luascript library
+-- $Id: haserl.c,v 1.32 2005/11/22 15:56:42 nangel Exp $
+-- Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License, version 2,
+-- as published by the Free Software Foundation.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, write to the Free Software
+-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+--
+-- --------------------------------------------------------------------------
+
+haserl, FORM, ENV = {}, {}, {}
+
+function haserl.setfield (f, v)
+ -- From programming in Lua 1st Ed.
+ local t = _G -- start with the table of globals
+ for w, d in string.gfind(f, '([%w_]+)(.?)') do
+ if (tonumber(w)) then
+ w = tonumber(w)
+ end
+ if d == '.' then -- not last field?
+ t[w] = t[w] or {} -- create table if absent
+ t = t[w] -- get the table
+ else -- last field
+ t[w] = v -- do the assignment
+ end
+ end
+end
+
+function haserl.getfield (f)
+ local v = _G -- start with the table of globals
+ for w in string.gfind(f, '[%w_]+') do
+ v = v[w]
+ end
+ return v
+end
+
+function haserl.myputenv(key, value)
+ -- convert key to dotted form
+ key = string.gsub(key, '[\\]\\[]', '.' )
+ key = string.gsub(key, '[\\.]+', '.' )
+ -- and create a table if necessary
+ haserl.setfield (key, value)
+end
+
diff --git a/src/lua2c.c b/src/lua2c.c
new file mode 100644
index 0000000..f1f7abe
--- /dev/null
+++ b/src/lua2c.c
@@ -0,0 +1,97 @@
+/* --------------------------------------------------------------------------
+ * a simple lua 2 c function converter - a simple luac + bin2c
+ * Copyright (c) 2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ------------------------------------------------------------------------- */
+
+
+/* This program loads the source file, and then uses lua_dump to output it
+ * to a c const char array. Because lua_dump is used instead of the internal
+ * luaU_dump function, debugging info is in the output array. If you want
+ * to compile the array without debugging information, first use luac on the
+ * .lua source, and then run lua2c on that output:
+ * luac -s -o foo haserl_lualib.lua
+ * lua2c haserl_lualib foo >haserl_lualib.inc
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+lua_State *lua_vm = NULL;
+
+
+static void
+loadit (char *filename)
+{
+
+lua_vm = luaL_newstate();
+luaL_openlibs (lua_vm);
+
+if (luaL_loadfile(lua_vm, filename)) {
+ puts (lua_tostring(lua_vm, -1));
+ exit (-1);
+ }
+
+}
+
+static int writer (lua_State* L, const void* p, size_t size, void* u)
+{
+ static int count = 0;
+ int i;
+ for (i=0; i < size; i++ ) {
+ if ((count) && (count % 16) == 0 ) printf ("\n ");
+ printf ("%3d,", *((unsigned char *) (p+i)));
+ count++;
+ }
+
+ return (0);
+}
+
+
+static void
+dumpit()
+{
+lua_dump (lua_vm, writer, NULL);
+}
+
+
+int
+main (int argc, char *argv[]) {
+ if (argc != 3) {
+ printf("usage: %s varname luasource >output\n", argv[0]);
+ return (-1);
+ }
+
+ loadit (argv[2]);
+
+
+ printf ("/* This file was automatically generated from %s. DO NOT EDIT */\n\n", argv[2]);
+ printf ("static const unsigned char %s[] = { \n ", argv[1]);
+ dumpit();
+ printf ("\n};\n");
+
+ return (0);
+}
diff --git a/src/rfc2388.c b/src/rfc2388.c
new file mode 100644
index 0000000..7bdb3d7
--- /dev/null
+++ b/src/rfc2388.c
@@ -0,0 +1,514 @@
+/* --------------------------------------------------------------------------
+ * multipart/form-data handler functions (obviously, see rfc2388 for info)
+ * Copyright (c) 2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 2, as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#include "common.h"
+#include "h_error.h"
+#include "h_script.h"
+#include "h_bash.h"
+#include "sliding_buffer.h"
+#include "rfc2388.h"
+
+#if DEBUG
+#include <syslog.h>
+#endif
+
+#include "haserl.h"
+
+void
+empty_stdin (void)
+{
+ char c[2000];
+ while (read (STDIN_FILENO, &c, 2000))
+ {
+ };
+}
+
+
+void
+mime_var_init (mime_var_t * obj)
+{
+ obj->name = NULL;
+ obj->filename = NULL;
+ obj->type = NULL;
+ obj->tempname = NULL;
+ buffer_init (&(obj->value));
+ obj->fh = 0;
+}
+
+void
+mime_var_destroy (mime_var_t * obj)
+{
+ int status;
+
+ if (obj->name)
+ {
+ free (obj->name);
+ obj->name = NULL;
+ }
+ if (obj->filename)
+ {
+ free (obj->filename);
+ obj->filename = NULL;
+ }
+ if (obj->type)
+ {
+ free (obj->type);
+ obj->type = NULL;
+ }
+ if (obj->tempname)
+ {
+ free (obj->tempname);
+ obj->tempname = NULL;
+ }
+ buffer_destroy (&(obj->value));
+ if (obj->fh)
+ {
+ close (obj->fh);
+ if (global.uploadhandler)
+ {
+ wait (&status);
+ }
+ obj->fh = 0;
+ }
+}
+
+char *
+mime_substr (char *start, int len)
+{
+ char *ptr;
+
+ if (!start)
+ return NULL;
+ if (len < 0)
+ return NULL;
+ ptr = xmalloc (len + 2);
+ memcpy (ptr, start, len);
+ return ptr;
+
+}
+
+void
+mime_tag_add (mime_var_t * obj, char *str)
+{
+ char *a = NULL;
+ char *b = NULL;
+ static char *tag[] = { "name=\"", "filename=\"", "Content-Type: " };
+
+ a = strcasestr (str, tag[0]);
+ if (a)
+ {
+ a += strlen (tag[0]);
+ b = strchr (a, '"');
+ if (!obj->name)
+ obj->name = mime_substr (a, b - a);
+ }
+
+ a = strcasestr (str, tag[1]);
+ if (a)
+ {
+ a += strlen (tag[1]);
+ b = strchr (a, '"');
+ if (!obj->filename)
+ obj->filename = mime_substr (a, b - a);
+ }
+
+ a = strcasestr (str, tag[2]);
+ if (a)
+ {
+ a += strlen (tag[2]);
+ b = a + strlen (a);
+ if (!obj->type)
+ obj->type = mime_substr (a, b - a);
+ }
+}
+
+void
+mime_var_putenv (list_t * env, mime_var_t * obj)
+{
+ buffer_t buf;
+ buffer_init (&buf);
+ if (obj->name)
+ {
+ buffer_add (&(obj->value), "", 1);
+ buffer_add (&buf, obj->name, strlen (obj->name));
+ buffer_add (&buf, "=", 1);
+ buffer_add (&buf, (char *) obj->value.data,
+ strlen ((char *) obj->value.data) + 1);
+ myputenv (env, (char *) buf.data, global.var_prefix);
+ buffer_reset (&buf);
+ }
+ if (obj->filename)
+ {
+ buffer_add (&buf, obj->name, strlen (obj->name));
+ buffer_add (&buf, "_name=", 6);
+ buffer_add (&buf, obj->filename, strlen (obj->filename) + 1);
+ myputenv (env, (char *) buf.data, global.var_prefix);
+ buffer_reset (&buf);
+ }
+ buffer_destroy (&buf);
+}
+
+void
+mime_exec (mime_var_t * obj, char *fifo)
+{
+
+ int pid;
+ char *av[4];
+ char *type, *filename, *name;
+ char *c;
+ int fh;
+
+ pid = fork ();
+ if (pid == -1)
+ {
+ empty_stdin ();
+ die_with_message (NULL, NULL, g_err_msg[E_SUBSHELL_FAIL]);
+ }
+
+ if (pid == 0)
+ {
+
+ /* store the content type, filename, and form name */
+ /* we do not use global.var_prefix because it could be lua or shell
+ * or something else, and we are only shell here */
+ if (obj->type)
+ {
+
+ type = xmalloc (13 + strlen (obj->type) + 1);
+ sprintf (type, "CONTENT_TYPE=%s", obj->type);
+ putenv (type);
+ }
+ if (obj->filename)
+ {
+ filename = xmalloc (9 + strlen (obj->filename) + 1);
+ sprintf (filename, "FILENAME=%s", obj->filename);
+ putenv (filename);
+ }
+
+ if (obj->name)
+ {
+ name = xmalloc (5 + strlen (obj->name) + 1);
+ sprintf (name, "NAME=%s", obj->name);
+ putenv (name);
+ }
+
+ av[0] = global.uploadhandler;
+ av[1] = fifo;
+ av[2] = NULL;
+ execv (av[0], av);
+ /* if we get here, we had a failure. Not much we can do.
+ * We are the child, so we can't even warn the parent */
+#ifdef DEBUG
+ syslog (LOG_ERR, "execv of %s failed.", global.uploadhandler);
+#endif
+ fh = open (fifo, O_RDONLY);
+ while (read (fh, &c, 1))
+ {
+ }
+ exit (-1);
+ }
+ else
+ {
+ /* I'm parent */
+ }
+
+ /* control should get to this point only in the parent.
+ */
+} /* end mime_exec */
+
+void
+mime_var_open_target (mime_var_t * obj)
+{
+ char *tmpname;
+ token_t *curtoken;
+ curtoken = global.uploadlist;
+ int ok;
+
+ /* if upload_limit is zero, we die right here */
+ if (global.uploadkb == 0)
+ {
+ empty_stdin ();
+ die_with_message (NULL, NULL, "File uploads are not allowed.");
+ }
+
+
+ ok = -1;
+ tmpname = xmalloc (strlen (global.uploaddir) + 8);
+ strcpy (tmpname, global.uploaddir);
+ strcat (tmpname, "/XXXXXX");
+ obj->fh = mkstemp (tmpname);
+
+ if (obj->fh == -1)
+ {
+ ok = 0;
+ }
+
+ /* reuse the name as a fifo if we have a handler. We do this
+ * because tempnam uses TEMPDIR if defined, among other bugs
+ */
+ if ((ok) && global.uploadhandler)
+ {
+
+ /* I have a handler */
+ close (obj->fh);
+ unlink (tmpname);
+ if (mkfifo (tmpname, 0600))
+ ok = 0;
+ /* you must open the fifo for reading before writing
+ * on non linux systems
+ */
+ if (ok)
+ {
+ mime_exec (obj, tmpname);
+ obj->fh = open (tmpname, O_WRONLY);
+ }
+ if (obj->fh == -1)
+ ok = 0;
+ }
+ else
+ {
+ buffer_add (&(obj->value), tmpname, strlen (tmpname));
+ }
+
+ if (!ok)
+ {
+ empty_stdin ();
+ die_with_message (NULL, NULL, g_err_msg[E_FILE_OPEN_FAIL], tmpname);
+ }
+
+/* add this to the list of things to unlink at the end */
+#include <syslog.h>
+ openlog ("haserl", LOG_NDELAY, LOG_LOCAL0);
+
+ curtoken =
+ push_token_on_list (curtoken, NULL, tmpname, strlen (tmpname) + 1);
+ if (global.uploadlist == NULL)
+ {
+ global.uploadlist = curtoken;
+ }
+
+ syslog (LOG_ERR, "global.uploadlist is %p", global.uploadlist);
+
+}
+
+
+
+
+void
+mime_var_writer (mime_var_t * obj, char *str, int len)
+{
+ /* if not a file upload, then just a normal variable */
+ if (!obj->filename)
+ {
+ buffer_add (&(obj->value), str, len);
+ }
+
+ /* if a file upload, but don't have an open filehandle, open one */
+ if ((!obj->fh) && (obj->filename))
+ mime_var_open_target (obj);
+
+ /* if we have an open file, write the chunk */
+ if (obj->fh)
+ {
+ write (obj->fh, str, len);
+ }
+}
+
+/*
+ * Read multipart/form-data input (RFC2388), typically used when
+ * uploading a file.
+ */
+
+int
+rfc2388_handler (list_t * env)
+{
+ enum mime_state_t
+ { DISCARD, BOUNDARY, HEADER, CONTENT };
+
+
+ int state;
+ int i, x;
+ unsigned long max_len, content_length;
+ sliding_buffer_t sbuf;
+ char *crlf = "\r\n";
+ char *boundary;
+ char *str;
+ buffer_t buf;
+ mime_var_t var;
+
+ /* get the boundary info */
+ str = getenv ("CONTENT_TYPE");
+ i = strlen (str) - 9;
+ while ((i >= 0) && (memcmp ("boundary=", str + i, 9)))
+ {
+ i--;
+ }
+ if (i == -1)
+ {
+ empty_stdin ();
+ die_with_message (NULL, NULL, "No Mime Boundary Information Found");
+ }
+
+ i = i + 9;
+ if (str[i] == '"')
+ i++;
+
+ boundary = xmalloc (strlen (str + i) + 5); /* \r\n-- + NULL */
+ memcpy (boundary, crlf, 2);
+ memcpy (boundary + 2, "--", 2);
+ memcpy (boundary + 4, str + i, strlen (str + i) + 1);
+ if ((i > 0) && (str[i - 1] == '"'))
+ {
+ while ((boundary[i]) && (boundary[i] != '"')) i++;
+ boundary[i] = '\0';
+ }
+
+ /* Allow 2MB content, unless they have a global upload set */
+ max_len = ((global.uploadkb == 0) ? 2048 : global.uploadkb) *1024;
+ content_length = 0;
+
+ /* initialize a 128K sliding buffer */
+ s_buffer_init (&sbuf, 1024 * 128);
+ sbuf.fh = STDIN;
+
+ /* initialize the buffer, and make sure it doesn't point to null */
+ buffer_init (&buf);
+ buffer_add (&buf, "", 1);
+ buffer_reset (&buf);
+
+ state = DISCARD;
+ str = boundary + 2; /* skip the leading crlf */
+ do
+ {
+ /* x is true if this token ends with a matchstr or is at the end of stream */
+ x = s_buffer_read (&sbuf, str);
+ content_length += sbuf.len;
+ if (content_length >= max_len)
+ {
+ empty_stdin ();
+ free (boundary);
+ s_buffer_destroy (&sbuf);
+ buffer_destroy (&buf);
+ if (var.name)
+ {
+ mime_var_destroy (&var);
+ }
+ die_with_message (NULL, NULL,
+ "Attempted to send content larger than allowed limits.");
+ }
+
+ switch (state)
+ {
+
+ case DISCARD:
+ /* discard any text - used for first mime boundary */
+ if (x)
+ {
+ state = BOUNDARY;
+ str = crlf;
+ buffer_reset (&buf); /* reinitializes the buffer */
+ }
+ break;
+
+
+ case BOUNDARY:
+ if (!x)
+ {
+ buffer_add (&buf, sbuf.segment, sbuf.len);
+ }
+ if (x)
+ {
+ buffer_add (&buf, sbuf.segment, sbuf.len);
+ if (!memcmp (buf.data, boundary + 2, 2))
+ { /* "--" */
+ /* all done... what does that mean? */
+ str = boundary + 2;
+ state = DISCARD;
+ }
+ else
+ {
+ buffer_reset (&buf);
+ mime_var_init (&var);
+ state = HEADER;
+ str = crlf;
+ }
+ }
+ break;
+
+ case HEADER:
+ buffer_add (&buf, sbuf.segment, sbuf.len);
+ if (x)
+ {
+ if (sbuf.len == 0)
+ { /* blank line */
+ buffer_reset (&buf);
+ state = CONTENT;
+ str = boundary;
+ }
+ else
+ {
+ buffer_add (&buf, "", 1);
+ mime_tag_add (&var, (char *) buf.data);
+ buffer_reset (&buf);
+ }
+ }
+ break;
+
+ case CONTENT:
+ /* write to writer process, regardless */
+ mime_var_writer (&var, (char *) sbuf.segment, sbuf.len);
+ if (x)
+ {
+ buffer_reset (&buf);
+ mime_var_putenv (env, &var);
+ mime_var_destroy (&var);
+ state = BOUNDARY;
+ str = crlf;
+ }
+
+ break;
+
+ } /* end switch */
+
+ }
+ while (!sbuf.eof);
+ free (boundary);
+ s_buffer_destroy (&sbuf);
+ buffer_destroy (&buf);
+ return (0);
+}
diff --git a/src/rfc2388.h b/src/rfc2388.h
new file mode 100644
index 0000000..acad1ac
--- /dev/null
+++ b/src/rfc2388.h
@@ -0,0 +1,28 @@
+#ifndef _RFC2388_H
+#define _RFC2388_H 1
+
+typedef struct
+{
+ char *name; /* the variable name */
+ char *filename; /* the client-specified filename */
+ char *type; /* the mime-type */
+ char *tempname; /* the tempfilename */
+ buffer_t value; /* the value of the variable */
+ int fh; /* the output file handle */
+ } mime_var_t;
+
+
+/* rfc2388.c */
+void empty_stdin(void);
+void mime_var_init(mime_var_t *obj);
+void mime_var_destroy(mime_var_t *obj);
+char *mime_substr(char *start, int len);
+void mime_tag_add(mime_var_t *obj, char *str);
+void mime_var_putenv(list_t *env, mime_var_t *obj);
+void mime_exec(mime_var_t *obj, char *fifo);
+void mime_var_open_target(mime_var_t *obj);
+void mime_var_writer(mime_var_t *obj, char *str, int len);
+int rfc2388_handler(list_t *env);
+
+
+#endif /* _RFC2388_H */
diff --git a/src/sliding_buffer.c b/src/sliding_buffer.c
new file mode 100644
index 0000000..54adea6
--- /dev/null
+++ b/src/sliding_buffer.c
@@ -0,0 +1,170 @@
+/*
+ * --------------------------------------------------------------------------
+ * Sliding Buffer functions for haserl Copyright (c) 2007 Nathan Angelacos
+ * (nangel@users.sourceforge.net) This program is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License, version 2, as published by the Free Software Foundation. This
+ * program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details. You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * -------------------------------------------------------------------------
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "sliding_buffer.h"
+
+/*
+ * initialize a sliding buffer structure
+ */
+int
+s_buffer_init (sliding_buffer_t * sbuf, int size)
+{
+ sbuf->maxsize = size;
+ sbuf->buf = malloc (sbuf->maxsize);
+ /* reduce maxsize by one, so that you can add a NULL to the end of any
+ returned token and not have a memory overwrite */
+ sbuf->maxsize -= 1;
+ sbuf->fh = 0; /* use stdin by default */
+ sbuf->eof = 0;
+ sbuf->len = 0;
+ sbuf->ptr = sbuf->buf;
+ sbuf->bufsize = 0;
+ return (sbuf->buf != NULL); /* return true if the alloc succeeded */
+}
+
+/*
+ * destroy a sliding buffer structure
+ */
+void
+s_buffer_destroy (sliding_buffer_t * sbuf)
+{
+ free (sbuf->buf);
+}
+
+
+/*
+ * read the next segment from a sliding buffer. returns !=0 if the
+ * segment ends at a matchstr token, or if we are at the end of the string
+ * returns 0 if the segment does not end
+ */
+int
+s_buffer_read (sliding_buffer_t * sbuf, char *matchstr)
+{
+ int len, pos;
+ int r;
+
+ /*
+ * if eof and ptr ran off the buffer, then we are done
+ */
+ if ((sbuf->eof) && (sbuf->ptr > sbuf->buf))
+ {
+ return 0;
+ }
+
+ /*
+ * if need to fill the buffer, do so
+ */
+ if ((sbuf->bufsize == 0) ||
+ (sbuf->ptr >= (sbuf->buf + sbuf->bufsize - strlen (matchstr))))
+ {
+ len = sbuf->bufsize - (sbuf->ptr - sbuf->buf);
+ if (len)
+ {
+ memmove (sbuf->buf, sbuf->ptr, len);
+ }
+ sbuf->ptr = sbuf->buf;
+ sbuf->bufsize = len;
+ /* if the filedescriptor is invalid, we are obviously
+ * at an end of file condition.
+ */
+ if (fcntl (sbuf->fh, F_GETFL) == -1 ) {
+ r = 0; } else {
+ r = read (sbuf->fh, sbuf->buf + len, sbuf->maxsize - len);
+ }
+ /*
+ * only report eof when we've done a read of 0.
+ */
+ if (r == 0 || (r < 0 && errno != EINTR))
+ {
+ sbuf->eof = -1;
+ }
+ else
+ {
+ sbuf->bufsize += (r > 0) ? r : 0;
+ }
+ }
+
+ /*
+ * look for the matchstr
+ */
+ pos = 0;
+ len = sbuf->bufsize - (int) (sbuf->ptr - sbuf->buf) - strlen (matchstr);
+ while (memcmp (matchstr, sbuf->ptr + pos, strlen (matchstr)) && (pos < len))
+ {
+ pos++;
+ }
+
+ /*
+ * if we found it
+ */
+ if (pos < len)
+ {
+ sbuf->len = pos;
+ sbuf->segment = sbuf->ptr;
+ sbuf->ptr = sbuf->segment + pos + strlen (matchstr);
+ return -1;
+ }
+
+ if (sbuf->eof)
+ {
+ len += strlen (matchstr);
+ }
+
+ /*
+ * ran off the end, didn't find the matchstr
+ */
+ sbuf->segment = sbuf->ptr;
+ sbuf->len = len;
+ sbuf->ptr += sbuf->len;
+ return (sbuf->eof) ? (-1) : (0);
+}
+
+
+
+#ifdef TEST_FRAMEWORK
+
+main ()
+{
+ int x;
+ sliding_buffer_t sb;
+ char foo[200];
+
+ s_buffer_init (&sb, 32);
+
+ do
+ {
+ x = s_buffer_read (&sb, "&");
+ sprintf (foo, "%03d- %03d - %03d", x, sb.eof, sb.len);
+ write (1, foo, strlen (foo));
+ write (1, sb.segment, sb.len);
+ write (1, "\n", 1);
+ }
+ while ((!sb.eof));
+ s_buffer_destroy (&sb);
+}
+
+#endif
diff --git a/src/sliding_buffer.h b/src/sliding_buffer.h
new file mode 100644
index 0000000..838e386
--- /dev/null
+++ b/src/sliding_buffer.h
@@ -0,0 +1,25 @@
+#ifndef _SLIDING_BUF_H
+#define _SLIDING_BUF_H 1
+
+
+/* sliding buffer structure */
+typedef struct {
+ int fh; /* the input filehandle for the buffer */
+ unsigned char *buf; /* pointer to the buffer */
+ unsigned char *ptr; /* start positon (used internally) */
+ unsigned char *segment; /* the start position of this segment */
+ int len; /* length of this segment */
+ int maxsize; /* max size of buffer */
+ int bufsize; /* current size of buffer */
+ int eof; /* true if there is no more to read */
+ } sliding_buffer_t;
+
+
+
+/* sliding_buffer.c */
+int s_buffer_init(sliding_buffer_t *sbuf, int size);
+void s_buffer_destroy(sliding_buffer_t *sbuf);
+int s_buffer_read(sliding_buffer_t *sbuf, char *matchstr);
+
+
+#endif /* !_SLIDING_BUF_H */
diff --git a/src/stamp-h.in b/src/stamp-h.in
new file mode 100644
index 0000000..9788f70
--- /dev/null
+++ b/src/stamp-h.in
@@ -0,0 +1 @@
+timestamp