Dovecot: Полнотекстовый поиск

Dovecot: Полнотекстовый поиск

Чем больше писем у пользователей, тем дольше выполняется поиск по ним. Поэтому, рекомендуется подключать полнотекстовый поиск.

Установка

  • Установить пакеты:
1
apt install --yes -t 'stable-backports' dovecot-fts-xapian && apt install --yes xapian-tools

Настойка

  • В файле /etc/dovecot/dovecot.conf к директиве mail_plugins добавить fts fts_xapian.
  • В файл /etc/dovecot/dovecot.conf добавить следующее содержимое:
dovecot.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# -------------------------------------------------------------------------------------------------------------------- #
# FTS: XAPIAN
# -------------------------------------------------------------------------------------------------------------------- #

plugin {
  fts = xapian
  fts_xapian = partial=3 full=20 verbose=0

  fts_autoindex = yes
  fts_enforced = yes

  # Disable indexing of folders.
  fts_autoindex_exclude = \Junk
  fts_autoindex_exclude2 = \Spam
  fts_autoindex_exclude3 = \Trash

  # Index attachments.
  # fts_decoder = decode2text
}

service indexer-worker {
  # Limit size of indexer-worker RAM usage, ex: 512MB, 1GB, 2GB.
  vsz_limit = 2G
  process_limit = 0
}

# service decode2text {
#   executable = script /usr/lib/dovecot/decode2text.sh
#   user = dovecot
#   unix_listener decode2text {
#     mode = 0666
#   }
# }
  • Перезапустить сервис dovecot и запустить индексирование писем:
1
systemctl restart dovecot.service && doveadm index -A -q \*

Декодирование вложений

  • Создать файл /usr/lib/dovecot/decode2text.sh со следующим содержанием:
decode2text.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#!/usr/bin/env -S bash
# -------------------------------------------------------------------------------------------------------------------- #
# DOVECOT: ATTACHMENT DECODER SCRIPT
# -------------------------------------------------------------------------------------------------------------------- #
# @package    Bash
# @author     Kai Kimera
# @license    MIT
# @version    0.1.0
# @link       https://lib.onl/ru/2025/03/7d3b9bd5-e43d-52a7-9662-e58a0819c91a/
# -------------------------------------------------------------------------------------------------------------------- #

lib_dir='/usr/lib/dovecot'
content_type="${1}"

# The second parameter is the format's filename extension, which is used when found from a filename of
# application/octet-stream. You can also add more extensions by giving more parameters.
formats='application/pdf pdf
application/x-pdf pdf
application/msword doc
application/mspowerpoint ppt
application/vnd.ms-powerpoint ppt
application/ms-excel xls
application/x-msexcel xls
application/vnd.ms-excel xls
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx
application/vnd.oasis.opendocument.text odt
application/vnd.oasis.opendocument.spreadsheet ods
application/vnd.oasis.opendocument.presentation odp
'

[[ "${content_type}" == "" ]] && { echo "${formats}"; exit 0; }
fmt="$( echo "${formats}" | grep -w "^${content_type}" | cut -d ' ' -f 2 )"
[[ "${fmt}" == "" ]] && { echo >&2 "Content-Type: ${content_type} not supported"; exit 1; }

# Most decoders can't handle stdin directly, so write the attachment to a temp file.
path="$( mktemp )"; trap 'rm -f "${path}"' 0 1 2 3 14 15; cat > "${path}"

xml_unzip() {
  name="${1}"
  tempdir="$( mktemp -d )"; [[ "${tempdir}" == "" ]] && exit 1

  trap 'rm -rf "${path}" "${tempdir}"' 0 1 2 3 14 15
  cd "${tempdir}" || exit 1
  unzip -q "${path}" 2>/dev/null || exit 0
  find . -name "${name}" -print0 | xargs -0 cat | "${lib_dir}/xml2text"
}

wait_timeout() {
  child_pid=$!
  trap 'kill -9 "${child_pid}"; rm -f "${path}"' 1 2 3 14 15
  wait "${child_pid}"
}

LANG='en_US.UTF-8'; export LANG

if [[ "${fmt}" == "pdf" ]]; then
  /usr/bin/pdftotext "${path}" - 2>/dev/null&
  wait_timeout 2>/dev/null
elif [[ "${fmt}" == "doc" ]]; then
  ( /usr/bin/catdoc "${path}"; true ) 2>/dev/null&
  wait_timeout 2>/dev/null
elif [[ "${fmt}" == "ppt" ]]; then
  ( /usr/bin/catppt "${path}"; true ) 2>/dev/null&
  wait_timeout 2>/dev/null
elif [[ "${fmt}" == "xls" ]]; then
  ( /usr/bin/xls2csv "${path}"; true ) 2>/dev/null&
  wait_timeout 2>/dev/null
elif [[ "${fmt}" == "odt" || "${fmt}" == "ods" || "${fmt}" == "odp" ]]; then
  xml_unzip "content.xml"
elif [[ "${fmt}" == "docx" ]]; then
  xml_unzip "document.xml"
elif [[ "${fmt}" == "xlsx" ]]; then
  xml_unzip "sharedStrings.xml"
elif [[ "${fmt}" == "pptx" ]]; then
  xml_unzip "slide*.xml"
else
  echo "Buggy decoder script: ${fmt} not handled!" >&2; exit 1
fi

exit 0
  • Применить к файлу /usr/lib/dovecot/decode2text.sh бит исполнения:
1
chmod +x '/usr/lib/dovecot/decode2text.sh'
  • Раскомментировать строки service decode2text { <...> } в файле /etc/dovecot/dovecot.conf.

Задание

  • Создать файл /etc/cron.d/dovecot_fts_optimize со следующим содержанием:
dovecot_fts_optimize
1
2
3
4
5
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOME=/root

30 4 * * * root doveadm fts optimize -A > /dev/null 2>&1

Команды

  • Восстановить повреждённые почтовые ящики всех пользователей:
1
doveadm force-resync -A \*
  • Восстановить повреждённый почтовый ящик пользователя bob:
1
doveadm force-resync -u 'bob' \*
  • Просканировать и сопоставить письма в почтовых ящиках с письмами в индексе полнотекстового поиска для всех пользователей:
1
doveadm fts rescan -A
  • Просканировать и сопоставить письма в почтовом ящике с письмами в индексе полнотекстового поиска для пользователя bob:
1
doveadm fts rescan -u 'bob'
  • Добавить неиндексированные письма из почтовых ящиков в файл индекса/кэша для всех пользователей:
1
doveadm index -A -q \*
  • Добавить неиндексированные письма из почтового ящика в файл индекса/кэша для пользователя bob:
1
doveadm index -u 'bob' -q \*