]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
fts: Added support for handling multiple namespaces.
authorTimo Sirainen <tss@iki.fi>
Sat, 29 Nov 2008 23:27:19 +0000 (01:27 +0200)
committerTimo Sirainen <tss@iki.fi>
Sat, 29 Nov 2008 23:27:19 +0000 (01:27 +0200)
--HG--
branch : HEAD

13 files changed:
doc/solr-schema.xml
src/lib-storage/mail-storage-private.h
src/lib-storage/mail-storage.c
src/lib-storage/mail-storage.h
src/plugins/fts-solr/fts-backend-solr.c
src/plugins/fts-solr/fts-solr-plugin.c
src/plugins/fts-solr/fts-solr-plugin.h
src/plugins/fts-solr/solr-connection.c
src/plugins/fts-solr/solr-connection.h
src/plugins/fts/fts-storage.c
src/plugins/virtual/virtual-config.c
src/plugins/virtual/virtual-storage.c
src/plugins/virtual/virtual-storage.h

index 0bf21965837b65f7374a51a8bbcb77ae85b735b1..a3ccaec3bcfe9d9669b260cf2237fa9d6dbe3722 100644 (file)
@@ -43,6 +43,7 @@ want to modify the tokenizers and filters.
    <field name="uidv" type="long" indexed="true" stored="true" required="true" /> 
    <field name="box" type="string" indexed="true" stored="true" required="true" /> 
    <field name="user" type="string" indexed="true" stored="true" required="true" /> 
+   <field name="ns" type="string" indexed="true" stored="true" required="false" /> 
    <field name="last_uid" type="boolean" indexed="true" stored="false" /> 
    <field name="hdr" type="text" indexed="true" stored="false" /> 
    <field name="body" type="text" indexed="true" stored="false" /> 
