Skip to content
Permalink
Browse files

conversations: store GUID map items for parts as well

  • Loading branch information
brong committed Nov 16, 2016
1 parent ca67f02 commit f81816eaab345e11fc1057cd54f691e38913509d
Showing with 118 additions and 15 deletions.
  1. +113 −14 imap/conversations.c
  2. +2 −1 imap/conversations.h
  3. +3 −0 imap/search_xapian.c
@@ -1515,7 +1515,20 @@ EXPORTED int conversations_guid_foreach(struct conversations_state *state,
}
rec.uid = res;

rec.part = NULL;
p = strchr(p + 1, '[');
char *freeme = NULL;
if (p) {
const char *end = strchr(p+1, ']');
if (!end) {
r = IMAP_INTERNAL;
break;
}
rec.part = freeme = xstrndup(p+1, end-p-1);
}

r = cb(&rec, rock);
free(freeme);
if (r) break;
}

@@ -1524,48 +1537,134 @@ EXPORTED int conversations_guid_foreach(struct conversations_state *state,
return r;
}

static int conversations_set_guid(struct conversations_state *state,
struct mailbox *mailbox,
const struct index_record *record,
int add)
static int conversations_guid_setitem(struct conversations_state *state, const char *guidrep,
const char *item, int add)
{
int folder = folder_number(state, mailbox->name, /*create*/1);
struct buf item = BUF_INITIALIZER;
char *key = strconcat("G", message_guid_encode(&record->guid), (char *)NULL);
char *key = strconcat("G", guidrep, (char *)NULL);
size_t datalen = 0;
const char *data;
strarray_t *old = NULL;

int r = cyrusdb_fetch(state->db, key, strlen(key), &data, &datalen, &state->txn);
if (!r) old = strarray_nsplit(data, datalen, ",", /*flags*/0);
if (!r && datalen) old = strarray_nsplit(data, datalen, ",", /*flags*/0);
if (!old) old = strarray_new();

buf_printf(&item, "%d:%u", folder, record->uid);

if (add) {
strarray_add(old, buf_cstring(&item));
/* XXX - could do with an API like:
* strarray_add_sorted(old, item, cmpstringp_raw) */
strarray_add(old, item);
strarray_sort(old, cmpstringp_raw);
}
else {
strarray_remove_all(old, buf_cstring(&item));
strarray_remove_all(old, item);
/* stays sorted :) */
}

char *new = strarray_join(old, ",");

if (new)
if (new && strlen(new))
r = cyrusdb_store(state->db, key, strlen(key), new, strlen(new), &state->txn);
else
r = cyrusdb_delete(state->db, key, strlen(key), &state->txn, /*force*/1);

buf_free(&item);
strarray_free(old);
free(new);
free(key);

return r;
}

static int body_is_rfc822(struct body *body)
{
return body &&
!strcasecmpsafe(body->type, "MESSAGE") &&
!strcasecmpsafe(body->subtype, "RFC822");
}

/* interface message_read_bodystructure which makes sure the cache record
* exists and adds the MESSAGE/RFC822 wrapper to make fetch BODY[*]
* work consistently */
static void loadbody(struct mailbox *mailbox, const struct index_record *record,
struct body **bodyp)
{
if (*bodyp) return;
if (mailbox_cacherecord(mailbox, record)) return;
struct body *body = xzmalloc(sizeof(struct body));
message_read_bodystructure(record, &body->subpart);
body->type = xstrdup("MESSAGE");
body->subtype = xstrdup("RFC822");
body->header_offset = 0;
body->header_size = 0;
body->content_offset = 0;
body->content_offset = record->size;
body->content_guid = record->guid;
*bodyp = body;
}

static int _guid_addbody(struct conversations_state *state, struct body *body,
const char *base, const char *part, int add)
{
struct buf buf = BUF_INITIALIZER;
int r = 0;
if (!body) return 0;

if (!message_guid_isnull(&body->content_guid)) {
buf_setcstr(&buf, base);
if (part) buf_printf(&buf, "[%s]", part);
const char *guidrep = message_guid_encode(&body->content_guid);
r = conversations_guid_setitem(state, guidrep, buf_cstring(&buf), add);
if (r) goto done;
}

if (body_is_rfc822(body))
body = body->subpart;
else if (!body->numparts)
goto done;

if (body->numparts) {
int i;
for (i = 0; i < body->numparts; i++) {
buf_reset(&buf);
if (part) buf_printf(&buf, "%s.", part);
buf_printf(&buf, "%d", i+1);
_guid_addbody(state, &body->subpart[i], base, buf_cstring(&buf), add);
}
}
else {
buf_reset(&buf);
if (part) buf_printf(&buf, "%s.", part);
buf_printf(&buf, "%d", 1);
_guid_addbody(state, body, base, buf_cstring(&buf), add);
}

done:
buf_free(&buf);
return r;
}

static int conversations_set_guid(struct conversations_state *state,
struct mailbox *mailbox,
const struct index_record *record,
int add)
{
int folder = folder_number(state, mailbox->name, /*create*/1);
struct buf item = BUF_INITIALIZER;
struct body *body = NULL;
int r = 0;

/* process the GUID of the full message itself */
buf_printf(&item, "%d:%u", folder, record->uid);
const char *base = buf_cstring(&item);

loadbody(mailbox, record, &body);

r = _guid_addbody(state, body, base, NULL, add);

message_free_body(body);
buf_free(&item);
return r;
}

EXPORTED int conversations_update_record(struct conversations_state *cstate,
struct mailbox *mailbox,
const struct index_record *old,
@@ -97,8 +97,9 @@ struct conv_folder {
};

struct conv_guidrec {
const char* mboxname;
const char *mboxname;
uint32_t uid;
const char *part;
};

struct conv_sender {
@@ -890,6 +890,9 @@ static int xapian_run_guid_cb(const conv_guidrec_t *rec, void *rock)
{
xapian_builder_t *bb = (xapian_builder_t *)rock;

/* we only want full message matches here */
if (rec->part) return 0;

if (!(bb->opts & SEARCH_MULTIPLE)) {
if (strcmp(rec->mboxname, bb->mailbox->name))
return 0;

0 comments on commit f81816e

Please sign in to comment.
You can’t perform that action at this time.