/*
 * NAXSI, a web application firewall for NGINX
 * Copyright (C) 2011, Thibault 'bui' Koechlin
 *  
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * 
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations
 * including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * file(s) with this exception, you may extend this exception to your
 * version of the file(s), but you are not obligated to do so.  If you
 * do not wish to do so, delete this exception statement from your
 * version.  If you delete this exception statement from all source
 * files in the program, then also delete it here.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "naxsi.h"


char	*
strnchr(const char *s, int c, int len)
{
  int	cpt;
  for (cpt = 0; cpt < len && s[cpt]; cpt++)
    if (s[cpt] == c) 
      return ((char *) s+cpt);
  return (NULL);
}

char	*
strncasechr(const char *s, int c, int len)
{
  int	cpt;
  for (cpt = 0; cpt < len && s[cpt]; cpt++)
    if (tolower(s[cpt]) == c) 
      return ((char *) s+cpt);
  return (NULL);
}

/*
** strstr: faster, stronger, harder
** (because strstr from libc is very slow)
*/
char *
strfaststr(unsigned char *haystack, unsigned int hl, 
		 unsigned char *needle, unsigned int nl)
{
  char	*cpt, *found, *end;
  if (hl < nl || !haystack || !needle || !nl || !hl) return (NULL);
  cpt = (char *) haystack;
  end = (char *) haystack + hl;
  while (cpt < end) {
      found = strncasechr((const char *) cpt, (int) needle[0], hl);
      if (!found) return (NULL);
      if (nl == 1) return (found);
      if (!strncasecmp((const char *)found+1, (const char *) needle+1, nl-1))
	return ((char *) found);
      else {
	  if (found+nl >= end)
	    break;
	  if (found+nl < end)
	    cpt = found+1;
	}
    }
  return (NULL);
}

/* unescape routine, returns number of nullbytes present */
int naxsi_unescape(ngx_str_t *str) {
  u_char *dst, *src;
  u_int nullbytes = 0, i;
  
  dst = str->data;
  src = str->data;
      
  naxsi_unescape_uri(&src, &dst,
		     str->len, 0);      
  str->len =  src - str->data;
  //tmp hack fix, avoid %00 & co (null byte) encoding :p
  for (i = 0; i < str->len; i++)
    if (str->data[i] == 0x0)
      {
	nullbytes++;
	str->data[i] = '0';
      }
  return (nullbytes);
}


/*
** Patched ngx_unescape_uri : 
** The original one does not care if the character following % is in valid range.
** For example, with the original one :
** '%uff' -> 'uff'
*/
void
naxsi_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)
{
    u_char  *d, *s, ch, c, decoded;
    enum {
        sw_usual = 0,
        sw_quoted,
        sw_quoted_second
    } state;

    d = *dst;
    s = *src;

    state = 0;
    decoded = 0;

    while (size--) {

        ch = *s++;

        switch (state) {
        case sw_usual:
            if (ch == '?'
                && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT)))
            {
                *d++ = ch;
                goto done;
            }

            if (ch == '%') {
                state = sw_quoted;
                break;
            }

            *d++ = ch;
            break;

        case sw_quoted:

            if (ch >= '0' && ch <= '9') {
                decoded = (u_char) (ch - '0');
                state = sw_quoted_second;
                break;
            }

            c = (u_char) (ch | 0x20);
            if (c >= 'a' && c <= 'f') {
                decoded = (u_char) (c - 'a' + 10);
                state = sw_quoted_second;
                break;
            }

            /* the invalid quoted character */

            state = sw_usual;
	    *d++ = '%';
            *d++ = ch;

            break;

        case sw_quoted_second:

            state = sw_usual;

            if (ch >= '0' && ch <= '9') {
                ch = (u_char) ((decoded << 4) + ch - '0');

                if (type & NGX_UNESCAPE_REDIRECT) {
                    if (ch > '%' && ch < 0x7f) {
                        *d++ = ch;
                        break;
                    }

                    *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);

                    break;
                }

                *d++ = ch;

                break;
            }

            c = (u_char) (ch | 0x20);
            if (c >= 'a' && c <= 'f') {
                ch = (u_char) ((decoded << 4) + c - 'a' + 10);

                if (type & NGX_UNESCAPE_URI) {
                    if (ch == '?') {
                        *d++ = ch;
                        goto done;
                    }

                    *d++ = ch;
                    break;
                }

                if (type & NGX_UNESCAPE_REDIRECT) {
                    if (ch == '?') {
                        *d++ = ch;
                        goto done;
                    }

                    if (ch > '%' && ch < 0x7f) {
                        *d++ = ch;
                        break;
                    }

                    *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
                    break;
                }

                *d++ = ch;

                break;
            }
	    
            /* the invalid quoted character */
	    *d++ = ch;

            break;
        }
    }

