updated webclient setttings
This commit is contained in:
+123
-11
@@ -270,36 +270,71 @@ CREATE TABLE IF NOT EXISTS esrv_mailbox_aliases (
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Per-mailbox sender allow/block list. pattern is either an exact address
|
||||
-- Per-mailbox sender allow/block/junk list. pattern is either an exact address
|
||||
-- ("spam@evil.com") or a whole-domain wildcard ("@evil.com"). A single table with a
|
||||
-- list_type column, not two near-identical tables.
|
||||
-- list_type column, not three near-identical tables.
|
||||
-- 'block' is admin-only (Prefix+"/mailboxes/{id}/lists") and hard-rejects at RCPT
|
||||
-- time (db.IsBlocked, checked in smtpserver's Rcpt()) — an anti-abuse tool, not
|
||||
-- something an end user self-manages. 'junk' is the mailbox owner's own self-service
|
||||
-- Blocklist (webmail Settings, or the message view's "Mark as Junk" action) and is a
|
||||
-- *soft* block instead: mail is still accepted and delivered, just straight to Junk
|
||||
-- (db.IsJunked, checked in smtpserver's deliverLocally, bypassing spam scoring
|
||||
-- entirely the same way 'allow' does) rather than bounced. Different tools for
|
||||
-- different jobs — collapsing them into one would either strip admins of a real hard
|
||||
-- reject or hand end users the ability to bounce mail on another user's behalf.
|
||||
CREATE TABLE IF NOT EXISTS esrv_mailbox_allowblock (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
mailbox_id INTEGER NOT NULL REFERENCES esrv_mailboxes(id),
|
||||
list_type TEXT NOT NULL CHECK(list_type IN ('allow','block')),
|
||||
list_type TEXT NOT NULL CHECK(list_type IN ('allow','block','junk')),
|
||||
pattern TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(mailbox_id, list_type, pattern)
|
||||
);
|
||||
|
||||
-- A mailbox owner's own address book — deliberately separate from
|
||||
-- esrv_mailbox_smime_contacts/esrv_mailbox_pgp_contacts (those hold a certificate/key
|
||||
-- per address for signing/encryption, not a person's name/phone) and from
|
||||
-- SuggestRecipients' history-based autocomplete (mailbox_messages.go — reuses
|
||||
-- cached_to/cached_from rather than a dedicated table, so it has no name/phone either
|
||||
-- and can't be edited). name is required; phone is optional free text (no format
|
||||
-- enforced — international numbers, extensions, etc. all vary too much to validate
|
||||
-- usefully here).
|
||||
CREATE TABLE IF NOT EXISTS esrv_mailbox_contacts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
mailbox_id INTEGER NOT NULL REFERENCES esrv_mailboxes(id),
|
||||
email TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
phone TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(mailbox_id, email)
|
||||
);
|
||||
|
||||
-- Simple first-match-wins filter rules, evaluated in priority order (lower first) at
|
||||
-- delivery time, before a message is encrypted and stored — so from/to/subject
|
||||
-- delivery time, before a message is encrypted and stored — so from/to/subject/body
|
||||
-- matching works against the real message, not just the plaintext cache columns below.
|
||||
-- condition_field/op/value are the legacy single-condition columns, kept for rows
|
||||
-- created before multi-condition support existed. Every rule created since then
|
||||
-- stores its full condition list in conditions_json (a JSON array of
|
||||
-- {field,op,value}) instead, combined per match_type ("all"=AND, "any"=OR); a rule
|
||||
-- with an empty conditions_json falls back to the legacy columns as a single
|
||||
-- {field,op,value}, value optionally "\n"-joined to mean "any of these" — see
|
||||
-- mailstore.matchCondition) instead, combined per match_type ("all"=AND, "any"=OR); a
|
||||
-- rule with an empty conditions_json falls back to the legacy columns as a single
|
||||
-- condition — see MailboxFilterRule.Conditions() in mailbox_models.go.
|
||||
-- has_attachment/recipient_type conditions match against a synthetic "yes"/"no" or
|
||||
-- "to"/"cc"/"bcc" header value computed at delivery time (see smtpserver's
|
||||
-- deliverLocally), not a real message header.
|
||||
-- action_options_json holds action-specific parameters that don't apply to every
|
||||
-- action (currently just "forward"'s keep_copy) — see MailboxFilterRule.ActionOptions().
|
||||
CREATE TABLE IF NOT EXISTS esrv_mailbox_filter_rules (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
mailbox_id INTEGER NOT NULL REFERENCES esrv_mailboxes(id),
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
priority INTEGER NOT NULL DEFAULT 0,
|
||||
condition_field TEXT NOT NULL CHECK(condition_field IN ('from','to','subject')),
|
||||
condition_field TEXT NOT NULL CHECK(condition_field IN ('from','to','subject','body','has_attachment','recipient_type')),
|
||||
condition_op TEXT NOT NULL CHECK(condition_op IN ('contains','equals','starts_with')),
|
||||
condition_value TEXT NOT NULL,
|
||||
action TEXT NOT NULL CHECK(action IN ('move_to_folder','delete','mark_read','mark_as_spam')),
|
||||
action TEXT NOT NULL CHECK(action IN ('move_to_folder','delete','mark_read','mark_as_spam','forward')),
|
||||
action_value TEXT NOT NULL DEFAULT '',
|
||||
action_options_json TEXT NOT NULL DEFAULT '',
|
||||
is_active INTEGER NOT NULL DEFAULT 1,
|
||||
conditions_json TEXT NOT NULL DEFAULT '',
|
||||
match_type TEXT NOT NULL DEFAULT 'all',
|
||||
@@ -470,6 +505,23 @@ CREATE TABLE IF NOT EXISTS esrv_mailbox_signatures (
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_mailbox_signatures_mailbox ON esrv_mailbox_signatures(mailbox_id);
|
||||
|
||||
-- Per-alias override of which signature is the default-for-new/default-for-reply,
|
||||
-- for a mailbox with one or more send-as aliases (esrv_mailbox_aliases) wanting a
|
||||
-- different signature depending which address they're composing as — e.g. a
|
||||
-- "Support" signature for support@ and a personal one for their own address.
|
||||
-- Purely additive on top of esrv_mailbox_signatures' own is_default_new/is_default_reply
|
||||
-- columns, which remain the fallback default (for_email = the mailbox's own primary
|
||||
-- address, or an alias with no override row here) — see GetDefaultSignature.
|
||||
CREATE TABLE IF NOT EXISTS esrv_mailbox_signature_alias_defaults (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
mailbox_id INTEGER NOT NULL REFERENCES esrv_mailboxes(id),
|
||||
for_email TEXT NOT NULL,
|
||||
for_reply INTEGER NOT NULL DEFAULT 0,
|
||||
signature_id INTEGER NOT NULL REFERENCES esrv_mailbox_signatures(id),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(mailbox_id, for_email, for_reply)
|
||||
);
|
||||
|
||||
-- Other people's PGP public keys a mailbox owner has collected, added by hand —
|
||||
-- mirrors esrv_mailbox_smime_contacts. Used to offer "Encrypt (PGP)" for a
|
||||
-- recipient in compose.
|
||||
@@ -525,6 +577,12 @@ func migrateAddedColumns(db *sql.DB) {
|
||||
`ALTER TABLE esrv_mailbox_folders ADD COLUMN restore_parent_root TEXT NOT NULL DEFAULT ''`,
|
||||
`ALTER TABLE esrv_mailbox_messages ADD COLUMN restore_folder TEXT NOT NULL DEFAULT ''`,
|
||||
`ALTER TABLE esrv_mailboxes ADD COLUMN remote_images_mode TEXT NOT NULL DEFAULT 'ask'`,
|
||||
// The condition_field/action CHECK constraints (adding body/has_attachment/
|
||||
// recipient_type and forward) aren't retrofittable via ALTER TABLE either — see
|
||||
// migrateFilterRulesAdvancedCheck below. name/action_options_json themselves are
|
||||
// plain columns and migrate fine here.
|
||||
`ALTER TABLE esrv_mailbox_filter_rules ADD COLUMN name TEXT NOT NULL DEFAULT ''`,
|
||||
`ALTER TABLE esrv_mailbox_filter_rules ADD COLUMN action_options_json TEXT NOT NULL DEFAULT ''`,
|
||||
}
|
||||
// The three old columns above were NOT NULL with no default, so simply adding
|
||||
// key_pem left them behind still blocking every new insert (which only ever sets
|
||||
@@ -544,12 +602,37 @@ func migrateAddedColumns(db *sql.DB) {
|
||||
db.Exec(`UPDATE esrv_admin_users SET must_change_username = 1 WHERE username = ? AND must_change_password = 1`, DefaultAdminUsername)
|
||||
migrateSpamRenamedToJunk(db)
|
||||
migrateFilterRulesMarkAsSpamCheck(db)
|
||||
migrateFilterRulesAdvancedCheck(db)
|
||||
migrateAllowBlockJunkCheck(db)
|
||||
}
|
||||
|
||||
// migrateAllowBlockJunkCheck rebuilds esrv_mailbox_allowblock for any DB created
|
||||
// before 'junk' was added to the list_type CHECK constraint (the self-service
|
||||
// webmail Blocklist feature) — same rename/recreate/copy/drop approach as
|
||||
// migrateFilterRulesMarkAsSpamCheck above.
|
||||
func migrateAllowBlockJunkCheck(db *sql.DB) {
|
||||
var tableSQL string
|
||||
if err := db.QueryRow(`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'esrv_mailbox_allowblock'`).Scan(&tableSQL); err != nil {
|
||||
return
|
||||
}
|
||||
if strings.Contains(tableSQL, "'junk'") {
|
||||
return
|
||||
}
|
||||
if _, err := db.Exec(`ALTER TABLE esrv_mailbox_allowblock RENAME TO esrv_mailbox_allowblock_old`); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err := db.Exec(schema); err != nil {
|
||||
return
|
||||
}
|
||||
db.Exec(`INSERT INTO esrv_mailbox_allowblock (id, mailbox_id, list_type, pattern, created_at)
|
||||
SELECT id, mailbox_id, list_type, pattern, created_at FROM esrv_mailbox_allowblock_old`)
|
||||
db.Exec(`DROP TABLE esrv_mailbox_allowblock_old`)
|
||||
}
|
||||
|
||||
// migrateFilterRulesMarkAsSpamCheck rebuilds esrv_mailbox_filter_rules for any DB
|
||||
// created before 'mark_as_spam' was added to the action CHECK constraint (webmail's
|
||||
// "Mark as Junk" auto-blacklist rule, see webmail_mail.go's ensureJunkRuleForSender) —
|
||||
// SQLite can't ALTER a CHECK constraint on an existing table, so the only way to widen
|
||||
// created before 'mark_as_spam' was added to the action CHECK constraint (still a
|
||||
// valid rule-builder action today — see the Rules section of Settings) — SQLite
|
||||
// can't ALTER a CHECK constraint on an existing table, so the only way to widen
|
||||
// it is to recreate the table under the current schema and copy the rows across.
|
||||
// Detects the stale constraint by inspecting sqlite_master rather than tracking a
|
||||
// schema-version number, so it stays a no-op forever once a DB is caught up.
|
||||
@@ -574,6 +657,35 @@ func migrateFilterRulesMarkAsSpamCheck(db *sql.DB) {
|
||||
db.Exec(`DROP TABLE esrv_mailbox_filter_rules_old`)
|
||||
}
|
||||
|
||||
// migrateFilterRulesAdvancedCheck rebuilds esrv_mailbox_filter_rules for any DB created
|
||||
// before the advanced rule builder (body/has_attachment/recipient_type conditions,
|
||||
// forward action) widened condition_field/action's CHECK constraints — same
|
||||
// rename/recreate/copy/drop approach as migrateFilterRulesMarkAsSpamCheck above, and
|
||||
// deliberately run after it so a DB that predates both migrations gets caught up by
|
||||
// each in turn without either one needing to know about the other's column set.
|
||||
// name/action_options_json are plain columns already added by migrateAddedColumns
|
||||
// above by the time this runs, so the INSERT below can select them unconditionally.
|
||||
func migrateFilterRulesAdvancedCheck(db *sql.DB) {
|
||||
var tableSQL string
|
||||
if err := db.QueryRow(`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'esrv_mailbox_filter_rules'`).Scan(&tableSQL); err != nil {
|
||||
return
|
||||
}
|
||||
if strings.Contains(tableSQL, "'forward'") {
|
||||
return
|
||||
}
|
||||
if _, err := db.Exec(`ALTER TABLE esrv_mailbox_filter_rules RENAME TO esrv_mailbox_filter_rules_old`); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err := db.Exec(schema); err != nil {
|
||||
return
|
||||
}
|
||||
db.Exec(`INSERT INTO esrv_mailbox_filter_rules
|
||||
(id, mailbox_id, name, priority, condition_field, condition_op, condition_value, action, action_value, action_options_json, is_active, conditions_json, match_type, created_at)
|
||||
SELECT id, mailbox_id, name, priority, condition_field, condition_op, condition_value, action, action_value, action_options_json, is_active, conditions_json, match_type, created_at
|
||||
FROM esrv_mailbox_filter_rules_old`)
|
||||
db.Exec(`DROP TABLE esrv_mailbox_filter_rules_old`)
|
||||
}
|
||||
|
||||
// migrateSpamRenamedToJunk renames the standard "Spam" folder to "Junk" for mailboxes
|
||||
// that already had messages/records under the old name — "Junk" is what most desktop
|
||||
// IMAP clients look for by name (see db.StandardMailboxFolders' doc comment). Always
|
||||
|
||||
Reference in New Issue
Block a user