Files
BARANTMail/BARANTMailConfig.h
Brad ArantandClaude Sonnet 5 407cc2e915 Add per-domain DKIM signing, admin console TLS, log rotation, and resolver timeout scoping
- 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>
2026-10-06 17:39:06 +00:00

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