done:

    *dst = d;
    *src = s;
}

//#define whitelist_heavy_debug

#ifdef whitelist_heavy_debug
#define whitelist_light_debug
#define whitelist_debug
#endif

#ifdef whitelist_debug
#define whitelist_light_debug
#endif


/* push rule into disabled rules. */
ngx_int_t 
ngx_http_wlr_push_disabled(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc, 
			   ngx_http_rule_t *curr) {
  ngx_http_rule_t	**dr;
#ifdef whitelist_debug
  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "[naxsi] rule %d disabled",
		     curr->wl_id[0]);
#endif
  if (!dlc->disabled_rules)
    dlc->disabled_rules = ngx_array_create(cf->pool, 4, 
					   sizeof(ngx_http_rule_t *));
  if (!dlc->disabled_rules)
    return (NGX_ERROR);
  dr = ngx_array_push(dlc->disabled_rules);
  if (!dr)
    return (NGX_ERROR);
  *dr = (ngx_http_rule_t *) curr;
  return (NGX_OK);
}

/* merge the two rules into father_wl, meaning 
   ids. Not locations, as we are getting rid of it */
ngx_int_t 
ngx_http_wlr_merge(ngx_conf_t *cf, ngx_http_whitelist_rule_t *father_wl, 
		   ngx_http_rule_t *curr) {
  uint i;
  ngx_int_t		*tmp_ptr;
  
  if (!father_wl->ids)
    {
      father_wl->ids = ngx_array_create(cf->pool, 3, sizeof(int));
      if (!father_wl->ids)
	return (NGX_ERROR);
    }
  for (i = 0; curr->wl_id[i] >= 0 ; i++) {
    tmp_ptr = ngx_array_push(father_wl->ids);
    if (!tmp_ptr)
      return (NGX_ERROR);
    *tmp_ptr = curr->wl_id[i];
  }
  return (NGX_OK);
}

/*check rule, returns associed zone, as well as location index.
  location index refers to $URL:bla or $ARGS_VAR:bla */
#define custloc_array(x) ((ngx_http_custom_rule_location_t *) x)

//#define whitelist_heavy_debug

ngx_int_t 
ngx_http_wlr_identify(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc, 
		      ngx_http_rule_t *curr, int *zone,
		      int *uri_idx, int *name_idx) {
  
  uint	i;
  
  /*
    identify global match zones (|ARGS|BODY|HEADERS|URL|FILE_EXT)
   */
  if (curr->br->body || curr->br->body_var)
    *zone = BODY;
  else if (curr->br->headers || curr->br->headers_var)
    *zone = HEADERS;
  else if (curr->br->args || curr->br->args_var)
    *zone = ARGS;
  else if (curr->br->url) /*don't assume that named $URL means zone is URL.*/
    *zone = URL;
  else if (curr->br->file_ext)
    *zone = FILE_EXT;
  /*
    if we're facing a WL in the style $URL:/bla|ARGS (or any other zone),
    push it to 
   */
  for (i = 0; i < curr->br->custom_locations->nelts; i++) {
    /*
      locate target URL if exists ($URL:/bla|ARGS) or ($URL:/bla|$ARGS_VAR:foo)
     */
    if (custloc_array(curr->br->custom_locations->elts)[i].specific_url) {
#ifdef whitelist_heavy_debug
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
			 "whitelist has URI %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
#endif
      *uri_idx = i;
    }
    /*
      identify named match zones ($ARGS_VAR:bla|$HEADERS_VAR:bla|$BODY_VAR:bla)
    */
    if (custloc_array(curr->br->custom_locations->elts)[i].body_var) {
#ifdef whitelist_heavy_debug
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
			 "whitelist has body_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
#endif
      *name_idx = i;
      *zone = BODY;
    }
    if (custloc_array(curr->br->custom_locations->elts)[i].headers_var) {
#ifdef whitelist_heavy_debug
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
			 "whitelist has header_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
#endif
      *name_idx = i;
      *zone = HEADERS;
    }
    if (custloc_array(curr->br->custom_locations->elts)[i].args_var) {
#ifdef whitelist_heavy_debug
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
			 "whitelist has arg_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
#endif
      *name_idx = i;
      *zone = ARGS;
    }
  }
  if (*zone == -1)
    return (NGX_ERROR);
  return (NGX_OK);
}