index 7d0f0d24d6a7a3701cab01bb046c2cb44cbe9e85..4af0f9024fcdb1bc4190027dfd4a49329a8f4d8b 100644 (file)
@@ -146,8 +146,8 @@ struct mailbox_vfuncs {
                                          ARRAY_TYPE(mailboxes) *mailboxes,
                                          bool only_with_msgs);
        void (*get_virtual_box_patterns)(struct mailbox *box,
-                                        ARRAY_TYPE(const_string) *includes,
-                                        ARRAY_TYPE(const_string) *excludes);
+                               ARRAY_TYPE(mailbox_virtual_patterns) *includes,
+                               ARRAY_TYPE(mailbox_virtual_patterns) *excludes);
 
        struct mail *
                (*mail_alloc)(struct mailbox_transaction_context *t,
index 9fe26e6bc49e4500027eb3d10bc1c3e17998e4e8..6dbff9c1e6643a5704a9b1d98a71d025a9fc9f65 100644 (file)
@@ -651,13 +651,16 @@ void mailbox_get_virtual_backend_boxes(struct mailbox *box,
 }
 
 void mailbox_get_virtual_box_patterns(struct mailbox *box,
-                                     ARRAY_TYPE(const_string) *includes,
-                                     ARRAY_TYPE(const_string) *excludes)
+                               ARRAY_TYPE(mailbox_virtual_patterns) *includes,
+                               ARRAY_TYPE(mailbox_virtual_patterns) *excludes)
 {
        if (box->v.get_virtual_box_patterns == NULL) {
-               const char *name = box->name;
+               struct mailbox_virtual_pattern pat;
 
-               array_append(includes, &name, 1);
+               memset(&pat, 0, sizeof(pat));
+               pat.ns = box->storage->ns;
+               pat.pattern = box->name;
+               array_append(includes, &pat, 1);
        } else {
                box->v.get_virtual_box_patterns(box, includes, excludes);
        }
index 1b9f67010438410e684be168e6e49ef413b2d8d3..ac210cc41383e0fce1a0ac234ec6959e20954885 100644 (file)
@@ -234,6 +234,12 @@ struct mail_storage_callbacks {
 
 };
 
+struct mailbox_virtual_pattern {
+       struct mail_namespace *ns;
+       const char *pattern;
+};
+ARRAY_DEFINE_TYPE(mailbox_virtual_patterns, struct mailbox_virtual_pattern);
+
 ARRAY_DEFINE_TYPE(mailboxes, struct mailbox *);
 
 typedef void mailbox_notify_callback_t(struct mailbox *box, void *context);
@@ -432,8 +438,8 @@ void mailbox_get_virtual_backend_boxes(struct mailbox *box,
 /* If mailbox is a virtual mailbox, return all mailbox list patterns that
    are used to figure out which mailboxes belong to the virtual mailbox. */
 void mailbox_get_virtual_box_patterns(struct mailbox *box,
-                                     ARRAY_TYPE(const_string) *includes,
-                                     ARRAY_TYPE(const_string) *excludes);
+                               ARRAY_TYPE(mailbox_virtual_patterns) *includes,
+                               ARRAY_TYPE(mailbox_virtual_patterns) *excludes);
 
 /* Initialize header lookup for given headers. */
 struct mailbox_header_lookup_ctx *
index 33f27393ddc4b7d9d94e6fe2f6d151cc0cf78e68..eed251ece671ad3ed088de81e131b6920e060105 100644 (file)
 
 #define SOLR_CMDBUF_SIZE (1024*64)
 #define SOLR_MAX_ROWS 100000
+#define FTS_SOLR_MAX_BOX_INC_PATTERNS 5
+#define FTS_SOLR_MAX_BOX_EXC_PATTERNS 5
 
 struct solr_fts_backend {
        struct fts_backend backend;
-       char *id_username;
+       char *id_username, *id_namespace;
+       struct mail_namespace *default_ns;
 };
 
 struct solr_fts_backend_build_context {
@@ -28,18 +31,23 @@ struct solr_fts_backend_build_context {
        bool headers;
 };
 
+struct solr_virtual_uid_map_context {
+       struct fts_backend *backend;
+       struct mailbox *box;
+       string_t *vname;
+};
+
 struct fts_backend_solr_get_last_uids_context {
+       struct fts_backend *backend;
        pool_t pool;
        ARRAY_TYPE(fts_backend_uid_map) *last_uids;
+
+       struct mailbox *box;
+       string_t *vname;
 };
 
 static struct solr_connection *solr_conn = NULL;
 
-static void solr_quote_str(string_t *dest, const char *str)
-{
-       solr_connection_quote_str(solr_conn, dest, str);
-}
-
 static void
 xml_encode_data(string_t *dest, const unsigned char *data, unsigned int len)
 {
@@ -97,18 +105,50 @@ static const char *solr_escape_id_str(const char *str)
        return str_c(tmp);
 }
 
+static void solr_quote(string_t *dest, const char *str)
+{
+       str_append_c(dest, '"');
+       str_append(dest, str_escape(str));
+       str_append_c(dest, '"');
+}
+
+static void solr_quote_http(string_t *dest, const char *str)
+{
+       str_append(dest, "%22");
+       solr_connection_http_escape(solr_conn, dest, str);
+       str_append(dest, "%22");
+}
+
 static struct fts_backend *
 fts_backend_solr_init(struct mailbox *box)
 {
        const struct fts_solr_settings *set = &fts_solr_settings;
        struct solr_fts_backend *backend;
-       const char *username = box->storage->ns->user->username;
+       struct mail_namespace *ns = box->storage->ns;
+       const char *str;
 
        if (solr_conn == NULL)
                solr_conn = solr_connection_init(set->url, set->debug);
 
        backend = i_new(struct solr_fts_backend, 1);
-       backend->id_username = i_strdup(solr_escape_id_str(username));
+       str = fts_solr_settings.default_ns_prefix;
+       if (str != NULL) {
+               backend->default_ns =
+                       mail_namespace_find_prefix(ns->user->namespaces, str);
+               if (backend->default_ns == NULL) {
+                       i_fatal("fts_solr: default_ns setting points to "
+                               "nonexisting namespace");
+               }
+       } else {
+               backend->default_ns =
+                       mail_namespace_find_inbox(ns->user->namespaces);
+       }
+       str = solr_escape_id_str(ns->user->username);
+       backend->id_username = i_strdup(str);
+       if (box->storage->ns != backend->default_ns) {
+               str = solr_escape_id_str(ns->prefix);
+               backend->id_namespace = i_strdup(str);
+       }
        backend->backend = fts_backend_solr;
 
        if (set->substring_search)
@@ -120,20 +160,40 @@ static void fts_backend_solr_deinit(struct fts_backend *_backend)
 {
        struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend;
 
+       i_free(backend->id_namespace);
        i_free(backend->id_username);
        i_free(backend);
 }
 
-static const char *fts_backend_solr_username(struct fts_backend *_backend)
+static void
+solr_add_ns_query(string_t *str, struct fts_backend *_backend,
+                 struct mail_namespace *ns)
 {
        struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend;
 
-       return backend->id_username;
+       if (ns == backend->default_ns || *ns->prefix == '\0')
+               str_append(str, " -ns:[* TO *]");
+       else {
+               str_append(str, " ns:");
+               solr_quote(str, ns->prefix);
+       }
+}
+
+static void
+solr_add_ns_query_http(string_t *str, struct fts_backend *backend,
+                      struct mail_namespace *ns)
+{
+       string_t *tmp;
+
+       tmp = t_str_new(64);
+       solr_add_ns_query(tmp, backend, ns);
+       solr_connection_http_escape(solr_conn, str, str_c(tmp));
 }
 
 static int fts_backend_solr_get_last_uid_fallback(struct fts_backend *backend,
                                                  uint32_t *last_uid_r)
 {
+       struct mailbox *box = backend->box;
        struct mailbox_status status;
        ARRAY_TYPE(seq_range) uids;
        const struct seq_range *uidvals;
@@ -141,13 +201,14 @@ static int fts_backend_solr_get_last_uid_fallback(struct fts_backend *backend,
        string_t *str;
 
        str = t_str_new(256);
-       str_append(str, "fl=uid&rows=1&sort=uid%20desc&q=");
+       str_append(str, "fl=uid&rows=1&sort=uid+desc&q=");
 
-       mailbox_get_status(backend->box, STATUS_UIDVALIDITY, &status);
-       str_printfa(str, "uidv:%u%%20box:", status.uidvalidity);
-       solr_quote_str(str, backend->box->name);
-       str_append(str, "%20user:");
-       solr_quote_str(str, backend->box->storage->ns->user->username);
+       mailbox_get_status(box, STATUS_UIDVALIDITY, &status);
+       str_printfa(str, "uidv:%u+box:", status.uidvalidity);
+       solr_quote_http(str, box->name);
+       solr_add_ns_query_http(str, backend, box->storage->ns);
+       str_append(str, "+user:");
+       solr_quote_http(str, box->storage->ns->user->username);
 
        t_array_init(&uids, 1);
        if (solr_connection_select(solr_conn, str_c(str),
@@ -170,6 +231,7 @@ static int fts_backend_solr_get_last_uid_fallback(struct fts_backend *backend,
 static int fts_backend_solr_get_last_uid(struct fts_backend *backend,
                                         uint32_t *last_uid_r)
 {
+       struct mailbox *box = backend->box;
        struct mailbox_status status;
        ARRAY_TYPE(seq_range) uids;
        const struct seq_range *uidvals;
@@ -177,13 +239,14 @@ static int fts_backend_solr_get_last_uid(struct fts_backend *backend,
        string_t *str;
 
        str = t_str_new(256);
-       str_append(str, "fl=uid&rows=1&q=last_uid:TRUE%20");
+       str_append(str, "fl=uid&rows=1&q=last_uid:TRUE+");
 
-       mailbox_get_status(backend->box, STATUS_UIDVALIDITY, &status);
-       str_printfa(str, "uidv:%u%%20box:", status.uidvalidity);
-       solr_quote_str(str, backend->box->name);
-       str_append(str, "%20user:");
-       solr_quote_str(str, backend->box->storage->ns->user->username);
+       mailbox_get_status(box, STATUS_UIDVALIDITY, &status);
+       str_printfa(str, "uidv:%u+box:", status.uidvalidity);
+       solr_quote_http(str, box->name);
+       solr_add_ns_query_http(str, backend, box->storage->ns);
+       str_append(str, "+user:");
+       solr_quote_http(str, box->storage->ns->user->username);
 
        t_array_init(&uids, 1);
        if (solr_connection_select(solr_conn, str_c(str),
@@ -205,41 +268,67 @@ static int fts_backend_solr_get_last_uid(struct fts_backend *backend,
        return 0;
 }
 
+static const char *
+solr_get_vmailbox(struct fts_backend *_backend,
+                 struct mailbox *box, const char *ns_prefix,
+                 const char *mailbox, string_t *dest)
+{
+       struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend;
+       struct mail_namespace *namespaces = box->storage->ns->user->namespaces;
+       struct mail_namespace *ns;
+
+       if (ns_prefix == NULL)
+               ns = backend->default_ns;
+       else {
+               ns = mail_namespace_find_prefix(namespaces, ns_prefix);
+               if (ns == NULL)
+                       return FALSE;
+       }
+       return mail_namespace_get_vname(ns, dest, mailbox);
+}
+
 static bool
-solr_virtual_get_last_uids(const char *mailbox, uint32_t uidvalidity,
-                          uint32_t *uid, void *context)
+solr_virtual_get_last_uids(const char *ns_prefix, const char *mailbox,
+                          uint32_t uidvalidity, uint32_t *uid, void *context)
 {
        struct fts_backend_solr_get_last_uids_context *ctx = context;
        struct fts_backend_uid_map *map;
+       const char *vname;
+
+       vname = solr_get_vmailbox(ctx->backend, ctx->box, ns_prefix,
+                                 mailbox, ctx->vname);
 
        map = array_append_space(ctx->last_uids);
-       map->mailbox = p_strdup(ctx->pool, mailbox);
+       map->mailbox = p_strdup(ctx->pool, vname);
        map->uidvalidity = uidvalidity;
        map->uid = *uid;
        return FALSE;
 }
 
-static void add_pattern_as_solr(string_t *str, const char *pattern)
+static void
+solr_add_pattern(string_t *str, const struct mailbox_virtual_pattern *pattern)
 {
-       const char *p;
+       const char *name, *p;
+
+       name = pattern->pattern;
+       if (!mail_namespace_update_name(pattern->ns, &name))
+               name = mail_namespace_fix_sep(pattern->ns, name);
 
        /* first check if there are any wildcards in the pattern */
-       for (p = pattern; *p != '\0'; p++) {
+       for (p = name; *p != '\0'; p++) {
                if (*p == '%' || *p == '*')
                        break;
        }
        if (*p == '\0') {
                /* full mailbox name */
-               str_append_c(str, '"');
-               str_append(str, str_escape(pattern));
-               str_append_c(str, '"');
+               solr_quote(str, name);
                return;
        }
 
        /* there are at least some wildcards. */
-       for (p = pattern; *p != '\0'; p++) {
+       for (p = name; *p != '\0'; p++) {
                if (*p == '%' || *p == '*') {
-                       if (p == pattern || (p[-1] != '%' && p[-1] != '*'))
+                       if (p == name || (p[-1] != '%' && p[-1] != '*'))
                                str_append_c(str, '*');
                } else {
                        if (!i_isalnum(*p))
@@ -250,12 +339,13 @@ static void add_pattern_as_solr(string_t *str, const char *pattern)
 }
 
 static void
-fts_backend_solr_filter_mailboxes(struct solr_connection *solr_conn,
+fts_backend_solr_filter_mailboxes(struct fts_backend *_backend,
                                  string_t *str, struct mailbox *box)
 {
-       ARRAY_TYPE(const_string) includes_arr, excludes_arr;
-       const char *const *includes, *const *excludes;
-       unsigned int i, inc_count, exc_count;
+       struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend;
+       ARRAY_TYPE(mailbox_virtual_patterns) includes_arr, excludes_arr;
+       const struct mailbox_virtual_pattern *includes, *excludes;
+       unsigned int i, inc_count, exc_count, len;
        string_t *fq;
 
        t_array_init(&includes_arr, 16);
@@ -269,27 +359,41 @@ fts_backend_solr_filter_mailboxes(struct solr_connection *solr_conn,
           Solr doesn't allow them, so in that case we'll need to return
           all mailboxes. */
        for (i = 0; i < inc_count; i++) {
-               if (*includes[i] == '*' || *includes[i] == '%')
+               if (*includes[i].pattern == '*' ||
+                   *includes[i].pattern == '%')
                        break;
        }
 
        fq = t_str_new(128);
-       if (i == inc_count) {
+       if (i == inc_count && inc_count <= FTS_SOLR_MAX_BOX_INC_PATTERNS) {
                /* we can filter what mailboxes we want returned */
                str_append_c(fq, '(');
                for (i = 0; i < inc_count; i++) {
                        if (i != 0)
                                str_append(fq, " OR ");
+                       str_append_c(fq, '(');
                        str_append(fq, "box:");
-                       add_pattern_as_solr(fq, includes[i]);
+                       solr_add_pattern(fq, &includes[i]);
+                       solr_add_ns_query(fq, _backend, includes[i].ns);
+                       str_append_c(fq, ')');
                }
                str_append_c(fq, ')');
        }
+       exc_count = I_MIN(FTS_SOLR_MAX_BOX_EXC_PATTERNS, exc_count);
        for (i = 0; i < exc_count; i++) {
-               if (str_len(fq) > 0)
+               if (str_len(fq) > len)
                        str_append_c(fq, ' ');
+               str_append_c(fq, '(');
                str_append(fq, "-box:");
-               add_pattern_as_solr(fq, excludes[i]);
+               solr_add_pattern(fq, &excludes[i]);
+               if (excludes[i].ns == backend->default_ns) {
+                       str_append(fq, " OR NOT");
+                       solr_add_ns_query(fq, _backend, excludes[i].ns);
+               } else if (*excludes[i].ns->prefix != '\0') {
+                       str_append(fq, " OR -ns:");
+                       solr_quote(fq, excludes[i].ns->prefix);
+               }
+               str_append_c(fq, ')');
        }
        if (str_len(fq) > 0) {
                str_append(str, "&fq=");
@@ -305,14 +409,17 @@ fts_backend_solr_get_all_last_uids(struct fts_backend *backend, pool_t pool,
        string_t *str;
 
        memset(&ctx, 0, sizeof(ctx));
+       ctx.backend = backend;
        ctx.pool = pool;
        ctx.last_uids = last_uids;
+       ctx.box = backend->box;
+       ctx.vname = t_str_new(256);
 
        str = t_str_new(256);
-       str_printfa(str, "fl=uid,box,uidv&rows=%u&q=last_uid:TRUE%%20user:",
+       str_printfa(str, "fl=uid,box,uidv,ns&rows=%u&q=last_uid:TRUE+user:",
                    SOLR_MAX_ROWS);
-       solr_quote_str(str, backend->box->storage->ns->user->username);
-       fts_backend_solr_filter_mailboxes(solr_conn, str, backend->box);
+       solr_quote_http(str, backend->box->storage->ns->user->username);
+       fts_backend_solr_filter_mailboxes(backend, str, backend->box);
 
        return solr_connection_select(solr_conn, str_c(str),
                                      solr_virtual_get_last_uids, &ctx,
@@ -343,6 +450,8 @@ static void
 fts_backend_solr_add_doc_prefix(struct solr_fts_backend_build_context *ctx,
                                uint32_t uid)
 {
+       struct solr_fts_backend *backend =
+               (struct solr_fts_backend *)ctx->ctx.backend;
        struct mailbox *box = ctx->ctx.backend->box;
 
        str_printfa(ctx->cmd, "<doc>"
@@ -350,6 +459,11 @@ fts_backend_solr_add_doc_prefix(struct solr_fts_backend_build_context *ctx,
                    "<field name=\"uidv\">%u</field>",
                    uid, ctx->uid_validity);
 
+       if (box->storage->ns != backend->default_ns) {
+               str_append(ctx->cmd, "<field name=\"ns\">");
+               xml_encode(ctx->cmd, box->storage->ns->prefix);
+               str_append(ctx->cmd, "</field>");
+       }
        str_append(ctx->cmd, "<field name=\"box\">");
        xml_encode(ctx->cmd, box->name);
        str_append(ctx->cmd, "</field><field name=\"user\">");
@@ -357,6 +471,26 @@ fts_backend_solr_add_doc_prefix(struct solr_fts_backend_build_context *ctx,
        str_append(ctx->cmd, "</field>");
 }
 
+static void xml_encode_id(string_t *str, struct fts_backend *_backend,
+                         uint32_t uid, uint32_t uid_validity,
+                         const char *mailbox)
+{
+       struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend;
+
+       if (uid != 0)
+               str_printfa(str, "%u/", uid);
+       else
+               str_append(str, "L/");
+       if (backend->id_namespace != NULL) {
+               xml_encode(str, backend->id_namespace);
+               str_append_c(str, '/');
+       }
+       str_printfa(str, "%u/", uid_validity);
+       xml_encode(str, backend->id_username);
+       str_append_c(str, '/');
+       xml_encode(str, mailbox);
+}
+
 static int
 fts_backend_solr_build_more(struct fts_backend_build_context *_ctx,
                            uint32_t uid, const unsigned char *data,
@@ -379,11 +513,9 @@ fts_backend_solr_build_more(struct fts_backend_build_context *_ctx,
                ctx->prev_uid = uid;
 
                fts_backend_solr_add_doc_prefix(ctx, uid);
-               str_printfa(cmd, "<field name=\"id\">%u/%u/",
-                           uid, ctx->uid_validity);
-               xml_encode(cmd, fts_backend_solr_username(ctx->ctx.backend));
-               str_append_c(cmd, '/');
-               xml_encode(cmd, box->name);
+               str_printfa(cmd, "<field name=\"id\">");
+               xml_encode_id(cmd, _ctx->backend, uid, ctx->uid_validity,
+                             box->name);
                str_append(cmd, "</field>");
 
                ctx->headers = headers;
@@ -425,10 +557,9 @@ fts_backed_solr_build_commit(struct solr_fts_backend_build_context *ctx)
           in future by reindexing some messages. */
        fts_backend_solr_add_doc_prefix(ctx, ctx->prev_uid);
        str_printfa(ctx->cmd, "<field name=\"last_uid\">TRUE</field>"
-                   "<field name=\"id\">L/%u/", ctx->uid_validity);
-       xml_encode(ctx->cmd, fts_backend_solr_username(ctx->ctx.backend));
-       str_append_c(ctx->cmd, '/');
-       xml_encode(ctx->cmd, box->name);
+                   "<field name=\"id\">");
+       xml_encode_id(ctx->cmd, ctx->ctx.backend, 0, ctx->uid_validity,
+                     box->name);
        str_append(ctx->cmd, "</field></doc></add>");
 
        solr_connection_post_more(ctx->post, str_data(ctx->cmd),
@@ -466,11 +597,9 @@ fts_backend_solr_expunge(struct fts_backend *backend, struct mail *mail)
                string_t *cmd;
 
                cmd = t_str_new(256);
-               str_printfa(cmd, "<delete><id>%u/%u/",
-                           mail->uid, status.uidvalidity);
-               xml_encode(cmd, fts_backend_solr_username(backend));
-               str_append_c(cmd, '/');
-               xml_encode(cmd, mail->box->name);
+               str_append(cmd, "<delete><id>");
+               xml_encode_id(cmd, backend, mail->uid, status.uidvalidity,
+                             mail->box->name);
                str_append(cmd, "</id></delete>");
 
                (void)solr_connection_post(solr_conn, str_c(cmd));
@@ -495,13 +624,16 @@ static void fts_backend_solr_unlock(struct fts_backend *backend ATTR_UNUSED)
 {
 }
 
-static bool solr_virtual_uid_map(const char *mailbox, uint32_t uidvalidity,
-                                uint32_t *uid, void *context)
+static bool solr_virtual_uid_map(const char *ns_prefix, const char *mailbox,
+                                uint32_t uidvalidity, uint32_t *uid,
+                                void *context)
 {
-       struct mailbox *box = context;
+       struct solr_virtual_uid_map_context *ctx = context;
+       const char *vname;
 
-       return mailbox_get_virtual_uid(box, mailbox, uidvalidity,
-                                      *uid, uid);
+       vname = solr_get_vmailbox(ctx->backend, ctx->box, ns_prefix,
+                                 mailbox, ctx->vname);
+       return mailbox_get_virtual_uid(ctx->box, vname, uidvalidity, *uid, uid);
 }
 
 static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx,
@@ -510,6 +642,7 @@ static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx,
                                   ARRAY_TYPE(fts_score_map) *scores)
 {
        struct mailbox *box = ctx->backend->box;
+       struct solr_virtual_uid_map_context uid_map_ctx;
        const struct fts_backend_lookup_field *fields;
        unsigned int i, count;
        struct mailbox_status status;
@@ -521,11 +654,11 @@ static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx,
 
        str = t_str_new(256);
        if (!virtual) {
-               str_printfa(str, "fl=uid,score&rows=%u&sort=uid%%20asc&q=",
+               str_printfa(str, "fl=uid,score&rows=%u&sort=uid+asc&q=",
                            status.uidnext);
        } else {
-               str_printfa(str, "fl=uid,score,box,uidv&rows=%u"
-                           "&sort=box%%20asc,uid%%20asc&q=",
+               str_printfa(str, "fl=uid,score,box,uidv,ns&rows=%u"
+                           "&sort=box+asc,uid+asc&q=",
                            SOLR_MAX_ROWS);
        }
 
@@ -533,7 +666,7 @@ static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx,
        fields = array_get(&ctx->fields, &count);
        for (i = 0; i < count; i++) {
                if (i > 0)
-                       str_append(str, "%20");
+                       str_append_c(str, '+');
 
                if ((fields[i].flags & FTS_LOOKUP_FLAG_INVERT) != 0)
                        str_append_c(str, '-');
@@ -549,18 +682,19 @@ static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx,
                        /* both */
                        str_append(str, "any:");
                }
-               solr_quote_str(str, fields[i].key);
+               solr_quote_http(str, fields[i].key);
        }
 
        /* use a separate filter query for selecting the mailbox. it shouldn't
           affect the score and there could be some caching benefits too. */
        str_append(str, "&fq=user:");
-       solr_quote_str(str, box->storage->ns->user->username);
+       solr_quote_http(str, box->storage->ns->user->username);
        if (virtual)
-               fts_backend_solr_filter_mailboxes(solr_conn, str, box);
+               fts_backend_solr_filter_mailboxes(ctx->backend, str, box);
        else {
-               str_printfa(str, "%%20uidv:%u%%20box:", status.uidvalidity);
-               solr_quote_str(str, box->name);
+               str_printfa(str, "+uidv:%u+box:", status.uidvalidity);
+               solr_quote_http(str, box->name);
+               solr_add_ns_query_http(str, ctx->backend, box->storage->ns);
        }
 
        array_clear(maybe_uids);
@@ -568,8 +702,13 @@ static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx,
                return solr_connection_select(solr_conn, str_c(str), NULL, NULL,
                                              definite_uids, scores);
        } else {
+               memset(&uid_map_ctx, 0, sizeof(uid_map_ctx));
+               uid_map_ctx.backend = ctx->backend;
+               uid_map_ctx.box = box;
+               uid_map_ctx.vname = t_str_new(256);
                return solr_connection_select(solr_conn, str_c(str),
-                                             solr_virtual_uid_map, box,
+                                             solr_virtual_uid_map,
+                                             &uid_map_ctx,
                                              definite_uids, scores);
        }
 }
index d0d2409c28992ed2e8fdea1a18382b8c169e428d..f09b396200aa7c8b2bd206417281b346f5b9e874 100644 (file)
@@ -24,6 +24,9 @@ static void fts_solr_plugin_init_settings(const char *str)
                        set->debug = TRUE;
                } else if (strcmp(*tmp, "break-imap-search") == 0) {
                        set->substring_search = TRUE;
+               } else if (strcmp(*tmp, "default_ns=") == 0) {
+                       i_free(set->default_ns_prefix);
+                       set->default_ns_prefix = i_strdup(*tmp + 11);
                } else {
                        i_fatal("fts_solr: Invalid setting: %s", *tmp);
                }
index ee8c1850a937fe9fb2a8905bff3e7b44816cede8..2721d6c5b1f7d70b59790e39d961f5cbc28ed334 100644 (file)
@@ -4,7 +4,7 @@
 #include "fts-api-private.h"
 
 struct fts_solr_settings {
-       char *url;
+       char *url, *default_ns_prefix;
        bool debug;
        bool substring_search;
 };
index 7ea856ede74d2a6a61722d289ba6369e32273970..29ceef0d8fb57ecfae4bb7862b99fabb6f72d8d3 100644 (file)
@@ -24,6 +24,7 @@ enum solr_xml_content_state {
        SOLR_XML_CONTENT_STATE_UID,
        SOLR_XML_CONTENT_STATE_SCORE,
        SOLR_XML_CONTENT_STATE_MAILBOX,
+       SOLR_XML_CONTENT_STATE_NAMESPACE,
        SOLR_XML_CONTENT_STATE_UIDVALIDITY
 };
 
@@ -34,7 +35,7 @@ struct solr_lookup_xml_context {
 
        uint32_t uid, uidvalidity;
        float score;
-       char *mailbox;
+       char *mailbox, *ns;
 
        solr_uid_map_callback_t *callback;
        void *context;
@@ -192,16 +193,6 @@ void solr_connection_deinit(struct solr_connection *conn)
        i_free(conn);
 }
 
-void solr_connection_quote_str(struct solr_connection *conn, string_t *dest,
-                              const char *str)
-{
-       char *encoded;
-
-       encoded = curl_easy_escape(conn->curl, str_escape(str), 0);
-       str_printfa(dest, "%%22%s%%22", encoded);
-       curl_free(encoded);
-}
-
 void solr_connection_http_escape(struct solr_connection *conn, string_t *dest,
                                 const char *str)
 {
@@ -251,6 +242,7 @@ solr_lookup_xml_start(void *context, const char *name, const char **attrs)
                        ctx->uid = 0;
                        ctx->score = 0;
                        i_free_and_null(ctx->mailbox);
+                       i_free_and_null(ctx->ns);
                        ctx->uidvalidity = 0;
                }
                break;
@@ -262,6 +254,8 @@ solr_lookup_xml_start(void *context, const char *name, const char **attrs)
                        ctx->content_state = SOLR_XML_CONTENT_STATE_SCORE;
                else if (strcmp(name_attr, "box") == 0)
                        ctx->content_state = SOLR_XML_CONTENT_STATE_MAILBOX;
+               else if (strcmp(name_attr, "ns") == 0)
+                       ctx->content_state = SOLR_XML_CONTENT_STATE_NAMESPACE;
                else if (strcmp(name_attr, "uidv") == 0)
                        ctx->content_state = SOLR_XML_CONTENT_STATE_UIDVALIDITY;
                else 
@@ -287,7 +281,7 @@ static void solr_lookup_add_doc(struct solr_lookup_xml_context *ctx)
                        i_error("fts_solr: Query didn't return mailbox");
                        return;
                }
-               if (!ctx->callback(ctx->mailbox, ctx->uidvalidity,
+               if (!ctx->callback(ctx->ns, ctx->mailbox, ctx->uidvalidity,
                                   &ctx->uid, ctx->context))
                        return;
        }
@@ -357,6 +351,12 @@ static void solr_lookup_xml_data(void *context, const char *str, int len)
                i_free(ctx->mailbox);
                ctx->mailbox = new_name;
                break;
+       case SOLR_XML_CONTENT_STATE_NAMESPACE:
+               new_name = ctx->ns == NULL ? i_strndup(str, len) :
+                       i_strconcat(ctx->ns, t_strndup(str, len), NULL);
+               i_free(ctx->ns);
+               ctx->ns = new_name;
+               break;
        case SOLR_XML_CONTENT_STATE_UIDVALIDITY:
                if (uint32_parse(str, len, &ctx->uidvalidity) < 0)
                        i_error("fts_solr: received invalid uidvalidity");
index 7d0220b2535bb513db98917920930c253ba6eb6c..2a05c1eb71dd30b1dbc7d53bccb921aa3f8110ff 100644 (file)
@@ -5,14 +5,13 @@
 #include "fts-api.h"
 
 /* Returns TRUE if UID conversion was done, FALSE if uid should be skipped. */
-typedef bool solr_uid_map_callback_t(const char *mailbox, uint32_t uidvalidity,
-                                    uint32_t *uid, void *context);
+typedef bool solr_uid_map_callback_t(const char *ns_prefix, const char *mailbox,
+                                    uint32_t uidvalidity, uint32_t *uid,
+                                    void *context);
 
 struct solr_connection *solr_connection_init(const char *url, bool debug);
 void solr_connection_deinit(struct solr_connection *conn);
 
-void solr_connection_quote_str(struct solr_connection *conn, string_t *dest,
-                              const char *str);
 void solr_connection_http_escape(struct solr_connection *conn, string_t *dest,
                                 const char *str);
 
index 36c1c217e7631ae9d81668f19a0b0676292a877d..421848c039d39b2c2311580cc75bfa9eee63ec6b 100644 (file)
@@ -7,6 +7,7 @@
 #include "istream.h"
 #include "message-parser.h"
 #include "message-decoder.h"
+#include "mail-namespace.h"
 #include "mail-search-build.h"
 #include "mail-storage-private.h"
 #include "fts-api-private.h"
@@ -289,6 +290,8 @@ static int fts_build_init_virtual_next(struct fts_search_context *fctx)
        struct mailbox *const *boxes;
        const struct fts_backend_uid_map *last_uids;
        unsigned int boxi, uidi, box_count, last_uid_count;
+       const char *vname;
+       string_t *tmp;
        int ret, vret = 0;
 
        if (vctx->pool == NULL)
@@ -300,10 +303,13 @@ static int fts_build_init_virtual_next(struct fts_search_context *fctx)
        boxes = array_get(&vctx->mailboxes, &box_count);
        last_uids = array_get(&vctx->last_uids, &last_uid_count);
 
+       tmp = t_str_new(256);
        boxi = vctx->boxi;
        uidi = vctx->uidi;
        while (vret == 0 && boxi < box_count && uidi < last_uid_count) {
-               ret = strcmp(boxes[boxi]->name, last_uids[uidi].mailbox);
+               vname = mail_namespace_get_vname(boxes[boxi]->storage->ns, tmp,
+                                                boxes[boxi]->name);
+               ret = strcmp(vname, last_uids[uidi].mailbox);
                if (ret == 0) {
                        /* match. check also that uidvalidity matches. */
                        mailbox_get_status(boxes[boxi], STATUS_UIDVALIDITY,
index bfd0d34e3ad0baf04533829371417097a97e64df..f57050df81bf597f3b39d4b818b947c15a514fc4 100644 (file)
@@ -119,11 +119,11 @@ virtual_config_parse_line(struct virtual_parse_context *ctx, const char *line,
        if (strcasecmp(line, "INBOX") == 0)
                line = "INBOX";
        bbox->name = p_strdup(ctx->pool, line);
+       bbox->ns = mail_namespace_find(user->namespaces, &line);
        if (strchr(bbox->name, '*') != NULL ||
            strchr(bbox->name, '%') != NULL) {
                name = bbox->name[0] == '-' ? bbox->name + 1 : bbox->name;
                bbox->glob = imap_match_init(ctx->pool, name, TRUE, ctx->sep);
-               bbox->ns = mail_namespace_find(user->namespaces, &line);
                ctx->have_wildcards = TRUE;
        }
        array_append(&ctx->mbox->backend_boxes, &bbox, 1);
@@ -134,19 +134,25 @@ static void
 virtual_mailbox_get_list_patterns(struct virtual_parse_context *ctx)
 {
        struct virtual_mailbox *mbox = ctx->mbox;
-       ARRAY_TYPE(const_string) *dest;
+       ARRAY_TYPE(mailbox_virtual_patterns) *dest;
+       struct mailbox_virtual_pattern pattern;
        struct virtual_backend_box *const *bboxes;
        unsigned int i, count;
 
+       memset(&pattern, 0, sizeof(pattern));
        bboxes = array_get_modifiable(&mbox->backend_boxes, &count);
        p_array_init(&mbox->list_include_patterns, ctx->pool, count);
        p_array_init(&mbox->list_exclude_patterns, ctx->pool, count);
        for (i = 0; i < count; i++) {
-               if (*bboxes[i]->name == '-')
-                       dest = &mbox->list_exclude_patterns;
-               else
+               pattern.ns = bboxes[i]->ns;
+               pattern.pattern = bboxes[i]->name;
+               if (*pattern.pattern != '-')
                        dest = &mbox->list_include_patterns;
-               array_append(dest, &bboxes[i]->name, 1);
+               else {
+                       dest = &mbox->list_exclude_patterns;
+                       pattern.pattern++;
+               }
+               array_append(dest, &pattern, 1);
        }
 }
 
index 2445e0c44c075557a1d8df279465781f5ffabb60..d922b4223e010afdaf37657fef412d920706702b 100644 (file)
@@ -595,8 +595,8 @@ virtual_get_virtual_backend_boxes(struct mailbox *box,
 
 static void
 virtual_get_virtual_box_patterns(struct mailbox *box,
-                                ARRAY_TYPE(const_string) *includes,
-                                ARRAY_TYPE(const_string) *excludes)
+                                ARRAY_TYPE(mailbox_virtual_patterns) *includes,
+                                ARRAY_TYPE(mailbox_virtual_patterns) *excludes)
 {
        struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
 
index 2984b3d32c7bb1bcf3010f37527adf3a2c80fbcf..3f12460a89f58e6076b2f8ffa0f67311cb2aa157 100644 (file)
@@ -109,8 +109,8 @@ struct virtual_mailbox {
        /* Mailboxes this virtual mailbox consists of, sorted by mailbox_id */
        ARRAY_TYPE(virtual_backend_box) backend_boxes;
 
-       ARRAY_TYPE(const_string) list_include_patterns;
-       ARRAY_TYPE(const_string) list_exclude_patterns;
+       ARRAY_TYPE(mailbox_virtual_patterns) list_include_patterns;
+       ARRAY_TYPE(mailbox_virtual_patterns) list_exclude_patterns;
 
        unsigned int uids_mapped:1;
        unsigned int sync_initialized:1;