From 47d4be888451042ce498eb2a105b3771fc07299c Mon Sep 17 00:00:00 2001
From: andryyy <andre.peters@debinux.de>
Date: Sun, 10 Mar 2019 09:35:26 +0100
Subject: [PATCH 1/5] [Dovecot] v2.3.5 (PH 0.5.5) [Dovecot] Change Solr cronjob
 to fit dovecot-fts

---
 data/Dockerfiles/dovecot/Dockerfile           | 4 ++--
 data/Dockerfiles/dovecot/docker-entrypoint.sh | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/data/Dockerfiles/dovecot/Dockerfile b/data/Dockerfiles/dovecot/Dockerfile
index 1320df1f..bc4fca5c 100644
--- a/data/Dockerfiles/dovecot/Dockerfile
+++ b/data/Dockerfiles/dovecot/Dockerfile
@@ -3,8 +3,8 @@ LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
 
 ARG DEBIAN_FRONTEND=noninteractive
 ENV LC_ALL C
-ENV DOVECOT_VERSION 2.3.4
-ENV PIGEONHOLE_VERSION 0.5.4
+ENV DOVECOT_VERSION 2.3.5
+ENV PIGEONHOLE_VERSION 0.5.5
 
 RUN apt-get update && apt-get -y --no-install-recommends install \
   automake \
diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh
index 0589579d..80db4b48 100755
--- a/data/Dockerfiles/dovecot/docker-entrypoint.sh
+++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh
@@ -174,7 +174,7 @@ echo '30 3 * * *   vmail /usr/local/bin/doveadm quota recalc -A' > /etc/cron.d/d
 echo '* * * * *    vmail /usr/local/bin/trim_logs.sh >> /dev/console 2>&1' > /etc/cron.d/trim_logs
 echo '25 * * * *   vmail /usr/local/bin/maildir_gc.sh >> /dev/console 2>&1' > /etc/cron.d/maildir_gc
 echo '30 1 * * *   root  /usr/local/bin/sa-rules.sh  >> /dev/console 2>&1' > /etc/cron.d/sa-rules
-echo '0 2 * * *    root  /usr/bin/curl http://solr:8983/solr/dovecot/update?optimize=true >> /dev/console 2>&1' > /etc/cron.d/solr-optimize
+echo '0 2 * * *    root  /usr/bin/curl http://solr:8983/solr/dovecot-fts/update?optimize=true >> /dev/console 2>&1' > /etc/cron.d/solr-optimize
 echo '*/20 * * * * vmail /usr/local/bin/quarantine_notify.py >> /dev/console 2>&1' > /etc/cron.d/quarantine_notify
 
 # Fix more than 1 hardlink issue

