Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F120824908
annotate.testc
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
57 KB
Referenced Files
None
Subscribers
None
annotate.testc
View Options
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "cunit/cunit.h"
#include "assert.h"
#include "xmalloc.h"
#include "retry.h"
#include "util.h"
#include "imap/global.h"
#include "libcyr_cfg.h"
#include "imap/annotate.h"
#include "imap/mboxlist.h"
#include "imap/imap_err.h"
#define DBDIR "test-dbdir"
#define MBOXNAME1_INT "user.smurf"
#define MBOXNAME2_INT "user.smurfette"
#define PARTITION "default"
#define COMMENT "/comment"
#define EXENTRY "/vendor/example.com/a-non-default-entry"
#define VALUE_SHARED "value.shared"
#define SIZE_SHARED "size.shared"
#define VALUE0 "Hello World"
#define LENGTH0 "11"
#define VALUE1 "lorem ipsum"
#define VALUE2 "dolor sit amet"
#define ACL "anyone\tlrswipkxtecdan\t"
static int isadmin;
static const char *userid;
static struct auth_state *auth_state;
static const char *old_annotation_definitions = NULL;
static void config_read_string(const char *s)
{
char *fname = xstrdup("/tmp/cyrus-cunit-configXXXXXX");
int fd = mkstemp(fname);
retry_write(fd, s, strlen(s));
config_reset();
config_read(fname, 0);
unlink(fname);
free(fname);
close(fd);
}
static void set_annotation_definitions(const char *s)
{
static const char *fname = DBDIR"/conf/annotations.def";
int fd;
if (s) {
fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
assert(fd >= 0);
retry_write(fd, s, strlen(s));
close(fd);
}
else {
unlink(fname);
}
imapopts[IMAPOPT_ANNOTATION_DEFINITIONS].val.s = fname;
}
static int fexists(const char *fname)
{
struct stat sb;
int r;
r = stat(fname, &sb);
if (r < 0)
r = -errno;
return r;
}
static void fetch_cb(const char *mboxname, uint32_t uid,
const char *entry, struct attvaluelist *avlist,
void *rock)
{
strarray_t *results = (strarray_t *)rock;
struct buf buf = BUF_INITIALIZER;
buf_printf(&buf, "mboxname=\"%s\" uid=%u entry=\"%s\"",
mboxname, uid, entry);
for ( ; avlist ; avlist = avlist->next) {
buf_printf(&buf, " %s=", avlist->attrib);
if (avlist->value.s)
buf_printf(&buf, "\"%s\"", buf_cstring(&avlist->value));
else
buf_printf(&buf, "NIL");
}
strarray_appendm(results, buf_release(&buf));
}
/* test_begin_without_open deleted -- begin has been removed */
/* test_commit_without_begin deleted -- begin has been removed */
/* test_store_without_begin deleted -- begin has been removed */
static void test_getset_server_shared(void)
{
int r;
annotate_state_t *astate = NULL;
strarray_t entries = STRARRAY_INITIALIZER;
strarray_t attribs = STRARRAY_INITIALIZER;
strarray_t results = STRARRAY_INITIALIZER;
struct entryattlist *ealist = NULL;
struct buf val = BUF_INITIALIZER;
struct buf val2 = BUF_INITIALIZER;
annotate_init(NULL, NULL);
annotatemore_open();
astate = annotate_state_new();
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
strarray_append(&entries, COMMENT);
strarray_append(&attribs, VALUE_SHARED);
/* check that there is no value initially */
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=NIL"
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", COMMENT, /*userid*/"", &val);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(val.s);
/* set a value */
/* pretend to be admin */
annotate_state_set_auth(astate, /*isadmin*/1, userid, auth_state);
buf_appendcstr(&val, VALUE0);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
/* check that we can fetch the value back in the same txn */
r = annotate_state_fetch(astate, &entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=\"" VALUE0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
r = annotate_state_commit(&astate);
CU_ASSERT_EQUAL(r, 0);
/* check that we can fetch the value back in a new txn */
astate = annotate_state_new();
annotate_state_set_auth(astate, /*isadmin*/0, userid, auth_state);
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=\"" VALUE0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
annotate_state_abort(&astate);
annotatemore_close();
/* check that we can fetch the value back after close and re-open */
annotatemore_open();
astate = annotate_state_new();
annotate_state_set_auth(astate, /*isadmin*/0, userid, auth_state);
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=\"" VALUE0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
/* delete the value */
buf_free(&val);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
/* pretend to be admin */
annotate_state_set_auth(astate, /*isadmin*/1, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
r = annotate_state_commit(&astate);
CU_ASSERT_EQUAL(r, 0);
/* check that there is no value any more */
astate = annotate_state_new();
annotate_state_set_auth(astate, /*isadmin*/0, userid, auth_state);
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=NIL"
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", COMMENT, /*userid*/"", &val);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(val.s);
annotate_state_abort(&astate);
annotatemore_close();
strarray_fini(&entries);
strarray_fini(&attribs);
strarray_fini(&results);
buf_free(&val);
}
static void test_getset_mailbox_shared(void)
{
int r;
annotate_state_t *astate = NULL;
strarray_t entries = STRARRAY_INITIALIZER;
strarray_t attribs = STRARRAY_INITIALIZER;
strarray_t results = STRARRAY_INITIALIZER;
struct entryattlist *ealist = NULL;
struct buf val = BUF_INITIALIZER;
struct buf val2 = BUF_INITIALIZER;
struct mailbox *mailbox = NULL;
annotate_init(NULL, NULL);
annotatemore_open();
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox);
CU_ASSERT_EQUAL_FATAL(r, 0);
astate = annotate_state_new();
r = annotate_state_set_mailbox(astate, mailbox);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
strarray_append(&entries, COMMENT);
strarray_append(&attribs, VALUE_SHARED);
/* check that there is no value initially */
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"" MBOXNAME1_INT "\" " \
"uid=0 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=NIL"
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(MBOXNAME1_INT, COMMENT, /*userid*/"", &val);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(val.s);
/* set a value */
buf_appendcstr(&val, VALUE0);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
/* check that we can fetch the value back in the same txn */
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"" MBOXNAME1_INT "\" " \
"uid=0 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=\"" VALUE0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(MBOXNAME1_INT, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
r = annotate_state_commit(&astate);
CU_ASSERT_EQUAL(r, 0);
/* check that we can fetch the value back in a new txn */
astate = annotate_state_new();
r = annotate_state_set_mailbox(astate, mailbox);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"" MBOXNAME1_INT "\" " \
"uid=0 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=\"" VALUE0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(MBOXNAME1_INT, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
annotate_state_abort(&astate);
annotatemore_close();
/* check that we can fetch the value back after close and re-open */
annotatemore_open();
astate = annotate_state_new();
r = annotate_state_set_mailbox(astate, mailbox);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"" MBOXNAME1_INT "\" " \
"uid=0 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=\"" VALUE0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(MBOXNAME1_INT, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
/* delete the value */
buf_free(&val);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
r = annotate_state_commit(&astate);
CU_ASSERT_EQUAL(r, 0);
/* check that there is no value any more */
astate = annotate_state_new();
r = annotate_state_set_mailbox(astate, mailbox);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"" MBOXNAME1_INT "\" " \
"uid=0 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=NIL"
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
buf_free(&val);
r = annotatemore_lookup(MBOXNAME1_INT, COMMENT, /*userid*/"", &val);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(val.s);
annotate_state_abort(&astate);
annotatemore_close();
strarray_fini(&entries);
strarray_fini(&attribs);
strarray_fini(&results);
buf_free(&val);
mailbox_close(&mailbox);
}
static void test_getset_message_shared(void)
{
int r;
annotate_state_t *astate = NULL;
strarray_t entries = STRARRAY_INITIALIZER;
strarray_t attribs = STRARRAY_INITIALIZER;
strarray_t results = STRARRAY_INITIALIZER;
struct entryattlist *ealist = NULL;
struct buf val = BUF_INITIALIZER;
struct buf val2 = BUF_INITIALIZER;
struct mailbox *mailbox = NULL;
annotate_init(NULL, NULL);
annotatemore_open();
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox);
CU_ASSERT_EQUAL_FATAL(r, 0);
r = mailbox_get_annotate_state(mailbox, 42, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
strarray_append(&entries, COMMENT);
strarray_append(&attribs, VALUE_SHARED);
/* check that there is no value initially */
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"" MBOXNAME1_INT "\" " \
"uid=42 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=NIL"
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(val.s);
/* set a value */
buf_appendcstr(&val, VALUE0);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
/* check that we can fetch the value back in the same txn */
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"" MBOXNAME1_INT "\" " \
"uid=42 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=\"" VALUE0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
r = mailbox_commit(mailbox);
CU_ASSERT_EQUAL(r, 0);
mailbox_close(&mailbox);
/* check that we can fetch the value back in a new txn */
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox);
CU_ASSERT_EQUAL_FATAL(r, 0);
r = mailbox_get_annotate_state(mailbox, 42, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"" MBOXNAME1_INT "\" " \
"uid=42 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=\"" VALUE0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
r = mailbox_commit(mailbox);
CU_ASSERT_EQUAL(r, 0);
mailbox_close(&mailbox);
annotatemore_close();
/* check that we can fetch the value back after close and re-open */
annotatemore_open();
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox);
CU_ASSERT_EQUAL_FATAL(r, 0);
r = mailbox_get_annotate_state(mailbox, 42, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"" MBOXNAME1_INT "\" " \
"uid=42 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=\"" VALUE0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
/* delete the value */
buf_free(&val);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
r = mailbox_commit(mailbox);
CU_ASSERT_EQUAL(r, 0);
mailbox_close(&mailbox);
/* check that there is no value any more */
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox);
CU_ASSERT_EQUAL_FATAL(r, 0);
r = mailbox_get_annotate_state(mailbox, 42, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"" MBOXNAME1_INT "\" " \
"uid=42 " \
"entry=\"" COMMENT "\" " \
VALUE_SHARED "=NIL"
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
buf_free(&val);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(val.s);
r = mailbox_commit(mailbox);
CU_ASSERT_EQUAL(r, 0);
mailbox_close(&mailbox);
annotatemore_close();
strarray_fini(&entries);
strarray_fini(&attribs);
strarray_fini(&results);
buf_free(&val);
mailbox_close(&mailbox);
}
static void test_delete(void)
{
int r;
annotate_state_t *astate = NULL;
struct mboxlist_entry mbentry;
strarray_t entries = STRARRAY_INITIALIZER;
strarray_t attribs = STRARRAY_INITIALIZER;
struct entryattlist *ealist = NULL;
struct buf val = BUF_INITIALIZER;
struct buf val2 = BUF_INITIALIZER;
struct mailbox *mailbox = NULL;
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), -ENOENT);
annotate_init(NULL, NULL);
annotatemore_open();
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox);
CU_ASSERT_EQUAL_FATAL(r, 0);
memset(&mbentry, 0, sizeof(mbentry));
mbentry.name = MBOXNAME1_INT;
mbentry.mbtype = 0;
mbentry.partition = PARTITION;
mbentry.acl = ACL;
strarray_append(&entries, COMMENT);
strarray_append(&attribs, VALUE_SHARED);
/* set some values */
buf_appendcstr(&val, VALUE0);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
astate = annotate_state_new();
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_set_mailbox(astate, mailbox);
CU_ASSERT_EQUAL(r, 0);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
r = annotate_state_commit(&astate);
CU_ASSERT_EQUAL(r, 0);
buf_reset(&val);
buf_appendcstr(&val, VALUE1);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = mailbox_get_annotate_state(mailbox, 42, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
buf_reset(&val);
buf_appendcstr(&val, VALUE2);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = mailbox_get_annotate_state(mailbox, 127, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
r = mailbox_commit(mailbox);
CU_ASSERT_EQUAL(r, 0);
mailbox_close(&mailbox);
/* check that we can fetch the values back */
r = annotatemore_msg_lookup(MBOXNAME1_INT, 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE1);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 127, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE2);
buf_free(&val2);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), 0);
/* delete all the entries associated with the mailbox */
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox);
CU_ASSERT_EQUAL_FATAL(r, 0);
r = annotate_delete_mailbox(mailbox);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), -ENOENT);
/* check that the values are gone */
r = annotatemore_msg_lookup(MBOXNAME1_INT, 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 127, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
annotatemore_close();
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), -ENOENT);
strarray_fini(&entries);
strarray_fini(&attribs);
buf_free(&val);
mailbox_close(&mailbox);
}
static void test_rename(void)
{
int r;
annotate_state_t *astate = NULL;
strarray_t entries = STRARRAY_INITIALIZER;
strarray_t attribs = STRARRAY_INITIALIZER;
struct entryattlist *ealist = NULL;
struct buf val = BUF_INITIALIZER;
struct buf val2 = BUF_INITIALIZER;
struct mailbox *mailbox1 = NULL;
struct mailbox *mailbox2 = NULL;
annotate_init(NULL, NULL);
annotatemore_open();
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), -ENOENT);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurfette/cyrus.annotations"), -ENOENT);
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox1);
CU_ASSERT_EQUAL_FATAL(r, 0);
r = mailbox_open_iwl(MBOXNAME2_INT, &mailbox2);
CU_ASSERT_EQUAL_FATAL(r, 0);
strarray_append(&entries, COMMENT);
strarray_append(&attribs, VALUE_SHARED);
/* set some values */
buf_appendcstr(&val, VALUE0);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
astate = annotate_state_new();
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_set_mailbox(astate, mailbox1);
CU_ASSERT_EQUAL(r, 0);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
r = annotate_state_commit(&astate);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
buf_reset(&val);
buf_appendcstr(&val, VALUE1);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = mailbox_get_annotate_state(mailbox1, 42, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
buf_reset(&val);
buf_appendcstr(&val, VALUE2);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = mailbox_get_annotate_state(mailbox1, 127, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
r = mailbox_commit(mailbox1);
CU_ASSERT_EQUAL(r, 0);
mailbox_close(&mailbox1);
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox1);
CU_ASSERT_EQUAL_FATAL(r, 0);
/* check that we can fetch the values back */
r = annotatemore_msg_lookup(MBOXNAME1_INT, 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE1);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 127, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE2);
buf_free(&val2);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), 0);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurfette/cyrus.annotations"), -ENOENT);
/* rename MBOXNAME1 -> MBOXNAME2 */
r = annotate_rename_mailbox(mailbox1, mailbox2);
CU_ASSERT_EQUAL(r, 0);
r = mailbox_copy_files(mailbox1, PARTITION, MBOXNAME2_INT);
CU_ASSERT_EQUAL(r, 0);
r = mailbox_rename_cleanup(&mailbox1, 0);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(mailbox1);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), -ENOENT);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurfette/cyrus.annotations"), 0);
/* check that the values are gone under the old name */
r = annotatemore_msg_lookup(MBOXNAME1_INT, 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 127, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
/* check that the values are present under the new name */
r = annotatemore_msg_lookup(MBOXNAME2_INT, 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME2_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE1);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME2_INT, 127, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE2);
buf_free(&val2);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), -ENOENT);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurfette/cyrus.annotations"), 0);
annotatemore_close();
strarray_fini(&entries);
strarray_fini(&attribs);
buf_free(&val);
mailbox_close(&mailbox1);
mailbox_close(&mailbox2);
}
static void test_abort(void)
{
int r;
annotate_state_t *astate = NULL;
strarray_t entries = STRARRAY_INITIALIZER;
strarray_t attribs = STRARRAY_INITIALIZER;
struct entryattlist *ealist = NULL;
struct buf val = BUF_INITIALIZER;
struct buf val2 = BUF_INITIALIZER;
struct mailbox *mailbox = NULL;
annotate_init(NULL, NULL);
annotatemore_open();
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox);
CU_ASSERT_EQUAL_FATAL(r, 0);
strarray_append(&entries, COMMENT);
strarray_append(&attribs, VALUE_SHARED);
/* check that the values we'll be setting are not already present */
buf_free(&val2);
r = annotatemore_msg_lookup("", 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
/* set a per-server value */
buf_appendcstr(&val, VALUE0);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
astate = annotate_state_new();
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, /*isadmin*/1, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
/* but abort the txn */
annotate_state_abort(&astate);
/* check that the values are still not present */
buf_free(&val2);
r = annotatemore_msg_lookup("", 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
/* set a per-mailbox value */
buf_appendcstr(&val, VALUE0);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
astate = annotate_state_new();
r = annotate_state_set_mailbox(astate, mailbox);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, /*isadmin*/0, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
/* but abort the txn */
annotate_state_abort(&astate);
/* check that the values are still not present */
buf_free(&val2);
r = annotatemore_msg_lookup("", 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
/* set a per-message value */
buf_reset(&val);
buf_appendcstr(&val, VALUE1);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = mailbox_get_annotate_state(mailbox, 42, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
/* but abort the txn */
annotate_state_abort(&mailbox->annot_state);
/* check that the values are still not present */
buf_free(&val2);
r = annotatemore_msg_lookup("", 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 0, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
annotatemore_close();
strarray_fini(&entries);
strarray_fini(&attribs);
buf_free(&val);
buf_free(&val2);
mailbox_close(&mailbox);
}
static void test_msg_copy(void)
{
int r;
annotate_state_t *astate = NULL;
strarray_t entries = STRARRAY_INITIALIZER;
strarray_t attribs = STRARRAY_INITIALIZER;
struct entryattlist *ealist = NULL;
struct buf val = BUF_INITIALIZER;
struct buf val2 = BUF_INITIALIZER;
struct mailbox *mailbox1 = NULL;
struct mailbox *mailbox2 = NULL;
annotate_init(NULL, NULL);
annotatemore_open();
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), -ENOENT);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurfette/cyrus.annotations"), -ENOENT);
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox1);
CU_ASSERT_EQUAL_FATAL(r, 0);
r = mailbox_open_iwl(MBOXNAME2_INT, &mailbox2);
CU_ASSERT_EQUAL_FATAL(r, 0);
strarray_append(&entries, COMMENT);
strarray_append(&attribs, VALUE_SHARED);
/* set some values */
buf_appendcstr(&val, VALUE0);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = mailbox_get_annotate_state(mailbox1, 17, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
buf_reset(&val);
buf_appendcstr(&val, VALUE1);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = mailbox_get_annotate_state(mailbox1, 42, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
buf_reset(&val);
buf_appendcstr(&val, VALUE2);
setentryatt(&ealist, COMMENT, VALUE_SHARED, &val);
r = mailbox_get_annotate_state(mailbox1, 127, &astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_store(astate, ealist);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
r = mailbox_commit(mailbox1);
CU_ASSERT_EQUAL(r, 0);
mailbox_close(&mailbox1);
/* check that we can fetch the values back */
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 17, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE1);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 127, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE2);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME2_INT, 35, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL_FATAL(val2.s);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), 0);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurfette/cyrus.annotations"), -ENOENT);
/* copy MBOXNAME1,17 -> MBOXNAME2,35 */
r = mailbox_open_iwl(MBOXNAME1_INT, &mailbox1);
CU_ASSERT_EQUAL_FATAL(r, 0);
/* ensure we have an astate connected to the destination
* mailbox, so that the annotation txn will be committed
* when we close the mailbox */
r = mailbox_get_annotate_state(mailbox2, 35, &astate);
CU_ASSERT_EQUAL(r, 0);
r = annotate_msg_copy(mailbox1, 17, mailbox2, 35, "smurf");
CU_ASSERT_EQUAL(r, 0);
r = mailbox_commit(mailbox2);
CU_ASSERT_EQUAL(r, 0);
mailbox_close(&mailbox2);
mailbox_close(&mailbox1);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), 0);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurfette/cyrus.annotations"), 0);
/* check that the values copied are present for both mailboxes */
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 17, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME2_INT, 35, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
/* check that the values not copied are only present in the source
* mailbox */
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE1);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME1_INT, 127, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE2);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME2_INT, 42, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL(val2.s);
buf_free(&val2);
r = annotatemore_msg_lookup(MBOXNAME2_INT, 127, COMMENT, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NULL(val2.s);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurf/cyrus.annotations"), 0);
CU_ASSERT_EQUAL(fexists(DBDIR"/data/user/smurfette/cyrus.annotations"), 0);
annotatemore_close();
strarray_fini(&entries);
strarray_fini(&attribs);
buf_free(&val);
buf_free(&val2);
mailbox_close(&mailbox1);
mailbox_close(&mailbox2);
}
static void test_missing_definitions_file(void)
{
set_annotation_definitions(NULL);
CU_SYSLOG_MATCH("annotations\\.def: could not open.*No such file");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
CU_ASSERT_SYSLOG(/*all*/0, 1);
}
static void test_broken_definitions_file_1(void)
{
set_annotation_definitions(
EXENTRY",sXerver,string,backend,value.shared,\n");
CU_SYSLOG_MATCH("invalid annotation scope.*'sXerver'");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
CU_ASSERT_SYSLOG(/*all*/0, 1);
}
static void test_broken_definitions_file_2(void)
{
set_annotation_definitions(
EXENTRY",server,stXring,backend,value.shared,\n");
CU_SYSLOG_MATCH("invalid annotation type.*'stXring'");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
CU_ASSERT_SYSLOG(/*all*/0, 1);
}
static void test_broken_definitions_file_3(void)
{
set_annotation_definitions(
EXENTRY",server,string,bacXkend,value.shared,\n");
CU_SYSLOG_MATCH("invalid annotation proxy type.*'bacXkend'");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
CU_ASSERT_SYSLOG(/*all*/0, 1);
}
static void test_broken_definitions_file_4(void)
{
set_annotation_definitions(
EXENTRY",server,string,backend,valuXe.shared,\n");
CU_SYSLOG_MATCH("invalid annotation attributes.*'valuXe.shared'");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
CU_ASSERT_SYSLOG(/*all*/0, 1);
}
static void test_broken_definitions_file_5(void)
{
set_annotation_definitions(
"/flags/foobar,message,string,backend,value.shared,\n");
CU_SYSLOG_MATCH("message entry under /flags/");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
CU_ASSERT_SYSLOG(/*all*/0, 1);
}
static void test_broken_definitions_file_6(void)
{
set_annotation_definitions(
"/vendor/cmu/cyrus-imapd/foobar,server,string,backend,value.shared,\n");
CU_SYSLOG_MATCH("annotation under /vendor/cmu/cyrus-imapd/");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
CU_ASSERT_SYSLOG(/*all*/0, 1);
}
static void test_broken_definitions_file_7(void)
{
set_annotation_definitions(
EXENTRY",server,string,backend,value.shared,,,,\n");
CU_SYSLOG_MATCH("junk at end of line");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
CU_ASSERT_SYSLOG(/*all*/0, 1);
}
static void test_broken_definitions_file_8(void)
{
set_annotation_definitions(
EXENTRY",server,string,\n");
CU_SYSLOG_MATCH("invalid annotation attributes.*");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
/* Please note that this one complains to syslog twice */
CU_ASSERT_SYSLOG(/*all*/0, 2);
}
static void test_broken_definitions_file_9(void)
{
/* test that when parsing a bitfield, only the first
* invalid name is reported in the error context */
set_annotation_definitions(
EXENTRY",server,string,backend,value valXue valYue,\n");
CU_SYSLOG_MATCH("invalid annotation attributes.*'valXue'");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
CU_ASSERT_SYSLOG(/*all*/0, 1);
}
static void test_broken_definitions_file_10(void)
{
set_annotation_definitions(
EXENTRY",ser@ver,string,backend,value.shared,\n");
CU_SYSLOG_MATCH("invalid character.*'@");
annotate_init(NULL, NULL);
/* if we got here, we didn't fatal() */
/* but we did complain to syslog */
CU_ASSERT_SYSLOG(/*all*/0, 1);
}
static void test_getset_server_undefined(void)
{
int r;
annotate_state_t *astate = NULL;
strarray_t entries = STRARRAY_INITIALIZER;
strarray_t attribs = STRARRAY_INITIALIZER;
strarray_t results = STRARRAY_INITIALIZER;
struct entryattlist *ealist = NULL;
struct buf val = BUF_INITIALIZER;
struct buf val2 = BUF_INITIALIZER;
annotate_init(NULL, NULL);
annotatemore_open();
astate = annotate_state_new();
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
strarray_append(&entries, EXENTRY);
strarray_append(&attribs, VALUE_SHARED);
/* check that there is no value initially */
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" EXENTRY "\" " \
VALUE_SHARED "=NIL"
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", EXENTRY, /*userid*/"", &val);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(val.s);
/* setting a value should fail */
buf_appendcstr(&val, VALUE0);
setentryatt(&ealist, EXENTRY, VALUE_SHARED, &val);
annotate_state_set_auth(astate, /*isadmin*/1, userid, auth_state);
r = annotate_state_store(astate, ealist);
annotate_state_set_auth(astate, /*isadmin*/0, userid, auth_state);
CU_ASSERT_EQUAL(r, IMAP_PERMISSION_DENIED);
freeentryatts(ealist);
ealist = NULL;
r = annotate_state_commit(&astate);
CU_ASSERT_EQUAL(r, 0);
/* check that there is no value */
astate = annotate_state_new();
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, /*isadmin*/0, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" EXENTRY "\" " \
VALUE_SHARED "=NIL"
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
buf_free(&val);
r = annotatemore_lookup(/*mboxname*/"", EXENTRY, /*userid*/"", &val);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(val.s);
annotate_state_abort(&astate);
annotatemore_close();
strarray_fini(&entries);
strarray_fini(&attribs);
strarray_fini(&results);
buf_free(&val);
buf_free(&val2);
}
static void test_getset_server_defined(void)
{
int r;
annotate_state_t *astate = NULL;
strarray_t entries = STRARRAY_INITIALIZER;
strarray_t attribs = STRARRAY_INITIALIZER;
strarray_t results = STRARRAY_INITIALIZER;
struct entryattlist *ealist = NULL;
struct buf val = BUF_INITIALIZER;
struct buf val2 = BUF_INITIALIZER;
set_annotation_definitions(
EXENTRY",server,string,backend,value.shared,\n");
annotate_init(NULL, NULL);
annotatemore_open();
astate = annotate_state_new();
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
strarray_append(&entries, EXENTRY);
strarray_append(&attribs, VALUE_SHARED);
strarray_append(&attribs, SIZE_SHARED);
/* check that there is no value initially */
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" EXENTRY "\" " \
VALUE_SHARED "=NIL " \
SIZE_SHARED "=\"0\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", EXENTRY, /*userid*/"", &val);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(val.s);
/* set a value */
buf_appendcstr(&val, VALUE0);
setentryatt(&ealist, EXENTRY, VALUE_SHARED, &val);
annotate_state_set_auth(astate, /*isadmin*/1, userid, auth_state);
r = annotate_state_store(astate, ealist);
annotate_state_set_auth(astate, /*isadmin*/0, userid, auth_state);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
/* check that we can fetch the value back in the same txn */
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" EXENTRY "\" " \
VALUE_SHARED "=\"" VALUE0 "\" " \
SIZE_SHARED "=\"" LENGTH0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", EXENTRY, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
r = annotate_state_commit(&astate);
CU_ASSERT_EQUAL(r, 0);
/* check that we can fetch the value back in a new txn */
astate = annotate_state_new();
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" EXENTRY "\" " \
VALUE_SHARED "=\"" VALUE0 "\" " \
SIZE_SHARED "=\"" LENGTH0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", EXENTRY, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
annotate_state_abort(&astate);
annotatemore_close();
/* check that we can fetch the value back after close and re-open */
annotatemore_open();
astate = annotate_state_new();
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" EXENTRY "\" " \
VALUE_SHARED "=\"" VALUE0 "\" " \
SIZE_SHARED "=\"" LENGTH0 "\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", EXENTRY, /*userid*/"", &val2);
CU_ASSERT_EQUAL_FATAL(r, 0);
CU_ASSERT_PTR_NOT_NULL_FATAL(val2.s);
CU_ASSERT_STRING_EQUAL(buf_cstring(&val2), VALUE0);
buf_free(&val2);
buf_free(&val);
setentryatt(&ealist, EXENTRY, VALUE_SHARED, &val);
annotate_state_set_auth(astate, /*isadmin*/1, userid, auth_state);
r = annotate_state_store(astate, ealist);
annotate_state_set_auth(astate, /*isadmin*/0, userid, auth_state);
CU_ASSERT_EQUAL(r, 0);
freeentryatts(ealist);
ealist = NULL;
r = annotate_state_commit(&astate);
CU_ASSERT_EQUAL(r, 0);
/* check that there is no value any more */
astate = annotate_state_new();
r = annotate_state_set_server(astate);
CU_ASSERT_EQUAL(r, 0);
annotate_state_set_auth(astate, isadmin, userid, auth_state);
r = annotate_state_fetch(astate,
&entries, &attribs,
fetch_cb, &results,
NULL);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL_FATAL(results.count, 1);
#define EXPECTED \
"mboxname=\"\" " \
"uid=0 " \
"entry=\"" EXENTRY "\" " \
VALUE_SHARED "=NIL " \
SIZE_SHARED "=\"0\""
CU_ASSERT_STRING_EQUAL(results.data[0], EXPECTED);
#undef EXPECTED
strarray_truncate(&results, 0);
r = annotatemore_lookup(/*mboxname*/"", EXENTRY, /*userid*/"", &val);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_PTR_NULL(val.s);
annotate_state_abort(&astate);
annotatemore_close();
strarray_fini(&entries);
strarray_fini(&attribs);
strarray_fini(&results);
buf_free(&val);
}
static const char *stringifyea(const struct entryattlist *ea)
{
const struct attvaluelist *av;
static struct buf buf;
buf_reset(&buf);
for ( ; ea ; ea = ea->next) {
buf_printf(&buf, "(%s", ea->entry);
for (av = ea->attvalues ; av ; av = av->next) {
buf_printf(&buf, "(%s\"", av->attrib);
buf_appendmap(&buf, av->value.s, av->value.len);
buf_appendcstr(&buf, "\")");
}
buf_putc(&buf, ')');
}
return buf_cstring(&buf);
}
static void test_setentryatt(void)
{
struct entryattlist *eal = NULL;
struct buf val = BUF_INITIALIZER;
CU_ASSERT_PTR_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal), "");
/* Test data courtesy http://hipsteripsum.me */
/* append an ea and av */
buf_init_ro(&val, "coffee", 6);
setentryatt(&eal, "letterpress", "single-origin", &val);
CU_ASSERT_PTR_NOT_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal),
"(letterpress(single-origin\"coffee\"))");
/* append another ea and av */
buf_init_ro(&val, "mustache", 8);
setentryatt(&eal, "cosby", "sweater", &val);
CU_ASSERT_PTR_NOT_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal),
"(letterpress(single-origin\"coffee\"))"
"(cosby(sweater\"mustache\"))");
/* append a third ea and av */
buf_init_ro(&val, "portland", 8);
setentryatt(&eal, "cred", "artisan", &val);
CU_ASSERT_PTR_NOT_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal),
"(letterpress(single-origin\"coffee\"))"
"(cosby(sweater\"mustache\"))"
"(cred(artisan\"portland\"))");
/* replace the value in an av */
buf_init_ro(&val, "shoreditch", 10);
setentryatt(&eal, "cosby", "sweater", &val);
CU_ASSERT_PTR_NOT_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal),
"(letterpress(single-origin\"coffee\"))"
"(cosby(sweater\"shoreditch\"))"
"(cred(artisan\"portland\"))");
/* add an av to an existing ea */
buf_init_ro(&val, "gluten-free", 11);
setentryatt(&eal, "letterpress", "biodiesel", &val);
CU_ASSERT_PTR_NOT_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal),
"(letterpress(single-origin\"coffee\")"
"(biodiesel\"gluten-free\"))"
"(cosby(sweater\"shoreditch\"))"
"(cred(artisan\"portland\"))");
freeentryatts(eal);
buf_free(&val);
}
static void test_clearentryatt(void)
{
struct entryattlist *eal = NULL;
struct buf val = BUF_INITIALIZER;
/* Test data courtesy http://hipsteripsum.me */
/* append an ea and av */
buf_init_ro(&val, "coffee", 6);
setentryatt(&eal, "letterpress", "single-origin", &val);
/* add an av to an existing ea */
buf_init_ro(&val, "gluten-free", 11);
setentryatt(&eal, "letterpress", "biodiesel", &val);
/* add another av to an existing ea */
buf_init_ro(&val, "organic", 7);
setentryatt(&eal, "letterpress", "keffiyeh", &val);
/* append another ea and av */
buf_init_ro(&val, "shoreditch", 10);
setentryatt(&eal, "cosby", "sweater", &val);
/* append a third ea and av */
buf_init_ro(&val, "portland", 8);
setentryatt(&eal, "cred", "artisan", &val);
CU_ASSERT_PTR_NOT_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal),
"(letterpress(single-origin\"coffee\")"
"(biodiesel\"gluten-free\")"
"(keffiyeh\"organic\"))"
"(cosby(sweater\"shoreditch\"))"
"(cred(artisan\"portland\"))");
clearentryatt(&eal, "cosby", "sweater");
CU_ASSERT_PTR_NOT_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal),
"(letterpress(single-origin\"coffee\")"
"(biodiesel\"gluten-free\")"
"(keffiyeh\"organic\"))"
"(cred(artisan\"portland\"))");
clearentryatt(&eal, "letterpress", "biodiesel");
CU_ASSERT_PTR_NOT_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal),
"(letterpress(single-origin\"coffee\")"
"(keffiyeh\"organic\"))"
"(cred(artisan\"portland\"))");
clearentryatt(&eal, "letterpress", "single-origin");
CU_ASSERT_PTR_NOT_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal),
"(letterpress"
"(keffiyeh\"organic\"))"
"(cred(artisan\"portland\"))");
clearentryatt(&eal, "letterpress", "keffiyeh");
CU_ASSERT_PTR_NOT_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal),
"(cred(artisan\"portland\"))");
clearentryatt(&eal, "cred", "artisan");
CU_ASSERT_PTR_NULL(eal);
CU_ASSERT_STRING_EQUAL(stringifyea(eal), "");
freeentryatts(eal);
buf_free(&val);
}
static int set_up(void)
{
int r;
struct mboxlist_entry mbentry;
struct mailbox *mailbox;
const char * const *d;
static const char * const dirs[] = {
DBDIR,
DBDIR"/db",
DBDIR"/conf",
DBDIR"/data",
DBDIR"/data/user",
DBDIR"/data/user/smurf",
DBDIR"/data/user/smurfette",
NULL
};
r = system("rm -rf " DBDIR);
if (r)
return r;
r = fexists(DBDIR);
if (r != -ENOENT)
return ENOTDIR;
for (d = dirs ; *d ; d++) {
r = mkdir(*d, 0777);
if (r < 0) {
int e = errno;
perror(*d);
return e;
}
}
libcyrus_config_setstring(CYRUSOPT_CONFIG_DIR, DBDIR);
config_read_string(
"configdirectory: "DBDIR"/conf\n"
"defaultpartition: "PARTITION"\n"
"partition-"PARTITION": "DBDIR"/data\n"
);
cyrusdb_init();
config_mboxlist_db = "skiplist";
config_annotation_db = "skiplist";
config_quota_db = "skiplist";
userid = "smurf";
isadmin = 0;
auth_state = auth_newstate(userid);
quotadb_init(0);
quotadb_open(NULL);
mboxlist_init(0);
mboxlist_open(NULL);
memset(&mbentry, 0, sizeof(mbentry));
mbentry.name = MBOXNAME1_INT;
mbentry.mbtype = 0;
mbentry.partition = PARTITION;
mbentry.acl = ACL;
r = mboxlist_update(&mbentry, /*localonly*/1);
if (r)
return r;
r = mailbox_create(MBOXNAME1_INT, /*mbtype*/0, PARTITION, ACL,
/*uniqueid*/NULL,
/*options*/0, /*uidvalidity*/0,
/*highestmodseq*/0, &mailbox);
if (r)
return r;
mailbox_close(&mailbox);
memset(&mbentry, 0, sizeof(mbentry));
mbentry.name = MBOXNAME2_INT;
mbentry.mbtype = 0;
mbentry.partition = PARTITION;
mbentry.acl = ACL;
r = mboxlist_update(&mbentry, /*localonly*/1);
if (r)
return r;
r = mailbox_create(MBOXNAME2_INT, /*mbtype*/0, PARTITION, ACL,
/*uniqueid*/NULL,
/*options*/0, /*uidvalidity*/0,
/*highestmodseq*/0, &mailbox);
if (r)
return r;
mailbox_close(&mailbox);
old_annotation_definitions =
imapopts[IMAPOPT_ANNOTATION_DEFINITIONS].val.s;
return 0;
}
static int tear_down(void)
{
int r;
mboxlist_close();
mboxlist_done();
quotadb_close();
quotadb_done();
annotate_done();
imapopts[IMAPOPT_ANNOTATION_DEFINITIONS].val.s =
old_annotation_definitions;
auth_freestate(auth_state);
cyrusdb_done();
config_mboxlist_db = NULL;
config_annotation_db = NULL;
r = system("rm -rf " DBDIR);
if (r) r = -1;
return r;
}
/* vim: set ft=c: */
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Apr 24, 10:29 AM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18816417
Default Alt Text
annotate.testc (57 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline