src/http/modules/ngx_http_ssi_filter_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>

  8. #define NGX_HTTP_SSI_ERROR          1

  9. #define NGX_HTTP_SSI_DATE_LEN       2048

  10. #define NGX_HTTP_SSI_ADD_PREFIX     1
  11. #define NGX_HTTP_SSI_ADD_ZERO       2


  12. typedef struct {
  13.     ngx_flag_t    enable;
  14.     ngx_flag_t    silent_errors;
  15.     ngx_flag_t    ignore_recycled_buffers;
  16.     ngx_flag_t    last_modified;

  17.     ngx_hash_t    types;

  18.     size_t        min_file_chunk;
  19.     size_t        value_len;

  20.     ngx_array_t  *types_keys;
  21. } ngx_http_ssi_loc_conf_t;


  22. typedef struct {
  23.     ngx_str_t     name;
  24.     ngx_uint_t    key;
  25.     ngx_str_t     value;
  26. } ngx_http_ssi_var_t;


  27. typedef struct {
  28.     ngx_str_t     name;
  29.     ngx_chain_t  *bufs;
  30.     ngx_uint_t    count;
  31. } ngx_http_ssi_block_t;


  32. typedef enum {
  33.     ssi_start_state = 0,
  34.     ssi_tag_state,
  35.     ssi_comment0_state,
  36.     ssi_comment1_state,
  37.     ssi_sharp_state,
  38.     ssi_precommand_state,
  39.     ssi_command_state,
  40.     ssi_preparam_state,
  41.     ssi_param_state,
  42.     ssi_preequal_state,
  43.     ssi_prevalue_state,
  44.     ssi_double_quoted_value_state,
  45.     ssi_quoted_value_state,
  46.     ssi_quoted_symbol_state,
  47.     ssi_postparam_state,
  48.     ssi_comment_end0_state,
  49.     ssi_comment_end1_state,
  50.     ssi_error_state,
  51.     ssi_error_end0_state,
  52.     ssi_error_end1_state
  53. } ngx_http_ssi_state_e;


  54. static ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r,
  55.     ngx_http_ssi_ctx_t *ctx);
  56. static void ngx_http_ssi_buffered(ngx_http_request_t *r,
  57.     ngx_http_ssi_ctx_t *ctx);
  58. static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,
  59.     ngx_http_ssi_ctx_t *ctx);
  60. static ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r,
  61.     ngx_str_t *name, ngx_uint_t key);
  62. static ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r,
  63.     ngx_http_ssi_ctx_t *ctx, ngx_str_t *text, ngx_uint_t flags);
  64. static ngx_int_t ngx_http_ssi_regex_match(ngx_http_request_t *r,
  65.     ngx_str_t *pattern, ngx_str_t *str);

  66. static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
  67.     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
  68. static ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data,
  69.     ngx_int_t rc);
  70. static ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data,
  71.     ngx_int_t rc);
  72. static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,
  73.     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
  74. static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,
  75.     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
  76. static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r,
  77.     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
  78. static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,
  79.     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
  80. static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r,
  81.     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
  82. static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,
  83.     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
  84. static ngx_int_t ngx_http_ssi_block(ngx_http_request_t *r,
  85.     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
  86. static ngx_int_t ngx_http_ssi_endblock(ngx_http_request_t *r,
  87.     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);

  88. static ngx_int_t ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
  89.     ngx_http_variable_value_t *v, uintptr_t gmt);

  90. static ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf);
  91. static void *ngx_http_ssi_create_main_conf(ngx_conf_t *cf);
  92. static char *ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf);
  93. static void *ngx_http_ssi_create_loc_conf(ngx_conf_t *cf);
  94. static char *ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf,
  95.     void *parent, void *child);
  96. static ngx_int_t ngx_http_ssi_filter_init(ngx_conf_t *cf);


  97. static ngx_command_t  ngx_http_ssi_filter_commands[] = {

  98.     { ngx_string("ssi"),
  99.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
  100.                         |NGX_CONF_FLAG,
  101.       ngx_conf_set_flag_slot,
  102.       NGX_HTTP_LOC_CONF_OFFSET,
  103.       offsetof(ngx_http_ssi_loc_conf_t, enable),
  104.       NULL },

  105.     { ngx_string("ssi_silent_errors"),
  106.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  107.       ngx_conf_set_flag_slot,
  108.       NGX_HTTP_LOC_CONF_OFFSET,
  109.       offsetof(ngx_http_ssi_loc_conf_t, silent_errors),
  110.       NULL },

  111.     { ngx_string("ssi_ignore_recycled_buffers"),
  112.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  113.       ngx_conf_set_flag_slot,
  114.       NGX_HTTP_LOC_CONF_OFFSET,
  115.       offsetof(ngx_http_ssi_loc_conf_t, ignore_recycled_buffers),
  116.       NULL },

  117.     { ngx_string("ssi_min_file_chunk"),
  118.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  119.       ngx_conf_set_size_slot,
  120.       NGX_HTTP_LOC_CONF_OFFSET,
  121.       offsetof(ngx_http_ssi_loc_conf_t, min_file_chunk),
  122.       NULL },

  123.     { ngx_string("ssi_value_length"),
  124.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  125.       ngx_conf_set_size_slot,
  126.       NGX_HTTP_LOC_CONF_OFFSET,
  127.       offsetof(ngx_http_ssi_loc_conf_t, value_len),
  128.       NULL },

  129.     { ngx_string("ssi_types"),
  130.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  131.       ngx_http_types_slot,
  132.       NGX_HTTP_LOC_CONF_OFFSET,
  133.       offsetof(ngx_http_ssi_loc_conf_t, types_keys),
  134.       &ngx_http_html_default_types[0] },

  135.     { ngx_string("ssi_last_modified"),
  136.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  137.       ngx_conf_set_flag_slot,
  138.       NGX_HTTP_LOC_CONF_OFFSET,
  139.       offsetof(ngx_http_ssi_loc_conf_t, last_modified),
  140.       NULL },

  141.       ngx_null_command
  142. };



  143. static ngx_http_module_t  ngx_http_ssi_filter_module_ctx = {
  144.     ngx_http_ssi_preconfiguration,         /* preconfiguration */
  145.     ngx_http_ssi_filter_init,              /* postconfiguration */

  146.     ngx_http_ssi_create_main_conf,         /* create main configuration */
  147.     ngx_http_ssi_init_main_conf,           /* init main configuration */

  148.     NULL,                                  /* create server configuration */
  149.     NULL,                                  /* merge server configuration */

  150.     ngx_http_ssi_create_loc_conf,          /* create location configuration */
  151.     ngx_http_ssi_merge_loc_conf            /* merge location configuration */
  152. };


  153. ngx_module_t  ngx_http_ssi_filter_module = {
  154.     NGX_MODULE_V1,
  155.     &ngx_http_ssi_filter_module_ctx,       /* module context */
  156.     ngx_http_ssi_filter_commands,          /* module directives */
  157.     NGX_HTTP_MODULE,                       /* module type */
  158.     NULL,                                  /* init master */
  159.     NULL,                                  /* init module */
  160.     NULL,                                  /* init process */
  161.     NULL,                                  /* init thread */
  162.     NULL,                                  /* exit thread */
  163.     NULL,                                  /* exit process */
  164.     NULL,                                  /* exit master */
  165.     NGX_MODULE_V1_PADDING
  166. };


  167. static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
  168. static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;


  169. static u_char ngx_http_ssi_string[] = "<!--";

  170. static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");
  171. static ngx_str_t ngx_http_ssi_timefmt = ngx_string("%A, %d-%b-%Y %H:%M:%S %Z");
  172. static ngx_str_t ngx_http_ssi_null_string = ngx_null_string;


  173. #define  NGX_HTTP_SSI_INCLUDE_VIRTUAL  0
  174. #define  NGX_HTTP_SSI_INCLUDE_FILE     1
  175. #define  NGX_HTTP_SSI_INCLUDE_WAIT     2
  176. #define  NGX_HTTP_SSI_INCLUDE_SET      3
  177. #define  NGX_HTTP_SSI_INCLUDE_STUB     4

  178. #define  NGX_HTTP_SSI_ECHO_VAR         0
  179. #define  NGX_HTTP_SSI_ECHO_DEFAULT     1
  180. #define  NGX_HTTP_SSI_ECHO_ENCODING    2

  181. #define  NGX_HTTP_SSI_CONFIG_ERRMSG    0
  182. #define  NGX_HTTP_SSI_CONFIG_TIMEFMT   1

  183. #define  NGX_HTTP_SSI_SET_VAR          0
  184. #define  NGX_HTTP_SSI_SET_VALUE        1

  185. #define  NGX_HTTP_SSI_IF_EXPR          0

  186. #define  NGX_HTTP_SSI_BLOCK_NAME       0


  187. static ngx_http_ssi_param_t  ngx_http_ssi_include_params[] = {
  188.     { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 },
  189.     { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0, 0 },
  190.     { ngx_string("wait"), NGX_HTTP_SSI_INCLUDE_WAIT, 0, 0 },
  191.     { ngx_string("set"), NGX_HTTP_SSI_INCLUDE_SET, 0, 0 },
  192.     { ngx_string("stub"), NGX_HTTP_SSI_INCLUDE_STUB, 0, 0 },
  193.     { ngx_null_string, 0, 0, 0 }
  194. };


  195. static ngx_http_ssi_param_t  ngx_http_ssi_echo_params[] = {
  196.     { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1, 0 },
  197.     { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0, 0 },
  198.     { ngx_string("encoding"), NGX_HTTP_SSI_ECHO_ENCODING, 0, 0 },
  199.     { ngx_null_string, 0, 0, 0 }
  200. };


  201. static ngx_http_ssi_param_t  ngx_http_ssi_config_params[] = {
  202.     { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG, 0, 0 },
  203.     { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT, 0, 0 },
  204.     { ngx_null_string, 0, 0, 0 }
  205. };


  206. static ngx_http_ssi_param_t  ngx_http_ssi_set_params[] = {
  207.     { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 1, 0 },
  208.     { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE, 1, 0 },
  209.     { ngx_null_string, 0, 0, 0 }
  210. };


  211. static ngx_http_ssi_param_t  ngx_http_ssi_if_params[] = {
  212.     { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 1, 0 },
  213.     { ngx_null_string, 0, 0, 0 }
  214. };


  215. static ngx_http_ssi_param_t  ngx_http_ssi_block_params[] = {
  216.     { ngx_string("name"), NGX_HTTP_SSI_BLOCK_NAME, 1, 0 },
  217.     { ngx_null_string, 0, 0, 0 }
  218. };


  219. static ngx_http_ssi_param_t  ngx_http_ssi_no_params[] = {
  220.     { ngx_null_string, 0, 0, 0 }
  221. };


  222. static ngx_http_ssi_command_t  ngx_http_ssi_commands[] = {
  223.     { ngx_string("include"), ngx_http_ssi_include,
  224.                        ngx_http_ssi_include_params, 0, 0, 1 },
  225.     { ngx_string("echo"), ngx_http_ssi_echo,
  226.                        ngx_http_ssi_echo_params, 0, 0, 0 },
  227.     { ngx_string("config"), ngx_http_ssi_config,
  228.                        ngx_http_ssi_config_params, 0, 0, 0 },
  229.     { ngx_string("set"), ngx_http_ssi_set, ngx_http_ssi_set_params, 0, 0, 0 },

  230.     { ngx_string("if"), ngx_http_ssi_if, ngx_http_ssi_if_params, 0, 0, 0 },
  231.     { ngx_string("elif"), ngx_http_ssi_if, ngx_http_ssi_if_params,
  232.                        NGX_HTTP_SSI_COND_IF, 0, 0 },
  233.     { ngx_string("else"), ngx_http_ssi_else, ngx_http_ssi_no_params,
  234.                        NGX_HTTP_SSI_COND_IF, 0, 0 },
  235.     { ngx_string("endif"), ngx_http_ssi_endif, ngx_http_ssi_no_params,
  236.                        NGX_HTTP_SSI_COND_ELSE, 0, 0 },

  237.     { ngx_string("block"), ngx_http_ssi_block,
  238.                        ngx_http_ssi_block_params, 0, 0, 0 },
  239.     { ngx_string("endblock"), ngx_http_ssi_endblock,
  240.                        ngx_http_ssi_no_params, 0, 1, 0 },

  241.     { ngx_null_string, NULL, NULL, 0, 0, 0 }
  242. };


  243. static ngx_http_variable_t  ngx_http_ssi_vars[] = {

  244.     { ngx_string("date_local"), NULL, ngx_http_ssi_date_gmt_local_variable, 0,
  245.       NGX_HTTP_VAR_NOCACHEABLE, 0 },

  246.     { ngx_string("date_gmt"), NULL, ngx_http_ssi_date_gmt_local_variable, 1,
  247.       NGX_HTTP_VAR_NOCACHEABLE, 0 },

  248.     { ngx_null_string, NULL, NULL, 0, 0, 0 }
  249. };



  250. static ngx_int_t
  251. ngx_http_ssi_header_filter(ngx_http_request_t *r)
  252. {
  253.     ngx_http_ssi_ctx_t       *ctx;
  254.     ngx_http_ssi_loc_conf_t  *slcf;

  255.     slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);

  256.     if (!slcf->enable
  257.         || r->headers_out.content_length_n == 0
  258.         || ngx_http_test_content_type(r, &slcf->types) == NULL)
  259.     {
  260.         return ngx_http_next_header_filter(r);
  261.     }

  262.     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t));
  263.     if (ctx == NULL) {
  264.         return NGX_ERROR;
  265.     }

  266.     ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);


  267.     ctx->value_len = slcf->value_len;
  268.     ctx->last_out = &ctx->out;

  269.     ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
  270.     ctx->output = 1;

  271.     ctx->params.elts = ctx->params_array;
  272.     ctx->params.size = sizeof(ngx_table_elt_t);
  273.     ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N;
  274.     ctx->params.pool = r->pool;

  275.     ctx->timefmt = ngx_http_ssi_timefmt;
  276.     ngx_str_set(&ctx->errmsg,
  277.                 "[an error occurred while processing the directive]");

  278.     r->filter_need_in_memory = 1;

  279.     if (r == r->main) {
  280.         ngx_http_clear_content_length(r);
  281.         ngx_http_clear_accept_ranges(r);

  282.         if (!slcf->last_modified) {
  283.             ngx_http_clear_last_modified(r);
  284.             ngx_http_clear_etag(r);

  285.         } else {
  286.             ngx_http_weak_etag(r);
  287.         }
  288.     }

  289.     return ngx_http_next_header_filter(r);
  290. }


  291. static ngx_int_t
  292. ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
  293. {
  294.     size_t                     len;
  295.     ngx_int_t                  rc;
  296.     ngx_buf_t                 *b;
  297.     ngx_uint_t                 i, index;
  298.     ngx_chain_t               *cl, **ll;
  299.     ngx_table_elt_t           *param;
  300.     ngx_http_ssi_ctx_t        *ctx, *mctx;
  301.     ngx_http_ssi_block_t      *bl;
  302.     ngx_http_ssi_param_t      *prm;
  303.     ngx_http_ssi_command_t    *cmd;
  304.     ngx_http_ssi_loc_conf_t   *slcf;
  305.     ngx_http_ssi_main_conf_t  *smcf;
  306.     ngx_str_t                 *params[NGX_HTTP_SSI_MAX_PARAMS + 1];

  307.     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);

  308.     if (ctx == NULL
  309.         || (in == NULL
  310.             && ctx->buf == NULL
  311.             && ctx->in == NULL
  312.             && ctx->busy == NULL))
  313.     {
  314.         return ngx_http_next_body_filter(r, in);
  315.     }

  316.     /* add the incoming chain to the chain ctx->in */

  317.     if (in) {
  318.         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
  319.             return NGX_ERROR;
  320.         }
  321.     }

  322.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  323.                    "http ssi filter \"%V?%V\"", &r->uri, &r->args);

  324.     if (ctx->wait) {

  325.         if (r != r->connection->data) {
  326.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  327.                            "http ssi filter wait \"%V?%V\" non-active",
  328.                            &ctx->wait->uri, &ctx->wait->args);

  329.             return NGX_AGAIN;
  330.         }

  331.         if (ctx->wait->done) {
  332.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  333.                            "http ssi filter wait \"%V?%V\" done",
  334.                            &ctx->wait->uri, &ctx->wait->args);

  335.             ctx->wait = NULL;

  336.         } else {
  337.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  338.                            "http ssi filter wait \"%V?%V\"",
  339.                            &ctx->wait->uri, &ctx->wait->args);

  340.             return ngx_http_next_body_filter(r, NULL);
  341.         }
  342.     }

  343.     slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);

  344.     while (ctx->in || ctx->buf) {

  345.         if (ctx->buf == NULL) {
  346.             ctx->buf = ctx->in->buf;
  347.             ctx->in = ctx->in->next;
  348.             ctx->pos = ctx->buf->pos;
  349.         }

  350.         if (ctx->state == ssi_start_state) {
  351.             ctx->copy_start = ctx->pos;
  352.             ctx->copy_end = ctx->pos;
  353.         }

  354.         b = NULL;

  355.         while (ctx->pos < ctx->buf->last) {

  356.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  357.                            "saved: %d state: %d", ctx->saved, ctx->state);

  358.             rc = ngx_http_ssi_parse(r, ctx);

  359.             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  360.                            "parse: %d, looked: %d %p-%p",
  361.                            rc, ctx->looked, ctx->copy_start, ctx->copy_end);

  362.             if (rc == NGX_ERROR) {
  363.                 return rc;
  364.             }

  365.             if (ctx->copy_start != ctx->copy_end) {

  366.                 if (ctx->output) {

  367.                     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  368.                                    "saved: %d", ctx->saved);

  369.                     if (ctx->saved) {

  370.                         if (ctx->free) {
  371.                             cl = ctx->free;
  372.                             ctx->free = ctx->free->next;
  373.                             b = cl->buf;
  374.                             ngx_memzero(b, sizeof(ngx_buf_t));

  375.                         } else {
  376.                             b = ngx_calloc_buf(r->pool);
  377.                             if (b == NULL) {
  378.                                 return NGX_ERROR;
  379.                             }

  380.                             cl = ngx_alloc_chain_link(r->pool);
  381.                             if (cl == NULL) {
  382.                                 return NGX_ERROR;
  383.                             }

  384.                             cl->buf = b;
  385.                         }

  386.                         b->memory = 1;
  387.                         b->pos = ngx_http_ssi_string;
  388.                         b->last = ngx_http_ssi_string + ctx->saved;

  389.                         *ctx->last_out = cl;
  390.                         ctx->last_out = &cl->next;

  391.                         ctx->saved = 0;
  392.                     }

  393.                     if (ctx->free) {
  394.                         cl = ctx->free;
  395.                         ctx->free = ctx->free->next;
  396.                         b = cl->buf;

  397.                     } else {
  398.                         b = ngx_alloc_buf(r->pool);
  399.                         if (b == NULL) {
  400.                             return NGX_ERROR;
  401.                         }

  402.                         cl = ngx_alloc_chain_link(r->pool);
  403.                         if (cl == NULL) {
  404.                             return NGX_ERROR;
  405.                         }

  406.                         cl->buf = b;
  407.                     }

  408.                     ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));

  409.                     b->pos = ctx->copy_start;
  410.                     b->last = ctx->copy_end;
  411.                     b->shadow = NULL;
  412.                     b->last_buf = 0;
  413.                     b->recycled = 0;

  414.                     if (b->in_file) {
  415.                         if (slcf->min_file_chunk < (size_t) (b->last - b->pos))
  416.                         {
  417.                             b->file_last = b->file_pos
  418.                                                    + (b->last - ctx->buf->pos);
  419.                             b->file_pos += b->pos - ctx->buf->pos;

  420.                         } else {
  421.                             b->in_file = 0;
  422.                         }
  423.                     }

  424.                     cl->next = NULL;
  425.                     *ctx->last_out = cl;
  426.                     ctx->last_out = &cl->next;

  427.                 } else {
  428.                     if (ctx->block
  429.                         && ctx->saved + (ctx->copy_end - ctx->copy_start))
  430.                     {
  431.                         b = ngx_create_temp_buf(r->pool,
  432.                                ctx->saved + (ctx->copy_end - ctx->copy_start));

  433.                         if (b == NULL) {
  434.                             return NGX_ERROR;
  435.                         }

  436.                         if (ctx->saved) {
  437.                             b->last = ngx_cpymem(b->pos, ngx_http_ssi_string,
  438.                                                  ctx->saved);
  439.                         }

  440.                         b->last = ngx_cpymem(b->last, ctx->copy_start,
  441.                                              ctx->copy_end - ctx->copy_start);

  442.                         cl = ngx_alloc_chain_link(r->pool);
  443.                         if (cl == NULL) {
  444.                             return NGX_ERROR;
  445.                         }

  446.                         cl->buf = b;
  447.                         cl->next = NULL;

  448.                         b = NULL;

  449.                         mctx = ngx_http_get_module_ctx(r->main,
  450.                                                    ngx_http_ssi_filter_module);
  451.                         bl = mctx->blocks->elts;
  452.                         for (ll = &bl[mctx->blocks->nelts - 1].bufs;
  453.                              *ll;
  454.                              ll = &(*ll)->next)
  455.                         {
  456.                             /* void */
  457.                         }

  458.                         *ll = cl;
  459.                     }

  460.                     ctx->saved = 0;
  461.                 }
  462.             }

  463.             if (ctx->state == ssi_start_state) {
  464.                 ctx->copy_start = ctx->pos;
  465.                 ctx->copy_end = ctx->pos;

  466.             } else {
  467.                 ctx->copy_start = NULL;
  468.                 ctx->copy_end = NULL;
  469.             }

  470.             if (rc == NGX_AGAIN) {
  471.                 continue;
  472.             }


  473.             b = NULL;

  474.             if (rc == NGX_OK) {

  475.                 smcf = ngx_http_get_module_main_conf(r,
  476.                                                    ngx_http_ssi_filter_module);

  477.                 cmd = ngx_hash_find(&smcf->hash, ctx->key, ctx->command.data,
  478.                                     ctx->command.len);

  479.                 if (cmd == NULL) {
  480.                     if (ctx->output) {
  481.                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  482.                                       "invalid SSI command: \"%V\"",
  483.                                       &ctx->command);
  484.                         goto ssi_error;
  485.                     }

  486.                     continue;
  487.                 }

  488.                 if (!ctx->output && !cmd->block) {

  489.                     if (ctx->block) {

  490.                         /* reconstruct the SSI command text */

  491.                         len = 5 + ctx->command.len + 4;

  492.                         param = ctx->params.elts;
  493.                         for (i = 0; i < ctx->params.nelts; i++) {
  494.                             len += 1 + param[i].key.len + 2
  495.                                 + param[i].value.len + 1;
  496.                         }

  497.                         b = ngx_create_temp_buf(r->pool, len);

  498.                         if (b == NULL) {
  499.                             return NGX_ERROR;
  500.                         }

  501.                         cl = ngx_alloc_chain_link(r->pool);
  502.                         if (cl == NULL) {
  503.                             return NGX_ERROR;
  504.                         }

  505.                         cl->buf = b;
  506.                         cl->next = NULL;

  507.                         *b->last++ = '<';
  508.                         *b->last++ = '!';
  509.                         *b->last++ = '-';
  510.                         *b->last++ = '-';
  511.                         *b->last++ = '#';

  512.                         b->last = ngx_cpymem(b->last, ctx->command.data,
  513.                                              ctx->command.len);

  514.                         for (i = 0; i < ctx->params.nelts; i++) {
  515.                             *b->last++ = ' ';
  516.                             b->last = ngx_cpymem(b->last, param[i].key.data,
  517.                                                  param[i].key.len);
  518.                             *b->last++ = '=';
  519.                             *b->last++ = '"';
  520.                             b->last = ngx_cpymem(b->last, param[i].value.data,
  521.                                                  param[i].value.len);
  522.                             *b->last++ = '"';
  523.                         }

  524.                         *b->last++ = ' ';
  525.                         *b->last++ = '-';
  526.                         *b->last++ = '-';
  527.                         *b->last++ = '>';

  528.                         mctx = ngx_http_get_module_ctx(r->main,
  529.                                                    ngx_http_ssi_filter_module);
  530.                         bl = mctx->blocks->elts;
  531.                         for (ll = &bl[mctx->blocks->nelts - 1].bufs;
  532.                              *ll;
  533.                              ll = &(*ll)->next)
  534.                         {
  535.                             /* void */
  536.                         }

  537.                         *ll = cl;

  538.                         b = NULL;

  539.                         continue;
  540.                     }

  541.                     if (cmd->conditional == 0) {
  542.                         continue;
  543.                     }
  544.                 }

  545.                 if (cmd->conditional
  546.                     && (ctx->conditional == 0
  547.                         || ctx->conditional > cmd->conditional))
  548.                 {
  549.                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  550.                                   "invalid context of SSI command: \"%V\"",
  551.                                   &ctx->command);
  552.                     goto ssi_error;
  553.                 }

  554.                 if (ctx->params.nelts > NGX_HTTP_SSI_MAX_PARAMS) {
  555.                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  556.                                   "too many SSI command parameters: \"%V\"",
  557.                                   &ctx->command);
  558.                     goto ssi_error;
  559.                 }

  560.                 ngx_memzero(params,
  561.                            (NGX_HTTP_SSI_MAX_PARAMS + 1) * sizeof(ngx_str_t *));

  562.                 param = ctx->params.elts;

  563.                 for (i = 0; i < ctx->params.nelts; i++) {

  564.                     for (prm = cmd->params; prm->name.len; prm++) {

  565.                         if (param[i].key.len != prm->name.len
  566.                             || ngx_strncmp(param[i].key.data, prm->name.data,
  567.                                            prm->name.len) != 0)
  568.                         {
  569.                             continue;
  570.                         }

  571.                         if (!prm->multiple) {
  572.                             if (params[prm->index]) {
  573.                                 ngx_log_error(NGX_LOG_ERR,
  574.                                               r->connection->log, 0,
  575.                                               "duplicate \"%V\" parameter "
  576.                                               "in \"%V\" SSI command",
  577.                                               &param[i].key, &ctx->command);

  578.                                 goto ssi_error;
  579.                             }

  580.                             params[prm->index] = &param[i].value;

  581.                             break;
  582.                         }

  583.                         for (index = prm->index; params[index]; index++) {
  584.                             /* void */
  585.                         }

  586.                         params[index] = &param[i].value;

  587.                         break;
  588.                     }

  589.                     if (prm->name.len == 0) {
  590.                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  591.                                       "invalid parameter name: \"%V\" "
  592.                                       "in \"%V\" SSI command",
  593.                                       &param[i].key, &ctx->command);

  594.                         goto ssi_error;
  595.                     }
  596.                 }

  597.                 for (prm = cmd->params; prm->name.len; prm++) {
  598.                     if (prm->mandatory && params[prm->index] == 0) {
  599.                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  600.                                       "mandatory \"%V\" parameter is absent "
  601.                                       "in \"%V\" SSI command",
  602.                                       &prm->name, &ctx->command);

  603.                         goto ssi_error;
  604.                     }
  605.                 }

  606.                 if (cmd->flush && ctx->out) {

  607.                     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  608.                                    "ssi flush");

  609.                     if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) {
  610.                         return NGX_ERROR;
  611.                     }
  612.                 }

  613.                 rc = cmd->handler(r, ctx, params);

  614.                 if (rc == NGX_OK) {
  615.                     continue;
  616.                 }

  617.                 if (rc == NGX_DONE || rc == NGX_AGAIN || rc == NGX_ERROR) {
  618.                     ngx_http_ssi_buffered(r, ctx);
  619.                     return rc;
  620.                 }
  621.             }


  622.             /* rc == NGX_HTTP_SSI_ERROR */

  623.     ssi_error:

  624.             if (slcf->silent_errors) {
  625.                 continue;
  626.             }

  627.             if (ctx->free) {
  628.                 cl = ctx->free;
  629.                 ctx->free = ctx->free->next;
  630.                 b = cl->buf;
  631.                 ngx_memzero(b, sizeof(ngx_buf_t));

  632.             } else {
  633.                 b = ngx_calloc_buf(r->pool);
  634.                 if (b == NULL) {
  635.                     return NGX_ERROR;
  636.                 }

  637.                 cl = ngx_alloc_chain_link(r->pool);
  638.                 if (cl == NULL) {
  639.                     return NGX_ERROR;
  640.                 }

  641.                 cl->buf = b;
  642.             }

  643.             b->memory = 1;
  644.             b->pos = ctx->errmsg.data;
  645.             b->last = ctx->errmsg.data + ctx->errmsg.len;

  646.             cl->next = NULL;
  647.             *ctx->last_out = cl;
  648.             ctx->last_out = &cl->next;

  649.             continue;
  650.         }

  651.         if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) {
  652.             if (b == NULL) {
  653.                 if (ctx->free) {
  654.                     cl = ctx->free;
  655.                     ctx->free = ctx->free->next;
  656.                     b = cl->buf;
  657.                     ngx_memzero(b, sizeof(ngx_buf_t));

  658.                 } else {
  659.                     b = ngx_calloc_buf(r->pool);
  660.                     if (b == NULL) {
  661.                         return NGX_ERROR;
  662.                     }

  663.                     cl = ngx_alloc_chain_link(r->pool);
  664.                     if (cl == NULL) {
  665.                         return NGX_ERROR;
  666.                     }

  667.                     cl->buf = b;
  668.                 }

  669.                 b->sync = 1;

  670.                 cl->next = NULL;
  671.                 *ctx->last_out = cl;
  672.                 ctx->last_out = &cl->next;
  673.             }

  674.             b->last_buf = ctx->buf->last_buf;
  675.             b->shadow = ctx->buf;

  676.             if (slcf->ignore_recycled_buffers == 0)  {
  677.                 b->recycled = ctx->buf->recycled;
  678.             }
  679.         }

  680.         ctx->buf = NULL;

  681.         ctx->saved = ctx->looked;
  682.     }

  683.     if (ctx->out == NULL && ctx->busy == NULL) {
  684.         return NGX_OK;
  685.     }

  686.     return ngx_http_ssi_output(r, ctx);
  687. }


  688. static ngx_int_t
  689. ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
  690. {
  691.     ngx_int_t     rc;
  692.     ngx_buf_t    *b;
  693.     ngx_chain_t  *cl;

  694. #if 1
  695.     b = NULL;
  696.     for (cl = ctx->out; cl; cl = cl->next) {
  697.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  698.                        "ssi out: %p %p", cl->buf, cl->buf->pos);
  699.         if (cl->buf == b) {
  700.             ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
  701.                           "the same buf was used in ssi");
  702.             ngx_debug_point();
  703.             return NGX_ERROR;
  704.         }
  705.         b = cl->buf;
  706.     }
  707. #endif

  708.     rc = ngx_http_next_body_filter(r, ctx->out);

  709.     if (ctx->busy == NULL) {
  710.         ctx->busy = ctx->out;

  711.     } else {
  712.         for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
  713.         cl->next = ctx->out;
  714.     }

  715.     ctx->out = NULL;
  716.     ctx->last_out = &ctx->out;

  717.     while (ctx->busy) {

  718.         cl = ctx->busy;
  719.         b = cl->buf;

  720.         if (ngx_buf_size(b) != 0) {
  721.             break;
  722.         }

  723.         if (b->shadow) {
  724.             b->shadow->pos = b->shadow->last;
  725.         }

  726.         ctx->busy = cl->next;

  727.         if (ngx_buf_in_memory(b) || b->in_file) {
  728.             /* add data bufs only to the free buf chain */

  729.             cl->next = ctx->free;
  730.             ctx->free = cl;
  731.         }
  732.     }

  733.     ngx_http_ssi_buffered(r, ctx);

  734.     return rc;
  735. }


  736. static void
  737. ngx_http_ssi_buffered(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
  738. {
  739.     if (ctx->in || ctx->buf) {
  740.         r->buffered |= NGX_HTTP_SSI_BUFFERED;

  741.     } else {
  742.         r->buffered &= ~NGX_HTTP_SSI_BUFFERED;
  743.     }
  744. }


  745. static ngx_int_t
  746. ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
  747. {
  748.     u_char                *p, *value, *last, *copy_end, ch;
  749.     size_t                 looked;
  750.     ngx_http_ssi_state_e   state;

  751.     state = ctx->state;
  752.     looked = ctx->looked;
  753.     last = ctx->buf->last;
  754.     copy_end = ctx->copy_end;

  755.     for (p = ctx->pos; p < last; p++) {

  756.         ch = *p;

  757.         if (state == ssi_start_state) {

  758.             /* the tight loop */

  759.             for ( ;; ) {
  760.                 if (ch == '<') {
  761.                     copy_end = p;
  762.                     looked = 1;
  763.                     state = ssi_tag_state;

  764.                     goto tag_started;
  765.                 }

  766.                 if (++p == last) {
  767.                     break;
  768.                 }

  769.                 ch = *p;
  770.             }

  771.             ctx->state = state;
  772.             ctx->pos = p;
  773.             ctx->looked = looked;
  774.             ctx->copy_end = p;

  775.             if (ctx->copy_start == NULL) {
  776.                 ctx->copy_start = ctx->buf->pos;
  777.             }

  778.             return NGX_AGAIN;

  779.         tag_started:

  780.             continue;
  781.         }

  782.         switch (state) {

  783.         case ssi_start_state:
  784.             /* not reached */
  785.             break;

  786.         case ssi_tag_state:
  787.             switch (ch) {
  788.             case '!':
  789.                 looked = 2;
  790.                 state = ssi_comment0_state;
  791.                 break;

  792.             case '<':
  793.                 copy_end = p;
  794.                 break;

  795.             default:
  796.                 copy_end = p;
  797.                 looked = 0;
  798.                 state = ssi_start_state;
  799.                 break;
  800.             }

  801.             break;

  802.         case ssi_comment0_state:
  803.             switch (ch) {
  804.             case '-':
  805.                 looked = 3;
  806.                 state = ssi_comment1_state;
  807.                 break;

  808.             case '<':
  809.                 copy_end = p;
  810.                 looked = 1;
  811.                 state = ssi_tag_state;
  812.                 break;

  813.             default:
  814.                 copy_end = p;
  815.                 looked = 0;
  816.                 state = ssi_start_state;
  817.                 break;
  818.             }

  819.             break;

  820.         case ssi_comment1_state:
  821.             switch (ch) {
  822.             case '-':
  823.                 looked = 4;
  824.                 state = ssi_sharp_state;
  825.                 break;

  826.             case '<':
  827.                 copy_end = p;
  828.                 looked = 1;
  829.                 state = ssi_tag_state;
  830.                 break;

  831.             default:
  832.                 copy_end = p;
  833.                 looked = 0;
  834.                 state = ssi_start_state;
  835.                 break;
  836.             }

  837.             break;

  838.         case ssi_sharp_state:
  839.             switch (ch) {
  840.             case '#':
  841.                 if (p - ctx->pos < 4) {
  842.                     ctx->saved = 0;
  843.                 }
  844.                 looked = 0;
  845.                 state = ssi_precommand_state;
  846.                 break;

  847.             case '<':
  848.                 copy_end = p;
  849.                 looked = 1;
  850.                 state = ssi_tag_state;
  851.                 break;

  852.             default:
  853.                 copy_end = p;
  854.                 looked = 0;
  855.                 state = ssi_start_state;
  856.                 break;
  857.             }

  858.             break;

  859.         case ssi_precommand_state:
  860.             switch (ch) {
  861.             case ' ':
  862.             case CR:
  863.             case LF:
  864.             case '\t':
  865.                 break;

  866.             default:
  867.                 ctx->command.len = 1;
  868.                 ctx->command.data = ngx_pnalloc(r->pool,
  869.                                                 NGX_HTTP_SSI_COMMAND_LEN);
  870.                 if (ctx->command.data == NULL) {
  871.                     return NGX_ERROR;
  872.                 }

  873.                 ctx->command.data[0] = ch;

  874.                 ctx->key = 0;
  875.                 ctx->key = ngx_hash(ctx->key, ch);

  876.                 ctx->params.nelts = 0;

  877.                 state = ssi_command_state;
  878.                 break;
  879.             }

  880.             break;

  881.         case ssi_command_state:
  882.             switch (ch) {
  883.             case ' ':
  884.             case CR:
  885.             case LF:
  886.             case '\t':
  887.                 state = ssi_preparam_state;
  888.                 break;

  889.             case '-':
  890.                 state = ssi_comment_end0_state;
  891.                 break;

  892.             default:
  893.                 if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) {
  894.                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  895.                                   "the \"%V%c...\" SSI command is too long",
  896.                                   &ctx->command, ch);

  897.                     state = ssi_error_state;
  898.                     break;
  899.                 }

  900.                 ctx->command.data[ctx->command.len++] = ch;
  901.                 ctx->key = ngx_hash(ctx->key, ch);
  902.             }

  903.             break;

  904.         case ssi_preparam_state:
  905.             switch (ch) {
  906.             case ' ':
  907.             case CR:
  908.             case LF:
  909.             case '\t':
  910.                 break;

  911.             case '-':
  912.                 state = ssi_comment_end0_state;
  913.                 break;

  914.             default:
  915.                 ctx->param = ngx_array_push(&ctx->params);
  916.                 if (ctx->param == NULL) {
  917.                     return NGX_ERROR;
  918.                 }

  919.                 ctx->param->key.len = 1;
  920.                 ctx->param->key.data = ngx_pnalloc(r->pool,
  921.                                                    NGX_HTTP_SSI_PARAM_LEN);
  922.                 if (ctx->param->key.data == NULL) {
  923.                     return NGX_ERROR;
  924.                 }

  925.                 ctx->param->key.data[0] = ch;

  926.                 ctx->param->value.len = 0;

  927.                 if (ctx->value_buf == NULL) {
  928.                     ctx->param->value.data = ngx_pnalloc(r->pool,
  929.                                                          ctx->value_len + 1);
  930.                     if (ctx->param->value.data == NULL) {
  931.                         return NGX_ERROR;
  932.                     }

  933.                 } else {
  934.                     ctx->param->value.data = ctx->value_buf;
  935.                 }

  936.                 state = ssi_param_state;
  937.                 break;
  938.             }

  939.             break;

  940.         case ssi_param_state:
  941.             switch (ch) {
  942.             case ' ':
  943.             case CR:
  944.             case LF:
  945.             case '\t':
  946.                 state = ssi_preequal_state;
  947.                 break;

  948.             case '=':
  949.                 state = ssi_prevalue_state;
  950.                 break;

  951.             case '-':
  952.                 state = ssi_error_end0_state;

  953.                 ctx->param->key.data[ctx->param->key.len++] = ch;
  954.                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  955.                               "invalid \"%V\" parameter in \"%V\" SSI command",
  956.                               &ctx->param->key, &ctx->command);
  957.                 break;

  958.             default:
  959.                 if (ctx->param->key.len == NGX_HTTP_SSI_PARAM_LEN) {
  960.                     state = ssi_error_state;
  961.                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  962.                                   "too long \"%V%c...\" parameter in "
  963.                                   "\"%V\" SSI command",
  964.                                   &ctx->param->key, ch, &ctx->command);
  965.                     break;
  966.                 }

  967.                 ctx->param->key.data[ctx->param->key.len++] = ch;
  968.             }

  969.             break;

  970.         case ssi_preequal_state:
  971.             switch (ch) {
  972.             case ' ':
  973.             case CR:
  974.             case LF:
  975.             case '\t':
  976.                 break;

  977.             case '=':
  978.                 state = ssi_prevalue_state;
  979.                 break;

  980.             default:
  981.                 if (ch == '-') {
  982.                     state = ssi_error_end0_state;
  983.                 } else {
  984.                     state = ssi_error_state;
  985.                 }

  986.                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  987.                               "unexpected \"%c\" symbol after \"%V\" "
  988.                               "parameter in \"%V\" SSI command",
  989.                               ch, &ctx->param->key, &ctx->command);
  990.                 break;
  991.             }

  992.             break;

  993.         case ssi_prevalue_state:
  994.             switch (ch) {
  995.             case ' ':
  996.             case CR:
  997.             case LF:
  998.             case '\t':
  999.                 break;

  1000.             case '"':
  1001.                 state = ssi_double_quoted_value_state;
  1002.                 break;

  1003.             case '\'':
  1004.                 state = ssi_quoted_value_state;
  1005.                 break;

  1006.             default:
  1007.                 if (ch == '-') {
  1008.                     state = ssi_error_end0_state;
  1009.                 } else {
  1010.                     state = ssi_error_state;
  1011.                 }

  1012.                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1013.                               "unexpected \"%c\" symbol before value of "
  1014.                               "\"%V\" parameter in \"%V\" SSI command",
  1015.                               ch, &ctx->param->key, &ctx->command);
  1016.                 break;
  1017.             }

  1018.             break;

  1019.         case ssi_double_quoted_value_state:
  1020.             switch (ch) {
  1021.             case '"':
  1022.                 state = ssi_postparam_state;
  1023.                 break;

  1024.             case '\\':
  1025.                 ctx->saved_state = ssi_double_quoted_value_state;
  1026.                 state = ssi_quoted_symbol_state;

  1027.                 /* fall through */

  1028.             default:
  1029.                 if (ctx->param->value.len == ctx->value_len) {
  1030.                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1031.                                   "too long \"%V%c...\" value of \"%V\" "
  1032.                                   "parameter in \"%V\" SSI command",
  1033.                                   &ctx->param->value, ch, &ctx->param->key,
  1034.                                   &ctx->command);
  1035.                     state = ssi_error_state;
  1036.                     break;
  1037.                 }

  1038.                 ctx->param->value.data[ctx->param->value.len++] = ch;
  1039.             }

  1040.             break;

  1041.         case ssi_quoted_value_state:
  1042.             switch (ch) {
  1043.             case '\'':
  1044.                 state = ssi_postparam_state;
  1045.                 break;

  1046.             case '\\':
  1047.                 ctx->saved_state = ssi_quoted_value_state;
  1048.                 state = ssi_quoted_symbol_state;

  1049.                 /* fall through */

  1050.             default:
  1051.                 if (ctx->param->value.len == ctx->value_len) {
  1052.                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1053.                                   "too long \"%V%c...\" value of \"%V\" "
  1054.                                   "parameter in \"%V\" SSI command",
  1055.                                   &ctx->param->value, ch, &ctx->param->key,
  1056.                                   &ctx->command);
  1057.                     state = ssi_error_state;
  1058.                     break;
  1059.                 }

  1060.                 ctx->param->value.data[ctx->param->value.len++] = ch;
  1061.             }

  1062.             break;

  1063.         case ssi_quoted_symbol_state:
  1064.             state = ctx->saved_state;

  1065.             if (ctx->param->value.len == ctx->value_len) {
  1066.                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1067.                               "too long \"%V%c...\" value of \"%V\" "
  1068.                               "parameter in \"%V\" SSI command",
  1069.                               &ctx->param->value, ch, &ctx->param->key,
  1070.                               &ctx->command);
  1071.                 state = ssi_error_state;
  1072.                 break;
  1073.             }

  1074.             ctx->param->value.data[ctx->param->value.len++] = ch;

  1075.             break;

  1076.         case ssi_postparam_state:

  1077.             if (ctx->param->value.len + 1 < ctx->value_len / 2) {
  1078.                 value = ngx_pnalloc(r->pool, ctx->param->value.len + 1);
  1079.                 if (value == NULL) {
  1080.                     return NGX_ERROR;
  1081.                 }

  1082.                 ngx_memcpy(value, ctx->param->value.data,
  1083.                            ctx->param->value.len);

  1084.                 ctx->value_buf = ctx->param->value.data;
  1085.                 ctx->param->value.data = value;

  1086.             } else {
  1087.                 ctx->value_buf = NULL;
  1088.             }

  1089.             switch (ch) {
  1090.             case ' ':
  1091.             case CR:
  1092.             case LF:
  1093.             case '\t':
  1094.                 state = ssi_preparam_state;
  1095.                 break;

  1096.             case '-':
  1097.                 state = ssi_comment_end0_state;
  1098.                 break;

  1099.             default:
  1100.                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1101.                               "unexpected \"%c\" symbol after \"%V\" value "
  1102.                               "of \"%V\" parameter in \"%V\" SSI command",
  1103.                               ch, &ctx->param->value, &ctx->param->key,
  1104.                               &ctx->command);
  1105.                 state = ssi_error_state;
  1106.                 break;
  1107.             }

  1108.             break;

  1109.         case ssi_comment_end0_state:
  1110.             switch (ch) {
  1111.             case '-':
  1112.                 state = ssi_comment_end1_state;
  1113.                 break;

  1114.             default:
  1115.                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1116.                               "unexpected \"%c\" symbol in \"%V\" SSI command",
  1117.                               ch, &ctx->command);
  1118.                 state = ssi_error_state;
  1119.                 break;
  1120.             }

  1121.             break;

  1122.         case ssi_comment_end1_state:
  1123.             switch (ch) {
  1124.             case '>':
  1125.                 ctx->state = ssi_start_state;
  1126.                 ctx->pos = p + 1;
  1127.                 ctx->looked = looked;
  1128.                 ctx->copy_end = copy_end;

  1129.                 if (ctx->copy_start == NULL && copy_end) {
  1130.                     ctx->copy_start = ctx->buf->pos;
  1131.                 }

  1132.                 return NGX_OK;

  1133.             default:
  1134.                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1135.                               "unexpected \"%c\" symbol in \"%V\" SSI command",
  1136.                               ch, &ctx->command);
  1137.                 state = ssi_error_state;
  1138.                 break;
  1139.             }

  1140.             break;

  1141.         case ssi_error_state:
  1142.             switch (ch) {
  1143.             case '-':
  1144.                 state = ssi_error_end0_state;
  1145.                 break;

  1146.             default:
  1147.                 break;
  1148.             }

  1149.             break;

  1150.         case ssi_error_end0_state:
  1151.             switch (ch) {
  1152.             case '-':
  1153.                 state = ssi_error_end1_state;
  1154.                 break;

  1155.             default:
  1156.                 state = ssi_error_state;
  1157.                 break;
  1158.             }

  1159.             break;

  1160.         case ssi_error_end1_state:
  1161.             switch (ch) {
  1162.             case '>':
  1163.                 ctx->state = ssi_start_state;
  1164.                 ctx->pos = p + 1;
  1165.                 ctx->looked = looked;
  1166.                 ctx->copy_end = copy_end;

  1167.                 if (ctx->copy_start == NULL && copy_end) {
  1168.                     ctx->copy_start = ctx->buf->pos;
  1169.                 }

  1170.                 return NGX_HTTP_SSI_ERROR;

  1171.             default:
  1172.                 state = ssi_error_state;
  1173.                 break;
  1174.             }

  1175.             break;
  1176.         }
  1177.     }

  1178.     ctx->state = state;
  1179.     ctx->pos = p;
  1180.     ctx->looked = looked;

  1181.     ctx->copy_end = (state == ssi_start_state) ? p : copy_end;

  1182.     if (ctx->copy_start == NULL && ctx->copy_end) {
  1183.         ctx->copy_start = ctx->buf->pos;
  1184.     }

  1185.     return NGX_AGAIN;
  1186. }


  1187. static ngx_str_t *
  1188. ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name,
  1189.     ngx_uint_t key)
  1190. {
  1191.     ngx_uint_t           i;
  1192.     ngx_list_part_t     *part;
  1193.     ngx_http_ssi_var_t  *var;
  1194.     ngx_http_ssi_ctx_t  *ctx;

  1195.     ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);

  1196. #if (NGX_PCRE)
  1197.     {
  1198.     ngx_str_t  *value;

  1199.     if (key >= '0' && key <= '9') {
  1200.         i = key - '0';

  1201.         if (i < ctx->ncaptures) {
  1202.             value = ngx_palloc(r->pool, sizeof(ngx_str_t));
  1203.             if (value == NULL) {
  1204.                 return NULL;
  1205.             }

  1206.             i *= 2;

  1207.             value->data = ctx->captures_data + ctx->captures[i];
  1208.             value->len = ctx->captures[i + 1] - ctx->captures[i];

  1209.             return value;
  1210.         }
  1211.     }
  1212.     }
  1213. #endif

  1214.     if (ctx->variables == NULL) {
  1215.         return NULL;
  1216.     }

  1217.     part = &ctx->variables->part;
  1218.     var = part->elts;

  1219.     for (i = 0; /* void */ ; i++) {

  1220.         if (i >= part->nelts) {
  1221.             if (part->next == NULL) {
  1222.                 break;
  1223.             }

  1224.             part = part->next;
  1225.             var = part->elts;
  1226.             i = 0;
  1227.         }

  1228.         if (name->len != var[i].name.len) {
  1229.             continue;
  1230.         }

  1231.         if (key != var[i].key) {
  1232.             continue;
  1233.         }

  1234.         if (ngx_strncmp(name->data, var[i].name.data, name->len) == 0) {
  1235.             return &var[i].value;
  1236.         }
  1237.     }

  1238.     return NULL;
  1239. }


  1240. static ngx_int_t
  1241. ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
  1242.     ngx_str_t *text, ngx_uint_t flags)
  1243. {
  1244.     u_char                      ch, *p, **value, *data, *part_data;
  1245.     size_t                     *size, len, prefix, part_len;
  1246.     ngx_str_t                   var, *val;
  1247.     ngx_int_t                   key;
  1248.     ngx_uint_t                  i, n, bracket, quoted;
  1249.     ngx_array_t                 lengths, values;
  1250.     ngx_http_variable_value_t  *vv;

  1251.     n = ngx_http_script_variables_count(text);

  1252.     if (n == 0) {

  1253.         data = text->data;
  1254.         p = data;

  1255.         if ((flags & NGX_HTTP_SSI_ADD_PREFIX) && text->data[0] != '/') {

  1256.             for (prefix = r->uri.len; prefix; prefix--) {
  1257.                 if (r->uri.data[prefix - 1] == '/') {
  1258.                     break;
  1259.                 }
  1260.             }

  1261.             if (prefix) {
  1262.                 len = prefix + text->len;

  1263.                 data = ngx_pnalloc(r->pool, len);
  1264.                 if (data == NULL) {
  1265.                     return NGX_ERROR;
  1266.                 }

  1267.                 p = ngx_copy(data, r->uri.data, prefix);
  1268.             }
  1269.         }

  1270.         quoted = 0;

  1271.         for (i = 0; i < text->len; i++) {
  1272.             ch = text->data[i];

  1273.             if (!quoted) {

  1274.                 if (ch == '\\') {
  1275.                     quoted = 1;
  1276.                     continue;
  1277.                 }

  1278.             } else {
  1279.                 quoted = 0;

  1280.                 if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
  1281.                     *p++ = '\\';
  1282.                 }
  1283.             }

  1284.             *p++ = ch;
  1285.         }

  1286.         text->len = p - data;
  1287.         text->data = data;

  1288.         return NGX_OK;
  1289.     }

  1290.     if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {
  1291.         return NGX_ERROR;
  1292.     }

  1293.     if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {
  1294.         return NGX_ERROR;
  1295.     }

  1296.     len = 0;
  1297.     i = 0;

  1298.     while (i < text->len) {

  1299.         if (text->data[i] == '$') {

  1300.             var.len = 0;

  1301.             if (++i == text->len) {
  1302.                 goto invalid_variable;
  1303.             }

  1304.             if (text->data[i] == '{') {
  1305.                 bracket = 1;

  1306.                 if (++i == text->len) {
  1307.                     goto invalid_variable;
  1308.                 }

  1309.                 var.data = &text->data[i];

  1310.             } else {
  1311.                 bracket = 0;
  1312.                 var.data = &text->data[i];
  1313.             }

  1314.             for ( /* void */ ; i < text->len; i++, var.len++) {
  1315.                 ch = text->data[i];

  1316.                 if (ch == '}' && bracket) {
  1317.                     i++;
  1318.                     bracket = 0;
  1319.                     break;
  1320.                 }

  1321.                 if ((ch >= 'A' && ch <= 'Z')
  1322.                     || (ch >= 'a' && ch <= 'z')
  1323.                     || (ch >= '0' && ch <= '9')
  1324.                     || ch == '_')
  1325.                 {
  1326.                     continue;
  1327.                 }

  1328.                 break;
  1329.             }

  1330.             if (bracket) {
  1331.                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1332.                               "the closing bracket in \"%V\" "
  1333.                               "variable is missing", &var);
  1334.                 return NGX_HTTP_SSI_ERROR;
  1335.             }

  1336.             if (var.len == 0) {
  1337.                 goto invalid_variable;
  1338.             }

  1339.             key = ngx_hash_strlow(var.data, var.data, var.len);

  1340.             val = ngx_http_ssi_get_variable(r, &var, key);

  1341.             if (val == NULL) {
  1342.                 vv = ngx_http_get_variable(r, &var, key);
  1343.                 if (vv == NULL) {
  1344.                     return NGX_ERROR;
  1345.                 }

  1346.                 if (vv->not_found) {
  1347.                     continue;
  1348.                 }

  1349.                 part_data = vv->data;
  1350.                 part_len = vv->len;

  1351.             } else {
  1352.                 part_data = val->data;
  1353.                 part_len = val->len;
  1354.             }

  1355.         } else {
  1356.             part_data = &text->data[i];
  1357.             quoted = 0;

  1358.             for (p = part_data; i < text->len; i++) {
  1359.                 ch = text->data[i];

  1360.                 if (!quoted) {

  1361.                     if (ch == '\\') {
  1362.                         quoted = 1;
  1363.                         continue;
  1364.                     }

  1365.                     if (ch == '$') {
  1366.                         break;
  1367.                     }

  1368.                 } else {
  1369.                     quoted = 0;

  1370.                     if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
  1371.                         *p++ = '\\';
  1372.                     }
  1373.                 }

  1374.                 *p++ = ch;
  1375.             }

  1376.             part_len = p - part_data;
  1377.         }

  1378.         len += part_len;

  1379.         size = ngx_array_push(&lengths);
  1380.         if (size == NULL) {
  1381.             return NGX_ERROR;
  1382.         }

  1383.         *size = part_len;

  1384.         value = ngx_array_push(&values);
  1385.         if (value == NULL) {
  1386.             return NGX_ERROR;
  1387.         }

  1388.         *value = part_data;
  1389.     }

  1390.     prefix = 0;

  1391.     size = lengths.elts;
  1392.     value = values.elts;

  1393.     if (flags & NGX_HTTP_SSI_ADD_PREFIX) {
  1394.         for (i = 0; i < values.nelts; i++) {
  1395.             if (size[i] != 0) {
  1396.                 if (*value[i] != '/') {
  1397.                     for (prefix = r->uri.len; prefix; prefix--) {
  1398.                         if (r->uri.data[prefix - 1] == '/') {
  1399.                             len += prefix;
  1400.                             break;
  1401.                         }
  1402.                     }
  1403.                 }

  1404.                 break;
  1405.             }
  1406.         }
  1407.     }

  1408.     p = ngx_pnalloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));
  1409.     if (p == NULL) {
  1410.         return NGX_ERROR;
  1411.     }

  1412.     text->len = len;
  1413.     text->data = p;

  1414.     p = ngx_copy(p, r->uri.data, prefix);

  1415.     for (i = 0; i < values.nelts; i++) {
  1416.         p = ngx_copy(p, value[i], size[i]);
  1417.     }

  1418.     return NGX_OK;

  1419. invalid_variable:

  1420.     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1421.                   "invalid variable name in \"%V\"", text);

  1422.     return NGX_HTTP_SSI_ERROR;
  1423. }


  1424. static ngx_int_t
  1425. ngx_http_ssi_regex_match(ngx_http_request_t *r, ngx_str_t *pattern,
  1426.     ngx_str_t *str)
  1427. {
  1428. #if (NGX_PCRE)
  1429.     int                   rc, *captures;
  1430.     u_char               *p, errstr[NGX_MAX_CONF_ERRSTR];
  1431.     size_t                size;
  1432.     ngx_int_t             key;
  1433.     ngx_str_t            *vv, name, value;
  1434.     ngx_uint_t            i, n;
  1435.     ngx_http_ssi_ctx_t   *ctx;
  1436.     ngx_http_ssi_var_t   *var;
  1437.     ngx_regex_compile_t   rgc;

  1438.     ngx_memzero(&rgc, sizeof(ngx_regex_compile_t));

  1439.     rgc.pattern = *pattern;
  1440.     rgc.pool = r->pool;
  1441.     rgc.err.len = NGX_MAX_CONF_ERRSTR;
  1442.     rgc.err.data = errstr;

  1443.     if (ngx_regex_compile(&rgc) != NGX_OK) {
  1444.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%V", &rgc.err);
  1445.         return NGX_HTTP_SSI_ERROR;
  1446.     }

  1447.     n = (rgc.captures + 1) * 3;

  1448.     captures = ngx_palloc(r->pool, n * sizeof(int));
  1449.     if (captures == NULL) {
  1450.         return NGX_ERROR;
  1451.     }

  1452.     rc = ngx_regex_exec(rgc.regex, str, captures, n);

  1453.     if (rc < NGX_REGEX_NO_MATCHED) {
  1454.         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
  1455.                       ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
  1456.                       rc, str, pattern);
  1457.         return NGX_HTTP_SSI_ERROR;
  1458.     }

  1459.     if (rc == NGX_REGEX_NO_MATCHED) {
  1460.         return NGX_DECLINED;
  1461.     }

  1462.     ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);

  1463.     ctx->ncaptures = rc;
  1464.     ctx->captures = captures;
  1465.     ctx->captures_data = str->data;

  1466.     if (rgc.named_captures > 0) {

  1467.         if (ctx->variables == NULL) {
  1468.             ctx->variables = ngx_list_create(r->pool, 4,
  1469.                                              sizeof(ngx_http_ssi_var_t));
  1470.             if (ctx->variables == NULL) {
  1471.                 return NGX_ERROR;
  1472.             }
  1473.         }

  1474.         size = rgc.name_size;
  1475.         p = rgc.names;

  1476.         for (i = 0; i < (ngx_uint_t) rgc.named_captures; i++, p += size) {

  1477.             name.data = &p[2];
  1478.             name.len = ngx_strlen(name.data);

  1479.             n = 2 * ((p[0] << 8) + p[1]);

  1480.             value.data = &str->data[captures[n]];
  1481.             value.len = captures[n + 1] - captures[n];

  1482.             key = ngx_hash_strlow(name.data, name.data, name.len);

  1483.             vv = ngx_http_ssi_get_variable(r, &name, key);

  1484.             if (vv) {
  1485.                 *vv = value;
  1486.                 continue;
  1487.             }

  1488.             var = ngx_list_push(ctx->variables);
  1489.             if (var == NULL) {
  1490.                 return NGX_ERROR;
  1491.             }

  1492.             var->name = name;
  1493.             var->key = key;
  1494.             var->value = value;
  1495.         }
  1496.     }

  1497.     return NGX_OK;

  1498. #else

  1499.     ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
  1500.                   "the using of the regex \"%V\" in SSI requires PCRE library",
  1501.                   pattern);
  1502.     return NGX_HTTP_SSI_ERROR;

  1503. #endif
  1504. }


  1505. static ngx_int_t
  1506. ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
  1507.     ngx_str_t **params)
  1508. {
  1509.     ngx_int_t                    rc, key;
  1510.     ngx_str_t                   *uri, *file, *wait, *set, *stub, args;
  1511.     ngx_buf_t                   *b;
  1512.     ngx_uint_t                   flags, i;
  1513.     ngx_chain_t                 *cl, *tl, **ll, *out;
  1514.     ngx_http_request_t          *sr;
  1515.     ngx_http_ssi_var_t          *var;
  1516.     ngx_http_ssi_ctx_t          *mctx;
  1517.     ngx_http_ssi_block_t        *bl;
  1518.     ngx_http_post_subrequest_t  *psr;

  1519.     uri = params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];
  1520.     file = params[NGX_HTTP_SSI_INCLUDE_FILE];
  1521.     wait = params[NGX_HTTP_SSI_INCLUDE_WAIT];
  1522.     set = params[NGX_HTTP_SSI_INCLUDE_SET];
  1523.     stub = params[NGX_HTTP_SSI_INCLUDE_STUB];

  1524.     if (uri && file) {
  1525.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1526.                       "inlcusion may be either virtual=\"%V\" or file=\"%V\"",
  1527.                       uri, file);
  1528.         return NGX_HTTP_SSI_ERROR;
  1529.     }

  1530.     if (uri == NULL && file == NULL) {
  1531.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1532.                       "no parameter in \"include\" SSI command");
  1533.         return NGX_HTTP_SSI_ERROR;
  1534.     }

  1535.     if (set && stub) {
  1536.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1537.                       "\"set\" and \"stub\" cannot be used together "
  1538.                       "in \"include\" SSI command");
  1539.         return NGX_HTTP_SSI_ERROR;
  1540.     }

  1541.     if (wait) {
  1542.         if (uri == NULL) {
  1543.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1544.                           "\"wait\" cannot be used with file=\"%V\"", file);
  1545.             return NGX_HTTP_SSI_ERROR;
  1546.         }

  1547.         if (wait->len == 2
  1548.             && ngx_strncasecmp(wait->data, (u_char *) "no", 2) == 0)
  1549.         {
  1550.             wait = NULL;

  1551.         } else if (wait->len != 3
  1552.                    || ngx_strncasecmp(wait->data, (u_char *) "yes", 3) != 0)
  1553.         {
  1554.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1555.                           "invalid value \"%V\" in the \"wait\" parameter",
  1556.                           wait);
  1557.             return NGX_HTTP_SSI_ERROR;
  1558.         }
  1559.     }

  1560.     if (uri == NULL) {
  1561.         uri = file;
  1562.         wait = (ngx_str_t *) -1;
  1563.     }

  1564.     rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX);

  1565.     if (rc != NGX_OK) {
  1566.         return rc;
  1567.     }

  1568.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1569.                    "ssi include: \"%V\"", uri);

  1570.     ngx_str_null(&args);
  1571.     flags = NGX_HTTP_LOG_UNSAFE;

  1572.     if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
  1573.         return NGX_HTTP_SSI_ERROR;
  1574.     }

  1575.     psr = NULL;

  1576.     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);

  1577.     if (stub) {
  1578.         if (mctx->blocks) {
  1579.             bl = mctx->blocks->elts;
  1580.             for (i = 0; i < mctx->blocks->nelts; i++) {
  1581.                 if (stub->len == bl[i].name.len
  1582.                     && ngx_strncmp(stub->data, bl[i].name.data, stub->len) == 0)
  1583.                 {
  1584.                     goto found;
  1585.                 }
  1586.             }
  1587.         }

  1588.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1589.                       "\"stub\"=\"%V\" for \"include\" not found", stub);
  1590.         return NGX_HTTP_SSI_ERROR;

  1591.     found:

  1592.         psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
  1593.         if (psr == NULL) {
  1594.             return NGX_ERROR;
  1595.         }

  1596.         psr->handler = ngx_http_ssi_stub_output;

  1597.         if (bl[i].count++) {

  1598.             out = NULL;
  1599.             ll = &out;

  1600.             for (tl = bl[i].bufs; tl; tl = tl->next) {

  1601.                 if (ctx->free) {
  1602.                     cl = ctx->free;
  1603.                     ctx->free = ctx->free->next;
  1604.                     b = cl->buf;

  1605.                 } else {
  1606.                     b = ngx_alloc_buf(r->pool);
  1607.                     if (b == NULL) {
  1608.                         return NGX_ERROR;
  1609.                     }

  1610.                     cl = ngx_alloc_chain_link(r->pool);
  1611.                     if (cl == NULL) {
  1612.                         return NGX_ERROR;
  1613.                     }

  1614.                     cl->buf = b;
  1615.                 }

  1616.                 ngx_memcpy(b, tl->buf, sizeof(ngx_buf_t));

  1617.                 b->pos = b->start;

  1618.                 *ll = cl;
  1619.                 cl->next = NULL;
  1620.                 ll = &cl->next;
  1621.             }

  1622.             psr->data = out;

  1623.         } else {
  1624.             psr->data = bl[i].bufs;
  1625.         }
  1626.     }

  1627.     if (wait) {
  1628.         flags |= NGX_HTTP_SUBREQUEST_WAITED;
  1629.     }

  1630.     if (set) {
  1631.         key = ngx_hash_strlow(set->data, set->data, set->len);

  1632.         psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
  1633.         if (psr == NULL) {
  1634.             return NGX_ERROR;
  1635.         }

  1636.         psr->handler = ngx_http_ssi_set_variable;
  1637.         psr->data = ngx_http_ssi_get_variable(r, set, key);

  1638.         if (psr->data == NULL) {

  1639.             if (mctx->variables == NULL) {
  1640.                 mctx->variables = ngx_list_create(r->pool, 4,
  1641.                                                   sizeof(ngx_http_ssi_var_t));
  1642.                 if (mctx->variables == NULL) {
  1643.                     return NGX_ERROR;
  1644.                 }
  1645.             }

  1646.             var = ngx_list_push(mctx->variables);
  1647.             if (var == NULL) {
  1648.                 return NGX_ERROR;
  1649.             }

  1650.             var->name = *set;
  1651.             var->key = key;
  1652.             var->value = ngx_http_ssi_null_string;
  1653.             psr->data = &var->value;
  1654.         }

  1655.         flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;
  1656.     }

  1657.     if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) {
  1658.         return NGX_HTTP_SSI_ERROR;
  1659.     }

  1660.     if (wait == NULL && set == NULL) {
  1661.         return NGX_OK;
  1662.     }

  1663.     if (ctx->wait == NULL) {
  1664.         ctx->wait = sr;

  1665.         return NGX_AGAIN;

  1666.     } else {
  1667.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1668.                       "can only wait for one subrequest at a time");
  1669.     }

  1670.     return NGX_OK;
  1671. }


  1672. static ngx_int_t
  1673. ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data, ngx_int_t rc)
  1674. {
  1675.     ngx_chain_t  *out;

  1676.     if (rc == NGX_ERROR || r->connection->error || r->request_output) {
  1677.         return rc;
  1678.     }

  1679.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1680.                    "ssi stub output: \"%V?%V\"", &r->uri, &r->args);

  1681.     out = data;

  1682.     if (!r->header_sent) {
  1683.         r->headers_out.content_type_len =
  1684.                                       r->parent->headers_out.content_type_len;
  1685.         r->headers_out.content_type = r->parent->headers_out.content_type;

  1686.         if (ngx_http_send_header(r) == NGX_ERROR) {
  1687.             return NGX_ERROR;
  1688.         }
  1689.     }

  1690.     return ngx_http_output_filter(r, out);
  1691. }


  1692. static ngx_int_t
  1693. ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data, ngx_int_t rc)
  1694. {
  1695.     ngx_str_t  *value = data;

  1696.     if (r->upstream) {
  1697.         value->len = r->upstream->buffer.last - r->upstream->buffer.pos;
  1698.         value->data = r->upstream->buffer.pos;
  1699.     }

  1700.     return rc;
  1701. }


  1702. static ngx_int_t
  1703. ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
  1704.     ngx_str_t **params)
  1705. {
  1706.     u_char                     *p;
  1707.     uintptr_t                   len;
  1708.     ngx_int_t                   key;
  1709.     ngx_buf_t                  *b;
  1710.     ngx_str_t                  *var, *value, *enc, text;
  1711.     ngx_chain_t                *cl;
  1712.     ngx_http_variable_value_t  *vv;

  1713.     var = params[NGX_HTTP_SSI_ECHO_VAR];

  1714.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1715.                    "ssi echo \"%V\"", var);

  1716.     key = ngx_hash_strlow(var->data, var->data, var->len);

  1717.     value = ngx_http_ssi_get_variable(r, var, key);

  1718.     if (value == NULL) {
  1719.         vv = ngx_http_get_variable(r, var, key);

  1720.         if (vv == NULL) {
  1721.             return NGX_HTTP_SSI_ERROR;
  1722.         }

  1723.         if (!vv->not_found) {
  1724.             text.data = vv->data;
  1725.             text.len = vv->len;
  1726.             value = &text;
  1727.         }
  1728.     }

  1729.     if (value == NULL) {
  1730.         value = params[NGX_HTTP_SSI_ECHO_DEFAULT];

  1731.         if (value == NULL) {
  1732.             value = &ngx_http_ssi_none;

  1733.         } else if (value->len == 0) {
  1734.             return NGX_OK;
  1735.         }

  1736.     } else {
  1737.         if (value->len == 0) {
  1738.             return NGX_OK;
  1739.         }
  1740.     }

  1741.     enc = params[NGX_HTTP_SSI_ECHO_ENCODING];

  1742.     if (enc) {
  1743.         if (enc->len == 4 && ngx_strncmp(enc->data, "none", 4) == 0) {

  1744.             ctx->encoding = NGX_HTTP_SSI_NO_ENCODING;

  1745.         } else if (enc->len == 3 && ngx_strncmp(enc->data, "url", 3) == 0) {

  1746.             ctx->encoding = NGX_HTTP_SSI_URL_ENCODING;

  1747.         } else if (enc->len == 6 && ngx_strncmp(enc->data, "entity", 6) == 0) {

  1748.             ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;

  1749.         } else {
  1750.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1751.                           "unknown encoding \"%V\" in the \"echo\" command",
  1752.                           enc);
  1753.         }
  1754.     }

  1755.     p = value->data;

  1756.     switch (ctx->encoding) {

  1757.     case NGX_HTTP_SSI_URL_ENCODING:
  1758.         len = 2 * ngx_escape_uri(NULL, value->data, value->len,
  1759.                                  NGX_ESCAPE_HTML);

  1760.         if (len) {
  1761.             p = ngx_pnalloc(r->pool, value->len + len);
  1762.             if (p == NULL) {
  1763.                 return NGX_HTTP_SSI_ERROR;
  1764.             }

  1765.             (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML);
  1766.         }

  1767.         len += value->len;
  1768.         break;

  1769.     case NGX_HTTP_SSI_ENTITY_ENCODING:
  1770.         len = ngx_escape_html(NULL, value->data, value->len);

  1771.         if (len) {
  1772.             p = ngx_pnalloc(r->pool, value->len + len);
  1773.             if (p == NULL) {
  1774.                 return NGX_HTTP_SSI_ERROR;
  1775.             }

  1776.             (void) ngx_escape_html(p, value->data, value->len);
  1777.         }

  1778.         len += value->len;
  1779.         break;

  1780.     default: /* NGX_HTTP_SSI_NO_ENCODING */
  1781.         len = value->len;
  1782.         break;
  1783.     }

  1784.     b = ngx_calloc_buf(r->pool);
  1785.     if (b == NULL) {
  1786.         return NGX_HTTP_SSI_ERROR;
  1787.     }

  1788.     cl = ngx_alloc_chain_link(r->pool);
  1789.     if (cl == NULL) {
  1790.         return NGX_HTTP_SSI_ERROR;
  1791.     }

  1792.     b->memory = 1;
  1793.     b->pos = p;
  1794.     b->last = p + len;

  1795.     cl->buf = b;
  1796.     cl->next = NULL;
  1797.     *ctx->last_out = cl;
  1798.     ctx->last_out = &cl->next;

  1799.     return NGX_OK;
  1800. }


  1801. static ngx_int_t
  1802. ngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
  1803.     ngx_str_t **params)
  1804. {
  1805.     ngx_str_t  *value;

  1806.     value = params[NGX_HTTP_SSI_CONFIG_TIMEFMT];

  1807.     if (value) {
  1808.         ctx->timefmt.len = value->len;
  1809.         ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1);
  1810.         if (ctx->timefmt.data == NULL) {
  1811.             return NGX_HTTP_SSI_ERROR;
  1812.         }

  1813.         ngx_cpystrn(ctx->timefmt.data, value->data, value->len + 1);
  1814.     }

  1815.     value = params[NGX_HTTP_SSI_CONFIG_ERRMSG];

  1816.     if (value) {
  1817.         ctx->errmsg = *value;
  1818.     }

  1819.     return NGX_OK;
  1820. }


  1821. static ngx_int_t
  1822. ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
  1823.     ngx_str_t **params)
  1824. {
  1825.     ngx_int_t            key, rc;
  1826.     ngx_str_t           *name, *value, *vv;
  1827.     ngx_http_ssi_var_t  *var;
  1828.     ngx_http_ssi_ctx_t  *mctx;

  1829.     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);

  1830.     if (mctx->variables == NULL) {
  1831.         mctx->variables = ngx_list_create(r->pool, 4,
  1832.                                           sizeof(ngx_http_ssi_var_t));
  1833.         if (mctx->variables == NULL) {
  1834.             return NGX_ERROR;
  1835.         }
  1836.     }

  1837.     name = params[NGX_HTTP_SSI_SET_VAR];
  1838.     value = params[NGX_HTTP_SSI_SET_VALUE];

  1839.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1840.                    "ssi set \"%V\" \"%V\"", name, value);

  1841.     rc = ngx_http_ssi_evaluate_string(r, ctx, value, 0);

  1842.     if (rc != NGX_OK) {
  1843.         return rc;
  1844.     }

  1845.     key = ngx_hash_strlow(name->data, name->data, name->len);

  1846.     vv = ngx_http_ssi_get_variable(r, name, key);

  1847.     if (vv) {
  1848.         *vv = *value;
  1849.         return NGX_OK;
  1850.     }

  1851.     var = ngx_list_push(mctx->variables);
  1852.     if (var == NULL) {
  1853.         return NGX_ERROR;
  1854.     }

  1855.     var->name = *name;
  1856.     var->key = key;
  1857.     var->value = *value;

  1858.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1859.                    "set: \"%V\"=\"%V\"", name, value);

  1860.     return NGX_OK;
  1861. }


  1862. static ngx_int_t
  1863. ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
  1864.     ngx_str_t **params)
  1865. {
  1866.     u_char       *p, *last;
  1867.     ngx_str_t    *expr, left, right;
  1868.     ngx_int_t     rc;
  1869.     ngx_uint_t    negative, noregex, flags;

  1870.     if (ctx->command.len == 2) {
  1871.         if (ctx->conditional) {
  1872.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1873.                           "the \"if\" command inside the \"if\" command");
  1874.             return NGX_HTTP_SSI_ERROR;
  1875.         }
  1876.     }

  1877.     if (ctx->output_chosen) {
  1878.         ctx->output = 0;
  1879.         return NGX_OK;
  1880.     }

  1881.     expr = params[NGX_HTTP_SSI_IF_EXPR];

  1882.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1883.                    "ssi if expr=\"%V\"", expr);

  1884.     left.data = expr->data;
  1885.     last = expr->data + expr->len;

  1886.     for (p = left.data; p < last; p++) {
  1887.         if (*p >= 'A' && *p <= 'Z') {
  1888.             *p |= 0x20;
  1889.             continue;
  1890.         }

  1891.         if ((*p >= 'a' && *p <= 'z')
  1892.              || (*p >= '0' && *p <= '9')
  1893.              || *p == '$' || *p == '{' || *p == '}' || *p == '_'
  1894.              || *p == '"' || *p == '\'')
  1895.         {
  1896.             continue;
  1897.         }

  1898.         break;
  1899.     }

  1900.     left.len = p - left.data;

  1901.     while (p < last && *p == ' ') {
  1902.         p++;
  1903.     }

  1904.     flags = 0;

  1905.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1906.                    "left: \"%V\"", &left);

  1907.     rc = ngx_http_ssi_evaluate_string(r, ctx, &left, flags);

  1908.     if (rc != NGX_OK) {
  1909.         return rc;
  1910.     }

  1911.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1912.                    "evaluted left: \"%V\"", &left);

  1913.     if (p == last) {
  1914.         if (left.len) {
  1915.             ctx->output = 1;
  1916.             ctx->output_chosen = 1;

  1917.         } else {
  1918.             ctx->output = 0;
  1919.         }

  1920.         ctx->conditional = NGX_HTTP_SSI_COND_IF;

  1921.         return NGX_OK;
  1922.     }

  1923.     if (p < last && *p == '=') {
  1924.         negative = 0;
  1925.         p++;

  1926.     } else if (p + 1 < last && *p == '!' && *(p + 1) == '=') {
  1927.         negative = 1;
  1928.         p += 2;

  1929.     } else {
  1930.         goto invalid_expression;
  1931.     }

  1932.     while (p < last && *p == ' ') {
  1933.         p++;
  1934.     }

  1935.     if (p < last - 1 && *p == '/') {
  1936.         if (*(last - 1) != '/') {
  1937.             goto invalid_expression;
  1938.         }

  1939.         noregex = 0;
  1940.         flags = NGX_HTTP_SSI_ADD_ZERO;
  1941.         last--;
  1942.         p++;

  1943.     } else {
  1944.         noregex = 1;
  1945.         flags = 0;

  1946.         if (p < last - 1 && p[0] == '\\' && p[1] == '/') {
  1947.             p++;
  1948.         }
  1949.     }

  1950.     right.len = last - p;
  1951.     right.data = p;

  1952.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1953.                    "right: \"%V\"", &right);

  1954.     rc = ngx_http_ssi_evaluate_string(r, ctx, &right, flags);

  1955.     if (rc != NGX_OK) {
  1956.         return rc;
  1957.     }

  1958.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1959.                    "evaluted right: \"%V\"", &right);

  1960.     if (noregex) {
  1961.         if (left.len != right.len) {
  1962.             rc = -1;

  1963.         } else {
  1964.             rc = ngx_strncmp(left.data, right.data, right.len);
  1965.         }

  1966.     } else {
  1967.         right.data[right.len] = '\0';

  1968.         rc = ngx_http_ssi_regex_match(r, &right, &left);

  1969.         if (rc == NGX_OK) {
  1970.             rc = 0;
  1971.         } else if (rc == NGX_DECLINED) {
  1972.             rc = -1;
  1973.         } else {
  1974.             return rc;
  1975.         }
  1976.     }

  1977.     if ((rc == 0 && !negative) || (rc != 0 && negative)) {
  1978.         ctx->output = 1;
  1979.         ctx->output_chosen = 1;

  1980.     } else {
  1981.         ctx->output = 0;
  1982.     }

  1983.     ctx->conditional = NGX_HTTP_SSI_COND_IF;

  1984.     return NGX_OK;

  1985. invalid_expression:

  1986.     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1987.                   "invalid expression in \"%V\"", expr);

  1988.     return NGX_HTTP_SSI_ERROR;
  1989. }


  1990. static ngx_int_t
  1991. ngx_http_ssi_else(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
  1992.     ngx_str_t **params)
  1993. {
  1994.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1995.                    "ssi else");

  1996.     if (ctx->output_chosen) {
  1997.         ctx->output = 0;
  1998.     } else {
  1999.         ctx->output = 1;
  2000.     }

  2001.     ctx->conditional = NGX_HTTP_SSI_COND_ELSE;

  2002.     return NGX_OK;
  2003. }


  2004. static ngx_int_t
  2005. ngx_http_ssi_endif(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
  2006.     ngx_str_t **params)
  2007. {
  2008.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  2009.                    "ssi endif");

  2010.     ctx->output = 1;
  2011.     ctx->output_chosen = 0;
  2012.     ctx->conditional = 0;

  2013.     return NGX_OK;
  2014. }


  2015. static ngx_int_t
  2016. ngx_http_ssi_block(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
  2017.     ngx_str_t **params)
  2018. {
  2019.     ngx_http_ssi_ctx_t    *mctx;
  2020.     ngx_http_ssi_block_t  *bl;

  2021.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  2022.                    "ssi block");

  2023.     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);

  2024.     if (mctx->blocks == NULL) {
  2025.         mctx->blocks = ngx_array_create(r->pool, 4,
  2026.                                         sizeof(ngx_http_ssi_block_t));
  2027.         if (mctx->blocks == NULL) {
  2028.             return NGX_HTTP_SSI_ERROR;
  2029.         }
  2030.     }

  2031.     bl = ngx_array_push(mctx->blocks);
  2032.     if (bl == NULL) {
  2033.         return NGX_HTTP_SSI_ERROR;
  2034.     }

  2035.     bl->name = *params[NGX_HTTP_SSI_BLOCK_NAME];
  2036.     bl->bufs = NULL;
  2037.     bl->count = 0;

  2038.     ctx->output = 0;
  2039.     ctx->block = 1;

  2040.     return NGX_OK;
  2041. }


  2042. static ngx_int_t
  2043. ngx_http_ssi_endblock(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
  2044.     ngx_str_t **params)
  2045. {
  2046.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  2047.                    "ssi endblock");

  2048.     ctx->output = 1;
  2049.     ctx->block = 0;

  2050.     return NGX_OK;
  2051. }


  2052. static ngx_int_t
  2053. ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
  2054.     ngx_http_variable_value_t *v, uintptr_t gmt)
  2055. {
  2056.     ngx_http_ssi_ctx_t  *ctx;
  2057.     ngx_time_t          *tp;
  2058.     ngx_str_t           *timefmt;
  2059.     struct tm            tm;
  2060.     char                 buf[NGX_HTTP_SSI_DATE_LEN];

  2061.     v->valid = 1;
  2062.     v->no_cacheable = 0;
  2063.     v->not_found = 0;

  2064.     tp = ngx_timeofday();

  2065.     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);

  2066.     timefmt = ctx ? &ctx->timefmt : &ngx_http_ssi_timefmt;

  2067.     if (timefmt->len == sizeof("%s") - 1
  2068.         && timefmt->data[0] == '%' && timefmt->data[1] == 's')
  2069.     {
  2070.         v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);
  2071.         if (v->data == NULL) {
  2072.             return NGX_ERROR;
  2073.         }

  2074.         v->len = ngx_sprintf(v->data, "%T", tp->sec) - v->data;

  2075.         return NGX_OK;
  2076.     }

  2077.     if (gmt) {
  2078.         ngx_libc_gmtime(tp->sec, &tm);
  2079.     } else {
  2080.         ngx_libc_localtime(tp->sec, &tm);
  2081.     }

  2082.     v->len = strftime(buf, NGX_HTTP_SSI_DATE_LEN,
  2083.                       (char *) timefmt->data, &tm);
  2084.     if (v->len == 0) {
  2085.         return NGX_ERROR;
  2086.     }

  2087.     v->data = ngx_pnalloc(r->pool, v->len);
  2088.     if (v->data == NULL) {
  2089.         return NGX_ERROR;
  2090.     }

  2091.     ngx_memcpy(v->data, buf, v->len);

  2092.     return NGX_OK;
  2093. }


  2094. static ngx_int_t
  2095. ngx_http_ssi_preconfiguration(ngx_conf_t *cf)
  2096. {
  2097.     ngx_int_t                  rc;
  2098.     ngx_http_variable_t       *var, *v;
  2099.     ngx_http_ssi_command_t    *cmd;
  2100.     ngx_http_ssi_main_conf_t  *smcf;

  2101.     for (v = ngx_http_ssi_vars; v->name.len; v++) {
  2102.         var = ngx_http_add_variable(cf, &v->name, v->flags);
  2103.         if (var == NULL) {
  2104.             return NGX_ERROR;
  2105.         }

  2106.         var->get_handler = v->get_handler;
  2107.         var->data = v->data;
  2108.     }

  2109.     smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);

  2110.     for (cmd = ngx_http_ssi_commands; cmd->name.len; cmd++) {
  2111.         rc = ngx_hash_add_key(&smcf->commands, &cmd->name, cmd,
  2112.                               NGX_HASH_READONLY_KEY);

  2113.         if (rc == NGX_OK) {
  2114.             continue;
  2115.         }

  2116.         if (rc == NGX_BUSY) {
  2117.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  2118.                                "conflicting SSI command \"%V\"", &cmd->name);
  2119.         }

  2120.         return NGX_ERROR;
  2121.     }

  2122.     return NGX_OK;
  2123. }


  2124. static void *
  2125. ngx_http_ssi_create_main_conf(ngx_conf_t *cf)
  2126. {
  2127.     ngx_http_ssi_main_conf_t  *smcf;

  2128.     smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t));
  2129.     if (smcf == NULL) {
  2130.         return NULL;
  2131.     }

  2132.     smcf->commands.pool = cf->pool;
  2133.     smcf->commands.temp_pool = cf->temp_pool;

  2134.     if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) {
  2135.         return NULL;
  2136.     }

  2137.     return smcf;
  2138. }


  2139. static char *
  2140. ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf)
  2141. {
  2142.     ngx_http_ssi_main_conf_t *smcf = conf;

  2143.     ngx_hash_init_t  hash;

  2144.     hash.hash = &smcf->hash;
  2145.     hash.key = ngx_hash_key;
  2146.     hash.max_size = 1024;
  2147.     hash.bucket_size = ngx_cacheline_size;
  2148.     hash.name = "ssi_command_hash";
  2149.     hash.pool = cf->pool;
  2150.     hash.temp_pool = NULL;

  2151.     if (ngx_hash_init(&hash, smcf->commands.keys.elts,
  2152.                       smcf->commands.keys.nelts)
  2153.         != NGX_OK)
  2154.     {
  2155.         return NGX_CONF_ERROR;
  2156.     }

  2157.     return NGX_CONF_OK;
  2158. }


  2159. static void *
  2160. ngx_http_ssi_create_loc_conf(ngx_conf_t *cf)
  2161. {
  2162.     ngx_http_ssi_loc_conf_t  *slcf;

  2163.     slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));
  2164.     if (slcf == NULL) {
  2165.         return NULL;
  2166.     }

  2167.     /*
  2168.      * set by ngx_pcalloc():
  2169.      *
  2170.      *     conf->types = { NULL };
  2171.      *     conf->types_keys = NULL;
  2172.      */

  2173.     slcf->enable = NGX_CONF_UNSET;
  2174.     slcf->silent_errors = NGX_CONF_UNSET;
  2175.     slcf->ignore_recycled_buffers = NGX_CONF_UNSET;
  2176.     slcf->last_modified = NGX_CONF_UNSET;

  2177.     slcf->min_file_chunk = NGX_CONF_UNSET_SIZE;
  2178.     slcf->value_len = NGX_CONF_UNSET_SIZE;

  2179.     return slcf;
  2180. }


  2181. static char *
  2182. ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  2183. {
  2184.     ngx_http_ssi_loc_conf_t *prev = parent;
  2185.     ngx_http_ssi_loc_conf_t *conf = child;

  2186.     ngx_conf_merge_value(conf->enable, prev->enable, 0);
  2187.     ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);
  2188.     ngx_conf_merge_value(conf->ignore_recycled_buffers,
  2189.                          prev->ignore_recycled_buffers, 0);
  2190.     ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);

  2191.     ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);
  2192.     ngx_conf_merge_size_value(conf->value_len, prev->value_len, 255);

  2193.     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
  2194.                              &prev->types_keys, &prev->types,
  2195.                              ngx_http_html_default_types)
  2196.         != NGX_OK)
  2197.     {
  2198.         return NGX_CONF_ERROR;
  2199.     }

  2200.     return NGX_CONF_OK;
  2201. }


  2202. static ngx_int_t
  2203. ngx_http_ssi_filter_init(ngx_conf_t *cf)
  2204. {
  2205.     ngx_http_next_header_filter = ngx_http_top_header_filter;
  2206.     ngx_http_top_header_filter = ngx_http_ssi_header_filter;

  2207.     ngx_http_next_body_filter = ngx_http_top_body_filter;
  2208.     ngx_http_top_body_filter = ngx_http_ssi_body_filter;

  2209.     return NGX_OK;
  2210. }