diff --git a/include/bongoagent.h b/include/bongoagent.h
index fffd15a..23e245f 100644
--- a/include/bongoagent.h
+++ b/include/bongoagent.h
@@ -95,6 +95,22 @@ typedef struct _BongoConfigItem {
void *destination;
} BongoConfigItem;
+/* JSON represents every integral value as int, while agent configuration
+ * structures also contain byte, short, long and time_t destinations. Keep
+ * the actual destination width in the schema so the generic loader never
+ * writes through an incorrectly typed or unaligned int pointer. */
+typedef struct _BongoConfigIntegerDestination {
+ void *pointer;
+ size_t size;
+} BongoConfigIntegerDestination;
+
+#define BONGO_CONFIG_INT(source, destination) \
+ { BONGO_JSON_INT, (source), \
+ &(BongoConfigIntegerDestination){ (destination), sizeof(*(destination)) } }
+
+BOOL SetBongoConfigInteger(BongoConfigIntegerDestination *destination,
+ int value);
+
BOOL ReadBongoConfiguration(BongoConfigItem *config, char *filename);
BOOL ProcessBongoConfiguration(BongoConfigItem *config, BongoJsonNode *node);
BOOL SetBongoConfigItem(BongoConfigItem *schema, BongoJsonNode *node);
diff --git a/src/agents/antispam/spamd.c b/src/agents/antispam/spamd.c
index 029e204..d1424aa 100644
--- a/src/agents/antispam/spamd.c
+++ b/src/agents/antispam/spamd.c
@@ -337,9 +337,9 @@ static BongoConfigItem SpamdHostList = { BONGO_JSON_STRING, NULL, &ASpam.spamd.h
static BongoConfigItem SpamdConfigSchema[] = {
{ BONGO_JSON_BOOL, "o:enabled/b", &ASpam.spamd.enabled },
- { BONGO_JSON_INT, "o:timeout/i", &ASpam.spamd.connectionTimeout },
- { BONGO_JSON_INT, "o:maximum_message_size/i", &ASpam.spamd.maximumMessageSize },
- { BONGO_JSON_INT, "o:maximum_response_size/i", &ASpam.spamd.maximumResponseSize },
+ BONGO_CONFIG_INT("o:timeout/i", &ASpam.spamd.connectionTimeout),
+ BONGO_CONFIG_INT("o:maximum_message_size/i", &ASpam.spamd.maximumMessageSize),
+ BONGO_CONFIG_INT("o:maximum_response_size/i", &ASpam.spamd.maximumResponseSize),
{ BONGO_JSON_ARRAY, "o:hosts/a", &SpamdHostList},
{ BONGO_JSON_NULL, NULL, NULL },
};
diff --git a/src/agents/avirus/avirus.c b/src/agents/avirus/avirus.c
index 763fdb5..19c2ce9 100755
--- a/src/agents/avirus/avirus.c
+++ b/src/agents/avirus/avirus.c
@@ -351,9 +351,9 @@ static BongoConfigItem ClamHostList = {
};
static BongoConfigItem AVirusConfigSchema[] = {
- { BONGO_JSON_INT, "o:flags/i", &AVirus.flags },
- { BONGO_JSON_INT, "o:queue/i", &AVirus.nmap.queue },
- { BONGO_JSON_INT, "o:timeout/i", &AVirus.clamd.connectionTimeout },
+ BONGO_CONFIG_INT("o:flags/i", &AVirus.flags),
+ BONGO_CONFIG_INT("o:queue/i", &AVirus.nmap.queue),
+ BONGO_CONFIG_INT("o:timeout/i", &AVirus.clamd.connectionTimeout),
{ BONGO_JSON_ARRAY, "o:hosts/a", &ClamHostList},
{ BONGO_JSON_NULL, NULL, NULL },
};
diff --git a/src/agents/imap/fetch.h b/src/agents/imap/fetch.h
index 3a9ed75..edaa7c1 100644
--- a/src/agents/imap/fetch.h
+++ b/src/agents/imap/fetch.h
@@ -19,34 +19,34 @@
*
****************************************************************************/
-#define F_UID (1<<0)
-#define F_FLAGS (1<<1)
-#define F_ENVELOPE (1<<2)
-#define F_XSENDER (1<<3)
-#define F_INTERNALDATE (1<<4)
-#define F_BODYSTRUCTURE (1<<5)
-#define F_RFC822_TEXT (1<<6)
-#define F_RFC822_SIZE (1<<7)
-#define F_RFC822_HEADER (1<<8)
-#define F_RFC822_BOTH (1<<9)
-#define F_BODY (1<<10)
-#define F_BODY_HEADER (1<<11)
-#define F_BODY_HEADER_PARTIAL (1<<12)
-#define F_BODY_TEXT (1<<13)
-#define F_BODY_TEXT_PARTIAL (1<<14)
-#define F_BODY_BOTH (1<<15)
-#define F_BODY_BOTH_PARTIAL (1<<16)
-#define F_BODY_MIME (1<<17)
-#define F_BODY_MIME_PARTIAL (1<<18)
-#define F_BODY_HEADER_FIELDS (1<<19)
-#define F_BODY_HEADER_FIELDS_NOT (1<<20)
-#define F_BODY_PART (1<<21)
-#define F_BODY_SEEN (1<<22)
-#define F_BINARY (1<<23)
-#define F_BINARY_SIZE (1<<24)
+#define F_UID (1UL<<0)
+#define F_FLAGS (1UL<<1)
+#define F_ENVELOPE (1UL<<2)
+#define F_XSENDER (1UL<<3)
+#define F_INTERNALDATE (1UL<<4)
+#define F_BODYSTRUCTURE (1UL<<5)
+#define F_RFC822_TEXT (1UL<<6)
+#define F_RFC822_SIZE (1UL<<7)
+#define F_RFC822_HEADER (1UL<<8)
+#define F_RFC822_BOTH (1UL<<9)
+#define F_BODY (1UL<<10)
+#define F_BODY_HEADER (1UL<<11)
+#define F_BODY_HEADER_PARTIAL (1UL<<12)
+#define F_BODY_TEXT (1UL<<13)
+#define F_BODY_TEXT_PARTIAL (1UL<<14)
+#define F_BODY_BOTH (1UL<<15)
+#define F_BODY_BOTH_PARTIAL (1UL<<16)
+#define F_BODY_MIME (1UL<<17)
+#define F_BODY_MIME_PARTIAL (1UL<<18)
+#define F_BODY_HEADER_FIELDS (1UL<<19)
+#define F_BODY_HEADER_FIELDS_NOT (1UL<<20)
+#define F_BODY_PART (1UL<<21)
+#define F_BODY_SEEN (1UL<<22)
+#define F_BINARY (1UL<<23)
+#define F_BINARY_SIZE (1UL<<24)
-#define F_PARSE_ERROR (1<<30)
-#define F_SYSTEM_ERROR (1<<31)
+#define F_PARSE_ERROR (1UL<<30)
+#define F_SYSTEM_ERROR (1UL<<31)
#define F_NEED_HEADER (F_BODY_HEADER | F_BODY_HEADER_PARTIAL | F_RFC822_HEADER | F_RFC822_BOTH | F_ENVELOPE | F_BODY_HEADER_FIELDS | F_BODY_HEADER_FIELDS_NOT | F_BODY_MIME | F_BODY_MIME_PARTIAL)
#define F_NEED_MIME (F_BODY_PART | F_BODYSTRUCTURE | F_BODY | F_BINARY | F_BINARY_SIZE)
diff --git a/src/agents/imap/imapd.c b/src/agents/imap/imapd.c
index 772d2c4..34f6b15 100644
--- a/src/agents/imap/imapd.c
+++ b/src/agents/imap/imapd.c
@@ -4479,12 +4479,12 @@ static BongoConfigItem IMAPProxyProtocolNetwork = {
};
static BongoConfigItem IMAPConfig[] = {
- { BONGO_JSON_INT, "o:port/i", &Imap.server.port },
- { BONGO_JSON_INT, "o:port_ssl/i", &Imap.server.ssl.port },
+ BONGO_CONFIG_INT("o:port/i", &Imap.server.port),
+ BONGO_CONFIG_INT("o:port_ssl/i", &Imap.server.ssl.port),
{ BONGO_JSON_BOOL, "o:allow_legacy_tls/b", &Imap.server.allowLegacyTLS },
{ BONGO_JSON_BOOL, "o:proxy_protocol_enabled/b", &Imap.server.proxyProtocolEnabled },
{ BONGO_JSON_ARRAY, "o:proxy_protocol_networks/a", &IMAPProxyProtocolNetwork },
- { BONGO_JSON_INT, "o:threads_max/i", &Imap.session.threads.max },
+ BONGO_CONFIG_INT("o:threads_max/i", &Imap.session.threads.max),
{ BONGO_JSON_NULL, NULL, NULL }
};
diff --git a/src/agents/imap/search-result.c b/src/agents/imap/search-result.c
index 179c571..ea894b9 100644
--- a/src/agents/imap/search-result.c
+++ b/src/agents/imap/search-result.c
@@ -119,6 +119,38 @@ IMAPSearchParseReturn(const char *parameters, unsigned int *options,
return 1;
}
+int
+IMAPSearchParseStoreHit(const char *response, uint32_t *uid)
+{
+ const char *cursor;
+ uint64_t parsed_uid = 0;
+ size_t digits = 0;
+
+ if (!response || !uid) return 0;
+ cursor = response;
+ while (*cursor == ' ' || *cursor == '\t') cursor++;
+ while (isxdigit((unsigned char)*cursor)) cursor++;
+ if (!isspace((unsigned char)*cursor)) return 0;
+ while (*cursor == ' ' || *cursor == '\t') cursor++;
+ while (isxdigit((unsigned char)*cursor)) {
+ unsigned int value;
+
+ if (*cursor >= '0' && *cursor <= '9')
+ value = (unsigned int)(*cursor - '0');
+ else
+ value = (unsigned int)(toupper((unsigned char)*cursor) - 'A' + 10);
+ if (parsed_uid > (UINT32_MAX - value) / 16) return 0;
+ parsed_uid = parsed_uid * 16 + value;
+ cursor++;
+ digits++;
+ }
+ while (*cursor == ' ' || *cursor == '\t' ||
+ *cursor == '\r' || *cursor == '\n') cursor++;
+ if (!digits || *cursor) return 0;
+ *uid = (uint32_t)parsed_uid;
+ return 1;
+}
+
static int
AppendQuotedTag(char *buffer, size_t buffer_size, size_t *length,
const char *tag)
diff --git a/src/agents/imap/search-result.h b/src/agents/imap/search-result.h
index fb67308..2a63eee 100644
--- a/src/agents/imap/search-result.h
+++ b/src/agents/imap/search-result.h
@@ -38,6 +38,9 @@
int IMAPSearchParseReturn(const char *parameters, unsigned int *options,
const char **criteria);
+/* Parses the document GUID and IMAP UID returned by an NMAP SEARCH hit. */
+int IMAPSearchParseStoreHit(const char *response, uint32_t *uid);
+
/* Formats one complete untagged ESEARCH response, including CRLF. */
size_t IMAPSearchFormatEsearch(char *buffer, size_t buffer_size,
const char *tag, int by_uid,
diff --git a/src/agents/imap/search.c b/src/agents/imap/search.c
index 0db5891..7091332 100644
--- a/src/agents/imap/search.c
+++ b/src/agents/imap/search.c
@@ -533,13 +533,13 @@ __inline static long
FullListProcessMatch(ImapSession *session __attribute__((unused)), SearchKey *key __attribute__((unused)), char *response)
{
long ccode;
- char *ptr;
unsigned long sequenceNum;
+ uint32_t uid;
- ptr = strchr(response, ' ');
- if (ptr) {
- ptr++;
- ccode = UidToSequenceNum(&session->folder.selected.message[0], session->folder.selected.messageCount, (uint32_t)HexToUInt64(ptr, NULL), &sequenceNum);
+ if (IMAPSearchParseStoreHit(response, &uid)) {
+ ccode = UidToSequenceNum(&session->folder.selected.message[0],
+ session->folder.selected.messageCount,
+ uid, &sequenceNum);
if (ccode == STATUS_CONTINUE) {
key->matchList[sequenceNum] = -2 - key->matchList[sequenceNum];
return(STATUS_CONTINUE);
@@ -558,7 +558,7 @@ FullListSubstringSearch(ImapSession *session __attribute__((unused)), SearchKey
for (;;) {
ccode = NMAPReadResponse(session->store.conn, session->store.response, sizeof(session->store.response), TRUE);
if (ccode == 2001) {
- FullListProcessMatch(session, key, session->store.response + 5);
+ FullListProcessMatch(session, key, session->store.response);
continue;
}
break;
diff --git a/src/agents/imap/tests/search-result-test.c b/src/agents/imap/tests/search-result-test.c
index 727403c..4a096d8 100644
--- a/src/agents/imap/tests/search-result-test.c
+++ b/src/agents/imap/tests/search-result-test.c
@@ -32,6 +32,7 @@ main(void)
uint32_t matches[] = { 2, 3, 4, 9, 11, 12 };
uint32_t saved[] = { 3, 9, 99 };
uint32_t mailbox[] = { 1, 3, 8, 9, 12 };
+ uint32_t uid;
char buffer[256];
assert(IMAPSearchParseReturn("ALL", &options, &criteria));
@@ -49,6 +50,13 @@ main(void)
&criteria));
assert(!IMAPSearchParseReturn("RETURN (ALL)", &options, &criteria));
+ assert(IMAPSearchParseStoreHit("0000000000000010 00000001", &uid));
+ assert(uid == 1);
+ assert(IMAPSearchParseStoreHit("abcdef0123456789 ffffffff\r\n", &uid));
+ assert(uid == UINT32_MAX);
+ assert(!IMAPSearchParseStoreHit("0000000000000010", &uid));
+ assert(!IMAPSearchParseStoreHit("0000000000000010 100000000", &uid));
+
assert(IMAPSearchFormatEsearch(buffer, sizeof(buffer), "A42", 0,
IMAP_SEARCH_RETURN_MIN | IMAP_SEARCH_RETURN_MAX |
IMAP_SEARCH_RETURN_ALL | IMAP_SEARCH_RETURN_COUNT,
diff --git a/src/agents/mailprox/mailprox.c b/src/agents/mailprox/mailprox.c
index 6239f15..4ad6e10 100644
--- a/src/agents/mailprox/mailprox.c
+++ b/src/agents/mailprox/mailprox.c
@@ -816,9 +816,9 @@ ProxyServer(void *ignored)
// MailProxy.contexts
static BongoConfigItem ProxyConfig[] = {
{ BONGO_JSON_STRING, "o:postmaster/s", &MailProxy.postmaster },
- { BONGO_JSON_INT, "o:interval/i", &MailProxy.runInterval }, // in seconds
- { BONGO_JSON_INT, "o:max_threads/i", &MailProxy.max.threadsParallel },
- { BONGO_JSON_INT, "o:max_accounts/i", &MailProxy.max.accounts },
+ BONGO_CONFIG_INT("o:interval/i", &MailProxy.runInterval), // in seconds
+ BONGO_CONFIG_INT("o:max_threads/i", &MailProxy.max.threadsParallel),
+ BONGO_CONFIG_INT("o:max_accounts/i", &MailProxy.max.accounts),
{ BONGO_JSON_STRING, "o:queue/s", &MailProxy.nmap.address },
{ BONGO_JSON_NULL, NULL, NULL }
};
diff --git a/src/agents/pop/pop3.c b/src/agents/pop/pop3.c
index 45729fe..c296284 100644
--- a/src/agents/pop/pop3.c
+++ b/src/agents/pop/pop3.c
@@ -2378,8 +2378,8 @@ static BongoConfigItem POP3ProxyProtocolNetwork = {
};
static BongoConfigItem POP3Config[] = {
- { BONGO_JSON_INT, "o:port/i", &POP3ServerPort },
- { BONGO_JSON_INT, "o:port_ssl/i", &POP3ServerPortSSL },
+ BONGO_CONFIG_INT("o:port/i", &POP3ServerPort),
+ BONGO_CONFIG_INT("o:port_ssl/i", &POP3ServerPortSSL),
{ BONGO_JSON_BOOL, "o:allow_legacy_tls/b", &POP3.server.ssl.allowLegacyTLS },
{ BONGO_JSON_BOOL, "o:proxy_protocol_enabled/b", &POP3.server.proxyProtocolEnabled },
{ BONGO_JSON_ARRAY, "o:proxy_protocol_networks/a", &POP3ProxyProtocolNetwork },
diff --git a/src/agents/queue/conf.c b/src/agents/queue/conf.c
index 04ef63f..ad7d7e5 100644
--- a/src/agents/queue/conf.c
+++ b/src/agents/queue/conf.c
@@ -81,26 +81,26 @@ static BongoConfigItem QueueConfig[] = {
{ BONGO_JSON_BOOL, "o:debug/b", &Conf.debug },
{ BONGO_JSON_ARRAY, "o:domains/a", &domainsConfig },
{ BONGO_JSON_BOOL, "o:limitremoteprocessing/b", &Conf.deferEnabled },
- { BONGO_JSON_INT, "o:limitremotebegweekday/i", &Conf.i_deferStartWD },
- { BONGO_JSON_INT, "o:limitremotebegweekend/i", &Conf.i_deferStartWE },
- { BONGO_JSON_INT, "o:limitremoteendweekday/i", &Conf.i_deferEndWD },
- { BONGO_JSON_INT, "o:limitremoteendweekend/i", &Conf.i_deferEndWE },
- { BONGO_JSON_INT, "o:queuetuning_concurrent/i", &Conf.maxConcurrentWorkers },
- { BONGO_JSON_INT, "o:queuetuning_sequential/i", &Conf.maxSequentialWorkers },
- { BONGO_JSON_INT, "o:queuetuning_load_high/i", &Conf.loadMonitorHigh },
- { BONGO_JSON_INT, "o:queuetuning_load_low/i", &Conf.loadMonitorLow },
- { BONGO_JSON_INT, "o:queuetuning_trigger/i", &Conf.limitTrigger },
- { BONGO_JSON_INT, "o:queuetimeout/i", &Conf.maxLinger },
+ BONGO_CONFIG_INT("o:limitremotebegweekday/i", &Conf.i_deferStartWD),
+ BONGO_CONFIG_INT("o:limitremotebegweekend/i", &Conf.i_deferStartWE),
+ BONGO_CONFIG_INT("o:limitremoteendweekday/i", &Conf.i_deferEndWD),
+ BONGO_CONFIG_INT("o:limitremoteendweekend/i", &Conf.i_deferEndWE),
+ BONGO_CONFIG_INT("o:queuetuning_concurrent/i", &Conf.maxConcurrentWorkers),
+ BONGO_CONFIG_INT("o:queuetuning_sequential/i", &Conf.maxSequentialWorkers),
+ BONGO_CONFIG_INT("o:queuetuning_load_high/i", &Conf.loadMonitorHigh),
+ BONGO_CONFIG_INT("o:queuetuning_load_low/i", &Conf.loadMonitorLow),
+ BONGO_CONFIG_INT("o:queuetuning_trigger/i", &Conf.limitTrigger),
+ BONGO_CONFIG_INT("o:queuetimeout/i", &Conf.maxLinger),
{ BONGO_JSON_STRING, "o:quotamessage/s", &Conf.quotaMessage },
- { BONGO_JSON_INT, "o:port/i", &Agent.agent.port },
+ BONGO_CONFIG_INT("o:port/i", &Agent.agent.port),
{ BONGO_JSON_BOOL, "o:forwardundeliverable_enabled/b", &Conf.forwardUndeliverableEnabled },
{ BONGO_JSON_STRING, "o:forwardundeliverable_to/s", &Conf.forwardUndeliverableAddress },
- { BONGO_JSON_INT, "o:queueinterval/i", &Conf.queueInterval },
- { BONGO_JSON_INT, "o:minimumfreespace/i", &Conf.minimumFree },
+ BONGO_CONFIG_INT("o:queueinterval/i", &Conf.queueInterval),
+ BONGO_CONFIG_INT("o:minimumfreespace/i", &Conf.minimumFree),
{ BONGO_JSON_ARRAY, "o:trustedhosts/a", &trustedHostsConfig },
{ BONGO_JSON_BOOL, "o:rtsantispamconfig_enabled/b", &Conf.bounceBlockSpam },
- { BONGO_JSON_INT, "o:rtsantispamconfig_delay/i", &Conf.bounceInterval },
- { BONGO_JSON_INT, "o:rtsantispamconfig_threshold/i", &Conf.bounceMax },
+ BONGO_CONFIG_INT("o:rtsantispamconfig_delay/i", &Conf.bounceInterval),
+ BONGO_CONFIG_INT("o:rtsantispamconfig_threshold/i", &Conf.bounceMax),
{ BONGO_JSON_BOOL, "o:bouncereturn/b", &Conf.b_bounceReturn },
{ BONGO_JSON_BOOL, "o:bounceccpostmaster/b", &Conf.b_bounceCCPostmaster },
{ BONGO_JSON_NULL, NULL, NULL }
diff --git a/src/agents/sieve/managesieved.c b/src/agents/sieve/managesieved.c
index 7079358..c549f5c 100644
--- a/src/agents/sieve/managesieved.c
+++ b/src/agents/sieve/managesieved.c
@@ -53,12 +53,12 @@ static BongoConfigItem ProxyProtocolNetwork = {
static BongoConfigItem SieveConfig[] = {
{ BONGO_JSON_STRING, "o:listen_address/s", &listen_address },
- { BONGO_JSON_INT, "o:port/i", &server_port },
+ BONGO_CONFIG_INT("o:port/i", &server_port),
{ BONGO_JSON_BOOL, "o:allow_legacy_tls/b", &allow_legacy_tls },
{ BONGO_JSON_BOOL, "o:proxy_protocol_enabled/b", &proxy_protocol_enabled },
{ BONGO_JSON_ARRAY, "o:proxy_protocol_networks/a", &ProxyProtocolNetwork },
- { BONGO_JSON_INT, "o:maximum_script_size/i", &maximum_script_size },
- { BONGO_JSON_INT, "o:maximum_connections/i", &maximum_connections },
+ BONGO_CONFIG_INT("o:maximum_script_size/i", &maximum_script_size),
+ BONGO_CONFIG_INT("o:maximum_connections/i", &maximum_connections),
{ BONGO_JSON_NULL, NULL, NULL }
};
diff --git a/src/agents/smtp/smtpd.c b/src/agents/smtp/smtpd.c
index 5764a1c..b0a1ed3 100644
--- a/src/agents/smtp/smtpd.c
+++ b/src/agents/smtp/smtpd.c
@@ -125,22 +125,22 @@ static BongoConfigItem ProxyProtocolNetworkList = {
};
static BongoConfigItem SMTPConfig[] = {
- { BONGO_JSON_INT, "o:port/i", &SMTP.port },
- { BONGO_JSON_INT, "o:port_ssl/i", &SMTP.port_ssl },
+ BONGO_CONFIG_INT("o:port/i", &SMTP.port),
+ BONGO_CONFIG_INT("o:port_ssl/i", &SMTP.port_ssl),
{ BONGO_JSON_BOOL, "o:submission_enabled/b", &SMTP.submission_enabled },
- { BONGO_JSON_INT, "o:submission_port/i", &SMTP.submission_port },
- { BONGO_JSON_INT, "o:submission_port_ssl/i", &SMTP.submission_port_ssl },
+ BONGO_CONFIG_INT("o:submission_port/i", &SMTP.submission_port),
+ BONGO_CONFIG_INT("o:submission_port_ssl/i", &SMTP.submission_port_ssl),
{ BONGO_JSON_BOOL, "o:submission_require_auth/b", &SMTP.submission_require_auth },
{ BONGO_JSON_BOOL, "o:submission_allow_sender_override/b", &SMTP.submission_allow_sender_override },
{ BONGO_JSON_BOOL, "o:internal_relay_enabled/b", &SMTP.internal_relay_enabled },
- { BONGO_JSON_INT, "o:internal_relay_port/i", &SMTP.internal_relay_port },
+ BONGO_CONFIG_INT("o:internal_relay_port/i", &SMTP.internal_relay_port),
{ BONGO_JSON_STRING, "o:internal_relay_bind_address/s", &SMTP.internal_relay_bind_address },
{ BONGO_JSON_ARRAY, "o:internal_relay_networks/a", &InternalRelayNetworkList },
{ BONGO_JSON_BOOL, "o:internal_relay_allow_legacy_tls/b", &SMTP.internal_relay_allow_legacy_tls },
- { BONGO_JSON_INT, "o:internal_relay_messages_per_minute/i", &SMTP.internal_relay_messages_per_minute },
- { BONGO_JSON_INT, "o:internal_relay_recipients_per_minute/i", &SMTP.internal_relay_recipients_per_minute },
- { BONGO_JSON_INT, "o:internal_relay_bytes_per_minute/i", &SMTP.internal_relay_bytes_per_minute },
- { BONGO_JSON_INT, "o:internal_relay_connections_per_ip/i", &SMTP.internal_relay_connections_per_ip },
+ BONGO_CONFIG_INT("o:internal_relay_messages_per_minute/i", &SMTP.internal_relay_messages_per_minute),
+ BONGO_CONFIG_INT("o:internal_relay_recipients_per_minute/i", &SMTP.internal_relay_recipients_per_minute),
+ BONGO_CONFIG_INT("o:internal_relay_bytes_per_minute/i", &SMTP.internal_relay_bytes_per_minute),
+ BONGO_CONFIG_INT("o:internal_relay_connections_per_ip/i", &SMTP.internal_relay_connections_per_ip),
{ BONGO_JSON_STRING, "o:internal_relay_domain/s", &SMTP.internal_relay_domain },
{ BONGO_JSON_ARRAY, "o:internal_relay_device_mappings/a", &InternalRelayDeviceMappingList },
{ BONGO_JSON_ARRAY, "o:internal_relay_dns_suffixes/a", &InternalRelayDNSSuffixList },
@@ -155,16 +155,16 @@ static BongoConfigItem SMTPConfig[] = {
{ BONGO_JSON_BOOL, "o:verify_address/b", &SMTP.verify_address },
{ BONGO_JSON_BOOL, "o:accept_etrn/b", &SMTP.accept_etrn },
{ BONGO_JSON_BOOL, "o:send_etrn/b", &SMTP.send_etrn },
- { BONGO_JSON_INT, "o:maximum_recipients/i", &SMTP.max_recips },
- { BONGO_JSON_INT, "o:message_size_limit/i", &SMTP.message_limit },
+ BONGO_CONFIG_INT("o:maximum_recipients/i", &SMTP.max_recips),
+ BONGO_CONFIG_INT("o:message_size_limit/i", &SMTP.message_limit),
{ BONGO_JSON_BOOL, "o:use_relay_host/b", &SMTP.use_relay },
{ BONGO_JSON_STRING, "o:relay_host/s", &SMTP.relay_host },
{ BONGO_JSON_BOOL, "o:block_rts_spam/b", &SMTP.block_rts },
- { BONGO_JSON_INT, "o:max_thread_load/i", &SMTP.max_thread_load },
- { BONGO_JSON_INT, "o:max_flood_count/i", &SMTP.max_flood_count }, // UNUSED ?
- { BONGO_JSON_INT, "o:max_null_sender/i", &SMTP.max_null_sender },
- { BONGO_JSON_INT, "o:socket_timeout/i", &SMTP.socket_timeout },
- { BONGO_JSON_INT, "o:max_mx_servers/i", &SMTP.max_mx_servers },
+ BONGO_CONFIG_INT("o:max_thread_load/i", &SMTP.max_thread_load),
+ BONGO_CONFIG_INT("o:max_flood_count/i", &SMTP.max_flood_count), // UNUSED ?
+ BONGO_CONFIG_INT("o:max_null_sender/i", &SMTP.max_null_sender),
+ BONGO_CONFIG_INT("o:socket_timeout/i", &SMTP.socket_timeout),
+ BONGO_CONFIG_INT("o:max_mx_servers/i", &SMTP.max_mx_servers),
{ BONGO_JSON_BOOL, "o:relay_local_mail/b", &SMTP.relay_local },
{ BONGO_JSON_BOOL, "o:require_auth/b", &SMTP.require_auth },
{ BONGO_JSON_BOOL, "o:spf_verify/b", &SMTP.spf_verify },
diff --git a/src/agents/store/search.c b/src/agents/store/search.c
index 55ad6df..5495535 100644
--- a/src/agents/store/search.c
+++ b/src/agents/store/search.c
@@ -41,10 +41,9 @@ ReadSourceFile(void *source, char *buffer, unsigned long maxRead)
__inline static CCode
ReportHit(Connection *conn, StoreObject *document)
{
- // FIXME: imap uids
return ConnWriteF(conn,
"2001 " GUID_FMT " %08x\r\n",
- document->guid, (uint32_t) document->guid);
+ document->guid, document->imap_uid);
}
static CCode
diff --git a/src/apps/config/tests/test_configuration.py b/src/apps/config/tests/test_configuration.py
index 0e1d412..f43d5d7 100644
--- a/src/apps/config/tests/test_configuration.py
+++ b/src/apps/config/tests/test_configuration.py
@@ -25,6 +25,7 @@ from __future__ import annotations
import sys
import unittest
+from unittest import mock
from pathlib import Path
SOURCE = Path(__file__).resolve().parents[4]
@@ -102,6 +103,28 @@ class ConfigurationModelTest(unittest.TestCase):
self.assertFalse(errors(validate_all(
configs, defaults, check_files=False)))
+ def test_web_databases_follow_configured_state_directory(self):
+ defaults = templates()
+ with mock.patch.dict("os.environ", {
+ "BONGO_STATE_DIR": "/srv/bongo-test-state"}):
+ configs = apply_scenario(defaults, Scenario(
+ hostname="mail.example.test",
+ bind_address="127.0.0.1",
+ domains=["example.test"],
+ admin_password="0123456789ab",
+ web_mode="direct",
+ web_public_url="http://127.0.0.1:8080",
+ ))
+ self.assertEqual(
+ configs["web"]["session_database"],
+ "/srv/bongo-test-state/web/sessions.db")
+ self.assertEqual(
+ configs["web"]["task_database"],
+ "/srv/bongo-test-state/web/tasks.db")
+ self.assertEqual(
+ configs["web"]["identity_database"],
+ "/srv/bongo-test-state/web/identities.db")
+
def test_proxy_requires_trusted_network(self):
defaults = templates()
configs = apply_scenario(defaults, Scenario(
diff --git a/src/libs/cal/bongo-cal-recur.c b/src/libs/cal/bongo-cal-recur.c
index 8b1e242..dcff435 100644
--- a/src/libs/cal/bongo-cal-recur.c
+++ b/src/libs/cal/bongo-cal-recur.c
@@ -1108,8 +1108,10 @@ GenerateInstancesForChunk (BongoCalInstance *inst,
DPRINT("now have %d occurrences\n", occs->len);
DPRINT("and %d exception occurrences\n", exOccs->len);
- qsort (rdatePeriods->data, rdatePeriods->len,
- sizeof (CalObjRecurrenceDate), CalObjCompareFunc);
+ if (rdatePeriods->len > 1) {
+ qsort (rdatePeriods->data, rdatePeriods->len,
+ sizeof (CalObjRecurrenceDate), CalObjCompareFunc);
+ }
/* Create the final array, by removing the exceptions from the
occurrences, and removing any duplicates. */
@@ -1782,6 +1784,12 @@ CalObjInitializeRecurData (RecurData *recurData,
static void
CalObjSortOccurrences (GArray *occs)
{
+ /* GLib leaves data NULL for an empty array. The C library declares the
+ * qsort base argument non-null even when nmemb is zero, so avoid passing
+ * the empty-array representation through to qsort. */
+ if (!occs || occs->len < 2)
+ return;
+
qsort (occs->data, occs->len, sizeof (CalObjTime),
CalObjCompareFunc);
}
diff --git a/src/libs/python/bongo/configuration/model.py b/src/libs/python/bongo/configuration/model.py
index 6aa1390..b89cb65 100644
--- a/src/libs/python/bongo/configuration/model.py
+++ b/src/libs/python/bongo/configuration/model.py
@@ -72,6 +72,15 @@ def _safe_absolute_path(value: Any) -> bool:
return value != "/" and ".." not in Path(value).parts
+def _state_directory() -> Path:
+ """Return the state root compiled into Bongo, with a source-tree fallback."""
+ try:
+ from bongo import Xpl
+ return Path(Xpl.DEFAULT_STATE_DIR)
+ except ImportError:
+ return Path(os.environ.get("BONGO_STATE_DIR", "/var/lib/bongo"))
+
+
@dataclass(frozen=True)
class Issue:
document: str
@@ -284,6 +293,10 @@ def apply_scenario(templates: Mapping[str, Any], scenario: Scenario) -> dict[str
web["tls_key"] = ""
web["public_base_url"] = scenario.web_public_url
web["client_ip_header"] = "X-Forwarded-For"
+ web_state = _state_directory() / "web"
+ web["session_database"] = str(web_state / "sessions.db")
+ web["task_database"] = str(web_state / "tasks.db")
+ web["identity_database"] = str(web_state / "identities.db")
configs["authentication"].update({
"AuthLdapEnabled": scenario.ldap_enabled,
diff --git a/src/libs/util/CMakeLists.txt b/src/libs/util/CMakeLists.txt
index cb8433c..ee15df9 100644
--- a/src/libs/util/CMakeLists.txt
+++ b/src/libs/util/CMakeLists.txt
@@ -12,6 +12,7 @@ add_library(bongoutil SHARED
quicksort.c
utf7mod.c
logging.c
+ config-integer.c
bongoagent.c
bongoutil.c)
@@ -22,3 +23,14 @@ target_link_libraries(bongoutil
)
install(TARGETS bongoutil DESTINATION ${LIB_INSTALL_DIR})
+
+if(BUILD_TESTING)
+ add_executable(util-config-integer-test
+ tests/config-integer-test.c config-integer.c)
+ target_compile_definitions(util-config-integer-test PRIVATE _NO_BONGO_GLOBALS)
+ add_test(NAME util-config-integer COMMAND util-config-integer-test)
+
+ add_executable(util-array-test tests/array-test.c array.c)
+ target_link_libraries(util-array-test PRIVATE GLib2::GLib2)
+ add_test(NAME util-array COMMAND util-array-test)
+endif()
diff --git a/src/libs/util/array.c b/src/libs/util/array.c
index 6626c6a..d032e1e 100644
--- a/src/libs/util/array.c
+++ b/src/libs/util/array.c
@@ -7,7 +7,7 @@ GArrayFindUnsorted(GArray *array, void *needle, ArrayCompareFunc compare)
{
unsigned int i;
- if (!array || !compare) {
+ if (!array || !needle || !compare || array->len == 0) {
return -1;
}
@@ -25,7 +25,7 @@ GArrayFindSorted(GArray *array, void *needle, ssize_t type_size, ArrayCompareFun
{
void *data;
- if (!array || !compare) {
+ if (!array || !needle || !compare || type_size <= 0 || array->len == 0) {
return -1;
}
diff --git a/src/libs/util/bongoagent.c b/src/libs/util/bongoagent.c
index e8af8b0..96fd6ac 100644
--- a/src/libs/util/bongoagent.c
+++ b/src/libs/util/bongoagent.c
@@ -139,7 +139,11 @@ FreeBongoConfiguration(BongoConfigItem *config) {
BOOL
SetBongoConfigItem(BongoConfigItem *schema, BongoJsonNode *node) {
- if (!node || node->type != schema->type) {
+ if (!schema || !node) {
+ XplConsolePrintf("config: missing schema or data node\r\n");
+ return FALSE;
+ }
+ if (node->type != schema->type) {
XplConsolePrintf("config: didn't find data at %s (type: %d, want: %d) \r\n", schema->source, node->type, schema->type);
return FALSE;
}
@@ -156,8 +160,15 @@ SetBongoConfigItem(BongoConfigItem *schema, BongoJsonNode *node) {
}
break;
case BONGO_JSON_INT: {
- int *dest = (int *)schema->destination;
- *dest = BongoJsonNodeAsInt(node);
+ BongoConfigIntegerDestination *destination = schema->destination;
+ int value = BongoJsonNodeAsInt(node);
+
+ if (!SetBongoConfigInteger(destination, value)) {
+ XplConsolePrintf("config: unsupported integer destination size %zu at %s\r\n",
+ destination ? destination->size : 0U,
+ schema->source);
+ return FALSE;
+ }
}
break;
case BONGO_JSON_STRING: {
diff --git a/src/libs/util/config-integer.c b/src/libs/util/config-integer.c
new file mode 100644
index 0000000..b68d1c9
--- /dev/null
+++ b/src/libs/util/config-integer.c
@@ -0,0 +1,45 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2001 Novell, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Novell, Inc.
+ *
+ ****************************************************************************/
+
+#include
+#include
+
+#include
+
+BOOL
+SetBongoConfigInteger(BongoConfigIntegerDestination *destination, int value)
+{
+ if (!destination || !destination->pointer)
+ return FALSE;
+ if (destination->size == sizeof(uint8_t)) {
+ uint8_t converted = (uint8_t)value;
+ memcpy(destination->pointer, &converted, sizeof(converted));
+ } else if (destination->size == sizeof(uint16_t)) {
+ uint16_t converted = (uint16_t)value;
+ memcpy(destination->pointer, &converted, sizeof(converted));
+ } else if (destination->size == sizeof(uint32_t)) {
+ uint32_t converted = (uint32_t)value;
+ memcpy(destination->pointer, &converted, sizeof(converted));
+ } else if (destination->size == sizeof(uint64_t)) {
+ uint64_t converted = (uint64_t)(int64_t)value;
+ memcpy(destination->pointer, &converted, sizeof(converted));
+ } else {
+ return FALSE;
+ }
+ return TRUE;
+}
diff --git a/src/libs/util/tests/array-test.c b/src/libs/util/tests/array-test.c
new file mode 100644
index 0000000..f078666
--- /dev/null
+++ b/src/libs/util/tests/array-test.c
@@ -0,0 +1,54 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2001 Novell, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Novell, Inc.
+ *
+ ****************************************************************************/
+
+#include
+
+#include
+
+#include
+
+static int
+CompareInt(const void *left, const void *right)
+{
+ int a = *(const int *)left;
+ int b = *(const int *)right;
+ return (a > b) - (a < b);
+}
+
+int
+main(void)
+{
+ GArray *values = g_array_new(FALSE, FALSE, sizeof(int));
+ int needle = 7;
+ int value;
+
+ assert(values != NULL);
+ assert(GArrayFindSorted(values, &needle, sizeof(int), CompareInt) == -1);
+ assert(GArrayFindSorted(values, NULL, sizeof(int), CompareInt) == -1);
+ assert(GArrayFindSorted(values, &needle, 0, CompareInt) == -1);
+
+ value = 3;
+ g_array_append_val(values, value);
+ value = 7;
+ g_array_append_val(values, value);
+ value = 9;
+ g_array_append_val(values, value);
+ assert(GArrayFindSorted(values, &needle, sizeof(int), CompareInt) == 1);
+ g_array_free(values, TRUE);
+ return 0;
+}
diff --git a/src/libs/util/tests/config-integer-test.c b/src/libs/util/tests/config-integer-test.c
new file mode 100644
index 0000000..79ee79c
--- /dev/null
+++ b/src/libs/util/tests/config-integer-test.c
@@ -0,0 +1,55 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2001 Novell, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Novell, Inc.
+ *
+ ****************************************************************************/
+
+#include
+#include
+#include
+
+#include
+
+static void
+CheckIntegerWidth(size_t offset, size_t width, int input, uint64_t expected)
+{
+ unsigned char storage[32];
+ BongoConfigIntegerDestination destination;
+ uint64_t actual = 0;
+
+ assert(offset + width <= sizeof(storage));
+ memset(storage, 0xa5, sizeof(storage));
+ destination.pointer = storage + offset;
+ destination.size = width;
+ assert(SetBongoConfigInteger(&destination, input));
+ memcpy(&actual, storage + offset, width);
+ if (width < sizeof(actual))
+ actual &= (UINT64_C(1) << (width * 8U)) - 1U;
+ assert(actual == expected);
+ assert(storage[offset - 1U] == 0xa5);
+ assert(storage[offset + width] == 0xa5);
+}
+
+int
+main(void)
+{
+ /* Every destination starts at an intentionally unaligned offset. */
+ CheckIntegerWidth(1U, sizeof(uint8_t), 0x7f, UINT64_C(0x7f));
+ CheckIntegerWidth(1U, sizeof(uint16_t), 65535, UINT64_C(0xffff));
+ CheckIntegerWidth(1U, sizeof(uint32_t), -7, UINT64_C(0xfffffff9));
+ CheckIntegerWidth(1U, sizeof(uint64_t), -7,
+ UINT64_C(0xfffffffffffffff9));
+ return 0;
+}