Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package grommunio-index for openSUSE:Factory
checked in at 2024-06-14 19:03:05
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/grommunio-index (Old)
and /work/SRC/openSUSE:Factory/.grommunio-index.new.19518 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "grommunio-index"
Fri Jun 14 19:03:05 2024 rev:3 rq:1180912 version:1.0.6.f40d25b
Changes:
--------
--- /work/SRC/openSUSE:Factory/grommunio-index/grommunio-index.changes
2023-01-18 13:11:49.317083885 +0100
+++
/work/SRC/openSUSE:Factory/.grommunio-index.new.19518/grommunio-index.changes
2024-06-14 19:07:44.635827974 +0200
@@ -1,0 +2,6 @@
+Fri Jun 7 12:32:59 UTC 2024 - Jan Engelhardt <[email protected]>
+
+- Update to snapshot 1.0.6
+ * Switch to user identity groindex/groweb+gromoxcf
+
+-------------------------------------------------------------------
Old:
----
grommunio-index-0.1.18.6a0f73a.tar.xz
New:
----
debian.grommunio-index.postinst
grommunio-index-1.0.6.f40d25b.tar.xz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ grommunio-index.spec ++++++
--- /var/tmp/diff_new_pack.4DGvsW/_old 2024-06-14 19:07:45.163846744 +0200
+++ /var/tmp/diff_new_pack.4DGvsW/_new 2024-06-14 19:07:45.163846744 +0200
@@ -1,7 +1,7 @@
#
# spec file for package grommunio-index
#
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
Name: grommunio-index
-Version: 0.1.18.6a0f73a
+Version: 1.0.6.f40d25b
Release: 0
Summary: Generator for grommunio-web search indexes
License: AGPL-3.0-or-later
@@ -30,12 +30,29 @@
%else
BuildRequires: gcc-c++
%endif
+%if 0%{?suse_version}
+BuildRequires: libmysqlclient-devel >= 5.6
+%else
+BuildRequires: mariadb-devel >= 5.6
+%endif
BuildRequires: libexmdbpp-devel >= 1.8.0
BuildRequires: libexmdbpp0 >= 1.8.0
+BuildRequires: pkgconfig(libHX) >= 3.27
BuildRequires: pkgconfig(sqlite3)
BuildRequires: pkgconfig(systemd)
Requires: libexmdbpp0 >= 1.8.0
-Requires: user(groweb)
+%if 0%{?suse_version} >= 1500
+BuildRequires: sysuser-tools
+%sysusers_requires
+%else
+Requires(pre): %_sbindir/groupadd
+Requires(pre): %_sbindir/useradd
+%endif
+Requires(pre): group(groweb)
+Requires(pre): group(gromoxcf)
+Requires: group(gromoxcf)
+Requires: group(groweb)
+Requires: user(groindex)
%define services grommunio-index.service grommunio-index.timer
%description
@@ -45,48 +62,65 @@
%autosetup -p1
%build
+>user.pre
+%if 0%{?suse_version} >= 1500
+%sysusers_generate_pre %_sourcedir/system-user-groindex.conf user
system-user-groindex.conf
+%endif
+
+pushd .
%if 0%{?suse_version} && 0%{?suse_version} < 1550
%cmake -DCMAKE_CXX_COMPILER=%_bindir/g++-11
%else
-%if 0%{?centos_version} == 800
-echo '#!/bin/sh -ex' >cxx
-echo 'exec g++ "$@" -lstdc++fs' >>cxx
-ls -al cxx
-chmod a+x cxx
-export CXX="$PWD/cxx"
-%cmake
-%else
%cmake
%endif
-%endif
%cmake_build
+popd
%install
-%if 0%{?centos_version} == 800
-export CXX="$PWD/cxx"
-%endif
+pushd .
%cmake_install
+popd
mkdir -p "%buildroot/%_datadir/%name"
-%pre
+%pre -f user.pre
+%if 0%{?rhel} || 0%{?fedora_version}
+getent group groindex >/dev/null || %_sbindir/groupadd -r groindex
+getent passwd groindex >/dev/null || %_sbindir/useradd -g groindex -s
/bin/false \
+ -r -c "user for %name" -d / groindex
+usermod groindex -aG groweb
+usermod groindex -aG gromoxcf
+%endif
+%if 0%{?service_add_pre:1}
%service_add_pre %services
+%endif
%post
+find /var/lib/grommunio-web/sqlite-index/ -mindepth 1 "(" -type d -o -type f
")" -exec chmod g+w,o-w {} + || :
+find /var/lib/grommunio-web/sqlite-index/ -mindepth 1 "(" -type d -o -type f
")" -exec chgrp -h groweb {} + || :
+%if 0%{?service_add_post:1}
%service_add_post %services
-if test -x /bin/systemctl; then
- systemctl enable --now grommunio-index.timer || :
-fi
+%else
+%systemd_post %services
+%endif
%preun
+%if 0%{?service_del_preun:1}
%service_del_preun %services
+%else
+%systemd_preun %services
+%endif
%postun
+%if 0%{?service_del_postun:1}
%service_del_postun %services
+%else
+%systemd_postun_with_restart %services
+%endif
%files
%_bindir/grommunio-index*
-%_sbindir/grommunio-index*
%_datadir/%name/
+%_sysusersdir/*.conf
%_unitdir/*
%license LICENSE.txt
++++++ _service ++++++
--- /var/tmp/diff_new_pack.4DGvsW/_old 2024-06-14 19:07:45.195847880 +0200
+++ /var/tmp/diff_new_pack.4DGvsW/_new 2024-06-14 19:07:45.199848023 +0200
@@ -1,16 +1,15 @@
<services>
- <service name="tar_scm" mode="disabled">
+ <service name="tar_scm" mode="manual">
<param name="scm">git</param>
<param
name="url">https://github.com/grommunio/grommunio-index</param>
<param name="filename">grommunio-index</param>
<param name="revision">master</param>
- <param name="parent-tag">0.1</param>
<param name="versionformat">@PARENT_TAG@.@TAG_OFFSET@.%h</param>
</service>
- <service name="recompress" mode="disabled">
+ <service name="recompress" mode="manual">
<param name="file">*.tar</param>
<param name="compression">xz</param>
</service>
- <service name="set_version" mode="disabled"/>
+ <service name="set_version" mode="manual"/>
</services>
++++++ debian.changelog ++++++
--- /var/tmp/diff_new_pack.4DGvsW/_old 2024-06-14 19:07:45.227849019 +0200
+++ /var/tmp/diff_new_pack.4DGvsW/_new 2024-06-14 19:07:45.231849161 +0200
@@ -1,4 +1,4 @@
-grommunio-index (0.1.18.6a0f73a) unstable; urgency=low
+grommunio-index (1.0.6.f40d25b) unstable; urgency=low
* Initial package.
++++++ debian.control ++++++
--- /var/tmp/diff_new_pack.4DGvsW/_old 2024-06-14 19:07:45.255850013 +0200
+++ /var/tmp/diff_new_pack.4DGvsW/_new 2024-06-14 19:07:45.259850156 +0200
@@ -10,6 +10,8 @@
Package: grommunio-index
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}
+Requires: system-user-groindex,
+ system-group-groweb, system-group-gromoxcf
Description: Generator for grommunio-web search indexes
.
++++++ debian.grommunio-index.postinst ++++++
usermod grommunio -aG groweb || :
find /var/lib/grommunio-web/sqlite-index/ -mindepth 1 "(" -type d -o -type f
")" -exec chmod g+w,o-w {} + || :
find /var/lib/grommunio-web/sqlite-index/ -mindepth 1 "(" -type d -o -type f
")" -exec chgrp -h groweb {} + || :
++++++ grommunio-index-0.1.18.6a0f73a.tar.xz ->
grommunio-index-1.0.6.f40d25b.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/grommunio-index-0.1.18.6a0f73a/CMakeLists.txt
new/grommunio-index-1.0.6.f40d25b/CMakeLists.txt
--- old/grommunio-index-0.1.18.6a0f73a/CMakeLists.txt 2023-01-16
20:03:36.000000000 +0100
+++ new/grommunio-index-1.0.6.f40d25b/CMakeLists.txt 2024-06-06
09:43:59.000000000 +0200
@@ -1,15 +1,24 @@
cmake_minimum_required(VERSION 3.14)
-project(grommunio-index VERSION 0.1)
+project(grommunio-index VERSION 1.0)
set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(SQLite3 REQUIRED)
find_package(exmdbpp 1.8 REQUIRED)
find_package(PkgConfig REQUIRED)
+pkg_get_variable(SYSUSERDIR systemd sysusersdir)
pkg_get_variable(UNITDIR systemd systemdsystemunitdir)
+pkg_check_modules(HX REQUIRED "libHX >= 3.27")
+find_program(MARIADB_CONFIG NAMES mariadb_config mysql_config)
+execute_process(COMMAND ${MARIADB_CONFIG} --cflags OUTPUT_VARIABLE
MYSQL_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
+separate_arguments(MYSQL_CFLAGS UNIX_COMMAND "${MYSQL_CFLAGS}")
+execute_process(COMMAND ${MARIADB_CONFIG} --libs OUTPUT_VARIABLE
MYSQL_LIBRARIES OUTPUT_STRIP_TRAILING_WHITESPACE)
+separate_arguments(MYSQL_LIBRARIES UNIX_COMMAND "${MYSQL_LIBRARIES}")
add_executable(grommunio-index grommunio-index.cpp)
target_include_directories(grommunio-index PRIVATE ${SQLite3_INCLUDE_DIRS})
-target_link_libraries(grommunio-index ${SQLite3_LIBRARIES} exmdbpp::exmdbpp)
+target_compile_options(grommunio-index PRIVATE ${HX_CFLAGS} ${MYSQL_CFLAGS}
-Wall)
+target_link_libraries(grommunio-index ${HX_LIBRARIES} ${MYSQL_LIBRARIES}
${SQLite3_LIBRARIES} exmdbpp::exmdbpp)
option(LOGGING_TRACE "Enable TRACE logging level" OFF)
if(LOGGING_TRACE)
@@ -17,5 +26,5 @@
endif()
install(TARGETS grommunio-index RUNTIME)
-install(PROGRAMS grommunio-index-run.sh DESTINATION sbin)
install(FILES grommunio-index.service grommunio-index.timer DESTINATION
${UNITDIR})
+install(FILES system-user-groindex.conf DESTINATION ${SYSUSERDIR})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/grommunio-index-0.1.18.6a0f73a/README.rst
new/grommunio-index-1.0.6.f40d25b/README.rst
--- old/grommunio-index-0.1.18.6a0f73a/README.rst 2023-01-16
20:03:36.000000000 +0100
+++ new/grommunio-index-1.0.6.f40d25b/README.rst 2024-06-06
09:43:59.000000000 +0200
@@ -3,12 +3,12 @@
A C++17 program for the generation of grommunio-web fulltext search indexes.
-|shield-agpl|_ |shield-release|_ |shield-cov|_ |shield-loc|
+|shield-agpl| |shield-release| |shield-loc|
.. |shield-agpl| image:: https://img.shields.io/badge/license-AGPL--3.0-green
-.. _shield-agpl: LICENSE.txt
+ :target: LICENSE.txt
.. |shield-release| image::
https://shields.io/github/v/tag/grommunio/grommunio-index
-.. _shield-release: https://github.com/grommunio/grommunio-index/tags
+ :target: https://github.com/grommunio/grommunio-index/tags
.. |shield-loc| image::
https://img.shields.io/github/languages/code-size/grommunio/grommunio-index
Support
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/grommunio-index-0.1.18.6a0f73a/changelog.rst
new/grommunio-index-1.0.6.f40d25b/changelog.rst
--- old/grommunio-index-0.1.18.6a0f73a/changelog.rst 1970-01-01
01:00:00.000000000 +0100
+++ new/grommunio-index-1.0.6.f40d25b/changelog.rst 2024-06-06
09:43:59.000000000 +0200
@@ -0,0 +1,7 @@
+v1.0 (2023-12-31)
+=================
+
+Behavioral changes:
+
+* Merge grommunio-index-run.sh function in grommunio-index itself;
+ new -A command-line option
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/grommunio-index-0.1.18.6a0f73a/grommunio-index-run.sh
new/grommunio-index-1.0.6.f40d25b/grommunio-index-run.sh
--- old/grommunio-index-0.1.18.6a0f73a/grommunio-index-run.sh 2023-01-16
20:03:36.000000000 +0100
+++ new/grommunio-index-1.0.6.f40d25b/grommunio-index-run.sh 1970-01-01
01:00:00.000000000 +0100
@@ -1,23 +0,0 @@
-#!/bin/bash
-
-MYSQL_CFG="/etc/gromox/mysql_adaptor.cfg"
-
-if [ ! -e "${MYSQL_CFG}" ] ; then
- echo "MySQL configuration not found. ($MYSQL_CFG)"
- exit 1
-fi
-
-mysql_params="--skip-column-names --skip-line-numbers"
-mysql_username=$(sed -ne 's/mysql_username\s*=\s*\(.*\)/-u\1/p' ${MYSQL_CFG})
-mysql_password=$(sed -ne 's/mysql_password\s*=\s*\(.*\)/-p\1/p' ${MYSQL_CFG})
-mysql_dbname=$(sed -ne 's/mysql_dbname\s*=\s*\(.*\)/\1/p' ${MYSQL_CFG})
-mysql_host=$(sed -ne 's/mysql_host\s*=\s*\(.*\)/-h\1/p' ${MYSQL_CFG})
-mysql_port=$(sed -ne 's/mysql_port\s*=\s*\(.*\)/-P\1/p' ${MYSQL_CFG})
-mysql_query='select username, maildir from users where id <> 0 and maildir <>
"";'
-mysql_cmd="mysql ${mysql_params} ${mysql_username} ${mysql_password}
${mysql_host} ${mysql_port} ${mysql_dbname}"
-web_index_path="/var/lib/grommunio-web/sqlite-index"
-
-echo "${mysql_query[@]}" | ${mysql_cmd} | while read -r username maildir ; do
- [ -e "${web_index_path}/${username}/" ] || mkdir
"${web_index_path}/${username}/"
- grommunio-index "$maildir" -o "$web_index_path/$username/index.sqlite3"
-done
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/grommunio-index-0.1.18.6a0f73a/grommunio-index.cpp
new/grommunio-index-1.0.6.f40d25b/grommunio-index.cpp
--- old/grommunio-index-0.1.18.6a0f73a/grommunio-index.cpp 2023-01-16
20:03:36.000000000 +0100
+++ new/grommunio-index-1.0.6.f40d25b/grommunio-index.cpp 2024-06-06
09:43:59.000000000 +0200
@@ -1,24 +1,98 @@
/*
* SPDX-License-Identifier: AGPL-3.0-or-later
- * SPDX-FileCopyrightText: 2022 grommunio GmbH
+ * SPDX-FileCopyrightText: 2022-2023 grommunio GmbH
*/
#include <algorithm>
+#include <array>
+#include <cerrno>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
#include <filesystem>
+#include <getopt.h>
#include <iostream>
+#include <limits>
+#include <map>
+#include <memory>
+#include <mysql.h>
#include <optional>
+#include <sqlite3.h>
+#include <stdexcept>
#include <string>
-
+#include <string_view>
+#include <unistd.h>
+#include <unordered_map>
+#include <utility>
+#include <vector>
#include <exmdbpp/constants.h>
#include <exmdbpp/queries.h>
#include <exmdbpp/requests.h>
#include <exmdbpp/util.h>
-#include <sqlite3.h>
+#include <libHX/proc.h>
+#include <libHX/string.h>
+#include <sys/stat.h>
-
-using namespace std;
+using namespace std::string_literals;
using namespace exmdbpp;
using namespace exmdbpp::constants;
using namespace exmdbpp::queries;
+namespace fs = std::filesystem;
+
+namespace {
+
+using DB_ROW = char **;
+
+class DB_RESULT { /* from gromox/database_mysql.hpp */
+ public:
+ DB_RESULT() = default;
+ DB_RESULT(MYSQL_RES *r) noexcept : m_res(r) {}
+ DB_RESULT(DB_RESULT &&o) noexcept : m_res(o.m_res) { o.m_res = nullptr;
}
+ ~DB_RESULT() { clear(); }
+
+ DB_RESULT &operator=(DB_RESULT &&o) noexcept
+ {
+ clear();
+ m_res = o.m_res;
+ o.m_res = nullptr;
+ return *this;
+ }
+ void clear() {
+ if (m_res != nullptr)
+ mysql_free_result(m_res);
+ m_res = nullptr;
+ }
+ operator bool() const noexcept { return m_res != nullptr; }
+ bool operator==(std::nullptr_t) const noexcept { return m_res ==
nullptr; }
+ bool operator!=(std::nullptr_t) const noexcept { return m_res !=
nullptr; }
+ MYSQL_RES *get() const noexcept { return m_res; }
+ void *release() noexcept
+ {
+ void *p = m_res;
+ m_res = nullptr;
+ return p;
+ }
+
+ size_t num_rows() const { return mysql_num_rows(m_res); }
+ DB_ROW fetch_row() { return mysql_fetch_row(m_res); }
+
+ private:
+ MYSQL_RES *m_res = nullptr;
+};
+
+struct our_del {
+ inline void operator()(FILE *x) const { fclose(x); }
+ inline void operator()(MYSQL *x) const { mysql_close(x); }
+};
+
+struct user_row {
+ std::string username, dir, host;
+};
+
+}
+
+using kvpairs = std::map<std::string, std::string>;
enum {RESULT_OK, RESULT_ARGERR_SYN, RESULT_ARGERR_SEM, RESULT_EXMDB_ERR}; ///<
Exit codes
enum LEVEL {FATAL, ERROR, WARNING, STATUS, INFO, DEBUG, TRACE, LOGLEVELS};
///< Log levels
@@ -52,8 +126,8 @@
{
if(level > verbosity)
return;
- cout << "[" << levelname[level] << "] ";
- (cout << ... << args) << endl;
+ std::cout << "[" << levelname[level] << "] ";
+ (std::cout << ... << args) << std::endl;
}
};
@@ -82,12 +156,12 @@
* @returns unordered_map containing objects
*/
template<typename K, typename V, class InputIt, typename F>
-inline void mkMapMv(InputIt first, InputIt last, unordered_map<K, V>& umap,
F&& key)
+inline void mkMapMv(InputIt first, InputIt last, std::unordered_map<K, V>&
umap, F&& key)
{
umap.clear();
umap.reserve(distance(first, last));
for(; first != last; ++first)
- umap.try_emplace(key(*first), move(*first));
+ umap.try_emplace(key(*first), std::move(*first));
}
/**
@@ -109,11 +183,11 @@
* @return Joined string
*/
template<class InputIt, typename F>
-inline string strjoin(InputIt first, InputIt last, const std::string_view&
glue = "",F&& tf=[](InputIt it){return *it;})
+inline std::string strjoin(InputIt first, InputIt last, const
std::string_view& glue = "",F&& tf=[](InputIt it){return *it;})
{
if(first == last)
- return string();
- string str(tf(first));
+ return std::string();
+ std::string str(tf(first));
while(++first != last)
{
str += glue;
@@ -133,9 +207,9 @@
* @return Reference to dest
*/
template<typename... Args>
-inline string& strjoin(string& dest, const Args&... args)
+inline std::string& strjoin(std::string& dest, Args&&... args)
{
- ((dest += args), ...);
+ ((dest += std::forward<Args>(args)), ...);
return dest;
}
@@ -167,7 +241,7 @@
* @tparam tag { description }
*/
template<uint32_t tag>
-inline void addTagStrLine(string& dest, const ExmdbQueries::PropvalList& pl)
+inline void addTagStrLine(std::string& dest, const ExmdbQueries::PropvalList&
pl)
{
static_assert(PropvalType::tagType(tag) != PropvalType::STRING ||
PropvalType::tagType(tag) != PropvalType::WSTRING,
"Can only add string tags");
@@ -194,11 +268,11 @@
if(db)
sqlite3_close(db);
recheck = other.recheck;
- dbpath = move(other.dbpath);
- usrpath = move(other.usrpath);
- client = move(other.client);
- reuse = move(other.reuse);
- namedProptags = move(other.namedProptags);
+ dbpath = std::move(other.dbpath);
+ usrpath = std::move(other.usrpath);
+ client = std::move(other.client);
+ reuse = std::move(other.reuse);
+ namedProptags = std::move(other.namedProptags);
db = other.db;
update = other.update;
other.db = nullptr;
@@ -224,7 +298,7 @@
* @param exmdbPort Port for exmdb connection
* @param outpath Path of the output database or empty for
default
*/
- IndexDB(const filesystem::path& userdir, const string& exmdbHost, const
string& exmdbPort, const string& outpath,
+ IndexDB(const fs::path& userdir, const std::string& exmdbHost, const
std::string& exmdbPort, const std::string& outpath,
bool create=false, bool recheck=false) :
usrpath(userdir), client(exmdbHost, exmdbPort, userdir, true,
ExmdbClient::AUTO_RECONNECT),
recheck(recheck)
@@ -233,24 +307,24 @@
{
dbpath = userdir;
dbpath /= "exmdb";
- if(!filesystem::exists(dbpath))
- throw runtime_error("Cannot access "s +
dbpath.c_str() + " (absent or permission problem)");
+ if(!fs::exists(dbpath))
+ throw std::runtime_error("Cannot access "s +
dbpath.c_str() + " (absent or permission problem)");
dbpath /= "index.sqlite3";
}
else
{
dbpath = outpath;
- if(filesystem::is_directory(dbpath))
+ if(fs::is_directory(dbpath))
dbpath /= "index.sqlite3";
}
- update = filesystem::exists(dbpath);
+ update = fs::exists(dbpath);
if (update)
msg<STATUS>("Updating existing index "s +
dbpath.c_str());
else
msg<STATUS>("Creating new index "s + dbpath.c_str());
int res = sqlite3_open(dbpath.c_str(), &db);
if(res != SQLITE_OK)
- throw runtime_error(string("Failed to open index
database: ")+sqlite3_errmsg(db));
+ throw std::runtime_error("Failed to open index
database: "s + sqlite3_errmsg(db));
if(update && create)
{
sqliteExec("DROP TABLE IF EXISTS hierarchy;"
@@ -273,7 +347,7 @@
" date UNINDEXED, "
" tokenize=unicode61)");
if(res != SQLITE_OK)
- throw runtime_error(string("Failed to initialize index
database: ")+sqlite3_errmsg(db));
+ throw std::runtime_error("Failed to initialize index
database: "s + sqlite3_errmsg(db));
}
/**
@@ -301,7 +375,7 @@
}
private:
- using PropvalMap = unordered_map<uint32_t, structures::TaggedPropval>;
///< Tag ID -> TaggedPropval mapping
+ using PropvalMap = std::unordered_map<uint32_t,
structures::TaggedPropval>; ///< Tag ID -> TaggedPropval mapping
/**
* @brief SQLite3 statement wrapper class
@@ -322,7 +396,7 @@
{
int res = sqlite3_prepare_v2(db, zSql, -1, &stmt,
nullptr);
if(res != SQLITE_OK)
- throw runtime_error(sqlite3_errmsg(db));
+ throw std::runtime_error(sqlite3_errmsg(db));
}
~SQLiteStmt() {sqlite3_finalize(stmt);}
@@ -348,9 +422,9 @@
template<typename... fArgs, typename... cArgs>
void call(int(*func)(sqlite3_stmt*, fArgs...), cArgs&&... args)
{
- int res = func(stmt, args...);
+ int res = func(stmt, std::forward<cArgs>(args)...);
if(res != SQLITE_OK)
- throw
runtime_error(sqlite3_errmsg(sqlite3_db_handle(stmt)));
+ throw
std::runtime_error(sqlite3_errmsg(sqlite3_db_handle(stmt)));
}
/**
@@ -367,8 +441,8 @@
{
if(str.size() == 0)
return call(sqlite3_bind_null, index);
- if(str.size() > numeric_limits<int>::max())
- throw out_of_range("String lengths exceeds
maximum");
+ if(str.size() > std::numeric_limits<int>::max())
+ throw std::out_of_range("String lengths exceeds
maximum");
call(sqlite3_bind_text, index, str.data(),
int(str.size()), SQLITE_STATIC);
}
@@ -396,7 +470,7 @@
{
int index = sqlite3_bind_parameter_index(stmt, name);
if(!index)
- throw out_of_range(string("Cannot find named
bind parameter ")+name);
+ throw std::out_of_range("Cannot find named bind
parameter "s + name);
return index;
}
@@ -413,7 +487,7 @@
{
int result = sqlite3_step(stmt);
if(result != SQLITE_DONE && result != SQLITE_ROW)
- throw runtime_error("SQLite query failed:
"+to_string(result));
+ throw std::runtime_error("SQLite query failed:
" + std::to_string(result));
return result;
}
@@ -425,7 +499,7 @@
*/
struct Message
{
- inline Message(uint64_t mid, uint64_t fid,
structures::TaggedPropval& entryid) : mid(mid), fid(fid),
entryid(move(entryid)) {}
+ inline Message(uint64_t mid, uint64_t fid,
structures::TaggedPropval& entryid) : mid(mid), fid(fid),
entryid(std::move(entryid)) {}
uint64_t mid, fid;
structures::TaggedPropval entryid;
@@ -442,7 +516,7 @@
struct
{
- string attchs, body, other, rcpts, sender, sending, subject,
messageclass;
+ std::string attchs, body, other, rcpts, sender, sending,
subject, messageclass;
PropvalMap props;
void reset()
@@ -452,8 +526,8 @@
}
} reuse; ///< Objects that can be reused to save on memory allocations
- static array<structures::PropertyName, 14> namedTags; ///< Array of
named tags to query
- static constexpr array<uint16_t, 14> namedTagTypes = {
+ static std::array<structures::PropertyName, 14> namedTags; ///< Array
of named tags to query
+ static constexpr std::array<uint16_t, 14> namedTagTypes = {
PropvalType::STRING_ARRAY,
PropvalType::STRING, PropvalType::STRING, PropvalType::STRING,
PropvalType::STRING, PropvalType::STRING, PropvalType::STRING,
@@ -463,7 +537,7 @@
PropvalType::STRING_ARRAY
}; ///< Types of the named tags
- static constexpr array<uint32_t, 12> msgtags1 = {
+ static constexpr std::array<uint32_t, 12> msgtags1 = {
PropTag::ENTRYID, PropTag::SENTREPRESENTINGNAME,
PropTag::SENTREPRESENTINGSMTPADDRESS,
PropTag::SUBJECT, PropTag::BODY, PropTag::SENDERNAME,
PropTag::SENDERSMTPADDRESS, PropTag::INTERNETCODEPAGE,
@@ -471,7 +545,7 @@
PropTag::MESSAGEDELIVERYTIME, PropTag::LASTMODIFICATIONTIME
}; ///< Part 1 of message tags to query
- static constexpr array<uint32_t, 19> msgtags2 = {
+ static constexpr std::array<uint32_t, 19> msgtags2 = {
PropTag::DISPLAYNAME, PropTag::DISPLAYNAMEPREFIX,
PropTag::HOMETELEPHONENUMBER,
PropTag::MOBILETELEPHONENUMBER, PropTag::BUSINESSTELEPHONENUMBER,
PropTag::BUSINESSFAXNUMBER, PropTag::ASSISTANTTELEPHONENUMBER,
@@ -482,10 +556,10 @@
PropTag::PRIMARYTELEPHONENUMBER, PropTag::RADIOTELEPHONENUMBER,
PropTag::TELEXNUMBER,
}; ///< Part 2 of message tags to query
- filesystem::path usrpath; ///< Path to the user's home directory
- filesystem::path dbpath; ///< Path to the index database
+ fs::path usrpath; ///< Path to the user's home directory
+ fs::path dbpath; ///< Path to the index database
ExmdbClient client; ///< Exmdb client to use
- vector<uint32_t> namedProptags; ///< Store specific named proptag IDs
+ std::vector<uint32_t> namedProptags; ///< Store specific named proptag
IDs
sqlite3* db = nullptr; ///< SQLite database connection
bool update = false; ///< Whether index is updated
bool recheck = false; ///< Whether to check all folders regardless of
timestamp
@@ -521,7 +595,7 @@
{
auto res = find_if(tplist.begin(), tplist.end(), [tag](const
structures::TaggedPropval& t){return t.tag == tag;});
if(res == tplist.end())
- throw out_of_range("Failed to find tag.");
+ throw std::out_of_range("Failed to find tag.");
return *res;
}
@@ -530,7 +604,7 @@
*
* @return Pair of messages and hierarchy entries to update
*/
- pair<vector<Message>, vector<Hierarchy>> getUpdates()
+ std::pair<std::vector<Message>, std::vector<Hierarchy>> getUpdates()
{
using namespace exmdbpp::constants;
using namespace exmdbpp::requests;
@@ -546,9 +620,9 @@
client.send<UnloadTableRequest>(usrpath, lhtResponse.tableId);
msg<DEBUG>("Loaded ", qtResponse.entries.size(), " folders");
SQLiteStmt stmt(db, "SELECT commit_max, max_cn FROM hierarchy
WHERE folder_id=?");
- vector<Message> messages;
- vector<Hierarchy> hierarchy;
- for(auto& entry : qtResponse.entries)
+ std::vector<Message> messages;
+ std::vector<Hierarchy> hierarchy;
+ for(auto& entry : qtResponse.entries) try
{
uint64_t lastCn = 0, maxCn = 0;
uint64_t folderIdGc = getTag(entry,
PropTag::FOLDERID).value.u64;
@@ -577,12 +651,15 @@
uint64_t cn = util::gcToValue(getTag(content,
PropTag::CHANGENUMBER).value.u64);
if(cn <= lastCn)
continue;
- maxCn = max(maxCn, cn);
+ maxCn = std::max(maxCn, cn);
messages.emplace_back(getTag(content,
PropTag::MID).value.u64, folderId, getTag(content, PropTag::ENTRYID));
}
msg<TRACE>("Checked folder ", folderId, " with ",
contents.entries.size(), " messages. ",
"Total updates now at ", messages.size(),
".");
hierarchy.emplace_back(folderId, lctm, maxCn);
+ } catch (const std::out_of_range &e) {
+ msg<ERROR>("An essential property was missing from a
folder; cannot proceed");
+ throw EXIT_FAILURE;
}
msg<INFO>("Need to update ", messages.size(), " message",
messages.size() == 1? "": "s",
" and ", hierarchy.size(), " hierarchy entr",
hierarchy.size() == 1? "y" : "ies", '.');
@@ -596,7 +673,7 @@
*/
void removeMessages(const std::vector<Message>& messages)
{
- msg<DEBUG>("Removing modified messages");
+ msg<DEBUG>("Removing ", messages.size(), " modified messages");
if(!update)
return;
SQLiteStmt stmt(db, "DELETE FROM messages WHERE message_id=?");
@@ -615,11 +692,12 @@
*
* @param messages Messages to insert
*/
- void insertMessages(const vector<Message>& messages)
+ void insertMessages(const std::vector<Message>& messages)
{
msg<DEBUG>("Inserting new messages");
if(messages.empty())
return;
+ client.reconnect();
namedProptags = getNamedProptags();
std::vector<uint32_t> msgtags;
msgtags.resize(namedProptags.size()+msgtags1.size()+msgtags2.size());
@@ -635,7 +713,7 @@
for(const Message& message : messages)
{
try {insertMessage(stmt, message, msgtags);}
- catch (exception& e)
+ catch (const std::exception& e)
{msg<ERROR>("Failed to insert message ", message.fid,
"/", util::gcToValue(message.mid), ": ", e.what());}
}
sqliteExec("COMMIT");
@@ -648,7 +726,7 @@
* @param message Message to insert
* @param msgtags List of tag IDs to query
*/
- void insertMessage(SQLiteStmt& stmt, const Message& message, const
vector<uint32_t>& msgtags)
+ void insertMessage(SQLiteStmt& stmt, const Message& message, const
std::vector<uint32_t>& msgtags)
{
using namespace constants;
using namespace requests;
@@ -657,7 +735,7 @@
reuse.reset();
stmt.call(sqlite3_reset);
uint32_t instance =
client.send<LoadMessageInstanceRequest>(usrpath, "", 65001, false, 0,
message.mid).instanceId;
- auto rcpts =
client.send<GetMessageInstanceRecipientsRequest>(usrpath, instance, 0,
numeric_limits<uint16_t>::max());
+ auto rcpts =
client.send<GetMessageInstanceRecipientsRequest>(usrpath, instance, 0,
std::numeric_limits<uint16_t>::max());
auto attchs =
client.send<QueryMessageInstanceAttachmentsTableRequest>(usrpath, instance,
attchProps, 0, 0);
auto propvals =
client.send<GetInstancePropertiesRequest>(usrpath, 0, instance,
msgtags).propvals;
client.send<UnloadInstanceRequest>(usrpath, instance);
@@ -728,7 +806,7 @@
using namespace requests;
auto response = client.send<GetNamedPropIdsRequest>(usrpath,
false, namedTags);
if(response.propIds.size() != namedTagTypes.size())
- throw out_of_range("Number of named property IDs does
not match expected count");
+ throw std::out_of_range("Number of named property IDs
does not match expected count");
std::vector<uint32_t> propTags(namedTagTypes.size());
transform(namedTagTypes.begin(), namedTagTypes.end(),
response.propIds.begin(), propTags.begin(),
[](uint16_t id, uint16_t type) {return uint32_t(id)
<< 16 | type;});
@@ -756,7 +834,7 @@
}
};
-array<structures::PropertyName, 14> IndexDB::namedTags = {
+std::array<structures::PropertyName, 14> IndexDB::namedTags = {
structures::PropertyName(structures::GUID("00020329-0000-0000-C000-000000000046"),
"Keywords"), //categories
structures::PropertyName(structures::GUID("00062004-0000-0000-C000-000000000046"),
0x8005), //fileas
structures::PropertyName(structures::GUID("00062002-0000-0000-C000-000000000046"),
0x8208), //location
@@ -775,12 +853,13 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
-static string exmdbHost; ///< Exmdb host to connect to
-static string exmdbPort; ///< Port of the exmdb connection
-static optional<string> userpath; ///< Path to the user's home directory
-static string outpath; ///< Index database path (empty for default)
+static std::string exmdbHost; ///< Exmdb host to connect to
+static std::string exmdbPort; ///< Port of the exmdb connection
+static std::optional<std::string> userpath; ///< Path to the user's home
directory
+static std::string outpath; ///< Index database path (empty for default)
static bool recheck = false; ///< Check folders even when they were not
changed since the last indexing
static bool create = false; ///< Always create a new index instead of updating
+static bool do_all_users;
/**
* @brief Print help message
@@ -789,11 +868,13 @@
*/
[[noreturn]] static void printHelp(const char* name)
{
- cout << "grommunio mailbox indexing tool\n"
+ std::cout << "grommunio mailbox indexing tool\n"
"\nUsage: " << name << " [-c] [-e host] [-f] [-h] [-o file] [-p
port] [-q] [-v] <userpath>\n"
+ "Usage: " << name << " -A [-c] [-f] [-h] [-p port] [-q]
[-v]\n"
"\nPositional arguments:\n"
"\t userpath\t\tPath to the user's mailbox directory\n"
"\nOptional arguments:\n"
+ "\t-A\t--all \tAutomatically process all local users (-e, -o
ignored)\n"
"\t-c\t--create \tCreate a new index instead of updating\n"
"\t-e\t--host \tHostname of the exmdb server\n"
"\t-h\t--help \tShow this help message and exit\n"
@@ -806,108 +887,196 @@
}
/**
- * @brief Advance by one command line argument
- *
- * If there are no more arguments, exit with error.
- *
- * @param cur Pointer to current argument
- *
- * @return Next argument
- */
-static const char* nextArg(const char** &cur)
-{
- if(*++cur == nullptr)
- {
- msg<FATAL>("Missing option argument after '", *(cur-1), "'");
- exit(RESULT_ARGERR_SYN);
- }
- return *cur;
-}
-
-/**
* @brief Parse command line arguments
*
* Does not return in case of error.
*
* @param argv nullptr-terminated array of command line arguments
*/
-static void parseArgs(const char* argv[])
+static void parseArgs(int argc, char **argv)
{
- bool noopt = false;
- for(const char** argp = argv+1; *argp != nullptr; ++argp)
- {
- const char* arg = *argp;
- if(noopt || *arg == '-')
- {
- if(*(++arg) == '-')
- {
- ++arg;
- if(!strcmp(arg, "create")) create = true;
- else if(!strcmp(arg, "help"))
printHelp(*argv);
- else if(!strcmp(arg, "host")) exmdbHost =
nextArg(argp);
- else if(!strcmp(arg, "out")) outpath =
nextArg(argp);
- else if(!strcmp(arg, "port")) exmdbPort =
nextArg(argp);
- else if(!strcmp(arg, "quiet")) --verbosity;
- else if(!strcmp(arg, "recheck")) recheck
= true;
- else if(!strcmp(arg, "verbose"))++verbosity;
- else if(!*arg) noopt = true;
- else
- {
- msg<FATAL>("Unknown option '", arg,
"'");
- exit(RESULT_ARGERR_SYN);
- }
- }
- else
- for(const char* sopt = arg; *sopt; ++sopt)
- {
- switch(*sopt)
- {
- case 'c': create = true; break;
- case 'e': exmdbHost = nextArg(argp);
break;
- case 'h': printHelp(*argv); //printHelp
never return
- case 'o': outpath = nextArg(argp);
break;
- case 'p': exmdbPort = nextArg(argp);
break;
- case 'q': --verbosity; break;
- case 'r': recheck = true; break;
- case 'v': ++verbosity; break;
- default:
- msg<FATAL>("Unknown short
option '", *sopt, "'");
- exit(RESULT_ARGERR_SYN);
- }
- }
- }
- else if(userpath.has_value())
- {
- msg<FATAL>("Too many arguments.");
+ static const struct option longopts[] = {
+ {"all", false, nullptr, 'A'},
+ {"create", false, nullptr, 'c'},
+ {"host", true, nullptr, 'e'},
+ {"help", false, nullptr, 'h'},
+ {"outpath", true, nullptr, 'o'},
+ {"port", true, nullptr, 'p'},
+ {"quiet", false, nullptr, 'q'},
+ {"recheck", false, nullptr, 'r'},
+ {"verbose", false, nullptr, 'v'},
+ {},
+ };
+
+ int c;
+ while ((c = getopt_long(argc, argv, "Ace:ho:p:qrv", longopts, nullptr))
>= 0) {
+ switch (c) {
+ case 'A': do_all_users = true; break;
+ case 'c': create = true; break;
+ case 'e': exmdbHost = optarg; break;
+ case 'h': printHelp(*argv); break;
+ case 'o': outpath = optarg; break;
+ case 'p': exmdbPort = optarg; break;
+ case 'q': --verbosity; break;
+ case 'r': recheck = true; break;
+ case 'v': ++verbosity; break;
+ default:
exit(RESULT_ARGERR_SYN);
}
- else
- userpath.emplace(arg);
}
- if(!userpath.has_value())
- {
- msg<FATAL>("Usage: grommunio-index MAILDIR");
- msg<STATUS>("Option overview: grommunio-index -h");
- exit(RESULT_ARGERR_SYN);
+ if (do_all_users) {
+ if (!exmdbHost.empty() || !outpath.empty() || argc > optind) {
+ msg<FATAL>("Cannot combine -A with -e/-o/userpath");
+ exit(RESULT_ARGERR_SYN);
+ }
+ } else {
+ if (argc > optind)
+ userpath.emplace(argv[optind++]);
+ if (!userpath.has_value()) {
+ msg<FATAL>("Usage: grommunio-index MAILDIR");
+ msg<STATUS>("Option overview: grommunio-index -h");
+ exit(RESULT_ARGERR_SYN);
+ }
}
if(exmdbHost.empty())
exmdbHost = "localhost";
if(exmdbPort.empty())
exmdbPort = "5000";
- verbosity = min(max(verbosity, 0), LOGLEVELS-1);
+ verbosity = std::min(std::max(verbosity, 0), LOGLEVELS-1);
}
-int main(int, const char* argv[])
+static int single_mode()
{
- parseArgs(argv);
msg<DEBUG>("exmdb=", exmdbHost, ":", exmdbPort, ", user=",
userpath.value(), ", output=", outpath.empty()? "<default>" : outpath);
IndexDB cache;
try {
cache = IndexDB(userpath.value(), exmdbHost, exmdbPort,
outpath, create, recheck);
cache.refresh();
- } catch(const runtime_error& err) {
+ } catch(const std::runtime_error& err) {
msg<FATAL>(err.what());
return RESULT_ARGERR_SEM;
}
return 0;
}
+
+static kvpairs am_read_config(const char *path)
+{
+ std::unique_ptr<FILE, our_del> fp(fopen(path, "r"));
+ if (fp == nullptr) {
+ fprintf(stderr, "%s: %s\n", path, strerror(errno));
+ throw EXIT_FAILURE;
+ }
+ kvpairs vars;
+ hxmc_t *ln = nullptr;
+ while (HX_getl(&ln, fp.get()) != nullptr) {
+ auto eq = strchr(ln, '=');
+ if (eq == nullptr)
+ continue;
+ if (*ln == '#')
+ continue;
+ HX_chomp(ln);
+ *eq++ = '\0';
+ HX_strrtrim(ln);
+ HX_strltrim(eq);
+ vars[ln] = eq;
+ }
+ HXmc_free(ln);
+ /* fill with defaults if empty */
+ vars.try_emplace("mysql_username", "root");
+ vars.try_emplace("mysql_dbname", "grommunio");
+ vars.try_emplace("mysql_host", "localhost");
+ return vars;
+}
+
+static std::vector<user_row> am_read_users(kvpairs &&vars)
+{
+ std::unique_ptr<MYSQL, our_del> conn(mysql_init(nullptr));
+ if (conn == nullptr)
+ throw EXIT_FAILURE;
+ auto pass = vars.find("mysql_password");
+ if (mysql_real_connect(conn.get(), vars["mysql_host"].c_str(),
+ vars["mysql_username"].c_str(), pass != vars.end() ?
pass->second.c_str() : nullptr,
+ vars["mysql_dbname"].c_str(), strtoul(vars["mysql_port"].c_str(),
nullptr, 0),
+ nullptr, 0) == nullptr) {
+ fprintf(stderr, "mysql_connect: %s\n", mysql_error(conn.get()));
+ throw EXIT_FAILURE;
+ }
+ if (mysql_set_character_set(conn.get(), "utf8mb4") != 0) {
+ fprintf(stderr, "\"utf8mb4\" not available: %s",
mysql_error(conn.get()));
+ throw EXIT_FAILURE;
+ }
+
+ static constexpr char query[] =
+ "SELECT u.username, u.maildir, s.hostname FROM users u "
+ "LEFT JOIN servers s ON u.homeserver=s.id WHERE u.id>0";
+ if (mysql_query(conn.get(), query) != 0) {
+ fprintf(stderr, "%s: %s\n", query, mysql_error(conn.get()));
+ throw EXIT_FAILURE;
+ }
+ DB_RESULT myres = mysql_store_result(conn.get());
+ if (myres == nullptr) {
+ fprintf(stderr, "result: %s\n", mysql_error(conn.get()));
+ throw EXIT_FAILURE;
+ }
+ std::vector<user_row> ulist;
+ for (DB_ROW row; (row = myres.fetch_row()) != nullptr; ) {
+ if (row[0] == nullptr)
+ continue;
+ struct stat sb;
+ if (row[1] == nullptr || stat(row[1], &sb) != 0 ||
!S_ISDIR(sb.st_mode))
+ /* Homedir not present here */
+ continue;
+ auto host = row[2] != nullptr && row[2][0] != '\0' ? row[2] :
"::1";
+ ulist.emplace_back(user_row{row[0], row[1], host});
+ }
+ return ulist;
+}
+
+int main(int argc, char **argv) try
+{
+ parseArgs(argc, argv);
+
+ auto cfg = am_read_config("/etc/gromox/mysql_adaptor.cfg");
+ /* Generated index files should not be world-readable */
+ umask(07);
+ if (!do_all_users)
+ return single_mode();
+
+ if (geteuid() == 0) {
+ auto ret = HXproc_switch_user("groindex", "groweb");
+ if (static_cast<int>(ret) < 0) {
+ fprintf(stderr, "switch_user grommunio/groweb: %s\n",
strerror(errno));
+ return EXIT_FAILURE;
+ }
+ /* setuid often disables coredumps, so restart to get them
back. */
+ execv(argv[0], argv);
+ }
+ int bigret = EXIT_SUCCESS;
+ static const std::string index_root =
"/var/lib/grommunio-web/sqlite-index";
+ for (auto &&u : am_read_users(std::move(cfg))) {
+ auto index_home = index_root + "/" + u.username;
+ if (mkdir(index_home.c_str(), 0777) != 0 && errno != EEXIST) {
+ fprintf(stderr, "mkdir %s: %s\n", index_home.c_str(),
strerror(errno));
+ bigret = EXIT_FAILURE;
+ continue;
+ }
+ auto index_file = index_home + "/index.sqlite3";
+ auto now = time(nullptr);
+ char tmbuf[32];
+ strftime(tmbuf, sizeof(tmbuf), "%FT%T", localtime(&now));
+ fprintf(stderr, "[%s] %s %s -e %s -o %s\n", tmbuf, argv[0],
+ u.dir.c_str(), u.host.c_str(), index_file.c_str());
+ userpath.emplace(std::move(u.dir));
+ exmdbHost = std::move(u.host);
+ outpath = std::move(index_file);
+ auto ret = single_mode();
+ if (ret != 0) {
+ fprintf(stderr, "\t... exited with status %d\n", ret);
+ bigret = EXIT_FAILURE;
+ }
+ fprintf(stderr, "\n");
+ }
+ return bigret;
+} catch (int e) {
+ return e;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/grommunio-index-0.1.18.6a0f73a/grommunio-index.service
new/grommunio-index-1.0.6.f40d25b/grommunio-index.service
--- old/grommunio-index-0.1.18.6a0f73a/grommunio-index.service 2023-01-16
20:03:36.000000000 +0100
+++ new/grommunio-index-1.0.6.f40d25b/grommunio-index.service 2024-06-06
09:43:59.000000000 +0200
@@ -13,5 +13,5 @@
ProtectControlGroups=true
RestrictRealtime=true
Type=oneshot
-User=groweb
-ExecStart=/usr/sbin/grommunio-index-run.sh
+User=groindex
+ExecStart=/usr/bin/grommunio-index -Aq
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/grommunio-index-0.1.18.6a0f73a/grommunio-index.timer
new/grommunio-index-1.0.6.f40d25b/grommunio-index.timer
--- old/grommunio-index-0.1.18.6a0f73a/grommunio-index.timer 2023-01-16
20:03:36.000000000 +0100
+++ new/grommunio-index-1.0.6.f40d25b/grommunio-index.timer 2024-06-06
09:43:59.000000000 +0200
@@ -1,11 +1,10 @@
[Unit]
-Description=Daily regeneration of grommunio FTS indexes
+Description=Recurrent regeneration of grommunio FTS indexes
[Timer]
-OnCalendar=daily
-AccuracySec=12h
-Persistent=true
-RandomizedDelaySec=6000
+OnCalendar=*:0/15
+RandomizedDelaySec=300
+Persistent=false
[Install]
WantedBy=timers.target
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/grommunio-index-0.1.18.6a0f73a/system-user-groindex.conf
new/grommunio-index-1.0.6.f40d25b/system-user-groindex.conf
--- old/grommunio-index-0.1.18.6a0f73a/system-user-groindex.conf
1970-01-01 01:00:00.000000000 +0100
+++ new/grommunio-index-1.0.6.f40d25b/system-user-groindex.conf 2024-06-06
09:43:59.000000000 +0200
@@ -0,0 +1,3 @@
+u groindex - "user for grommunio-index"
+m groindex groweb
+m groindex gromoxcf
++++++ grommunio-index.dsc ++++++
--- /var/tmp/diff_new_pack.4DGvsW/_old 2024-06-14 19:07:45.387854706 +0200
+++ /var/tmp/diff_new_pack.4DGvsW/_new 2024-06-14 19:07:45.391854848 +0200
@@ -1,7 +1,7 @@
Format: 1.0
Source: grommunio-index
Architecture: any
-Version: 0.1.18.6a0f73a
+Version: 1.0.6.f40d25b
DEBTRANSFORM-RELEASE: 1
Maintainer: Grommunio <[email protected]>
Homepage: https://grommunio.com/