- DKIM signing was hardcoded to one domain; now keyed per-domain off the sending address, fixing mail for every domain but the original. - Admin console (port 1027) is now TLS-wrapped instead of plaintext. - barantmail.log now rotates by size (config-driven) instead of growing unbounded (previously hit 23GB). - ResolverThreadScope.h: a <setenv> scoping fix for name resolution inside a <try> block that previously did not survive its own container. - IMAPArgs.cpp: fixed "UID STORE 1:*" wrongly requiring UID 1 to exist, which silently made empty-folder operations delete nothing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
139 lines
4.7 KiB
C++
139 lines
4.7 KiB
C++
#ifndef __BARANTMailConfig_h__
|
|
#define __BARANTMailConfig_h__
|
|
|
|
#include "TLSInfo.h"
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace coreutils { class JFile; }
|
|
|
|
namespace mail {
|
|
|
|
///
|
|
/// Server startup configuration, loaded from a JSON file via
|
|
/// CoreUtils' JFile - mirrors APIServer's own ApiServerConfig (see
|
|
/// that class's doc comment for JFile's create-on-missing behavior).
|
|
///
|
|
/// Schema:
|
|
/// {
|
|
/// "hostName": "mail.example.com",
|
|
/// "mailPath": "/var/mail",
|
|
/// "smtp": { "port": 25 },
|
|
/// "smtpTLS": { "port": 465 },
|
|
/// "imap": { "port": 993 },
|
|
/// "pop3": { "port": 995 },
|
|
/// "tls": {
|
|
/// "certificate": "/path/to/fullchain.pem",
|
|
/// "key": "/path/to/privkey.pem",
|
|
/// "caCertificate": "/path/to/ca.pem"
|
|
/// },
|
|
/// "dkim": [
|
|
/// { "domain": "example.com", "selector": "default",
|
|
/// "privateKey": "/path/to/example.com.private.pem" },
|
|
/// { "domain": "second-domain.com", "selector": "default",
|
|
/// "privateKey": "/path/to/second-domain.com.private.pem" }
|
|
/// ],
|
|
/// "log": { "maxSizeBytes": 104857600, "maxBackups": 5 }
|
|
/// }
|
|
///
|
|
/// "hostName", "mailPath", and "smtp.port" are required - plain port
|
|
/// 25 is the one listener every deployment needs (an inbound peer
|
|
/// MTA relaying mail in has no TLS-first negotiation to fall back
|
|
/// on). "smtpTLS"/"imap"/"pop3" are each independently optional -
|
|
/// leaving one out just means that listener isn't started, so this
|
|
/// same file covers a plaintext-only relay just as well as a full
|
|
/// mail server. The moment any one of those three IS present,
|
|
/// "tls.certificate" and "tls.key" become required - there is no
|
|
/// path through this class that hands back a submission/IMAPS/POP3S
|
|
/// listener request without a real certificate configured for it.
|
|
/// "dkim" is entirely optional (an empty/missing array - outbound
|
|
/// mail goes out unsigned for any domain with no matching entry).
|
|
/// Each entry's three fields are all required. A message is signed
|
|
/// using whichever entry's "domain" matches the sender address's
|
|
/// own domain (case-sensitive exact match) - a domain with no entry
|
|
/// here just goes out unsigned, same as if "dkim" were absent
|
|
/// entirely.
|
|
/// "log" is entirely optional - "maxSizeBytes"/"maxBackups" each
|
|
/// independently default (100MB / 5 backups) if omitted, and the
|
|
/// whole section can be left out for the same defaults. Applied to
|
|
/// coreutils::Log's static rotation fields once this config loads -
|
|
/// see Log.h's own doc comment for what rotation actually does.
|
|
///
|
|
|
|
class BARANTMailConfig {
|
|
|
|
public:
|
|
|
|
BARANTMailConfig(const std::string &path);
|
|
~BARANTMailConfig();
|
|
|
|
std::string getHostName();
|
|
std::string getMailPath();
|
|
|
|
int getSmtpPort();
|
|
|
|
bool hasSmtpTLS();
|
|
int getSmtpTLSPort();
|
|
|
|
bool hasImap();
|
|
int getImapPort();
|
|
|
|
bool hasPop3();
|
|
int getPop3Port();
|
|
|
|
///
|
|
/// NULL if none of smtpTLS/imap/pop3 are configured. Owned by
|
|
/// this BARANTMailConfig, valid for as long as it is - pass
|
|
/// straight through to a server's tlsInfo parameter, do not
|
|
/// delete, and don't let this config object go out of scope
|
|
/// while a server built from this pointer is still running.
|
|
///
|
|
|
|
core::TLSInfo *getTlsInfo();
|
|
|
|
struct DkimEntry {
|
|
std::string domain;
|
|
std::string selector;
|
|
std::string privateKeyPath;
|
|
};
|
|
|
|
/// Empty if "dkim" is absent from the config file.
|
|
std::vector<DkimEntry> getDkimEntries();
|
|
|
|
bool hasLog();
|
|
size_t getLogMaxSizeBytes();
|
|
int getLogMaxBackups();
|
|
|
|
///
|
|
/// The admin console (see ConsoleServer/ConsoleSession) - "port"
|
|
/// defaults to 1027 if omitted. "user"/"password" are required
|
|
/// once "admin" is present at all; there is no path through this
|
|
/// class that hands back an admin listener with nothing to check
|
|
/// logins against.
|
|
///
|
|
|
|
bool hasAdmin();
|
|
int getAdminPort();
|
|
std::string getAdminUser();
|
|
std::string getAdminPassword();
|
|
|
|
private:
|
|
|
|
coreutils::JFile *file;
|
|
|
|
// Owned, persistent storage for the TLSInfo fields below -
|
|
// TLSInfo's own ZString members are non-owning views, so they
|
|
// have to point at something that outlives them; pointing them at
|
|
// temporary std::strings decoded during construction would leave
|
|
// them dangling the moment this constructor returned.
|
|
std::string certPath, keyPath, caPath;
|
|
|
|
core::TLSInfo tlsInfo;
|
|
bool hasTls = false;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|