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; +}