]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imap: support for FETCH SNIPPET
authorJosef 'Jeff' Sipek <jeff.sipek@dovecot.fi>
Thu, 4 Jan 2018 17:44:16 +0000 (12:44 -0500)
committerJosef 'Jeff' Sipek <jeff.sipek@dovecot.fi>
Wed, 24 Jan 2018 15:09:32 +0000 (10:09 -0500)
configure.ac
src/imap/imap-fetch-body.c
src/imap/imap-fetch.c
src/imap/imap-fetch.h

index 11c4ff145666f5072a02be9452b02a7d7480fdc5..c39e2498cdd12a9ac8724742e100cab8bc378a23 100644 (file)
@@ -2953,7 +2953,7 @@ dnl **
 dnl IDLE doesn't really belong to banner. It's there just to make Blackberries
 dnl happy, because otherwise BIS server disables push email.
 capability_banner="IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE"
-capability="$capability_banner SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE"
+capability="$capability_banner SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY"
 AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", [IMAP capabilities])
 AC_DEFINE_UNQUOTED(CAPABILITY_BANNER_STRING, "$capability_banner", [IMAP capabilities advertised in banner])
 
index b17e24b94bde35ce1c8223496f0b60b9e584a88a..5b8203d8dff74ccfda21cdf0e10f849073bf9066 100644 (file)
@@ -587,3 +587,92 @@ bool imap_fetch_rfc822_init(struct imap_fetch_init_context *ctx)
        ctx->error = t_strconcat("Unknown parameter ", name, NULL);
        return FALSE;
 }
+
+static int ATTR_NULL(3)
+fetch_snippet(struct imap_fetch_context *ctx, struct mail *mail,
+             void *context)
+{
+       const bool lazy = context != NULL;
+       enum mail_lookup_abort temp_lookup_abort = lazy ? MAIL_LOOKUP_ABORT_NOT_IN_CACHE : mail->lookup_abort;
+       enum mail_lookup_abort orig_lookup_abort = mail->lookup_abort;
+       const char *snippet;
+       const char *str;
+       int ret;
+
+       mail->lookup_abort = temp_lookup_abort;
+       ret = mail_get_special(mail, MAIL_FETCH_BODY_SNIPPET, &snippet);
+       mail->lookup_abort = orig_lookup_abort;
+
+       if (ret == 0) {
+               /* got it => nothing to do */
+               snippet++; /* skip over snippet version byte */
+       } else if (mailbox_get_last_mail_error(mail->box) != MAIL_ERROR_LOOKUP_ABORTED) {
+               /* actual error => bail */
+               return -1;
+       } else if (lazy) {
+               /* not in cache && lazy => give up */
+               return 1;
+       } else {
+               /*
+                * not in cache && !lazy => someone higher up set
+                * MAIL_LOOKUP_ABORT_NOT_IN_CACHE and so even though we got
+                * a non-lazy request we failed the cache lookup.
+                *
+                * This is not an error, but since the scenario is
+                * sufficiently convoluted this else branch serves to
+                * document it.
+                */
+               return 1;
+       }
+
+       str = t_strdup_printf(" SNIPPET FUZZY {%"PRIuSIZE_T"}\r\n", strlen(snippet));
+       if (ctx->state.cur_first) {
+               str++;
+               ctx->state.cur_first = FALSE;
+       }
+       o_stream_nsend_str(ctx->client->output, str);
+       o_stream_nsend_str(ctx->client->output, snippet);
+
+       return 1;
+}
+
+bool imap_fetch_snippet_init(struct imap_fetch_init_context *ctx)
+{
+       const struct imap_arg *list_args;
+       unsigned int list_count;
+       bool lazy;
+
+       lazy = FALSE;
+
+       if (imap_arg_get_list_full(&ctx->args[0], &list_args, &list_count)) {
+               unsigned int i;
+
+               for (i = 0; i < list_count; i++) {
+                       const char *str;
+
+                       if (!imap_arg_get_atom(&list_args[i], &str)) {
+                               ctx->error = "Invalid SNIPPET algorithm/modifier";
+                               return FALSE;
+                       }
+
+                       if (strcasecmp(str, "LAZY") == 0 ||
+                           strcasecmp(str, "LAZY=FUZZY") == 0) {
+                               lazy = TRUE;
+                       } else if (strcasecmp(str, "FUZZY") == 0) {
+                               /* nothing to do */
+                       } else {
+                               ctx->error = t_strdup_printf("'%s' is not a "
+                                                            "supported SNIPPET algorithm/modifier",
+                                                            str);
+                               return FALSE;
+                       }
+               }
+
+               ctx->args += list_count;
+       }
+
+       ctx->fetch_ctx->fetch_data |= MAIL_FETCH_BODY_SNIPPET;
+       ctx->fetch_ctx->flags_update_seen = TRUE;
+       imap_fetch_add_handler(ctx, 0, "NIL", fetch_snippet, (void *) lazy);
+       return TRUE;
+}
index 9744f7b70b242dba91c75edf57c7016ac107a5d1..2fda83c44b8592b5f589e2f7a4f41837f2b0065a 100644 (file)
@@ -997,6 +997,7 @@ imap_fetch_default_handlers[] = {
        { "INTERNALDATE", fetch_internaldate_init },
        { "MODSEQ", imap_fetch_modseq_init },
        { "RFC822", imap_fetch_rfc822_init },
+       { "SNIPPET", imap_fetch_snippet_init },
        { "UID", imap_fetch_uid_init },
        { "X-GUID", fetch_guid_init },
        { "X-MAILBOX", fetch_x_mailbox_init },
index 0bd6b3a4ec8f9adf0a3891aca0c7312435d13df4..5d26b28672f6fff81ce2491278e3a18613fb87fc 100644 (file)
@@ -154,6 +154,7 @@ bool imap_fetch_uid_init(struct imap_fetch_init_context *ctx);
 bool imap_fetch_body_section_init(struct imap_fetch_init_context *ctx);
 bool imap_fetch_rfc822_init(struct imap_fetch_init_context *ctx);
 bool imap_fetch_binary_init(struct imap_fetch_init_context *ctx);
+bool imap_fetch_snippet_init(struct imap_fetch_init_context *ctx);
 
 void imap_fetch_handlers_init(void);
 void imap_fetch_handlers_deinit(void);