Database changes:
================
New statement db_sql_domain_flags, similar to db_sql_whitelisted, but it can
set two additional flags to selectively enable or disable DMARC or ADSP on a
per domain basis.  For a minimum DMARC implementation, the following three
statements are also new: db_sql_dmarc_agg_domain, db_sql_dmarc_agg_record, and
db_sql_set_dmarc_agg.  Their usage is exemplified in odbx_example.conf.

New variables for incoming messages:

message variables:
	dmarc_dkim    pass/fail/none
	dmarc_spf     pass/fail/none
	dmarc_reason  why DMARC policy was overridden
	dmarc_dispo   was the message accepted/rejected/quarantined

domain variables:
	dmarc_record  an extract of the record published
	dmarc_rua     address for sending aggregate reports
	dmarc_ri      served report interval
	original_ri   required report interval
	spf_result    actual SPF result for a domain
	dkim_result   actual DKIM result for a domain
	dkim_order    order of domain among dkim signers (reverse alpha)
	prefix_len    chars of domain name prepended to org_domain

and a new variable is available for db_sql_domain_flags:
	org_domain    the "organizational domain" relative to a message's From:

The auth_type variable can get the additional enumerated values "org", "dmarc",
"aligned", and "nx", which respectively mean a domain is the organizational
domain of message's From: header field, is aligned with it, and has no DNS
server.  The variable is still named auth_type, even though it doesn't imply
authentication succeeded; for example, if DMARC is enabled, auth_type can have
the spf_helo flag with an spf_result of "softfail".

In odbx_example.sql, domain.last is split into last_recv and last_sent, and the
type is changed to INT UNSIGNED in order to get rid of lossy conversions on
daylight saving changes.  BIGINT columns ino, mtime, and pid can be safely
reduced to INT, which is a 32-bit datum in MySQL.  You may want to check actual
values by `df -i` and `cat /proc/sys/kernel/pid_max`.

Existing tables can be upgraded from v1.4 like so:

ALTER TABLE domain
  ADD COLUMN dmarc_rua VARCHAR(64) NOT NULL DEFAULT '' AFTER domain,
  ADD COLUMN dmarc_rec VARCHAR(63) NOT NULL DEFAULT '' AFTER dmarc_rua,
  ADD COLUMN dmarc_ri MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 AFTER sent,
  ADD COLUMN original_ri MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 AFTER dmarc_ri,
  ADD COLUMN add_dmarc TINYINT NOT NULL DEFAULT 0 AFTER whitelisted,
  ADD COLUMN add_adsp TINYINT NOT NULL DEFAULT 0 AFTER add_dmarc,
  ADD COLUMN prefix_len TINYINT NOT NULL DEFAULT 0 AFTER add_adsp,
  ADD COLUMN last_report INT UNSIGNED NOT NULL DEFAULT 0,
  ADD COLUMN last_recv INT UNSIGNED NOT NULL DEFAULT 0,
  ADD COLUMN last_sent INT UNSIGNED NOT NULL DEFAULT 0;

UPDATE domain SET
  last_recv = UNIX_TIMESTAMP(CONVERT_TZ(last, 'SYSTEM', '+00:00')) + 0
  WHERE recv > 0;

UPDATE domain SET
  last_sent = UNIX_TIMESTAMP(CONVERT_TZ(last, 'SYSTEM', '+00:00')) + 0
  WHERE sent > 0;

ALTER TABLE domain DROP COLUMN last;

ALTER TABLE msg_ref
 CHANGE COLUMN auth auth SET ('author', 'spf_helo', 'spf',
   'dkim', 'org', 'dmarc', 'aligned', 'vbr', 'rep', 'rep_s', 'dnswl', 'nx')
   NOT NULL,
 ADD COLUMN spf ENUM ('none', 'neutral', 'pass', 'fail', 'softfail',
   'temperror', 'permerror') NOT NULL AFTER auth,
 ADD COLUMN dkim ENUM ('none', 'pass', 'fail', 'policy', 'neutral',
   'temperror', 'permerror') NOT NULL AFTER spf,
 ADD COLUMN dkim_order TINYINT UNSIGNED NOT NULL DEFAULT 0 AFTER dkim;

CREATE INDEX by_msg_auth ON msg_ref (message_in, auth);

ALTER TABLE message_in
  CHANGE COLUMN ino ino INT UNSIGNED NOT NULL,
  CHANGE COLUMN mtime mtime INT UNSIGNED NOT NULL,
  CHANGE COLUMN pid pid INT UNSIGNED NOT NULL,
  ADD COLUMN dmarc_dkim ENUM ('none', 'fail', 'pass') NOT NULL
    DEFAULT 'none' AFTER message_id,
  ADD COLUMN dmarc_spf ENUM ('none', 'fail', 'pass') NOT NULL
    DEFAULT 'none' AFTER dmarc_dkim,
  ADD COLUMN dmarc_reason ENUM ('none', 'forwarded', 'sampled_out',
    'trusted_forwarder', 'mailing_list', 'local_policy', 'other') NOT NULL
    DEFAULT 'none' AFTER dmarc_spf,
  ADD COLUMN dmarc_dispo ENUM ('none', 'quarantine', 'reject') NOT NULL
    DEFAULT 'none' AFTER dmarc_reason;

ALTER TABLE message_out
  CHANGE COLUMN ino ino INT UNSIGNED NOT NULL,
  CHANGE COLUMN mtime mtime INT UNSIGNED NOT NULL,
  CHANGE COLUMN pid pid INT UNSIGNED NOT NULL;

All three procedures changed, some more than others.  So have the configured
statements db_sql_insert_msg_ref and db_sql_insert_message.


New configuration parameters:
============================
log_dkim_order_above     = 0 (int)
publicsuffix             = NULL (filename)
honored_report_interval  = 86400 (seconds)


New dependencies:
================
libidn2 (e.g. Debian libidn2-0-dev)
libunistring  (e.g. Debian libunistring-dev)
zlib (e.g. Debian zlib1g-dev)
uuid (e.g. Debian uuid-dev) --optionally linked with
public suffix list (e.g. Debian publicsuffix) --used if configured


Bug fixes and other changes:
===========================
Column 70 wrapping of From:, To:, Cc:, and Reply-To: now occurs before signing.
This uses the same algorithm and code used by Courier's esmtpclient.

OpenDKIM bug 219 caused a DNS error to look like a temperror.  It is fixed in
OpenDKIM v2.10.1.  A workaround is compiled in when in zdkimfilter v1.5 is
configured with an older version of OpenDKIM.

Issue a temperror if SERVFAIL or similar happens during ADSP lookup; this
can cause a temporary reject if tempfail_on_error is set.

Log temperrors at verbosity >= 3; permerrors at verbosity >= 4.

Have log files line-buffered, to avoid garbled lines.

Added configure --enable-dkimsign-setuid switch, and make V=0 for less verbose
build.

Format of zfilter_db test arguments: use slash (/) instead of comma (,) to
separate stats tokens, so as to allow commas in tokens.  Thus,
	--set-stats dir [message data|domain[,token]]
becomes
	--set-stats dir [message data|domain[/token]]
and
	--set-stats-domain domain[,token]
becomes
	--set-stats-domain domain[/token]


