Skip to content

Commit

Permalink
Merge branch 'prime'
Browse files Browse the repository at this point in the history
  • Loading branch information
jengelh committed Feb 7, 2025
2 parents 353b6a2 + 97971a6 commit b4902cb
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 53 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
AC_INIT([gromox], [2.40])
AC_INIT([gromox], [2.41])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([build-aux])
AC_PREFIX_DEFAULT([/usr])
Expand Down
30 changes: 30 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
Gromox 2.41 (2025-02-07)
========================

Fixes:

* ldap_adaptor: plug two memory leaks
* exmdb: plug a memory leak related to subscription destruction
* oxcical: do process TRIGGER duration value type for reminders
* midb: resolve flakey update of flagged/replied/forwarded status
* midb: when a MAPI message has changed and requires a new IMAPUID,
convey the expunge of the old IMAPUID much sooner to IMAP clients
* pop3: resolve a NULL deref/crash during RETR command
* imap: restore SEARCH command looking at right portion of a QP-encoded message

Enhancements:

* ews: send flag status to clients
* imap: include username for IMAP actions when imap_cmd_debug logging is
activated
* midb, imap: print asynchronous notification events when imap_cmd_debug>=2
* oxcical: invalid iCal timezone inputs are now logged when
<daemon>_log_level=6 (debug)

Behavioral changes:

* mkprivate: new message stores now have `frightsVisible` set on the calendar
folder, because grommunio-web is picky about the existence of the calendar
folder even if obtaining just freebusy blocks.


Gromox 2.40 (2025-01-28)
========================

Expand Down
5 changes: 3 additions & 2 deletions doc/gromox-exm2eml.8
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.\" SPDX-FileCopyrightText: 2024 grommunio GmbH
.TH gromox\-exm2eml 8 "" "Gromox" "Gromox admin reference"
.SH Name
gromox\-exm2eml \(em Utility to export messages to various formats
gromox\-exm2eml \(em Utility to export Gromox messages to various formats
.SH Synopsis
\fBgromox\-exm2eml \-u\fP \fI[email protected]\fP
[\fIfolder_id\fP\fB:\fP]\fImessage_id\fP
Expand Down Expand Up @@ -39,7 +39,8 @@ Default: 1
Show properties in detail (enhances \fB\-t\fP).
.TP
\fB\-t\fP
Show a diagnostic tree view of the source data as it is being read.
As the source message is read, print a diagnostic tree view of the MAPI
properties to stderr.
.TP
\fB\-u\fP [\fIuser\fP]\fB@\fIdomin.example\fP
Source mail store from which to load the message from. For the public folder of
Expand Down
18 changes: 10 additions & 8 deletions doc/gromox-mt2exm.8
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.\" SPDX-License-Identifier: CC-BY-SA-4.0 or-later
.\" SPDX-FileCopyrightText: 2021-2022 grommunio GmbH
.\" SPDX-FileCopyrightText: 2021-2025 grommunio GmbH
.TH gromox\-mt2exm 8 "" "Gromox" "Gromox admin reference"
.SH Name
gromox\-mt2exm \(em Utility for importing various mail items
gromox\-mt2exm \(em Utility for importing various mail items into Gromox
.SH Synopsis
\fBgromox\-mt2exm\fP [...] [\fB\-Dcpt\fP] \fB-u\fP
[\fIlocalpart\fP]\fB@\fP\fIdomain.example\fP
Expand All @@ -15,17 +15,19 @@ Gromox mail store.
There is no cross-check for already-imported messages at this time. Messages
from the input stream will always generate new messages in the target mailbox.
.PP
Note that messages are always owned by the store they are in. Especially if
Note that messages are always owned by the store they are in. Note that, when
importing to a public store, you may need to set some additional permissions on
that public store's folders so that a particular user is able to modify
messages.
messages after they have been imported. (All import/export utilites use
superuser access and need no permissions.)
.SH Options
.TP
\fB\-B\fP \fIname\fP
For unanchored messages (such as produced by the \-\-only\-obj option of
gromox\-pff2mt and gromox\-kdb2mt), place them in this specific folder. The
accepted names are documented in the gromox\-mbop(8) manpage under section
"Folder specification".
Place unanchored messages (loose objects that do not request placement in any
particular folder) in the folder specified by \-B. The accepted names are
documented in the gromox\-mbop(8) manpage under section "Folder specification".
Unanchored messages can be produced by e.g. gromox\-eml2mt, gromox\-tnef2mt,
gromox\-pff2mt \-\-only\-obj, gromox\-kdb2mt \-\-only\-obj.
.br
Default: \fIdraft\fP
.TP
Expand Down
2 changes: 1 addition & 1 deletion exch/emsmdb/asyncemsmdb_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ int asyncemsmdb_interface_async_wait(uint32_t async_id,
pout->result = ecRejected;
return DISPATCH_SUCCESS;
}
if (emsmdb_interface_check_notify(&pin->acxh)) {
if (emsmdb_interface_notifications_pending(pin->acxh)) {
delete pwait;
pout->flags_out = FLAG_NOTIFICATION_PENDING;
pout->result = ecSuccess;
Expand Down
5 changes: 3 additions & 2 deletions exch/emsmdb/emsmdb_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,16 +186,17 @@ BOOL emsmdb_interface_check_acxh(ACXH *pacxh,
return TRUE;
}

BOOL emsmdb_interface_check_notify(ACXH *pacxh)
bool emsmdb_interface_notifications_pending(ACXH &acxh)
{
auto pacxh = &acxh;
if (pacxh->handle_type != HANDLE_EXCHANGE_ASYNCEMSMDB)
return FALSE;
std::lock_guard gl_hold(g_lock);
auto iter = g_handle_hash.find(pacxh->guid);
if (iter == g_handle_hash.end())
return false;
auto phandle = &iter->second;
return double_list_get_nodes_num(&phandle->notify_list) > 0 ? TRUE : false;
return double_list_get_nodes_num(&phandle->notify_list) > 0;
}

/* called by moh_emsmdb module */
Expand Down
2 changes: 1 addition & 1 deletion exch/emsmdb/emsmdb_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ int emsmdb_interface_async_connect_ex(CXH cxh, ACXH *pacxh);
void emsmdb_interface_unbind_rpc_handle(uint64_t hrpc);
BOOL emsmdb_interface_check_acxh(ACXH *pacxh,
char *username, uint16_t *pcxr, BOOL b_touch);
BOOL emsmdb_interface_check_notify(ACXH *pacxh);
extern bool emsmdb_interface_notifications_pending(ACXH &);
extern void emsmdb_interface_touch_handle(const CXH &);
extern const char *emsmdb_interface_get_username();
extern const GUID *emsmdb_interface_get_handle();
Expand Down
25 changes: 18 additions & 7 deletions exch/exmdb/db_engine.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only WITH linking exception
// SPDX-FileCopyrightText: 2021-2024 grommunio GmbH
// SPDX-FileCopyrightText: 2021-2025 grommunio GmbH
// This file is part of Gromox.
#include <algorithm>
#include <cassert>
Expand Down Expand Up @@ -712,6 +712,11 @@ POPULATING_NODE::~POPULATING_NODE()
free(folder_ids.pll);
}

/**
* Construct pseudo-packets for different clients based on who is watching
* @folder_id/@message_id. The output ID_ARRAYS is referencing db data, so
* watch your lifetimes. (The ID_ARRAYS object must not outlive @db).
*/
static db_conn::ID_ARRAYS db_engine_classify_id_array(const db_base &db,
unsigned int bits, uint64_t folder_id, uint64_t message_id) try
{
Expand All @@ -720,8 +725,10 @@ static db_conn::ID_ARRAYS db_engine_classify_id_array(const db_base &db,
if (!(sub.notification_type & bits))
continue;
if (sub.b_whole || (sub.folder_id == folder_id &&
sub.message_id == message_id))
out[sub.remote_id].push_back(sub.sub_id);
sub.message_id == message_id)) {
auto rid = sub.remote_id.has_value() ? sub.remote_id->c_str() : nullptr;
out[rid].push_back(sub.sub_id);
}
}
return out;
} catch (const std::bad_alloc &) {
Expand Down Expand Up @@ -3845,8 +3852,10 @@ void db_conn::notify_message_movecopy(BOOL b_copy, uint64_t folder_id,
continue;
}
if (pnsub->b_whole || (pnsub->folder_id == old_fid &&
pnsub->message_id == old_mid))
recv_list[pnsub->remote_id].push_back(pnsub->sub_id);
pnsub->message_id == old_mid)) {
auto rid = sub.remote_id.has_value() ? sub.remote_id->c_str() : nullptr;
recv_list[rid].push_back(pnsub->sub_id);
}
}
if (recv_list.size() > 0) {
datagram.dir = deconst(dir);
Expand Down Expand Up @@ -3896,8 +3905,10 @@ void db_conn::notify_folder_movecopy(BOOL b_copy, uint64_t parent_id,
}
if (pnsub->b_whole ||
(pnsub->folder_id == folder_id && pnsub->message_id == 0) ||
(pnsub->folder_id == old_fid && pnsub->message_id == 0))
recv_list[pnsub->remote_id].push_back(pnsub->sub_id);
(pnsub->folder_id == old_fid && pnsub->message_id == 0)) {
auto rid = sub.remote_id.has_value() ? sub.remote_id->c_str() : nullptr;
recv_list[rid].push_back(pnsub->sub_id);
}
}
if (recv_list.size() > 0) {
datagram.dir = deconst(dir);
Expand Down
3 changes: 2 additions & 1 deletion exch/exmdb/db_engine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ struct table_node {
};

struct nsub_node {
char *remote_id = nullptr;
std::optional<std::string> remote_id;
uint32_t sub_id = 0;
uint8_t notification_type = 0;
BOOL b_whole = false;
Expand Down Expand Up @@ -167,6 +167,7 @@ struct db_conn {
strcasecmp(a, b) < 0;
}
};
/* remote_id => subscription ids */
using ID_ARRAYS = std::map<const char *, std::vector<uint32_t>, xless>;
/* As long as any NOTIFQ object is alive, dbase should be held at least read-locked. */
using NOTIFQ = std::vector<std::pair<DB_NOTIFY_DATAGRAM, ID_ARRAYS>>;
Expand Down
9 changes: 2 additions & 7 deletions exch/exmdb/store.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,13 +377,8 @@ BOOL exmdb_server::subscribe_notification(const char *dir,
nsub_node sub, *pnsub = &sub;
pnsub->sub_id = last_id + 1;
auto remote_id = exmdb_server::get_remote_id();
if (NULL == remote_id) {
pnsub->remote_id = NULL;
} else {
pnsub->remote_id = strdup(remote_id);
if (pnsub->remote_id == nullptr)
return FALSE;
}
if (remote_id != nullptr)
sub.remote_id.emplace(remote_id);
pnsub->notification_type = notification_type;
pnsub->b_whole = b_whole;
if (0 == folder_id) {
Expand Down
17 changes: 9 additions & 8 deletions exch/ldap_adaptor.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// SPDX-FileCopyrightText: 2020–2021 grommunio GmbH
// SPDX-FileCopyrightText: 2020–2025 grommunio GmbH
// This file is part of Gromox.
#include <cassert>
#include <cerrno>
Expand Down Expand Up @@ -29,6 +29,7 @@ DECLARE_SVC_API(,);

namespace {
struct ldapfree {
void operator()(char *s) const { ldap_memfree(s); }
void operator()(LDAP *ld) const { ldap_unbind_ext_s(ld, nullptr, nullptr); }
void operator()(LDAPMessage *m) const { ldap_msgfree(m); }
};
Expand Down Expand Up @@ -159,15 +160,15 @@ static int gx_ldap_bind(ldap_ptr &ld, const char *dn, struct berval *bv)
}

static int gx_ldap_search(ldap_ptr &ld, const char *base, const char *filter,
char **attrs, LDAPMessage **msg)
char **attrs, ldap_msg &msg)
{
if (ld == nullptr)
ld = make_conn(g_ldap_host, g_bind_user.c_str(),
g_bind_pass.c_str(), g_use_tls, DO_BIND);
if (ld == nullptr)
return LDAP_SERVER_DOWN;
auto ret = ldap_search_ext_s(ld.get(), base, LDAP_SCOPE_SUBTREE,
filter, attrs, true, nullptr, nullptr, nullptr, 1, msg);
filter, attrs, true, nullptr, nullptr, nullptr, 1, &unique_tie(msg));
if (ret == LDAP_LOCAL_ERROR && g_edir_workaround)
/* try full reconnect */;
else if (ret != LDAP_SERVER_DOWN)
Expand All @@ -177,7 +178,7 @@ static int gx_ldap_search(ldap_ptr &ld, const char *base, const char *filter,
if (ld == nullptr)
return ret;
return ldap_search_ext_s(ld.get(), base, LDAP_SCOPE_SUBTREE,
filter, attrs, true, nullptr, nullptr, nullptr, 1, msg);
filter, attrs, true, nullptr, nullptr, nullptr, 1, &~unique_tie(msg));
}

static BOOL ldaplogin_host(ldap_ptr &tok_meta, ldap_ptr &tok_bind,
Expand All @@ -190,7 +191,7 @@ static BOOL ldaplogin_host(ldap_ptr &tok_meta, ldap_ptr &tok_bind,
auto filter = attr + "="s + quoted;
auto ret = gx_ldap_search(tok_meta,
base_dn.size() > 0 ? base_dn.c_str() : nullptr,
filter.c_str(), const_cast<char **>(no_attrs), &unique_tie(msg));
filter.c_str(), const_cast<char **>(no_attrs), msg);
auto act_base = base_dn.size() > 0 ? base_dn.c_str() : "(ldap.conf->BASE)";
if (ret != LDAP_SUCCESS && ret != LDAP_SIZELIMIT_EXCEEDED) {
mlog(LV_ERR, "ldap_adaptor: error during search in base \"%s\" for \"%s\": %s",
Expand All @@ -212,17 +213,17 @@ static BOOL ldaplogin_host(ldap_ptr &tok_meta, ldap_ptr &tok_bind,
auto firstmsg = ldap_first_message(tok_meta.get(), msg.get());
if (firstmsg == nullptr)
return FALSE;
auto dn = ldap_get_dn(tok_meta.get(), firstmsg);
std::unique_ptr<char[], ldapfree> dn(ldap_get_dn(tok_meta.get(), firstmsg));
if (dn == nullptr)
return FALSE;

struct berval bv;
bv.bv_val = deconst(znul(password));
bv.bv_len = password != nullptr ? strlen(password) : 0;
ret = gx_ldap_bind(tok_bind, dn, &bv);
ret = gx_ldap_bind(tok_bind, dn.get(), &bv);
if (ret == LDAP_SUCCESS)
return TRUE;
mlog(LV_ERR, "ldap_adaptor: ldap_simple_bind \"%s\": %s", dn, ldap_err2string(ret));
mlog(LV_ERR, "ldap_adaptor: ldap_simple_bind \"%s\": %s", dn.get(), ldap_err2string(ret));
return FALSE;
}

Expand Down
Loading

0 comments on commit b4902cb

Please sign in to comment.