ngx_http_whitelist_rule_t *
ngx_http_wlr_find(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc,
		  ngx_http_rule_t *curr, int zone, int uri_idx,
		  int name_idx, char **fullname) {
  uint i;
  
  /* Create unique string for rule, and try to find it in existing rules.*/
  /*name AND uri*/
  
  if (uri_idx != -1 && name_idx != -1) {
#ifdef whitelist_heavy_debug
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
		       "whitelist has uri + name");
#endif
    /* allocate one extra byte in case curr->br->target_name is set. */
    *fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[name_idx].target.len +
			    custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len + 3);
    /* if WL targets variable name instead of content, prefix hash with '#' */
    if (curr->br->target_name)
      strncat(*fullname, (const char *) "#", 1);
    strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[uri_idx].target.data, 
	    custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len);
    strncat(*fullname, (const char *) "#", 1);
    strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[name_idx].target.data, 
	    custloc_array(curr->br->custom_locations->elts)[name_idx].target.len);
  }
  /* only uri */
  else if (uri_idx != -1 && name_idx == -1) {
#ifdef whitelist_heavy_debug
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
		       "whitelist has uri");
#endif
    //XXX set flag only_uri
    *fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len + 1);
    strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[uri_idx].target.data, 
	    custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len);
  }
  /* only name */
  else if (name_idx != -1) {
#ifdef whitelist_heavy_debug
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
		       "whitelist has name");