From 73f836f83e3a6b81ead6b6458fe0ad9d5ec122fd Mon Sep 17 00:00:00 2001
From: andryyy <andre.peters@debinux.de>
Date: Sun, 10 Mar 2019 09:36:19 +0100
Subject: [PATCH 2/5] [Compose Update Rspamd, Dovecot and Solr images

---
 docker-compose.yml | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/docker-compose.yml b/docker-compose.yml
index d968cf0d..8c1d785f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -71,7 +71,7 @@ services:
             - clamd
 
     rspamd-mailcow:
-      image: mailcow/rspamd:1.36
+      image: mailcow/rspamd:1.37
       build: ./data/Dockerfiles/rspamd
       stop_grace_period: 30s
       depends_on:
@@ -164,7 +164,7 @@ services:
             - sogo
 
     dovecot-mailcow:
-      image: mailcow/dovecot:1.63
+      image: mailcow/dovecot:1.64
       build: ./data/Dockerfiles/dovecot
       cap_add:
         - NET_BIND_SERVICE
@@ -395,11 +395,12 @@ services:
             - dockerapi
 
     solr-mailcow:
-      image: mailcow/solr:1.2
+      image: mailcow/solr:1.3
       build: ./data/Dockerfiles/solr
       restart: always
       volumes:
         - solr-vol-1:/opt/solr/server/solr/dovecot/data
+        - ./data/conf/solr:/etc/solr
       dns:
         - ${IPV4_NETWORK:-172.22.1}.254
       environment:

From c7c115d63a02a7142838a37831d2defaeee7be59 Mon Sep 17 00:00:00 2001
From: andryyy <andre.peters@debinux.de>
Date: Sun, 10 Mar 2019 09:36:33 +0100
Subject: [PATCH 3/5] [Solr] Use fixed, recommended schema but add
 EdgeNGramFilterFactory

---
 data/Dockerfiles/solr/Dockerfile           |   5 +-
 data/Dockerfiles/solr/docker-entrypoint.sh | 390 ++-------------------
 data/conf/solr/solr-config-7.7.0.xml       | 289 +++++++++++++++
 data/conf/solr/solr-schema-7.7.0.xml       |  50 +++
 data/web/inc/functions.inc.php             |   4 +-
 5 files changed, 366 insertions(+), 372 deletions(-)
 create mode 100644 data/conf/solr/solr-config-7.7.0.xml
 create mode 100644 data/conf/solr/solr-schema-7.7.0.xml

diff --git a/data/Dockerfiles/solr/Dockerfile b/data/Dockerfiles/solr/Dockerfile
index 67cd3384..25235664 100644
--- a/data/Dockerfiles/solr/Dockerfile
+++ b/data/Dockerfiles/solr/Dockerfile
@@ -1,9 +1,8 @@
-FROM solr:7-alpine
+FROM solr:7.7-alpine
 USER root
 COPY docker-entrypoint.sh /
 
 RUN apk --no-cache add su-exec curl tzdata \
-  && chmod +x /docker-entrypoint.sh \
-  && /docker-entrypoint.sh --bootstrap
+  && chmod +x /docker-entrypoint.sh
 
 ENTRYPOINT ["/docker-entrypoint.sh"]
diff --git a/data/Dockerfiles/solr/docker-entrypoint.sh b/data/Dockerfiles/solr/docker-entrypoint.sh
index 108f8b5a..0634874f 100755
--- a/data/Dockerfiles/solr/docker-entrypoint.sh
+++ b/data/Dockerfiles/solr/docker-entrypoint.sh
@@ -26,395 +26,51 @@ fi
 # run the optional initdb
 . /opt/docker-solr/scripts/run-initdb
 
-function solr_config() {
-  curl -XPOST http://localhost:8983/solr/dovecot/schema -H 'Content-type:application/json' -d '{
-    "add-field-type":{
-      "name":"long",
-      "class":"solr.TrieLongField"
-    },
-    "add-field-type":{
-      "name":"dovecot_text",
-      "class":"solr.TextField",
-      "autoGeneratePhraseQueries":true,
-      "positionIncrementGap":100,
-      "indexAnalyser":{
-        "charFilter":{
-          "class":"solr.MappingCharFilterFactory",
-          "mapping":"mapping-FoldToASCII.txt"
-        },
-        "charFilter":{
-          "class":"solr.MappingCharFilterFactory",
-          "mapping":"mapping-ISOLatin1Accent.txt"
-        },
-        "charFilter":{
-          "class":"solr.HTMLStripCharFilterFactory"
-        },
-        "tokenizer":{
-          "class":"solr.StandardTokenizerFactory"
-        },
-        "filter":{
-          "class":"solr.StopFilterFactory",
-          "words":"stopwords.txt",
-          "ignoreCase":true
-        },
-        "filter":{
-          "class":"solr.WordDelimiterGraphFilterFactory",
-          "generateWordParts":1,
-          "generateNumberParts":1,
-          "splitOnCaseChange":1,
-          "splitOnNumerics":1,
-          "catenateWords":1,
-          "catenateNumbers":1,
-          "catenateAll":1
-        },
-        "filter":{
-          "class":"solr.FlattenGraphFilterFactory"
-        },
-        "filter":{
-          "class":"solr.LowerCaseFilterFactory"
-        },
-        "filter":{
-          "class":"solr.KeywordMarkerFilterFactory",
-          "protected":"protwords.txt"
-        },
-        "filter":{
-          "class":"solr.PorterStemFilterFactory"
-        }
-      },
-      "queryAnalyzer":{
-        "tokenizer":{
-          "class":"solr.StandardTokenizerFactory"
-        },
-        "filter":{
-          "class":"solr.SynonymGraphFilterFactory",
-          "expand":true,
-          "ignoreCase":true,
-          "synonyms":synonyms.txt
-        },
-        "filter":{
-          "class":"solr.FlattenGraphFilterFactory"
-        },
-        "filter":{
-          "class":"solr.StopFilterFactory",
-          "words":"stopwords.txt",
-          "ignoreCase":true
-        },
-        "filter":{
-          "class":"solr.WordDelimiterGraphFilterFactory",
-          "generateWordParts":1,
-          "generateNumberParts":1,
-          "splitOnCaseChange":1,
-          "splitOnNumerics":1,
-          "catenateWords":1,
-          "catenateNumbers":1,
-          "catenateAll":1
-        },
-        "filter":{
-          "class":"solr.LowerCaseFilterFactory"
-        },
-        "filter":{
-          "class":"solr.KeywordMarkerFilterFactory",
-          "protected":"protwords.txt"
-        },
-        "filter":{
-          "class":"solr.PorterStemFilterFactory"
-        }
-      }
-    },
-    "add-field":{
-      "name":"uid",
-      "type":"long",
-      "indexed":true,
-      "stored":true,
-      "required":true
-    },
-    "add-field":{
-      "name":"box",
-      "type":"string",
-      "indexed":true,
-      "stored":true,
-      "required":true
-    },
-    "add-field":{
-      "name":"user",
-      "type":"string",
-      "indexed":true,
-      "stored":true,
-      "required":true
-    },
-    "add-field":{
-      "name":"hdr",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-
-    },
-    "add-field":{
-      "name":"body",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    },
-    "add-field":{
-      "name":"from",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    },
-    "add-field":{
-      "name":"to",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    },
-    "add-field":{
-      "name":"cc",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    },
-    "add-field":{
-      "name":"bcc",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    },
-    "add-field":{
-      "name":"subject",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    }
-  }'
-
-  curl -XPOST http://localhost:8983/solr/dovecot/schema -H 'Content-type:application/json' -d '{
-    "replace-field-type":{
-      "name":"long",
-      "class":"solr.TrieLongField"
-    },
-    "replace-field-type":{
-      "name":"dovecot_text",
-      "class":"solr.TextField",
-      "autoGeneratePhraseQueries":true,
-      "positionIncrementGap":100,
-      "indexAnalyser":{
-        "charFilter":{
-          "class":"solr.MappingCharFilterFactory",
-          "mapping":"mapping-FoldToASCII.txt"
-        },
-        "charFilter":{
-          "class":"solr.MappingCharFilterFactory",
-          "mapping":"mapping-ISOLatin1Accent.txt"
-        },
-        "charFilter":{
-          "class":"solr.HTMLStripCharFilterFactory"
-        },
-        "tokenizer":{
-          "class":"solr.StandardTokenizerFactory"
-        },
-        "filter":{
-          "class":"solr.StopFilterFactory",
-          "words":"stopwords.txt",
-          "ignoreCase":true
-        },
-        "filter":{
-          "class":"solr.WordDelimiterGraphFilterFactory",
-          "generateWordParts":1,
-          "generateNumberParts":1,
-          "splitOnCaseChange":1,
-          "splitOnNumerics":1,
-          "catenateWords":1,
-          "catenateNumbers":1,
-          "catenateAll":1
-        },
-        "filter":{
-          "class":"solr.FlattenGraphFilterFactory"
-        },
-        "filter":{
-          "class":"solr.LowerCaseFilterFactory"
-        },
-        "filter":{
-          "class":"solr.KeywordMarkerFilterFactory",
-          "protected":"protwords.txt"
-        },
-        "filter":{
-          "class":"solr.PorterStemFilterFactory"
-        }
-      },
-      "queryAnalyzer":{
-        "tokenizer":{
-          "class":"solr.StandardTokenizerFactory"
-        },
-        "filter":{
-          "class":"solr.SynonymGraphFilterFactory",
-          "expand":true,
-          "ignoreCase":true,
-          "synonyms":synonyms.txt
-        },
-        "filter":{
-          "class":"solr.FlattenGraphFilterFactory"
-        },
-        "filter":{
-          "class":"solr.StopFilterFactory",
-          "words":"stopwords.txt",
-          "ignoreCase":true
-        },
-        "filter":{
-          "class":"solr.WordDelimiterGraphFilterFactory",
-          "generateWordParts":1,
-          "generateNumberParts":1,
-          "splitOnCaseChange":1,
-          "splitOnNumerics":1,
-          "catenateWords":1,
-          "catenateNumbers":1,
-          "catenateAll":1
-        },
-        "filter":{
-          "class":"solr.LowerCaseFilterFactory"
-        },
-        "filter":{
-          "class":"solr.KeywordMarkerFilterFactory",
-          "protected":"protwords.txt"
-        },
-        "filter":{
-          "class":"solr.PorterStemFilterFactory"
-        }
-      }
-    },
-    "replace-field":{
-      "name":"uid",
-      "type":"long",
-      "indexed":true,
-      "stored":true,
-      "required":true
-    },
-    "replace-field":{
-      "name":"box",
-      "type":"string",
-      "indexed":true,
-      "stored":true,
-      "required":true
-    },
-    "replace-field":{
-      "name":"user",
-      "type":"string",
-      "indexed":true,
-      "stored":true,
-      "required":true
-    },
-    "replace-field":{
-      "name":"hdr",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-
-    },
-    "replace-field":{
-      "name":"body",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    },
-    "replace-field":{
-      "name":"from",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    },
-    "replace-field":{
-      "name":"to",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    },
-    "replace-field":{
-      "name":"cc",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    },
-    "replace-field":{
-      "name":"bcc",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    },
-    "replace-field":{
-      "name":"subject",
-      "type":"dovecot_text",
-      "indexed":true,
-      "stored":false
-    }
-  }'
-
-  curl -XPOST http://localhost:8983/solr/dovecot/config -H 'Content-type:application/json' -d '{
-    "update-requesthandler":{
-      "name":"/select",
-      "class":"solr.SearchHandler",
-      "defaults":{
-        "wt":"xml"
-      }
-    }
-  }'
-
-  curl -XPOST http://localhost:8983/solr/dovecot/config/updateHandler -d '{
-    "set-property": {
-      "updateHandler.autoSoftCommit.maxDocs":500,
-      "updateHandler.autoSoftCommit.maxTime":120000,
-      "updateHandler.autoCommit.maxDocs":200,
-      "updateHandler.autoCommit.maxTime":1800000,
-      "updateHandler.autoCommit.openSearcher":false
-    }
-  }'
-}
-
 # fixing volume permission
 
-[[ -d /opt/solr/server/solr/dovecot/data ]] && chown -R solr:solr /opt/solr/server/solr/dovecot/data
+[[ -d /opt/solr/server/solr/dovecot-fts/data ]] && chown -R solr:solr /opt/solr/server/solr/dovecot-fts/data
 if [[ "${1}" != "--bootstrap" ]]; then
   sed -i '/SOLR_HEAP=/c\SOLR_HEAP="'${SOLR_HEAP:-1024}'m"' /opt/solr/bin/solr.in.sh
 else
   sed -i '/SOLR_HEAP=/c\SOLR_HEAP="256m"' /opt/solr/bin/solr.in.sh
 fi
 
-# start a Solr so we can use the Schema API, but only on localhost,
-# so that clients don't see Solr until we have configured it.
-
-echo "Starting local Solr instance to setup configuration"
-su-exec solr start-local-solr
-
 # keep a sentinel file so we don't try to create the core a second time
 # for example when we restart a container.
+# todo: check if a core exists without sentinel file
 
-SENTINEL=/opt/docker-solr/core_created
+SENTINEL=/opt/docker-solr/fts_core_created
 
 if [[ -f ${SENTINEL} ]]; then
   echo "skipping core creation"
 else
-  echo "Creating core \"dovecot\""
-  su-exec solr /opt/solr/bin/solr create -c "dovecot"
+  echo "Starting local Solr instance to setup configuration"
+  su-exec solr start-local-solr
+
+  echo "Creating core \"dovecot-fts\""
+  su-exec solr /opt/solr/bin/solr create -c "dovecot-fts"
 
   # See https://github.com/docker-solr/docker-solr/issues/27
   echo "Checking core"
   while ! wget -O - 'http://localhost:8983/solr/admin/cores?action=STATUS' | grep -q instanceDir; do
     echo "Could not find any cores, waiting..."
