Permalink
Browse files

conversations: store GUID map items for parts as well

  • Loading branch information...
1 parent ca67f02 commit f81816eaab345e11fc1057cd54f691e38913509d @brong brong committed Nov 16, 2016
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
View
@@ -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.