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