File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / configfile-glue.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 10:32:47 2013 UTC (10 years, 8 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_35p0, v1_4_35, v1_4_33, HEAD
1.4.33

    1: #include "base.h"
    2: #include "buffer.h"
    3: #include "array.h"
    4: #include "log.h"
    5: #include "plugin.h"
    6: 
    7: #include "configfile.h"
    8: 
    9: #include <string.h>
   10: #include <stdlib.h>
   11: 
   12: /**
   13:  * like all glue code this file contains functions which
   14:  * are the external interface of lighttpd. The functions
   15:  * are used by the server itself and the plugins.
   16:  *
   17:  * The main-goal is to have a small library in the end
   18:  * which is linked against both and which will define
   19:  * the interface itself in the end.
   20:  *
   21:  */
   22: 
   23: 
   24: /* handle global options */
   25: 
   26: /* parse config array */
   27: int config_insert_values_internal(server *srv, array *ca, const config_values_t cv[]) {
   28: 	size_t i;
   29: 	data_unset *du;
   30: 
   31: 	for (i = 0; cv[i].key; i++) {
   32: 
   33: 		if (NULL == (du = array_get_element(ca, cv[i].key))) {
   34: 			/* no found */
   35: 
   36: 			continue;
   37: 		}
   38: 
   39: 		switch (cv[i].type) {
   40: 		case T_CONFIG_ARRAY:
   41: 			if (du->type == TYPE_ARRAY) {
   42: 				size_t j;
   43: 				data_array *da = (data_array *)du;
   44: 
   45: 				for (j = 0; j < da->value->used; j++) {
   46: 					if (da->value->data[j]->type == TYPE_STRING) {
   47: 						data_string *ds = data_string_init();
   48: 
   49: 						buffer_copy_string_buffer(ds->value, ((data_string *)(da->value->data[j]))->value);
   50: 						if (!da->is_index_key) {
   51: 							/* the id's were generated automaticly, as we copy now we might have to renumber them
   52: 							 * this is used to prepend server.modules by mod_indexfile as it has to be loaded
   53: 							 * before mod_fastcgi and friends */
   54: 							buffer_copy_string_buffer(ds->key, ((data_string *)(da->value->data[j]))->key);
   55: 						}
   56: 
   57: 						array_insert_unique(cv[i].destination, (data_unset *)ds);
   58: 					} else {
   59: 						log_error_write(srv, __FILE__, __LINE__, "sssd",
   60: 								"the key of an array can only be a string or a integer, variable:",
   61: 								cv[i].key, "type:", da->value->data[j]->type);
   62: 
   63: 						return -1;
   64: 					}
   65: 				}
   66: 			} else {
   67: 				log_error_write(srv, __FILE__, __LINE__, "ss", cv[i].key, "should have been a array of strings like ... = ( \"...\" )");
   68: 
   69: 				return -1;
   70: 			}
   71: 			break;
   72: 		case T_CONFIG_STRING:
   73: 			if (du->type == TYPE_STRING) {
   74: 				data_string *ds = (data_string *)du;
   75: 
   76: 				buffer_copy_string_buffer(cv[i].destination, ds->value);
   77: 			} else {
   78: 				log_error_write(srv, __FILE__, __LINE__, "ssss", cv[i].key, "should have been a string like ... = \"...\"");
   79: 
   80: 				return -1;
   81: 			}
   82: 			break;
   83: 		case T_CONFIG_SHORT:
   84: 			switch(du->type) {
   85: 			case TYPE_INTEGER: {
   86: 				data_integer *di = (data_integer *)du;
   87: 
   88: 				*((unsigned short *)(cv[i].destination)) = di->value;
   89: 				break;
   90: 			}
   91: 			case TYPE_STRING: {
   92: 				data_string *ds = (data_string *)du;
   93: 
   94: 				/* If the value came from an environment variable, then it is a
   95: 				 * data_string, although it may contain a number in ASCII
   96: 				 * decimal format.  We try to interpret the string as a decimal
   97: 				 * short before giving up, in order to support setting numeric
   98: 				 * values with environment variables (eg, port number).
   99: 				 */
  100: 				if (ds->value->ptr && *ds->value->ptr) {
  101: 					char *e;
  102: 					long l = strtol(ds->value->ptr, &e, 10);
  103: 					if (e != ds->value->ptr && !*e && l >=0 && l <= 65535) {
  104: 						*((unsigned short *)(cv[i].destination)) = l;
  105: 						break;
  106: 					}
  107: 				}
  108: 
  109: 				log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected a short:", cv[i].key, ds->value);
  110: 
  111: 				return -1;
  112: 			}
  113: 			default:
  114: 				log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected a short integer, range 0 ... 65535");
  115: 				return -1;
  116: 			}
  117: 			break;
  118: 		case T_CONFIG_INT:
  119: 			switch(du->type) {
  120: 			case TYPE_INTEGER: {
  121: 				data_integer *di = (data_integer *)du;
  122: 
  123: 				*((unsigned int *)(cv[i].destination)) = di->value;
  124: 				break;
  125: 			}
  126: 			case TYPE_STRING: {
  127: 				data_string *ds = (data_string *)du;
  128: 
  129: 				if (ds->value->ptr && *ds->value->ptr) {
  130: 					char *e;
  131: 					long l = strtol(ds->value->ptr, &e, 10);
  132: 					if (e != ds->value->ptr && !*e && l >= 0) {
  133: 						*((unsigned int *)(cv[i].destination)) = l;
  134: 						break;
  135: 					}
  136: 				}
  137: 
  138: 
  139: 				log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected an integer:", cv[i].key, ds->value);
  140: 
  141: 				return -1;
  142: 			}
  143: 			default:
  144: 				log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected an integer, range 0 ... 4294967295");
  145: 				return -1;
  146: 			}
  147: 			break;
  148: 		case T_CONFIG_BOOLEAN:
  149: 			if (du->type == TYPE_STRING) {
  150: 				data_string *ds = (data_string *)du;
  151: 
  152: 				if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) {
  153: 					*((unsigned short *)(cv[i].destination)) = 1;
  154: 				} else if (buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) {
  155: 					*((unsigned short *)(cv[i].destination)) = 0;
  156: 				} else {
  157: 					log_error_write(srv, __FILE__, __LINE__, "ssbs", "ERROR: unexpected value for key:", cv[i].key, ds->value, "(enable|disable)");
  158: 
  159: 					return -1;
  160: 				}
  161: 			} else {
  162: 				log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: unexpected type for key:", cv[i].key, "(string)", "\"(enable|disable)\"");
  163: 
  164: 				return -1;
  165: 			}
  166: 			break;
  167: 		case T_CONFIG_LOCAL:
  168: 		case T_CONFIG_UNSET:
  169: 			break;
  170: 		case T_CONFIG_UNSUPPORTED:
  171: 			log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found unsupported key:", cv[i].key, "-", (char *)(cv[i].destination));
  172: 
  173: 			srv->config_unsupported = 1;
  174: 
  175: 			break;
  176: 		case T_CONFIG_DEPRECATED:
  177: 			log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found deprecated key:", cv[i].key, "-", (char *)(cv[i].destination));
  178: 
  179: 			srv->config_deprecated = 1;
  180: 
  181: 			break;
  182: 		}
  183: 	}
  184: 
  185: 	return 0;
  186: }
  187: 
  188: int config_insert_values_global(server *srv, array *ca, const config_values_t cv[]) {
  189: 	size_t i;
  190: 	data_unset *du;
  191: 
  192: 	for (i = 0; cv[i].key; i++) {
  193: 		data_string *touched;
  194: 
  195: 		if (NULL == (du = array_get_element(ca, cv[i].key))) {
  196: 			/* no found */
  197: 
  198: 			continue;
  199: 		}
  200: 
  201: 		/* touched */
  202: 		touched = data_string_init();
  203: 
  204: 		buffer_copy_string_len(touched->value, CONST_STR_LEN(""));
  205: 		buffer_copy_string_buffer(touched->key, du->key);
  206: 
  207: 		array_insert_unique(srv->config_touched, (data_unset *)touched);
  208: 	}
  209: 
  210: 	return config_insert_values_internal(srv, ca, cv);
  211: }
  212: 
  213: static unsigned short sock_addr_get_port(sock_addr *addr) {
  214: #ifdef HAVE_IPV6
  215: 	return ntohs(addr->plain.sa_family ? addr->ipv6.sin6_port : addr->ipv4.sin_port);
  216: #else
  217: 	return ntohs(addr->ipv4.sin_port);
  218: #endif
  219: }
  220: 
  221: static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc);
  222: 
  223: static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) {
  224: 	buffer *l;
  225: 	server_socket *srv_sock = con->srv_socket;
  226: 
  227: 	/* check parent first */
  228: 	if (dc->parent && dc->parent->context_ndx) {
  229: 		/**
  230: 		 * a nested conditional 
  231: 		 *
  232: 		 * if the parent is not decided yet or false, we can't be true either 
  233: 		 */
  234: 		if (con->conf.log_condition_handling) {
  235: 			log_error_write(srv, __FILE__, __LINE__,  "sb", "go parent", dc->parent->key);
  236: 		}
  237: 
  238: 		switch (config_check_cond_cached(srv, con, dc->parent)) {
  239: 		case COND_RESULT_FALSE:
  240: 			return COND_RESULT_FALSE;
  241: 		case COND_RESULT_UNSET:
  242: 			return COND_RESULT_UNSET;
  243: 		default:
  244: 			break;
  245: 		}
  246: 	}
  247: 
  248: 	if (dc->prev) {
  249: 		/**
  250: 		 * a else branch
  251: 		 *
  252: 		 * we can only be executed, if all of our previous brothers 
  253: 		 * are false
  254: 		 */
  255: 		if (con->conf.log_condition_handling) {
  256: 			log_error_write(srv, __FILE__, __LINE__,  "sb", "go prev", dc->prev->key);
  257: 		}
  258: 
  259: 		/* make sure prev is checked first */
  260: 		config_check_cond_cached(srv, con, dc->prev);
  261: 
  262: 		/* one of prev set me to FALSE */
  263: 		switch (con->cond_cache[dc->context_ndx].result) {
  264: 		case COND_RESULT_FALSE:
  265: 			return con->cond_cache[dc->context_ndx].result;
  266: 		default:
  267: 			break;
  268: 		}
  269: 	}
  270: 
  271: 	if (!con->conditional_is_valid[dc->comp]) {
  272: 		if (con->conf.log_condition_handling) {
  273: 			log_error_write(srv, __FILE__, __LINE__,  "dss", 
  274: 				dc->comp,
  275: 				dc->key->ptr,
  276: 				con->conditional_is_valid[dc->comp] ? "yeah" : "nej");
  277: 		}
  278: 
  279: 		return COND_RESULT_UNSET;
  280: 	}
  281: 
  282: 	/* pass the rules */
  283: 
  284: 	switch (dc->comp) {
  285: 	case COMP_HTTP_HOST: {
  286: 		char *ck_colon = NULL, *val_colon = NULL;
  287: 
  288: 		if (!buffer_is_empty(con->uri.authority)) {
  289: 
  290: 			/*
  291: 			 * append server-port to the HTTP_POST if necessary
  292: 			 */
  293: 
  294: 			l = con->uri.authority;
  295: 
  296: 			switch(dc->cond) {
  297: 			case CONFIG_COND_NE:
  298: 			case CONFIG_COND_EQ:
  299: 				ck_colon = strchr(dc->string->ptr, ':');
  300: 				val_colon = strchr(l->ptr, ':');
  301: 
  302: 				if (NULL != ck_colon && NULL == val_colon) {
  303: 					/* condition "host:port" but client send "host" */
  304: 					buffer_copy_string_buffer(srv->cond_check_buf, l);
  305: 					buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":"));
  306: 					buffer_append_long(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr)));
  307: 					l = srv->cond_check_buf;
  308: 				} else if (NULL != val_colon && NULL == ck_colon) {
  309: 					/* condition "host" but client send "host:port" */
  310: 					buffer_copy_string_len(srv->cond_check_buf, l->ptr, val_colon - l->ptr);
  311: 					l = srv->cond_check_buf;
  312: 				}
  313: 				break;
  314: 			default:
  315: 				break;
  316: 			}
  317: #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
  318: 		} else if (!buffer_is_empty(con->tlsext_server_name)) {
  319: 			l = con->tlsext_server_name;
  320: #endif
  321: 		} else {
  322: 			l = srv->empty_string;
  323: 		}
  324: 		break;
  325: 	}
  326: 	case COMP_HTTP_REMOTE_IP: {
  327: 		char *nm_slash;
  328: 		/* handle remoteip limitations
  329: 		 *
  330: 		 * "10.0.0.1" is provided for all comparisions
  331: 		 *
  332: 		 * only for == and != we support
  333: 		 *
  334: 		 * "10.0.0.1/24"
  335: 		 */
  336: 
  337: 		if ((dc->cond == CONFIG_COND_EQ ||
  338: 		     dc->cond == CONFIG_COND_NE) &&
  339: 		    (con->dst_addr.plain.sa_family == AF_INET) &&
  340: 		    (NULL != (nm_slash = strchr(dc->string->ptr, '/')))) {
  341: 			int nm_bits;
  342: 			long nm;
  343: 			char *err;
  344: 			struct in_addr val_inp;
  345: 
  346: 			if (*(nm_slash+1) == '\0') {
  347: 				log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", dc->string);
  348: 
  349: 				return COND_RESULT_FALSE;
  350: 			}
  351: 
  352: 			nm_bits = strtol(nm_slash + 1, &err, 10);
  353: 
  354: 			if (*err) {
  355: 				log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", dc->string, err);
  356: 
  357: 				return COND_RESULT_FALSE;
  358: 			}
  359: 
  360: 			/* take IP convert to the native */
  361: 			buffer_copy_string_len(srv->cond_check_buf, dc->string->ptr, nm_slash - dc->string->ptr);
  362: #ifdef __WIN32
  363: 			if (INADDR_NONE == (val_inp.s_addr = inet_addr(srv->cond_check_buf->ptr))) {
  364: 				log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf);
  365: 
  366: 				return COND_RESULT_FALSE;
  367: 			}
  368: 
  369: #else
  370: 			if (0 == inet_aton(srv->cond_check_buf->ptr, &val_inp)) {
  371: 				log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf);
  372: 
  373: 				return COND_RESULT_FALSE;
  374: 			}
  375: #endif
  376: 
  377: 			/* build netmask */
  378: 			nm = htonl(~((1 << (32 - nm_bits)) - 1));
  379: 
  380: 			if ((val_inp.s_addr & nm) == (con->dst_addr.ipv4.sin_addr.s_addr & nm)) {
  381: 				return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
  382: 			} else {
  383: 				return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
  384: 			}
  385: 		} else {
  386: 			l = con->dst_addr_buf;
  387: 		}
  388: 		break;
  389: 	}
  390: 	case COMP_HTTP_SCHEME:
  391: 		l = con->uri.scheme;
  392: 		break;
  393: 
  394: 	case COMP_HTTP_URL:
  395: 		l = con->uri.path;
  396: 		break;
  397: 
  398: 	case COMP_HTTP_QUERY_STRING:
  399: 		l = con->uri.query;
  400: 		break;
  401: 
  402: 	case COMP_SERVER_SOCKET:
  403: 		l = srv_sock->srv_token;
  404: 		break;
  405: 
  406: 	case COMP_HTTP_REFERER: {
  407: 		data_string *ds;
  408: 
  409: 		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Referer"))) {
  410: 			l = ds->value;
  411: 		} else {
  412: 			l = srv->empty_string;
  413: 		}
  414: 		break;
  415: 	}
  416: 	case COMP_HTTP_COOKIE: {
  417: 		data_string *ds;
  418: 		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) {
  419: 			l = ds->value;
  420: 		} else {
  421: 			l = srv->empty_string;
  422: 		}
  423: 		break;
  424: 	}
  425: 	case COMP_HTTP_USER_AGENT: {
  426: 		data_string *ds;
  427: 		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) {
  428: 			l = ds->value;
  429: 		} else {
  430: 			l = srv->empty_string;
  431: 		}
  432: 		break;
  433: 	}
  434: 	case COMP_HTTP_REQUEST_METHOD: {
  435: 		const char *method = get_http_method_name(con->request.http_method);
  436: 
  437: 		/* we only have the request method as const char but we need a buffer for comparing */
  438: 
  439: 		buffer_copy_string(srv->tmp_buf, method);
  440: 
  441: 		l = srv->tmp_buf;
  442: 
  443: 		break;
  444: 	}
  445: 	case COMP_HTTP_LANGUAGE: {
  446: 		data_string *ds;
  447: 		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Accept-Language"))) {
  448: 			l = ds->value;
  449: 		} else {
  450: 			l = srv->empty_string;
  451: 		}
  452: 		break;
  453: 	}
  454: 	default:
  455: 		return COND_RESULT_FALSE;
  456: 	}
  457: 
  458: 	if (NULL == l) {
  459: 		if (con->conf.log_condition_handling) {
  460: 			log_error_write(srv, __FILE__, __LINE__,  "bsbs", dc->comp_key,
  461: 					"(", l, ") compare to NULL");
  462: 		}
  463: 		return COND_RESULT_FALSE;
  464: 	}
  465: 
  466: 	if (con->conf.log_condition_handling) {
  467: 		log_error_write(srv, __FILE__, __LINE__,  "bsbsb", dc->comp_key,
  468: 				"(", l, ") compare to ", dc->string);
  469: 	}
  470: 	switch(dc->cond) {
  471: 	case CONFIG_COND_NE:
  472: 	case CONFIG_COND_EQ:
  473: 		if (buffer_is_equal(l, dc->string)) {
  474: 			return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
  475: 		} else {
  476: 			return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
  477: 		}
  478: 		break;
  479: #ifdef HAVE_PCRE_H
  480: 	case CONFIG_COND_NOMATCH:
  481: 	case CONFIG_COND_MATCH: {
  482: 		cond_cache_t *cache = &con->cond_cache[dc->context_ndx];
  483: 		int n;
  484: 
  485: #ifndef elementsof
  486: #define elementsof(x) (sizeof(x) / sizeof(x[0]))
  487: #endif
  488: 		n = pcre_exec(dc->regex, dc->regex_study, l->ptr, l->used - 1, 0, 0,
  489: 				cache->matches, elementsof(cache->matches));
  490: 
  491: 		cache->patterncount = n;
  492: 		if (n > 0) {
  493: 			cache->comp_value = l;
  494: 			cache->comp_type  = dc->comp;
  495: 			return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
  496: 		} else {
  497: 			/* cache is already cleared */
  498: 			return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
  499: 		}
  500: 		break;
  501: 	}
  502: #endif
  503: 	default:
  504: 		/* no way */
  505: 		break;
  506: 	}
  507: 
  508: 	return COND_RESULT_FALSE;
  509: }
  510: 
  511: static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc) {
  512: 	cond_cache_t *caches = con->cond_cache;
  513: 
  514: 	if (COND_RESULT_UNSET == caches[dc->context_ndx].result) {
  515: 		if (COND_RESULT_TRUE == (caches[dc->context_ndx].result = config_check_cond_nocache(srv, con, dc))) {
  516: 			if (dc->next) {
  517: 				data_config *c;
  518: 				if (con->conf.log_condition_handling) {
  519: 					log_error_write(srv, __FILE__, __LINE__, "s",
  520: 							"setting remains of chaining to false");
  521: 				}
  522: 				for (c = dc->next; c; c = c->next) {
  523: 					caches[c->context_ndx].result = COND_RESULT_FALSE;
  524: 				}
  525: 			}
  526: 		}
  527: 		caches[dc->context_ndx].comp_type = dc->comp;
  528: 
  529: 		if (con->conf.log_condition_handling) {
  530: 			log_error_write(srv, __FILE__, __LINE__, "dss", dc->context_ndx,
  531: 					"(uncached) result:",
  532: 					caches[dc->context_ndx].result == COND_RESULT_UNSET ? "unknown" :
  533: 						(caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false"));
  534: 		}
  535: 	} else {
  536: 		if (con->conf.log_condition_handling) {
  537: 			log_error_write(srv, __FILE__, __LINE__, "dss", dc->context_ndx,
  538: 					"(cached) result:",
  539: 					caches[dc->context_ndx].result == COND_RESULT_UNSET ? "unknown" : 
  540: 						(caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false"));
  541: 		}
  542: 	}
  543: 	return caches[dc->context_ndx].result;
  544: }
  545: 
  546: /**
  547:  * reset the config-cache for a named item
  548:  *
  549:  * if the item is COND_LAST_ELEMENT we reset all items
  550:  */
  551: void config_cond_cache_reset_item(server *srv, connection *con, comp_key_t item) {
  552: 	size_t i;
  553: 
  554: 	for (i = 0; i < srv->config_context->used; i++) {
  555: 		if (item == COMP_LAST_ELEMENT || 
  556: 		    con->cond_cache[i].comp_type == item) {
  557: 			con->cond_cache[i].result = COND_RESULT_UNSET;
  558: 			con->cond_cache[i].patterncount = 0;
  559: 			con->cond_cache[i].comp_value = NULL;
  560: 		}
  561: 	}
  562: }
  563: 
  564: /**
  565:  * reset the config cache to its initial state at connection start
  566:  */
  567: void config_cond_cache_reset(server *srv, connection *con) {
  568: 	size_t i;
  569: 
  570: 	config_cond_cache_reset_all_items(srv, con);
  571: 
  572: 	for (i = 0; i < COMP_LAST_ELEMENT; i++) {
  573: 		con->conditional_is_valid[i] = 0;
  574: 	}
  575: }
  576: 
  577: int config_check_cond(server *srv, connection *con, data_config *dc) {
  578: 	if (con->conf.log_condition_handling) {
  579: 		log_error_write(srv, __FILE__, __LINE__,  "s",  "=== start of condition block ===");
  580: 	}
  581: 	return (config_check_cond_cached(srv, con, dc) == COND_RESULT_TRUE);
  582: }
  583: 
  584: int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n)
  585: {
  586: 	cond_cache_t *cache = &con->cond_cache[dc->context_ndx];
  587: 	if (n >= cache->patterncount) {
  588: 		return 0;
  589: 	}
  590: 
  591: 	n <<= 1; /* n *= 2 */
  592: 	buffer_append_string_len(buf,
  593: 			cache->comp_value->ptr + cache->matches[n],
  594: 			cache->matches[n + 1] - cache->matches[n]);
  595: 	return 1;
  596: }
  597: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>