#endif
    *fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[name_idx].target.len + 2);
    if (curr->br->target_name)
      strncat(*fullname, (const char *) "#", 1);
    strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[name_idx].target.data, 
	    custloc_array(curr->br->custom_locations->elts)[name_idx].target.len);
  }
  /* problem houston */
  else
    return (NULL);
  
  for (i = 0; i < dlc->tmp_wlr->nelts; i++)
    if (!strcmp((const char *)*fullname, (const char *)((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->data) && 
	((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone == (uint) zone)
      {
#ifdef whitelist_heavy_debug
	ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
			   "found existing 'same' WL : %V", ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name);
#endif
	return (&((ngx_http_whitelist_rule_t *)dlc->tmp_wlr->elts)[i]);
      }
  return (NULL);
}



#define httprule_array(x) ((ngx_http_rule_t *) x)


ngx_int_t
ngx_http_wlr_finalize_hashtables(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t  *dlc) {
  int get_sz = 0, headers_sz = 0, body_sz = 0, uri_sz = 0;
  ngx_array_t *get_ar = NULL, *headers_ar = NULL, *body_ar = NULL, *uri_ar = NULL;
  ngx_hash_key_t *arr_node;
  ngx_hash_init_t hash_init;
  uint i;
  
#ifdef whitelist_heavy_debug
  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
		     "finalizing hashtables");
#endif
  
  for (i = 0; i < dlc->tmp_wlr->nelts; i++) {
    switch (((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone) {
    case FILE_EXT:
    case BODY:
      body_sz++;
      break;
    case HEADERS:
      headers_sz++;
      break;
    case URL:
      uri_sz++;
      break;
    case ARGS:
      get_sz++;
      break;
    case UNKNOWN:
    default:
      return (NGX_ERROR);
    }
  }
#ifdef whitelist_heavy_debug
  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
		     "nb items : body:%d headers:%d uri:%d get:%d",
		     body_sz, headers_sz, uri_sz, get_sz);
#endif

  if (get_sz)
    get_ar = ngx_array_create(cf->pool, get_sz, sizeof(ngx_hash_key_t));
  if (headers_sz)
    headers_ar = ngx_array_create(cf->pool, headers_sz, sizeof(ngx_hash_key_t));
  if (body_sz)
    body_ar = ngx_array_create(cf->pool, body_sz, sizeof(ngx_hash_key_t));
  if (uri_sz)
    uri_ar = ngx_array_create(cf->pool, uri_sz, sizeof(ngx_hash_key_t));
  for (i = 0; i < dlc->tmp_wlr->nelts; i++) {
    switch (((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone) {
    case FILE_EXT:
    case BODY:
      arr_node = (ngx_hash_key_t*) ngx_array_push(body_ar);
      break;
    case HEADERS:
      arr_node = (ngx_hash_key_t*) ngx_array_push(headers_ar);
      break;
    case URL:
      arr_node = (ngx_hash_key_t*) ngx_array_push(uri_ar);
      break;
    case ARGS:
      arr_node = (ngx_hash_key_t*) ngx_array_push(get_ar);
      break;
    default:
      return (NGX_ERROR);
    }
    ngx_memset(arr_node, 0, sizeof(ngx_hash_key_t));
    arr_node->key = *(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name);
    arr_node->key_hash = ngx_hash_key_lc(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->data, 
					 ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->len);
    arr_node->value = (void *) &(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i]);
#ifdef whitelist_heavy_debug
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing new WL, zone:%d, target:%V, %d IDs",
		       ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone ,  ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name,
		        ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->nelts);
    unsigned int z;
    for (z = 0; z < ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->nelts; z++)
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "id:%d",
    			 ((int *)((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->elts)[z]);
#endif    
  }
  hash_init.key = &ngx_hash_key_lc;
  hash_init.pool = cf->pool;
  hash_init.temp_pool = NULL;
  hash_init.max_size  = 1024;
  hash_init.bucket_size = 512;
  
  if (body_ar) {
    dlc->wlr_body_hash =  (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
    hash_init.hash = dlc->wlr_body_hash;
    hash_init.name = "wlr_body_hash";
    if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) body_ar->elts, 
		      body_ar->nelts) != NGX_OK) {
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$BODY hashtable init failed");
      return (NGX_ERROR);
    }

#ifdef whitelist_debug
    else
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$BODY hashtable init successed !");
#endif
  }
  if (uri_ar) {
    dlc->wlr_url_hash =  (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
    hash_init.hash = dlc->wlr_url_hash;
    hash_init.name = "wlr_url_hash";
    if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) uri_ar->elts, 
		      uri_ar->nelts) != NGX_OK) {
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$URL hashtable init failed");
      return (NGX_ERROR);
    }
#ifdef whitelist_debug
    else
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$URL hashtable init successed !");
#endif
  }
  if (get_ar) {
    dlc->wlr_args_hash =  (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
    hash_init.hash = dlc->wlr_args_hash;
    hash_init.name = "wlr_args_hash";
    if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) get_ar->elts, 
		      get_ar->nelts) != NGX_OK) {
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$ARGS hashtable init failed");
      return (NGX_ERROR);
    }
#ifdef whitelist_debug
    else
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$ARGS hashtable init successed %d !",
			 dlc->wlr_args_hash->size);
#endif

  }
  if (headers_ar) {
    dlc->wlr_headers_hash =  (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
    hash_init.hash = dlc->wlr_headers_hash;
    hash_init.name = "wlr_headers_hash";
    if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) headers_ar->elts, 
		      headers_ar->nelts) != NGX_OK) {
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$HEADERS hashtable init failed");
      return (NGX_ERROR);
    }
#ifdef whitelist_debug
    else
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$HEADERS hashtable init successed %d !",
			 dlc->wlr_headers_hash->size);
#endif

  }
  return (NGX_OK);
}


/*
** This function will take the whitelist basicrules generated during the configuration
** parsing phase, and aggregate them to build hashtables according to the matchzones.
** 
** As whitelist can be in the form :
** "mz:$URL:bla|$ARGS_VAR:foo"
** "mz:$URL:bla|ARGS"
** "mz:$HEADERS_VAR:Cookie"
** ...
**
** So, we will aggregate all the rules that are pointing to the same URL together, 
** as well as rules targetting the same argument name / zone.
*/

//#define whitelist_heavy_debug
ngx_int_t
ngx_http_dummy_create_hashtables_n(ngx_http_dummy_loc_conf_t *dlc, 
				   ngx_conf_t *cf)
{
  int				zone, uri_idx, name_idx, ret;
  ngx_http_rule_t		*curr_r/*, *father_r*/;
  ngx_http_whitelist_rule_t	*father_wlr;
  char			*fullname;
  uint	i;

  if (!dlc->whitelist_rules  || dlc->whitelist_rules->nelts < 1) {
#ifdef whitelist_heavy_debug    
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
		       "No whitelist registred, but it's your call.");    
#endif
    return (NGX_OK);
  }
#ifdef whitelist_heavy_debug
  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
		     "Building whitelist hashtables, %d items in list",
		     dlc->whitelist_rules->nelts);
#endif
  dlc->tmp_wlr = ngx_array_create(cf->pool, dlc->whitelist_rules->nelts,
  				  sizeof(ngx_http_whitelist_rule_t));
  /* iterate through each stored whitelist rule. */
  for (i = 0; i < dlc->whitelist_rules->nelts; i++) {
    uri_idx = name_idx = zone = -1;
    /*a whitelist is in fact just another basic_rule_t */
    curr_r = &(httprule_array(dlc->whitelist_rules->elts)[i]);
#ifdef whitelist_heavy_debug
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
		       "Processing wl %d/%p", i, curr_r);
#endif
    /*no custom location at all means that the rule is disabled */
    if (!curr_r->br->custom_locations) {
#ifdef whitelist_heavy_debug
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
			 "WL %d is a disable rule.", i);
#endif
      if (ngx_http_wlr_push_disabled(cf, dlc, curr_r) == NGX_ERROR)
	return (NGX_ERROR);
      continue;
    }
    ret = ngx_http_wlr_identify(cf, dlc, curr_r, &zone, &uri_idx, &name_idx);
    if (ret != NGX_OK)
      {
	ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
			   "Following whitelist doesn't target any zone or is incorrect :");
	if (name_idx != -1)
	  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist target name : %V", 
			     &(custloc_array(curr_r->br->custom_locations->elts)[name_idx].target));
	else
	  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has no target name.");
	if (uri_idx != -1)
	  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist target uri : %V", 
			     &(custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target));
	else
	  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelists has no target uri.");
	return (NGX_ERROR);
      }
    /*
      ngx_http_whitelist_rule_t *
      ngx_http_wlr_find(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc,
      ngx_http_rule_t *curr, int zone, int uri_idx,
      int name_idx, char **fullname) {
      
    */
    father_wlr = ngx_http_wlr_find(cf, dlc, curr_r, zone, uri_idx, name_idx, (char **) &fullname);
    if (!father_wlr) {
#ifdef whitelist_heavy_debug
      ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 
			 "creating fresh WL [%s].", fullname);
