/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999-2021 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
. */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
size_t
mu_sieve_value_create (mu_sieve_machine_t mach, mu_sieve_data_type type,
struct mu_locus_range const *locus,
void *data)
{
size_t idx;
mu_sieve_value_t *val;
if (mach->valcount == mach->valmax)
{
mu_i_sv_2nrealloc (mach, (void**) &mach->valspace, &mach->valmax,
sizeof mach->valspace[0]);
}
idx = mach->valcount++;
val = &mach->valspace[idx];
memset (val, 0, sizeof *val);
val->type = type;
/* Copy locus. */
val->locus.beg.mu_file =
mu_i_sv_id_str (mach, mu_i_sv_id_num (mach, locus->beg.mu_file));
val->locus.beg.mu_line = locus->beg.mu_line;
val->locus.beg.mu_col = locus->beg.mu_col;
val->locus.end.mu_file =
mu_i_sv_id_str (mach, mu_i_sv_id_num (mach, locus->end.mu_file));
val->locus.end.mu_line = locus->end.mu_line;
val->locus.end.mu_col = locus->end.mu_col;
mu_locus_range_init (&val->locus);
mu_locus_range_copy (&val->locus, locus);
switch (type)
{
case SVT_NUMBER:
val->v.number = * (long *) data;
break;
case SVT_STRING:
val->v.list.first = mu_i_sv_string_create (mach, (char *)data);
val->v.list.count = 1;
break;
case SVT_STRING_LIST:
val->v.list = *(mu_sieve_slice_t)data;
break;
case SVT_TAG:
val->v.string = data;
break;
default:
mu_error ("%s", _("invalid data type"));
abort ();
}
return idx;
}
mu_sieve_value_t *
mu_sieve_get_arg_untyped (mu_sieve_machine_t mach, size_t index)
{
if (index >= mach->argcount)
{
mu_sieve_error (mach, _("INTERNAL ERROR: %s,%zu,%zu,%zu argument index %zu out of range"),
mach->identifier,
mach->argstart,
mach->argcount,
mach->tagcount,
index);
abort ();
}
return mach->valspace + mach->argstart + index;
}
mu_sieve_value_t *
mu_sieve_get_arg_optional (mu_sieve_machine_t mach, size_t index)
{
if (index >= mach->argcount)
return NULL;
return mach->valspace + mach->argstart + index;
}
void
mu_sieve_value_get (mu_sieve_machine_t mach, mu_sieve_value_t *val,
mu_sieve_data_type type, void *ret)
{
if (val->type == SVT_STRING && type == SVT_STRING_LIST)
/* compatible types; note that the operation is not commutative: it's
OK if actual type is single string and string list is expected, but
not vice-versa. */;
else if (val->type != type)
{
if (val->tag)
mu_sieve_error (mach,
_("tag :%s has type %s, instead of expected %s"),
val->tag,
mu_sieve_type_str (val->type),
mu_sieve_type_str (type));
else
{
size_t idx = val - mu_sieve_get_arg_untyped (mach, 0);
if (idx < mach->argcount)
mu_sieve_error (mach,
_("argument %zu has type %s, instead of expected %s"),
idx,
mu_sieve_type_str (val->type),
mu_sieve_type_str (type));
else
abort ();
}
mu_sieve_abort (mach);
}
switch (type)
{
case SVT_VOID:
*(void**) ret = NULL;
break;
case SVT_NUMBER:
*(size_t*) ret = val->v.number;
break;
case SVT_STRING:
*(char**) ret = mu_sieve_string (mach, &val->v.list, 0);
break;
case SVT_STRING_LIST:
*(struct mu_sieve_slice *) ret = val->v.list;
break;
case SVT_TAG:
*(char**) ret = val->v.string;
break;
default:
abort ();
}
}
void
mu_sieve_get_arg (mu_sieve_machine_t mach, size_t index,
mu_sieve_data_type type, void *ret)
{
mu_sieve_value_get (mach, mu_sieve_get_arg_untyped (mach, index),
type, ret);
}
mu_sieve_value_t *
mu_sieve_get_tag_untyped (mu_sieve_machine_t mach, char const *name)
{
size_t i;
mu_sieve_value_t *tag = mach->valspace + mach->argstart + mach->argcount;
for (i = 0; i < mach->tagcount; i++)
{
if (strcmp (tag[i].tag, name) == 0)
return &tag[i];
}
return NULL;
}
mu_sieve_value_t *
mu_sieve_get_tag_n (mu_sieve_machine_t mach, size_t n)
{
if (n >= mach->tagcount)
abort ();
return &mach->valspace[mach->argstart + mach->argcount + n];
}
int
mu_sieve_get_tag (mu_sieve_machine_t mach,
char *name, mu_sieve_data_type type,
void *ret)
{
mu_sieve_value_t *val = mu_sieve_get_tag_untyped (mach, name);
if (val)
{
if (ret)
mu_sieve_value_get (mach, val, type, ret);
}
return val != NULL;
}
void
mu_sieve_error (mu_sieve_machine_t mach, const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
mu_stream_printf (mach->errstream, "\033s<%d>", MU_LOG_ERROR);
if (mach->identifier)
mu_stream_printf (mach->errstream, "%s: ", mach->identifier);
mu_stream_vprintf (mach->errstream, fmt, ap);
mu_stream_write (mach->errstream, "\n", 1, NULL);
va_end (ap);
}
const char *
mu_sieve_type_str (mu_sieve_data_type type)
{
switch (type)
{
case SVT_VOID:
return "void";
case SVT_NUMBER:
return "number";
case SVT_STRING:
return "string";
case SVT_STRING_LIST:
return "string-list";
case SVT_TAG:
return "tag";
}
return "unknown";
}
void
mu_i_sv_debug (mu_sieve_machine_t mach, size_t pc, const char *fmt, ...)
{
va_list ap;
if (mach->state_flags & MU_SV_SAVED_DBG_STATE)
{
unsigned severity = MU_LOG_DEBUG;
mu_stream_ioctl (mach->dbgstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_SEVERITY, &severity);
if (mach->locus.beg.mu_file)
{
int mode = mach->dbg_mode | MU_LOGMODE_LOCUS;
mu_stream_ioctl (mach->dbgstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_LOCUS_RANGE, &mach->locus);
mu_stream_ioctl (mach->dbgstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_MODE, &mode);
}
}
va_start (ap, fmt);
mu_stream_printf (mach->dbgstream, "%4zu: ", pc);
mu_stream_vprintf (mach->dbgstream, fmt, ap);
mu_stream_write (mach->dbgstream, "\n", 1, NULL);
va_end (ap);
}
void
mu_i_sv_debug_command (mu_sieve_machine_t mach,
size_t pc,
char const *what)
{
size_t i;
if (mach->state_flags & MU_SV_SAVED_DBG_STATE)
{
unsigned severity = MU_LOG_DEBUG;
mu_stream_ioctl (mach->dbgstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_SEVERITY, &severity);
if (mach->locus.beg.mu_file)
{
int mode = mach->dbg_mode | MU_LOGMODE_LOCUS;
mu_stream_ioctl (mach->dbgstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_LOCUS_RANGE, &mach->locus);
mu_stream_ioctl (mach->dbgstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_MODE, &mode);
}
}
mu_stream_printf (mach->dbgstream, "%4zu: %s: %s",
pc, what, mach->identifier);
for (i = 0; i < mach->argcount; i++)
mu_i_sv_valf (mach, mach->dbgstream, &mach->valspace[mach->argstart + i]);
for (i = 0; i < mach->tagcount; i++)
mu_i_sv_valf (mach, mach->dbgstream, mu_sieve_get_tag_n (mach, i));
mu_stream_write (mach->dbgstream, "\n", 1, NULL);
}
void
mu_i_sv_trace (mu_sieve_machine_t mach, const char *what)
{
size_t i;
if (!mu_debug_level_p (mu_sieve_debug_handle, MU_DEBUG_TRACE4))
return;
mu_stream_printf (mach->errstream, "\033s<%d>", MU_LOG_DEBUG);
mu_stream_printf (mach->errstream, "%zu: %s %s", mach->msgno, what,
mach->identifier);
for (i = 0; i < mach->argcount; i++)
mu_i_sv_valf (mach, mach->errstream, mu_sieve_get_arg_untyped (mach, i));
for (i = 0; i < mach->tagcount; i++)
mu_i_sv_valf (mach, mach->errstream, mu_sieve_get_tag_n (mach, i));
mu_stream_printf (mach->errstream, "\n");
}
void
mu_sieve_log_action (mu_sieve_machine_t mach, const char *action,
const char *fmt, ...)
{
va_list ap;
if (!mach->logger)
return;
va_start (ap, fmt);
mach->logger (mach, action, fmt, ap);
va_end (ap);
}
int
mu_sieve_vlist_do (mu_sieve_machine_t mach, mu_sieve_value_t *val,
mu_list_action_t ac, void *data)
{
size_t i;
switch (val->type)
{
case SVT_STRING_LIST:
case SVT_STRING:
for (i = 0; i < val->v.list.count; i++)
{
int rc = ac (mu_sieve_string (mach, &val->v.list, i), data);
if (rc)
return rc;
}
return 0;
default:
mu_error ("mu_sieve_vlist_do: unexpected list type %d", val->type);
return EINVAL;
}
}
int
mu_sieve_vlist_compare (mu_sieve_machine_t mach,
mu_sieve_value_t *a, mu_sieve_value_t *b,
mu_sieve_retrieve_t retr, mu_list_folder_t fold,
void *data)
{
int rc = 0;
size_t i;
mu_sieve_comparator_t comp = mu_sieve_get_comparator (mach);
mu_sieve_relcmp_t test = mu_sieve_get_relcmp (mach);
char *relcmp;
mu_list_t tmp;
if (!(a->type == SVT_STRING_LIST || a->type == SVT_STRING))
abort ();
rc = mu_list_create (&tmp);
if (rc)
{
mu_sieve_error (mach, "mu_list_create: %s", mu_strerror (rc));
mu_sieve_abort (mach);
}
mu_list_set_destroy_item (tmp, mu_list_free_item);
for (i = 0; i < a->v.list.count; i++)
{
char *item = mu_sieve_string (mach, &a->v.list, i);
char *sample;
size_t j;
for (j = 0; (rc = retr (item, data, j, &sample)) == 0; j++)
{
if (!sample)
continue;
rc = mu_list_append (tmp, sample);
if (rc)
{
free (sample);
mu_list_destroy (&tmp);
mu_sieve_error (mach, "mu_list_append: %s", mu_strerror (rc));
mu_sieve_abort (mach);
}
}
if (rc != MU_ERR_NOENT)
{
mu_list_destroy (&tmp);
mu_sieve_error (mach, "retriever failure: %s", mu_strerror (rc));
mu_sieve_abort (mach);
}
}
if (mu_sieve_get_tag (mach, "count", SVT_STRING, &relcmp))
{
size_t limit;
size_t count;
mu_sieve_relcmpn_t stest;
struct mu_sieve_slice slice;
char *str, *p;
if (fold)
{
count = 0;
rc = mu_list_fold (tmp, fold, data, &count, &count);
if (rc)
{
mu_sieve_error (mach, "mu_list_fold: %s", mu_strerror (rc));
mu_sieve_abort (mach);
}
}
else
mu_list_count (tmp, &count);
mu_sieve_get_arg (mach, 1, SVT_STRING_LIST, &slice);
str = mu_sieve_string (mach, &slice, 0);
limit = strtoul (str, &p, 10);
if (*p)
{
mu_sieve_error (mach, _("%s: not an integer"), str);
mu_sieve_abort (mach);
}
mu_sieve_str_to_relcmp (relcmp, NULL, &stest);
rc = stest (count, limit);
}
else
{
mu_iterator_t itr;
mu_list_get_iterator (tmp, &itr);
rc = 0;
for (mu_iterator_first (itr); rc == 0 && !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
char const *val;
mu_iterator_current (itr, (void**)&val);
for (i = 0; i < b->v.list.count; i++)
{
mu_sieve_string_t *s = mu_sieve_string_raw (mach, &b->v.list, i);
rc = test (comp (mach, s, val), 0);
if (rc)
break;
}
}
mu_iterator_destroy (&itr);
}
mu_list_destroy (&tmp);
return rc;
}
void
mu_sieve_stream_save (mu_sieve_machine_t mach)
{
if (mach->state_flags & MU_SV_SAVED_STATE)
return;
if (mu_stream_ioctl (mach->errstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_GET_MODE, &mach->err_mode) == 0
&& mu_stream_ioctl (mach->errstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_GET_LOCUS_RANGE,
&mach->err_locus) == 0)
mach->state_flags |= MU_SV_SAVED_ERR_STATE;
if (mu_stream_ioctl (mach->dbgstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_GET_MODE, &mach->dbg_mode) == 0
&& mu_stream_ioctl (mach->dbgstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_GET_LOCUS_RANGE,
&mach->dbg_locus) == 0)
mach->state_flags |= MU_SV_SAVED_DBG_STATE;
mach->state_flags |= MU_SV_SAVED_STATE;
}
void
mu_sieve_stream_restore (mu_sieve_machine_t mach)
{
if (!(mach->state_flags & MU_SV_SAVED_STATE))
return;
if (mach->state_flags & MU_SV_SAVED_ERR_STATE)
{
mu_stream_ioctl (mach->errstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_MODE, &mach->err_mode);
mu_stream_ioctl (mach->errstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_LOCUS_RANGE, &mach->err_locus);
}
if (mach->dbgstream != mach->errstream
&& (mach->state_flags & MU_SV_SAVED_DBG_STATE))
{
mu_stream_ioctl (mach->dbgstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_MODE, &mach->dbg_mode);
mu_stream_ioctl (mach->dbgstream, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_LOCUS_RANGE, &mach->dbg_locus);
}
mach->state_flags = 0;
}