dsync_mailbox_dup(pool_t pool, const struct dsync_mailbox *box)
{
struct dsync_mailbox *dest;
- const char *const *cache_fields = NULL, *dup;
+ const struct mailbox_cache_field *cache_fields = NULL;
+ struct mailbox_cache_field *dup;
unsigned int i, count = 0;
dest = p_new(pool, struct dsync_mailbox, 1);
else {
p_array_init(&dest->cache_fields, pool, count);
for (i = 0; i < count; i++) {
- dup = p_strdup(pool, cache_fields[i]);
- array_append(&dest->cache_fields, &dup, 1);
+ dup = array_append_space(&dest->cache_fields);
+ *dup = cache_fields[i];
+ dup->name = p_strdup(pool, dup->name);
}
}
return dest;
otherwise it's the last rename timestamp. */
time_t last_change;
enum dsync_mailbox_flags flags;
- ARRAY_TYPE(const_string) cache_fields;
+ ARRAY_TYPE(mailbox_cache_field) cache_fields;
};
ARRAY_DEFINE_TYPE(dsync_mailbox, struct dsync_mailbox *);
#define dsync_mailbox_is_noselect(dsync_box) \
static void
proxy_client_worker_select_mailbox(struct dsync_worker *_worker,
const mailbox_guid_t *mailbox,
- const ARRAY_TYPE(const_string) *cache_fields)
+ const ARRAY_TYPE(mailbox_cache_field) *cache_fields)
{
struct proxy_client_dsync_worker *worker =
(struct proxy_client_dsync_worker *)_worker;
str_append(str, "BOX-SELECT\t");
dsync_proxy_mailbox_guid_export(str, mailbox);
if (cache_fields != NULL)
- dsync_proxy_strings_export(str, cache_fields);
+ dsync_proxy_cache_fields_export(str, cache_fields);
str_append_c(str, '\n');
proxy_client_worker_cmd(worker, str);
} T_END;
cmd_box_select(struct dsync_proxy_server *server, const char *const *args)
{
struct dsync_mailbox box;
- unsigned int i, count;
+ const char *error;
memset(&box, 0, sizeof(box));
if (args[0] == NULL ||
}
args++;
- count = str_array_length(args);
- t_array_init(&box.cache_fields, count + 1);
- for (i = 0; i < count; i++)
- array_append(&box.cache_fields, &args[i], 1);
+ if (dsync_proxy_cache_fields_import(args, pool_datastack_create(),
+ &box.cache_fields, &error) < 0) {
+ i_error("box-select: %s", error);
+ return -1;
+ }
dsync_worker_select_mailbox(server->worker, &box);
return 1;
}
#include "hex-binary.h"
#include "mail-types.h"
#include "imap-util.h"
+#include "mail-cache.h"
#include "dsync-data.h"
#include "dsync-proxy.h"
#include <stdlib.h>
-void dsync_proxy_strings_export(string_t *str,
- const ARRAY_TYPE(const_string) *strings)
+#define DSYNC_CACHE_DECISION_NO 'n'
+#define DSYNC_CACHE_DECISION_YES 'y'
+#define DSYNC_CACHE_DECISION_TEMP 't'
+#define DSYNC_CACHE_DECISION_FORCED 'f'
+
+void dsync_proxy_cache_fields_export(string_t *str,
+ const ARRAY_TYPE(mailbox_cache_field) *_fields)
{
- const char *const *fields;
+ const struct mailbox_cache_field *fields;
unsigned int i, count;
- if (!array_is_created(strings))
+ if (!array_is_created(_fields))
return;
- fields = array_get(strings, &count);
+ fields = array_get(_fields, &count);
for (i = 0; i < count; i++) {
str_append_c(str, '\t');
- str_tabescape_write(str, fields[i]);
+ str_tabescape_write(str, fields[i].name);
+ str_append_c(str, '\t');
+ switch (fields[i].decision & ~MAIL_CACHE_DECISION_FORCED) {
+ case MAIL_CACHE_DECISION_NO:
+ str_append_c(str, DSYNC_CACHE_DECISION_NO);
+ break;
+ case MAIL_CACHE_DECISION_YES:
+ str_append_c(str, DSYNC_CACHE_DECISION_YES);
+ break;
+ case MAIL_CACHE_DECISION_TEMP:
+ str_append_c(str, DSYNC_CACHE_DECISION_TEMP);
+ break;
+ }
+ if ((fields[i].decision & MAIL_CACHE_DECISION_FORCED) != 0)
+ str_append_c(str, DSYNC_CACHE_DECISION_FORCED);
+ str_printfa(str, "\t%ld", fields[i].last_used);
+ }
+}
+
+static int dsync_proxy_cache_dec_parse(const char *str,
+ enum mail_cache_decision_type *dec_r)
+{
+ switch (*str++) {
+ case DSYNC_CACHE_DECISION_NO:
+ *dec_r = MAIL_CACHE_DECISION_NO;
+ break;
+ case DSYNC_CACHE_DECISION_YES:
+ *dec_r = MAIL_CACHE_DECISION_YES;
+ break;
+ case DSYNC_CACHE_DECISION_TEMP:
+ *dec_r = MAIL_CACHE_DECISION_TEMP;
+ break;
+ default:
+ return -1;
+ }
+ if (*str == DSYNC_CACHE_DECISION_FORCED) {
+ *dec_r |= MAIL_CACHE_DECISION_FORCED;
+ str++;
+ }
+ if (*str != '\0')
+ return -1;
+ return 0;
+}
+
+int dsync_proxy_cache_fields_import(const char *const *args, pool_t pool,
+ ARRAY_TYPE(mailbox_cache_field) *fields,
+ const char **error_r)
+{
+ struct mailbox_cache_field *cache_field;
+ enum mail_cache_decision_type dec;
+ unsigned int i, count = str_array_length(args);
+
+ if (count % 3 != 0) {
+ *error_r = "Invalid mailbox cache fields";
+ return -1;
+ }
+ if (!array_is_created(fields))
+ p_array_init(fields, pool, (count/3) + 1);
+ for (i = 0; i < count; i += 3) {
+ cache_field = array_append_space(fields);
+ cache_field->name = p_strdup(pool, args[i]);
+
+ if (dsync_proxy_cache_dec_parse(args[i+1], &dec) < 0) {
+ *error_r = "Invalid cache decision";
+ return -1;
+ }
+ cache_field->decision = dec;
+ if (str_to_time(args[i+2], &cache_field->last_used) < 0) {
+ *error_r = "Invalid cache last_used";
+ return -1;
+ }
}
+ return 0;
}
void dsync_proxy_msg_export(string_t *str,
box->uid_validity, box->uid_next, box->message_count,
(unsigned long long)box->highest_modseq,
box->first_recent_uid);
- dsync_proxy_strings_export(str, &box->cache_fields);
+ dsync_proxy_cache_fields_export(str, &box->cache_fields);
}
int dsync_proxy_mailbox_import_unescaped(pool_t pool, const char *const *args,
args += i;
count -= i;
- p_array_init(&box_r->cache_fields, pool, count + 1);
- for (i = 0; i < count; i++) {
- const char *field_name = p_strdup(pool, args[i]);
- array_append(&box_r->cache_fields, &field_name, 1);
- }
+ if (dsync_proxy_cache_fields_import(args, pool, &box_r->cache_fields,
+ error_r) < 0)
+ return -1;
return 0;
}
#define DSYNC_PROXY_CLIENT_TIMEOUT_MSECS (14*60*1000)
#define DSYNC_PROXY_SERVER_TIMEOUT_MSECS (15*60*1000)
-#define DSYNC_PROXY_CLIENT_GREETING_LINE "dsync-client\t1"
-#define DSYNC_PROXY_SERVER_GREETING_LINE "dsync-server\t1"
+#define DSYNC_PROXY_CLIENT_GREETING_LINE "dsync-client\t2"
+#define DSYNC_PROXY_SERVER_GREETING_LINE "dsync-server\t2"
struct dsync_message;
struct dsync_mailbox;
-void dsync_proxy_strings_export(string_t *str,
- const ARRAY_TYPE(const_string) *strings);
+void dsync_proxy_cache_fields_export(string_t *str,
+ const ARRAY_TYPE(mailbox_cache_field) *fields);
+int dsync_proxy_cache_fields_import(const char *const *args, pool_t pool,
+ ARRAY_TYPE(mailbox_cache_field) *fields,
+ const char **error_r);
void dsync_proxy_msg_export(string_t *str, const struct dsync_message *msg);
int dsync_proxy_msg_parse_flags(pool_t pool, const char *str,
struct local_dsync_dir_change *dir_change, change_lookup;
struct local_dsync_mailbox *old_lbox;
enum mail_error error;
- const char *const *fields;
- unsigned int i, field_count;
+ struct mailbox_cache_field *cache_fields;
+ unsigned int i, cache_field_count;
memset(dsync_box_r, 0, sizeof(*dsync_box_r));
dsync_box_r->highest_modseq = status.highest_modseq;
p_clear(iter->ret_pool);
- fields = array_get(metadata.cache_fields, &field_count);
- p_array_init(&dsync_box_r->cache_fields, iter->ret_pool, field_count);
- for (i = 0; i < field_count; i++) {
- const char *field_name = p_strdup(iter->ret_pool, fields[i]);
-
- array_append(&dsync_box_r->cache_fields, &field_name, 1);
+ p_array_init(&dsync_box_r->cache_fields, iter->ret_pool,
+ array_count(metadata.cache_fields));
+ array_append_array(&dsync_box_r->cache_fields, metadata.cache_fields);
+ cache_fields = array_get_modifiable(&dsync_box_r->cache_fields,
+ &cache_field_count);
+ for (i = 0; i < cache_field_count; i++) {
+ cache_fields[i].name =
+ p_strdup(iter->ret_pool, cache_fields[i].name);
}
old_lbox = hash_table_lookup(worker->mailbox_hash,
static void
local_worker_set_cache_fields(struct local_dsync_worker *worker,
- const ARRAY_TYPE(const_string) *cache_fields)
+ const ARRAY_TYPE(mailbox_cache_field) *cache_fields)
{
struct mailbox_update update;
- const char *const *fields, **new_fields;
+ const struct mailbox_cache_field *fields;
+ struct mailbox_cache_field *new_fields;
unsigned int count;
fields = array_get(cache_fields, &count);
- new_fields = t_new(const char *, count + 1);
- memcpy(new_fields, fields, sizeof(const char *) * count);
+ new_fields = t_new(struct mailbox_cache_field, count + 1);
+ memcpy(new_fields, fields, sizeof(struct mailbox_cache_field) * count);
memset(&update, 0, sizeof(update));
- update.cache_fields = new_fields;
+ update.cache_updates = new_fields;
mailbox_update(worker->selected_box, &update);
}
static void
local_worker_select_mailbox(struct dsync_worker *_worker,
const mailbox_guid_t *mailbox,
- const ARRAY_TYPE(const_string) *cache_fields)
+ const ARRAY_TYPE(mailbox_cache_field) *cache_fields)
{
struct local_dsync_worker *worker =
(struct local_dsync_worker *)_worker;
void (*select_mailbox)(struct dsync_worker *worker,
const mailbox_guid_t *mailbox,
- const ARRAY_TYPE(const_string) *cache_fields);
+ const ARRAY_TYPE(mailbox_cache_field) *cache_fields);
void (*msg_update_metadata)(struct dsync_worker *worker,
const struct dsync_message *msg);
void (*msg_update_uid)(struct dsync_worker *worker,
bool dsync_mailboxes_equal(const struct dsync_mailbox *box1,
const struct dsync_mailbox *box2)
{
- const char *const *f1 = NULL, *const *f2 = NULL;
+ const struct mailbox_cache_field *f1 = NULL, *f2 = NULL;
unsigned int i, f1_count = 0, f2_count = 0;
if (strcmp(box1->name, box2->name) != 0 ||
if (f1_count != f2_count)
return FALSE;
for (i = 0; i < f1_count; i++) {
- if (strcmp(f1[i], f2[i]) != 0)
+ if (strcmp(f1[i].name, f2[i].name) != 0 ||
+ f1[i].decision != f2[i].decision ||
+ f1[i].last_used != f2[i].last_used)
return FALSE;
}
return TRUE;
#include "lib.h"
#include "array.h"
#include "str.h"
+#include "mail-cache.h"
#include "dsync-proxy.h"
#include "test-dsync-common.h"
#include "test-common.h"
static void test_dsync_proxy_mailbox(void)
{
- static const char *cache1 = "cache1";
- static const char *cache2 = "cache2";
+ static struct mailbox_cache_field cache1 =
+ { "cache1", MAIL_CACHE_DECISION_NO, 1234 };
+ static struct mailbox_cache_field cache2 =
+ { "cache2", MAIL_CACHE_DECISION_TEMP | MAIL_CACHE_DECISION_FORCED, 0 };
string_t *str;
struct dsync_mailbox box_in, box_out;
const char *error;
static void
test_worker_select_mailbox(struct dsync_worker *_worker,
const mailbox_guid_t *mailbox,
- const ARRAY_TYPE(const_string) *cache_fields)
+ const ARRAY_TYPE(mailbox_cache_field) *cache_fields)
{
struct test_dsync_worker *worker = (struct test_dsync_worker *)_worker;
struct dsync_mailbox box;
mailbox_guid_t selected_mailbox;
mailbox_guid_t *msg_iter_mailboxes;
unsigned int msg_iter_mailbox_count;
- const ARRAY_TYPE(const_string) *cache_fields;
+ const ARRAY_TYPE(mailbox_cache_field) *cache_fields;
};
struct dsync_worker *dsync_worker_init_test(void);
if (mailbox_open(box) < 0)
return -1;
}
- if (update->cache_fields != NULL)
- index_storage_mailbox_update_cache_fields(box, update);
+ if (update->cache_updates != NULL)
+ index_storage_mailbox_update_cache(box, update);
return mdbox_write_index_header(box, update, NULL);
}
if (mailbox_open(box) < 0)
return -1;
}
- if (update->cache_fields != NULL)
- index_storage_mailbox_update_cache_fields(box, update);
+ if (update->cache_updates != NULL)
+ index_storage_mailbox_update_cache(box, update);
return sdbox_mailbox_create_indexes(box, update, NULL);
}
{
const struct mail_cache_field *fields;
enum mail_cache_decision_type dec;
- ARRAY_TYPE(const_string) *cache_fields;
+ ARRAY_TYPE(mailbox_cache_field) *cache_fields;
+ struct mailbox_cache_field *cf;
unsigned int i, count;
fields = mail_cache_register_get_list(box->cache,
pool_datastack_create(), &count);
- cache_fields = t_new(ARRAY_TYPE(const_string), 1);
+ cache_fields = t_new(ARRAY_TYPE(mailbox_cache_field), 1);
t_array_init(cache_fields, count);
for (i = 0; i < count; i++) {
dec = fields[i].decision & ~MAIL_CACHE_DECISION_FORCED;
- if (dec != MAIL_CACHE_DECISION_NO)
- array_append(cache_fields, &fields[i].name, 1);
+ if (dec != MAIL_CACHE_DECISION_NO) {
+ cf = array_append_space(cache_fields);
+ cf->name = fields[i].name;
+ cf->decision = fields[i].decision;
+ cf->last_used = fields[i].last_used;
+ }
}
metadata_r->cache_fields = cache_fields;
}
mail_index_alloc_cache_unref(&box->index);
}
-void index_storage_mailbox_update_cache_fields(struct mailbox *box,
- const struct mailbox_update *update)
+void index_storage_mailbox_update_cache(struct mailbox *box,
+ const struct mailbox_update *update)
{
- const char *const *field_names = update->cache_fields;
+ const struct mailbox_cache_field *updates = update->cache_updates;
ARRAY_DEFINE(new_fields, struct mail_cache_field);
const struct mail_cache_field *old_fields;
struct mail_cache_field field;
/* There shouldn't be many fields, so don't worry about O(n^2). */
t_array_init(&new_fields, 32);
- for (i = 0; field_names[i] != NULL; i++) {
+ for (i = 0; updates[i].name != NULL; i++) {
/* see if it's an existing field */
for (j = 0; j < old_count; j++) {
- if (strcmp(field_names[i], old_fields[j].name) == 0)
+ if (strcmp(updates[i].name, old_fields[j].name) == 0)
break;
}
if (j != old_count) {
field = old_fields[j];
- if (field.decision == MAIL_CACHE_DECISION_NO)
- field.decision = MAIL_CACHE_DECISION_TEMP;
- array_append(&new_fields, &field, 1);
- } else if (strncmp(field_names[i], "hdr.", 4) == 0) {
+ } else if (strncmp(updates[i].name, "hdr.", 4) == 0) {
/* new header */
memset(&field, 0, sizeof(field));
- field.name = field_names[i];
+ field.name = updates[i].name;
field.type = MAIL_CACHE_FIELD_HEADER;
- field.decision = MAIL_CACHE_DECISION_TEMP;
- array_append(&new_fields, &field, 1);
} else {
/* new unknown field. we can't do anything about
this since we don't know its type */
+ continue;
}
+ field.decision = updates[i].decision;
+ if (updates[i].last_used != (time_t)-1)
+ field.last_used = updates[i].last_used;
+ array_append(&new_fields, &field, 1);
}
if (array_count(&new_fields) > 0) {
mail_cache_register_fields(box->cache,
if (mailbox_open(box) < 0)
return -1;
- if (update->cache_fields != NULL)
- index_storage_mailbox_update_cache_fields(box, update);
+ if (update->cache_updates != NULL)
+ index_storage_mailbox_update_cache(box, update);
/* make sure we get the latest index info */
(void)mail_index_refresh(box->index);
{
T_BEGIN {
struct mailbox_metadata src_metadata;
- const char *const *namep;
+ const struct mailbox_cache_field *field;
buffer_t *buf;
if (mailbox_get_metadata(src_mail->box,
i_unreached();
buf = buffer_create_dynamic(pool_datastack_create(), 1024);
- array_foreach(src_metadata.cache_fields, namep) {
+ array_foreach(src_metadata.cache_fields, field) {
mail_copy_cache_field(ctx, src_mail, dest_seq,
- *namep, buf);
+ field->name, buf);
}
} T_END;
}
void index_storage_mailbox_free(struct mailbox *box);
int index_storage_mailbox_update(struct mailbox *box,
const struct mailbox_update *update);
-void index_storage_mailbox_update_cache_fields(struct mailbox *box,
- const struct mailbox_update *update);
+void index_storage_mailbox_update_cache(struct mailbox *box,
+ const struct mailbox_update *update);
int index_storage_mailbox_delete(struct mailbox *box);
int index_storage_mailbox_delete_dir(struct mailbox *box, bool mailbox_deleted);
int index_storage_mailbox_rename(struct mailbox *src, struct mailbox *dest,
unsigned int allow_new_keywords:1;
};
+struct mailbox_cache_field {
+ const char *name;
+ int decision; /* enum mail_cache_decision_type */
+ /* last_used is unchanged, if it's (time_t)-1 */
+ time_t last_used;
+};
+ARRAY_DEFINE_TYPE(mailbox_cache_field, struct mailbox_cache_field);
+
struct mailbox_metadata {
guid_128_t guid;
/* sum of virtual size of all messages in mailbox */
uint64_t virtual_size;
/* Fields that have "temp" or "yes" caching decision. */
- const ARRAY_TYPE(const_string) *cache_fields;
+ const ARRAY_TYPE(mailbox_cache_field) *cache_fields;
/* Fields that should be precached */
enum mail_fetch_field precache_fields;
};
uint32_t min_next_uid;
uint32_t min_first_recent_uid;
uint64_t min_highest_modseq;
- /* Add these fields to be temporarily cached, if they aren't already. */
- const char *const *cache_fields;
+ /* Modify caching decisions, terminated by name=NULL */
+ const struct mailbox_cache_field *cache_updates;
};
struct mail_transaction_commit_changes {