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