1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_http.h>
11
12
13typedef struct {
14    ngx_array_t  *codes;        /* uintptr_t */
15
16    ngx_uint_t    stack_size;
17
18    ngx_flag_t    log;
19    ngx_flag_t    uninitialized_variable_warn;
20} ngx_http_rewrite_loc_conf_t;
21
22
23static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf);
24static char *ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf,
25    void *parent, void *child);
26static ngx_int_t ngx_http_rewrite_init(ngx_conf_t *cf);
27static char *ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
28static char *ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd,
29    void *conf);
30static char *ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd,
31    void *conf);
32static char *ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd,
33    void *conf);
34static char * ngx_http_rewrite_if_condition(ngx_conf_t *cf,
35    ngx_http_rewrite_loc_conf_t *lcf);
36static char *ngx_http_rewrite_variable(ngx_conf_t *cf,
37    ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);
38static char *ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd,
39    void *conf);
40static char * ngx_http_rewrite_value(ngx_conf_t *cf,
41    ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);
42
43
44static ngx_command_t  ngx_http_rewrite_commands[] = {
45
46    { ngx_string("rewrite"),
47      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
48                       |NGX_CONF_TAKE23,
49      ngx_http_rewrite,
50      NGX_HTTP_LOC_CONF_OFFSET,
51      0,
52      NULL },
53
54    { ngx_string("return"),
55      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
56                       |NGX_CONF_TAKE12,
57      ngx_http_rewrite_return,
58      NGX_HTTP_LOC_CONF_OFFSET,
59      0,
60      NULL },
61
62    { ngx_string("break"),
63      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
64                       |NGX_CONF_NOARGS,
65      ngx_http_rewrite_break,
66      NGX_HTTP_LOC_CONF_OFFSET,
67      0,
68      NULL },
69
70    { ngx_string("if"),
71      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,
72      ngx_http_rewrite_if,
73      NGX_HTTP_LOC_CONF_OFFSET,
74      0,
75      NULL },
76
77    { ngx_string("set"),
78      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
79                       |NGX_CONF_TAKE2,
80      ngx_http_rewrite_set,
81      NGX_HTTP_LOC_CONF_OFFSET,
82      0,
83      NULL },
84
85    { ngx_string("rewrite_log"),
86      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF
87                        |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
88      ngx_conf_set_flag_slot,
89      NGX_HTTP_LOC_CONF_OFFSET,
90      offsetof(ngx_http_rewrite_loc_conf_t, log),
91      NULL },
92
93    { ngx_string("uninitialized_variable_warn"),
94      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF
95                        |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
96      ngx_conf_set_flag_slot,
97      NGX_HTTP_LOC_CONF_OFFSET,
98      offsetof(ngx_http_rewrite_loc_conf_t, uninitialized_variable_warn),
99      NULL },
100
101      ngx_null_command
102};
103
104
105static ngx_http_module_t  ngx_http_rewrite_module_ctx = {
106    NULL,                                  /* preconfiguration */
107    ngx_http_rewrite_init,                 /* postconfiguration */
108
109    NULL,                                  /* create main configuration */
110    NULL,                                  /* init main configuration */
111
112    NULL,                                  /* create server configuration */
113    NULL,                                  /* merge server configuration */
114
115    ngx_http_rewrite_create_loc_conf,      /* create location configuration */
116    ngx_http_rewrite_merge_loc_conf        /* merge location configuration */
117};
118
119
120ngx_module_t  ngx_http_rewrite_module = {
121    NGX_MODULE_V1,
122    &ngx_http_rewrite_module_ctx,          /* module context */
123    ngx_http_rewrite_commands,             /* module directives */
124    NGX_HTTP_MODULE,                       /* module type */
125    NULL,                                  /* init master */
126    NULL,                                  /* init module */
127    NULL,                                  /* init process */
128    NULL,                                  /* init thread */
129    NULL,                                  /* exit thread */
130    NULL,                                  /* exit process */
131    NULL,                                  /* exit master */
132    NGX_MODULE_V1_PADDING
133};
134
135
136static ngx_int_t
137ngx_http_rewrite_handler(ngx_http_request_t *r)
138{
139    ngx_int_t                     index;
140    ngx_http_script_code_pt       code;
141    ngx_http_script_engine_t     *e;
142    ngx_http_core_srv_conf_t     *cscf;
143    ngx_http_core_main_conf_t    *cmcf;
144    ngx_http_rewrite_loc_conf_t  *rlcf;
145
146    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
147    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
148    index = cmcf->phase_engine.location_rewrite_index;
149
150    if (r->phase_handler == index && r->loc_conf == cscf->ctx->loc_conf) {
151        /* skipping location rewrite phase for server null location */
152        return NGX_DECLINED;
153    }
154
155    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
156
157    if (rlcf->codes == NULL) {
158        return NGX_DECLINED;
159    }
160
161    e = ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t));
162    if (e == NULL) {
163        return NGX_HTTP_INTERNAL_SERVER_ERROR;
164    }
165
166    e->sp = ngx_pcalloc(r->pool,
167                        rlcf->stack_size * sizeof(ngx_http_variable_value_t));
168    if (e->sp == NULL) {
169        return NGX_HTTP_INTERNAL_SERVER_ERROR;
170    }
171
172    e->ip = rlcf->codes->elts;
173    e->request = r;
174    e->quote = 1;
175    e->log = rlcf->log;
176    e->status = NGX_DECLINED;
177
178    while (*(uintptr_t *) e->ip) {
179        code = *(ngx_http_script_code_pt *) e->ip;
180        code(e);
181    }
182
183    if (e->status < NGX_HTTP_BAD_REQUEST) {
184        return e->status;
185    }
186
187    if (r->err_status == 0) {
188        return e->status;
189    }
190
191    return r->err_status;
192}
193
194
195static ngx_int_t
196ngx_http_rewrite_var(ngx_http_request_t *r, ngx_http_variable_value_t *v,
197    uintptr_t data)
198{
199    ngx_http_variable_t          *var;
200    ngx_http_core_main_conf_t    *cmcf;
201    ngx_http_rewrite_loc_conf_t  *rlcf;
202
203    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
204
205    if (rlcf->uninitialized_variable_warn == 0) {
206        *v = ngx_http_variable_null_value;
207        return NGX_OK;
208    }
209
210    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
211
212    var = cmcf->variables.elts;
213
214    /*
215     * the ngx_http_rewrite_module sets variables directly in r->variables,
216     * and they should be handled by ngx_http_get_indexed_variable(),
217     * so the handler is called only if the variable is not initialized
218     */
219
220    ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
221                  "using uninitialized \"%V\" variable", &var[data].name);
222
223    *v = ngx_http_variable_null_value;
224
225    return NGX_OK;
226}
227
228
229static void *
230ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf)
231{
232    ngx_http_rewrite_loc_conf_t  *conf;
233
234    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t));
235    if (conf == NULL) {
236        return NULL;
237    }
238
239    conf->stack_size = NGX_CONF_UNSET_UINT;
240    conf->log = NGX_CONF_UNSET;
241    conf->uninitialized_variable_warn = NGX_CONF_UNSET;
242
243    return conf;
244}
245
246
247static char *
248ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
249{
250    ngx_http_rewrite_loc_conf_t *prev = parent;
251    ngx_http_rewrite_loc_conf_t *conf = child;
252
253    uintptr_t  *code;
254
255    ngx_conf_merge_value(conf->log, prev->log, 0);
256    ngx_conf_merge_value(conf->uninitialized_variable_warn,
257                         prev->uninitialized_variable_warn, 1);
258    ngx_conf_merge_uint_value(conf->stack_size, prev->stack_size, 10);
259
260    if (conf->codes == NULL) {
261        return NGX_CONF_OK;
262    }
263
264    if (conf->codes == prev->codes) {
265        return NGX_CONF_OK;
266    }
267
268    code = ngx_array_push_n(conf->codes, sizeof(uintptr_t));
269    if (code == NULL) {
270        return NGX_CONF_ERROR;
271    }
272
273    *code = (uintptr_t) NULL;
274
275    return NGX_CONF_OK;
276}
277
278
279static ngx_int_t
280ngx_http_rewrite_init(ngx_conf_t *cf)
281{
282    ngx_http_handler_pt        *h;
283    ngx_http_core_main_conf_t  *cmcf;
284
285    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
286
287    h = ngx_array_push(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers);
288    if (h == NULL) {
289        return NGX_ERROR;
290    }
291
292    *h = ngx_http_rewrite_handler;
293
294    h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);
295    if (h == NULL) {
296        return NGX_ERROR;
297    }
298
299    *h = ngx_http_rewrite_handler;
300
301    return NGX_OK;
302}
303
304
305static char *
306ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
307{
308    ngx_http_rewrite_loc_conf_t  *lcf = conf;
309
310    ngx_str_t                         *value;
311    ngx_uint_t                         last;
312    ngx_regex_compile_t                rc;
313    ngx_http_script_code_pt           *code;
314    ngx_http_script_compile_t          sc;
315    ngx_http_script_regex_code_t      *regex;
316    ngx_http_script_regex_end_code_t  *regex_end;
317    u_char                             errstr[NGX_MAX_CONF_ERRSTR];
318
319    regex = ngx_http_script_start_code(cf->pool, &lcf->codes,
320                                       sizeof(ngx_http_script_regex_code_t));
321    if (regex == NULL) {
322        return NGX_CONF_ERROR;
323    }
324
325    ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));
326
327    value = cf->args->elts;
328
329    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
330
331    rc.pattern = value[1];
332    rc.err.len = NGX_MAX_CONF_ERRSTR;
333    rc.err.data = errstr;
334
335    /* TODO: NGX_REGEX_CASELESS */
336
337    regex->regex = ngx_http_regex_compile(cf, &rc);
338    if (regex->regex == NULL) {
339        return NGX_CONF_ERROR;
340    }
341
342    regex->code = ngx_http_script_regex_start_code;
343    regex->uri = 1;
344    regex->name = value[1];
345
346    if (value[2].data[value[2].len - 1] == '?') {
347
348        /* the last "?" drops the original arguments */
349        value[2].len--;
350
351    } else {
352        regex->add_args = 1;
353    }
354
355    last = 0;
356
357    if (ngx_strncmp(value[2].data, "http://", sizeof("http://") - 1) == 0
358        || ngx_strncmp(value[2].data, "https://", sizeof("https://") - 1) == 0
359        || ngx_strncmp(value[2].data, "$scheme", sizeof("$scheme") - 1) == 0)
360    {
361        regex->status = NGX_HTTP_MOVED_TEMPORARILY;
362        regex->redirect = 1;
363        last = 1;
364    }
365
366    if (cf->args->nelts == 4) {
367        if (ngx_strcmp(value[3].data, "last") == 0) {
368            last = 1;
369
370        } else if (ngx_strcmp(value[3].data, "break") == 0) {
371            regex->break_cycle = 1;
372            last = 1;
373
374        } else if (ngx_strcmp(value[3].data, "redirect") == 0) {
375            regex->status = NGX_HTTP_MOVED_TEMPORARILY;
376            regex->redirect = 1;
377            last = 1;
378
379        } else if (ngx_strcmp(value[3].data, "permanent") == 0) {
380            regex->status = NGX_HTTP_MOVED_PERMANENTLY;
381            regex->redirect = 1;
382            last = 1;
383
384        } else {
385            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
386                               "invalid parameter \"%V\"", &value[3]);
387            return NGX_CONF_ERROR;
388        }
389    }
390
391    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
392
393    sc.cf = cf;
394    sc.source = &value[2];
395    sc.lengths = &regex->lengths;
396    sc.values = &lcf->codes;
397    sc.variables = ngx_http_script_variables_count(&value[2]);
398    sc.main = regex;
399    sc.complete_lengths = 1;
400    sc.compile_args = !regex->redirect;
401
402    if (ngx_http_script_compile(&sc) != NGX_OK) {
403        return NGX_CONF_ERROR;
404    }
405
406    regex = sc.main;
407
408    regex->size = sc.size;
409    regex->args = sc.args;
410
411    if (sc.variables == 0 && !sc.dup_capture) {
412        regex->lengths = NULL;
413    }
414
415    regex_end = ngx_http_script_add_code(lcf->codes,
416                                      sizeof(ngx_http_script_regex_end_code_t),
417                                      &regex);
418    if (regex_end == NULL) {
419        return NGX_CONF_ERROR;
420    }
421
422    regex_end->code = ngx_http_script_regex_end_code;
423    regex_end->uri = regex->uri;
424    regex_end->args = regex->args;
425    regex_end->add_args = regex->add_args;
426    regex_end->redirect = regex->redirect;
427
428    if (last) {
429        code = ngx_http_script_add_code(lcf->codes, sizeof(uintptr_t), &regex);
430        if (code == NULL) {
431            return NGX_CONF_ERROR;
432        }
433
434        *code = NULL;
435    }
436
437    regex->next = (u_char *) lcf->codes->elts + lcf->codes->nelts
438                                              - (u_char *) regex;
439
440    return NGX_CONF_OK;
441}
442
443
444static char *
445ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
446{
447    ngx_http_rewrite_loc_conf_t  *lcf = conf;
448
449    u_char                            *p;
450    ngx_str_t                         *value, *v;
451    ngx_http_script_return_code_t     *ret;
452    ngx_http_compile_complex_value_t   ccv;
453
454    ret = ngx_http_script_start_code(cf->pool, &lcf->codes,
455                                     sizeof(ngx_http_script_return_code_t));
456    if (ret == NULL) {
457        return NGX_CONF_ERROR;
458    }
459
460    value = cf->args->elts;
461
462    ngx_memzero(ret, sizeof(ngx_http_script_return_code_t));
463
464    ret->code = ngx_http_script_return_code;
465
466    p = value[1].data;
467
468    ret->status = ngx_atoi(p, value[1].len);
469
470    if (ret->status == (uintptr_t) NGX_ERROR) {
471
472        if (cf->args->nelts == 2
473            && (ngx_strncmp(p, "http://", sizeof("http://") - 1) == 0
474                || ngx_strncmp(p, "https://", sizeof("https://") - 1) == 0
475                || ngx_strncmp(p, "$scheme", sizeof("$scheme") - 1) == 0))
476        {
477            ret->status = NGX_HTTP_MOVED_TEMPORARILY;
478            v = &value[1];
479
480        } else {
481            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
482                               "invalid return code \"%V\"", &value[1]);
483            return NGX_CONF_ERROR;
484        }
485
486    } else {
487
488        if (ret->status > 999) {
489            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
490                               "invalid return code \"%V\"", &value[1]);
491            return NGX_CONF_ERROR;
492        }
493
494        if (cf->args->nelts == 2) {
495            return NGX_CONF_OK;
496        }
497
498        v = &value[2];
499    }
500
501    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
502
503    ccv.cf = cf;
504    ccv.value = v;
505    ccv.complex_value = &ret->text;
506
507    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
508        return NGX_CONF_ERROR;
509    }
510
511    return NGX_CONF_OK;
512}
513
514
515static char *
516ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
517{
518    ngx_http_rewrite_loc_conf_t *lcf = conf;
519
520    ngx_http_script_code_pt  *code;
521
522    code = ngx_http_script_start_code(cf->pool, &lcf->codes, sizeof(uintptr_t));
523    if (code == NULL) {
524        return NGX_CONF_ERROR;
525    }
526
527    *code = ngx_http_script_break_code;
528
529    return NGX_CONF_OK;
530}
531
532
533static char *
534ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
535{
536    ngx_http_rewrite_loc_conf_t  *lcf = conf;
537
538    void                         *mconf;
539    char                         *rv;
540    u_char                       *elts;
541    ngx_uint_t                    i;
542    ngx_conf_t                    save;
543    ngx_http_module_t            *module;
544    ngx_http_conf_ctx_t          *ctx, *pctx;
545    ngx_http_core_loc_conf_t     *clcf, *pclcf;
546    ngx_http_script_if_code_t    *if_code;
547    ngx_http_rewrite_loc_conf_t  *nlcf;
548
549    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
550    if (ctx == NULL) {
551        return NGX_CONF_ERROR;
552    }
553
554    pctx = cf->ctx;
555    ctx->main_conf = pctx->main_conf;
556    ctx->srv_conf = pctx->srv_conf;
557
558    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
559    if (ctx->loc_conf == NULL) {
560        return NGX_CONF_ERROR;
561    }
562
563    for (i = 0; cf->cycle->modules[i]; i++) {
564        if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
565            continue;
566        }
567
568        module = cf->cycle->modules[i]->ctx;
569
570        if (module->create_loc_conf) {
571
572            mconf = module->create_loc_conf(cf);
573            if (mconf == NULL) {
574                return NGX_CONF_ERROR;
575            }
576
577            ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = mconf;
578        }
579    }
580
581    pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
582
583    clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
584    clcf->loc_conf = ctx->loc_conf;
585    clcf->name = pclcf->name;
586    clcf->noname = 1;
587
588    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
589        return NGX_CONF_ERROR;
590    }
591
592    if (ngx_http_rewrite_if_condition(cf, lcf) != NGX_CONF_OK) {
593        return NGX_CONF_ERROR;
594    }
595
596    if_code = ngx_array_push_n(lcf->codes, sizeof(ngx_http_script_if_code_t));
597    if (if_code == NULL) {
598        return NGX_CONF_ERROR;
599    }
600
601    if_code->code = ngx_http_script_if_code;
602
603    elts = lcf->codes->elts;
604
605
606    /* the inner directives must be compiled to the same code array */
607
608    nlcf = ctx->loc_conf[ngx_http_rewrite_module.ctx_index];
609    nlcf->codes = lcf->codes;
610
611
612    save = *cf;
613    cf->ctx = ctx;
614
615    if (cf->cmd_type == NGX_HTTP_SRV_CONF) {
616        if_code->loc_conf = NULL;
617        cf->cmd_type = NGX_HTTP_SIF_CONF;
618
619    } else {
620        if_code->loc_conf = ctx->loc_conf;
621        cf->cmd_type = NGX_HTTP_LIF_CONF;
622    }
623
624    rv = ngx_conf_parse(cf, NULL);
625
626    *cf = save;
627
628    if (rv != NGX_CONF_OK) {
629        return rv;
630    }
631
632
633    if (elts != lcf->codes->elts) {
634        if_code = (ngx_http_script_if_code_t *)
635                   ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts));
636    }
637
638    if_code->next = (u_char *) lcf->codes->elts + lcf->codes->nelts
639                                                - (u_char *) if_code;
640
641    /* the code array belong to parent block */
642
643    nlcf->codes = NULL;
644
645    return NGX_CONF_OK;
646}
647
648
649static char *
650ngx_http_rewrite_if_condition(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf)
651{
652    u_char                        *p;
653    size_t                         len;
654    ngx_str_t                     *value;
655    ngx_uint_t                     cur, last;
656    ngx_regex_compile_t            rc;
657    ngx_http_script_code_pt       *code;
658    ngx_http_script_file_code_t   *fop;
659    ngx_http_script_regex_code_t  *regex;
660    u_char                         errstr[NGX_MAX_CONF_ERRSTR];
661
662    value = cf->args->elts;
663    last = cf->args->nelts - 1;
664
665    if (value[1].len < 1 || value[1].data[0] != '(') {
666        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
667                           "invalid condition \"%V\"", &value[1]);
668        return NGX_CONF_ERROR;
669    }
670
671    if (value[1].len == 1) {
672        cur = 2;
673
674    } else {
675        cur = 1;
676        value[1].len--;
677        value[1].data++;
678    }
679
680    if (value[last].len < 1 || value[last].data[value[last].len - 1] != ')') {
681        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
682                           "invalid condition \"%V\"", &value[last]);
683        return NGX_CONF_ERROR;
684    }
685
686    if (value[last].len == 1) {
687        last--;
688
689    } else {
690        value[last].len--;
691        value[last].data[value[last].len] = '\0';
692    }
693
694    len = value[cur].len;
695    p = value[cur].data;
696
697    if (len > 1 && p[0] == '$') {
698
699        if (cur != last && cur + 2 != last) {
700            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
701                               "invalid condition \"%V\"", &value[cur]);
702            return NGX_CONF_ERROR;
703        }
704
705        if (ngx_http_rewrite_variable(cf, lcf, &value[cur]) != NGX_CONF_OK) {
706            return NGX_CONF_ERROR;
707        }
708
709        if (cur == last) {
710            return NGX_CONF_OK;
711        }
712
713        cur++;
714
715        len = value[cur].len;
716        p = value[cur].data;
717
718        if (len == 1 && p[0] == '=') {
719
720            if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {
721                return NGX_CONF_ERROR;
722            }
723
724            code = ngx_http_script_start_code(cf->pool, &lcf->codes,
725                                              sizeof(uintptr_t));
726            if (code == NULL) {
727                return NGX_CONF_ERROR;
728            }
729
730            *code = ngx_http_script_equal_code;
731
732            return NGX_CONF_OK;
733        }
734
735        if (len == 2 && p[0] == '!' && p[1] == '=') {
736
737            if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {
738                return NGX_CONF_ERROR;
739            }
740
741            code = ngx_http_script_start_code(cf->pool, &lcf->codes,
742                                              sizeof(uintptr_t));
743            if (code == NULL) {
744                return NGX_CONF_ERROR;
745            }
746
747            *code = ngx_http_script_not_equal_code;
748            return NGX_CONF_OK;
749        }
750
751        if ((len == 1 && p[0] == '~')
752            || (len == 2 && p[0] == '~' && p[1] == '*')
753            || (len == 2 && p[0] == '!' && p[1] == '~')
754            || (len == 3 && p[0] == '!' && p[1] == '~' && p[2] == '*'))
755        {
756            regex = ngx_http_script_start_code(cf->pool, &lcf->codes,
757                                         sizeof(ngx_http_script_regex_code_t));
758            if (regex == NULL) {
759                return NGX_CONF_ERROR;
760            }
761
762            ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));
763
764            ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
765
766            rc.pattern = value[last];
767            rc.options = (p[len - 1] == '*') ? NGX_REGEX_CASELESS : 0;
768            rc.err.len = NGX_MAX_CONF_ERRSTR;
769            rc.err.data = errstr;
770
771            regex->regex = ngx_http_regex_compile(cf, &rc);
772            if (regex->regex == NULL) {
773                return NGX_CONF_ERROR;
774            }
775
776            regex->code = ngx_http_script_regex_start_code;
777            regex->next = sizeof(ngx_http_script_regex_code_t);
778            regex->test = 1;
779            if (p[0] == '!') {
780                regex->negative_test = 1;
781            }
782            regex->name = value[last];
783
784            return NGX_CONF_OK;
785        }
786
787        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
788                           "unexpected \"%V\" in condition", &value[cur]);
789        return NGX_CONF_ERROR;
790
791    } else if ((len == 2 && p[0] == '-')
792               || (len == 3 && p[0] == '!' && p[1] == '-'))
793    {
794        if (cur + 1 != last) {
795            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
796                               "invalid condition \"%V\"", &value[cur]);
797            return NGX_CONF_ERROR;
798        }
799
800        value[last].data[value[last].len] = '\0';
801        value[last].len++;
802
803        if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {
804            return NGX_CONF_ERROR;
805        }
806
807        fop = ngx_http_script_start_code(cf->pool, &lcf->codes,
808                                          sizeof(ngx_http_script_file_code_t));
809        if (fop == NULL) {
810            return NGX_CONF_ERROR;
811        }
812
813        fop->code = ngx_http_script_file_code;
814
815        if (p[1] == 'f') {
816            fop->op = ngx_http_script_file_plain;
817            return NGX_CONF_OK;
818        }
819
820        if (p[1] == 'd') {
821            fop->op = ngx_http_script_file_dir;
822            return NGX_CONF_OK;
823        }
824
825        if (p[1] == 'e') {
826            fop->op = ngx_http_script_file_exists;
827            return NGX_CONF_OK;
828        }
829
830        if (p[1] == 'x') {
831            fop->op = ngx_http_script_file_exec;
832            return NGX_CONF_OK;
833        }
834
835        if (p[0] == '!') {
836            if (p[2] == 'f') {
837                fop->op = ngx_http_script_file_not_plain;
838                return NGX_CONF_OK;
839            }
840
841            if (p[2] == 'd') {
842                fop->op = ngx_http_script_file_not_dir;
843                return NGX_CONF_OK;
844            }
845
846            if (p[2] == 'e') {
847                fop->op = ngx_http_script_file_not_exists;
848                return NGX_CONF_OK;
849            }
850
851            if (p[2] == 'x') {
852                fop->op = ngx_http_script_file_not_exec;
853                return NGX_CONF_OK;
854            }
855        }
856
857        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
858                           "invalid condition \"%V\"", &value[cur]);
859        return NGX_CONF_ERROR;
860    }
861
862    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
863                       "invalid condition \"%V\"", &value[cur]);
864
865    return NGX_CONF_ERROR;
866}
867
868
869static char *
870ngx_http_rewrite_variable(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,
871    ngx_str_t *value)
872{
873    ngx_int_t                    index;
874    ngx_http_script_var_code_t  *var_code;
875
876    value->len--;
877    value->data++;
878
879    index = ngx_http_get_variable_index(cf, value);
880
881    if (index == NGX_ERROR) {
882        return NGX_CONF_ERROR;
883    }
884
885    var_code = ngx_http_script_start_code(cf->pool, &lcf->codes,
886                                          sizeof(ngx_http_script_var_code_t));
887    if (var_code == NULL) {
888        return NGX_CONF_ERROR;
889    }
890
891    var_code->code = ngx_http_script_var_code;
892    var_code->index = index;
893
894    return NGX_CONF_OK;
895}
896
897
898static char *
899ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
900{
901    ngx_http_rewrite_loc_conf_t  *lcf = conf;
902
903    ngx_int_t                            index;
904    ngx_str_t                           *value;
905    ngx_http_variable_t                 *v;
906    ngx_http_script_var_code_t          *vcode;
907    ngx_http_script_var_handler_code_t  *vhcode;
908
909    value = cf->args->elts;
910
911    if (value[1].data[0] != '$') {
912        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
913                           "invalid variable name \"%V\"", &value[1]);
914        return NGX_CONF_ERROR;
915    }
916
917    value[1].len--;
918    value[1].data++;
919
920    v = ngx_http_add_variable(cf, &value[1],
921                              NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_WEAK);
922    if (v == NULL) {
923        return NGX_CONF_ERROR;
924    }
925
926    index = ngx_http_get_variable_index(cf, &value[1]);
927    if (index == NGX_ERROR) {
928        return NGX_CONF_ERROR;
929    }
930
931    if (v->get_handler == NULL) {
932        v->get_handler = ngx_http_rewrite_var;
933        v->data = index;
934    }
935
936    if (ngx_http_rewrite_value(cf, lcf, &value[2]) != NGX_CONF_OK) {
937        return NGX_CONF_ERROR;
938    }
939
940    if (v->set_handler) {
941        vhcode = ngx_http_script_start_code(cf->pool, &lcf->codes,
942                                   sizeof(ngx_http_script_var_handler_code_t));
943        if (vhcode == NULL) {
944            return NGX_CONF_ERROR;
945        }
946
947        vhcode->code = ngx_http_script_var_set_handler_code;
948        vhcode->handler = v->set_handler;
949        vhcode->data = v->data;
950
951        return NGX_CONF_OK;
952    }
953
954    vcode = ngx_http_script_start_code(cf->pool, &lcf->codes,
955                                       sizeof(ngx_http_script_var_code_t));
956    if (vcode == NULL) {
957        return NGX_CONF_ERROR;
958    }
959
960    vcode->code = ngx_http_script_set_var_code;
961    vcode->index = (uintptr_t) index;
962
963    return NGX_CONF_OK;
964}
965
966
967static char *
968ngx_http_rewrite_value(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,
969    ngx_str_t *value)
970{
971    ngx_int_t                              n;
972    ngx_http_script_compile_t              sc;
973    ngx_http_script_value_code_t          *val;
974    ngx_http_script_complex_value_code_t  *complex;
975
976    n = ngx_http_script_variables_count(value);
977
978    if (n == 0) {
979        val = ngx_http_script_start_code(cf->pool, &lcf->codes,
980                                         sizeof(ngx_http_script_value_code_t));
981        if (val == NULL) {
982            return NGX_CONF_ERROR;
983        }
984
985        n = ngx_atoi(value->data, value->len);
986
987        if (n == NGX_ERROR) {
988            n = 0;
989        }
990
991        val->code = ngx_http_script_value_code;
992        val->value = (uintptr_t) n;
993        val->text_len = (uintptr_t) value->len;
994        val->text_data = (uintptr_t) value->data;
995
996        return NGX_CONF_OK;
997    }
998
999    complex = ngx_http_script_start_code(cf->pool, &lcf->codes,
1000                                 sizeof(ngx_http_script_complex_value_code_t));
1001    if (complex == NULL) {
1002        return NGX_CONF_ERROR;
1003    }
1004
1005    complex->code = ngx_http_script_complex_value_code;
1006    complex->lengths = NULL;
1007
1008    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1009
1010    sc.cf = cf;
1011    sc.source = value;
1012    sc.lengths = &complex->lengths;
1013    sc.values = &lcf->codes;
1014    sc.variables = n;
1015    sc.complete_lengths = 1;
1016
1017    if (ngx_http_script_compile(&sc) != NGX_OK) {
1018        return NGX_CONF_ERROR;
1019    }
1020
1021    return NGX_CONF_OK;
1022}
1023