diff options
author | Nathan Angelacos <nangel@alpinelinux.org> | 2008-12-17 01:34:15 +0000 |
---|---|---|
committer | Nathan Angelacos <nangel@alpinelinux.org> | 2008-12-17 01:34:15 +0000 |
commit | 64b823303af9dcb002370d0611c5783a8c610442 (patch) | |
tree | 883d4d70a5a4f3c593063db13c1e1c2e19b52129 /src | |
parent | b6de69e02e1a4b2ffebc93cc609bbd000949591d (diff) | |
download | haserl-64b823303af9dcb002370d0611c5783a8c610442.tar.bz2 haserl-64b823303af9dcb002370d0611c5783a8c610442.tar.xz |
Reverted back to 0.9.24 codebase, releasing 0.9.25 off of it. 0.9.25 pre had new llist libraries that seem to be borked
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 66 | ||||
-rw-r--r-- | src/common.c | 370 | ||||
-rw-r--r-- | src/common.h | 62 | ||||
-rw-r--r-- | src/config.h.in | 103 | ||||
-rw-r--r-- | src/h_bash.c | 651 | ||||
-rw-r--r-- | src/h_bash.h | 41 | ||||
-rw-r--r-- | src/h_error.c | 103 | ||||
-rw-r--r-- | src/h_error.h | 19 | ||||
-rw-r--r-- | src/h_lua.c | 155 | ||||
-rw-r--r-- | src/h_lua.h | 12 | ||||
-rw-r--r-- | src/h_lua_common.c | 117 | ||||
-rw-r--r-- | src/h_lua_common.h | 7 | ||||
-rw-r--r-- | src/h_luac.c | 55 | ||||
-rw-r--r-- | src/h_luac.h | 7 | ||||
-rw-r--r-- | src/h_script.c | 614 | ||||
-rw-r--r-- | src/h_script.h | 52 | ||||
-rw-r--r-- | src/haserl.c | 917 | ||||
-rw-r--r-- | src/haserl.h | 77 | ||||
-rw-r--r-- | src/haserl_lualib.inc | 90 | ||||
-rw-r--r-- | src/haserl_lualib.lua | 54 | ||||
-rw-r--r-- | src/lua2c.c | 97 | ||||
-rw-r--r-- | src/rfc2388.c | 508 | ||||
-rw-r--r-- | src/rfc2388.h | 28 | ||||
-rw-r--r-- | src/sliding_buffer.c | 180 | ||||
-rw-r--r-- | src/sliding_buffer.h | 27 | ||||
-rw-r--r-- | src/stamp-h.in | 1 |
26 files changed, 4413 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..5d38878 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,66 @@ +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 + + +#--------- Unit Tests ---------------------- +#TESTS = utest_common + +#check_PROGRAMS = utest_common + +#utest_common: utest_common.c + +#--------- End Unit Tests ------------------- + diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..2ee4afb --- /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 == NULL) + { + return (-1); + } + argv_array[arg_count - 1].string = instr; + argv_array[arg_count].quoted = 0; + 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..215b591 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,103 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Include bash extensions */ +#undef BASHEXTENSIONS + +/* 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..dcb371c --- /dev/null +++ b/src/h_bash.c @@ -0,0 +1,651 @@ +/*----------------------------------------------------------------- + * 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" + +/* Local subshell variables */ +static int subshell_pipe[2]; +static int subshell_pid; + + +void +bash_setup (char *shell, list_t * env) +{ + int retcode = 0; + int count; + argv_t *argv; + char *av[20]; + list_t *next; + + 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]); + close (subshell_pipe[PARENT_OUT]); + 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--; + } + + + /* populate the environment */ + while (env) + { + next = env->next; + putenv (env->buf); + env = next; + } + + 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 */ + close (subshell_pipe[PARENT_IN]); + } + } + + /* 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)); +} + +#ifdef BASHEXTENSIONS +/* generate an IF statment */ +void +bash_if (buffer_t * buf, char *str, size_t len) +{ + static char err_msg[] = + "echo 'error: missing expression for if'\nexit 99\n"; + static char if_start[] = "if [[ "; + static char if_end[] = " ]]\nthen\n"; + static char ex_start[] = "if "; + static char ex_end[] = "\nthen\n"; + + if (len == 0) + { + buffer_add (buf, err_msg, strlen (err_msg)); + } + else if (str[0] == '|') + { + str[0] = ' '; + buffer_add (buf, ex_start, strlen (ex_start)); + buffer_add (buf, str, len); + buffer_add (buf, ex_end, strlen (ex_end)); + } + else + { + buffer_add (buf, if_start, strlen (if_start)); + buffer_add (buf, str, len); + buffer_add (buf, if_end, strlen (if_end)); + } +} + + +/* generate an ELIF statment */ +void +bash_elif (buffer_t * buf, char *str, size_t len) +{ + static char err_msg[] = + "echo 'error: missing expression for elif'\nexit 99\n"; + static char elif_start[] = "elif [[ "; + static char elif_end[] = " ]]\nthen\n"; + static char ex_start[] = "elif "; + static char ex_end[] = "\nthen\n"; + + if (len == 0) + { + buffer_add (buf, err_msg, strlen (err_msg)); + } + else if (str[0] == '|') + { + str[0] = ' '; + buffer_add (buf, ex_start, strlen (ex_start)); + buffer_add (buf, str, len); + buffer_add (buf, ex_end, strlen (ex_end)); + } + else + { + buffer_add (buf, elif_start, strlen (elif_start)); + buffer_add (buf, str, len); + buffer_add (buf, elif_end, strlen (elif_end)); + } +} + + +/* generate an ELSE statment */ +void +bash_else (buffer_t * buf, char *str, size_t len) +{ + static char else_start[] = "else"; + static char else_start2[] = "else #"; + static char else_end[] = "\n"; + + if (len == 0) + { + buffer_add (buf, else_start, strlen (else_start)); + } + else + { + buffer_add (buf, else_start2, strlen (else_start2)); + buffer_add (buf, str, len); + } + buffer_add (buf, else_end, strlen (else_end)); +} + + +/* generate a FI statment */ +void +bash_endif (buffer_t * buf, char *str, size_t len) +{ + static char fi_start[] = "fi"; + static char fi_start2[] = "fi #"; + static char fi_end[] = "\n"; + + if (len == 0) + { + buffer_add (buf, fi_start, strlen (fi_start)); + } + else + { + buffer_add (buf, fi_start2, strlen (fi_start2)); + buffer_add (buf, str, len); + } + buffer_add (buf, fi_end, strlen (fi_end)); +} + + +/* generate a CASE statment */ +void +bash_case (buffer_t * buf, char *str, size_t len) +{ + static char err_msg[] = + "echo 'error: missing expression for case'\nexit 99\n"; + static char case_start[] = "case "; + static char case_end[] = " in\n"; + /* + create a bogus case condition, nul+esc+eof, so nl+;;+nl + can be prepended to each when/otherwise/endcase, which + eliminates the need for ;; or <% ;; %> in the page source + */ + static char case_bogus[] = "\"\\000\\040\\004\") :\n"; + + if (len == 0) + { + buffer_add (buf, err_msg, strlen (err_msg)); + } + else + { + buffer_add (buf, case_start, strlen (case_start)); + buffer_add (buf, str, len); + buffer_add (buf, case_end, strlen (case_end)); + buffer_add (buf, case_bogus, strlen (case_bogus)); + } +} + + +/* generate a WHEN statment */ +void +bash_when (buffer_t * buf, char *str, size_t len) +{ + static char err_msg[] = + "echo 'error: missing expression for when'\nexit 99\n"; + static char when_start[] = "\n;;\n"; + static char when_end[] = ")\n"; + + if (len == 0) + { + buffer_add (buf, err_msg, strlen (err_msg)); + } + else + { + buffer_add (buf, when_start, strlen (when_start)); + buffer_add (buf, str, len - 1); + buffer_add (buf, when_end, strlen (when_end)); + } +} + + +/* generate an OTHERWISE statment */ +void +bash_otherwise (buffer_t * buf, char *str, size_t len) +{ + static char otherwise_start[] = "\n;;\n"; + static char otherwise_start1[] = "*)"; + static char otherwise_start2[] = "*) #"; + static char otherwise_end[] = "\n"; + + buffer_add (buf, otherwise_start, strlen (otherwise_start)); + + if (len == 0) + { + buffer_add (buf, otherwise_start1, strlen (otherwise_start1)); + } + else + { + buffer_add (buf, otherwise_start2, strlen (otherwise_start2)); + buffer_add (buf, str, len); + } + buffer_add (buf, otherwise_end, strlen (otherwise_end)); +} + + +/* generate a ENDCASE statment */ +void +bash_endcase (buffer_t * buf, char *str, size_t len) +{ + static char endcase_start[] = "\n;;\n"; + static char endcase_start1[] = "esac"; + static char endcase_start2[] = "esac #"; + static char endcase_end[] = "\n"; + + buffer_add (buf, endcase_start, strlen (endcase_start)); + + if (len == 0) + { + buffer_add (buf, endcase_start1, strlen (endcase_start1)); + } + else + { + buffer_add (buf, endcase_start2, strlen (endcase_start2)); + buffer_add (buf, str, len); + } + buffer_add (buf, endcase_end, strlen (endcase_end)); +} + + +/* generate a WHILE statment */ +void +bash_while (buffer_t * buf, char *str, size_t len) +{ + static char err_msg[] = + "echo 'error: missing expression for while'\nexit 99\n"; + static char while_start[] = "while [[ "; + static char while_end[] = " ]]\ndo\n"; + static char ex_start[] = "while "; + static char ex_end[] = "\ndo\n"; + + if (len == 0) + { + buffer_add (buf, err_msg, strlen (err_msg)); + } + else if (str[0] == '|') + { + str[0] = ' '; + buffer_add (buf, ex_start, strlen (ex_start)); + buffer_add (buf, str, len); + buffer_add (buf, ex_end, strlen (ex_end)); + } + else + { + buffer_add (buf, while_start, strlen (while_start)); + buffer_add (buf, str, len); + buffer_add (buf, while_end, strlen (while_end)); + } +} + + +/* generate an ENDWHILE statment */ +void +bash_endwhile (buffer_t * buf, char *str, size_t len) +{ + static char endwhile_start[] = "done"; + static char endwhile_start2[] = "done #"; + static char endwhile_end[] = "\n"; + + if (len == 0) + { + buffer_add (buf, endwhile_start, strlen (endwhile_start)); + } + else + { + buffer_add (buf, endwhile_start2, strlen (endwhile_start2)); + buffer_add (buf, str, len); + } + buffer_add (buf, endwhile_end, strlen (endwhile_end)); +} + + +/* generate an UNTIL statment */ +void +bash_until (buffer_t * buf, char *str, size_t len) +{ + static char err_msg[] = + "echo 'error: missing expression for until'\nexit 99\n"; + static char until_start[] = "until [[ "; + static char until_end[] = " ]]\ndo\n"; + static char ex_start[] = "until "; + static char ex_end[] = "\ndo\n"; + + if (len == 0) + { + buffer_add (buf, err_msg, strlen (err_msg)); + } + else if (str[0] == '|') + { + str[0] = ' '; + buffer_add (buf, ex_start, strlen (ex_start)); + buffer_add (buf, str, len); + buffer_add (buf, ex_end, strlen (ex_end)); + } + else + { + buffer_add (buf, until_start, strlen (until_start)); + buffer_add (buf, str, len); + buffer_add (buf, until_end, strlen (until_end)); + } +} + + +/* generate an ENDUNTIL statment */ +void +bash_enduntil (buffer_t * buf, char *str, size_t len) +{ + static char enduntil_start[] = "done"; + static char enduntil_start2[] = "done #"; + static char enduntil_end[] = "\n"; + + if (len == 0) + { + buffer_add (buf, enduntil_start, strlen (enduntil_start)); + } + else + { + buffer_add (buf, enduntil_start2, strlen (enduntil_start2)); + buffer_add (buf, str, len); + } + buffer_add (buf, enduntil_end, strlen (enduntil_end)); +} + + +/* generate a FOR statment */ +void +bash_for (buffer_t * buf, char *str, size_t len) +{ + static char err_msg[] = + "echo 'error: missing expression for for'\nexit 99\n"; + static char for_start[] = "for "; + static char for_end[] = "\ndo\n"; + + if (len == 0) + { + buffer_add (buf, err_msg, strlen (err_msg)); + } + else + { + buffer_add (buf, for_start, strlen (for_start)); + buffer_add (buf, str, len); + buffer_add (buf, for_end, strlen (for_end)); + } +} + + +/* generate an ENDFOR statment */ +void +bash_endfor (buffer_t * buf, char *str, size_t len) +{ + static char endfor_start[] = "done"; + static char endfor_start2[] = "done #"; + static char endfor_end[] = "\n"; + + if (len == 0) + { + buffer_add (buf, endfor_start, strlen (endfor_start)); + } + else + { + buffer_add (buf, endfor_start2, strlen (endfor_start2)); + buffer_add (buf, str, len); + } + buffer_add (buf, endfor_end, strlen (endfor_end)); +} + + +/* generate an UNLESS statment */ +void +bash_unless (buffer_t * buf, char *str, size_t len) +{ + static char err_msg[] = + "echo 'error: missing expression for unless'\nexit 99\n"; + static char unless_start[] = "if [[ ! ( "; + static char unless_end[] = " ) ]]\nthen\n"; + static char ex_start[] = "if ! "; + static char ex_end[] = "\nthen\n"; + + if (len == 0) + { + buffer_add (buf, err_msg, strlen (err_msg)); + } + else if (str[0] == '|') + { + str[0] = ' '; + buffer_add (buf, ex_start, strlen (ex_start)); + buffer_add (buf, str, len); + buffer_add (buf, ex_end, strlen (ex_end)); + } + else + { + buffer_add (buf, unless_start, strlen (unless_start)); + buffer_add (buf, str, len); + buffer_add (buf, unless_end, strlen (unless_end)); + } +} + + +/* generate an ELUN statment */ +void +bash_elun (buffer_t * buf, char *str, size_t len) +{ + static char err_msg[] = + "echo 'error: missing expression for elun'\nexit 99\n"; + static char elun_start[] = "elif [[ ! ( "; + static char elun_end[] = " ) ]]\nthen\n"; + static char ex_start[] = "elif ! "; + static char ex_end[] = "\nthen\n"; + + if (len == 0) + { + buffer_add (buf, err_msg, strlen (err_msg)); + } + else if (str[0] == '|') + { + str[0] = ' '; + buffer_add (buf, ex_start, strlen (ex_start)); + buffer_add (buf, str, len); + buffer_add (buf, ex_end, strlen (ex_end)); + } + else + { + buffer_add (buf, elun_start, strlen (elun_start)); + buffer_add (buf, str, len); + buffer_add (buf, elun_end, strlen (elun_end)); + } +} + + +/* generate an UNELSE statment */ +void +bash_unelse (buffer_t * buf, char *str, size_t len) +{ + static char unelse_start[] = "else"; + static char unelse_start2[] = "else #"; + static char unelse_end[] = "\n"; + + if (len == 0) + { + buffer_add (buf, unelse_start, strlen (unelse_start)); + } + else + { + buffer_add (buf, unelse_start2, strlen (unelse_start2)); + buffer_add (buf, str, len); + } + buffer_add (buf, unelse_end, strlen (unelse_end)); +} + + +/* generate a ENDUNLESS statment */ +void +bash_endunless (buffer_t * buf, char *str, size_t len) +{ + static char endunless_start[] = "fi"; + static char endunless_start2[] = "fi #"; + static char endunless_end[] = "\n"; + + if (len == 0) + { + buffer_add (buf, endunless_start, strlen (endunless_start)); + } + else + { + buffer_add (buf, endunless_start2, strlen (endunless_start2)); + buffer_add (buf, str, len); + } + buffer_add (buf, endunless_end, strlen (endunless_end)); +} +#endif + +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..d827aa6 --- /dev/null +++ b/src/h_bash.h @@ -0,0 +1,41 @@ +/* + * $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_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); + +#ifdef BASHEXTENSIONS +void bash_if(buffer_t *buf, char *str, size_t len); +void bash_elif(buffer_t *buf, char *str, size_t len); +void bash_else(buffer_t *buf, char *str, size_t len); +void bash_endif(buffer_t *buf, char *str, size_t len); +void bash_case(buffer_t *buf, char *str, size_t len); +void bash_when(buffer_t *buf, char *str, size_t len); +void bash_otherwise(buffer_t *buf, char *str, size_t len); +void bash_endcase(buffer_t *buf, char *str, size_t len); +void bash_while(buffer_t *buf, char *str, size_t len); +void bash_endwhile(buffer_t *buf, char *str, size_t len); +void bash_until(buffer_t *buf, char *str, size_t len); +void bash_enduntil(buffer_t *buf, char *str, size_t len); +void bash_for(buffer_t *buf, char *str, size_t len); +void bash_endfor(buffer_t *buf, char *str, size_t len); +void bash_unless(buffer_t *buf, char *str, size_t len); +void bash_elun(buffer_t *buf, char *str, size_t len); +void bash_unelse(buffer_t *buf, char *str, size_t len); +void bash_endunless(buffer_t *buf, char *str, size_t len); +#endif + +#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> before <%c", + "Missing %c>", + "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..1b1e8af --- /dev/null +++ b/src/h_lua.h @@ -0,0 +1,12 @@ +#ifndef H_LUA_H +#define H_LUA_H 1 + + +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..e29c97b --- /dev/null +++ b/src/h_lua_common.c @@ -0,0 +1,117 @@ +/* -------------------------------------------------------------------------- + * 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 = memchr (str, '=', strlen (str)); + if (value) + { + *value = (char) '\0'; + 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..029fc41 --- /dev/null +++ b/src/h_script.c @@ -0,0 +1,614 @@ +/*-------------------------------------------------------------------------- + * 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" + +#ifdef BASHEXTENSIONS +/* HTML, RUN, INCLUDE, EVAL, COMMENT, IF, ELIF, ELSE, ENDIF, CASE, WHEN, + OTHERWISE, ENDCASE, WHILE, ENDWHILE, UNTIL, ENDUNTIL, FOR, ENDFOR, + UNLESS, ELUN, UNELSE, ENDUNLESS, NOOP }; */ +#else +/* HTML, RUN, INCLUDE, EVAL, COMMENT, NOOP */ +#endif + +const char *g_tag[] = { + "", + "", + "in", + "=", + "#", +#ifdef BASHEXTENSIONS + "if", + "elif", + "else", + "endif", + "case", + "when", + "otherwise", + "endcase", + "while", + "endwhile", + "until", + "enduntil", + "for", + "endfor", + "unless", + "elun", + "unelse", + "endunless", +#endif + "" +}; + +/* 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 + +/* return the point in a script where the "comment" ends */ +char * +skip_comment (char *startbuf, char *endbuf) +{ + unsigned int c_lev = 1; + char *s_tag, *e_tag; + + startbuf += 2; + while (startbuf < endbuf) + { + s_tag = strstr (startbuf, open_tag); + e_tag = strstr (startbuf, close_tag); + + if (!e_tag) + { + break; + } + + if ((s_tag) && (s_tag < e_tag)) + { + c_lev++; + startbuf = s_tag + 2; + continue; + } + /* otherwise, we have an end tag first */ + c_lev--; + startbuf = e_tag; + if (c_lev == 0) + { + return (startbuf); + } + startbuf += 2; + + } + return NULL; +} + +/* 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. + * + * For nesting comments, this function IS aware of + * the comment tag, and will try accomodate commented + * tags. Commented script never enters the token list + */ +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 this is a comment tag, the end is at the end of the comment */ + if ((start) && (memcmp (start + 2, g_tag[COMMENT], 1) == 0)) + { + /* save any bare html before the comment */ + curtoken = push_token_on_list (curtoken, scriptbuf, curpos, + start - curpos); + if (firsttoken == NULL) + firsttoken = curtoken; + /* push start of token to end of token */ + end = skip_comment (start, endpos); + if (end) + { + curpos = end + 2; + continue; + } + } + + 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; + 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); + } + else if (memcmp (cp, g_tag[EVAL], 1) == 0) + + { + me->tag = EVAL; + me->buf = find_whitespace (me->buf); + me->len = strlen (me->buf); + } +#ifdef BASHEXTENSIONS + else if (memcmp (cp, g_tag[IF], 2) == 0) + { + me->tag = IF; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[ELIF], 4) == 0) + { + me->tag = ELIF; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[ELSE], 4) == 0) + { + me->tag = ELSE; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[ENDIF], 5) == 0) + { + me->tag = ENDIF; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[CASE], 4) == 0) + { + me->tag = CASE; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[WHEN], 4) == 0) + { + me->tag = WHEN; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[OTHERWISE], 9) == 0) + { + me->tag = OTHERWISE; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[ENDCASE], 7) == 0) + { + me->tag = ENDCASE; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[WHILE], 5) == 0) + { + me->tag = WHILE; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[ENDWHILE], 8) == 0) + { + me->tag = ENDWHILE; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[UNTIL], 5) == 0) + { + me->tag = UNTIL; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[ENDUNTIL], 8) == 0) + { + me->tag = ENDUNTIL; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[FOR], 3) == 0) + { + me->tag = FOR; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[ENDFOR], 6) == 0) + { + me->tag = ENDFOR; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[UNLESS], 6) == 0) + { + me->tag = UNLESS; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[ELUN], 4) == 0) + { + me->tag = ELUN; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[UNELSE], 6) == 0) + { + me->tag = UNELSE; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } + else if (memcmp (cp, g_tag[ENDUNLESS], 9) == 0) + { + me->tag = ENDUNLESS; + me->buf = find_whitespace (me->buf); + me->buf = skip_whitespace (me->buf); + me->len = strlen (me->buf); + } +#endif /* BASHEXTENSIONS */ + 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; +#ifdef BASHEXTENSIONS + case IF: + shell_if (buf, token->buf, token->len); + break; + case ELIF: + shell_elif (buf, token->buf, token->len); + break; + case ELSE: + shell_else (buf, token->buf, token->len); + break; + case ENDIF: + shell_endif (buf, token->buf, token->len); + break; + case CASE: + shell_case (buf, token->buf, token->len); + break; + case WHEN: + shell_when (buf, token->buf, token->len); + break; + case OTHERWISE: + shell_otherwise (buf, token->buf, token->len); + break; + case ENDCASE: + shell_endcase (buf, token->buf, token->len); + break; + case WHILE: + shell_while (buf, token->buf, token->len); + break; + case ENDWHILE: + shell_endwhile (buf, token->buf, token->len); + break; + case UNTIL: + shell_until (buf, token->buf, token->len); + break; + case ENDUNTIL: + shell_enduntil (buf, token->buf, token->len); + break; + case FOR: + shell_for (buf, token->buf, token->len); + break; + case ENDFOR: + shell_endfor (buf, token->buf, token->len); + break; + case UNLESS: + shell_unless (buf, token->buf, token->len); + break; + case ELUN: + shell_elun (buf, token->buf, token->len); + break; + case UNELSE: + shell_unelse (buf, token->buf, token->len); + break; + case ENDUNLESS: + shell_endunless (buf, token->buf, token->len); + break; +#endif + 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..e1098fe --- /dev/null +++ b/src/h_script.h @@ -0,0 +1,52 @@ +/* + * $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 */ +#ifdef BASHEXTENSIONS +enum tag_t { HTML, RUN, INCLUDE, EVAL, COMMENT, IF, ELIF, ELSE, ENDIF, CASE, WHEN, OTHERWISE, ENDCASE, WHILE, ENDWHILE, UNTIL, ENDUNTIL, FOR, ENDFOR, UNLESS, ELUN, UNELSE, ENDUNLESS, NOOP }; +#else +enum tag_t { HTML, RUN, INCLUDE, EVAL, COMMENT, NOOP }; +#endif + + +/* 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..da7c755 --- /dev/null +++ b/src/haserl.c @@ -0,0 +1,917 @@ +/* -------------------------------------------------------------------------- + * 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 + +#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); + +#ifdef BASHEXTENSIONS +void (*shell_if) (buffer_t * buf, char *str, size_t len); +void (*shell_elif) (buffer_t * buf, char *str, size_t len); +void (*shell_else) (buffer_t * buf, char *str, size_t len); +void (*shell_endif) (buffer_t * buf, char *str, size_t len); +void (*shell_case) (buffer_t * buf, char *str, size_t len); +void (*shell_when) (buffer_t * buf, char *str, size_t len); +void (*shell_otherwise) (buffer_t * buf, char *str, size_t len); +void (*shell_endcase) (buffer_t * buf, char *str, size_t len); +void (*shell_while) (buffer_t * buf, char *str, size_t len); +void (*shell_endwhile) (buffer_t * buf, char *str, size_t len); +void (*shell_until) (buffer_t * buf, char *str, size_t len); +void (*shell_enduntil) (buffer_t * buf, char *str, size_t len); +void (*shell_for) (buffer_t * buf, char *str, size_t len); +void (*shell_endfor) (buffer_t * buf, char *str, size_t len); +void (*shell_unless) (buffer_t * buf, char *str, size_t len); +void (*shell_elun) (buffer_t * buf, char *str, size_t len); +void (*shell_unelse) (buffer_t * buf, char *str, size_t len); +void (*shell_endunless) (buffer_t * buf, char *str, size_t len); +#endif + +/* 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; + char *temp = NULL; + int array = 0; + int len; + + temp = memchr (str, '=', strlen (str)); + /* if we don't have an equal sign, exit early */ + if (temp == 0) + { + return (cur); + } + + keylen = (size_t) (temp - str); + + /* is this an array */ + if (memcmp (str + keylen - 2, "[]", 2) == 0) + { + keylen = keylen - 2; + array = 1; + } + + entry = xmalloc (strlen (str) + strlen (prefix) + 1); + entry[0] = '\0'; + if (strlen (prefix)) + { + strncat (entry, prefix, strlen (prefix)); + } + + if (array == 1) + { + strncat (entry, str, keylen); + strcat (entry, str + keylen + 2); + } + else + { + strcat (entry, str); + } + + /* does the value already exist? */ + len = keylen + strlen (prefix) + 1; + while (cur != NULL) + { + if (memcmp (cur->buf, entry, len) == 0) + { + if (array == 1) + { + /* if an array, create a new string with this + * value added to the end of the old value(s) + */ + temp = xmalloc (strlen (cur->buf) + len + 1); + memmove (temp, cur->buf, strlen (cur->buf) + 1); + strcat (temp, "\n"); + strcat (temp, str + keylen + 3); + free (entry); + entry = temp; + } + /* delete the old entry */ + 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; + 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); + myputenv (env, token, global.cookie_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); + myputenv (env, token, global.get_prefix); + token = strtok (NULL, "&;"); + } + free (qs); + return (0); +} + + +/* + * Read cgi variables from stdin (for POST queries) + */ + +int +ReadCGIPOSTValues (list_t * env) +{ + size_t content_length = 0; + size_t max_len; + size_t i, j, x; + sliding_buffer_t sbuf; + buffer_t token; + unsigned char *data; + const char *CONTENT_LENGTH = "CONTENT_LENGTH"; + + 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; + if ((getenv (CONTENT_LENGTH)) || + (strtoul (getenv(CONTENT_LENGTH), NULL, 10) == 0 )) + { + sbuf.maxread = strtoul (getenv (CONTENT_LENGTH), NULL, 10); + } + 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 */ + j = strlen ((char *) data); + for (i = 0; i <= j; i++) + { + if (data[i] == '+') + { + data[i] = ' '; + } + } + unescape_url ((char *) data); + myputenv (env, (char *) data, global.var_prefix); + myputenv (env, (char *) data, global.post_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 = "FORM_"; + global.get_prefix = "GET_"; + global.post_prefix = "POST_"; + global.cookie_prefix = "COOKIE_"; + 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 = argv; + int av2c = argc; + + int command; + int count; + + list_t *env = NULL; + + 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\n" +#ifdef USE_LUA + "This version includes Lua (precompiled" +#ifdef INCLUDE_LUASHELL + " and interpreted" +#endif + ")\n" +#endif +#ifdef BASHEXTENSIONS + "Unsupported bash extensions supplied by simnux enabled\n" +#endif + ); + return (0); + break; + default: /* more than one */ + /* split combined #! args - linux bundles them as one */ + command = argc_argv (argv[1], &av, ""); + + if (command > 1) + { + /* rebuild argv into new av2 */ + av2c = argc - 1 + command; + av2 = xmalloc (sizeof (char *) * av2c); + av2[0] = argv[0]; + for (count = 1; count <= command; count++) + { + av2[count] = av[count-1].string; + } + for (; count < av2c; count++) { + av2[count] = argv[count - command + 1]; + } + } + + parseCommandLine (av2c, av2); + free (av); + if (av2 != argv) free (av2); + + if (optind < av2c) + { + filename = av2[optind]; + } + else + { + die_with_message (NULL, NULL, "No script file specified"); + } + + break; + } + + + 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; + +#ifdef BASHEXTENSIONS + shell_if = &bash_if; + shell_elif = &bash_elif; + shell_else = &bash_else; + shell_endif = &bash_endif; + shell_case = &bash_case; + shell_when = &bash_when; + shell_otherwise = &bash_otherwise; + shell_endcase = &bash_endcase; + shell_while = &bash_while; + shell_endwhile = &bash_endwhile; + shell_until = &bash_until; + shell_enduntil = &bash_enduntil; + shell_for = &bash_for; + shell_endfor = &bash_endfor; + shell_unless = &bash_unless; + shell_elun = &bash_elun; + shell_unelse = &bash_unelse; + shell_endunless = &bash_endunless; +#endif + +#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') /* luac only */ +#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); + } + } + } + +/* 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 (); + } + + + 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); + + return (0); + +} diff --git a/src/haserl.h b/src/haserl.h new file mode 100644 index 0000000..e7b0e1f --- /dev/null +++ b/src/haserl.h @@ -0,0 +1,77 @@ +#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 *get_prefix; /* what name we give to POST variables */ + char *post_prefix; /* what name we give to POST variables */ + char *cookie_prefix; /* what name we give to COOKIE 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); + +#ifdef BASHEXTENSIONS +extern void (*shell_if)(buffer_t *buf, char *str, size_t len); +extern void (*shell_elif)(buffer_t *buf, char *str, size_t len); +extern void (*shell_else)(buffer_t *buf, char *str, size_t len); +extern void (*shell_endif)(buffer_t *buf, char *str, size_t len); +extern void (*shell_case)(buffer_t *buf, char *str, size_t len); +extern void (*shell_when)(buffer_t *buf, char *str, size_t len); +extern void (*shell_otherwise)(buffer_t *buf, char *str, size_t len); +extern void (*shell_endcase)(buffer_t *buf, char *str, size_t len); +extern void (*shell_while)(buffer_t *buf, char *str, size_t len); +extern void (*shell_endwhile)(buffer_t *buf, char *str, size_t len); +extern void (*shell_until)(buffer_t *buf, char *str, size_t len); +extern void (*shell_enduntil)(buffer_t *buf, char *str, size_t len); +extern void (*shell_for)(buffer_t *buf, char *str, size_t len); +extern void (*shell_endfor)(buffer_t *buf, char *str, size_t len); +extern void (*shell_unless)(buffer_t *buf, char *str, size_t len); +extern void (*shell_elun)(buffer_t *buf, char *str, size_t len); +extern void (*shell_unelse)(buffer_t *buf, char *str, size_t len); +extern void (*shell_endunless)(buffer_t *buf, char *str, size_t len); +#endif +#endif /* !_HASERL_H */ + diff --git a/src/haserl_lualib.inc b/src/haserl_lualib.inc new file mode 100644 index 0000000..e533384 --- /dev/null +++ b/src/haserl_lualib.inc @@ -0,0 +1,90 @@ +/* This file was automatically generated from haserl_lualib.lua. DO NOT EDIT */ + +static const unsigned char haserl_lualib[] = { + 27, 76,117, 97, 81, 0, 1, 4, 4, 4, 8, 0, 19, 0, 0, 0, + 64,104, 97,115,101,114,108, 95,108,117, 97,108,105, 98, 46,108, + 117, 97, 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, + 15, 0, 0, 0, 40, 91, 37,119, 95, 37, 45, 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, 29, 0, 0, 0, + 25, 0, 0, 0, 26, 0, 0, 0, 26, 0, 0, 0, 26, 0, 0, 0, + 26, 0, 0, 0, 26, 0, 0, 0, 26, 0, 0, 0, 27, 0, 0, 0, + 27, 0, 0, 0, 27, 0, 0, 0, 27, 0, 0, 0, 27, 0, 0, 0, + 28, 0, 0, 0, 28, 0, 0, 0, 28, 0, 0, 0, 28, 0, 0, 0, + 30, 0, 0, 0, 30, 0, 0, 0, 31, 0, 0, 0, 31, 0, 0, 0, + 31, 0, 0, 0, 31, 0, 0, 0, 31, 0, 0, 0, 32, 0, 0, 0, + 32, 0, 0, 0, 34, 0, 0, 0, 26, 0, 0, 0, 35, 0, 0, 0, + 37, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0,102, 0, 0, 0, + 0, 0, 28, 0, 0, 0, 2, 0, 0, 0,118, 0, 0, 0, 0, 0, + 28, 0, 0, 0, 2, 0, 0, 0,116, 0, 1, 0, 0, 0, 28, 0, + 0, 0, 16, 0, 0, 0, 40,102,111,114, 32,103,101,110,101,114, + 97,116,111,114, 41, 0, 6, 0, 0, 0, 28, 0, 0, 0, 12, 0, + 0, 0, 40,102,111,114, 32,115,116, 97,116,101, 41, 0, 6, 0, + 0, 0, 28, 0, 0, 0, 14, 0, 0, 0, 40,102,111,114, 32, 99, + 111,110,116,114,111,108, 41, 0, 6, 0, 0, 0, 28, 0, 0, 0, + 2, 0, 0, 0,119, 0, 7, 0, 0, 0, 26, 0, 0, 0, 2, 0, + 0, 0,100, 0, 7, 0, 0, 0, 26, 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, 12, 0, 0, 0, 40, 0, 0, 0, 41, + 0, 0, 0, 41, 0, 0, 0, 41, 0, 0, 0, 41, 0, 0, 0, 41, + 0, 0, 0, 41, 0, 0, 0, 42, 0, 0, 0, 41, 0, 0, 0, 42, + 0, 0, 0, 44, 0, 0, 0, 45, 0, 0, 0, 6, 0, 0, 0, 2, + 0, 0, 0,102, 0, 0, 0, 0, 0, 11, 0, 0, 0, 2, 0, 0, + 0,118, 0, 1, 0, 0, 0, 11, 0, 0, 0, 16, 0, 0, 0, 40, + 102,111,114, 32,103,101,110,101,114, 97,116,111,114, 41, 0, 6, + 0, 0, 0, 10, 0, 0, 0, 12, 0, 0, 0, 40,102,111,114, 32, + 115,116, 97,116,101, 41, 0, 6, 0, 0, 0, 10, 0, 0, 0, 14, + 0, 0, 0, 40,102,111,114, 32, 99,111,110,116,114,111,108, 41, + 0, 6, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0,119, 0, 7, + 0, 0, 0, 8, 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, 20, 0, 0, 0, 49, 0, 0, 0, 49, 0, 0, + 0, 49, 0, 0, 0, 49, 0, 0, 0, 49, 0, 0, 0, 49, 0, 0, + 0, 49, 0, 0, 0, 50, 0, 0, 0, 50, 0, 0, 0, 50, 0, 0, + 0, 50, 0, 0, 0, 50, 0, 0, 0, 50, 0, 0, 0, 50, 0, 0, + 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, + 0, 52, 0, 0, 0, 53, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, + 0,107,101,121, 0, 0, 0, 0, 0, 19, 0, 0, 0, 6, 0, 0, + 0,118, 97,108,117,101, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, 0, 21, 0, 0, 0, 21, 0, 0, 0, 21, + 0, 0, 0, 21, 0, 0, 0, 21, 0, 0, 0, 21, 0, 0, 0, 23, + 0, 0, 0, 37, 0, 0, 0, 23, 0, 0, 0, 39, 0, 0, 0, 45, + 0, 0, 0, 39, 0, 0, 0, 47, 0, 0, 0, 53, 0, 0, 0, 47, + 0, 0, 0, 53, 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..94e0e43 --- /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..8aa5d19 --- /dev/null +++ b/src/rfc2388.c @@ -0,0 +1,508 @@ +/* -------------------------------------------------------------------------- + * 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" + +#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); + myputenv (env, (char *) buf.data, global.post_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); + myputenv (env, (char *) buf.data, global.post_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 */ + 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); + } + + curtoken = + push_token_on_list (curtoken, NULL, tmpname, strlen (tmpname) + 1); + if (global.uploadlist == NULL) + { + global.uploadlist = curtoken; + } + +} + + + + +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; + if (getenv ("CONTENT_LENGTH")) + { + sbuf.maxread = strtoul (getenv ("CONTENT_LENGTH"), NULL, 10); + } + + /* 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..2b5ef33 --- /dev/null +++ b/src/sliding_buffer.c @@ -0,0 +1,180 @@ +/* + * -------------------------------------------------------------------------- + * 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; + sbuf->maxread = 0; + sbuf->nrread = 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 + { + size_t n = sbuf->maxsize - len; + if ( sbuf->maxread && sbuf->maxread < sbuf->nrread + n) + n = sbuf->maxread - sbuf->nrread; + r = read (sbuf->fh, sbuf->buf + len, n); + } + /* + * 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; + sbuf->nrread += (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..167e461 --- /dev/null +++ b/src/sliding_buffer.h @@ -0,0 +1,27 @@ +#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 */ + size_t len; /* length of this segment */ + size_t maxsize; /* max size of buffer */ + size_t bufsize; /* current size of buffer */ + size_t maxread; /* maximum number of bytes to read from fh, ignored if 0 */ + size_t nrread; /* number of bytes read from fh */ + 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 |