You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1629 lines
54 KiB
1629 lines
54 KiB
4 years ago
|
/***************************************************************************/
|
||
|
/* */
|
||
|
/* Project: OpenSLP - OpenSource implementation of Service Location */
|
||
|
/* Protocol Version 2 */
|
||
|
/* */
|
||
|
/* File: slpd_database.c */
|
||
|
/* */
|
||
|
/* Abstract: This files contains an implementation of LDAPv3 search */
|
||
|
/* filters for SLP (as specified in RFC 2254). */
|
||
|
/* */
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
/* */
|
||
|
/* Please submit patches to http://www.openslp.org */
|
||
|
/* */
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
/* */
|
||
|
/* Copyright (C) 2000 Caldera Systems, Inc */
|
||
|
/* All rights reserved. */
|
||
|
/* */
|
||
|
/* Redistribution and use in source and binary forms, with or without */
|
||
|
/* modification, are permitted provided that the following conditions are */
|
||
|
/* met: */
|
||
|
/* */
|
||
|
/* Redistributions of source code must retain the above copyright */
|
||
|
/* notice, this list of conditions and the following disclaimer. */
|
||
|
/* */
|
||
|
/* Redistributions in binary form must reproduce the above copyright */
|
||
|
/* notice, this list of conditions and the following disclaimer in */
|
||
|
/* the documentation and/or other materials provided with the */
|
||
|
/* distribution. */
|
||
|
/* */
|
||
|
/* Neither the name of Caldera Systems nor the names of its */
|
||
|
/* contributors may be used to endorse or promote products derived */
|
||
|
/* from this software without specific prior written permission. */
|
||
|
/* */
|
||
|
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
|
||
|
/* `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
|
||
|
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */
|
||
|
/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA */
|
||
|
/* SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
|
||
|
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
|
||
|
/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */
|
||
|
/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
|
||
|
/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
|
||
|
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */
|
||
|
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
|
||
|
/* */
|
||
|
/***************************************************************************/
|
||
|
|
||
|
/*********/
|
||
|
/* TODO: The current implementation reparses the predicate string every */
|
||
|
/* time it is evaluated. This implementation should be refactored to */
|
||
|
/* parse the predicate string once, build some sort of data- */
|
||
|
/* structure out of that, and then compare the predicate DS with the */
|
||
|
/* attribute DS. */
|
||
|
/*********/
|
||
|
|
||
|
/*********
|
||
|
*
|
||
|
* Assumptions:
|
||
|
* - If a tag specified in the query string does not exist, that's a FALSE
|
||
|
* - The "Undefined" value mentioned in the LDAPv3 RFC (2251) is synonymous
|
||
|
* with "false".
|
||
|
* - The "Approx" operator matches as an equal operator.
|
||
|
* - Variable matching on type is strict: ie, the right-hand-side of an
|
||
|
* operator must evaluate to the same type as the tag referenced on the
|
||
|
* left-hand-side.
|
||
|
*
|
||
|
* Known Bugs/TODO:
|
||
|
* - If trash follows the last operand to a binary argument, it will be
|
||
|
* ignored if the operand is not evaluated due to short circuiting:
|
||
|
* ie, in "(&(first=*)(second<=3)trash)", "trash" will be silently
|
||
|
* ignored _if_ "(first=*)" is true.
|
||
|
* - Escaped '*' characters are treated as wildcards in the string equality
|
||
|
* operator: ie, in "(string=abc\2axyz)" will match the literal string
|
||
|
* "abc\2axyz" instead of "abc*xyz".
|
||
|
* - No operations can be performed on opaque data types.
|
||
|
*********/
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <ctype.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "slpd_predicate.h"
|
||
|
|
||
|
#include "../libslpattr/libslpattr.h"
|
||
|
#include "../libslpattr/libslpattr_internal.h"
|
||
|
|
||
|
/* The character that is a wildcard. */
|
||
|
#define WILDCARD ('*')
|
||
|
#define BRACKET_OPEN '('
|
||
|
#define BRACKET_CLOSE ')'
|
||
|
|
||
|
#ifndef MIN
|
||
|
#define MIN(x,y) (x < y ? x : y)
|
||
|
#endif
|
||
|
|
||
|
/************************* <Lifted from slp_attr.c> ***********************/
|
||
|
|
||
|
/* Tests a character to see if it reserved (as defined in RFC 2608, p11). */
|
||
|
#define IS_RESERVED(x) (((x) == '(' || (x) == ')' || (x) == ',' || (x) == '\\' || (x) == '!' || (x) == '<' || (x) == '=' || (x) == '>' || (x) == '~') || ((((char)0x01 <= (x)) && ((char)0x1F >= (x))) || ((x) == (char)0x7F)))
|
||
|
|
||
|
|
||
|
#define IS_INVALID_VALUE_CHAR(x) IS_RESERVED(x)
|
||
|
|
||
|
#define IS_INVALID_TAG_CHAR(x) (IS_RESERVED(x) || ((x) == '*') || ((x) == (char)0x0D) || ((x) == (char)0x0A) || ((x) == (char)0x09) || ((x) == '_'))
|
||
|
|
||
|
#define IS_VALID_TAG_CHAR(x) (!IS_INVALID_TAG_CHAR(x))
|
||
|
|
||
|
/************************* </Lifted from slp_attr.c> ***********************/
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
const char *substr(const char *haystack,
|
||
|
const char *needle,
|
||
|
int needle_len)
|
||
|
/* Does a case insensitive substring match for needle in haystack. */
|
||
|
/* */
|
||
|
/* Returns pointer to the start of the substring. NULL if the substring is */
|
||
|
/* not found. */
|
||
|
/* FIXME This implementation isn't exactly blazingly fast... */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
const char *hs_cur;
|
||
|
|
||
|
for(hs_cur = haystack; *hs_cur != 0; hs_cur++)
|
||
|
{
|
||
|
if(strncasecmp(hs_cur, needle, needle_len) == 0)
|
||
|
{
|
||
|
return hs_cur;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
typedef enum
|
||
|
{
|
||
|
FR_UNSET /* Placeholder. Used to detect errors. */,
|
||
|
FR_INTERNAL_SYSTEM_ERROR /* Internal error. */,
|
||
|
FR_PARSE_ERROR /* Parse error detected. */,
|
||
|
FR_MEMORY_ALLOC_FAILED /* Ran out of memory. */,
|
||
|
FR_EVAL_TRUE /* Expression successfully evaluated to true. */,
|
||
|
FR_EVAL_FALSE /* Expression successfully evaluated to false. */
|
||
|
} FilterResult;
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
int escaped_verify(char *escaped, int len, int *punescaped_len)
|
||
|
/* Verifies and finds the length of an escaped string */
|
||
|
/* */
|
||
|
/* Params: */
|
||
|
/* escaped -- (IN) string of escaped characters */
|
||
|
/* len -- (IN) length of escaped */
|
||
|
/* unescaped_len -- (OUT) pointer to location to write the unescaped */
|
||
|
/* length of escaped to */
|
||
|
/* */
|
||
|
/* Returns: */
|
||
|
/* 1 if valid string, 0 if invalid. If 0, then punescaped_count will not */
|
||
|
/* be set */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
int i;
|
||
|
int unescaped_len;
|
||
|
int seq_pos; /* Position in the current escape sequence. Set to zero when not in escape seq. */
|
||
|
|
||
|
seq_pos = 0;
|
||
|
|
||
|
for(i = unescaped_len = 0; i < len; i++)
|
||
|
{
|
||
|
/* Verify escape sequences. */
|
||
|
if(seq_pos == 1 || seq_pos == 2)
|
||
|
{
|
||
|
if(!isxdigit((int) escaped[i]))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if(seq_pos == 2)
|
||
|
{
|
||
|
seq_pos = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
seq_pos++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
unescaped_len++;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Check for escape sequences. */
|
||
|
if(escaped[i] == '\\')
|
||
|
{
|
||
|
seq_pos = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(punescaped_len)
|
||
|
{
|
||
|
*punescaped_len = unescaped_len;
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
int unescape_check(char d1, char d2, char *val)
|
||
|
/* Unescape the character represented by the two given hex values. */
|
||
|
/* */
|
||
|
/* Params: */
|
||
|
/* d1 -- (IN) First hex char */
|
||
|
/* d1 -- (IN) second hex char */
|
||
|
/* val -- (OUT) the value of the character is written to here */
|
||
|
/* */
|
||
|
/* Returns: */
|
||
|
/* 1 on successful conversion, 0 if one or more of the hex digits are */
|
||
|
/* invalid */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
if(!isxdigit((int) d1) || !isxdigit((int) d2))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if((d1 >= 'A') && (d1 <= 'F'))
|
||
|
d1 = d1 - 'A' + 0x0A;
|
||
|
else
|
||
|
d1 = d1 - '0';
|
||
|
|
||
|
if((d2 >= 'A') && (d2 <= 'F'))
|
||
|
d2 = d2 - 'A' + 0x0A;
|
||
|
else
|
||
|
d2 = d2 - '0';
|
||
|
|
||
|
*val = d2 + (d1 * 0x10);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
FilterResult unescape_cmp(const char *escaped, int escaped_len, const char *verbatim, int verbatim_len, SLPBoolean strict_len, int *punescaped_count)
|
||
|
/* Compares two strings that (potentially) contain escaped characters */
|
||
|
/* */
|
||
|
/* Params: */
|
||
|
/* escaped -- escaped string to compare */
|
||
|
/* escaped_len -- length of the escaped string (including esc chars) */
|
||
|
/* verbatim -- string to compare against (not escaped) */
|
||
|
/* verbatim_len -- length of the verbatim string */
|
||
|
/* strict_len -- if TRUE, the number of characters in verbatim that match */
|
||
|
/* must be _exactly_ verbatim_len. If FALSE, at most verbatim_len*/
|
||
|
/* characters may match */
|
||
|
/* unescaped_count -- (OUT) the number of unescaped characters. Can be */
|
||
|
/* NULL */
|
||
|
/* */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
char unesc; /* Holder for unescaped characters. */
|
||
|
|
||
|
int esc_i; /* Index into escaped. */
|
||
|
int ver_i; /* Index into verbatim. */
|
||
|
|
||
|
int unescaped_count;
|
||
|
|
||
|
unescaped_count = esc_i = ver_i = 0;
|
||
|
|
||
|
/***** Compare every pair of characters. *****/
|
||
|
while(1)
|
||
|
{
|
||
|
/**** Check for string end. ****/
|
||
|
if(esc_i >= escaped_len)
|
||
|
{
|
||
|
if(punescaped_count != NULL)
|
||
|
{
|
||
|
*punescaped_count = unescaped_count;
|
||
|
}
|
||
|
|
||
|
if(strict_len == SLP_TRUE)
|
||
|
{
|
||
|
if(ver_i >= verbatim_len)
|
||
|
{
|
||
|
return FR_EVAL_TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FR_EVAL_TRUE;
|
||
|
}
|
||
|
|
||
|
/**** Check for escaping ****/
|
||
|
if(escaped[esc_i] == '\\')
|
||
|
{
|
||
|
/*** Check for truncated escape values. ***/
|
||
|
if(esc_i + 2 >= escaped_len)
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
/*** Unescape. ***/
|
||
|
if(!unescape_check(escaped[esc_i+1], escaped[esc_i+2], &unesc))
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
/*** Increment. ***/
|
||
|
esc_i += 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
unesc = escaped[esc_i];
|
||
|
}
|
||
|
|
||
|
if(unesc != verbatim[ver_i]) /* quick check for equality*/
|
||
|
{
|
||
|
if(! isascii(unesc) /* case insensitive check */
|
||
|
|| ! isalpha(unesc)
|
||
|
|| ! isalpha(verbatim[ver_i])
|
||
|
|| tolower(unesc) != tolower(verbatim[ver_i]))
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unescaped_count++;
|
||
|
esc_i++;
|
||
|
ver_i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
void *my_memmem(char *haystack, int haystack_len, char *needle, int needle_len, int *punescaped_len)
|
||
|
/* locate an (escaped) substring */
|
||
|
/* */
|
||
|
/* Params: */
|
||
|
/* haystack -- (IN) unescaped memory to look in */
|
||
|
/* haystack_len -- (IN) length of haystack */
|
||
|
/* needle -- (IN) escaped memory to search for */
|
||
|
/* needle_len -- (IN) length of needle */
|
||
|
/* punescaped_len -- (OUT) the size of the unescaped needle. invalid if */
|
||
|
/* NULL is returned */
|
||
|
/* */
|
||
|
/* Returns: */
|
||
|
/* pointer to substring. NULL if there is no substring */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
int offset;
|
||
|
int search_len;
|
||
|
|
||
|
if(escaped_verify(haystack, haystack_len, &search_len) == 0)
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for(offset = 0; offset <= search_len; offset++)
|
||
|
{
|
||
|
FilterResult err;
|
||
|
|
||
|
err = unescape_cmp(needle, needle_len, haystack + offset, haystack_len - offset, SLP_FALSE, punescaped_len);
|
||
|
if(err == FR_EVAL_TRUE)
|
||
|
{
|
||
|
return(void *)(haystack + offset);
|
||
|
}
|
||
|
else if(err != FR_EVAL_FALSE)
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
FilterResult wildcard_wc_str(char *pattern, int pattern_len, char *str, int str_len)
|
||
|
/* */
|
||
|
/* Params: */
|
||
|
/* pattern -- the pattern to evaluate */
|
||
|
/* pattern_len -- the length of the pattern (in bytes) */
|
||
|
/* str -- the string to test the pattern on */
|
||
|
/* str_len -- the length of the string */
|
||
|
/* */
|
||
|
/* Returns: */
|
||
|
/* -1 error, 0 success, 1 failure */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
char *text_start; /* Pointer to the text following the WC(s) */
|
||
|
int text_len; /* Length of the text. */
|
||
|
char *found; /* The start of a string found in a search. */
|
||
|
|
||
|
char *rem_start; /* Start of the remaining characters being searched for. */
|
||
|
int rem_len; /* Length of the remaining charcters being searched for. */
|
||
|
|
||
|
if(pattern_len == 0 && str_len == 0)
|
||
|
{
|
||
|
return FR_EVAL_TRUE;
|
||
|
}
|
||
|
|
||
|
if(pattern_len == 0)
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
/***** Find text following wildcard *****/
|
||
|
/**** Find end of WCs ****/
|
||
|
text_start = pattern;
|
||
|
text_len = 0;
|
||
|
while(*text_start == WILDCARD)
|
||
|
{
|
||
|
text_start++;
|
||
|
if(text_start == pattern + pattern_len)
|
||
|
{
|
||
|
/* No following text. Therefore we match. */
|
||
|
return FR_EVAL_TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**** Find end of text. ****/
|
||
|
found = memchr(text_start, WILDCARD, pattern_len - text_len);
|
||
|
if(found == NULL)
|
||
|
{
|
||
|
/* No trailing WC. Set to end. */
|
||
|
found = pattern + pattern_len;
|
||
|
}
|
||
|
text_len = found - text_start;
|
||
|
|
||
|
/***** Look for the text in the source string *****/
|
||
|
rem_start = str;
|
||
|
rem_len = str_len;
|
||
|
while(1)
|
||
|
{
|
||
|
int result;
|
||
|
int match_len;
|
||
|
|
||
|
found = my_memmem(rem_start, rem_len, text_start, text_len, &match_len);
|
||
|
assert(found == NULL || ((found >= rem_start) && (found <= rem_start + rem_len)));
|
||
|
|
||
|
if(found == NULL)
|
||
|
{
|
||
|
/* Desired text is not in the string. */
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
rem_len = str_len - (found - str);
|
||
|
rem_start = found + 1;
|
||
|
|
||
|
/**** Make recursive call. ****/
|
||
|
result = wildcard_wc_str(text_start + text_len, (pattern + pattern_len) - (text_start + text_len), found + text_len, rem_len - match_len);
|
||
|
|
||
|
if(result != FR_EVAL_FALSE)
|
||
|
{
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
/*NOTREACHED*/
|
||
|
assert(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
FilterResult wildcard(const char *pattern, int pattern_len, const char *str, int str_len)
|
||
|
/* Check the pattern against the given string. Public interface to wildcard */
|
||
|
/* functionality. */
|
||
|
/* */
|
||
|
/* Params: */
|
||
|
/* pattern -- the pattern to evaluate */
|
||
|
/* pattern_len -- the length of the pattern (in bytes) */
|
||
|
/* str -- the string to test the pattern on */
|
||
|
/* str_len -- the length of the string */
|
||
|
/* */
|
||
|
/* Returns: */
|
||
|
/* -1 error, 0 success, 1 failure */
|
||
|
/* */
|
||
|
/* Implementation: */
|
||
|
/* This is the interface to wildcard finding. It actually just prepares */
|
||
|
/* for repeated calls to wildcard_wc_str() */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
char *first_wc; /* Position of the first WC in the pattern. */
|
||
|
int first_wc_len; /* Position of first_wc in pattern. */
|
||
|
int unescaped_first_wc_len; /* Position of first_wc in unescaped version of pattern. */
|
||
|
|
||
|
/***** Check text up to first WC *****/
|
||
|
/**** Get text ****/
|
||
|
first_wc = memchr(pattern, WILDCARD, pattern_len);
|
||
|
if(first_wc == NULL)
|
||
|
{
|
||
|
/* No wc */
|
||
|
return unescape_cmp(pattern, pattern_len, str, str_len, SLP_TRUE, NULL);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
first_wc_len = first_wc - pattern;
|
||
|
}
|
||
|
|
||
|
/**** Compare. ****/
|
||
|
unescaped_first_wc_len = first_wc_len;
|
||
|
if(first_wc_len > 0)
|
||
|
{
|
||
|
FilterResult err;
|
||
|
err = unescape_cmp(pattern, first_wc_len, str, str_len, SLP_FALSE, &unescaped_first_wc_len);
|
||
|
if(err != FR_EVAL_TRUE)
|
||
|
{
|
||
|
/* The leading text is different. */
|
||
|
return err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***** Start recursive call. *****/
|
||
|
return wildcard_wc_str(first_wc, pattern_len - first_wc_len, (char *)str + unescaped_first_wc_len, str_len - unescaped_first_wc_len);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
int is_bool_string(const char *str, int str_len, SLPBoolean *val)
|
||
|
/* Tests a string to see if it is a boolean. */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
if(str_len == 4 && memcmp(str, "true", 4) == 0) /* 4 -> length of string "true" */
|
||
|
{
|
||
|
*val = SLP_TRUE;
|
||
|
return 1;
|
||
|
}
|
||
|
if(str_len == 5 && memcmp(str, "false", 5) == 0) /* 5 -> len of "false" */
|
||
|
{
|
||
|
*val = SLP_FALSE;
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
typedef enum
|
||
|
{
|
||
|
EQUAL, APPROX, GREATER, LESS, PRESENT
|
||
|
} Operation;
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
FilterResult int_op(SLPAttributes slp_attr,
|
||
|
char *tag,
|
||
|
int tag_len,
|
||
|
char *rhs,
|
||
|
Operation op)
|
||
|
/* Perform an integer operation. */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
int rhs_val; /* The converted value of rhs. */
|
||
|
char *end; /* Pointer to the end of op. */
|
||
|
var_t *var; /* Pointer to the variable named by tag. */
|
||
|
value_t *value; /* A value in var. */
|
||
|
|
||
|
FilterResult result; /* Value to return. */
|
||
|
|
||
|
assert(op != PRESENT);
|
||
|
|
||
|
result = FR_UNSET; /* For verification. */ /* TODO Only do this in debug. */
|
||
|
|
||
|
/***** Verify and convert rhs. *****/
|
||
|
rhs_val = strtol(rhs, &end, 10);
|
||
|
|
||
|
if(*end != 0 && *end != BRACKET_CLOSE)
|
||
|
{
|
||
|
/* Trying to compare an int with a non-int. */
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
/***** Get variable. *****/
|
||
|
var = attr_val_find_str((struct xx_SLPAttributes *)slp_attr, tag, tag_len);
|
||
|
if(var == NULL)
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
/**** Check type. ****/
|
||
|
if(var->type != SLP_INTEGER)
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***** Compare. *****/
|
||
|
value = var->list;
|
||
|
assert(value != NULL);
|
||
|
assert(op != PRESENT);
|
||
|
switch(op)
|
||
|
{
|
||
|
case(EQUAL):
|
||
|
result = FR_EVAL_FALSE;
|
||
|
for(; value; value = value->next)
|
||
|
{
|
||
|
if(value->data.va_int == rhs_val)
|
||
|
{
|
||
|
result = FR_EVAL_TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case(APPROX):
|
||
|
assert(0); /* TODO: Figure out how this works later. */
|
||
|
result = FR_EVAL_FALSE;
|
||
|
break;
|
||
|
case(GREATER):
|
||
|
result = FR_EVAL_FALSE;
|
||
|
for(; value; value = value->next)
|
||
|
{
|
||
|
if(value->data.va_int >= rhs_val)
|
||
|
{
|
||
|
result = FR_EVAL_TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case(LESS):
|
||
|
result = FR_EVAL_FALSE;
|
||
|
for(; value; value = value->next)
|
||
|
{
|
||
|
if(value->data.va_int <= rhs_val)
|
||
|
{
|
||
|
result = FR_EVAL_TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
assert(0);
|
||
|
}
|
||
|
|
||
|
assert(result != FR_UNSET);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
FilterResult keyw_op(SLPAttributes slp_attr,
|
||
|
char *tag,
|
||
|
char *rhs,
|
||
|
Operation op)
|
||
|
/* Perform a keyword operation. */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
/* Note that the only test keywords are allowed to undergo is PRESENT,
|
||
|
* also note that PRESENT is handled by our calling function.
|
||
|
*
|
||
|
* We are therefore quite justified in saying:
|
||
|
*/
|
||
|
assert(op != PRESENT);
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
FilterResult bool_op(SLPAttributes slp_attr,
|
||
|
char *tag,
|
||
|
int tag_len,
|
||
|
char *rhs,
|
||
|
int rhs_len,
|
||
|
Operation op)
|
||
|
/* Perform a boolean operation. */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
SLPBoolean rhs_val; /* The value of the rhs. */
|
||
|
var_t *var;
|
||
|
|
||
|
FilterResult result;
|
||
|
|
||
|
assert(op != PRESENT);
|
||
|
|
||
|
result = FR_UNSET;
|
||
|
|
||
|
/* Note that the only valid non-PRESENT operator is EQUAL. */
|
||
|
if(op != EQUAL)
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
/***** Get and check the rhs value. *****/
|
||
|
if(!is_bool_string(rhs, rhs_len, &rhs_val))
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
/***** Get the tag value. *****/
|
||
|
var = attr_val_find_str((struct xx_SLPAttributes *)slp_attr, tag, tag_len);
|
||
|
if(var == NULL)
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
/**** Check type. ****/
|
||
|
if(var->type != SLP_BOOLEAN)
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
/***** Compare. *****/
|
||
|
if(var->list->data.va_bool == rhs_val)
|
||
|
{
|
||
|
result = FR_EVAL_TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
result = FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
assert(result != FR_UNSET);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
FilterResult str_op(SLPAttributes slp_attr,
|
||
|
char *tag,
|
||
|
int tag_len,
|
||
|
char *rhs,
|
||
|
int rhs_len,
|
||
|
Operation op)
|
||
|
/* Perform a string operation. */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
char *str_val; /* Converted value of rhs. */
|
||
|
int str_len; /* Length of converted value. */
|
||
|
|
||
|
var_t *var;
|
||
|
|
||
|
assert(op != PRESENT);
|
||
|
|
||
|
/***** Verify rhs. *****/
|
||
|
str_val = rhs;
|
||
|
str_len = rhs_len;
|
||
|
|
||
|
/***** Get tag value. *****/
|
||
|
var = attr_val_find_str((struct xx_SLPAttributes *)slp_attr, tag, tag_len);
|
||
|
if(var == NULL)
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
/**** Check type. ****/
|
||
|
if(var->type != SLP_STRING)
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***** Compare. *****/
|
||
|
assert(op != PRESENT);
|
||
|
|
||
|
if(op == APPROX)
|
||
|
{
|
||
|
assert(0); /* TODO: Figure out how this works later. */
|
||
|
}
|
||
|
else if(op == EQUAL)
|
||
|
{
|
||
|
int result;
|
||
|
value_t *value;
|
||
|
|
||
|
for(value = var->list; value; value = value->next)
|
||
|
{
|
||
|
result = wildcard(str_val, str_len, value->data.va_str, value->unescaped_len);
|
||
|
/* We only keep going if the test fails. Let caller handle other problems. */
|
||
|
if(result != FR_EVAL_FALSE)
|
||
|
{
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
value_t *value;
|
||
|
/* We know that the op must be comparative. */
|
||
|
assert(op == LESS || op == GREATER);
|
||
|
|
||
|
for(value = var->list; value; value = value->next)
|
||
|
{
|
||
|
int result;
|
||
|
|
||
|
result = memcmp(value->data.va_str, str_val, MIN(str_len, value->unescaped_len));
|
||
|
if(
|
||
|
(result <= 0 && op == LESS)
|
||
|
|| (result >= 0 && op == GREATER)
|
||
|
)
|
||
|
{
|
||
|
return FR_EVAL_TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
FilterResult opaque_op(SLPAttributes slp_attr,
|
||
|
char *tag,
|
||
|
int tag_len,
|
||
|
char *rhs,
|
||
|
int rhs_len,
|
||
|
Operation op)
|
||
|
/* Perform an opaque operation. */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
char *str_val; /* Converted value of rhs. */
|
||
|
int str_len; /* Length of converted value. */
|
||
|
|
||
|
var_t *var;
|
||
|
|
||
|
assert(op != PRESENT);
|
||
|
|
||
|
/***** Verify and convert rhs. *****/
|
||
|
str_val = rhs;
|
||
|
str_len = rhs_len;
|
||
|
|
||
|
/***** Get tag value. *****/
|
||
|
var = attr_val_find_str((struct xx_SLPAttributes *)slp_attr, tag, tag_len);
|
||
|
if(var == NULL)
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
/**** Check type. ****/
|
||
|
if(var->type != SLP_OPAQUE)
|
||
|
{
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***** Compare. *****/
|
||
|
assert(op != PRESENT);
|
||
|
|
||
|
if(op == APPROX)
|
||
|
{
|
||
|
assert(0); /* TODO: Figure out how this works later. */
|
||
|
}
|
||
|
else if(op == EQUAL)
|
||
|
{
|
||
|
int result;
|
||
|
value_t *value;
|
||
|
|
||
|
for(value = var->list; value; value = value->next)
|
||
|
{
|
||
|
result = wildcard(str_val, str_len, value->data.va_str, value->unescaped_len);
|
||
|
/* We only keep going if the test fails. Let caller handle other problems. */
|
||
|
if(result != FR_EVAL_FALSE)
|
||
|
{
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
value_t *value;
|
||
|
/* We know that the op must be comparative. */
|
||
|
assert(op == LESS || op == GREATER);
|
||
|
|
||
|
for(value = var->list; value; value = value->next)
|
||
|
{
|
||
|
int result;
|
||
|
|
||
|
result = memcmp(value->data.va_str, str_val, MIN(str_len, value->unescaped_len));
|
||
|
if(
|
||
|
(result <= 0 && op == LESS)
|
||
|
|| (result >= 0 && op == GREATER)
|
||
|
)
|
||
|
{
|
||
|
return FR_EVAL_TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
int is_int_string(const char *str)
|
||
|
/* Tests a string to see if it consists wholly of numeric characters. */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for(i=0; str[i] != '\0'; i++)
|
||
|
{
|
||
|
if(!((!isdigit((int)str[i])) || str[i] == '-'))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
char *find_substr(char *src, int src_len, char *to_find)
|
||
|
/* Returns ptr to start of substring, or null. */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for(i = 0; i < src_len; i++)
|
||
|
{
|
||
|
if(src[i] == *to_find)
|
||
|
{
|
||
|
int old_i = i; /* Save the old start. */
|
||
|
int find_index;
|
||
|
for(find_index = 0; (to_find[find_index] != '\0') && (i < src_len) && (to_find[find_index] == src[i]); to_find++, i++)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
if(to_find[find_index] == '\0')
|
||
|
{
|
||
|
return src + i;
|
||
|
}
|
||
|
|
||
|
i = old_i; /* Restor the old start. */
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
char *find_bracket_end(const char *str)
|
||
|
/* Finds a bracket matched to this one. Returns 0 if there isn't one. */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
int open_count;
|
||
|
const char *cur;
|
||
|
|
||
|
if(*str != BRACKET_OPEN)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
open_count = 0;
|
||
|
|
||
|
/***** Count brackets. *****/
|
||
|
for(cur = str; *cur != '\0'; cur++)
|
||
|
{
|
||
|
assert(open_count >= 0);
|
||
|
|
||
|
/**** Check current character. ****/
|
||
|
if(*cur == BRACKET_OPEN)
|
||
|
{
|
||
|
open_count++;
|
||
|
}
|
||
|
else if(*cur == BRACKET_CLOSE)
|
||
|
{
|
||
|
open_count--;
|
||
|
}
|
||
|
|
||
|
/**** Check if we've found bracket end. ****/
|
||
|
if(open_count == 0)
|
||
|
{
|
||
|
return(char *)cur;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
struct pair
|
||
|
/* Represents a string and its length. */
|
||
|
{
|
||
|
char *start;
|
||
|
char *end; /* Stop reading _BEFORE_ end. ie, at end-1. */
|
||
|
};
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
|
||
|
#if defined(ENABLE_SLPv1)
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
FilterResult filterv1(const char *start,
|
||
|
const char **end,
|
||
|
SLPAttributes slp_attr,
|
||
|
int recursion_depth)
|
||
|
/* Parameters: */
|
||
|
/* start -- (IN) The start of the string to work in. */
|
||
|
/* end -- (OUT) The end of the string processed. After successful processing*/
|
||
|
/* (ie, no PARSE_ERRORs), this will be pointed at the character*/
|
||
|
/* following the last char in this level of the expression. */
|
||
|
/* slp_attr -- (IN) The attributes handle to compare on. */
|
||
|
/* return_value -- (OUT) The result of the search. Only valid if SLP_OK was */
|
||
|
/* returned. */
|
||
|
/* */
|
||
|
/* Returns: SLP_OK on successful search (ie, the search was do-able). */
|
||
|
/* SLP_PARSE_ERROR on search error. The end of the expression is */
|
||
|
/* returned through end. */
|
||
|
/* NOTE: This attempt only implements one comparision operator (==) */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
char *operator; /* Pointer to the operator substring. */
|
||
|
const char *cur; /* Current working character. */
|
||
|
const char *last_char; /* The last character in the working string. */
|
||
|
FilterResult err = FR_UNSET; /* The result of an evaluation. */
|
||
|
FilterResult stop_condition = FR_UNSET;
|
||
|
|
||
|
if(recursion_depth <= 0)
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
recursion_depth--;
|
||
|
|
||
|
if(*start == 0)
|
||
|
{
|
||
|
*end = start;
|
||
|
return FR_EVAL_TRUE;
|
||
|
}
|
||
|
|
||
|
if(*start != BRACKET_OPEN)
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
/***** Get the current expression. *****/
|
||
|
last_char = *end = find_bracket_end(start);
|
||
|
if(*end == 0)
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
(*end)++; /* Move the end pointer past the closing bracket. */
|
||
|
|
||
|
/****
|
||
|
* Check for the three legal characters that can follow an expression,
|
||
|
* if the following character isn't one of those, the following character
|
||
|
* is trash.
|
||
|
****/
|
||
|
if(!(**end == BRACKET_OPEN || **end == BRACKET_CLOSE || **end == '\0'))
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
/***** check for boolean op. *****/
|
||
|
cur = start;
|
||
|
cur++;
|
||
|
|
||
|
switch(*cur)
|
||
|
{
|
||
|
case('&'): /***** And. *****/
|
||
|
case('|'): /***** Or. *****/
|
||
|
{
|
||
|
if(*cur == '&')
|
||
|
{
|
||
|
stop_condition = FR_EVAL_FALSE;
|
||
|
}
|
||
|
else if(*cur == '|')
|
||
|
{
|
||
|
stop_condition = FR_EVAL_TRUE;
|
||
|
}
|
||
|
|
||
|
cur++; /* Move past operator. */
|
||
|
|
||
|
/*** Ensure that we have at least one operator. ***/
|
||
|
if(*cur != BRACKET_OPEN || cur >= last_char)
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
/*** Evaluate each operand. ***/
|
||
|
/* NOTE: Due to the condition on the above "if", we are guarenteed that the first iteration of the loop is valid. */
|
||
|
do
|
||
|
{
|
||
|
err = filterv1(cur, &cur, slp_attr, recursion_depth);
|
||
|
/*** Propagate errors. ***/
|
||
|
if(err != FR_EVAL_TRUE && err != FR_EVAL_FALSE)
|
||
|
{
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*** Short circuit. ***/
|
||
|
if(err == stop_condition)
|
||
|
{
|
||
|
return stop_condition;
|
||
|
}
|
||
|
} while(*cur == BRACKET_OPEN && cur < last_char);
|
||
|
|
||
|
|
||
|
/*** If we ever get here, it means we've evaluated every operand without short circuiting -- meaning that the operation failed. ***/
|
||
|
return(stop_condition == FR_EVAL_TRUE ? FR_EVAL_FALSE : FR_EVAL_TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
case('!'): /***** Not. *****/
|
||
|
/**** Child. ****/
|
||
|
cur++;
|
||
|
err = filterv1(cur, &cur, slp_attr, recursion_depth);
|
||
|
/*** Return errors. ***/
|
||
|
if(err != FR_EVAL_TRUE && err != FR_EVAL_FALSE)
|
||
|
{
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*** Perform "not". ***/
|
||
|
return(err == FR_EVAL_TRUE ? FR_EVAL_FALSE : FR_EVAL_TRUE);
|
||
|
|
||
|
default: /***** Unknown operator. *****/
|
||
|
;
|
||
|
/* We don't do anything here because this will catch the first character of every leaf predicate. */
|
||
|
}
|
||
|
|
||
|
/***** Check for leaf operator. *****/
|
||
|
if(IS_VALID_TAG_CHAR(*cur))
|
||
|
{
|
||
|
Operation op;
|
||
|
char *lhs, *rhs; /* The two operands. */
|
||
|
char *val_start; /* The character after the operator. ie, the start of the rhs. */
|
||
|
int lhs_len, rhs_len; /* Length of the lhs/rhs. */
|
||
|
SLPType type;
|
||
|
SLPError slp_err;
|
||
|
|
||
|
/**** Demux leaf op. ****/
|
||
|
/* Support only "==", we look for the equals
|
||
|
* sign, and then poke around on either side of that for the real
|
||
|
* value.
|
||
|
*/
|
||
|
operator = (char *)memchr(cur, '=', last_char - cur);
|
||
|
if(operator == 0 || *(operator + 1) != '=')
|
||
|
{
|
||
|
/**** No search operator. ****/
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
/* The rhs always follows the operator. (This doesn't really make
|
||
|
sense for PRESENT ops, but ignore that). */
|
||
|
val_start = operator + 2;
|
||
|
|
||
|
if(operator == cur)
|
||
|
{/* Check that we can poke back one char. */
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
op = EQUAL;
|
||
|
|
||
|
/***** Get operands. *****/
|
||
|
/**** Left. ****/
|
||
|
lhs_len = operator - cur;
|
||
|
lhs = (char *)cur;
|
||
|
|
||
|
/**** Right ****/
|
||
|
rhs_len = last_char - val_start;
|
||
|
rhs = val_start;
|
||
|
|
||
|
/***** Do leaf operation. *****/
|
||
|
/**** Check that tag exists. ****/
|
||
|
slp_err = SLPAttrGetType_len(slp_attr, lhs, lhs_len, &type);
|
||
|
if(slp_err == SLP_TAG_ERROR)
|
||
|
{ /* Tag doesn't exist. */
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
else if(slp_err == SLP_OK)
|
||
|
{ /* Tag exists. */
|
||
|
/**** Do operation. *****/
|
||
|
if(op == PRESENT)
|
||
|
{
|
||
|
/*** Since the PRESENT operation is the same for all types,
|
||
|
do that now. ***/
|
||
|
return FR_EVAL_TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*** A type-specific operation. ***/
|
||
|
switch(type)
|
||
|
{
|
||
|
case(SLP_BOOLEAN):
|
||
|
err = bool_op(slp_attr, lhs, lhs_len, rhs, rhs_len, op);
|
||
|
break;
|
||
|
case(SLP_INTEGER):
|
||
|
err = int_op(slp_attr, lhs, lhs_len, rhs, op);
|
||
|
break;
|
||
|
case(SLP_KEYWORD):
|
||
|
err = keyw_op(slp_attr, lhs, rhs, op);
|
||
|
break;
|
||
|
case(SLP_STRING):
|
||
|
err = str_op(slp_attr, lhs, lhs_len, rhs, rhs_len, op);
|
||
|
break;
|
||
|
case(SLP_OPAQUE):
|
||
|
assert(0); /* Opaque is not yet supported. */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{ /* Some other tag-related error. */
|
||
|
err = FR_INTERNAL_SYSTEM_ERROR;
|
||
|
}
|
||
|
|
||
|
assert(err != FR_UNSET);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/***** No operator. *****/
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
FilterResult filter(const char *start,
|
||
|
const char **end,
|
||
|
SLPAttributes slp_attr,
|
||
|
int recursion_depth)
|
||
|
/* Parameters: */
|
||
|
/* start -- (IN) The start of the string to work in. */
|
||
|
/* end -- (OUT) The end of the string processed. After successful processing*/
|
||
|
/* (ie, no PARSE_ERRORs), this will be pointed at the character*/
|
||
|
/* following the last char in this level of the expression. */
|
||
|
/* slp_attr -- (IN) The attributes handle to compare on. */
|
||
|
/* return_value -- (OUT) The result of the search. Only valid if SLP_OK was */
|
||
|
/* returned. */
|
||
|
/* */
|
||
|
/* Returns: SLP_OK on successful search (ie, the search was do-able). */
|
||
|
/* SLP_PARSE_ERROR on search error. The end of the expression is */
|
||
|
/* returned through end. */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
{
|
||
|
char *operator; /* Pointer to the operator substring. */
|
||
|
const char *cur; /* Current working character. */
|
||
|
const char *last_char; /* The last character in the working string. */
|
||
|
FilterResult err = FR_UNSET; /* The result of an evaluation. */
|
||
|
FilterResult stop_condition = FR_UNSET;
|
||
|
|
||
|
if(recursion_depth <= 0)
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
recursion_depth--;
|
||
|
|
||
|
if(*start != BRACKET_OPEN)
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
/***** Get the current expression. *****/
|
||
|
last_char = *end = find_bracket_end(start);
|
||
|
if(*end == 0)
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
(*end)++; /* Move the end pointer past the closing bracket. */
|
||
|
|
||
|
/****
|
||
|
* Check for the three legal characters that can follow an expression,
|
||
|
* if the following character isn't one of those, the following character
|
||
|
* is trash.
|
||
|
****/
|
||
|
if(!(**end == BRACKET_OPEN || **end == BRACKET_CLOSE || **end == '\0'))
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
/***** check for boolean op. *****/
|
||
|
cur = start;
|
||
|
cur++;
|
||
|
|
||
|
switch(*cur)
|
||
|
{
|
||
|
case('&'): /***** And. *****/
|
||
|
case('|'): /***** Or. *****/
|
||
|
{
|
||
|
if(*cur == '&')
|
||
|
{
|
||
|
stop_condition = FR_EVAL_FALSE;
|
||
|
}
|
||
|
else if(*cur == '|')
|
||
|
{
|
||
|
stop_condition = FR_EVAL_TRUE;
|
||
|
}
|
||
|
|
||
|
cur++; /* Move past operator. */
|
||
|
|
||
|
/*** Ensure that we have at least one operator. ***/
|
||
|
if(*cur != BRACKET_OPEN || cur >= last_char)
|
||
|
{
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
/*** Evaluate each operand. ***/
|
||
|
/* NOTE: Due to the condition on the above "if", we are guarenteed that the first iteration of the loop is valid. */
|
||
|
do
|
||
|
{
|
||
|
err = filter(cur, &cur, slp_attr, recursion_depth);
|
||
|
/*** Propagate errors. ***/
|
||
|
if(err != FR_EVAL_TRUE && err != FR_EVAL_FALSE)
|
||
|
{
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*** Short circuit. ***/
|
||
|
if(err == stop_condition)
|
||
|
{
|
||
|
return stop_condition;
|
||
|
}
|
||
|
} while(*cur == BRACKET_OPEN && cur < last_char);
|
||
|
|
||
|
|
||
|
/*** If we ever get here, it means we've evaluated every operand without short circuiting -- meaning that the operation failed. ***/
|
||
|
return(stop_condition == FR_EVAL_TRUE ? FR_EVAL_FALSE : FR_EVAL_TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
case('!'): /***** Not. *****/
|
||
|
/**** Child. ****/
|
||
|
cur++;
|
||
|
err = filter(cur, &cur, slp_attr, recursion_depth);
|
||
|
/*** Return errors. ***/
|
||
|
if(err != FR_EVAL_TRUE && err != FR_EVAL_FALSE)
|
||
|
{
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*** Perform "not". ***/
|
||
|
return(err == FR_EVAL_TRUE ? FR_EVAL_FALSE : FR_EVAL_TRUE);
|
||
|
|
||
|
default: /***** Unknown operator. *****/
|
||
|
;
|
||
|
/* We don't do anything here because this will catch the first character of every leaf predicate. */
|
||
|
}
|
||
|
|
||
|
/***** Check for leaf operator. *****/
|
||
|
if(IS_VALID_TAG_CHAR(*cur))
|
||
|
{
|
||
|
Operation op;
|
||
|
char *lhs, *rhs; /* The two operands. */
|
||
|
char *val_start; /* The character after the operator. ie, the start of the rhs. */
|
||
|
int lhs_len, rhs_len; /* Length of the lhs/rhs. */
|
||
|
SLPType type;
|
||
|
SLPError slp_err;
|
||
|
|
||
|
/**** Demux leaf op. ****/
|
||
|
/* Since all search operators contain a "=", we look for the equals
|
||
|
* sign, and then poke around on either side of that for the real
|
||
|
* value.
|
||
|
*/
|
||
|
operator = (char *)memchr(cur, '=', last_char - cur);
|
||
|
if(operator == 0)
|
||
|
{
|
||
|
/**** No search operator. ****/
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
/* The rhs always follows the operator. (This doesn't really make
|
||
|
sense for PRESENT ops, but ignore that). */
|
||
|
val_start = operator + 1;
|
||
|
|
||
|
/* Check for APPROX, GREATER, or LESS. Note that we shuffle the
|
||
|
operator pointer back to point at the start of the op. */
|
||
|
if(operator == cur)
|
||
|
{/* Check that we can poke back one char. */
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
switch(*(operator - 1))
|
||
|
{
|
||
|
case('~'):
|
||
|
op = EQUAL; /* See Assumptions. */
|
||
|
operator--;
|
||
|
break;
|
||
|
case('>'):
|
||
|
op = GREATER;
|
||
|
operator--;
|
||
|
break;
|
||
|
case('<'):
|
||
|
op = LESS;
|
||
|
operator--;
|
||
|
break;
|
||
|
default: /* No prefix to the '='. */
|
||
|
/**** Check for PRESENT. ****/
|
||
|
if((operator == last_char - 2) && (*(operator+1) == '*'))
|
||
|
{
|
||
|
op = PRESENT;
|
||
|
}
|
||
|
/**** It's none of the above: therefore it's EQUAL. ****/
|
||
|
else
|
||
|
{
|
||
|
op = EQUAL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***** Get operands. *****/
|
||
|
/**** Left. ****/
|
||
|
lhs_len = operator - cur;
|
||
|
lhs = (char *)cur;
|
||
|
|
||
|
/**** Right ****/
|
||
|
rhs_len = last_char - val_start;
|
||
|
rhs = val_start;
|
||
|
|
||
|
/***** Do leaf operation. *****/
|
||
|
/**** Check that tag exists. ****/
|
||
|
slp_err = SLPAttrGetType_len(slp_attr, lhs, lhs_len, &type);
|
||
|
if(slp_err == SLP_TAG_ERROR)
|
||
|
{ /* Tag doesn't exist. */
|
||
|
return FR_EVAL_FALSE;
|
||
|
}
|
||
|
else if(slp_err == SLP_OK)
|
||
|
{ /* Tag exists. */
|
||
|
/**** Do operation. *****/
|
||
|
if(op == PRESENT)
|
||
|
{
|
||
|
/*** Since the PRESENT operation is the same for all types,
|
||
|
do that now. ***/
|
||
|
return FR_EVAL_TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*** A type-specific operation. ***/
|
||
|
switch(type)
|
||
|
{
|
||
|
case(SLP_BOOLEAN):
|
||
|
err = bool_op(slp_attr, lhs, lhs_len, rhs, rhs_len, op);
|
||
|
break;
|
||
|
case(SLP_INTEGER):
|
||
|
err = int_op(slp_attr, lhs, lhs_len, rhs, op);
|
||
|
break;
|
||
|
case(SLP_KEYWORD):
|
||
|
err = keyw_op(slp_attr, lhs, rhs, op);
|
||
|
break;
|
||
|
case(SLP_STRING):
|
||
|
err = str_op(slp_attr, lhs, lhs_len, rhs, rhs_len, op);
|
||
|
break;
|
||
|
case(SLP_OPAQUE):
|
||
|
assert(0); /* Opaque is not yet supported. */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{ /* Some other tag-related error. */
|
||
|
err = FR_INTERNAL_SYSTEM_ERROR;
|
||
|
}
|
||
|
|
||
|
assert(err != FR_UNSET);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/***** No operator. *****/
|
||
|
return FR_PARSE_ERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*=========================================================================*/
|
||
|
int SLPDPredicateTest(int version,
|
||
|
int attrlistlen,
|
||
|
const char* attrlist,
|
||
|
int predicatelen,
|
||
|
const char* predicate)
|
||
|
/* Determine whether the specified attribute list satisfies */
|
||
|
/* the specified predicate */
|
||
|
/* */
|
||
|
/* version (IN) SLP version of the predicate string (should always be */
|
||
|
/* 2 since we don't handle SLPv1 predicates yet) */
|
||
|
/* */
|
||
|
/* attrlistlen (IN) length of attrlist */
|
||
|
/* */
|
||
|
/* attr (IN) attribute list to test */
|
||
|
/* */
|
||
|
/* predicatelen (IN) length of the predicate string */
|
||
|
/* */
|
||
|
/* predicate (IN) the predicate string */
|
||
|
/* */
|
||
|
/* Returns: Boolean value. Zero of test fails. Non-zero if test fails */
|
||
|
/* or if there is a parse error in the predicate string */
|
||
|
/*=========================================================================*/
|
||
|
{
|
||
|
SLPAttributes attr;
|
||
|
const char *end; /* Pointer to the end of the parsed attribute string. */
|
||
|
FilterResult err;
|
||
|
char attrnull;
|
||
|
char prednull;
|
||
|
int result = 0;
|
||
|
|
||
|
/* An NULL or empty string is always true. */
|
||
|
if(predicate == 0 || *(char *)predicate == 0)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Attempt v1 too. */
|
||
|
if(version != 2
|
||
|
#if defined(ENABLE_SLPv1)
|
||
|
&& version != 1
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* TRICKY: Temporarily NULL terminate the attribute list */
|
||
|
/* and the predicate string. We can do this because */
|
||
|
/* there is room in the corresponding SLPv2 SRVREG */
|
||
|
/* and SRVRQST messages. Basically we are squashing */
|
||
|
/* the authcount and the spi string length. Don't */
|
||
|
/* worry, we fix things up later and it is MUCH */
|
||
|
/* faster than a malloc() for a new buffer 1 byte */
|
||
|
/* longer! */
|
||
|
prednull = predicate[predicatelen];
|
||
|
((char*)predicate)[predicatelen] = 0;
|
||
|
attrnull = attrlist[attrlistlen];
|
||
|
((char*)attrlist)[attrlistlen] = 0;
|
||
|
|
||
|
/* Generate an SLPAttr from the comma delimited list */
|
||
|
if(SLPAttrAlloc("en", NULL, SLP_FALSE, &attr) == 0)
|
||
|
{
|
||
|
if(SLPAttrFreshen(attr, attrlist) == 0)
|
||
|
{
|
||
|
switch(version)
|
||
|
{
|
||
|
#if defined(ENABLE_SLPv1)
|
||
|
case 1:
|
||
|
err=filterv1(predicate, &end, attr, SLPD_ATTR_RECURSION_DEPTH);
|
||
|
break;
|
||
|
#endif
|
||
|
default:
|
||
|
err=filter(predicate, &end, attr, SLPD_ATTR_RECURSION_DEPTH);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Check for trailing trash data. */
|
||
|
if((err == FR_EVAL_TRUE || err == FR_EVAL_FALSE) && *end != 0)
|
||
|
{
|
||
|
result = 0;
|
||
|
}
|
||
|
else if(err == FR_EVAL_TRUE)
|
||
|
{
|
||
|
result = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SLPAttrFree(attr);
|
||
|
}
|
||
|
|
||
|
/* Un null terminate */
|
||
|
((char*)predicate)[predicatelen] = prednull;
|
||
|
((char*)attrlist)[attrlistlen] = attrnull;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*=========================================================================*/
|
||
|
int SLPDFilterAttributes(int attrlistlen,
|
||
|
const char* attrlist,
|
||
|
int taglistlen,
|
||
|
const char* taglist,
|
||
|
int* resultlen,
|
||
|
char** result)
|
||
|
/* Copies attributes from the specified attribute list to a result string */
|
||
|
/* according to the taglist as described by section 10.4. of RFC 2608 */
|
||
|
/* */
|
||
|
/* version (IN) SLP version of the predicate string (should always be */
|
||
|
/* 2 since we don't handle SLPv1 predicates yet) */
|
||
|
/* */
|
||
|
/* attrlistlen (IN) length of attrlist */
|
||
|
/* */
|
||
|
/* attr (IN) attribute list to test */
|
||
|
/* */
|
||
|
/* predicatelen (IN) length of the predicate string */
|
||
|
/* */
|
||
|
/* predicate (IN) the predicate string */
|
||
|
/* */
|
||
|
/* Returns: Zero on success. Nonzero on failure */
|
||
|
/*=========================================================================*/
|
||
|
{
|
||
|
SLPAttributes attr;
|
||
|
FilterResult err;
|
||
|
char attrnull;
|
||
|
char tagnull;
|
||
|
|
||
|
*result = 0;
|
||
|
*resultlen = 0;
|
||
|
|
||
|
/* TRICKY: Temporarily NULL terminate the attribute list */
|
||
|
/* and the tag string. We can do this because */
|
||
|
/* there is room in the corresponding SLPv2 SRVREG */
|
||
|
/* and ATTRRQST messages. Basically we are squashing */
|
||
|
/* the authcount and the spi string length. Don't */
|
||
|
/* worry, we fix things up later and it is MUCH */
|
||
|
/* faster than a malloc() for a new buffer 1 byte */
|
||
|
/* longer! */
|
||
|
tagnull = taglist[taglistlen];
|
||
|
((char*)taglist)[taglistlen] = 0;
|
||
|
attrnull = attrlist[attrlistlen];
|
||
|
((char*)attrlist)[attrlistlen] = 0;
|
||
|
|
||
|
|
||
|
/* Generate an SLPAttr from the comma delimited list */
|
||
|
err = 1;
|
||
|
if(SLPAttrAlloc("en", NULL, SLP_FALSE, &attr) == 0)
|
||
|
{
|
||
|
if(SLPAttrFreshen(attr, attrlist) == 0)
|
||
|
{
|
||
|
err = SLPAttrSerialize(attr,
|
||
|
taglist,
|
||
|
result,
|
||
|
*resultlen,
|
||
|
resultlen,
|
||
|
SLP_FALSE);
|
||
|
}
|
||
|
|
||
|
SLPAttrFree(attr);
|
||
|
}
|
||
|
|
||
|
/* SLPAttrSerialize counts the NULL terminator which we don't care about*/
|
||
|
if(*resultlen)
|
||
|
{
|
||
|
*resultlen -= 1;
|
||
|
}
|
||
|
|
||
|
/* Un null terminate */
|
||
|
((char*)taglist)[taglistlen] = tagnull;
|
||
|
((char*)attrlist)[attrlistlen] = attrnull;
|
||
|
|
||
|
return(*resultlen == 0);
|
||
|
}
|