-    sleep 5
+    sleep 3
   done
-  echo "Created core \"dovecot\""
+
+  echo "Created core \"dovecot-fts\""
   touch ${SENTINEL}
+
+  echo "Stopping local Solr"
+  su-exec solr stop-local-solr
 fi
 
-echo "Starting configuration"
-while ! wget -O - 'http://localhost:8983/solr/admin/cores?action=STATUS' | grep -q instanceDir; do
-  echo "Waiting for Solr..."
-  sleep 5
-done
-solr_config
-echo "Stopping local Solr"
-su-exec solr stop-local-solr
+rm -f /opt/solr/server/solr/dovecot-fts/conf/schema.xml
+rm -f /opt/solr/server/solr/dovecot-fts/conf/managed-schema
+rm -f /opt/solr/server/solr/dovecot-fts/conf/solrconfig.xml
 
-if [[ "${1}" == "--bootstrap" ]]; then
-  exit 0
-else
-  exec su-exec solr solr-foreground
-fi
+cp /etc/solr/solr-config-7.7.0.xml /opt/solr/server/solr/dovecot-fts/conf/solrconfig.xml
+cp /etc/solr/solr-schema-7.7.0.xml /opt/solr/server/solr/dovecot-fts/conf/schema.xml
+
+chown -R solr:solr /opt/solr/server/solr/dovecot-fts/conf/{schema.xml,solrconfig.xml}
+
+exec su-exec solr solr-foreground
diff --git a/data/conf/solr/solr-config-7.7.0.xml b/data/conf/solr/solr-config-7.7.0.xml
new file mode 100644
index 00000000..3661874d
--- /dev/null
+++ b/data/conf/solr/solr-config-7.7.0.xml
@@ -0,0 +1,289 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!-- This is the default config with stuff non-essential to Dovecot removed. -->
+
+<config>
+  <!-- Controls what version of Lucene various components of Solr
+       adhere to.  Generally, you want to use the latest version to
+       get all bug fixes and improvements. It is highly recommended
+       that you fully re-index after changing this setting as it can
+       affect both how text is indexed and queried.
+  -->
+  <luceneMatchVersion>7.7.0</luceneMatchVersion>
+
+  <!-- A 'dir' option by itself adds any files found in the directory
+       to the classpath, this is useful for including all jars in a
+       directory.
+
+       When a 'regex' is specified in addition to a 'dir', only the
+       files in that directory which completely match the regex
+       (anchored on both ends) will be included.
+
+       If a 'dir' option (with or without a regex) is used and nothing
+       is found that matches, a warning will be logged.
+
+       The examples below can be used to load some solr-contribs along
+       with their external dependencies.
+    -->
+  <lib dir="${solr.install.dir:../../../..}/contrib/extraction/lib" regex=".*\.jar" />
+  <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-cell-\d.*\.jar" />
+
+  <lib dir="${solr.install.dir:../../../..}/contrib/clustering/lib/" regex=".*\.jar" />
+  <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-clustering-\d.*\.jar" />
+
+  <lib dir="${solr.install.dir:../../../..}/contrib/langid/lib/" regex=".*\.jar" />
+  <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-langid-\d.*\.jar" />
+
+  <lib dir="${solr.install.dir:../../../..}/contrib/velocity/lib" regex=".*\.jar" />
+  <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-velocity-\d.*\.jar" />
+
+  <!-- Data Directory
+
+       Used to specify an alternate directory to hold all index data
+       other than the default ./data under the Solr home.  If
+       replication is in use, this should match the replication
+       configuration.
+    -->
+  <dataDir>${solr.data.dir:}</dataDir>
+
+  <!-- The default high-performance update handler -->
+  <updateHandler class="solr.DirectUpdateHandler2">
+
+    <!-- Enables a transaction log, used for real-time get, durability, and
+         and solr cloud replica recovery.  The log can grow as big as
+         uncommitted changes to the index, so use of a hard autoCommit
+         is recommended (see below).
+         "dir" - the target directory for transaction logs, defaults to the
+                solr data directory.
+         "numVersionBuckets" - sets the number of buckets used to keep
+                track of max version values when checking for re-ordered
+                updates; increase this value to reduce the cost of
+                synchronizing access to version buckets during high-volume
+                indexing, this requires 8 bytes (long) * numVersionBuckets
+                of heap space per Solr core.
+    -->
+    <updateLog>
+      <str name="dir">${solr.ulog.dir:}</str>
+      <int name="numVersionBuckets">${solr.ulog.numVersionBuckets:65536}</int>
+    </updateLog>
+
+    <!-- AutoCommit
+
+         Perform a hard commit automatically under certain conditions.
+         Instead of enabling autoCommit, consider using "commitWithin"
+         when adding documents.
+
+         http://wiki.apache.org/solr/UpdateXmlMessages
+
+         maxDocs - Maximum number of documents to add since the last
+                   commit before automatically triggering a new commit.
+
+         maxTime - Maximum amount of time in ms that is allowed to pass
+                   since a document was added before automatically
+                   triggering a new commit.
+         openSearcher - if false, the commit causes recent index changes
+           to be flushed to stable storage, but does not cause a new
+           searcher to be opened to make those changes visible.
+
+         If the updateLog is enabled, then it's highly recommended to
+         have some sort of hard autoCommit to limit the log size.
+      -->
+    <autoCommit>
+      <maxTime>${solr.autoCommit.maxTime:15000}</maxTime>
+      <openSearcher>false</openSearcher>
+    </autoCommit>
+
+    <!-- softAutoCommit is like autoCommit except it causes a
+         'soft' commit which only ensures that changes are visible
+         but does not ensure that data is synced to disk.  This is
+         faster and more near-realtime friendly than a hard commit.
+      -->
+    <autoSoftCommit>
+      <maxTime>${solr.autoSoftCommit.maxTime:-1}</maxTime>
+    </autoSoftCommit>
+
+    <!-- Update Related Event Listeners
+
+         Various IndexWriter related events can trigger Listeners to
+         take actions.
+
+         postCommit - fired after every commit or optimize command
+         postOptimize - fired after every optimize command
+      -->
+
+  </updateHandler>
+
+  <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+       Query section - these settings control query time things like caches
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
+  <query>
+    <!-- Solr Internal Query Caches
+
+         There are two implementations of cache available for Solr,
+         LRUCache, based on a synchronized LinkedHashMap, and
+         FastLRUCache, based on a ConcurrentHashMap.
+
+         FastLRUCache has faster gets and slower puts in single
+         threaded operation and thus is generally faster than LRUCache
+         when the hit ratio of the cache is high (> 75%), and may be
+         faster under other scenarios on multi-cpu systems.
+    -->
+
+    <!-- Filter Cache
+
+         Cache used by SolrIndexSearcher for filters (DocSets),
+         unordered sets of *all* documents that match a query.  When a
+         new searcher is opened, its caches may be prepopulated or
+         "autowarmed" using data from caches in the old searcher.
+         autowarmCount is the number of items to prepopulate.  For
+         LRUCache, the autowarmed items will be the most recently
+         accessed items.
+
+         Parameters:
+           class - the SolrCache implementation LRUCache or
+               (LRUCache or FastLRUCache)
+           size - the maximum number of entries in the cache
+           initialSize - the initial capacity (number of entries) of
+               the cache.  (see java.util.HashMap)
+           autowarmCount - the number of entries to prepopulate from
+               and old cache.
+           maxRamMB - the maximum amount of RAM (in MB) that this cache is allowed
+                      to occupy. Note that when this option is specified, the size
+                      and initialSize parameters are ignored.
+      -->
+    <filterCache class="solr.FastLRUCache"
+                 size="512"
+                 initialSize="512"
+                 autowarmCount="0"/>
+
+    <!-- Query Result Cache
+
+         Caches results of searches - ordered lists of document ids
+         (DocList) based on a query, a sort, and the range of documents requested.
+         Additional supported parameter by LRUCache:
+            maxRamMB - the maximum amount of RAM (in MB) that this cache is allowed
+                       to occupy
+      -->
+    <queryResultCache class="solr.LRUCache"
+                      size="512"
+                      initialSize="512"
+                      autowarmCount="0"/>
+
+    <!-- Document Cache
+
+         Caches Lucene Document objects (the stored fields for each
+         document).  Since Lucene internal document ids are transient,
+         this cache will not be autowarmed.
+      -->
+    <documentCache class="solr.LRUCache"
+                   size="512"
+                   initialSize="512"
+                   autowarmCount="0"/>
+
+    <!-- custom cache currently used by block join -->
+    <cache name="perSegFilter"
+           class="solr.search.LRUCache"
+           size="10"
+           initialSize="0"
+           autowarmCount="10"
+           regenerator="solr.NoOpRegenerator" />
+
+    <!-- Lazy Field Loading
+
+         If true, stored fields that are not requested will be loaded
+         lazily.  This can result in a significant speed improvement
+         if the usual case is to not load all stored fields,
+         especially if the skipped fields are large compressed text
+         fields.
+    -->
+    <enableLazyFieldLoading>true</enableLazyFieldLoading>
+
+    <!-- Result Window Size
+
+         An optimization for use with the queryResultCache.  When a search
+         is requested, a superset of the requested number of document ids
+         are collected.  For example, if a search for a particular query
+         requests matching documents 10 through 19, and queryWindowSize is 50,
+         then documents 0 through 49 will be collected and cached.  Any further
+         requests in that range can be satisfied via the cache.
+      -->
+    <queryResultWindowSize>20</queryResultWindowSize>
+
+    <!-- Maximum number of documents to cache for any entry in the
+         queryResultCache.
+      -->
+    <queryResultMaxDocsCached>200</queryResultMaxDocsCached>
+
+    <!-- Use Cold Searcher
+
+         If a search request comes in and there is no current
+         registered searcher, then immediately register the still
+         warming searcher and use it.  If "false" then all requests
+         will block until the first searcher is done warming.
+      -->
+    <useColdSearcher>false</useColdSearcher>
+
+  </query>
+
+
+  <!-- Request Dispatcher
+
+       This section contains instructions for how the SolrDispatchFilter
+       should behave when processing requests for this SolrCore.
+
+    -->
+  <requestDispatcher>
+    <httpCaching never304="true" />
+  </requestDispatcher>
+
+  <!-- Request Handlers
+
+       http://wiki.apache.org/solr/SolrRequestHandler
+
+       Incoming queries will be dispatched to a specific handler by name
+       based on the path specified in the request.
+
+       If a Request Handler is declared with startup="lazy", then it will
+       not be initialized until the first request that uses it.
+
+    -->
+  <!-- SearchHandler
+
+       http://wiki.apache.org/solr/SearchHandler
+
+       For processing Search Queries, the primary Request Handler
+       provided with Solr is "SearchHandler" It delegates to a sequent
+       of SearchComponents (see below) and supports distributed
+       queries across multiple shards
+    -->
+  <requestHandler name="/select" class="solr.SearchHandler">
+    <!-- default values for query parameters can be specified, these
+         will be overridden by parameters in the request
+      -->
+    <lst name="defaults">
+      <str name="echoParams">explicit</str>
+      <int name="rows">10</int>
+    </lst>
+  </requestHandler>
+
+  <initParams path="/update/**,/select">
+    <lst name="defaults">
+      <str name="df">_text_</str>
+    </lst>
+  </initParams>
+
+  <!-- Response Writers
+
+       http://wiki.apache.org/solr/QueryResponseWriter
+
+       Request responses will be written using the writer specified by
+       the 'wt' request parameter matching the name of a registered
+       writer.
+
+       The "default" writer is the default and will be used if 'wt' is
+       not specified in the request.
+    -->
+  <queryResponseWriter name="xml"
+                       default="true"
+                       class="solr.XMLResponseWriter" />
+</config>
diff --git a/data/conf/solr/solr-schema-7.7.0.xml b/data/conf/solr/solr-schema-7.7.0.xml
new file mode 100644
index 00000000..a41cbb47
--- /dev/null
+++ b/data/conf/solr/solr-schema-7.7.0.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<schema name="dovecot" version="2.0">
+  <fieldType name="string" class="solr.StrField" omitNorms="true" sortMissingLast="true"/>
+  <fieldType name="long" class="solr.LongPointField" positionIncrementGap="0"/>
+  <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
+
+  <fieldType name="text" class="solr.TextField" autoGeneratePhraseQueries="true" positionIncrementGap="100">
+    <analyzer type="index">
+      <tokenizer class="solr.StandardTokenizerFactory"/>
+      <filter class="solr.EdgeNGramFilterFactory" minGramSize="3" maxGramSize="20"/>
+      <filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/>
+      <filter class="solr.WordDelimiterGraphFilterFactory" catenateNumbers="1" generateNumberParts="1" splitOnCaseChange="1" generateWordParts="1" splitOnNumerics="1" catenateAll="1" catenateWords="1"/>
+      <filter class="solr.FlattenGraphFilterFactory"/>
+      <filter class="solr.LowerCaseFilterFactory"/>
+      <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+      <filter class="solr.PorterStemFilterFactory"/>
+    </analyzer>
+    <analyzer type="query">
+      <tokenizer class="solr.StandardTokenizerFactory"/>
+      <filter class="solr.EdgeNGramFilterFactory" minGramSize="3" maxGramSize="20"/>
+      <filter class="solr.SynonymGraphFilterFactory" expand="true" ignoreCase="true" synonyms="synonyms.txt"/>
+      <filter class="solr.FlattenGraphFilterFactory"/>
+      <filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/>
+      <filter class="solr.WordDelimiterGraphFilterFactory" catenateNumbers="1" generateNumberParts="1" splitOnCaseChange="1" generateWordParts="1" splitOnNumerics="1" catenateAll="1" catenateWords="1"/>
+      <filter class="solr.LowerCaseFilterFactory"/>
+      <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+      <filter class="solr.PorterStemFilterFactory"/>
+    </analyzer>
+  </fieldType>
+
+  <field name="id" type="string" indexed="true" required="true" stored="true"/>
+  <field name="uid" type="long" indexed="true" required="true" stored="true"/>
+  <field name="box" type="string" indexed="true" required="true" stored="true"/>
+  <field name="user" type="string" indexed="true" required="true" stored="true"/>
+
+  <field name="hdr" type="text" indexed="true" stored="false"/>
+  <field name="body" type="text" indexed="true" stored="false"/>
+
+  <field name="from" type="text" indexed="true" stored="false"/>
+  <field name="to" type="text" indexed="true" stored="false"/>
+  <field name="cc" type="text" indexed="true" stored="false"/>
+  <field name="bcc" type="text" indexed="true" stored="false"/>
+  <field name="subject" type="text" indexed="true" stored="false"/>
+
+  <!-- Used by Solr internally: -->
+  <field name="_version_" type="long" indexed="true" stored="true"/>
+
+  <uniqueKey>id</uniqueKey>
+</schema>
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
index 34872aef..c869c122 100644
--- a/data/web/inc/functions.inc.php
+++ b/data/web/inc/functions.inc.php
@@ -1477,7 +1477,7 @@ function solr_status() {
   $endpoint = 'http://solr:8983/solr/admin/cores';
   $params = array(
     'action' => 'STATUS',
-    'core' => 'dovecot',
+    'core' => 'dovecot-fts',
     'indexInfo' => 'true'
   );
   $url = $endpoint . '?' . http_build_query($params);
@@ -1494,7 +1494,7 @@ function solr_status() {
   else {
     curl_close($curl);
     $status = json_decode($response, true);
-    return (!empty($status['status']['dovecot'])) ? $status['status']['dovecot'] : false;
+    return (!empty($status['status']['dovecot-fts'])) ? $status['status']['dovecot-fts'] : false;
   }
   return false;
 }

From 0a1e71f7ecdcaf6a27e054ca3580edcf372173e5 Mon Sep 17 00:00:00 2001
From: andryyy <andre.peters@debinux.de>
Date: Sun, 10 Mar 2019 09:40:31 +0100
Subject: [PATCH 4/5] [Dovecot] Use dovecot-fts core

---
 data/conf/dovecot/dovecot.conf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/data/conf/dovecot/dovecot.conf b/data/conf/dovecot/dovecot.conf
index d693d39c..b81604a2 100644
--- a/data/conf/dovecot/dovecot.conf
+++ b/data/conf/dovecot/dovecot.conf
@@ -308,7 +308,7 @@ plugin {
   acl = vfile
   fts = solr
   fts_autoindex = yes
-  fts_solr = url=http://solr:8983/solr/dovecot/
+  fts_solr = url=http://solr:8983/solr/dovecot-fts/
   quota = dict:Userquota::proxy::sqlquota
   quota_rule2 = Trash:storage=+100%%
   sieve = /var/vmail/sieve/%u.sieve

From 78b2bde9423cbb0cb938ca6590624568007de253 Mon Sep 17 00:00:00 2001
From: andryyy <andre.peters@debinux.de>
Date: Sun, 10 Mar 2019 10:20:49 +0100
Subject: [PATCH 5/5] [Web] Change core to dovecot-fts

---
 data/web/inc/functions.mailbox.inc.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php
index 753e1702..82517492 100644
--- a/data/web/inc/functions.mailbox.inc.php
+++ b/data/web/inc/functions.mailbox.inc.php
@@ -3525,7 +3525,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             }
             if (strtolower(getenv('SKIP_SOLR')) == 'n') {
               $curl = curl_init();
-              curl_setopt($curl, CURLOPT_URL, 'http://solr:8983/solr/dovecot/update?commit=true');
+              curl_setopt($curl, CURLOPT_URL, 'http://solr:8983/solr/dovecot-fts/update?commit=true');
               curl_setopt($curl, CURLOPT_HTTPHEADER,array('Content-Type: text/xml'));
               curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
               curl_setopt($curl, CURLOPT_POST, 1);