#include "lib.h"
#include "str.h"
#include "sieve-match-types.h"
#include "sieve-comparators.h"
#include "sieve-match.h"
#include <string.h>
#include <stdio.h>
static int mcht_matches_match_key
(struct sieve_match_context *mctx, const char *val, size_t val_size,
const char *key, size_t key_size);
const struct sieve_match_type_def matches_match_type = {
SIEVE_OBJECT("matches", &match_type_operand, SIEVE_MATCH_TYPE_MATCHES),
NULL,
sieve_match_substring_validate_context,
NULL, NULL, NULL,
mcht_matches_match_key,
NULL
};
#ifdef MATCH_DEBUG
#define debug_printf(...) printf ("match debug: " __VA_ARGS__)
#else
#define debug_printf(...)
#endif
static inline bool _string_find(const struct sieve_comparator *cmp,
const char **valp, const char *vend, const char **keyp, const char *kend)
{
while ( (*valp < vend) && (*keyp < kend) ) {
if ( !cmp->def->char_match(cmp, valp, vend, keyp, kend) )
(*valp)++;
}
return (*keyp == kend);
}
static char _scan_key_section
(string_t *section, const char **wcardp, const char *key_end)
{
str_truncate(section, 0);
while ( *wcardp < key_end && **wcardp != '*' && **wcardp != '?') {
if ( **wcardp == '\\' ) {
(*wcardp)++;
}
str_append_c(section, **wcardp);
(*wcardp)++;
}
if ( *wcardp < key_end ) {
return **wcardp;
}
i_assert( *wcardp == key_end );
return '\0';
}
static int mcht_matches_match_key
(struct sieve_match_context *mctx, const char *val, size_t val_size,
const char *key, size_t key_size)
{
const struct sieve_comparator *cmp = mctx->comparator;
struct sieve_match_values *mvalues;
string_t *mvalue = NULL, *mchars = NULL;
string_t *section, *subsection;
const char *vend, *kend, *vp, *kp, *wp, *pvp;
bool backtrack = FALSE;
char wcard = '\0';
char next_wcard = '\0';
unsigned int key_offset = 0;
if ( cmp->def == NULL || cmp->def->char_match == NULL )
return FALSE;
section = t_str_new(32);
subsection = t_str_new(32);
vend = (const char *) val + val_size;
kend = (const char *) key + key_size;
vp = val;
kp = key;
wp = key;
pvp = val;
if ( (mvalues = sieve_match_values_start(mctx->runenv)) != NULL ) {
sieve_match_values_add(mvalues, NULL);
mvalue = t_str_new(32);
mchars = t_str_new(32);
}
debug_printf("=== Start ===\n");
debug_printf(" key: %s\n", t_strdup_until(key, kend));
debug_printf(" value: %s\n", t_strdup_until(val, vend));
while (kp < kend && vp < vend ) {
const char *needle, *nend;
if ( !backtrack ) {
wcard = next_wcard;
key_offset = 0;
for (;;) {
next_wcard = _scan_key_section(section, &wp, kend);
if ( wcard == '\0' || str_len(section) > 0 )
break;
if ( next_wcard == '*' ) {
break;
}
if ( wp < kend )
wp++;
else
break;
key_offset++;
}
debug_printf("found wildcard '%c' at pos [%d]\n",
next_wcard, (int) (wp-key));
if ( mvalues != NULL )
str_truncate(mvalue, 0);
} else {
debug_printf("backtracked");
backtrack = FALSE;
}
needle = str_c(section);
nend = PTR_OFFSET(needle, str_len(section));
debug_printf(" section needle: '%s'\n", t_strdup_until(needle, nend));
debug_printf(" section key: '%s'\n", t_strdup_until(kp, kend));
debug_printf(" section remnant: '%s'\n", t_strdup_until(wp, kend));
debug_printf(" value remnant: '%s'\n", t_strdup_until(vp, vend));
debug_printf(" key offset: %d\n", key_offset);
pvp = vp;
if ( next_wcard == '\0' ) {
const char *qp, *qend;
debug_printf("next_wcard = NUL; must find needle at end\n");
if ( vend - str_len(section) < vp ) {
debug_printf(" wont match: value is too short\n");
break;
}
vp = PTR_OFFSET(vend, -str_len(section));
qend = vp;
qp = vp - key_offset;
if ( mvalues != NULL )
str_append_n(mvalue, pvp, qp-pvp);
if ( !cmp->def->char_match(cmp, &vp, vend, &needle, nend) ) {
debug_printf(" match at end failed\n");
break;
}
if ( mvalues != NULL ) {
sieve_match_values_add(mvalues, mvalue);
for ( ; qp < qend; qp++ )
sieve_match_values_add_char(mvalues, *qp);
}
kp = kend;
vp = vend;
debug_printf(" matched end of value\n");
break;
} else {
const char *prv = NULL;
const char *prk = NULL;
const char *prw = NULL;
const char *chars;
if ( mvalues != NULL )
str_truncate(mchars, 0);
if ( wcard == '\0' ) {
debug_printf("wcard = NUL; needle should be found at the beginning.\n");
debug_printf(" begin needle: '%s'\n", t_strdup_until(needle, nend));
debug_printf(" begin value: '%s'\n", t_strdup_until(vp, vend));
if ( !cmp->def->char_match(cmp, &vp, vend, &needle, nend) ) {
debug_printf(" failed to find needle at beginning\n");
break;
}
} else {
debug_printf("wcard != NUL; must find needle at an offset (>= %d).\n",
key_offset);
vp += key_offset;
if ( (vp >= vend) || !_string_find(cmp, &vp, vend, &needle, nend) ) {
debug_printf(" failed to find needle at an offset\n");
break;
}
prv = vp - str_len(section);
prk = kp;
prw = wp;
if ( mvalues != NULL ) {
const char *qend = vp - str_len(section);
const char *qp = qend - key_offset;
str_append_n(mvalue, pvp, qp-pvp);
for ( ; qp < qend; qp++ )
str_append_c(mchars, *qp);
}
}
if ( wp < kend ) wp++;
kp = wp;
while ( next_wcard == '?' ) {
debug_printf("next_wcard = '?'; need to match arbitrary character\n");
if ( mvalues != NULL )
str_append_c(mchars, *vp);
vp++;
next_wcard = _scan_key_section(subsection, &wp, kend);
debug_printf("found next wildcard '%c' at pos [%d] (fixed match)\n",
next_wcard, (int) (wp-key));
needle = str_c(subsection);
nend = PTR_OFFSET(needle, str_len(subsection));
debug_printf(" sub key: '%s'\n", t_strdup_until(needle, nend));
debug_printf(" value remnant: '%s'\n", vp <= vend ? t_strdup_until(vp, vend) : "");
if ( (needle == nend && next_wcard == '\0' && vp < vend ) ||
!cmp->def->char_match(cmp, &vp, vend, &needle, nend) ) {
debug_printf(" failed fixed match\n");
if ( prv != NULL && prv + 1 < vend ) {
vp = prv;
kp = prk;
wp = prw;
if ( mvalues != NULL )
str_append_c(mvalue, *vp);
vp++;
wcard = '*';
next_wcard = '?';
backtrack = TRUE;
debug_printf(" BACKTRACK\n");
}
break;
}
if ( wp < kend ) wp++;
kp = wp;
}
if ( !backtrack ) {
unsigned int i;
if ( next_wcard == '?' ) {
debug_printf("failed to match '?'\n");
break;
}
if ( mvalues != NULL ) {
if ( prv != NULL )
sieve_match_values_add(mvalues, mvalue);
chars = (const char *) str_data(mchars);
for ( i = 0; i < str_len(mchars); i++ ) {
sieve_match_values_add_char(mvalues, chars[i]);
}
}
if ( next_wcard != '*' ) {
debug_printf("failed to match at end of string\n");
break;
}
}
}
if ( kp == kend && next_wcard == '*' ) {
if ( mvalues != NULL ) {
str_truncate(mvalue, 0);
str_append_n(mvalue, vp, vend-vp);
sieve_match_values_add(mvalues, mvalue);
}
kp = kend;
vp = vend;
debug_printf("key ends with '*'\n");
break;
}
debug_printf("== Loop ==\n");
}
if ( vp == vend ) {
while ( kp < kend && *kp == '*' ) kp++;
}
debug_printf("=== Finish ===\n");
debug_printf(" result: %s\n", (kp == kend && vp == vend) ? "true" : "false");
if (kp == kend && vp == vend) {
if ( mvalues != NULL ) {
string_t *matched = str_new_const(pool_datastack_create(), val, val_size);
sieve_match_values_set(mvalues, 0, matched);
sieve_match_values_commit(mctx->runenv, &mvalues);
}
return TRUE;
}
sieve_match_values_abort(&mvalues);
return FALSE;
}