diff -Nrub clearsilver-0.10.3/cgi/cgi.c clearsilver-0.10.4/cgi/cgi.c --- clearsilver-0.10.3/cgi/cgi.c 2005-12-02 01:34:04.000000000 -0800 +++ clearsilver-0.10.4/cgi/cgi.c 2006-11-06 15:32:16.000000000 -0800 @@ -266,161 +266,18 @@ NEOERR *cgi_js_escape (const char *in, char **esc) { - int nl = 0; - int l = 0; - unsigned char *buf = (unsigned char *)in; - unsigned char *s; - - while (buf[l]) - { - if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' || - buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' || buf[l] < 32) - { - nl += 3; - } - nl++; - l++; - } - - s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1)); - if (s == NULL) - return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s", - buf); - - nl = 0; l = 0; - while (buf[l]) - { - if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' || - buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' || buf[l] < 32) - { - s[nl++] = '\\'; - s[nl++] = 'x'; - s[nl++] = "0123456789ABCDEF"[(buf[l] >> 4) & 0xF]; - s[nl++] = "0123456789ABCDEF"[buf[l] & 0xF]; - l++; - } - else - { - s[nl++] = buf[l++]; - } - } - s[nl] = '\0'; - - *esc = (char *)s; - return STATUS_OK; + return nerr_pass(neos_js_escape(in, esc)); } -// List of all characters that must be escaped -// List based on http://www.blooberry.com/indexdot/html/topics/urlencoding.htm -static char EscapedChars[] = "$&+,/:;=?@ \"<>#%{}|\\^~[]`"; - -// Check if a single character needs to be escaped -static BOOL is_reserved_char(char c) +NEOERR *cgi_url_escape (const char *buf, char **esc) { - int i = 0; - - if (c < 32 || c > 122) { - return TRUE; - } else { - while (EscapedChars[i]) { - if (c == EscapedChars[i]) { - return TRUE; - } - ++i; - } - } - return FALSE; + return nerr_pass(neos_url_escape(buf, esc, NULL)); } NEOERR *cgi_url_escape_more (const char *in, char **esc, const char *other) { - int nl = 0; - int l = 0; - int x = 0; - unsigned char *buf = (unsigned char *)in; - unsigned char *uother = (unsigned char *)other; - unsigned char *s; - int match = 0; - - while (buf[l]) - { - if (is_reserved_char(buf[l])) - { - nl += 2; - } - else if (uother) - { - x = 0; - while (uother[x]) - { - if (uother[x] == buf[l]) - { - nl +=2; - break; - } - x++; - } - } - nl++; - l++; - } - - s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1)); - if (s == NULL) - return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s", - buf); - - nl = 0; l = 0; - while (buf[l]) - { - match = 0; - if (buf[l] == ' ') - { - s[nl++] = '+'; - l++; - } - else - { - if (is_reserved_char(buf[l])) - { - match = 1; - } - else if (uother) - { - x = 0; - while (uother[x]) - { - if (uother[x] == buf[l]) - { - match = 1; - break; - } - x++; - } - } - if (match) - { - s[nl++] = '%'; - s[nl++] = "0123456789ABCDEF"[buf[l] / 16]; - s[nl++] = "0123456789ABCDEF"[buf[l] % 16]; - l++; - } - else - { - s[nl++] = buf[l++]; - } - } - } - s[nl] = '\0'; - - *esc = (char *)s; - return STATUS_OK; -} - -NEOERR *cgi_url_escape (const char *buf, char **esc) -{ - return nerr_pass(cgi_url_escape_more(buf, esc, NULL)); + return nerr_pass(neos_url_escape(in, esc, other)); } static NEOERR *_parse_query (CGI *cgi, char *query) @@ -520,7 +377,7 @@ l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL); if (l == NULL) return STATUS_OK; len = atoi (l); - if (len == 0) return STATUS_OK; + if (len <= 0) return STATUS_OK; cgi->data_expected = len; @@ -695,7 +552,8 @@ char *d = hdf_get_value(cgi->hdf, "Query.debug_pause", NULL); char *d_p = hdf_get_value(cgi->hdf, "Config.DebugPassword", NULL); - if (d && d_p && !strcmp(d, d_p)) { + if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) && + d && d_p && !strcmp(d, d_p)) { sleep(20); } } @@ -732,6 +590,10 @@ my_pcb->ctype = strdup(ctype); if (my_pcb->method == NULL || my_pcb->ctype == NULL) { + if (my_pcb->method != NULL) + free(my_pcb->method); + if (my_pcb->ctype != NULL) + free(my_pcb->ctype); free(my_pcb); return nerr_raise(NERR_NOMEM, "Unable to allocate memory to register parse cb"); } @@ -826,6 +688,7 @@ l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL); if (l == NULL) return STATUS_OK; len = atoi (l); + if (len <= 0) return STATUS_OK; x = 0; while (x < len) @@ -1199,7 +1062,8 @@ s = hdf_get_value (cgi->hdf, "Query.debug", NULL); e = hdf_get_value (cgi->hdf, "Config.DebugPassword", NULL); - if (s && e && !strcmp(s, e)) do_debug = 1; + if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) && + s && e && !strcmp(s, e)) do_debug = 1; do_timefooter = hdf_get_int_value (cgi->hdf, "Config.TimeFooter", 1); ws_strip_level = hdf_get_int_value (cgi->hdf, "Config.WhiteSpaceStrip", 1); @@ -1411,13 +1275,13 @@ { NEOERR *err; - err = cs_register_strfunc(cs, "url_escape", cgi_url_escape); + err = cs_register_esc_strfunc(cs, "url_escape", cgi_url_escape); if (err != STATUS_OK) return nerr_pass(err); - err = cs_register_strfunc(cs, "html_escape", cgi_html_escape_strfunc); + err = cs_register_esc_strfunc(cs, "html_escape", cgi_html_escape_strfunc); if (err != STATUS_OK) return nerr_pass(err); err = cs_register_strfunc(cs, "text_html", cgi_text_html_strfunc); if (err != STATUS_OK) return nerr_pass(err); - err = cs_register_strfunc(cs, "js_escape", cgi_js_escape); + err = cs_register_esc_strfunc(cs, "js_escape", cgi_js_escape); if (err != STATUS_OK) return nerr_pass(err); err = cs_register_strfunc(cs, "html_strip", cgi_html_strip_strfunc); if (err != STATUS_OK) return nerr_pass(err); @@ -1455,7 +1319,8 @@ debug = hdf_get_value (cgi->hdf, "Query.debug", NULL); t = hdf_get_value (cgi->hdf, "Config.DumpPassword", NULL); - if (debug && t && !strcmp (debug, t)) do_dump = 1; + if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) && + debug && t && !strcmp (debug, t)) do_dump = 1; do { @@ -1567,7 +1432,7 @@ host = hdf_get_value (cgi->hdf, "HTTP.Host", NULL); if (host == NULL) - host = hdf_get_value (cgi->hdf, "CGI.ServerName", NULL); + host = hdf_get_value (cgi->hdf, "CGI.ServerName", "localhost"); cgiwrap_writef ("Location: %s://%s", https ? "https" : "http", host); diff -Nrub clearsilver-0.10.3/cgi/cgiwrap.c clearsilver-0.10.4/cgi/cgiwrap.c --- clearsilver-0.10.3/cgi/cgiwrap.c 2006-01-22 15:43:00.000000000 -0800 +++ clearsilver-0.10.4/cgi/cgiwrap.c 2006-03-28 16:12:37.000000000 -0800 @@ -11,6 +11,7 @@ #include "cs_config.h" +#include #include #include #include @@ -221,6 +222,23 @@ } else { +#ifdef __UCLIBC__ + /* According to + * http://cvs.uclinux.org/cgi-bin/cvsweb.cgi/uClibc/libc/stdio/stdio.c#rev1.28 + * Note: there is a difference in behavior between glibc and uClibc here + * regarding fread() on a tty stream. glibc's fread() seems to return + * after reading all _available_ data even if not at end-of-file, while + * uClibc's fread() continues reading until all requested or eof or error. + * The latter behavior seems correct w.r.t. the standards. + * + * So, we use read on uClibc. This may be required on other platforms as + * well. Using raw and buffered i/o interchangeably can be problematic, + * but everyone should be going through the cgiwrap interfaces which only + * provide this one read function. + */ + *read_len = read (fileno(stdin), buf, buf_len); +#else *read_len = fread (buf, sizeof(char), buf_len, stdin); +#endif } } diff -Nrub clearsilver-0.10.3/cgi/date.c clearsilver-0.10.4/cgi/date.c --- clearsilver-0.10.3/cgi/date.c 2005-07-27 14:07:16.000000000 -0700 +++ clearsilver-0.10.4/cgi/date.c 2006-11-03 08:56:22.000000000 -0800 @@ -148,7 +148,7 @@ if(isalpha(*ip)) { /* ctime */ - sscanf(ip,"%s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year); + sscanf(ip,"%25s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year); } else if(ip[2] == '-') { /* RFC 850 (normal HTTP) */ diff -Nrub clearsilver-0.10.3/cgi/html.c clearsilver-0.10.4/cgi/html.c --- clearsilver-0.10.3/cgi/html.c 2005-11-30 20:03:09.000000000 -0800 +++ clearsilver-0.10.4/cgi/html.c 2006-10-19 16:26:35.000000000 -0700 @@ -316,9 +316,9 @@ err = string_append (out, ">"); else if (src[x] == '\n') if (opts->newlines_convert) - err = string_append (out, "
\n"); + err = string_append (out, "
\n"); else if (x && src[x-1] == '\n') - err = string_append (out, "

\n"); + err = string_append (out, "