#endif
      /* creates a new whitelist rule in the right place.
	 setup name and zone, create a new (empty) whitelist_location, as well
	 as a new (empty) id aray. */
      father_wlr = ngx_array_push(dlc->tmp_wlr);
      if (!father_wlr)
	return (NGX_ERROR);
      memset(father_wlr, 0, sizeof(ngx_http_whitelist_rule_t));
      father_wlr->name = ngx_pcalloc(cf->pool, sizeof(ngx_str_t));
      if (!father_wlr->name)
	return (NGX_ERROR);
      father_wlr->name->len = strlen((const char *) fullname);
      father_wlr->name->data = (unsigned char *) fullname;
      father_wlr->zone = zone;
      /* If there is URI and no name idx, specify it,
	 so that WL system won't get fooled by an argname like an URL */
      if (uri_idx != -1 && name_idx == -1)
	father_wlr->uri_only = 1;
      /* If target_name is present in son, report it. */
      if (curr_r->br && curr_r->br->target_name)
        father_wlr->target_name = curr_r->br->target_name; 
    }
    /*merges the two whitelist rules together, including custom_locations. */
    if (ngx_http_wlr_merge(cf, father_wlr, curr_r) != NGX_OK)
      return (NGX_ERROR);
  }
  
  /* and finally, build the hashtables for various zones. */
  if (ngx_http_wlr_finalize_hashtables(cf, dlc) != NGX_OK)
    return (NGX_ERROR);
  /* TODO : Free old whitelist_rules (dlc->whitelist_rules)*/
  return (NGX_OK);
}

/*
  function used for intensive log if dynamic flag is set.
  Output format :
  ip=<ip>&server=<server>&uri=<uri>&id=<id>&zone=<zone>&content=<content>
 */

static char *dummy_match_zones[] = {
  "HEADERS",
  "URL",
  "ARGS",
  "BODY",
  "FILE_EXT",
  "UNKNOWN",
  NULL
};


void naxsi_log_offending(ngx_str_t *name, ngx_str_t *val, ngx_http_request_t *req, ngx_http_rule_t *rule,
			 enum DUMMY_MATCH_ZONE	zone) {
  ngx_str_t			tmp_uri, tmp_val, tmp_name;
  ngx_str_t			empty=ngx_string("");
  
  //encode uri
  tmp_uri.len = req->uri.len + (2 * ngx_escape_uri(NULL, req->uri.data, req->uri.len,
						   NGX_ESCAPE_ARGS));
  tmp_uri.data = ngx_pcalloc(req->pool, tmp_uri.len+1);
  if (tmp_uri.data == NULL)
    return ;
  ngx_escape_uri(tmp_uri.data, req->uri.data, req->uri.len, NGX_ESCAPE_ARGS);
  //encode val
  if (val->len <= 0)
    tmp_val = empty;
  else {
    tmp_val.len = val->len + (2 * ngx_escape_uri(NULL, val->data, val->len,
						 NGX_ESCAPE_ARGS));
    tmp_val.data = ngx_pcalloc(req->pool, tmp_val.len+1);
    if (tmp_val.data == NULL)
      return ;
    ngx_escape_uri(tmp_val.data, val->data, val->len, NGX_ESCAPE_ARGS);
  }
  //encode name
  if (name->len <= 0)
    tmp_name = empty;
  else {
    tmp_name.len = name->len + (2 * ngx_escape_uri(NULL, name->data, name->len,
						   NGX_ESCAPE_ARGS));
    tmp_name.data = ngx_pcalloc(req->pool, tmp_name.len+1);
    if (tmp_name.data == NULL)
      return ;
    ngx_escape_uri(tmp_name.data, name->data, name->len, NGX_ESCAPE_ARGS);
  }
  
  ngx_log_debug(NGX_LOG_ERR, req->connection->log, 0, 
		"NAXSI_EXLOG: ip=%V&server=%V&uri=%V&id=%d&zone=%s&var_name=%V&content=%V", 
		&(req->connection->addr_text), &(req->headers_in.server),
		&(tmp_uri), rule->rule_id, dummy_match_zones[zone], &(tmp_name), &(tmp_val));
  if (tmp_val.len > 0)
    ngx_pfree(req->pool, tmp_val.data);
  if (tmp_name.len > 0)
    ngx_pfree(req->pool, tmp_name.data);
  if (tmp_uri.len > 0)
    ngx_pfree(req->pool, tmp_uri.data);
  
}
