summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c')
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c733
1 files changed, 733 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c
new file mode 100644
index 0000000..3c364b8
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c
@@ -0,0 +1,733 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "mail-storage.h"
+#include "imap-arg.h"
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-stringlist.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+#include "sieve-dump.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-imap4flags-common.h"
+
+/*
+ * Tagged arguments
+ */
+
+extern const struct sieve_argument_def tag_flags;
+extern const struct sieve_argument_def tag_flags_implicit;
+
+/*
+ * Common command functions
+ */
+
+bool ext_imap4flags_command_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg = cmd->first_positional;
+ struct sieve_ast_argument *arg2;
+ const struct sieve_extension *var_ext;
+
+ /* Check arguments */
+
+ if ( arg == NULL ) {
+ sieve_command_validate_error(valdtr, cmd,
+ "the %s %s expects at least one argument, but none was found",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd));
+ return FALSE;
+ }
+
+ if ( sieve_ast_argument_type(arg) != SAAT_STRING &&
+ sieve_ast_argument_type(arg) != SAAT_STRING_LIST )
+ {
+ sieve_argument_validate_error(valdtr, arg,
+ "the %s %s expects either a string (variable name) or "
+ "a string-list (list of flags) as first argument, but %s was found",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd),
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+
+ arg2 = sieve_ast_argument_next(arg);
+ if ( arg2 != NULL ) {
+ /* First, check syntax sanity */
+
+ if ( sieve_ast_argument_type(arg) != SAAT_STRING )
+ {
+ if ( sieve_command_is(cmd, tst_hasflag) ) {
+ if ( sieve_ast_argument_type(arg) != SAAT_STRING_LIST ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "if a second argument is specified for the hasflag, the first "
+ "must be a string-list (variable-list), but %s was found",
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+ } else {
+ sieve_argument_validate_error(valdtr, arg,
+ "if a second argument is specified for the %s %s, the first "
+ "must be a string (variable name), but %s was found",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd),
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+ }
+
+ /* Then, check whether the second argument is permitted */
+
+ var_ext = sieve_ext_variables_get_extension(cmd->ext->svinst);
+
+ if ( var_ext == NULL || !sieve_ext_variables_is_active(var_ext, valdtr) )
+ {
+ sieve_argument_validate_error(valdtr,arg,
+ "the %s %s only allows for the specification of a "
+ "variable name when the variables extension is active",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd));
+ return FALSE;
+ }
+
+ if ( !sieve_variable_argument_activate(var_ext, var_ext,
+ valdtr, cmd, arg, !sieve_command_is(cmd, tst_hasflag) ) )
+ return FALSE;
+
+ if ( sieve_ast_argument_type(arg2) != SAAT_STRING &&
+ sieve_ast_argument_type(arg2) != SAAT_STRING_LIST )
+ {
+ sieve_argument_validate_error(valdtr, arg2,
+ "the %s %s expects a string list (list of flags) as "
+ "second argument when two arguments are specified, "
+ "but %s was found",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd),
+ sieve_ast_argument_name(arg2));
+ return FALSE;
+ }
+ } else
+ arg2 = arg;
+
+ if ( !sieve_validator_argument_activate(valdtr, cmd, arg2, FALSE) )
+ return FALSE;
+
+ if ( !sieve_command_is(cmd, tst_hasflag) &&
+ sieve_argument_is_string_literal(arg2) ) {
+ struct ext_imap4flags_iter fiter;
+ const char *flag;
+
+ /* Warn the user about validity of verifiable flags */
+ ext_imap4flags_iter_init(&fiter, sieve_ast_argument_str(arg));
+
+ while ( (flag=ext_imap4flags_iter_get_flag(&fiter)) != NULL ) {
+ if ( !sieve_ext_imap4flags_flag_is_valid(flag) ) {
+ sieve_argument_validate_warning(valdtr, arg,
+ "IMAP flag '%s' specified for the %s command is invalid "
+ "and will be ignored (only first invalid is reported)",
+ str_sanitize(flag, 64), sieve_command_identifier(cmd));
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * Flags tag registration
+ */
+
+void ext_imap4flags_attach_flags_tag
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ const char *command)
+{
+ /* Register :flags tag with the command and we don't care whether it is
+ * registered or even whether it will be registered at all. The validator
+ * handles either situation gracefully
+ */
+
+ /* Tag specified by user */
+ sieve_validator_register_external_tag
+ (valdtr, command, ext, &tag_flags, SIEVE_OPT_SIDE_EFFECT);
+}
+
+void sieve_ext_imap4flags_register_side_effect
+(struct sieve_validator *valdtr, const struct sieve_extension *flg_ext,
+ const char *command)
+{
+ /* Implicit tag if none is specified */
+ sieve_validator_register_persistent_tag
+ (valdtr, command, flg_ext, &tag_flags_implicit);
+}
+
+
+/*
+ * Result context
+ */
+
+struct ext_imap4flags_result_context {
+ string_t *internal_flags;
+};
+
+static void _get_initial_flags
+(struct sieve_result *result, string_t *flags)
+{
+ const struct sieve_message_data *msgdata =
+ sieve_result_get_message_data(result);
+ enum mail_flags mail_flags;
+ const char *const *mail_keywords;
+
+ mail_flags = mail_get_flags(msgdata->mail);
+ mail_keywords = mail_get_keywords(msgdata->mail);
+
+ if ( (mail_flags & MAIL_FLAGGED) > 0 )
+ str_printfa(flags, " \\flagged");
+
+ if ( (mail_flags & MAIL_ANSWERED) > 0 )
+ str_printfa(flags, " \\answered");
+
+ if ( (mail_flags & MAIL_DELETED) > 0 )
+ str_printfa(flags, " \\deleted");
+
+ if ( (mail_flags & MAIL_SEEN) > 0 )
+ str_printfa(flags, " \\seen");
+
+ if ( (mail_flags & MAIL_DRAFT) > 0 )
+ str_printfa(flags, " \\draft");
+
+ while ( *mail_keywords != NULL ) {
+ str_printfa(flags, " %s", *mail_keywords);
+ mail_keywords++;
+ }
+}
+
+static inline struct ext_imap4flags_result_context *_get_result_context
+(const struct sieve_extension *this_ext, struct sieve_result *result)
+{
+ struct ext_imap4flags_result_context *rctx =
+ (struct ext_imap4flags_result_context *)
+ sieve_result_extension_get_context(result, this_ext);
+
+ if ( rctx == NULL ) {
+ pool_t pool = sieve_result_pool(result);
+
+ rctx =p_new(pool, struct ext_imap4flags_result_context, 1);
+ rctx->internal_flags = str_new(pool, 32);
+ _get_initial_flags(result, rctx->internal_flags);
+
+ sieve_result_extension_set_context
+ (result, this_ext, rctx);
+ }
+
+ return rctx;
+}
+
+static string_t *_get_flags_string
+(const struct sieve_extension *this_ext, struct sieve_result *result)
+{
+ struct ext_imap4flags_result_context *ctx =
+ _get_result_context(this_ext, result);
+
+ return ctx->internal_flags;
+}
+
+/*
+ * Runtime initialization
+ */
+
+static int ext_imap4flags_runtime_init
+(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ void *context ATTR_UNUSED, bool deferred ATTR_UNUSED)
+{
+ sieve_result_add_implicit_side_effect
+ (renv->result, NULL, TRUE, ext, &flags_side_effect, NULL);
+ return SIEVE_EXEC_OK;
+}
+
+const struct sieve_interpreter_extension
+imap4flags_interpreter_extension = {
+ .ext_def = &imap4flags_extension,
+ .run = ext_imap4flags_runtime_init
+};
+
+/*
+ * Flag handling
+ */
+
+/* FIXME: This currently accepts a potentially unlimited number of
+ * flags, making the internal or variable flag list indefinitely long
+ */
+
+bool sieve_ext_imap4flags_flag_is_valid(const char *flag)
+{
+ if ( *flag == '\0' )
+ return FALSE;
+
+ if ( *flag == '\\' ) {
+ /* System flag */
+ const char *atom = t_str_ucase(flag);
+
+ if (
+ (strcmp(atom, "\\ANSWERED") != 0) &&
+ (strcmp(atom, "\\FLAGGED") != 0) &&
+ (strcmp(atom, "\\DELETED") != 0) &&
+ (strcmp(atom, "\\SEEN") != 0) &&
+ (strcmp(atom, "\\DRAFT") != 0) )
+ {
+ return FALSE;
+ }
+ } else {
+ const char *p;
+
+ /* Custom keyword:
+ *
+ * Syntax (IMAP4rev1, RFC 3501, Section 9. Formal Syntax) :
+ * flag-keyword = atom
+ * atom = 1*ATOM-CHAR
+ */
+ p = flag;
+ while ( *p != '\0' ) {
+ if ( !IS_ATOM_CHAR(*p) )
+ return FALSE;
+ p++;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Flag iterator */
+
+static void ext_imap4flags_iter_clear
+(struct ext_imap4flags_iter *iter)
+{
+ i_zero(iter);
+}
+
+void ext_imap4flags_iter_init
+(struct ext_imap4flags_iter *iter, string_t *flags_list)
+{
+ ext_imap4flags_iter_clear(iter);
+ iter->flags_list = flags_list;
+}
+
+static string_t *ext_imap4flags_iter_get_flag_str
+(struct ext_imap4flags_iter *iter)
+{
+ unsigned int len;
+ const unsigned char *fp;
+ const unsigned char *fbegin;
+ const unsigned char *fstart;
+ const unsigned char *fend;
+
+ /* Return if not initialized */
+ if ( iter->flags_list == NULL ) return NULL;
+
+ /* Return if no more flags are available */
+ len = str_len(iter->flags_list);
+ if ( iter->offset >= len ) return NULL;
+
+ /* Mark string boundries */
+ fbegin = str_data(iter->flags_list);
+ fend = fbegin + len;
+
+ /* Start of this flag */
+ fstart = fbegin + iter->offset;
+
+ /* Scan for next flag */
+ fp = fstart;
+ for (;;) {
+ /* Have we reached the end or a flag boundary? */
+ if ( fp >= fend || *fp == ' ' ) {
+ /* Did we scan more than nothing ? */
+ if ( fp > fstart ) {
+ /* Return flag */
+ string_t *flag = t_str_new(fp-fstart+1);
+ str_append_data(flag, fstart, fp-fstart);
+
+ iter->last = fstart - fbegin;
+ iter->offset = fp - fbegin;
+
+ return flag;
+ }
+
+ fstart = fp + 1;
+ }
+
+ if ( fp >= fend ) break;
+
+ fp++;
+ }
+
+ iter->last = fstart - fbegin;
+ iter->offset = fp - fbegin;
+ return NULL;
+}
+
+const char *ext_imap4flags_iter_get_flag
+(struct ext_imap4flags_iter *iter)
+{
+ string_t *flag = ext_imap4flags_iter_get_flag_str(iter);
+
+ if ( flag == NULL ) return NULL;
+
+ return str_c(flag);
+}
+
+static void ext_imap4flags_iter_delete_last
+(struct ext_imap4flags_iter *iter)
+{
+ iter->offset++;
+ if ( iter->offset > str_len(iter->flags_list) )
+ iter->offset = str_len(iter->flags_list);
+ if ( iter->offset == str_len(iter->flags_list) && iter->last > 0 )
+ iter->last--;
+
+ str_delete(iter->flags_list, iter->last, iter->offset - iter->last);
+
+ iter->offset = iter->last;
+}
+
+/* Flag operations */
+
+static string_t *ext_imap4flags_get_flag_variable
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index)
+ ATTR_NULL(2);
+
+static bool flags_list_flag_exists
+(string_t *flags_list, const char *flag)
+{
+ const char *flg;
+ struct ext_imap4flags_iter flit;
+
+ ext_imap4flags_iter_init(&flit, flags_list);
+
+ while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
+ if ( strcasecmp(flg, flag) == 0 )
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void flags_list_flag_delete
+(string_t *flags_list, const char *flag)
+{
+ const char *flg;
+ struct ext_imap4flags_iter flit;
+
+ ext_imap4flags_iter_init(&flit, flags_list);
+
+ while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
+ if ( strcasecmp(flg, flag) == 0 ) {
+ ext_imap4flags_iter_delete_last(&flit);
+ }
+ }
+}
+
+static void flags_list_add_flags
+(string_t *flags_list, string_t *flags)
+{
+ const char *flg;
+ struct ext_imap4flags_iter flit;
+
+ ext_imap4flags_iter_init(&flit, flags);
+
+ while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
+ if ( sieve_ext_imap4flags_flag_is_valid(flg) &&
+ !flags_list_flag_exists(flags_list, flg) ) {
+ if ( str_len(flags_list) != 0 )
+ str_append_c(flags_list, ' ');
+ str_append(flags_list, flg);
+ }
+ }
+}
+
+static void flags_list_remove_flags
+(string_t *flags_list, string_t *flags)
+{
+ const char *flg;
+ struct ext_imap4flags_iter flit;
+
+ ext_imap4flags_iter_init(&flit, flags);
+
+ while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
+ flags_list_flag_delete(flags_list, flg);
+ }
+}
+
+static void flags_list_set_flags
+(string_t *flags_list, string_t *flags)
+{
+ str_truncate(flags_list, 0);
+ flags_list_add_flags(flags_list, flags);
+}
+
+static void flags_list_clear_flags
+(string_t *flags_list)
+{
+ str_truncate(flags_list, 0);
+}
+
+static string_t *ext_imap4flags_get_flag_variable
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index)
+{
+ string_t *flags;
+
+ if ( storage != NULL ) {
+ if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) {
+ const char *var_name, *var_id;
+
+ (void)sieve_variable_get_identifier(storage, var_index, &var_name);
+ var_id = sieve_variable_get_varid(storage, var_index);
+
+ sieve_runtime_trace(renv, 0, "update variable `%s' [%s]",
+ var_name, var_id);
+ }
+
+ if ( !sieve_variable_get_modifiable(storage, var_index, &flags) )
+ return NULL;
+ } else {
+ i_assert( sieve_extension_is(flg_ext, imap4flags_extension) );
+ flags = _get_flags_string(flg_ext, renv->result);
+ }
+
+ return flags;
+}
+
+int sieve_ext_imap4flags_set_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index,
+ struct sieve_stringlist *flags)
+{
+ string_t *cur_flags = ext_imap4flags_get_flag_variable
+ (renv, flg_ext, storage, var_index);
+
+ if ( cur_flags != NULL ) {
+ string_t *flags_item;
+ int ret;
+
+ flags_list_clear_flags(cur_flags);
+ while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+ "set flags `%s'", str_c(flags_item));
+
+ flags_list_add_flags(cur_flags, flags_item);
+ }
+
+ if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT;
+
+ return SIEVE_EXEC_OK;
+ }
+
+ return SIEVE_EXEC_BIN_CORRUPT;
+}
+
+int sieve_ext_imap4flags_add_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index,
+ struct sieve_stringlist *flags)
+{
+ string_t *cur_flags = ext_imap4flags_get_flag_variable
+ (renv, flg_ext, storage, var_index);
+
+ if ( cur_flags != NULL ) {
+ string_t *flags_item;
+ int ret;
+
+ while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+ "add flags `%s'", str_c(flags_item));
+
+ flags_list_add_flags(cur_flags, flags_item);
+ }
+
+ if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT;
+
+ return SIEVE_EXEC_OK;
+ }
+
+ return SIEVE_EXEC_BIN_CORRUPT;
+}
+
+int sieve_ext_imap4flags_remove_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index,
+ struct sieve_stringlist *flags)
+{
+ string_t *cur_flags = ext_imap4flags_get_flag_variable
+ (renv, flg_ext, storage, var_index);
+
+ if ( cur_flags != NULL ) {
+ string_t *flags_item;
+ int ret;
+
+ while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+ "remove flags `%s'", str_c(flags_item));
+
+ flags_list_remove_flags(cur_flags, flags_item);
+ }
+
+ if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT;
+
+ return SIEVE_EXEC_OK;
+ }
+
+ return SIEVE_EXEC_BIN_CORRUPT;
+}
+
+/* Flag stringlist */
+
+static int ext_imap4flags_stringlist_next_item
+ (struct sieve_stringlist *_strlist, string_t **str_r);
+static void ext_imap4flags_stringlist_reset
+ (struct sieve_stringlist *_strlist);
+
+struct ext_imap4flags_stringlist {
+ struct sieve_stringlist strlist;
+
+ struct sieve_stringlist *flags_list;
+ string_t *flags_string;
+ struct ext_imap4flags_iter flit;
+
+ bool normalize:1;
+};
+
+static struct sieve_stringlist *ext_imap4flags_stringlist_create
+(const struct sieve_runtime_env *renv, struct sieve_stringlist *flags_list,
+ bool normalize)
+{
+ struct ext_imap4flags_stringlist *strlist;
+
+ strlist = t_new(struct ext_imap4flags_stringlist, 1);
+ strlist->strlist.exec_status = SIEVE_EXEC_OK;
+ strlist->strlist.runenv = renv;
+ strlist->strlist.next_item = ext_imap4flags_stringlist_next_item;
+ strlist->strlist.reset = ext_imap4flags_stringlist_reset;
+ strlist->normalize = normalize;
+
+ strlist->flags_list = flags_list;
+
+ return &strlist->strlist;
+}
+
+static struct sieve_stringlist *ext_imap4flags_stringlist_create_single
+(const struct sieve_runtime_env *renv, string_t *flags_string, bool normalize)
+{
+ struct ext_imap4flags_stringlist *strlist;
+
+ strlist = t_new(struct ext_imap4flags_stringlist, 1);
+ strlist->strlist.exec_status = SIEVE_EXEC_OK;
+ strlist->strlist.runenv = renv;
+ strlist->strlist.next_item = ext_imap4flags_stringlist_next_item;
+ strlist->strlist.reset = ext_imap4flags_stringlist_reset;
+ strlist->normalize = normalize;
+
+ if ( normalize ) {
+ strlist->flags_string = t_str_new(256);
+ flags_list_set_flags(strlist->flags_string, flags_string);
+ } else {
+ strlist->flags_string = flags_string;
+ }
+
+ ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string);
+
+ return &strlist->strlist;
+}
+
+static int ext_imap4flags_stringlist_next_item
+(struct sieve_stringlist *_strlist, string_t **str_r)
+{
+ struct ext_imap4flags_stringlist *strlist =
+ (struct ext_imap4flags_stringlist *)_strlist;
+
+ while ( (*str_r=ext_imap4flags_iter_get_flag_str(&strlist->flit)) == NULL ) {
+ int ret;
+
+ if ( strlist->flags_list == NULL )
+ return 0;
+
+ if ( (ret=sieve_stringlist_next_item
+ (strlist->flags_list, &strlist->flags_string)) <= 0 )
+ return ret;
+
+ if ( strlist->flags_string == NULL )
+ return -1;
+
+ if ( strlist->normalize ) {
+ string_t *flags_string = t_str_new(256);
+
+ flags_list_set_flags(flags_string, strlist->flags_string);
+ strlist->flags_string = flags_string;
+ }
+
+ ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string);
+ }
+
+ return 1;
+}
+
+static void ext_imap4flags_stringlist_reset
+(struct sieve_stringlist *_strlist)
+{
+ struct ext_imap4flags_stringlist *strlist =
+ (struct ext_imap4flags_stringlist *)_strlist;
+
+ if ( strlist->flags_list != NULL ) {
+ sieve_stringlist_reset(strlist->flags_list);
+ ext_imap4flags_iter_clear(&strlist->flit);
+ } else {
+ ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string);
+ }
+}
+
+/* Flag access */
+
+struct sieve_stringlist *sieve_ext_imap4flags_get_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_stringlist *flags_list)
+{
+ if ( flags_list == NULL ) {
+ i_assert( sieve_extension_is(flg_ext, imap4flags_extension) );
+ return ext_imap4flags_stringlist_create_single
+ (renv, _get_flags_string(flg_ext, renv->result), FALSE);
+ }
+
+ return ext_imap4flags_stringlist_create(renv, flags_list, TRUE);
+}
+
+void ext_imap4flags_get_implicit_flags_init
+(struct ext_imap4flags_iter *iter, const struct sieve_extension *this_ext,
+ struct sieve_result *result)
+{
+ string_t *cur_flags = _get_flags_string(this_ext, result);
+
+ ext_imap4flags_iter_init(iter, cur_flags);
+}
+
+
+
+
+