\n"); else err = string_append_char (out, '\n'); else if (src[x] != '\r') @@ -602,51 +602,7 @@ NEOERR *html_escape_alloc (const char *src, int slen, char **out) { - NEOERR *err = STATUS_OK; - STRING out_s; - int x; - char *ptr; - - string_init(&out_s); - err = string_append (&out_s, ""); - if (err) return nerr_pass (err); - *out = NULL; - - x = 0; - while (x < slen) - { - ptr = strpbrk(src + x, "&<>\"\r"); - if (ptr == NULL || (ptr-src >= slen)) - { - err = string_appendn (&out_s, src + x, slen-x); - x = slen; - } - else - { - err = string_appendn (&out_s, src + x, (ptr - src) - x); - if (err != STATUS_OK) break; - x = ptr - src; - if (src[x] == '&') - err = string_append (&out_s, "&"); - else if (src[x] == '<') - err = string_append (&out_s, "<"); - else if (src[x] == '>') - err = string_append (&out_s, ">"); - else if (src[x] == '"') - err = string_append (&out_s, """); - else if (src[x] != '\r') - err = nerr_raise (NERR_ASSERT, "src[x] == '%c'", src[x]); - x++; - } - if (err != STATUS_OK) break; - } - if (err) - { - string_clear (&out_s); - return nerr_pass (err); - } - *out = out_s.buf; - return STATUS_OK; + return nerr_pass(neos_html_escape(src, slen, out)); } /* Replace ampersand with iso-8859-1 character code */ diff -Nrub clearsilver-0.10.3/cgi/Makefile clearsilver-0.10.4/cgi/Makefile --- clearsilver-0.10.3/cgi/Makefile 2006-01-31 18:53:23.000000000 -0800 +++ clearsilver-0.10.4/cgi/Makefile 2006-11-09 14:35:37.000000000 -0800 @@ -10,7 +10,7 @@ CGI_SRC = cgiwrap.c cgi.c html.c date.c rfc2388.c CGI_OBJ = $(CGI_SRC:%.c=%.o) -STATIC_EXE = static.cgi +STATIC_EXE = cs_static.cgi STATIC_SRC = static.c STATIC_OBJ = $(STATIC_SRC:%.c=%.o) STATIC_CSO = $(STATIC_EXE:%.cgi=%.cso) @@ -30,13 +30,13 @@ $(RANLIB) $@ $(STATIC_EXE): $(STATIC_OBJ) $(DEP_LIBS) - $(LD) $@ $(STATIC_OBJ) -L$(LIB_DIR) $(DLIBS) $(LIBS) + $(LD) $@ $(STATIC_OBJ) $(LDFLAGS) $(DLIBS) $(LIBS) $(STATIC_CSO): $(STATIC_OBJ) $(DEP_LIBS) - $(LDSHARED) -o $@ $(STATIC_OBJ) -L$(LIB_DIR) $(DLIBS) $(LIBS) + $(LDSHARED) -o $@ $(STATIC_OBJ) $(LDFLAGS) $(DLIBS) $(LIBS) $(CGICSTEST_EXE): $(CGICSTEST_OBJ) $(DEP_LIBS) - $(LD) $@ $(CGICSTEST_OBJ) -L$(LIB_DIR) $(DLIBS) $(LIBS) + $(LD) $@ $(CGICSTEST_OBJ) $(LDFLAGS) $(DLIBS) $(LIBS) install: all $(NEOTONIC_ROOT)/mkinstalldirs $(DESTDIR)$(cs_includedir)/cgi diff -Nrub clearsilver-0.10.3/cgi/rfc2388.c clearsilver-0.10.4/cgi/rfc2388.c --- clearsilver-0.10.3/cgi/rfc2388.c 2005-06-30 18:21:06.000000000 -0700 +++ clearsilver-0.10.4/cgi/rfc2388.c 2006-08-29 01:44:50.000000000 -0700 @@ -126,6 +126,7 @@ { int ofs = 0; char *p; + int to_read; if (cgi->buf == NULL) { @@ -154,7 +155,20 @@ ofs = cgi->readlen - cgi->nl; memmove(cgi->buf, cgi->buf + cgi->nl, ofs); } - cgiwrap_read (cgi->buf + ofs, cgi->buflen - ofs, &(cgi->readlen)); + // Read either as much buffer space as we have left, or up to + // the amount of data remaining according to Content-Length + // If there is no Content-Length, just use the buffer space, but recognize + // that it might not work on some servers or cgiwrap implementations. + // Some servers will close their end of the stdin pipe, so cgiwrap_read + // will return if we ask for too much. Techically, not including + // Content-Length is against the HTTP spec, so we should consider failing + // earlier if we don't have a length. + to_read = cgi->buflen - ofs; + if (cgi->data_expected && (to_read > cgi->data_expected - cgi->data_read)) + { + to_read = cgi->data_expected - cgi->data_read; + } + cgiwrap_read (cgi->buf + ofs, to_read, &(cgi->readlen)); if (cgi->readlen < 0) { return nerr_raise_errno (NERR_IO, "POST Read Error"); diff -Nrub clearsilver-0.10.3/cs/cs.h clearsilver-0.10.4/cs/cs.h --- clearsilver-0.10.3/cs/cs.h 2006-02-02 18:17:49.000000000 -0800 +++ clearsilver-0.10.4/cs/cs.h 2006-08-07 13:01:51.000000000 -0700 @@ -46,6 +46,7 @@ #include "util/neo_err.h" #include "util/ulist.h" #include "util/neo_hdf.h" +#include "util/neo_str.h" __BEGIN_DECLS @@ -101,6 +102,7 @@ typedef struct _parse CSPARSE; typedef struct _funct CS_FUNCTION; +typedef struct _escape_context CS_ECONTEXT; typedef struct _arg { @@ -122,6 +124,7 @@ int node_num; int cmd; int flags; + NEOS_ESCAPE escape; CSARG arg1; CSARG arg2; CSARG *vargs; @@ -131,10 +134,6 @@ struct _tree *next; } CSTREE; -/* Technically, the char * for this func should be const char *, but that - * would break existing code */ -typedef NEOERR* (*CSOUTFUNC)(void *, char *); - typedef struct _local_map { CSTOKEN_TYPE type; @@ -162,15 +161,44 @@ struct _macro *next; } CS_MACRO; + +/* CSOUTFUNC is a callback function for where cs_render will render the + * template to. + * Technically, the char * for this func should be const char *, but that + * would break existing code. */ +typedef NEOERR* (*CSOUTFUNC)(void *, char *); + +/* CSFUNCTION is a callback function used for handling a function made + * available inside the template. Used by cs_register_function. Exposed + * here as part of the experimental extension framework, this may change + * in future versions. */ typedef NEOERR* (*CSFUNCTION)(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result); + +/* CSSTRFUNC is a callback function for the more limited "string filter" + * function handling. String filters only take a string and return a + * string and have no "data" that is passed back, attempting to make them + * "safe" from extensions that might try to do silly things like SQL queries. + * */ typedef NEOERR* (*CSSTRFUNC)(const char *str, char **ret); +/* CSFILELOAD is a callback function to intercept file load requests and + * provide templates via another mechanism. This way you can load templates + * that you compiled-into your binary, from in-memory caches, or from a + * zip file, etc. The HDF is provided so you can choose to use the + * hdf_search_path function to find the file. contents should return + * a full malloc copy of the contents of the file, which the parser will modify + * and own and free. Use cs_register_fileload to set this function for + * your CSPARSE context. */ +typedef NEOERR* (*CSFILELOAD)(void *ctx, HDF *hdf, const char *filename, + char **contents); + struct _funct { char *name; int name_len; int n_args; + NEOS_ESCAPE escape; /* States escaping exemptions. default: NONE. */ CSFUNCTION function; CSSTRFUNC str_func; @@ -178,12 +206,36 @@ struct _funct *next; }; +/* This structure maintains the necessary running state to manage + * automatic escaping in tandem with the 'escape' directive. It is passed + * around inside _parse. + */ +struct _escape_context +{ + NEOS_ESCAPE global; /* Contains global default escaping mode: + none,html,js,url */ + NEOS_ESCAPE current; /* Used to pass around parse and evaluation specific + data from subfunctions upward. */ + NEOS_ESCAPE next_stack; /* This is a big fat workaround. Since STACK_ENTRYs + are only added to the stack after the + command[].parse_handler() is called for the call + it is being setup for, this is used to pass state + forward. E.g. This is used for 'def' to set UNDEF + escaping state to delay escaping status to + evaluation time. */ + NEOS_ESCAPE when_undef; /* Contains the escaping context to be used when a + UNDEF is being replaced at evaluation time. E.g. + this is set in call_eval to force the macro code + to get call's parsing context at eval time. */ +}; + struct _parse { const char *context; /* A string identifying where the parser is parsing */ int in_file; /* Indicates if current context is a file */ int offset; char *context_string; + CS_ECONTEXT escaping; /* Context container for escape data */ char *tag; /* Usually cs, but can be set via HDF Config.TagStart */ int taglen; @@ -209,6 +261,9 @@ void *output_ctx; CSOUTFUNC output_cb; + void *fileload_ctx; + CSFILELOAD fileload; + /* Global hdf struct */ /* smarti: Added for support for global hdf under local hdf */ HDF *global_hdf; @@ -328,6 +383,27 @@ void cs_destroy (CSPARSE **parse); /* + * Function: cs_register_fileload - register a fileload function + * Description: cs_register_fileload registers a fileload function that + * overrides the built-in function. The built-in function + * uses hdf_search_path and ne_file_load (based on stat/open/read) + * to find and load the file on every template render. + * You can override this function if you wish to provide + * other template search functions, or load the template + * from an in-memory cache, etc. + * This fileload function will be used by cs_parse, including + * any include: commands in the template. + * Input: parse - a pointer to an initialized CSPARSE structure + * ctx - pointer that is passed to the CSFILELOAD function when called + * fileload - a CSFILELOAD function + * Output: None + * Return: None + * + */ + +void cs_register_fileload(CSPARSE *parse, void *ctx, CSFILELOAD fileload); + +/* * Function: cs_register_strfunc - register a string handling function * Description: cs_register_strfunc will register a string function that * can be called during CS render. This not-callback is @@ -354,6 +430,23 @@ */ NEOERR *cs_register_strfunc(CSPARSE *parse, char *funcname, CSSTRFUNC str_func); +/* + * Function: cs_register_esc_strfunc - cs_register_strfunc with escaping context + * Description: cs_register_esc_strfunc functions exactly as cs_register_strfunc + * except that it changes the evaluation escaping context to disable + * default escaping. + * Input: parse - a pointer to a CSPARSE structure initialized with cs_init() + * funcname - the name for the CS function call + * Note that registering a duplicate funcname will + * raise a NERR_DUPLICATE error + * str_func - a CSSTRFUNC not-callback + * Return: NERR_NOMEM - failure to allocate any memory for data structures + * NERR_DUPLICATE - funcname already registered + * + */ +NEOERR *cs_register_esc_strfunc(CSPARSE *parse, char *funcname, + CSSTRFUNC str_func); + /* Testing functions for future function api. This api may change in the * future. */ NEOERR *cs_arg_parse(CSPARSE *parse, CSARG *args, const char *fmt, ...); diff -Nrub clearsilver-0.10.3/cs/csparse.c clearsilver-0.10.4/cs/csparse.c --- clearsilver-0.10.3/cs/csparse.c 2006-02-02 18:17:41.000000000 -0800 +++ clearsilver-0.10.4/cs/csparse.c 2006-10-19 16:26:11.000000000 -0700 @@ -59,13 +59,15 @@ ST_DEF = 1<<6, ST_LOOP = 1<<7, ST_ALT = 1<<8, + ST_ESCAPE = 1<<9, } CS_STATE; -#define ST_ANYWHERE (ST_EACH | ST_WITH | ST_ELSE | ST_IF | ST_GLOBAL | ST_DEF | ST_LOOP | ST_ALT) +#define ST_ANYWHERE (ST_EACH | ST_WITH | ST_ELSE | ST_IF | ST_GLOBAL | ST_DEF | ST_LOOP | ST_ALT | ST_ESCAPE) typedef struct _stack_entry { CS_STATE state; + NEOS_ESCAPE escape; CSTREE *tree; CSTREE *next_tree; int num_local; @@ -103,6 +105,8 @@ static NEOERR *loop_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); static NEOERR *alt_parse (CSPARSE *parse, int cmd, char *arg); static NEOERR *alt_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); +static NEOERR *escape_parse (CSPARSE *parse, int cmd, char *arg); +static NEOERR *escape_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); static NEOERR *render_node (CSPARSE *parse, CSTREE *node); static NEOERR *cs_init_internal (CSPARSE **parse, HDF *hdf, CSPARSE *parent); @@ -126,6 +130,8 @@ name_parse, name_eval, 1}, {"var", sizeof("var")-1, ST_ANYWHERE, ST_SAME, var_parse, var_eval, 1}, + {"uvar", sizeof("uvar")-1, ST_ANYWHERE, ST_SAME, + var_parse, var_eval, 1}, {"evar", sizeof("evar")-1, ST_ANYWHERE, ST_SAME, evar_parse, skip_eval, 1}, {"lvar", sizeof("lvar")-1, ST_ANYWHERE, ST_SAME, @@ -168,6 +174,25 @@ alt_parse, alt_eval, 1}, {"/alt", sizeof("/alt")-1, ST_ALT, ST_POP, end_parse, skip_eval, 1}, + {"escape", sizeof("escape")-1, ST_ANYWHERE, ST_ESCAPE, + escape_parse, escape_eval, 1}, + {"/escape", sizeof("/escape")-1, ST_ESCAPE, ST_POP, + end_parse, skip_eval, 1}, + {NULL}, +}; + +/* Possible Config.VarEscapeMode values */ +typedef struct _escape_modes +{ + char *mode; /* Add space for NUL */ + NEOS_ESCAPE context; /* Context of the name */ +} CS_ESCAPE_MODES; + +CS_ESCAPE_MODES EscapeModes[] = { + {"none", NEOS_ESCAPE_NONE}, + {"html", NEOS_ESCAPE_HTML}, + {"js", NEOS_ESCAPE_SCRIPT}, + {"url", NEOS_ESCAPE_URL}, {NULL}, }; @@ -288,14 +313,23 @@ if (path == NULL) return nerr_raise (NERR_ASSERT, "path is NULL"); + if (parse->fileload) + { + err = parse->fileload(parse->fileload_ctx, parse->hdf, path, &ibuf); + } + else + { if (path[0] != '/') { err = hdf_search_path (parse->hdf, path, fpath); + if (parse->global_hdf && nerr_handle(&err, NERR_NOT_FOUND)) + err = hdf_search_path(parse->global_hdf, path, fpath); if (err != STATUS_OK) return nerr_pass(err); path = fpath; } err = ne_load_file (path, &ibuf); + } if (err) return nerr_pass (err); save_context = parse->context; @@ -397,6 +431,8 @@ return "LOOP"; else if (state & ST_ALT) return "ALT"; + else if (state & ST_ESCAPE) + return "ESCAPE"; snprintf(buf, sizeof(buf), "Unknown state %d", state); return buf; @@ -405,7 +441,7 @@ NEOERR *cs_parse_string (CSPARSE *parse, char *ibuf, size_t ibuf_len) { NEOERR *err = STATUS_OK; - STACK_ENTRY *entry; + STACK_ENTRY *entry, *current_entry; char *p; char *token; int done = 0; @@ -509,6 +545,18 @@ entry->state = Commands[i].next_state; entry->tree = parse->current; entry->location = parse->offset; + /* Set the new stack escape context to the parent one */ + err = uListGet (parse->stack, -1, (void *)¤t_entry); + if (err != STATUS_OK) { + free (entry); + goto cs_parse_done; + } + entry->escape = current_entry->escape; + /* Get the future escape context from parse because when + * we parse "escape", the new stack has not yet been established. + */ + entry->escape = parse->escaping.next_stack; + parse->escaping.next_stack = parse->escaping.global; err = uListAppend(parse->stack, entry); if (err != STATUS_OK) { free (entry); @@ -538,17 +586,16 @@ { err = uListPop(parse->stack, (void *)&entry); if (err != STATUS_OK) goto cs_parse_done; - if (entry->state & (ST_IF | ST_ELSE)) - return nerr_raise (NERR_PARSE, "%s Non-terminted if clause", - find_context(parse, entry->location, tmp, sizeof(tmp))); - if (entry->state & ST_EACH) - return nerr_raise (NERR_PARSE, "%s Non-terminted each clause", - find_context(parse, entry->location, tmp, sizeof(tmp))); + if (entry->state & ~(ST_GLOBAL | ST_POP)) + return nerr_raise (NERR_PARSE, "%s Non-terminted %s clause", + find_context(parse, entry->location, tmp, sizeof(tmp)), + expand_state(entry->state)); } cs_parse_done: parse->offset = initial_offset; parse->context_string = initial_context; + parse->escaping.current = NEOS_ESCAPE_NONE; return nerr_pass(err); } @@ -1006,7 +1053,7 @@ static NEOERR *parse_expr2 (CSPARSE *parse, CSTOKEN *tokens, int ntokens, int lvalue, CSARG *arg) { - NEOERR *err; + NEOERR *err = STATUS_OK; char tmp[256]; char tmp2[256]; int x, op; @@ -1260,8 +1307,13 @@ return nerr_raise (NERR_NOMEM, "%s Unable to allocate memory for expression", find_context(parse, -1, tmp, sizeof(tmp))); + if (ntokens-3 > 0) { err = parse_expr2(parse, tokens + 2, ntokens-3, lvalue, arg->expr1); if (err) return nerr_pass(err); + } else { + free(arg->expr1); + arg->expr1 = NULL; + } nargs = rearrange_for_call(&(arg->expr1)); if (nargs != arg->function->n_args) { @@ -1354,6 +1406,65 @@ return STATUS_OK; } +static NEOERR *escape_parse (CSPARSE *parse, int cmd, char *arg) +{ + NEOERR *err; + char *a = NULL; + char tmp[256]; + CS_ESCAPE_MODES *esc_cursor; + CSTREE *node; + + /* ne_warn ("escape: %s", arg); */ + err = alloc_node (&node); + if (err) return nerr_pass(err); + node->cmd = cmd; + /* Since this throws an error always if there's a problem + * this flag seems pointless, but following convention, + * here it is. */ + if (arg[0] == '!') + node->flags |= CSF_REQUIRED; + arg++; /* ignore colon, space, etc */ + + /* Parse the arg - we're expecting a string */ + err = parse_expr (parse, arg, 0, &(node->arg1)); + if (err) + { + dealloc_node(&node); + return nerr_pass(err); + } + if (node->arg1.op_type != CS_TYPE_STRING) + { + dealloc_node(&node); + return nerr_raise (NERR_PARSE, "%s Invalid argument for escape: %s", + find_context(parse, -1, tmp, sizeof(tmp)), arg); + } + + a = neos_strip(node->arg1.s); /* Strip spaces for testing */ + + /* Ensure the mode specified is allowed */ + for (esc_cursor = &EscapeModes[0]; + esc_cursor->mode != NULL; + esc_cursor++) + if (!strncasecmp(a, esc_cursor->mode, strlen(esc_cursor->mode))) + { + if (err != STATUS_OK) return nerr_pass(err); + parse->escaping.next_stack = esc_cursor->context; + break; + } + /* Didn't find an acceptable value we were looking for */ + if (esc_cursor->mode == NULL) + { + dealloc_node(&node); + return nerr_raise (NERR_PARSE, "%s Invalid argument for escape: %s", + find_context(parse, -1, tmp, sizeof(tmp)), a); + } + + *(parse->next) = node; + parse->next = &(node->case_0); + parse->current = node; + return STATUS_OK; +} + static NEOERR *name_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) { NEOERR *err = STATUS_OK; @@ -1377,11 +1488,26 @@ { NEOERR *err; CSTREE *node; + STACK_ENTRY *entry; + + err = uListGet (parse->stack, -1, (void *)&entry); + if (err != STATUS_OK) return nerr_pass(err); /* ne_warn ("var: %s", arg); */ err = alloc_node (&node); if (err) return nerr_pass(err); node->cmd = cmd; + + /* Default escape the variable based on + * current stack's escape context except for + * uvar: + */ + if (!strcmp(Commands[cmd].cmd, "uvar")) + node->escape = NEOS_ESCAPE_NONE; + else + node->escape = entry->escape; + + if (arg[0] == '!') node->flags |= CSF_REQUIRED; arg++; @@ -1916,6 +2042,11 @@ * argument1 */ err = expr->function->function(parse, expr->function, expr->expr1, result); if (err) return nerr_pass(err); + /* Indicate whether or not an explicit escape call was made by + * setting the mode (usually NONE or FUNCTION). This is ORed to + * ensure that escaping calls within other functions do not get + * double-escaped. E.g. slice(html_escape(foo), 10, 20) */ + parse->escaping.current |= expr->function->escape; } else { @@ -2093,6 +2224,7 @@ NEOERR *err = STATUS_OK; CSARG val; + parse->escaping.current = NEOS_ESCAPE_NONE; err = eval_expr(parse, &(node->arg1), &val); if (err) return nerr_pass(err); if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) @@ -2107,11 +2239,31 @@ else { char *s = arg_eval (parse, &val); - /* Do we set it to blank if s == NULL? */ - if (s) + /* Determine if the node has been escaped by an explicit function. If not + * call to escape. node->escape should contain the default escaping from + * Config.VarEscapeMode and parse->escaping.current will have a non-zero + * value if an explicit escape call was made sooooo. + */ + if (s && parse->escaping.current == NEOS_ESCAPE_NONE) /* no explicit escape */ + { + char *escaped = NULL; + /* Use default escape if escape is UNDEF */ + if (node->escape == NEOS_ESCAPE_UNDEF) + err = neos_var_escape(parse->escaping.when_undef, s, &escaped); + else + err = neos_var_escape(node->escape, s, &escaped); + + if (escaped) { + err = parse->output_cb (parse->output_ctx, escaped); + free(escaped); + } + } + else if (s) + { /* already explicitly escaped */ err = parse->output_cb (parse->output_ctx, s); } + /* Do we set it to blank if s == NULL? */ } if (val.alloc) free(val.s); @@ -2260,6 +2412,16 @@ return nerr_pass(err); } +/* just calls through to the child nodes */ +static NEOERR *escape_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) +{ + NEOERR *err = STATUS_OK; + /* TODO(wad): Should I set a eval-time value here? */ + err = render_node (parse, node->case_0); + *next = node->next; + return nerr_pass(err); +} + static NEOERR *if_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) { @@ -2487,7 +2649,12 @@ if (with_map.map_alloc) free(with_map.s); parse->locals = with_map.next; } - } /* else WARNING */ + } + else + { + /* else WARNING */ + ne_warn("Invalid op_type for with: %s", expand_token_type(val.op_type, 1)); + } if (val.alloc) free(val.s); *next = node->next; @@ -2550,6 +2717,11 @@ int x = 0; BOOL last = FALSE; + /* Since def doesn't get a new stack entry until after this is run, + * setup a dumb var on the parse object to hold the future setting. + */ + parse->escaping.next_stack = NEOS_ESCAPE_UNDEF; + err = alloc_node (&node); if (err) return nerr_pass(err); node->cmd = cmd; @@ -2719,10 +2891,15 @@ char name[256]; int x = 0; int nargs = 0; + STACK_ENTRY *entry; + + err = uListGet (parse->stack, -1, (void *)&entry); + if (err != STATUS_OK) return nerr_pass(err); err = alloc_node (&node); if (err) return nerr_pass(err); node->cmd = cmd; + node->escape = entry->escape; arg++; s = arg; while (x < sizeof(name) && *s && *s != ' ' && *s != '#' && *s != '(') @@ -2821,6 +2998,15 @@ HDF *var; int x; + /* Reset the value of when_undef for the coming call evaluation. + * This is only used here so it there's no need to reset its value after + * the call. If this call is nested (escape == NEOS_ESCAPE_UNDEF), then + * leave the when_undef variable alone. The parent call_eval should have + * already defined it. + */ + if (node->escape != NEOS_ESCAPE_UNDEF) + parse->escaping.when_undef = node->escape; + macro = node->arg1.macro; if (macro->n_args) { @@ -3265,6 +3451,7 @@ } csf->function = function; csf->n_args = n_args; + csf->escape = NEOS_ESCAPE_NONE; csf->next = parse->functions; parse->functions = csf; @@ -3284,7 +3471,7 @@ long int *i; CSARG val; - while (*fmt || args || err) + while (*fmt) { memset(&val, 0, sizeof(val)); err = eval_expr(parse, args, &val); @@ -3315,6 +3502,7 @@ default: break; } + if (err) return nerr_pass(err); fmt++; args = args->next; if (val.alloc) free(val.s); @@ -3391,6 +3579,34 @@ } +static NEOERR * _builtin_str_find(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) +{ + NEOERR *err; + char *s = NULL; + char *substr = NULL; + char *pstr = NULL; + + result->op_type = CS_TYPE_NUM; + result->n = -1; + + err = cs_arg_parse(parse, args, "ss", &s, &substr); + if (err) return nerr_pass(err); + /* If null arguments, return -1 index */ + if (s == NULL || substr == NULL) { + if (s) free(s); + if (substr) free(substr); + return STATUS_OK; + } + pstr = strstr(s, substr); + if (pstr != NULL) { + result->n = (pstr - s) / sizeof(char); + } + free(s); + free(substr); + return STATUS_OK; +} + + static NEOERR * _builtin_name(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) { NEOERR *err; @@ -3661,6 +3877,17 @@ return STATUS_OK; } +NEOERR *cs_register_esc_strfunc(CSPARSE *parse, char *funcname, + CSSTRFUNC str_func) +{ + NEOERR *err; + + err = cs_register_strfunc(parse, funcname, str_func); + if (err) return nerr_pass(err); + parse->functions->escape = NEOS_ESCAPE_FUNCTION; + + return STATUS_OK; +} /* **** CS Initialize/Destroy ************************************ */ NEOERR *cs_init (CSPARSE **parse, HDF *hdf) { @@ -3672,6 +3899,8 @@ NEOERR *err = STATUS_OK; CSPARSE *my_parse; STACK_ENTRY *entry; + char *esc_value; + CS_ESCAPE_MODES *esc_cursor; err = nerr_init(); if (err != STATUS_OK) return nerr_pass (err); @@ -3711,6 +3940,7 @@ entry->state = ST_GLOBAL; entry->tree = my_parse->current; entry->location = 0; + entry->escape = NEOS_ESCAPE_NONE; err = uListAppend(my_parse->stack, entry); if (err != STATUS_OK) { free (entry); @@ -3721,6 +3951,32 @@ my_parse->taglen = strlen(my_parse->tag); my_parse->hdf = hdf; + /* Let's set the default escape data */ + my_parse->escaping.global = NEOS_ESCAPE_NONE; + my_parse->escaping.next_stack = NEOS_ESCAPE_NONE; + my_parse->escaping.when_undef = NEOS_ESCAPE_NONE; + + /* See CS_ESCAPE_MODES. 0 is "none" */ + esc_value = hdf_get_value(hdf, "Config.VarEscapeMode", EscapeModes[0].mode); + /* Let's ensure the specified escape mode is valid and proceed */ + for (esc_cursor = &EscapeModes[0]; + esc_cursor->mode != NULL; + esc_cursor++) + if (!strcmp(esc_value, esc_cursor->mode)) + { + my_parse->escaping.global = esc_cursor->context; + my_parse->escaping.next_stack = esc_cursor->context; + entry->escape = esc_cursor->context; + break; + } + /* Didn't find an acceptable value we were looking for */ + if (esc_cursor->mode == NULL) { + cs_destroy (&my_parse); + return nerr_raise (NERR_OUTOFRANGE, + "Invalid HDF value for Config.VarEscapeMode (none,html,js,url): %s", + esc_value); + } + if (parent == NULL) { static struct _builtin_functions { @@ -3736,6 +3992,7 @@ { "abs", 1, _builtin_abs }, { "max", 2, _builtin_max }, { "min", 2, _builtin_min }, + { "string.find", 2, _builtin_str_find }, { "string.slice", 3, _builtin_str_slice }, { "string.length", 1, _builtin_str_length }, #ifdef ENABLE_GETTEXT @@ -3769,6 +4026,8 @@ * is gone. */ my_parse->functions = parent->functions; my_parse->global_hdf = parent->global_hdf; + my_parse->fileload = parent->fileload; + my_parse->fileload_ctx = parent->fileload_ctx; my_parse->parent = parent; } @@ -3776,6 +4035,13 @@ return STATUS_OK; } +void cs_register_fileload(CSPARSE *parse, void *ctx, CSFILELOAD fileload) { + if (parse != NULL) { + parse->fileload_ctx = ctx; + parse->fileload = fileload; + } +} + void cs_destroy (CSPARSE **parse) { CSPARSE *my_parse = *parse; diff -Nrub clearsilver-0.10.3/cs/Makefile clearsilver-0.10.4/cs/Makefile --- clearsilver-0.10.3/cs/Makefile 2006-02-01 18:20:49.000000000 -0800 +++ clearsilver-0.10.4/cs/Makefile 2006-11-09 14:33:41.000000000 -0800 @@ -22,7 +22,7 @@ CSDUMP_SRC = csdump.c CSDUMP_OBJ = $(CSDUMP_SRC:%.c=%.o) -LIBS += -L$(LIB_DIR) -lneo_cs -lneo_utl # -lefence +LIBS += -lneo_cs -lneo_utl # -lefence TARGETS = $(CS_LIB) $(CSTEST_EXE) $(CSR_EXE) test @@ -32,7 +32,8 @@ test_var.cs test_paren.cs test_chuck.cs test_trak1.cs test_iter.cs \ test_each_array.cs test_name.cs test_with.cs test_numbers.cs \ test_splice.cs test_joo.cs test_first_last.cs test_abs_max_min.cs \ - test_comma.cs test_macro_set.cs test_func.cs + test_comma.cs test_macro_set.cs test_func.cs test_escape.cs \ + test_uvar.cs all: $(TARGETS) @@ -42,13 +43,13 @@ $(RANLIB) $@ $(CSTEST_EXE): $(CSTEST_OBJ) $(CS_LIB) - $(LD) $@ $(CSTEST_OBJ) $(LIBS) # -lefence + $(LD) $@ $(CSTEST_OBJ) $(LDFLAGS) $(LIBS) # -lefence $(CSR_EXE): $(CSR_OBJ) $(CS_LIB) - $(LD) $@ $(CSR_OBJ) $(LIBS) # -lefence + $(LD) $@ $(CSR_OBJ) $(LDFLAGS) $(LIBS) # -lefence $(CSDUMP_EXE): $(CSDUMP_OBJ) $(CS_LIB) - $(LD) $@ $(CSDUMP_OBJ) $(LIBS) + $(LD) $@ $(CSDUMP_OBJ) $(LDFLAGS) $(LIBS) ## BE VERY CAREFUL WHEN REGENERATING THESE gold: $(CSTEST_EXE) @@ -93,6 +94,7 @@ $(INSTALL) -m 644 cs.h $(DESTDIR)$(cs_includedir)/cs $(INSTALL) -m 644 $(CS_LIB) $(DESTDIR)$(libdir) $(INSTALL) $(CSTEST_EXE) $(DESTDIR)$(bindir) + $(INSTALL) $(CSR_EXE) $(DESTDIR)$(bindir) clean: $(RM) core *.o diff -Nrub clearsilver-0.10.3/cs/test13.cs clearsilver-0.10.4/cs/test13.cs --- clearsilver-0.10.3/cs/test13.cs 2005-06-30 11:51:51.000000000 -0700 +++ clearsilver-0.10.4/cs/test13.cs 2006-10-09 17:53:09.000000000 -0700 @@ -3,4 +3,4 @@ - + diff -Nrub clearsilver-0.10.3/cs/test_cgi_escape.cs clearsilver-0.10.4/cs/test_cgi_escape.cs --- clearsilver-0.10.3/cs/test_cgi_escape.cs 1969-12-31 16:00:00.000000000 -0800 +++ clearsilver-0.10.4/cs/test_cgi_escape.cs 2006-08-07 13:01:51.000000000 -0700 @@ -0,0 +1,68 @@ + + + + +<?cs var:Title ?> + + +default escape +explicit escape +implicit: +explicit escape: +uvar: +implicit_slice: +slice_explicit_escape: +explicit_escape_slice: + + +non-html output: + + + + + + + + get_var: + + get_var_inside_url_escape: + get_var_inside_url_escape_with_explicit_escape: + + + get_var_inside_none_escape: + get_var_inside_none_escape_with_explicit_url_escape: + + + + + + nested ifs + + + + +Calling get_var(UrlArg) from within 'escape: "html"'
+--> + + +Including test_escape.cs:
+--- + + + +---- + + + escape level 1 + + escape level 2 + + diff -Nrub clearsilver-0.10.3/cs/test.cs clearsilver-0.10.4/cs/test.cs --- clearsilver-0.10.3/cs/test.cs 2005-06-30 11:51:49.000000000 -0700 +++ clearsilver-0.10.4/cs/test.cs 2006-08-07 13:01:51.000000000 -0700 @@ -30,6 +30,9 @@ + + + x = x.num = diff -Nrub clearsilver-0.10.3/cs/test.cs.gold clearsilver-0.10.4/cs/test.cs.gold --- clearsilver-0.10.3/cs/test.cs.gold 2006-02-01 16:23:33.000000000 -0800 +++ clearsilver-0.10.4/cs/test.cs.gold 2006-10-09 17:53:19.000000000 -0700 @@ -36,6 +36,100 @@ wow2 +escape: not used +UrlArg: Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? +BlahJs: quote ' backslash \ semicolon ; end tag +Title: + + +escape: none +UrlArg: Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? +BlahJs: quote ' backslash \ semicolon ; end tag +Title: + + + +escape: html +UrlArg: Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? +BlahJs: quote ' backslash \ semicolon ; end tag </script> +Title: </title><script>alert(1)</script> + + + +escape: js +UrlArg: Secret Password~!@#$%^\x26*()+=-_|\x5C[]{}:\x22\x3B\x27\x3C\x3E,.? +BlahJs: quote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +Title: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3E + + + +escape: url +UrlArg: Secret+Password%7E!%40%23%24%25%5E%26*()%2B%3D-_%7C%5C%5B%5D%7B%7D%3A%22%3B%27%3C%3E%2C.%3F +BlahJs: quote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +Title: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E + + + +Nested escaping: html +The internal calls should take precedence +url -> UrlArg: Secret+Password%7E!%40%23%24%25%5E%26*()%2B%3D-_%7C%5C%5B%5D%7B%7D%3A%22%3B%27%3C%3E%2C.%3F +js -> BlahJs: quote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +html -> Title: </title><script>alert(1)</script> + + +Defining the macro echo_all inside of a "html" escape. + + +Calling echo_all() macro: + +not used: quote ' backslash \ semicolon ; end tag +none: quote ' backslash \ semicolon ; end tag +url: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +js: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +html: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script> + + + +Calling echo_all() macro from within "html": + +not used: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script> +none: quote ' backslash \ semicolon ; end tag +url: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +js: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +html: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script> + + + + +Calling echo_all() macro from within "js": + +not used: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +none: quote ' backslash \ semicolon ; end tag +url: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +js: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +html: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script> + + + + +Calling echo_all() macro from within "url": + +not used: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +none: quote ' backslash \ semicolon ; end tag +url: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +js: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +html: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script> + + + + +not used: </title><script>alert(1)</script> +none: +url: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E +js: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3E +html: </title><script>alert(1)</script> + + x = zero x.num = #0 diff -Nrub clearsilver-0.10.3/cs/test_escape.cs clearsilver-0.10.4/cs/test_escape.cs --- clearsilver-0.10.3/cs/test_escape.cs 1969-12-31 16:00:00.000000000 -0800 +++ clearsilver-0.10.4/cs/test_escape.cs 2006-08-07 13:01:51.000000000 -0700 @@ -0,0 +1,67 @@ +escape: not used +UrlArg: +BlahJs: +Title: + + +escape: none +UrlArg: +BlahJs: +Title: + + + +escape: html +UrlArg: +BlahJs: +Title: + + + +escape: js +UrlArg: +BlahJs: +Title: + + + +escape: url +UrlArg: +BlahJs: +Title: + + + +Nested escaping: html +The internal calls should take precedence +url -> UrlArg: +js -> BlahJs: +html -> Title: + + +Defining the macro echo_all inside of a "html" escape. + +not used: +none: +url: +js: +html: + + +Calling echo_all() macro: + + + +Calling echo_all() macro from within "html": + + + + +Calling echo_all() macro from within "js": + + + + +Calling echo_all() macro from within "url": + + diff -Nrub clearsilver-0.10.3/cs/test_escape.cs.gold clearsilver-0.10.4/cs/test_escape.cs.gold --- clearsilver-0.10.3/cs/test_escape.cs.gold 1969-12-31 16:00:00.000000000 -0800 +++ clearsilver-0.10.4/cs/test_escape.cs.gold 2006-10-09 17:53:20.000000000 -0700 @@ -0,0 +1,86 @@ +Parsing test_escape.cs +escape: not used +UrlArg: Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? +BlahJs: quote ' backslash \ semicolon ; end tag +Title: + + +escape: none +UrlArg: Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? +BlahJs: quote ' backslash \ semicolon ; end tag +Title: + + + +escape: html +UrlArg: Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? +BlahJs: quote ' backslash \ semicolon ; end tag </script> +Title: </title><script>alert(1)</script> + + + +escape: js +UrlArg: Secret Password~!@#$%^\x26*()+=-_|\x5C[]{}:\x22\x3B\x27\x3C\x3E,.? +BlahJs: quote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +Title: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3E + + + +escape: url +UrlArg: Secret+Password%7E!%40%23%24%25%5E%26*()%2B%3D-_%7C%5C%5B%5D%7B%7D%3A%22%3B%27%3C%3E%2C.%3F +BlahJs: quote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +Title: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E + + + +Nested escaping: html +The internal calls should take precedence +url -> UrlArg: Secret+Password%7E!%40%23%24%25%5E%26*()%2B%3D-_%7C%5C%5B%5D%7B%7D%3A%22%3B%27%3C%3E%2C.%3F +js -> BlahJs: quote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +html -> Title: </title><script>alert(1)</script> + + +Defining the macro echo_all inside of a "html" escape. + + +Calling echo_all() macro: + +not used: quote ' backslash \ semicolon ; end tag +none: quote ' backslash \ semicolon ; end tag +url: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +js: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +html: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script> + + + +Calling echo_all() macro from within "html": + +not used: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script> +none: quote ' backslash \ semicolon ; end tag +url: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +js: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +html: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script> + + + + +Calling echo_all() macro from within "js": + +not used: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +none: quote ' backslash \ semicolon ; end tag +url: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +js: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +html: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script> + + + + +Calling echo_all() macro from within "url": + +not used: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +none: quote ' backslash \ semicolon ; end tag +url: %3C%2Ftitle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3Equote+%27+backslash+%5C+semicolon+%3B+end+tag+%3C%2Fscript%3E +js: \x3C\x2Ftitle\x3E\x3Cscript\x3Ealert(1)\x3C\x2Fscript\x3Equote \x27 backslash \x5C semicolon \x3B end tag \x3C\x2Fscript\x3E +html: </title><script>alert(1)</script>quote ' backslash \ semicolon ; end tag </script> + + diff -Nrub clearsilver-0.10.3/cs/test.hdf clearsilver-0.10.4/cs/test.hdf --- clearsilver-0.10.3/cs/test.hdf 2005-06-30 11:51:50.000000000 -0700 +++ clearsilver-0.10.4/cs/test.hdf 2006-08-07 13:01:51.000000000 -0700 @@ -1,4 +1,5 @@ +Config.VarEscapeMode = none arg1 = 1 var = @@ -11,6 +12,9 @@ } Blah = wow +BlahJs = quote ' backslash \ semicolon ; end tag +Title = +UrlArg = Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? Foo = Worn Out Foo.Bar.Baz { diff -Nrub clearsilver-0.10.3/cs/test_str_find.cs clearsilver-0.10.4/cs/test_str_find.cs --- clearsilver-0.10.3/cs/test_str_find.cs 1969-12-31 16:00:00.000000000 -0800 +++ clearsilver-0.10.4/cs/test_str_find.cs 2006-10-18 16:57:23.000000000 -0700 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff -Nrub clearsilver-0.10.3/cs/test_str_find.cs.gold clearsilver-0.10.4/cs/test_str_find.cs.gold --- clearsilver-0.10.3/cs/test_str_find.cs.gold 1969-12-31 16:00:00.000000000 -0800 +++ clearsilver-0.10.4/cs/test_str_find.cs.gold 2006-10-18 16:57:23.000000000 -0700 @@ -0,0 +1,21 @@ +Parsing test_str_find.cs + +string.find("String1", "ring") +2 + +string.find("String1", "1") +6 + +string.find("String1", "S") +0 + +string.find("String1", "k") +-1 + +Foo = "TheString2" + +string.find(Foo, "Str") +3 + +string.find(Foo, 1+1) +9 diff -Nrub clearsilver-0.10.3/cs/test_uvar.cs clearsilver-0.10.4/cs/test_uvar.cs --- clearsilver-0.10.3/cs/test_uvar.cs 1969-12-31 16:00:00.000000000 -0800 +++ clearsilver-0.10.4/cs/test_uvar.cs 2006-08-07 13:01:51.000000000 -0700 @@ -0,0 +1,6 @@ + + + + + + diff -Nrub clearsilver-0.10.3/cs/test_uvar.cs.gold clearsilver-0.10.4/cs/test_uvar.cs.gold --- clearsilver-0.10.3/cs/test_uvar.cs.gold 1969-12-31 16:00:00.000000000 -0800 +++ clearsilver-0.10.4/cs/test_uvar.cs.gold 2006-10-09 17:53:20.000000000 -0700 @@ -0,0 +1,7 @@ +Parsing test_uvar.cs +quote ' backslash \ semicolon ; end tag Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? + +quote ' backslash \ semicolon ; end tag Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? +quote ' backslash \ semicolon ; end tag Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? +quote ' backslash \ semicolon ; end tag Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? +quote ' backslash \ semicolon ; end tag Secret Password~!@#$%^&*()+=-_|\[]{}:";'<>,.? diff -Nrub clearsilver-0.10.3/dso/Makefile clearsilver-0.10.4/dso/Makefile --- clearsilver-0.10.3/dso/Makefile 2005-12-02 02:35:00.000000000 -0800 +++ clearsilver-0.10.4/dso/Makefile 2006-07-12 23:49:45.000000000 -0700 @@ -9,7 +9,7 @@ NEO_SO = libneo.so NEO_STATIC_LIBS = -LIBS += -L$(LIB_DIR) $(DLIBS) $(DB2_LIB) +LIBS += $(DLIBS) $(DB2_LIB) TARGETS = $(NEO_SO) dsotest @@ -17,7 +17,7 @@ all: $(TARGETS) $(NEO_SO): $(NEO_STATIC_LIBS) $(DEP_LIBS) Makefile - /usr/bin/ld -rpath ../libs -shared -o libneo.so -whole-archive ../libs/libneo_cgi.a ../libs/libneo_cs.a ../libs/libneo_utl.a -no-whole-archive $(LIBS) + /usr/bin/ld -rpath ../libs -shared -o libneo.so -whole-archive ../libs/libneo_cgi.a ../libs/libneo_cs.a ../libs/libneo_utl.a -no-whole-archive $(LDFLAGS) $(LIBS) # $(LDSHARED) -o $@ $(LDFLAGS) -Wl,-whole-archive $(DLIBS) diff -Nrub clearsilver-0.10.3/imd/imd.c clearsilver-0.10.4/imd/imd.c --- clearsilver-0.10.3/imd/imd.c 2005-06-30 11:51:53.000000000 -0700 +++ clearsilver-0.10.4/imd/imd.c 2006-11-03 08:56:08.000000000 -0800 @@ -56,7 +56,7 @@ if(isalpha(*ip)) { /* ctime */ - sscanf(ip,"%s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year); + sscanf(ip,"%25s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year); } else if(ip[2] == '-') { /* RFC 850 (normal HTTP) */ @@ -1089,8 +1089,8 @@ err = cgi_init(&cgi, NULL); if (err != STATUS_OK) { - cgi_neo_error(cgi, err); nerr_log_error(err); + cgi_destroy(&cgi); return -1; } imd_file = hdf_get_value(cgi->hdf, "CGI.PathTranslated", NULL); @@ -1100,6 +1100,7 @@ { cgi_neo_error(cgi, err); nerr_log_error(err); + cgi_destroy(&cgi); return -1; } @@ -1113,6 +1114,7 @@ if (err) { nerr_log_error(err); + cgi_destroy(&cgi); return -1; } } @@ -1136,6 +1138,7 @@ { cgi_neo_error(cgi, err); nerr_log_error(err); + cgi_destroy(&cgi); return -1; } } @@ -1146,9 +1149,11 @@ { cgi_neo_error(cgi, err); nerr_log_error(err); + cgi_destroy(&cgi); return -1; } } } + cgi_destroy(&cgi); return 0; } Binary files clearsilver-0.10.3/java-jni/clearsilver.jar and clearsilver-0.10.4/java-jni/clearsilver.jar differ diff -Nrub clearsilver-0.10.3/java-jni/CSTest.java clearsilver-0.10.4/java-jni/CSTest.java --- clearsilver-0.10.3/java-jni/CSTest.java 2005-11-16 19:17:07.000000000 -0800 +++ clearsilver-0.10.4/java-jni/CSTest.java 2006-11-09 16:54:04.000000000 -0800 @@ -100,6 +100,13 @@ System.out.println("Caught exception of type " + e.getClass().getName() + "\n"); } + System.out.println("Testing HDF.writeFile()\n"); + file_hdf.writeFile("test1_out.hdf"); + file_hdf.writeFileAtomic("test1_out2.hdf"); + + System.out.println("Testing HDF.writeString()\n"); + System.out.println(file_hdf.writeString()); + System.out.println("Testing HDF.getObj()"); HDF foo_hdf = file_hdf.getObj("Foo"); System.out.println(foo_hdf.dump()); @@ -134,5 +141,90 @@ two.copy("entity.value", one); System.out.println("name should be barneyb: " + two.getValue("entity.value.name", "--undefined--") +"\n"); + + System.out.println("Testing HDF.exportDate()"); + HDF date_hdf = new HDF(); + date_hdf.exportDate("DatePST", "US/Pacific", 1142308218); + date_hdf.exportDate("DateEST", "US/Eastern", 1142308218); + System.out.println(date_hdf.dump()); + + // Test default escaping mode: html + HDF escape_hdf = new HDF(); + System.out.println("Testing escape mode: html"); + System.out.println("Config.VarEscapeMode = \"html\""); + escape_hdf.setValue("Config.VarEscapeMode", "html"); + cs = new CS(escape_hdf); + + System.out.println("Some.HTML = " + + ""); + escape_hdf.setValue("Some.HTML", + ""); + tmplstr = "Default HTML escaping: \n"; + System.out.println(tmplstr); + System.out.println("----"); + cs.parseStr(tmplstr); + System.out.println(cs.render()); + + // Test default escaping mode: js + escape_hdf = new HDF(); + System.out.println("Testing escape mode: js"); + System.out.println("Config.VarEscapeMode = \"js\""); + escape_hdf.setValue("Config.VarEscapeMode", "js"); + cs = new CS(escape_hdf); + + System.out.println("Some.HTML = " + + ""); + escape_hdf.setValue("Some.HTML", + ""); + tmplstr = "Default JS escaping: \n"; + System.out.println(tmplstr); + System.out.println("----"); + cs.parseStr(tmplstr); + System.out.println(cs.render()); + + // Test default escaping mode: url + escape_hdf = new HDF(); + System.out.println("Testing escape mode: url"); + System.out.println("Config.VarEscapeMode = \"url\""); + escape_hdf.setValue("Config.VarEscapeMode", "url"); + cs = new CS(escape_hdf); + + System.out.println("Some.HTML = " + + ""); + escape_hdf.setValue("Some.HTML", + ""); + tmplstr = "Default URL escaping: \n"; + System.out.println(tmplstr); + System.out.println("----"); + cs.parseStr(tmplstr); + System.out.println(cs.render()); + + // Test escape blocks + escape_hdf = new HDF(); + System.out.println("Testing escape blocks: none"); + System.out.println("Config.VarEscapeMode = \"none\""); + escape_hdf.setValue("Config.VarEscapeMode", "none"); + cs = new CS(escape_hdf); + + System.out.println("Some.HTML = " + + ""); + escape_hdf.setValue("Some.HTML", + ""); + tmplstr = "url escape block: \n" + + "" + + " " + + "\n" + + "js escape block: \n" + + "" + + " " + + "\n" + + "html escape block: \n" + + "" + + " " + + "\n"; + System.out.println(tmplstr); + System.out.println("----"); + cs.parseStr(tmplstr); + System.out.println(cs.render()); } }; diff -Nrub clearsilver-0.10.3/java-jni/HDF.java clearsilver-0.10.4/java-jni/HDF.java --- clearsilver-0.10.3/java-jni/HDF.java 2005-11-16 19:10:06.000000000 -0800 +++ clearsilver-0.10.4/java-jni/HDF.java 2006-10-09 17:47:43.000000000 -0700 @@ -3,6 +3,10 @@ import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + /** This class is a wrapper around the HDF C API. Many features of the C API * are not yet exposed through this wrapper. */ @@ -68,6 +72,45 @@ return _readFile(hdfptr, filename); } + /** Serializes HDF contents to a file (readable by readFile) + */ + public boolean writeFile(String filename) throws IOException { + if (hdfptr == 0) { + throw new NullPointerException("HDF is closed."); + } + return _writeFile(hdfptr, filename); + } + + /** Serializes HDF contents to a file (readable by readFile), but + * writes the file atomically by writing to a temp file then doing a + * rename(2) on it. + */ + public boolean writeFileAtomic(String filename) throws IOException { + if (hdfptr == 0) { + throw new NullPointerException("HDF is closed."); + } + return _writeFileAtomic(hdfptr, filename); + } + + /** Parses/loads the contents of the given string as HDF into the current + * HDF object. The loaded contents are merged with the existing contents. + */ + public boolean readString(String data) { + if (hdfptr == 0) { + throw new NullPointerException("HDF is closed."); + } + return _readString(hdfptr, data); + } + + /** Serializes HDF contents to a string (readable by readString) + */ + public String writeString() { + if (hdfptr == 0) { + throw new NullPointerException("HDF is closed."); + } + return _writeString(hdfptr); + } + /** Retrieves the integer value at the specified path in this HDF node's * subtree. If the value does not exist, or cannot be converted to an * integer, default_value will be returned. */ @@ -111,6 +154,66 @@ _setSymLink(hdfptr,hdf_name_src,hdf_name_dest); } + /** Export a date to a clearsilver tree using a specified timezone */ + public void exportDate(String hdfname, TimeZone timeZone, Date date) { + if (hdfptr == 0) { + throw new NullPointerException("HDF is closed."); + } + + Calendar cal = Calendar.getInstance(timeZone); + cal.setTime(date); + + String sec = Integer.toString(cal.get(Calendar.SECOND)); + setValue(hdfname + ".sec", sec.length() == 1 ? "0" + sec : sec); + + String min = Integer.toString(cal.get(Calendar.MINUTE)); + setValue(hdfname + ".min", min.length() == 1 ? "0" + min : min); + + setValue(hdfname + ".24hour", + Integer.toString(cal.get(Calendar.HOUR_OF_DAY))); + // java.util.Calendar uses represents 12 o'clock as 0 + setValue(hdfname + ".hour", + Integer.toString( + cal.get(Calendar.HOUR) == 0 ? 12 : cal.get(Calendar.HOUR))); + setValue(hdfname + ".am", + cal.get(Calendar.AM_PM) == Calendar.AM ? "1" : "0"); + setValue(hdfname + ".mday", + Integer.toString(cal.get(Calendar.DAY_OF_MONTH))); + setValue(hdfname + ".mon", + Integer.toString(cal.get(Calendar.MONTH)+1)); + setValue(hdfname + ".year", + Integer.toString(cal.get(Calendar.YEAR))); + setValue(hdfname + ".2yr", + Integer.toString(cal.get(Calendar.YEAR)).substring(2)); + setValue(hdfname + ".wday", + Integer.toString(cal.get(Calendar.DAY_OF_WEEK))); + + boolean tzNegative = timeZone.getRawOffset() < 0; + int tzAbsolute = java.lang.Math.abs(timeZone.getRawOffset()/1000); + String tzHour = Integer.toString(tzAbsolute/3600); + String tzMin = Integer.toString(tzAbsolute/60 - (tzAbsolute/3600)*60); + String tzString = (tzNegative ? "-" : "+") + + (tzHour.length() == 1 ? "0" + tzHour : tzHour) + + (tzMin.length() == 1 ? "0" + tzMin : tzMin); + setValue(hdfname + ".tzoffset", tzString); + } + + /** Export a date to a clearsilver tree using a specified timezone */ + public void exportDate(String hdfname, String tz, int tt) { + if (hdfptr == 0) { + throw new NullPointerException("HDF is closed."); + } + + TimeZone timeZone = TimeZone.getTimeZone(tz); + + if (timeZone == null) { + throw new RuntimeException("Unknown timezone: " + tz); + } + + Date date = new Date((long)tt * 1000); + + exportDate(hdfname, timeZone, date); + } /** Retrieves the HDF object that is the root of the subtree at hdfpath, or * null if no object exists at that path. */ @@ -125,8 +228,9 @@ return new HDF(obj_ptr, this); } - /** Retrieves the HDF for the first child of the root of the subtree at hdfpath, or - * null if no child exists of that path or if the path doesn't exist. */ + /** Retrieves the HDF for the first child of the root of the subtree + * at hdfpath, or null if no child exists of that path or if the + * path doesn't exist. */ public HDF getChild(String hdfpath) { if (hdfptr == 0) { throw new NullPointerException("HDF is closed."); @@ -138,6 +242,12 @@ return new HDF(obj_ptr, this); } + /** Return the root of the tree where the current node lies. If the + * current node is the root, return this. */ + public HDF getRootObj() { + return root != null ? root : this; + } + /** Retrieves the HDF object that is the root of the subtree at * hdfpath, create the subtree if it doesn't exist */ public HDF getOrCreateObj(String hdfpath) { @@ -223,6 +333,10 @@ private static native int _init(); private static native void _dealloc(int ptr); private static native boolean _readFile(int ptr, String filename); + private static native boolean _writeFile(int ptr, String filename); + private static native boolean _writeFileAtomic(int ptr, String filename); + private static native boolean _readString(int ptr, String data); + private static native String _writeString(int ptr); private static native int _getIntValue(int ptr, String hdfname, int default_value); private static native String _getValue(int ptr, String hdfname, diff -Nrub clearsilver-0.10.3/java-jni/javatest.gold clearsilver-0.10.4/java-jni/javatest.gold --- clearsilver-0.10.3/java-jni/javatest.gold 2005-11-16 19:17:29.000000000 -0800 +++ clearsilver-0.10.4/java-jni/javatest.gold 2006-11-09 16:54:04.000000000 -0800 @@ -65,6 +65,15 @@ Testing HDF.readFile() for a file that doesn't exist Caught exception of type java.io.FileNotFoundException +Testing HDF.writeFile() + +Testing HDF.writeString() + +Foo { +Bar = 10 +Baz = 20 +} + Testing HDF.getObj() Bar = 10 Baz = 20 @@ -88,3 +97,69 @@ Testing HDF.copy() name should be barneyb: barneyb +Testing HDF.exportDate() +DatePST.sec = 18 +DatePST.min = 50 +DatePST.24hour = 19 +DatePST.hour = 7 +DatePST.am = 0 +DatePST.mday = 13 +DatePST.mon = 3 +DatePST.year = 2006 +DatePST.2yr = 06 +DatePST.wday = 2 +DatePST.tzoffset = -0800 +DateEST.sec = 18 +DateEST.min = 50 +DateEST.24hour = 22 +DateEST.hour = 10 +DateEST.am = 0 +DateEST.mday = 13 +DateEST.mon = 3 +DateEST.year = 2006 +DateEST.2yr = 06 +DateEST.wday = 2 +DateEST.tzoffset = -0500 + +Testing escape mode: html +Config.VarEscapeMode = "html" +Some.HTML = +Default HTML escaping: + +---- +Default HTML escaping: <script src="some.js">alert('123');</script> + +Testing escape mode: js +Config.VarEscapeMode = "js" +Some.HTML = +Default JS escaping: + +---- +Default JS escaping: \x3Cscript src=\x22some.js\x22\x3Ealert(\x27123\x27)\x3B\x3C\x2Fscript\x3E + +Testing escape mode: url +Config.VarEscapeMode = "url" +Some.HTML = +Default URL escaping: + +---- +Default URL escaping: %3Cscript+src%3D%22some.js%22%3Ealert(%27123%27)%3B%3C%2Fscript%3E + +Testing escape blocks: none +Config.VarEscapeMode = "none" +Some.HTML = +url escape block: + +js escape block: + +html escape block: + + +---- +url escape block: + %3Cscript+src%3D%22some.js%22%3Ealert(%27123%27)%3B%3C%2Fscript%3E +js escape block: + \x3Cscript src=\x22some.js\x22\x3Ealert(\x27123\x27)\x3B\x3C\x2Fscript\x3E +html escape block: + <script src="some.js">alert('123');</script> + diff -Nrub clearsilver-0.10.3/java-jni/j_neo_util.c clearsilver-0.10.4/java-jni/j_neo_util.c --- clearsilver-0.10.3/java-jni/j_neo_util.c 2005-11-16 20:08:32.000000000 -0800 +++ clearsilver-0.10.4/java-jni/j_neo_util.c 2006-04-20 12:56:25.000000000 -0700 @@ -249,7 +249,7 @@ jboolean retval; filename = (*env)->GetStringUTFChars(env, j_filename, 0); - err = hdf_read_file(hdf, (char*)filename); + err = hdf_read_file(hdf, filename); (*env)->ReleaseStringUTFChars(env, j_filename, filename); if (err != STATUS_OK) { // Throw an exception. jNeoErr handles all types of errors other than @@ -269,6 +269,74 @@ return retval; } +JNIEXPORT jboolean JNICALL Java_org_clearsilver_HDF__1writeFile( + JNIEnv *env, jobject objClass, jint hdf_obj_ptr, jstring j_filename) { + HDF *hdf = (HDF *)hdf_obj_ptr; + NEOERR *err; + const char *filename; + jboolean retval; + + filename = (*env)->GetStringUTFChars(env, j_filename, 0); + err = hdf_write_file(hdf, filename); + (*env)->ReleaseStringUTFChars(env, j_filename, filename); + if (err != STATUS_OK) { + jNeoErr(env, err); + } + retval = (err == STATUS_OK); + return retval; +} + +JNIEXPORT jboolean JNICALL Java_org_clearsilver_HDF__1writeFileAtomic( + JNIEnv *env, jobject objClass, jint hdf_obj_ptr, jstring j_filename) { + HDF *hdf = (HDF *)hdf_obj_ptr; + NEOERR *err; + const char *filename; + jboolean retval; + + filename = (*env)->GetStringUTFChars(env, j_filename, 0); + err = hdf_write_file_atomic(hdf, filename); + (*env)->ReleaseStringUTFChars(env, j_filename, filename); + if (err != STATUS_OK) { + jNeoErr(env, err); + } + retval = (err == STATUS_OK); + return retval; +} + +JNIEXPORT jboolean JNICALL Java_org_clearsilver_HDF__1readString( + JNIEnv *env, jobject objClass, jint hdf_obj_ptr, jstring j_data) { + HDF *hdf = (HDF *)hdf_obj_ptr; + NEOERR *err; + const char *data; + jboolean retval; + + data = (*env)->GetStringUTFChars(env, j_data, 0); + err = hdf_read_string(hdf, data); + (*env)->ReleaseStringUTFChars(env, j_data, data); + if (err != STATUS_OK) { + jNeoErr(env, err); + } + retval = (err == STATUS_OK); + return retval; +} + +JNIEXPORT jstring JNICALL Java_org_clearsilver_HDF__1writeString( + JNIEnv *env, jclass objClass, jint hdf_obj_ptr) { + HDF *hdf = (HDF *)hdf_obj_ptr; + NEOERR *err; + char *output = NULL; + jstring retval = NULL; + + err = hdf_write_string(hdf, &output); + if (err != STATUS_OK) { + jNeoErr(env, err); + } else if (output) { + retval = (*env)->NewStringUTF(env, output); + free(output); + } + return retval; +} + JNIEXPORT jint JNICALL Java_org_clearsilver_HDF__1getObj( JNIEnv *env, jclass objClass, jint hdf_obj_ptr, jstring j_hdf_path) { HDF *hdf = (HDF *)hdf_obj_ptr; Binary files clearsilver-0.10.3/java-jni/libclearsilver-jni.so and clearsilver-0.10.4/java-jni/libclearsilver-jni.so differ diff -Nrub clearsilver-0.10.3/java-jni/Makefile clearsilver-0.10.4/java-jni/Makefile --- clearsilver-0.10.3/java-jni/Makefile 2005-12-02 02:35:28.000000000 -0800 +++ clearsilver-0.10.4/java-jni/Makefile 2006-08-07 13:05:13.000000000 -0700 @@ -18,7 +18,8 @@ CFLAGS += $(JAVA_INCLUDE_PATH) DLIBS += -lneo_cgi -lneo_cs -lneo_utl -LIBS += -L$(LIB_DIR) $(DLIBS) +LIBS += $(DLIBS) +LDFLAGS += -Wl,-soname=$(NEO_UTIL_SO) TARGETS = org_clearsilver_HDF.h org_clearsilver_CS.h $(NEO_UTIL_SO) diff -Nrub clearsilver-0.10.3/Makefile clearsilver-0.10.4/Makefile --- clearsilver-0.10.3/Makefile 2006-03-12 15:42:06.000000000 -0800 +++ clearsilver-0.10.4/Makefile 2006-11-14 22:54:13.000000000 -0800 @@ -29,7 +29,7 @@ @for mdir in $(SUBDIRS); do \ if test -d $$mdir; then \ if test -f $$mdir/Makefile.PL -a ! -f $$mdir/Makefile; then \ - cd $$mdir; $(PERL) Makefile.PL; cd ..; \ + cd $$mdir; $(PERL) Makefile.PL PREFIX=$(prefix); cd ..; \ fi; \ $(MAKE) -C $$mdir PREFIX=$(prefix); \ fi; \ @@ -46,7 +46,7 @@ @for mdir in $(SUBDIRS); do \ if test -d $$mdir; then \ if test -f $$mdir/Makefile.PL -a ! -f $$mdir/Makefile; then \ - cd $$mdir; $(PERL) Makefile.PL; cd ..; \ + cd $$mdir; $(PERL) Makefile.PL PREFIX=$(prefix); cd ..; \ fi; \ $(MAKE) -C $$mdir PREFIX=$(prefix) install; \ fi; \ @@ -90,15 +90,15 @@ clean: - @for mdir in $(SUBDIRS); do \ + -@for mdir in $(SUBDIRS); do \ $(MAKE) -C $$mdir clean; \ done distclean: - @for mdir in $(SUBDIRS); do \ + -@for mdir in $(SUBDIRS); do \ $(MAKE) -C $$mdir distclean; \ done - @for mdir in $(OUTDIRS); do \ + -@for mdir in $(OUTDIRS); do \ rm -rf $$mdir/*; \ done rm -f config.cache config.log config.status rules.mk cs_config.h @@ -108,8 +108,8 @@ mkdir -p $$mdir; \ done -CS_DISTDIR = clearsilver-0.10.3 -CS_LABEL = CLEARSILVER-0_10_3 +CS_DISTDIR = clearsilver-0.10.4 +CS_LABEL = CLEARSILVER-0_10_4 CS_FILES = README README.python INSTALL LICENSE CS_LICENSE rules.mk.in Makefile acconfig.h autogen.sh config.guess config.sub configure.in cs_config.h.in mkinstalldirs install-sh ClearSilver.h CS_DIRS = util cs cgi python scripts mod_ecs imd java-jni perl ruby dso csharp ports contrib m4 diff -Nrub clearsilver-0.10.3/mod_ecs/Makefile clearsilver-0.10.4/mod_ecs/Makefile --- clearsilver-0.10.3/mod_ecs/Makefile 2005-12-02 02:35:34.000000000 -0800 +++ clearsilver-0.10.4/mod_ecs/Makefile 2006-07-12 23:50:57.000000000 -0700 @@ -10,7 +10,7 @@ MOD_ECS_SO = mod_ecs.so DLIBS += -lneo_cgi -lneo_cs -lneo_utl -LIBS += -L$(LIB_DIR) $(DLIBS) $(DB2_LIB) +LIBS += $(DLIBS) $(DB2_LIB) TARGETS = $(MOD_ECS_SO) diff -Nrub clearsilver-0.10.3/ports/rpm/clearsilver.spec clearsilver-0.10.4/ports/rpm/clearsilver.spec --- clearsilver-0.10.3/ports/rpm/clearsilver.spec 2006-03-12 15:43:17.000000000 -0800 +++ clearsilver-0.10.4/ports/rpm/clearsilver.spec 2006-11-14 22:54:20.000000000 -0800 @@ -53,11 +53,11 @@ Summary: Neotonic ClearSilver Name: clearsilver -Version: 0.10.3 +Version: 0.10.4 Release: 1 Copyright: Open Source - Neotonic ClearSilver License (Apache 1.1 based) Group: Development/Libraries -Source: http://www.clearsilver.net/downloads/clearsilver-0.10.3.tar.gz +Source: http://www.clearsilver.net/downloads/clearsilver-0.10.4.tar.gz URL: http://www.clearsilver.net/ Vendor: Neotonic Software Corporation, Inc. Packager: Brandon Long diff -Nrub clearsilver-0.10.3/python/Makefile clearsilver-0.10.4/python/Makefile --- clearsilver-0.10.3/python/Makefile 2005-12-02 02:35:46.000000000 -0800 +++ clearsilver-0.10.4/python/Makefile 2006-07-12 23:51:49.000000000 -0700 @@ -13,7 +13,7 @@ CFLAGS += $(PYTHON_INC) DLIBS += -lneo_cgi -lneo_cs -lneo_utl -LIBS += -L$(LIB_DIR) $(DLIBS) $(DB2_LIB) +LIBS += $(DLIBS) $(DB2_LIB) ifeq ($(USE_MINGW32),1) TARGETS = $(NEO_UTIL_PYD) @@ -38,7 +38,7 @@ --def neo_cgi.def -o neo_cgi.pyd \ $(NEO_UTIL_OBJ) -s --entry _DllMain@12 \ --target=i386-mingw32 \ - $(PYTHON_LIB) $(LIBS) + $(LDFLAGS) $(PYTHON_LIB) $(LIBS) install: all $(NEOTONIC_ROOT)/mkinstalldirs $(DESTDIR)$(PYTHON_SITE) diff -Nrub clearsilver-0.10.3/python/neo_cgi.c clearsilver-0.10.4/python/neo_cgi.c --- clearsilver-0.10.3/python/neo_cgi.c 2005-11-30 20:15:03.000000000 -0800 +++ clearsilver-0.10.4/python/neo_cgi.c 2006-11-09 14:34:08.000000000 -0800 @@ -57,7 +57,7 @@ { cgi_destroy (&(ho->cgi)); } - PyMem_DEL(ho); + PyObject_DEL(ho); } PyObject * p_cgi_to_object (CGI *data) diff -Nrub clearsilver-0.10.3/python/neo_cs.c clearsilver-0.10.4/python/neo_cs.c --- clearsilver-0.10.3/python/neo_cs.c 2005-06-30 11:51:56.000000000 -0700 +++ clearsilver-0.10.4/python/neo_cs.c 2006-11-09 14:34:34.000000000 -0800 @@ -54,7 +54,7 @@ { cs_destroy (&(ho->data)); } - PyMem_DEL(ho); + PyObject_DEL(ho); } PyObject * p_cs_to_object (CSPARSE *data) diff -Nrub clearsilver-0.10.3/python/neo_util.c clearsilver-0.10.4/python/neo_util.c --- clearsilver-0.10.3/python/neo_util.c 2005-06-30 11:51:56.000000000 -0700 +++ clearsilver-0.10.4/python/neo_util.c 2006-11-09 14:34:58.000000000 -0800 @@ -76,7 +76,7 @@ { hdf_destroy (&(ho->data)); } - PyMem_DEL(ho); + PyObject_DEL(ho); } PyObject * p_hdf_to_object (HDF *data, int dealloc) diff -Nrub clearsilver-0.10.3/python/setup.py clearsilver-0.10.4/python/setup.py --- clearsilver-0.10.3/python/setup.py 2006-03-12 16:15:48.000000000 -0800 +++ clearsilver-0.10.4/python/setup.py 2006-11-14 22:54:32.000000000 -0800 @@ -13,7 +13,7 @@ from distutils.core import Extension from distutils import sysconfig -VERSION = "0.10.3" +VERSION = "0.10.4" INC_DIRS = ["../"] LIBRARIES = ["neo_cgi", "neo_cs", "neo_utl"] LIB_DIRS = ["../libs"] diff -Nrub clearsilver-0.10.3/ruby/hdftest.out clearsilver-0.10.4/ruby/hdftest.out --- clearsilver-0.10.3/ruby/hdftest.out 1969-12-31 16:00:00.000000000 -0800 +++ clearsilver-0.10.4/ruby/hdftest.out 2006-11-14 23:22:59.000000000 -0800 @@ -0,0 +1,27 @@ +1 = farming +2 = sewing +3 = bowling +party.1 [Drool="True"] = baloons +party.2 [Pink] = noise makers +party.3 << EOM +telling long +stories +EOM +arf.1 = farming +arf.2 = sewing +arf.3 = bowling +arf.party.1 [Drool="True"] = baloons +arf.party.2 [Pink] = noise makers +arf.party.3 << EOM +telling long +stories +EOM +party.2 attr (Pink=1) +This is a funny test. farming. + +baloons + +noise makers + +telling long +stories diff -Nrub clearsilver-0.10.3/util/neo_hash.c clearsilver-0.10.4/util/neo_hash.c --- clearsilver-0.10.3/util/neo_hash.c 2005-11-30 20:06:03.000000000 -0800 +++ clearsilver-0.10.4/util/neo_hash.c 2006-10-18 16:57:24.000000000 -0700 @@ -289,5 +289,5 @@ UINT32 ne_hash_int_hash(const void *a) { - return (UINT32)(a); + return (UINT32)(long)(a); } diff -Nrub clearsilver-0.10.3/util/neo_hdf.c clearsilver-0.10.4/util/neo_hdf.c --- clearsilver-0.10.3/util/neo_hdf.c 2006-03-07 12:24:44.000000000 -0800 +++ clearsilver-0.10.4/util/neo_hdf.c 2006-08-11 16:47:01.000000000 -0700 @@ -25,6 +25,7 @@ #include "neo_rand.h" #include "neo_hdf.h" #include "neo_str.h" +#include "neo_files.h" #include "ulist.h" /* Ok, in order to use the hash, we have to support n-len strings @@ -419,7 +420,7 @@ /* a set of NULL deletes the attr */ if (value == NULL) { - if (last == obj->attr) + if (attr == obj->attr) obj->attr = attr->next; else last->next = attr->next; @@ -996,16 +997,56 @@ return STATUS_OK; } +static NEOERR * _copy_attr (HDF_ATTR **dest, HDF_ATTR *src) +{ + HDF_ATTR *copy, *last = NULL; + + *dest = NULL; + while (src != NULL) + { + copy = (HDF_ATTR *)malloc(sizeof(HDF_ATTR)); + if (copy == NULL) + { + _dealloc_hdf_attr(dest); + return nerr_raise(NERR_NOMEM, "Unable to allocate copy of HDF_ATTR"); + } + copy->key = strdup(src->key); + copy->value = strdup(src->value); + copy->next = NULL; + if ((copy->key == NULL) || (copy->value == NULL)) + { + _dealloc_hdf_attr(dest); + return nerr_raise(NERR_NOMEM, "Unable to allocate copy of HDF_ATTR"); + } + if (last) { + last->next = copy; + } + else + { + *dest = copy; + } + last = copy; + src = src->next; + } + return STATUS_OK; +} + static NEOERR * _copy_nodes (HDF *dest, HDF *src) { NEOERR *err = STATUS_OK; HDF *dt, *st; + HDF_ATTR *attr_copy; st = src->child; while (st != NULL) { - err = _set_value(dest, st->name, st->value, 1, 1, 0, st->attr, &dt); + err = _copy_attr(&attr_copy, st->attr); if (err) return nerr_pass(err); + err = _set_value(dest, st->name, st->value, 1, 1, 0, attr_copy, &dt); + if (err) { + _dealloc_hdf_attr(&attr_copy); + return nerr_pass(err); + } if (src->child) { err = _copy_nodes (dt, st); @@ -1292,7 +1333,6 @@ } -/* HDF file looks like the following: */ #define SKIPWS(s) while (*s && isspace(*s)) s++; static int _copy_line (const char **s, char *buf, size_t buf_len) @@ -1311,33 +1351,32 @@ return x; } -static int _copy_line_alloc (const char **s, char **buf) +/* Copy the characters in the file (up to the next newline) into line + * and advance s to the next line */ +static NEOERR *_copy_line_advance(const char **s, STRING *line) { NEOERR *err; int x = 0; const char *st = *s; - STRING str; const char *nl; - string_init(&str); - nl = strchr(st, '\n'); if (nl == NULL) { x = strlen(st); - err = string_append(&str, st); + err = string_appendn(line, st, x); + if (err) return nerr_pass(err); *s = st + x; } else { x = nl - st; - err = string_appendn(&str, st, x); + err = string_appendn(line, st, x); + if (err) return nerr_pass(err); *s = nl + 1; } - *buf = str.buf; - - return x; + return STATUS_OK; } char *_strndup(const char *s, int len) { @@ -1495,260 +1534,40 @@ return STATUS_OK; } -static NEOERR* _hdf_read_string (HDF *hdf, const char **str, int *line, - int ignore) +#define INCLUDE_ERROR 0 +#define INCLUDE_IGNORE 1 +#define INCLUDE_FILE 2 + +static NEOERR* _hdf_read_string (HDF *hdf, const char **str, STRING *line, + const char *path, int *lineno, int include_handle) { NEOERR *err; HDF *lower; - char *buf = NULL; char *s; char *name, *value; HDF_ATTR *attr = NULL; while (**str != '\0') { - _copy_line_alloc(str, &buf); + /* Reset string length, but don't free the reserved buffer */ + line->len = 0; + err = _copy_line_advance(str, line); + if (err) return nerr_pass(err); attr = NULL; - (*line)++; - s = buf; + (*lineno)++; + s = line->buf; SKIPWS(s); if (!strncmp(s, "#include ", 9)) { - if (!ignore) + if (include_handle == INCLUDE_ERROR) { - if (buf != NULL) free(buf); return nerr_raise (NERR_PARSE, "[%d]: #include not supported in string parse", - *line); - } - } - else if (s[0] == '#') - { - /* comment: pass */ - } - else if (s[0] == '}') /* up */ - { - s = neos_strip(s); - if (strcmp(s, "}")) - { - err = nerr_raise(NERR_PARSE, - "[%d] Trailing garbage on line following }: %s", *line, - buf); - if (buf != NULL) free(buf); - return err; - } - if (buf != NULL) free(buf); - return STATUS_OK; - } - else if (s[0]) - { - /* Valid hdf name is [0-9a-zA-Z_.]+ */ - name = s; - while (*s && (isalnum(*s) || *s == '_' || *s == '.')) s++; - SKIPWS(s); - - if (s[0] == '[') /* attributes */ - { - *s = '\0'; - name = neos_strip(name); - s++; - err = parse_attr(&s, &attr); - if (err) - { - if (buf != NULL) free(buf); - return nerr_pass_ctx(err, "In String %d", *line); + *lineno); } - SKIPWS(s); - } - if (s[0] == '=') /* assignment */ + else if (include_handle == INCLUDE_FILE) { - *s = '\0'; - name = neos_strip(name); - s++; - value = neos_strip(s); - err = _set_value (hdf, name, value, 1, 1, 0, attr, NULL); - if (err != STATUS_OK) - { - if (buf != NULL) free(buf); - return nerr_pass_ctx(err, "In String %d", *line); - } - } - else if (s[0] == ':' && s[1] == '=') /* copy */ - { - *s = '\0'; - name = neos_strip(name); - s+=2; - value = neos_strip(s); - value = hdf_get_value(hdf->top, value, ""); - err = _set_value (hdf, name, value, 1, 1, 0, attr, NULL); - if (err != STATUS_OK) - { - if (buf != NULL) free(buf); - return nerr_pass_ctx(err, "In string %d", *line); - } - } - else if (s[0] == ':') /* link */ - { - *s = '\0'; - name = neos_strip(name); - s++; - value = neos_strip(s); - err = _set_value (hdf, name, value, 1, 1, 1, attr, NULL); - if (err != STATUS_OK) - { - if (buf != NULL) free(buf); - return nerr_pass_ctx(err, "In string %d", *line); - } - } - else if (s[0] == '{') /* deeper */ - { - *s = '\0'; - name = neos_strip(name); - lower = hdf_get_obj (hdf, name); - if (lower == NULL) - { - err = _set_value (hdf, name, NULL, 1, 1, 0, attr, &lower); - } - else - { - err = _set_value (lower, NULL, lower->value, 1, 1, 0, attr, NULL); - } - if (err != STATUS_OK) - { - if (buf != NULL) free(buf); - return nerr_pass_ctx(err, "In string %d", *line); - } - err = _hdf_read_string (lower, str, line, ignore); - if (err != STATUS_OK) - { - if (buf != NULL) free(buf); - return nerr_pass_ctx(err, "In string %d", *line); - } - } - else if (s[0] == '<' && s[1] == '<') /* multi-line assignment */ - { - char *m; - int msize = 0; - int mmax = 128; int l; - - *s = '\0'; - name = neos_strip(name); - s+=2; - value = neos_strip(s); - l = strlen(value); - if (l == 0) - { - err = nerr_raise(NERR_PARSE, - "[%d] No multi-assignment terminator given: %s", *line, - buf); - if (buf != NULL) free(buf); - return err; - } - m = (char *) malloc (mmax * sizeof(char)); - if (m == NULL) - { - if (buf != NULL) free(buf); - return nerr_raise(NERR_NOMEM, - "[%d] Unable to allocate memory for multi-line assignment to %s", - *line, name); - } - while (_copy_line (str, m+msize, mmax-msize) != 0) - { - if (!strncmp(value, m+msize, l) && isspace(m[msize+l])) - { - m[msize] = '\0'; - break; - } - msize += strlen(m+msize); - if (msize + l + 10 > mmax) - { - mmax += 128; - m = (char *) realloc (m, mmax * sizeof(char)); - if (m == NULL) - { - if (buf != NULL) free(buf); - return nerr_raise(NERR_NOMEM, - "[%d] Unable to allocate memory for multi-line assignment to %s: size=%d", - *line, name, mmax); - } - } - } - err = _set_value (hdf, name, m, 0, 1, 0, attr, NULL); - if (err != STATUS_OK) - { - free (m); - return nerr_pass_ctx(err, "In string %d", *line); - } - - } - else - { - err = nerr_raise(NERR_PARSE, "[%d] Unable to parse line %s", - *line, buf); - if (buf != NULL) free(buf); - return err; - } - } - if (buf != NULL) free(buf); - buf = NULL; - } - if (buf != NULL) free(buf); - return STATUS_OK; -} - -NEOERR * hdf_read_string (HDF *hdf, const char *str) -{ - int line = 0; - return nerr_pass (_hdf_read_string (hdf, &str, &line, 0)); -} - -NEOERR * hdf_read_string_ignore (HDF *hdf, const char *str, int ignore) -{ - int line = 0; - return nerr_pass (_hdf_read_string (hdf, &str, &line, ignore)); -} - -static int count_newlines (const char *s) -{ - int i = 0; - const char *n = s; - - n = strchr(s, '\n'); - while (n != NULL) - { - i++; - n = strchr(n+1, '\n'); - } - return i; -} - -static NEOERR* hdf_read_file_fp (HDF *hdf, FILE *fp, const char *path, - int *line) -{ - NEOERR *err; - STRING str; - HDF *lower; - HDF_ATTR *attr = NULL; - char *s; - char *name, *value; - int l; - - string_init(&str); - err = string_readline(&str, fp); - if (err) - { - string_clear(&str); - return nerr_pass(err); - } - while (str.len != 0) - { - attr = NULL; - (*line)++; - s = str.buf; - SKIPWS(s); - if (!strncmp(s, "#include ", 9)) - { s += 9; name = neos_strip(s); l = strlen(name); @@ -1760,8 +1579,8 @@ err = hdf_read_file(hdf, name); if (err != STATUS_OK) { - string_clear(&str); - return nerr_pass_ctx(err, "In file %s:%d", path, *line); + return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); + } } } else if (s[0] == '#') @@ -1774,12 +1593,10 @@ if (strcmp(s, "}")) { err = nerr_raise(NERR_PARSE, - "[%s:%d] Trailing garbage on line following }: %s", path, *line, - str.buf); - string_clear(&str); + "[%s:%d] Trailing garbage on line following }: %s", path, *lineno, + line->buf); return err; } - string_clear(&str); return STATUS_OK; } else if (s[0]) @@ -1797,8 +1614,7 @@ err = parse_attr(&s, &attr); if (err) { - string_clear(&str); - return nerr_pass_ctx(err, "In file %s:%d", path, *line); + return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); } SKIPWS(s); } @@ -1811,8 +1627,7 @@ err = _set_value (hdf, name, value, 1, 1, 0, attr, NULL); if (err != STATUS_OK) { - string_clear(&str); - return nerr_pass_ctx(err, "In file %s:%d", path, *line); + return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); } } else if (s[0] == ':' && s[1] == '=') /* copy */ @@ -1825,8 +1640,7 @@ err = _set_value (hdf, name, value, 1, 1, 0, attr, NULL); if (err != STATUS_OK) { - string_clear(&str); - return nerr_pass_ctx(err, "In file %s:%d", path, *line); + return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); } } else if (s[0] == ':') /* link */ @@ -1838,8 +1652,7 @@ err = _set_value (hdf, name, value, 1, 1, 1, attr, NULL); if (err != STATUS_OK) { - string_clear(&str); - return nerr_pass_ctx(err, "In file %s:%d", path, *line); + return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); } } else if (s[0] == '{') /* deeper */ @@ -1857,16 +1670,13 @@ } if (err != STATUS_OK) { - string_clear(&str); - return nerr_pass_ctx(err, "In file %s:%d", path, *line); + return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); } - err = hdf_read_file_fp(lower, fp, path, line); + err = _hdf_read_string (lower, str, line, path, lineno, include_handle); if (err != STATUS_OK) { - string_clear(&str); - return nerr_pass_ctx(err, "In file %s:%d", path, *line); + return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); } - if (feof(fp)) break; } else if (s[0] == '<' && s[1] == '<') /* multi-line assignment */ { @@ -1883,22 +1693,21 @@ if (l == 0) { err = nerr_raise(NERR_PARSE, - "[%s:%d] No multi-assignment terminator given: %s", path, *line, - str.buf); - string_clear(&str); + "[%s:%d] No multi-assignment terminator given: %s", path, *lineno, + line->buf); return err; } m = (char *) malloc (mmax * sizeof(char)); if (m == NULL) { - string_clear(&str); return nerr_raise(NERR_NOMEM, "[%s:%d] Unable to allocate memory for multi-line assignment to %s", - path, *line, name); + path, *lineno, name); } - while (fgets(m+msize, mmax-msize, fp) != NULL) + while (_copy_line (str, m+msize, mmax-msize) != 0) { - if (!strncmp(value, m+msize, l) && (m[msize+l] == '\r' || m[msize+l] == '\n')) + (*lineno)++; + if (!strncmp(value, m+msize, l) && isspace(m[msize+l])) { m[msize] = '\0'; break; @@ -1910,10 +1719,9 @@ m = (char *) realloc (m, mmax * sizeof(char)); if (m == NULL) { - string_clear(&str); return nerr_raise(NERR_NOMEM, "[%s:%d] Unable to allocate memory for multi-line assignment to %s: size=%d", - path, *line, name, mmax); + path, *lineno, name, mmax); } } } @@ -1921,33 +1729,44 @@ if (err != STATUS_OK) { free (m); - string_clear(&str); - return nerr_pass_ctx(err, "In file %s:%d", path, *line); + return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); } - /* count the newlines so we know what line we're on... +1 for EOM */ - (*line) = (*line) + count_newlines(m) + 1; + } else { - err = nerr_raise(NERR_PARSE, "[%s:%d] Unable to parse line %s", - path, *line, str.buf); - string_clear(&str); + err = nerr_raise(NERR_PARSE, "[%s:%d] Unable to parse line %s", path, + *lineno, line->buf); return err; } } - str.len = 0; - err = string_readline(&str, fp); - /* ne_warn("string buf len is %d", str.len); */ - if (err) - { - string_clear(&str); - return nerr_pass(err); - } } - string_clear(&str); return STATUS_OK; } +NEOERR * hdf_read_string (HDF *hdf, const char *str) +{ + NEOERR *err; + int lineno = 0; + STRING line; + string_init(&line); + err = _hdf_read_string(hdf, &str, &line, "", &lineno, INCLUDE_ERROR); + string_clear(&line); + return nerr_pass(err); +} + +NEOERR * hdf_read_string_ignore (HDF *hdf, const char *str, int ignore) +{ + NEOERR *err; + int lineno = 0; + STRING line; + string_init(&line); + err = _hdf_read_string(hdf, &str, &line, "", &lineno, + (ignore ? INCLUDE_IGNORE : INCLUDE_ERROR)); + string_clear(&line); + return nerr_pass(err); +} + /* The search path is part of the HDF by convention */ NEOERR* hdf_search_path (HDF *hdf, const char *path, char *full) { @@ -1985,12 +1804,24 @@ NEOERR* hdf_read_file (HDF *hdf, const char *path) { NEOERR *err; - FILE *fp; - int line = 0; + int lineno = 0; char fpath[_POSIX_PATH_MAX]; + char *ibuf = NULL; + const char *ptr = NULL; + HDF *top = hdf->top; + STRING line; + + string_init(&line); if (path == NULL) return nerr_raise(NERR_ASSERT, "Can't read NULL file"); + + if (top->fileload) + { + err = top->fileload(top->fileload_ctx, hdf, path, &ibuf); + } + else + { if (path[0] != '/') { err = hdf_search_path (hdf, path, fpath); @@ -1998,21 +1829,22 @@ path = fpath; } - fp = fopen(path, "r"); - if (fp == NULL) - { - if (errno == ENOENT) - { - return nerr_raise(NERR_NOT_FOUND, "File not found: %s", path); - } - else - { - return nerr_raise_errno(NERR_IO, "Unable to open file %s", path); - } + err = ne_load_file (path, &ibuf); } + if (err) return nerr_pass(err); - err = hdf_read_file_fp(hdf, fp, path, &line); - fclose(fp); + ptr = ibuf; + err = _hdf_read_string(hdf, &ptr, &line, path, &lineno, INCLUDE_FILE); + free(ibuf); + string_clear(&line); return nerr_pass(err); } +void hdf_register_fileload(HDF *hdf, void *ctx, HDFFILELOAD fileload) +{ + if (hdf == NULL) return; + if (hdf->top != NULL) hdf = hdf->top; + hdf->fileload_ctx = ctx; + hdf->fileload = fileload; +} + diff -Nrub clearsilver-0.10.3/util/neo_hdf.h clearsilver-0.10.4/util/neo_hdf.h --- clearsilver-0.10.3/util/neo_hdf.h 2005-06-30 17:34:16.000000000 -0700 +++ clearsilver-0.10.4/util/neo_hdf.h 2006-04-06 15:05:43.000000000 -0700 @@ -20,6 +20,23 @@ #define FORCE_HASH_AT 10 +typedef struct _hdf HDF; + +/* HDFFILELOAD is a callback function to intercept file load requests and + * provide templates via another mechanism. This way you can load templates + * that you compiled-into your binary, from in-memory caches, or from a + * zip file, etc. The HDF is provided so you can choose to use the + * hdf_search_path function to find the file. contents should return + * a full malloc copy of the contents of the file, which the parser will + * own and free. Use hdf_register_fileload to set this function for + * your top level HDF node. + * NOTE: Technically, we shouldn't need a separate copy for each parse, but + * using the separate copy makes this equivalent to the CSFILELOAD function. We + * can change this if we really want to save that copy at the expense of + * slightly more complicated code. */ +typedef NEOERR* (*HDFFILELOAD)(void *ctx, HDF *hdf, const char *filename, + char **contents); + typedef struct _attr { char *key; @@ -27,7 +44,7 @@ struct _attr *next; } HDF_ATTR; -typedef struct _hdf +struct _hdf { int link; int alloc_value; @@ -48,7 +65,12 @@ NE_HASH *hash; /* When using the HASH, we need to know where to append new children */ struct _hdf *last_child; -} HDF; + + /* Should only be set on the head node, used to override the default file + * load method */ + void *fileload_ctx; + HDFFILELOAD fileload; +}; /* * Function: hdf_init - Initialize an HDF data set @@ -539,6 +561,25 @@ */ NEOERR* hdf_search_path (HDF *hdf, const char *path, char *full); +/* + * Function: hdf_register_fileload - register a fileload function + * Description: hdf_register_fileload registers a fileload function that + * overrides the built-in function. The built-in function + * uses hdf_search_path and ne_file_load (based on stat/open/read) + * to find and load the file on every hdf_read_file (including + * #include). You can override this function if you wish to provide + * other file search functions, or load the hdf file + * from an in-memory cache, etc. + * Input: hdf - pointer to a head HDF node + * ctx - pointer that is passed to the HDFFILELOAD function when called + * fileload - a HDFFILELOAD function + * Output: None + * Return: None + * + */ + +void hdf_register_fileload(HDF *hdf, void *ctx, HDFFILELOAD fileload); + __END_DECLS #endif /* __NEO_HDF_H_ */ diff -Nrub clearsilver-0.10.3/util/neo_str.c clearsilver-0.10.4/util/neo_str.c --- clearsilver-0.10.3/util/neo_str.c 2005-06-30 17:42:50.000000000 -0700 +++ clearsilver-0.10.4/util/neo_str.c 2006-08-07 13:01:53.000000000 -0700 @@ -591,3 +591,239 @@ rs[i] = '\0'; return rs; } + +// List of all characters that must be escaped +// List based on http://www.blooberry.com/indexdot/html/topics/urlencoding.htm +static char EscapedChars[] = "$&+,/:;=?@ \"<>#%{}|\\^~[]`'"; + +// Check if a single character needs to be escaped +static BOOL is_reserved_char(char c) +{ + int i = 0; + + if (c < 32 || c > 122) { + return TRUE; + } else { + while (EscapedChars[i]) { + if (c == EscapedChars[i]) { + return TRUE; + } + ++i; + } + } + return FALSE; +} + +NEOERR *neos_js_escape (const char *in, char **esc) +{ + int nl = 0; + int l = 0; + unsigned char *buf = (unsigned char *)in; + unsigned char *s; + + while (buf[l]) + { + if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' || + buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' || + buf[l] == '&' || buf[l] == ';' || buf[l] < 32) + { + nl += 3; + } + nl++; + l++; + } + + s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1)); + if (s == NULL) + return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s", + buf); + + nl = 0; l = 0; + while (buf[l]) + { + if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' || + buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' || + buf[l] == '&' || buf[l] == ';' || buf[l] < 32) + { + s[nl++] = '\\'; + s[nl++] = 'x'; + s[nl++] = "0123456789ABCDEF"[(buf[l] >> 4) & 0xF]; + s[nl++] = "0123456789ABCDEF"[buf[l] & 0xF]; + l++; + } + else + { + s[nl++] = buf[l++]; + } + } + s[nl] = '\0'; + + *esc = (char *)s; + return STATUS_OK; +} + + +NEOERR *neos_url_escape (const char *in, char **esc, + const char *other) +{ + int nl = 0; + int l = 0; + int x = 0; + unsigned char *buf = (unsigned char *)in; + unsigned char *uother = (unsigned char *)other; + unsigned char *s; + int match = 0; + + while (buf[l]) + { + if (is_reserved_char(buf[l])) + { + nl += 2; + } + else if (uother) + { + x = 0; + while (uother[x]) + { + if (uother[x] == buf[l]) + { + nl +=2; + break; + } + x++; + } + } + nl++; + l++; + } + + s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1)); + if (s == NULL) + return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s", + buf); + + nl = 0; l = 0; + while (buf[l]) + { + match = 0; + if (buf[l] == ' ') + { + s[nl++] = '+'; + l++; + } + else + { + if (is_reserved_char(buf[l])) + { + match = 1; + } + else if (uother) + { + x = 0; + while (uother[x]) + { + if (uother[x] == buf[l]) + { + match = 1; + break; + } + x++; + } + } + if (match) + { + s[nl++] = '%'; + s[nl++] = "0123456789ABCDEF"[buf[l] / 16]; + s[nl++] = "0123456789ABCDEF"[buf[l] % 16]; + l++; + } + else + { + s[nl++] = buf[l++]; + } + } + } + s[nl] = '\0'; + + *esc = (char *)s; + return STATUS_OK; +} + +NEOERR *neos_html_escape (const char *src, int slen, + char **out) +{ + NEOERR *err = STATUS_OK; + STRING out_s; + int x; + char *ptr; + + string_init(&out_s); + err = string_append (&out_s, ""); + if (err) return nerr_pass (err); + *out = NULL; + + x = 0; + while (x < slen) + { + ptr = strpbrk(src + x, "&<>\"'\r"); + if (ptr == NULL || (ptr-src >= slen)) + { + err = string_appendn (&out_s, src + x, slen-x); + x = slen; + } + else + { + err = string_appendn (&out_s, src + x, (ptr - src) - x); + if (err != STATUS_OK) break; + x = ptr - src; + if (src[x] == '&') + err = string_append (&out_s, "&"); + else if (src[x] == '<') + err = string_append (&out_s, "<"); + else if (src[x] == '>') + err = string_append (&out_s, ">"); + else if (src[x] == '"') + err = string_append (&out_s, """); + else if (src[x] == '\'') + err = string_append (&out_s, "'"); + else if (src[x] != '\r') + err = nerr_raise (NERR_ASSERT, "src[x] == '%c'", src[x]); + x++; + } + if (err != STATUS_OK) break; + } + if (err) + { + string_clear (&out_s); + return nerr_pass (err); + } + *out = out_s.buf; + return STATUS_OK; +} + +NEOERR *neos_var_escape (NEOS_ESCAPE context, + const char *in, + char **esc) +{ + + /* Just dup and return if we do nothing. */ + if (context == NEOS_ESCAPE_NONE || + context == NEOS_ESCAPE_FUNCTION) + { + *esc = strdup(in); + return STATUS_OK; + } + + /* Now we escape based on context. This is the order of precedence: + * url > script > style > html + */ + if (context & NEOS_ESCAPE_URL) + return nerr_pass(neos_url_escape(in, esc, NULL)); + else if (context & NEOS_ESCAPE_SCRIPT) + return nerr_pass(neos_js_escape(in, esc)); + else if (context & NEOS_ESCAPE_HTML) + return nerr_pass(neos_html_escape(in, strlen(in), esc)); + + return nerr_raise(NERR_ASSERT, "unknown escape context supplied: %d", + context); +} diff -Nrub clearsilver-0.10.3/util/neo_str.h clearsilver-0.10.4/util/neo_str.h --- clearsilver-0.10.3/util/neo_str.h 2005-11-30 19:25:57.000000000 -0800 +++ clearsilver-0.10.4/util/neo_str.h 2006-08-07 13:01:53.000000000 -0700 @@ -51,7 +51,6 @@ int max; } STRING_ARRAY; - /* At some point, we should add the concept of "max len" to these so we * can't get DoS'd by someone sending us a line without an end point, * etc. */ @@ -74,6 +73,17 @@ BOOL reg_search (const char *re, const char *str); +/* NEOS_ESCAPE details the support escape contexts/modes handled + * by various NEOS helper methods and reused in CS itself. */ +typedef enum +{ + NEOS_ESCAPE_UNDEF = 0, /* Used to force eval-time checking */ + NEOS_ESCAPE_NONE = 1<<0, + NEOS_ESCAPE_HTML = 1<<1, + NEOS_ESCAPE_SCRIPT = 1<<2, + NEOS_ESCAPE_URL = 1<<3, + NEOS_ESCAPE_FUNCTION = 1<<4 /* Special case used to override the others */ +} NEOS_ESCAPE; NEOERR* neos_escape(UINT8 *buf, int buflen, char esc_char, const char *escape, char **esc); @@ -81,6 +91,22 @@ char *repr_string_alloc (const char *s); +/* This is the "super" escape call which will call the proper helper + * variable escape function based on the passed in context. */ +NEOERR *neos_var_escape (NEOS_ESCAPE context, + const char *in, + char **esc); + +/* Generic data escaping helper functions used by neos_contextual_escape + * and cs built-ins. */ +NEOERR *neos_url_escape (const char *in, char **esc, + const char *other); + +NEOERR *neos_js_escape (const char *in, char **esc); + +NEOERR *neos_html_escape (const char *src, int slen, + char **out); + __END_DECLS #endif /* __NEO_STR_H_ */ diff -Nrub clearsilver-0.10.3/util/test/hdf_copy_test.c clearsilver-0.10.4/util/test/hdf_copy_test.c --- clearsilver-0.10.3/util/test/hdf_copy_test.c 2005-06-30 11:52:10.000000000 -0700 +++ clearsilver-0.10.4/util/test/hdf_copy_test.c 2006-08-11 16:49:37.000000000 -0700 @@ -8,12 +8,13 @@ #include "util/neo_hdf.h" int main(void) { - HDF *hdf_1; + HDF *hdf_1, *hdf_2; HDF *cur_node,*last_node; hdf_init(&hdf_1); hdf_read_file(hdf_1,"hdf_copy_test.hdf"); + hdf_dump(hdf_1,NULL); cur_node = hdf_get_obj(hdf_1,"Chart"); last_node = cur_node; @@ -37,5 +38,13 @@ hdf_copy(last_node,"next_stage",hdf_get_obj(hdf_1,"TempHolderPlace")); hdf_dump(hdf_1,NULL); + /* Test copy and destroy, make sure we actually copy everything and + * don't reference anything */ + hdf_init(&hdf_2); + hdf_copy(hdf_2, "", hdf_1); + hdf_destroy(&hdf_1); + hdf_dump(hdf_2, NULL); + hdf_destroy(&hdf_2); + return 0; } diff -Nrub clearsilver-0.10.3/util/test/hdf_copy_test.hdf clearsilver-0.10.4/util/test/hdf_copy_test.hdf --- clearsilver-0.10.3/util/test/hdf_copy_test.hdf 2005-06-30 11:52:10.000000000 -0700 +++ clearsilver-0.10.4/util/test/hdf_copy_test.hdf 2006-08-11 16:00:23.000000000 -0700 @@ -19,21 +19,21 @@ Chart { next_stage { - id = 1 + id [attr=1, batter] = 1 Type = Mux Bucket = Discrete Bucket.FieldId = QUEUE Bucket.Dimension = Y next_stage { - id = 2 + id [attr=2, batter] = 2 Type = Mux Bucket = Time Bucket.FieldId = TIME Bucket.Dimension = Y next_stage { - id = 3 + id [attr=3] = 3 Type = Mux Bucket = Discrete Bucket.FieldId = AGENT diff -Nrub clearsilver-0.10.3/util/test/hdfloadtest.c clearsilver-0.10.4/util/test/hdfloadtest.c --- clearsilver-0.10.3/util/test/hdfloadtest.c 2005-06-30 11:52:11.000000000 -0700 +++ clearsilver-0.10.4/util/test/hdfloadtest.c 2006-04-05 19:34:49.000000000 -0700 @@ -36,8 +36,19 @@ for (x = 0; x < reps; x++) { - /* Half the time we test by loading the file and reading the string */ - if (x % 2 == 0) + err = hdf_read_file(hdf, file); + if (err != STATUS_OK) + { + nerr_log_error(err); + return -1; + } + } + tend = ne_timef(); + ne_warn("hdf_read_file test finished in %5.3fs, %5.3fs/rep", tend - tstart, (tend-tstart) / reps); + + tstart = ne_timef(); + + for (x = 0; x < reps; x++) { err = ne_load_file(file, &s); if (err != STATUS_OK) @@ -53,18 +64,29 @@ } free(s); } - else - { - err = hdf_read_file(hdf, file); + tend = ne_timef(); + ne_warn("load/hdf_read_string test finished in %5.3fs, %5.3fs/rep", tend - tstart, (tend-tstart) / reps); + + + err = ne_load_file(file, &s); if (err != STATUS_OK) { nerr_log_error(err); return -1; } + tstart = ne_timef(); + for (x = 0; x < reps; x++) + { + err = hdf_read_string(hdf, s); + if (err != STATUS_OK) + { + nerr_log_error(err); + return -1; } } tend = ne_timef(); - ne_warn("Load test finished in %5.3fs, %5.3fs/rep", tend - tstart, (tend-tstart) / reps); + free(s); + ne_warn("hdf_read_string test finished in %5.3fs, %5.3fs/rep", tend - tstart, (tend-tstart) / reps); /* hdf_dump(hdf, NULL); */ hdf_destroy(&hdf); diff -Nrub clearsilver-0.10.3/util/test/Makefile clearsilver-0.10.4/util/test/Makefile --- clearsilver-0.10.3/util/test/Makefile 2005-06-30 11:52:10.000000000 -0700 +++ clearsilver-0.10.4/util/test/Makefile 2006-07-12 23:53:21.000000000 -0700 @@ -47,7 +47,7 @@ ULISTTEST_OBJ = $(ULISTTEST_SRC:%.c=%.o) CFLAGS += -I$(NEOTONIC_ROOT)/util -LIBS += -L$(LIB_DIR) -lneo_utl +LIBS += -lneo_utl TARGETS = $(HDFTEST_EXE) $(LISTDIRTEST_EXE) $(HDFCOPYTEST_EXE) \ $(HDFSORTTEST_EXE) $(HDFDEALLOCTEST_EXE) \ @@ -57,34 +57,34 @@ all: $(TARGETS) $(ULISTTEST_EXE): $(ULISTTEST_OBJ) $(NTR_LIB) - $(LD) $@ $(ULISTTEST_OBJ) $(LIBS) + $(LD) $@ $(ULISTTEST_OBJ) $(LDFLAGS) $(LIBS) $(HDFTEST_EXE): $(HDFTEST_OBJ) $(NTR_LIB) - $(LD) $@ $(HDFTEST_OBJ) $(LIBS) + $(LD) $@ $(HDFTEST_OBJ) $(LDFLAGS) $(LIBS) $(HDFSORTTEST_EXE): $(HDFSORTTEST_OBJ) $(NTR_LIB) - $(LD) $@ $(HDFSORTTEST_OBJ) $(LIBS) + $(LD) $@ $(HDFSORTTEST_OBJ) $(LDFLAGS) $(LIBS) $(HDFDEALLOCTEST_EXE): $(HDFDEALLOCTEST_OBJ) $(NTR_LIB) - $(LD) $@ $(HDFDEALLOCTEST_OBJ) $(LIBS) + $(LD) $@ $(HDFDEALLOCTEST_OBJ) $(LDFLAGS) $(LIBS) $(HDFLOADTEST_EXE): $(HDFLOADTEST_OBJ) $(NTR_LIB) - $(LD) $@ $(HDFLOADTEST_OBJ) $(LIBS) # -lefence + $(LD) $@ $(HDFLOADTEST_OBJ) $(LDFLAGS) $(LIBS) # -lefence $(LISTDIRTEST_EXE): $(LISTDIRTEST_OBJ) $(NTR_LIB) - $(LD) $@ $(LISTDIRTEST_OBJ) $(LIBS) + $(LD) $@ $(LISTDIRTEST_OBJ) $(LDFLAGS) $(LIBS) $(HDFCOPYTEST_EXE): $(HDFCOPYTEST_OBJ) $(NTR_LIB) - $(LD) $@ $(HDFCOPYTEST_OBJ) $(LIBS) # -lefence + $(LD) $@ $(HDFCOPYTEST_OBJ) $(LDFLAGS) $(LIBS) # -lefence $(NETTEST_EXE): $(NETTEST_OBJ) $(NTR_LIB) - $(LD) $@ $(NETTEST_OBJ) $(LIBS) + $(LD) $@ $(NETTEST_OBJ) $(LDFLAGS) $(LIBS) $(DATETEST_EXE): $(DATETEST_OBJ) $(NTR_LIB) - $(LD) $@ $(DATETEST_OBJ) $(LIBS) + $(LD) $@ $(DATETEST_OBJ) $(LDFLAGS) $(LIBS) $(HASHTEST_EXE): $(HASHTEST_OBJ) $(NTR_LIB) - $(LD) $@ $(HASHTEST_OBJ) $(LIBS) # -lefence + $(LD) $@ $(HASHTEST_OBJ) $(LDFLAGS) $(LIBS) # -lefence clean: $(RM) *.o