#! /bin/sh /usr/share/dpatch/dpatch-run ## ngx_http_ssi_variables+for.dpatch by Kirill A. Kroinskiy ## ## All lines beginning with `## DP:' are a description of the patch. ## DP: Implement ngx_http_ssi_get_variable, ngx_http_ssi_set_variable, ## DP: $ssi_ variables and for/lastfor/endfor command @DPATCH@ diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c index 61c9592..2772225 100644 --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -73,8 +73,6 @@ static ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx); static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx); -static ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r, - ngx_str_t *name, ngx_uint_t key); static ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t *text, ngx_uint_t flags); @@ -82,7 +80,7 @@ static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); static ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data, ngx_int_t rc); -static ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data, +static ngx_int_t ngx_http_ssi_set_included_variable(ngx_http_request_t *r, void *data, ngx_int_t rc); static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); @@ -90,6 +88,12 @@ static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); +static ngx_int_t ngx_http_ssi_for(ngx_http_request_t *r, + ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); +static ngx_int_t ngx_http_ssi_lastfor(ngx_http_request_t *r, + ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); +static ngx_int_t ngx_http_ssi_endfor(ngx_http_request_t *r, + ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r, @@ -224,6 +228,10 @@ static ngx_str_t ngx_http_ssi_null_string = ngx_null_string; #define NGX_HTTP_SSI_BLOCK_NAME 0 +#define NGX_HTTP_SSI_FOR_VAR 0 +#define NGX_HTTP_SSI_FOR_SEP 1 +#define NGX_HTTP_SSI_FOR_DATA 2 + static ngx_http_ssi_param_t ngx_http_ssi_include_params[] = { { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 }, @@ -269,6 +277,14 @@ static ngx_http_ssi_param_t ngx_http_ssi_block_params[] = { }; +static ngx_http_ssi_param_t ngx_http_ssi_for_params[] = { + { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 0, 0 }, + { ngx_string("sep"), NGX_HTTP_SSI_FOR_SEP, 0, 0 }, + { ngx_string("data"), NGX_HTTP_SSI_FOR_DATA, 1, 0 }, + { ngx_null_string, 0, 0, 0 } +}; + + static ngx_http_ssi_param_t ngx_http_ssi_no_params[] = { { ngx_null_string, 0, 0, 0 } }; @@ -296,6 +312,10 @@ static ngx_http_ssi_command_t ngx_http_ssi_commands[] = { { ngx_string("endblock"), ngx_http_ssi_endblock, ngx_http_ssi_no_params, 0, 1, 0 }, + { ngx_string("for"), ngx_http_ssi_for, ngx_http_ssi_for_params, 0, 0, 0 }, + { ngx_string("lastfor"), ngx_http_ssi_lastfor, ngx_http_ssi_no_params, 1, 0, 0 }, + { ngx_string("endfor"), ngx_http_ssi_endfor, ngx_http_ssi_no_params, 1, 0, 0 }, + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -1520,22 +1540,29 @@ ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx) } -static ngx_str_t * -ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name, - ngx_uint_t key) +ngx_str_t * +ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t *key) { ngx_uint_t i; ngx_list_part_t *part; ngx_http_ssi_var_t *var; - ngx_http_ssi_ctx_t *ctx; + ngx_http_ssi_ctx_t *mctx; + + if (*key == 0) { + *key = ngx_hash_strlow(name->data, name->data, name->len); + } + + mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module); - ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module); + if (mctx == NULL) { + return NULL; + } - if (ctx->variables == NULL) { + if (mctx->variables == NULL) { return NULL; } - part = &ctx->variables->part; + part = &mctx->variables->part; var = part->elts; for (i = 0; /* void */ ; i++) { @@ -1554,7 +1581,7 @@ ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name, continue; } - if (key != var[i].key) { + if (*key != var[i].key) { continue; } @@ -1566,6 +1593,50 @@ ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name, return NULL; } +ngx_str_t * +ngx_http_ssi_set_variable(ngx_http_request_t *r, ngx_str_t *name, + ngx_uint_t *key, ngx_str_t *value) +{ + ngx_str_t *vv; + ngx_http_ssi_var_t *var; + ngx_http_ssi_ctx_t *mctx; + + mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module); + + if (mctx == NULL) { + return NULL; + } + + if (mctx->variables == NULL) { + mctx->variables = ngx_list_create(r->main->pool, 4, + sizeof(ngx_http_ssi_var_t)); + if (mctx->variables == NULL) { + return NULL; + } + } + + vv = ngx_http_ssi_get_variable(r, name, key); + if (vv) { + *vv = *value; + return vv; + } + + if (*key == 0) { + *key = ngx_hash_strlow(name->data, name->data, name->len); + } + + var = ngx_list_push(mctx->variables); + if (var == NULL) { + return NULL; + } + + var->name = *name; + var->key = *key; + var->value = *value; + + return &var->value; +} + static ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, @@ -1574,8 +1645,7 @@ ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, u_char ch, *p, **value, *data, *part_data; size_t *size, len, prefix, part_len; ngx_str_t var, *val; - ngx_int_t key; - ngx_uint_t i, n, bracket, quoted; + ngx_uint_t i, n, bracket, quoted, key = 0; ngx_array_t lengths, values; ngx_http_variable_value_t *vv; @@ -1701,11 +1771,10 @@ ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, goto invalid_variable; } - key = ngx_hash_strlow(var.data, var.data, var.len); - - val = ngx_http_ssi_get_variable(r, &var, key); + val = ngx_http_ssi_get_variable(r, &var, &key); if (val == NULL) { + vv = ngx_http_get_variable(r, &var, key, flags & NGX_HTTP_SSI_EXPR_TEST); if (vv == NULL) { @@ -1826,13 +1895,12 @@ ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, { u_char *dst, *src; size_t len; - ngx_int_t rc, key; + ngx_int_t rc; ngx_str_t *uri, *file, *wait, *set, *stub, args; ngx_buf_t *b; - ngx_uint_t flags, i; + ngx_uint_t flags, i, key; ngx_chain_t *cl, *tl, **ll, *out; ngx_http_request_t *sr; - ngx_http_ssi_var_t *var; ngx_http_ssi_ctx_t *mctx; ngx_http_ssi_block_t *bl; ngx_http_post_subrequest_t *psr; @@ -1994,35 +2062,17 @@ ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, } if (set) { - key = ngx_hash_strlow(set->data, set->data, set->len); - psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); if (psr == NULL) { return NGX_ERROR; } - psr->handler = ngx_http_ssi_set_variable; - psr->data = ngx_http_ssi_get_variable(r, set, key); + psr->handler = ngx_http_ssi_set_included_variable; + psr->data = ngx_http_ssi_get_variable(r, set, &key); if (psr->data == NULL) { - - if (mctx->variables == NULL) { - mctx->variables = ngx_list_create(r->pool, 4, - sizeof(ngx_http_ssi_var_t)); - if (mctx->variables == NULL) { - return NGX_ERROR; - } - } - - var = ngx_list_push(mctx->variables); - if (var == NULL) { - return NGX_ERROR; - } - - var->name = *set; - var->key = key; - var->value = ngx_http_ssi_null_string; - psr->data = &var->value; + psr->data = ngx_http_ssi_set_variable(r, set, &key, + &ngx_http_ssi_null_string); } flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED; @@ -2079,7 +2129,7 @@ ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data, ngx_int_t rc) static ngx_int_t -ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data, ngx_int_t rc) +ngx_http_ssi_set_included_variable(ngx_http_request_t *r, void *data, ngx_int_t rc) { ngx_str_t *value = data; @@ -2098,9 +2148,9 @@ ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, { u_char *p; uintptr_t len; - ngx_int_t key; ngx_buf_t *b; ngx_str_t *var, *value, *enc, text; + ngx_uint_t key = 0; ngx_chain_t *cl; ngx_http_variable_value_t *vv; @@ -2109,9 +2159,7 @@ ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ssi echo \"%V\"", var); - key = ngx_hash_strlow(var->data, var->data, var->len); - - value = ngx_http_ssi_get_variable(r, var, key); + value = ngx_http_ssi_get_variable(r, var, &key); if (value == NULL) { vv = ngx_http_get_variable(r, var, key, 1); @@ -2261,20 +2309,9 @@ static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params) { - ngx_int_t key, rc; - ngx_str_t *name, *value, *vv; - ngx_http_ssi_var_t *var; - ngx_http_ssi_ctx_t *mctx; - - mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module); - - if (mctx->variables == NULL) { - mctx->variables = ngx_list_create(r->pool, 4, - sizeof(ngx_http_ssi_var_t)); - if (mctx->variables == NULL) { - return NGX_ERROR; - } - } + ngx_int_t rc; + ngx_str_t *name, *value; + ngx_uint_t key; name = params[NGX_HTTP_SSI_SET_VAR]; value = params[NGX_HTTP_SSI_SET_VALUE]; @@ -2288,26 +2325,204 @@ ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, return rc; } - key = ngx_hash_strlow(name->data, name->data, name->len); + if (ngx_http_ssi_set_variable(r, name, &key, value) == NULL) { + return NGX_ERROR; + } - vv = ngx_http_ssi_get_variable(r, name, key); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "set: \"%V\"=\"%V\"", name, value); - if (vv) { - *vv = *value; - return NGX_OK; + return NGX_OK; +} + + +static ngx_int_t ngx_http_ssi_for(ngx_http_request_t *r, + ngx_http_ssi_ctx_t *ctx, ngx_str_t **params) +{ + u_char *p; + u_char *last; + ngx_str_t val; + ngx_str_t *var; + ngx_str_t *sep; + ngx_str_t *data; + ngx_str_t *value; + ngx_str_t var_default = ngx_string("_"); + ngx_str_t sep_default = ngx_string(" "); + ngx_uint_t key = 0; + ngx_http_variable_value_t *vv; + + var = params[NGX_HTTP_SSI_FOR_VAR]; + sep = params[NGX_HTTP_SSI_FOR_SEP]; + data = params[NGX_HTTP_SSI_FOR_DATA]; + + if (data == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no \"data\" parameter in \"for\" SSI command"); + return NGX_HTTP_SSI_ERROR; } - var = ngx_list_push(mctx->variables); - if (var == NULL) { + if (!sep) { + ctx->for_sep.len = sep_default.len; + ctx->for_sep.data = ngx_palloc(r->pool, sep_default.len); + if (ctx->for_sep.data == NULL) { + return NGX_ERROR; + } + ngx_memcpy(ctx->for_sep.data, sep_default.data, sep_default.len); + + sep = &ctx->for_sep; + } else { + ctx->for_sep = *sep; + } + + + if (!var) { + ctx->for_var.len = var_default.len; + ctx->for_var.data = ngx_palloc(r->pool, var_default.len); + if (ctx->for_var.data == NULL) { + return NGX_ERROR; + } + ngx_memcpy(ctx->for_var.data, var_default.data, var_default.len); + + var = &ctx->for_var; + } else { + ctx->for_var = *var; + } + + value = ngx_http_ssi_get_variable(r, data, &key); + + if (value == NULL) { + vv = ngx_http_get_variable(r, data, key, 1); + + if (vv == NULL) { + return NGX_HTTP_SSI_ERROR; + } + + if (!vv->not_found) { + ctx->for_data.data = vv->data; + ctx->for_data.len = vv->len; + value = &ctx->for_data; + } + } else { + ctx->for_data = *value; + } + + + if (value == NULL) { return NGX_ERROR; } - var->name = *name; - var->key = key; - var->value = *value; + /* save a parser status */ + ctx->for_buf = ctx->buf; + ctx->for_pos = ctx->pos; + ctx->for_copy_start = ctx->copy_start; + ctx->for_copy_end = ctx->copy_end; + ctx->for_state = ctx->state; + ctx->for_looked = ctx->looked; + ctx->have_last_for = 0; + + ctx->output = 1; + ctx->conditional = 1; + + for (p = ctx->for_data.data, last = p + ctx->for_data.len - ctx->for_sep.len; + p <= last; p++) { + if (ngx_strncmp(p, ctx->for_sep.data, ctx->for_sep.len) == 0) { + val.data = ctx->for_data.data; + val.len = p - val.data; + + ctx->for_data.data = val.data + val.len + ctx->for_sep.len; + ctx->for_data.len = ctx->for_data.len - val.len - ctx->for_sep.len; + + key = 0; + if (ngx_http_ssi_set_variable(r, &ctx->for_var, &key, &val) == NULL) { + return NGX_ERROR; + } + + return NGX_OK; + } + } + + if (ngx_http_ssi_set_variable(r, &ctx->for_var, &key, &ctx->for_data) == NULL) { + return NGX_ERROR; + } + + ctx->for_data.len = 0; + + return NGX_OK; +} + +static ngx_int_t +ngx_http_ssi_lastfor(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, + ngx_str_t **params) +{ + ctx->have_last_for = 1; + + if (ctx->for_data.len == 0) { + ctx->output = 1; + } else { + ctx->output = 0; + } + + return NGX_OK; +} + +static ngx_int_t +ngx_http_ssi_endfor(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, + ngx_str_t **params) +{ + u_char *p; + u_char *last; + ngx_str_t val; + ngx_uint_t key = 0; + + if (ctx->for_data.len == 0) { + ctx->output = 1; + ctx->conditional = 0; + return NGX_OK; + } + + for (p = ctx->for_data.data, last = p + ctx->for_data.len - ctx->for_sep.len; + p < last; p++) { + if (ngx_strncmp(p, ctx->for_sep.data, ctx->for_sep.len) == 0) { + val.data = ctx->for_data.data; + val.len = p - val.data; + + ctx->for_data.data = val.data + val.len + ctx->for_sep.len; + ctx->for_data.len = ctx->for_data.len - val.len - ctx->for_sep.len; + + key = 0; + if (ngx_http_ssi_set_variable(r, &ctx->for_var, &key, &val) == NULL) { + return NGX_ERROR; + } + + break; + } + } + + if (p == last) { + val = ctx->for_data; + if (ngx_strncmp(p, ctx->for_sep.data, ctx->for_sep.len) == 0) { + val.len -= ctx->for_sep.len; + } + if (ngx_http_ssi_set_variable(r, &ctx->for_var, &key, &val) == NULL) { + return NGX_ERROR; + } + + ctx->for_data.len = 0; + if (ctx->have_last_for) { + ctx->output = 0; + } + } else { + ctx->output = 1; + } + + /* revert back saved parser status */ + ctx->buf = ctx->for_buf; + ctx->pos = ctx->for_pos; + ctx->copy_start = ctx->for_copy_start; + ctx->copy_end = ctx->for_copy_end; + ctx->state = ctx->for_state; + ctx->looked = ctx->for_looked; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "set: \"%V\"=\"%V\"", name, value); return NGX_OK; } @@ -2799,3 +3014,10 @@ ngx_http_ssi_filter_init(ngx_conf_t *cf) return NGX_OK; } + +/* Local Variables: */ +/* mode: c */ +/* c-basic-offset: 4 */ +/* c-file-offsets: ((arglist-cont-nonempty . 4)) */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/http/modules/ngx_http_ssi_filter_module.h b/src/http/modules/ngx_http_ssi_filter_module.h index 6ab1884..e73d53f 100644 --- a/src/http/modules/ngx_http_ssi_filter_module.h +++ b/src/http/modules/ngx_http_ssi_filter_module.h @@ -64,6 +64,19 @@ typedef struct { ngx_list_t *variables; ngx_array_t *blocks; + /* for hacks */ + ngx_buf_t *for_buf; + u_char *for_pos; + u_char *for_copy_start; + u_char *for_copy_end; + ngx_uint_t for_state; + size_t for_looked; + ngx_flag_t have_last_for; + + ngx_str_t for_data; + ngx_str_t for_sep; + ngx_str_t for_var; + unsigned conditional:2; unsigned encoding:2; unsigned block:1; @@ -103,5 +116,16 @@ typedef struct { extern ngx_module_t ngx_http_ssi_filter_module; +ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name, + ngx_uint_t *key); +ngx_str_t *ngx_http_ssi_set_variable(ngx_http_request_t *r, ngx_str_t *name, + ngx_uint_t *key, ngx_str_t *value); #endif /* _NGX_HTTP_SSI_FILTER_H_INCLUDED_ */ + +/* Local Variables: */ +/* mode: c */ +/* c-basic-offset: 4 */ +/* c-file-offsets: ((arglist-cont-nonempty . 4)) */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c index 998c4ce..daf1292 100644 --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -27,6 +27,10 @@ static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +#if (NGX_HTTP_SSI) +static ngx_int_t ngx_http_variable_ssi(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +#endif static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -499,6 +503,17 @@ ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key, return NULL; } +#if (NGX_HTTP_SSI) + if (ngx_strncmp(name->data, "ssi_", 7) == 0) { + + if (ngx_http_variable_ssi(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } +#endif + if (ngx_strncmp(name->data, "arg_", 4) == 0) { if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) { @@ -508,6 +523,8 @@ ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key, return NULL; } + + vv->not_found = 1; if (nowarn == 0) { @@ -766,6 +783,37 @@ ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v, } +#if (NGX_HTTP_SSI) +static ngx_int_t +ngx_http_variable_ssi(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + ngx_str_t *vv; + ngx_str_t s; + ngx_uint_t key; + + s.len = name->len - (sizeof("ssi_") - 1); + s.data = name->data + sizeof("ssi_") - 1; + + vv = ngx_http_ssi_get_variable(r, &s, &key); + + if (vv == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->len = vv->len; + v->valid = 1; + v->no_cacheable = 1; + v->not_found = 0; + v->data = vv->data; + + return NGX_OK; +} +#endif + + static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) @@ -1590,6 +1638,15 @@ ngx_http_variables_init_vars(ngx_conf_t *cf) continue; } +#if (NGX_HTTP_SSI) + if (ngx_strncmp(v[i].name.data, "ssi_", 7) == 0) { + v[i].get_handler = ngx_http_variable_ssi; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } +#endif + if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) { v[i].get_handler = ngx_http_variable_argument; v[i].data = (uintptr_t) &v[i].name;