diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index dcd469bde..2ae18ccee 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -25,3 +25,8 @@ spnego ssh ubuntu workarounds +lfill +ond +retuns +rfill +Uppercases diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 721c54507..6126b1bb5 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -54,6 +54,7 @@ bantime barfoo bayes bcc +benumber bernat bindir bitfield @@ -62,8 +63,8 @@ blitiri bodystructure Boehm Bokmal -Borenstein boollist +Borenstein bsd bsdauth bsearch @@ -134,6 +135,7 @@ deleteheader dentries DESTNAME destuser +dextended DHd Dhesi dirnamename @@ -218,6 +220,7 @@ fgrep fieldstr fileinto findtime +findutils firewalls firstsaved flatcurve @@ -265,6 +268,7 @@ hba hdr hendrik hexblob +hexlify hexuc hhmm highestmodseq @@ -356,6 +360,7 @@ ldaps LDFLAGS ldif leastconn +lenumber letsencrypt LFs lgdb @@ -574,8 +579,8 @@ opie opsec optboolean optname -OR'ing orcpt +OR'ing ORing OSFILE otherinstance @@ -779,6 +784,7 @@ sysexits syslogd syslogging sysreport +systemt tabindex TABLEGEN Taddressverification @@ -837,6 +843,7 @@ unaccessed unaliased unauth UNDRAFT +unhexlify unhibernate unhibernated unhibernating diff --git a/data/settings.js b/data/settings.js index 8667e6957..70a1f3d74 100644 --- a/data/settings.js +++ b/data/settings.js @@ -4333,7 +4333,7 @@ If \`no\`, there will be no report for the authentication result.` }, auth_policy_request_attributes: { - default: 'login=%{requested_username} pwhash=%{hashed_password} remote=%{rip} device_id=%{client_id} protocol=%{protocol} session_id=%{session} fail_type=%{fail_type}', + default: 'login=%{requested_username} pwhash=%{hashed_password} remote=%{remote_ip} device_id=%{client_id} protocol=%{protocol} session_id=%{session} fail_type=%{fail_type}', changed: { settings_auth_policy_request_attributes_changed: ` Default has changed.` @@ -7095,8 +7095,8 @@ The details of how this setting works depends on the used protocol: : ID command can be used to override: * Session ID - * Client IP and port (\`%{rip}\`, \`%{rport}\`) - * Server IP and port (\`%{lip}\`, \`%{lport}\`) + * Client IP and port (\`%{remote_ip}\`, \`%{remote_port}\`) + * Server IP and port (\`%{local_ip}\`, \`%{local_port}\`) \`forward_*\` fields can be sent to auth process's passdb lookup @@ -7107,7 +7107,7 @@ The details of how this setting works depends on the used protocol: : XCLIENT command can be used to override: * Session ID - * Client IP and port (\`%{rip}\`, \`%{rport}\`) + * Client IP and port (\`%{remote_ip}\`, \`%{remote_port}\`) \`forward_*\` fields can be sent to auth process's passdb lookup @@ -7118,7 +7118,7 @@ The details of how this setting works depends on the used protocol: : XCLIENT command can be used to override: * Session ID - * Client IP and port (\`%{rip}\`, \`%{rport}\`) + * Client IP and port (\`%{remote_ip}\`, \`%{remote_port}\`) The trust is always checked against the connecting IP address. Except if HAProxy is used, then the original client IP address is used. @@ -7127,7 +7127,7 @@ The details of how this setting works depends on the used protocol: : XCLIENT command can be used to override: * Session ID - * Client IP and port (\`%{rip}\`, \`%{rport}\`) + * Client IP and port (\`%{remote_ip}\`, \`%{remote_port}\`) * HELO - Overrides what the client sent earlier in the EHLO command * LOGIN - Currently unused * PROTO - Currently unused @@ -7141,7 +7141,7 @@ The details of how this setting works depends on the used protocol: : XCLIENT command can be used to override: * Session ID - * Client IP and port (\`%{rip}\`, \`%{rport}\`) + * Client IP and port (\`%{remote_ip}\`, \`%{remote_port}\`) * HELO - Overrides what the client sent earlier in the LHLO command * LOGIN - Currently unused * PROTO - Currently unused @@ -8700,7 +8700,7 @@ Configures a modifier string for values grouped by the \`%{value}\` : The original value. -\`%{domain}\` +\`%{user | domain}\` : If the value is in \`user@domain\` format, this contains the \`domain\` text. Otherwise empty.` }, diff --git a/data/updates.js b/data/updates.js index f1b7bd389..7e6fc3b38 100644 --- a/data/updates.js +++ b/data/updates.js @@ -75,6 +75,7 @@ export const updates = { variables_auth_variables_protocol: '2.4.0', variables_login_variables_protocol: '2.4.0', variables_owner_user_added: '2.4.0', + var_expand: '2.4.0', /* Tags used in doveadm.js */ diff --git a/docs/core/admin/migration.md b/docs/core/admin/migration.md index 2c7466eed..a012012db 100644 --- a/docs/core/admin/migration.md +++ b/docs/core/admin/migration.md @@ -213,14 +213,14 @@ Common settings: Master password auth: ``` -imapc_user = %u +imapc_user = %{user} imapc_password = supersecret ``` Master user auth: ``` -imapc_user = %u +imapc_user = %{user} imapc_master_user = master-user imapc_password = master-password ``` @@ -228,7 +228,7 @@ imapc_password = master-password Individual password auth: ``` -imapc_user = %u +imapc_user = %{user} # doveadm -o imapc_password=password backup -Ru user imapc: ``` @@ -250,7 +250,7 @@ pop3c_host = hostname # Authenticate as masteruser / masteruser-secret, but use a separate login # user. # If you don't have a master user, remove the pop3c_master_user setting. -pop3c_user = %u +pop3c_user = %{user} pop3c_master_user = masteruser pop3c_password = masteruser-secret diff --git a/docs/core/admin/rawlog.md b/docs/core/admin/rawlog.md index bbbaf3d46..7627d6c7d 100644 --- a/docs/core/admin/rawlog.md +++ b/docs/core/admin/rawlog.md @@ -99,7 +99,7 @@ If your userdb can't return a home directory directly, you can add: userdb db1 { # ... fields { - home = /home/%u + home = /home/%{user} # or temporarily even e.g. home = /tmp/temp-home } } diff --git a/docs/core/admin/testing.md b/docs/core/admin/testing.md index a9391d7d1..caef2420a 100644 --- a/docs/core/admin/testing.md +++ b/docs/core/admin/testing.md @@ -259,7 +259,7 @@ System configuration Enable LMTP delivery times in the configuration: ```[dovecot.conf] -deliver_log_format = msgid=%m from=<%f> size=%p vsize=%w session=%{session_time}ms delivery=%{delivery_time}ms: %$ +deliver_log_format = msgid=%{msgid} from=<%{from}> size=%{size} vsize=%{vsize} session=%{session_time}ms delivery=%{delivery_time}ms: %{message} ``` You can then see log entries like: @@ -332,7 +332,7 @@ total_user_count = 800 rampup_time = 0s user lmtptest { - username_format = testuser%n + username_format = testuser%{num} count = 100% mail_inbox_delivery_interval = 1s @@ -390,7 +390,7 @@ total_user_count = 2000000 rampup_time = 600s user pop3 { - username_format = testuser%7n + username_format = testuser%{num | fill('0', 7)} username_start_index = 1 count = 100% @@ -433,7 +433,7 @@ total_user_count = 4000000 rampup_time = 60s user imap_poweruser { - username_format = testuser%7n + username_format = testuser%{num | fill('0', 7)} username_start_index = 2000000 count = 50% diff --git a/docs/core/config/auth/databases/ldap.md b/docs/core/config/auth/databases/ldap.md index b838ce950..0ef00bdbd 100644 --- a/docs/core/config/auth/databases/ldap.md +++ b/docs/core/config/auth/databases/ldap.md @@ -83,7 +83,7 @@ $ ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f doveauth_access.ldif The two important settings in password lookups are: - [[setting,ldap_filter]] specifies the LDAP filter how user is found from the - LDAP. You can use all the normal [[variable]] like `%u` in the filter. + LDAP. You can use all the normal [[variable]] like `%{user}` in the filter. - [[setting,passdb_fields]] specifies a list of attributes that are returned and how to produce the returned value. @@ -133,7 +133,7 @@ to the same case as it's in the LDAP database. You can do this by returning "user" field in [[setting,passdb_fields]] setting, as shown in the above example. If you can't normalize the username in LDAP, you can alternatively -lowercase the username via [[setting,auth_username_format,%Lu]]. +lowercase the username via [[setting,auth_username_format,%{user | lower}]]. #### Use Worker @@ -160,7 +160,7 @@ A typical configuration would look like: passdb ldap { bind = no default_password_scheme = MD5 - ldap_filter = (&(objectClass=posixAccount)(uid=%u)) + ldap_filter = (&(objectClass=posixAccount)(uid=%{user})) fields { user = %{ldap:uid} password = %{ldap:userPassword} @@ -201,7 +201,7 @@ Example: ```[dovecot.conf] passdb ldap { bind = yes - ldap_filter = (&(objectClass=posixAccount)(uid=%u)) + ldap_filter = (&(objectClass=posixAccount)(uid=%{user})) fields { user = %{ldap:uid} } @@ -224,14 +224,14 @@ so the prefetch optimization doesn't help. If you're using DN template, [[setting,passdb_fields]] and [[setting,ldap_filter]] settings are completely ignored. That means you can't make passdb return any [[link,passdb_extra_fields]]. You should also set -[[setting,auth_username_format,%Lu]] in `dovecot.conf` to normalize the +[[setting,auth_username_format,%{user | lower}]] in `dovecot.conf` to normalize the username by lowercasing it. ::: code-group ```[dovecot.conf] passdb ldap { bind = yes - bind_userdn = cn=%u,ou=people,o=org + bind_userdn = cn=%{user},ou=people,o=org } ``` ::: @@ -355,7 +355,7 @@ The most important settings are: ::: code-group ```[dovecot.conf] - passdb_ldap_bind_userdn = %u + passdb_ldap_bind_userdn = %{user} passdb_ldap_bind = yes ``` ::: @@ -403,7 +403,7 @@ distinct values inside each [[setting,passdb]] / [[setting,userdb]] section): ::: code-group ```[dovecot.conf] -ldap_filter = (mailRoutingAddress=%u) +ldap_filter = (mailRoutingAddress=%{user}) ``` ::: @@ -411,7 +411,7 @@ ldap_filter = (mailRoutingAddress=%u) ::: code-group ```[dovecot.conf] - ldap_filter = (mailRoutingAddress=%u) + ldap_filter = (mailRoutingAddress=%{user}) ldap_iterate_filter = (objectClass=messageStoreRecipient) iterate_fields { user = %{ldap:mailRoutingAddress} @@ -434,7 +434,7 @@ The following variables can be used inside the [[setting,passdb]] / [[setting,us | `%{ldap_multi:attrName:::default}` | [[added,ldap_multi_added]] How to specify a column `":"` as separator, default explicitly defined. | | `%{ldap_multi:attrName:,}` | [[added,ldap_multi_added]] How to specify a comma `","` as separator, default is `""`. | | `%{ldap_multi:attrName:,:default}` | [[added,ldap_multi_added]] How to specify a comma `","` as separator, default explicitly defined. | -| `%{ldap_dn}` | Retrieves the Distinguished Name of the entry. | +| `%{ldap:dn}` | Retrieves the Distinguished Name of the entry. | ### Multiple Queries via userdbs @@ -463,7 +463,7 @@ userdb ldap2 { ### Variables and Domains -User names and domains may be distinguished using the Variables `%n` and `%d`. +User names and domains may be distinguished using the Variables `%{user | username}` and `%{user | domain}`. They split the previous username at the `@` character. The previous username is: @@ -477,19 +477,19 @@ The previous username is: If the (LDAP) password database has: ``` fields { - user = %n + user = %{user | username} } ``` then the domain part of the login name will be stripped by the password database. -- The userdb will not see any domain part, i.e. `%n` and `%u` are the same +- The userdb will not see any domain part, i.e. `%{user | username}` and `%{user}` are the same thing for the userdb. The userdb may set a new username, too, using: ``` fields { user = ... } ``` - This will be used for Logging `%u` and `%d` + This will be used for Logging `%{user}` and `%{user | domain}` variables in other parts of the configuration (e.g. quota file names). ::: code-group @@ -540,7 +540,7 @@ them globally with [[setting,mail_uid]] and [[setting,mail_gid]] settings instea returning them from LDAP. ``` -ldap_filter = (&(objectClass=posixAccount)(uid=%u)) +ldap_filter = (&(objectClass=posixAccount)(uid=%{user})) ldap_iterate_filter = (objectClass=posixAccount) fields { home = %{ldap:homeDirectory} @@ -580,12 +580,12 @@ fields { ``` You can add static fields that aren't looked up from LDAP. For example -create a "mail_path" field with value `/var/vmail/%d/%n/Maildir`: +create a "mail_path" field with value `/var/vmail/%{user | domain}/%{user | username}/Maildir`: ``` fields { quota_storage_size = %{ldap:quotaBytes}B - mail_path = /var/vmail/%d/%n/Maildir + mail_path = /var/vmail/%{user | domain}/%{user | username}/Maildir } ``` @@ -596,7 +596,7 @@ userDomain attribute doesn't exist, example.com is used instead. ### Variables and Domains User names and domains may be distinguished using the [[variable]] -`%n` and `%d`. They split the *previous username* at the "@" character. The +`%{user | username}` and `%{user | domain}`. They split the *previous username* at the "@" character. The *previous username* is: - For LMTP, it will be `user@hostname`, where hostname depends on e.g. @@ -604,9 +604,9 @@ User names and domains may be distinguished using the [[variable]] - For IMAP, it will be whatever the password database has designated as the username. If the (LDAP) password database [[setting,passdb_fields ]] - contains `user=%n`, then the domain part of the login name will be stripped by + contains `user=%{user | username}`, then the domain part of the login name will be stripped by the password database. The userdb will not see any domain part, i.e. - %n and %u are the same thing for the userdb. + %{user | username} and %{user} are the same thing for the userdb. The userdb may set a new username, too, using ``` @@ -619,5 +619,5 @@ This will be used for: - Logging -- `%u` and `%d` variables in other parts of the configuration (e.g. quota +- `%{user}` and `%{user | domain}` variables in other parts of the configuration (e.g. quota file names) diff --git a/docs/core/config/auth/databases/lua.md b/docs/core/config/auth/databases/lua.md index 53c86ff51..b929d3e5b 100644 --- a/docs/core/config/auth/databases/lua.md +++ b/docs/core/config/auth/databases/lua.md @@ -56,7 +56,7 @@ function auth_passdb_lookup(req) end function auth_passdb_get_cache_key() - return "%{username}\t%{protocol}" + return "%{user | username}\t%{protocol}" end ``` ::: diff --git a/docs/core/config/auth/databases/oauth2.md b/docs/core/config/auth/databases/oauth2.md index 578e0f473..60a086172 100644 --- a/docs/core/config/auth/databases/oauth2.md +++ b/docs/core/config/auth/databases/oauth2.md @@ -100,8 +100,8 @@ passdb static { fields { nopassword = yes proxy = yes - proxy_mech = %m - # ... + proxy_mech = %{mechanism} + # ... } } ``` @@ -113,7 +113,7 @@ oauth2 { # ... fields { proxy = y - proxy_mech = %m + proxy_mech = %{mech} } } ``` diff --git a/docs/core/config/auth/databases/passwd.md b/docs/core/config/auth/databases/passwd.md index 58557b2fe..e3ffe9d90 100644 --- a/docs/core/config/auth/databases/passwd.md +++ b/docs/core/config/auth/databases/passwd.md @@ -33,9 +33,9 @@ For example: ```[dovecot.conf] userdb passwd { fields { - home = /var/mail/%{username} + home = /var/mail/%{user | username} mail_driver = maildir - mail_path = /var/mail/%{username}/Maildir + mail_path = /var/mail/%{user | username}/Maildir } } ``` @@ -53,9 +53,9 @@ userdb passwd { fields { uid = %{passwd:uid:vmail} gid = %{passwd:gid:vmail} - home = /var/mail/%{username} + home = /var/mail/%{user | username} mail_driver = maildir - mail_path = /var/mail/%{username}/Maildir + mail_path = /var/mail/%{user | username}/Maildir } } ``` diff --git a/docs/core/config/auth/databases/passwd_file.md b/docs/core/config/auth/databases/passwd_file.md index f94efaf79..1431f8019 100644 --- a/docs/core/config/auth/databases/passwd_file.md +++ b/docs/core/config/auth/databases/passwd_file.md @@ -60,7 +60,7 @@ You can use all [[variable]] in the passwd-file filenames, for example: ```[dovecot.conf] passdb passwd-file { # Each domain has a separate passwd-file: - passwd_file_path = /etc/auth/%d/passwd + passwd_file_path = /etc/auth/%{user | domain}/passwd } ``` @@ -82,16 +82,16 @@ userdb lookup's fields. The available fields are: ```[dovecot.conf] passdb passwd-file { default_password_scheme = plain-md5 - auth_username_format = %n + auth_username_format = %{user | username} passwd_file_path = /etc/imap.passwd } userdb passwd-file { - auth_username_format = %n + auth_username_format = %{user | username} passwd_file_path = /etc/imap.passwd fields { uid:default = vmail gid:default = vmail - home:default = /home/vmail/%u + home:default = /home/vmail/%{user} } } ``` @@ -159,12 +159,12 @@ other `userdb` and `passdb` sections: ``` passdb passwd-file { - auth_username_format = %n + auth_username_format = %{user | username} passwd_file_path = /path/to/file-with-encrypted-passwords } userdb passwd-file { - auth_username_format = %n + auth_username_format = %{user | username} passwd_file_path = /path/to/file-with-encrypted-passwords } ``` diff --git a/docs/core/config/auth/databases/prefetch.md b/docs/core/config/auth/databases/prefetch.md index 77b9228fe..2b5bb9a12 100644 --- a/docs/core/config/auth/databases/prefetch.md +++ b/docs/core/config/auth/databases/prefetch.md @@ -73,7 +73,7 @@ mysql localhost { passdb sql { query = SELECT userid AS user, password, home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \ FROM users \ - WHERE userid = '%u' + WHERE userid = '%{user}' } } @@ -82,6 +82,6 @@ userdb prefetch { # The userdb below is used only by lda. userdb sql { - query = SELECT home, uid, gid FROM users WHERE userid = '%u' + query = SELECT home, uid, gid FROM users WHERE userid = '%{user}' } ``` diff --git a/docs/core/config/auth/databases/sql.md b/docs/core/config/auth/databases/sql.md index dd74e6cbb..ab23eb35e 100644 --- a/docs/core/config/auth/databases/sql.md +++ b/docs/core/config/auth/databases/sql.md @@ -31,7 +31,7 @@ password. It must return a field named `password`. If you have it by any other name in the database, you can use the SQL's `AS` keyword (`SELECT pw AS password ..`). -You can use all the normal [[variable]] such as `%u` in the SQL query. +You can use all the normal [[variable]] such as `%{user}` in the SQL query. If all the passwords are in same format, you can use [[setting,passdb_default_password_scheme]] to specify it. Otherwise each @@ -66,13 +66,13 @@ If the passwords are in some special format in the SQL server that Dovecot doesn't recognize, it's still possible to use them. Change the SQL query to return NULL as the password and return the row only if the password matches. You'll also need to return a non-NULL `nopassword` field. The password is in -`%w` variable. For example: +`%{password}` variable. For example: ```[dovecot.conf] passdb sql { query = SELECT NULL AS password, 'Y' as nopassword, userid AS user \ FROM users \ - WHERE userid = '%u' AND mysql_pass = password('%w') + WHERE userid = '%{user}' AND mysql_pass = password('%{password}') } ``` @@ -171,12 +171,12 @@ mysql /var/run/mysqld/mysqld.sock { passdb sql { query = SELECT userid AS username, domain, password \ FROM users \ - WHERE userid = '%n' AND domain = '%d' + WHERE userid = '%{user | username}' AND domain = '%{user | domain}' } userdb sql { query = SELECT home, uid, gid \ FROM users \ - WHERE userid = '%n' AND domain = '%d' + WHERE userid = '%{user | username}' AND domain = '%{user | domain}' # For using doveadm -A: iterate_query = SELECT userid AS username, domain FROM users } @@ -202,12 +202,12 @@ pgsql localhost { passdb sql { query = SELECT userid AS username, domain, password \ FROM users \ - WHERE userid = '%n' AND domain = '%d' + WHERE userid = '%{user | username}' AND domain = '%{user | domain}' } userdb sql { query = SELECT home, uid, gid \ FROM users \ - WHERE userid = '%n' AND domain = '%d' + WHERE userid = '%{user | username}' AND domain = '%{user | domain}' # For using doveadm -A: iterate_query = SELECT userid AS username, domain FROM users } @@ -224,12 +224,12 @@ sqlite_path = /path/to/sqlite.db passdb sql { query = SELECT userid AS username, domain, password \ FROM users \ - WHERE userid = '%n' AND domain = '%d' + WHERE userid = '%{user | username}' AND domain = '%{user | domain}' } userdb sql { query = SELECT home, uid, gid \ FROM users \ - WHERE userid = '%n' AND domain = '%d' + WHERE userid = '%{user | username}' AND domain = '%{user | domain}' # For using doveadm -A: iterate_query = SELECT userid AS username, domain FROM users } diff --git a/docs/core/config/auth/databases/static.md b/docs/core/config/auth/databases/static.md index 214415a5e..69ab31ed0 100644 --- a/docs/core/config/auth/databases/static.md +++ b/docs/core/config/auth/databases/static.md @@ -93,7 +93,7 @@ userdb static { fields { uid = 500 gid = 500 - home = /home/%u + home = /home/%{user} } } ``` diff --git a/docs/core/config/auth/master_users.md b/docs/core/config/auth/master_users.md index b41be3ca3..35e9d48c3 100644 --- a/docs/core/config/auth/master_users.md +++ b/docs/core/config/auth/master_users.md @@ -71,10 +71,10 @@ The options for handling this are: You can create a `default ACL`, that applies to all mailboxes. See example below. -2. Set `plugin { acl_user=%u }`. This preserves the master_user for other +2. Set `plugin { acl_user=%{user} }`. This preserves the master_user for other purposes (e.g. `%{master_user}` variable). -3. Set `plugin { master_user=%u }`. This fully hides that master user login is +3. Set `plugin { master_user=%{user} }`. This fully hides that master user login is being used. Example configuration: @@ -127,7 +127,7 @@ auth_master_user_separator = * passdb db1 { driver = sql - query = SELECT password FROM users WHERE userid = '%u' and master_user = true + query = SELECT password FROM users WHERE userid = '%{user}' and master_user = true master = yes result_success = continue } @@ -244,7 +244,7 @@ authentication. The query for that is as follows: passdb sql { query = SELECT user_name, domain_name, password \ FROM users \ - WHERE user_name = '%n' AND domain_name = '%d' + WHERE user_name = '%{user | username}' AND domain_name = '%{user | domain}' } ``` @@ -256,7 +256,7 @@ set to `1`. The query would be: passdb sql { query = SELECT user_name, domain_name, password \ FROM users \ - WHERE user_name = '%n' AND domain_name = '%d' AND masteradmin='1' + WHERE user_name = '%{user | username}' AND domain_name = '%{user | domain}' AND masteradmin='1' } ``` @@ -269,7 +269,7 @@ Your query would be as follows: passdb sql { query = SELECT user_name, domain_name, password \ FROM users \ - WHERE user_name = '%n' AND domain_name = '%d' AND owns_domain='1' AND '%d'='%{login_domain}' + WHERE user_name = '%{user | username}' AND domain_name = '%{user | domain}' AND owns_domain='1' AND '%{user | domain}'='%{login_domain}' } ``` @@ -285,7 +285,7 @@ they control. The query would be as follows: passdb sql { query = SELECT user_name, domain_name, password \ FROM users, ownership \ - WHERE user_name = '%n' AND domain_name = '%d' AND login_id='%u' AND owned_object='%{login_domain}' + WHERE user_name = '%{user | username}' AND domain_name = '%{user | domain}' AND login_id='%{user}' AND owned_object='%{login_domain}' } ``` @@ -296,9 +296,9 @@ into one giant query that does everything. passdb sql { query = SELECT user_name, domain_name, password \ FROM users, ownership \ - WHERE user_name = '%n' AND domain_name = '%d' \ - AND ( (masteradmin='1') OR (owns_domain='1' AND '%d'='%{login_domain}') \ - OR (login_id='%u' and owned_object='%{login_domain}') ) \ + WHERE user_name = '%{user | username}' AND domain_name = '%{user | domain}' \ + AND ( (masteradmin='1') OR (owns_domain='1' AND '%{user | domain}'='%{login_domain}') \ + OR (login_id='%{user}' and owned_object='%{login_domain}') ) \ GROUP BY uid } ``` diff --git a/docs/core/config/auth/mechanisms/kerberos.md b/docs/core/config/auth/mechanisms/kerberos.md index dec485bd5..90c804f2c 100644 --- a/docs/core/config/auth/mechanisms/kerberos.md +++ b/docs/core/config/auth/mechanisms/kerberos.md @@ -102,7 +102,7 @@ userdb static { fields { uid = vmail gid = vmail - home = /var/vmail/%u + home = /var/vmail/%{user} } } ``` diff --git a/docs/core/config/auth/mechanisms/winbind.md b/docs/core/config/auth/mechanisms/winbind.md index 7d4be7363..549e15615 100644 --- a/docs/core/config/auth/mechanisms/winbind.md +++ b/docs/core/config/auth/mechanisms/winbind.md @@ -16,7 +16,7 @@ The usernames, returned by winbind, can contain some domain part (either "DOMAIN\user" or "user@example.com"). Such usernames are always transformed to the form of "user@domain". To strip domain part (to obtain corresponding local username, for example), set -[[setting,auth_username_format,%n]]. +[[setting,auth_username_format,%{user | username}]]. Dovecot needs path to Samba's `ntlm_auth` binary to perform the authentication. You can change the path with diff --git a/docs/core/config/auth/mutltiple.md b/docs/core/config/auth/mutltiple.md index 4a2ee8a10..fbfe71594 100644 --- a/docs/core/config/auth/mutltiple.md +++ b/docs/core/config/auth/mutltiple.md @@ -22,7 +22,7 @@ directory. * Virtual users' mails: /var/vmail/domain/user/Maildir This can be done by simply having both system and virtual userdbs return home -directory properly (i.e. virtual users' `home=/var/vmail/%d/%n`) and then set +directory properly (i.e. virtual users' `home=/var/vmail/%{user | domain}/%{user | username}`) and then set [[setting,mail_path,~/Maildir]]. If it's not possible to have a home directory for virtual users (avoid that if @@ -47,7 +47,7 @@ mysql localhost { # try to authenticate using SQL database first passdb sql { - query = SELECT userid AS user, password FROM users WHERE userid = '%u' + query = SELECT userid AS user, password FROM users WHERE userid = '%{user}' } # fallback to PAM @@ -56,7 +56,7 @@ passdb pam { # look up users from SQL first (even if authentication was done using PAM!) userdb sql { - query = SELECT uid, gid, '/var/vmail/%d/%n' AS home FROM users WHERE userid = '%u' + query = SELECT uid, gid, '/var/vmail/%{user | domain}/%{user | username}' AS home FROM users WHERE userid = '%{user}' } # if not found, fallback to /etc/passwd diff --git a/docs/core/config/auth/passdb.md b/docs/core/config/auth/passdb.md index 5f71d56a4..16fc55baa 100644 --- a/docs/core/config/auth/passdb.md +++ b/docs/core/config/auth/passdb.md @@ -258,7 +258,7 @@ An example [[setting,passdb_sql_query]] would be: passdb sql { query = SELECT concat(user, '@', domain) AS user, password \ FROM users \ - WHERE user = '%n' and domain = '%d' + WHERE user = '%{user | username}' and domain = '%{user | domain}' } ``` @@ -268,7 +268,7 @@ You can also update "username" and "domain" fields separately: passdb sql { query = SELECT user AS username, domain, password \ FROM users \ - WHERE user = '%n' and domain = '%d' + WHERE user = '%{user | username}' and domain = '%{user | domain}' } ``` @@ -447,7 +447,7 @@ Import `name=value` to login events. ```[dovecot.conf] passdb sql { query = SELECT userid AS user, password, 'Y' as proxy, host \ - FROM users WHERE userid = '%u' + FROM users WHERE userid = '%{user}' } } ``` diff --git a/docs/core/config/auth/proxies.md b/docs/core/config/auth/proxies.md index c8b00fc51..886142c6e 100644 --- a/docs/core/config/auth/proxies.md +++ b/docs/core/config/auth/proxies.md @@ -332,7 +332,7 @@ mysql_password = secret passdb sql { query = SELECT NULL AS password, 'Y' as nopassword, host, destuser, 'Y' AS proxy \ FROM proxy \ - WHERE user = '%u' + WHERE user = '%{user}' } ``` @@ -364,12 +364,12 @@ mysql localhost { passdb sql { query = SELECT concat(user, '@', domain) AS user, password, host, 'Y' AS proxy_maybe \ FROM users \ - WHERE user = '%n' AND domain = '%d' + WHERE user = '%{user | username}' AND domain = '%{user | domain}' } userdb sql { query = SELECT user AS username, domain, home \ FROM users \ - WHERE user = '%n' AND domain = '%d' + WHERE user = '%{user | username}' AND domain = '%{user | domain}' } ``` ::: diff --git a/docs/core/config/auth/referrals.md b/docs/core/config/auth/referrals.md index b15988108..e0149ef7a 100644 --- a/docs/core/config/auth/referrals.md +++ b/docs/core/config/auth/referrals.md @@ -67,7 +67,7 @@ Forward user to another server after successful authentication (SQL): passdb sql { query = SELECT password, host, 'Y' as nologin \ FROM users \ - WHERE userid = '%u' + WHERE userid = '%{user}' } ``` diff --git a/docs/core/config/auth/userdb.md b/docs/core/config/auth/userdb.md index 59e1d17ec..727c54a8e 100644 --- a/docs/core/config/auth/userdb.md +++ b/docs/core/config/auth/userdb.md @@ -230,7 +230,7 @@ The separator setting can be overridden by returning userdb sql { query = SELECT home, uid, gid, CONCAT(quota_bytes, 'B') AS quota_storage_size, separator AS "namespace/default/separator" \ FROM users \ - WHERE username = '%n' and domain = '%d' + WHERE username = '%{user | username}' and domain = '%{user | domain}' } ``` ::: diff --git a/docs/core/config/guides/nfs.md b/docs/core/config/guides/nfs.md index 19a9091d3..ffbddbf89 100644 --- a/docs/core/config/guides/nfs.md +++ b/docs/core/config/guides/nfs.md @@ -64,13 +64,13 @@ Potential optimizations to use: downside is that it requires running periodic [[doveadm,purge]] for each user. Theses commands should be run via a doveadm proxy so they are run in the proper backends. -* Use [[setting,mail_volatile_path,/dev/shm/dovecot/%2.256Nu/%u]] to store some +* Use [[setting,mail_volatile_path,/dev/shm/dovecot/%{user | sha1 % 256 | hex(2)}/%{user}]] to store some temporary files (e.g. lock files) in tmpfs rather than NFS. -* Use [[setting,mailbox_list_index_prefix,/fast/%2.256Nu/%u]] to use "smaller +* Use [[setting,mailbox_list_index_prefix,/fast/%{user | sha1 % 256 | hex(2)}/%{user}]] to use "smaller fast storage" for index files and "larger slow storage" for mail files. Also use [[setting,mailbox_list_iter_from_index_dir,yes]] to list mailboxes via the fast index storage rather than the slow mail storage. -* Use [[setting,mailbox_list_iter_from_index_dir,/slow/%2.256Nu/%u]] to use +* Use [[setting,mailbox_list_iter_from_index_dir,/slow/%{user | sha1 % 256 | hex(2)}/%{user}]] to use "smaller fast storage" for new mails and "larger slow storage" for old mails. The [[doveadm,altmove]] command needs to be run periodically. Also use [[setting,mail_alt_check,no]] to disable a sanity check to make sure alt diff --git a/docs/core/config/guides/quick.md b/docs/core/config/guides/quick.md index 510171960..0cf0b6649 100644 --- a/docs/core/config/guides/quick.md +++ b/docs/core/config/guides/quick.md @@ -26,7 +26,7 @@ You need to create group `vmail` and user `vmail`. ::: code-group ```[dovecot.conf] -mail_home = /srv/mail/%Lu +mail_home = /srv/mail/%{user | lower} mail_driver = sdbox mail_path = ~/Mail diff --git a/docs/core/config/mailbox/formats/dbox.md b/docs/core/config/mailbox/formats/dbox.md index f26c0735c..548451e6b 100644 --- a/docs/core/config/mailbox/formats/dbox.md +++ b/docs/core/config/mailbox/formats/dbox.md @@ -194,13 +194,13 @@ example: ```[dovecot.conf] mail_driver = mdbox -mail_path = /var/vmail/%d/%n -mail_alt_path = /altstorage/vmail/%d/%n +mail_path = /var/vmail/%{user | domain}/%{user | username} +mail_alt_path = /altstorage/vmail/%{user | domain}/%{user | username} ``` -will make Dovecot look for message data first under `/var/vmail/%d/%n` +will make Dovecot look for message data first under `/var/vmail/%{user | domain}/%{user | username}` ("primary storage"), and if it is not found there it will look under -`/altstorage/vmail/%d/%n` ("alternate storage") instead. There's no problem +`/altstorage/vmail/%{user | domain}/%{user | username}` ("alternate storage") instead. There's no problem having the same (identical) file in both storages. Keep the unmounted `/altstorage` directory permissions such that Dovecot @@ -236,10 +236,10 @@ e.g. [[doveadm,fetch]] or [[doveadm,import]] commands to access the mails. For example, if you have: * [[setting,mail_driver,mdbox]], * [[setting,mail_path,~/mdbox]], -* [[setting,mail_index_path,/var/index/%u]], +* [[setting,mail_index_path,/var/index/%{user}]], use: -[[doveadm,import,-p mail_index_path=/var/index/%u mdbox_deleted:~/mdbox "" subject oops]]. +[[doveadm,import,-p mail_index_path=/var/index/%{user} mdbox_deleted:~/mdbox "" subject oops]]. This finds a deleted mail with subject "oops" and imports it into INBOX. diff --git a/docs/core/config/mailbox/formats/maildir.md b/docs/core/config/mailbox/formats/maildir.md index 48c72cfd6..cd9918339 100644 --- a/docs/core/config/mailbox/formats/maildir.md +++ b/docs/core/config/mailbox/formats/maildir.md @@ -349,7 +349,7 @@ can specify this with the [[setting,mail_control_path]] setting: ```[dovecot.conf] mail_driver = maildir mail_path = ~/Maildir -mail_control_path = /var/no-quota/%u +mail_control_path = /var/no-quota/%{user} ``` ### Index Files @@ -362,7 +362,7 @@ path. Example: ```[dovecot.conf] mail_driver = maildir mail_path = ~/Maildir -mail_index_path = /var/indexes/%u +mail_index_path = /var/indexes/%{user} ``` ### Optimizations diff --git a/docs/core/config/mailbox/formats/mbox.md b/docs/core/config/mailbox/formats/mbox.md index 0172149ec..9c77d0271 100644 --- a/docs/core/config/mailbox/formats/mbox.md +++ b/docs/core/config/mailbox/formats/mbox.md @@ -402,7 +402,7 @@ There are four possibilities why the error message could happen: the same index file for multiple different mboxes! - This could happen if you let Dovecot do mailbox autodetection and - it sometimes uses `/var/mail/%u` (when it exists) and other + it sometimes uses `/var/mail/%{user}` (when it exists) and other times `~/mail/inbox`. Use explicit [[link,mail_location]] settings to make sure the same INBOX is used. @@ -511,10 +511,10 @@ well. Usually `~/mail` is a good choice for this. For an installation such as this, the mail location is specified with: ```[dovecot.con] -# %u is replaced with the username that logs in +# %{user} is replaced with the username that logs in mail_driver = mbox mail_path = ~/mail -mail_inbox_path = /var/mail/%u +mail_inbox_path = /var/mail/%{user} ``` It's in no way a requirement to have the INBOX in `/var/mail/` directory. In @@ -546,8 +546,8 @@ change the index path. Example: ```[dovecot.conf] mail_driver = mbox mail_path = ~/mail -mail_inbox_path = /var/mail/%u -mail_index_path = /var/indexes/%u +mail_inbox_path = /var/mail/%{user} +mail_index_path = /var/indexes/%{user} ``` ### Locking @@ -615,7 +615,7 @@ files, you can work around not having a per-user directory: * Set [[setting,mail_path]] to an empty non-writable directory, e.g. [[setting,mail_path,/var/empty]]. * Set [[setting,mail_inbox_path]], e.g. - [[setting,mail_inbox_path,/var/mail/%u]]. + [[setting,mail_inbox_path,/var/mail/%{user}]]. * Note that if you have IMAP users, they'll see `/var/empty` as the directory containing other mailboxes than INBOX. If the directory is writable, all the users will have their mailboxes shared. diff --git a/docs/core/config/mailbox/mail_location.md b/docs/core/config/mailbox/mail_location.md index 443bcfd59..c71b70258 100644 --- a/docs/core/config/mailbox/mail_location.md +++ b/docs/core/config/mailbox/mail_location.md @@ -27,29 +27,17 @@ See [[variable]] for a full list, but the most commonly used ones are: | Variable | Description | | -------- | ----------- | -| `%u` | Full username. | -| `%n` | User part in `user@domain`; same as `%u` if there's no domain. | -| `%d` | Domain part in `user@domain`; empty if there's no domain. | +| `%{user}` | Full username. | +| `%{user | username}` | User part in `user@domain`; same as `%{user}` if there's no domain. | +| `%{user | domain}` | Domain part in `user@domain`; empty if there's no domain. | ### Directory Hashing -You can use three different kinds of hashes in [[variable]]. +Examples on how to do it: -* `%N` is MD5-based "new hash" which works similarly to `%H` except it - gives more uniform results. +* `%{ user | sha1 % 256 | hex(2)}` would give maximum 256 different hashes in range of `00` to `ff`. - * Example: `%2.256N` would return maximum 256 different hashes in range - `00..ff`. - -* `%M` returns a MD5 hash of the string as hex. This can be used for two - level hashing by getting substrings of the MD5 hash. - - * Example: `%1Mu/%2.1Mu/%u` returns directories from `0/0/user` to - `f/f/user`. - -* `%H` returns a 32bit hash of the given string as hex. - - * This is the old, deprecated method. `%N` should be used instead. +See also [[link,upgrading_directory_hashing]] ## Index Files @@ -64,7 +52,7 @@ setting. For example: ```[dovecot.conf] mail_driver = maildir mail_path = ~/Maildir -mail_index_path = /var/indexes/%u +mail_index_path = /var/indexes/%{user} ``` The index directories are created automatically, but note that it requires @@ -158,7 +146,7 @@ If you really don't want to set any home directory, you can use something like: ```[dovecot.conf] mail_driver = maildir -mail_path = /home/%u/Maildir +mail_path = /home/%{user}/Maildir ``` ## Per-User Mail Locations @@ -167,7 +155,7 @@ It's possible to override the default mail location for specific users by making the [[link,userdb]] return the settings as extra field. ::: tip -Note that `%h` doesn't work in the userdb queries or templates. `~/` gets +Note that `%{home}` doesn't work in the userdb queries or templates. `~/` gets expanded later, so use it instead. If you have explicit settings inside [[link,namespaces,namespace { .. }]] they @@ -179,7 +167,7 @@ need to be overridden in userdb with `namespace//` prefix. For example ```[dovecot.conf] userdb sql { - query = SELECT home, uid, gid, mail_path FROM users WHERE user = '%u' + query = SELECT home, uid, gid, mail_path FROM users WHERE user = '%{user}' } ``` @@ -269,10 +257,10 @@ multiple mailboxes, you'll also have to have a directory for them as well. Usually `~/mail` is a good choice for this. For installation such as this, the mail location settings are specified with, -where `%u` is replaced with the username that logs in: +where `%{user}` is replaced with the username that logs in: * [[setting,mail_driver,mbox]], * [[setting,mail_path,~/mail]], and -* [[setting,mail_inbox_path,/var/mail/%u]]. +* [[setting,mail_inbox_path,/var/mail/%{user}]]. Similarly if your INBOX is in `~/mbox`, use: * [[setting,mail_inbox_path,~/mbox]]. diff --git a/docs/core/config/namespaces.md b/docs/core/config/namespaces.md index 0670f0fd3..6ac3f14a9 100644 --- a/docs/core/config/namespaces.md +++ b/docs/core/config/namespaces.md @@ -182,7 +182,7 @@ with the given list. userdb static { fields { namespace += special - namespace/special/mail_path = /var/special/%u + namespace/special/mail_path = /var/special/%{user} namespace/special/prefix = special/ } } @@ -206,7 +206,7 @@ namespace { prefix = "#mbox/" mail_driver = mbox mail_path = ~/mail - mail_index_path = /var/mail/%u + mail_index_path = /var/mail/%{user} inbox = yes hidden = yes list = no @@ -256,7 +256,7 @@ namespace compat2 { } namespace compat3 { separator = / - prefix = ~%u/mail/ + prefix = ~%{user}/mail/ hidden = yes list = no alias_for = inbox diff --git a/docs/core/config/optimization.md b/docs/core/config/optimization.md index 44d51df8a..a4ecc01a8 100644 --- a/docs/core/config/optimization.md +++ b/docs/core/config/optimization.md @@ -71,7 +71,7 @@ the most useful optimization you can do. - [[setting,mail_prefetch_count]] setting may be helpful with some mailbox formats. -- [[setting,mail_volatile_path,/tmp/dovecot-volatile/%2.256Nu/%u]] moves, e.g., +- [[setting,mail_volatile_path,/tmp/dovecot-volatile/%{user | sha1 % 256 | hex(2)}/%{user}]] moves, e.g., lock files to the volatile directory. This is helpful especially if the [[link,mail_location,mail location settings]] otherwise point to a remote filesystem like NFS. diff --git a/docs/core/config/shared_mailboxes.md b/docs/core/config/shared_mailboxes.md index 9b6c368a8..8afb25789 100644 --- a/docs/core/config/shared_mailboxes.md +++ b/docs/core/config/shared_mailboxes.md @@ -274,7 +274,7 @@ the domain part and instead use [[setting,namespace_prefix,shared/$username/]]. [[changed,namespace_prefix_shared_variables_changed]] The shared namespaces now use `$user`, `$username` and `$domain` template variables, rather than -the old `%%u`, `%%n` and `%%d`. +the old `%%{user}`, `%%{user | username}` and `%%{user | domain}`. [[setting,namespace_list,children]] specifies that if no one has shared mailboxes to the user, the "shared" directory isn't listed by the LIST command. @@ -351,7 +351,7 @@ each domain: ```[dovecot.conf] acl_sharing_map { dict file { - path = /var/mail/%d/shared-mailboxes.db + path = /var/mail/%{user | domain}/shared-mailboxes.db } } ``` @@ -518,7 +518,7 @@ initially have "lookup" right, but later we don't: ### Troubleshooting - Make sure `$user` or `$username` is specified in the - [[setting,namespace_prefix]] setting rather than the old `%%u` or `%%n`. + [[setting,namespace_prefix]] setting rather than the old `%%{user}` or `%%{user | username}`. - Make sure the [[setting,mail_path]] is set correctly in the namespace location. [[setting,log_debug,category=mail]] will help you see @@ -700,7 +700,7 @@ and in `dovecot.conf`: ```[dovecot.conf] mail_driver = maildir -mail_path = /var/vmail/%d/%n/Maildir +mail_path = /var/vmail/%{user | domain}/%{user | username}/Maildir mail_access_groups = dovemail ``` diff --git a/docs/core/config/sieve/submission.md b/docs/core/config/sieve/submission.md index 56473adbf..37146ec6a 100644 --- a/docs/core/config/sieve/submission.md +++ b/docs/core/config/sieve/submission.md @@ -7,13 +7,13 @@ title: Submission ## `postmaster_address` -`postmaster_address = postmaster@%d` +`postmaster_address = postmaster@%{user | domain}` Email address to use in the From: field for outgoing email rejections. -The `%d` variable expands to the recipient domain. +The `%{user | domain}` variable expands to the recipient domain. -### Domain (`%d`) is Empty +### Domain (`%{user | domain}`) is Empty IMAP or POP3 protocol doesn't have explicit support for domains. The usernames are commonly in `user@domain` format, and that is also where @@ -21,11 +21,11 @@ Dovecot gets the domain from. If the username doesn't have `@domain`, then the domain is also usually empty (unless [[setting,auth_default_domain]] is used). -If you login as `user@domain`, but the %d is still empty, the problem is +If you login as `user@domain`, but the %{user | domain} is still empty, the problem is that your configuration lost the domain part by changing the username. Dovecot doesn't keep track of the domain separately from username, so if something changes username from `user@domain` to just plain `user`, the -domain is lost and %d returns nothing. If you have [[setting,auth_debug,yes]], +domain is lost and %{user | domain} returns nothing. If you have [[setting,auth_debug,yes]], this shows up in logs like: `Info: auth(user@domain.org): username changed user@domain.org -> user`. @@ -33,9 +33,6 @@ Below are some of the most common reasons for this. #### Settings -[[setting,auth_username_format,%Ln]] lowercases the username but also drops -the domain. Use [[setting,auth_username_format,%Lu]] instead. - [[setting,auth_username_format]] changes the username permanently when used globally. If used inside [[link,passdb,passdb]] or [[link,userdb,userdb]], it changes the username only for the duration of the lookup. See also @@ -51,7 +48,7 @@ username and domain are stored separately. For example: passdb sql { query = SELECT username AS user, password \ FROM users \ - WHERE username = '%n' AND domain = '%d' + WHERE username = '%{user | username}' AND domain = '%{user | domain}' } ``` @@ -63,7 +60,7 @@ is dropped. You can instead use: passdb sql { query = SELECT concat(username, '@', domain) AS user, password \ FROM users \ - WHERE username = '%n' AND domain = '%d' + WHERE username = '%{user | username}' AND domain = '%{user | domain}' } ``` @@ -74,7 +71,7 @@ merge them into a single user field: passdb sql { query = SELECT username, domain, password \ FROM users \ - WHERE username = '%n' AND domain = '%d' + WHERE username = '%{user | username}' AND domain = '%{user | domain}' } ``` diff --git a/docs/core/config/statistics.md b/docs/core/config/statistics.md index 41489d01c..67578d5e7 100644 --- a/docs/core/config/statistics.md +++ b/docs/core/config/statistics.md @@ -70,7 +70,7 @@ can be added to provide modifiers to the discrete value. This is done as variables are provided: * `%{value}` - The original value -* `%{domain}` - Text after the `@` character, or empty string if there is no `@`. +* `%{user | domain}` - Text after the `@` character, or empty string if there is no `@`. Example: diff --git a/docs/core/config/users/system.md b/docs/core/config/users/system.md index 27916e784..2b4a014ed 100644 --- a/docs/core/config/users/system.md +++ b/docs/core/config/users/system.md @@ -85,13 +85,13 @@ manually with: ```[dovecot.conf] mail_driver = mbox mail_path = ~/mail -mail_inbox_path = /var/mail/%u +mail_inbox_path = /var/mail/%{user} ``` Remember that the [[setting,mail_path]] setting is the mailbox root directory, -don't try to use the INBOX `/var/mail/%u` path in there because that +don't try to use the INBOX `/var/mail/%{user}` path in there because that isn't going to work (unless you really want to store mails under -`/var/mail/%u/` directory). +`/var/mail/%{user}/` directory). If you're also using other software than Dovecot to access mboxes, you should try to figure out what locking methods exactly they're using and diff --git a/docs/core/config/users/virtual.md b/docs/core/config/users/virtual.md index 21c50d60c..a7ab42244 100644 --- a/docs/core/config/users/virtual.md +++ b/docs/core/config/users/virtual.md @@ -42,8 +42,8 @@ something that shows up in your logs and maybe in some configuration, but they have no direct functionality. So although Dovecot makes it easier to handle "user@domain" style -usernames (eg. `%n` and `%d` [[variable]]), nothing breaks if you use, -for example, `domain%user` style usernames instead. +usernames (eg. `%{user | username}` and `%{user | domain}` [[variable]]), nothing breaks if you use, +for example, `domain%{user}ser` style usernames instead. However some [[link,authentication_mechanisms]] do have an explicit support for realms (pretty much the same as domains). If those mechanisms are used, @@ -175,7 +175,7 @@ If for example `home=/var/vmail/domain/user/` and ::: code-group ```[dovecot.conf] -mail_home = /var/vmail/%d/%n +mail_home = /var/vmail/%{user | domain}/%{user | username} mail_driver = maildir mail_path = ~/mail ``` @@ -215,22 +215,22 @@ Their mail is kept in their home directory at The usernames in the passwd and shadow files are expected to contain only the user part, no domain. This is because the path itself already -contained %d to specify the domain. If you want the files to contain +contained %{user | domain} to specify the domain. If you want the files to contain full `user@domain` names, you can change [[setting,auth_username_format]] to -`%u` or leave it out (it's default value is `%Lu`). +`%{user}` or leave it out (it's default value is `%{user|lower}`). ```[dovecot.conf] mail_driver = maildir -mail_path = /home/%d/%n/Maildir +mail_path = /home/%{user | domain}/%{user | username}/Maildir passdb passwd-file { auth_username_format = %Ln - passwd_file_path = /home/%d/etc/shadow + passwd_file_path = /home/%{user | domain}/etc/shadow } userdb passwd-file { auth_username_format = %Ln - passwd_file_path = /home/%d/etc/passwd + passwd_file_path = /home/%{user | domain}/etc/passwd } ``` @@ -251,7 +251,7 @@ userdb static { fields { uid = vmail gid = vmail - home = /var/mail/virtual/%d/%n + home = /var/mail/virtual/%{user | domain}/%{user | username} } } ``` @@ -274,7 +274,7 @@ passdb ldap { passdb static { fields { - user = %Ld + user = %{domain|lower} noauthenticate = yes } skip = authenticated diff --git a/docs/core/design/auth_process.md b/docs/core/design/auth_process.md index f462bcab8..ca2d3ccde 100644 --- a/docs/core/design/auth_process.md +++ b/docs/core/design/auth_process.md @@ -300,12 +300,12 @@ Success. A string containing [[variable]]. -When expanded, it uniquely identifies a passdb lookup. This is `%u` +When expanded, it uniquely identifies a passdb lookup. This is `%{user}` when the passdb lookup validity depends only on the username. With more complex databases such as SQL and LDAP this is created dynamically based on the password query in the configuration file. If there are multiple variables, they should be separated so that their -contents don't get mixed, for example `%u%r%l`. +contents don't get mixed, for example `%{user}%r%l`. `auth_cache_parse_key()` can be used to easily create a cache key from a query string. diff --git a/docs/core/design/mailbox_searching.md b/docs/core/design/mailbox_searching.md index 4c7502ce4..8d1419060 100644 --- a/docs/core/design/mailbox_searching.md +++ b/docs/core/design/mailbox_searching.md @@ -78,7 +78,7 @@ search_ctx = mailbox_search_init(trans, search_args, NULL, 0, NULL); mail_search_args_unref(&search_args); while (mailbox_search_next(ctx, &mail)) { - printf("matched uid %u\n", mail->uid); + printf("matched uid %{user}\n", mail->uid); } if (mailbox_search_deinit(&search_ctx) < 0) i_error("search failed"); diff --git a/docs/core/design/var_expand.md b/docs/core/design/var_expand.md new file mode 100644 index 000000000..a3092c0ec --- /dev/null +++ b/docs/core/design/var_expand.md @@ -0,0 +1,80 @@ +--- +layout: doc +title: Variable expansion design +dovecotlinks: + var_expand: variable expansion design +--- + +# Variable expansion design + +Dovecot comes with powerful variable expansion system, which allows constructing reusable text templates. +This has been upgraded since v2.3 to a more flexible systemt . + +## Syntax + +``` + ::= + ::= + ::= , "{" "}", "%" + ::= "|" , + ::= + ::= + ::= , + ::= "-" , + ::= "+", "-", "*", "/" + ::= + ::= "(" ")" + ::= "," + ::= , , , "=" , "=" , "=" + +NAME = string +VALUE = "string" or 'string' +NUMBER = [0-9]+ +``` + +## Design + +Internally, everything is stored in a binary-safe string container. There is no other data type internally. +This buffer can be set and unset, and the content can be tagged by filters to be binary or string. + +The system uses programs to perform the actual expansion. The given input is always first parsed into a list of programs. +Input that consists from multiple expansions separated by non-expansion strings is split into multiple programs. +Once program is compiled, it can be executed multiple times with different parameters. + +Program parameters consists from variable table(s), provider(s) and escape function. A program can be executed with different parameters. + +## Parameters + +Parameters are provided via `struct var_expand_params`. Variable mappings are provided via `struct var_expand_table` array, which is `VAR_EXPAND_TABLE_END` terminated list of +key-value mappings. Key's value can also be provided by a function. + +Providers are used to handle scoped variables, such as passdb, ldap etc. There are also global providers which are always available. + +Providers can be provide with `struct var_expand_provider` array which contains prefix and provider function, and is `VAR_EXPAND_TABLE_END` terminated. + +It is also possible to provide escape function, which is applied to each %{pipeline} output. + +Key functions and providers use the same context. + +Key-value tables and providers can also be provided as arrays of arrays, which must be NULL terminated. +Contexts for these must be provided in an array that is `VAR_EXPAND_CONTEXTS_END` terminated. + +When these arrays are used, first match wins. + +## Filters + +Filters are functions that accept input from left side and emit output to right side. They can accept positional and named parameters. +Some filters can start expressions, namely ones that do not require any input. + +## Variables + +Variables are always considered to be strings. NULL value is considered same as empty value. Variable names are unique, and if table contains +multiple variables with same name, the first is always used. + +Variable can exist as parameter to filters or as the first token in expression. + +## Output handling + +If a program ends up with binary tagged output, the output is automatically hex-encoded. +If there is no key in table or provider, error will occur. +This error can be negated with `default(value)` filter, which clears error. diff --git a/docs/core/man/doveadm-altmove.1.md b/docs/core/man/doveadm-altmove.1.md index aaa8ea008..f9889cdde 100644 --- a/docs/core/man/doveadm-altmove.1.md +++ b/docs/core/man/doveadm-altmove.1.md @@ -83,7 +83,7 @@ storage under /nfsmount: ```[dovecot.conf] mail_driver = mdbox mail_path = ~/mdbox -mail_alt_path = /nfsmount/%h/mdbox +mail_alt_path = /nfsmount/%{home}/mdbox ``` ```sh diff --git a/docs/core/plugins/last_login.md b/docs/core/plugins/last_login.md index 0e0af4f4f..89791b65d 100644 --- a/docs/core/plugins/last_login.md +++ b/docs/core/plugins/last_login.md @@ -44,7 +44,7 @@ redis_port = 6379 last_login { dict redis { } - #key = last-login/%u # default + #key = last-login/%{user} # default } ``` diff --git a/docs/core/plugins/mail_crypt.md b/docs/core/plugins/mail_crypt.md index 6c557521a..e12b2a4ec 100644 --- a/docs/core/plugins/mail_crypt.md +++ b/docs/core/plugins/mail_crypt.md @@ -161,7 +161,7 @@ mail_plugins { } mail_attribute { dict file { - path = %h/Maildir/dovecot-attributes + path = %{home}/Maildir/dovecot-attributes } } @@ -181,7 +181,7 @@ mail_plugins { } mail_attribute { dict file { - path = %h/Maildir/dovecot-attributes + path = %{home}/Maildir/dovecot-attributes } } @@ -196,7 +196,7 @@ provided via password query: passdb sql { query = SELECT email as user, password, '%Mw' AS userdb_crypt_user_key_password \ FROM virtual_users \ - WHERE email='%u' + WHERE email='%{user}' } ``` diff --git a/docs/core/plugins/quota.md b/docs/core/plugins/quota.md index 33c6e1ad5..0c6824405 100644 --- a/docs/core/plugins/quota.md +++ b/docs/core/plugins/quota.md @@ -169,7 +169,7 @@ Example (for MySQL): userdb sql { query = SELECT uid, gid, home, CONCAT(quota_limit_bytes, 'B') AS quota_storage_size \ FROM users \ - WHERE userid = '%u' + WHERE userid = '%{user}' passdb sql { # SQL with userdb prefetch: Remember to prefix quota_quota_storage_size with userdb_ @@ -177,7 +177,7 @@ passdb sql { query = SELECT userid AS user, password, uid AS userdb_uid, gid AS userdb_gid, \ CONCAT(quota_limit_bytes, 'B') AS userdb_quota_storage_size \ FROM users \ - WHERE userid = '%u' + WHERE userid = '%{user}' } ``` @@ -189,7 +189,7 @@ sql_driver = sqlite # alternatively: pgsql userdb sql { query = SELECT uid, gid, home, quota_limit_bytes || 'B' AS quota_storage_size \ FROM users \ - WHERE userid = '%u' + WHERE userid = '%{user}' } ``` @@ -386,8 +386,8 @@ Example: ```[dovecot.conf] mail_driver = mbox mail_path = ~/mail -mail_inbox_path = /var/mail/%u -mail_index_path = /var/no-quotas/index/%u +mail_inbox_path = /var/mail/%{user} +mail_index_path = /var/no-quotas/index/%{user} ``` #### Maildir @@ -405,12 +405,12 @@ Example: ```[dovecot.conf] mail_driver = maildir mail_path = ~/Maildir -mail_index_path = /var/no-quotas/index/%u -mail_control_path = /var/no-quotas/control/%u +mail_index_path = /var/no-quotas/index/%{user} +mail_control_path = /var/no-quotas/control/%{user} ``` Note that if you change the location of the control files, Dovecot will look -in the new control path directory (`/var/no-quotas/control/%u`) for the +in the new control path directory (`/var/no-quotas/control/%{user}`) for the mailbox `subscriptions` file. #### Configuration Examples @@ -538,13 +538,13 @@ quota user { warning warn-95 { quota_storage_percentage = 95 execute quota-warning { - args = 95 %u + args = 95 %{user} } } warning warn-80 { quota_storage_percentage = 80 execute quota-warning { - args = 80 %u + args = 80 %{user} } } warning warn-under { @@ -552,7 +552,7 @@ quota user { # user is no longer over quota threshold = under execute quota-warning { - args = below %u + args = below %{user} } } } @@ -629,7 +629,7 @@ quota_over_status { lazy_check = yes execute quota-warning { - args = mismatch %u + args = mismatch %{user} } } ``` diff --git a/docs/core/plugins/sieve.md b/docs/core/plugins/sieve.md index 186c61289..b147c85ee 100644 --- a/docs/core/plugins/sieve.md +++ b/docs/core/plugins/sieve.md @@ -221,7 +221,7 @@ compiled binaries on the local filesystem. For Example: ```[dovecot.conf] sieve = dict:file:/etc/dovecot/sieve.dict;name=keep;bindir=~/.sieve-bin # or -#sieve = dict:file:/etc/dovecot/sieve.dict;name=keep;bindir=/var/sieve-scripts/%u +#sieve = dict:file:/etc/dovecot/sieve.dict;name=keep;bindir=/var/sieve-scripts/%{user} ``` ::: tip @@ -420,7 +420,7 @@ compiled binaries on the local filesystem. For Example: ```[dovecot.conf] sieve = ldap:/etc/dovecot/sieve.ldap;name=keep;bindir=~/.sieve-bin # or -#sieve = ldap:/etc/dovecot/sieve.ldap;name=keep;bindir=/var/sieve-scripts/%u +#sieve = ldap:/etc/dovecot/sieve.ldap;name=keep;bindir=/var/sieve-scripts/%{user} ``` ::: tip @@ -441,7 +441,7 @@ parameters are specific to the Sieve ldap configuration: ###### `sieve_ldap_filter` -- Default: `(&(objectClass=posixAccount)(uid=%u))` +- Default: `(&(objectClass=posixAccount)(uid=%{user}))` - Values: [[link,settings_types_string]] The LDAP search filter that is used to find the entry containing the @@ -451,9 +451,9 @@ These variables can be used: | Variable | Long Name | Description | | -------- | --------- | ----------- | -| `%u` | `%{user}` | username | -| `%n` | `%{username}` | user part in user@domain, same as `%u` if there's no domain | -| `%d` | %{domain} | domain part in user@domain, empty if user there's no domain | +| `%{user}` | `%{user}` | username | +| `%{user | username}` | `%{user | username}` | user part in user@domain, same as `%{user}` if there's no domain | +| `%{user | domain}` | %{user | domain} | domain part in user@domain, empty if user there's no domain | | | `%{home}` | user's home directory | | | `%{name}` | name of the Sieve script | @@ -519,11 +519,11 @@ deref = never scope = subtree # Filter for user lookup. Some variables can be used: -# %u - username -# %n - user part in user@domain, same as %u if there's no domain -# %d - domain part in user@domain, empty if there's no domain +# %{user} - username +# %{user | username} - user part in user@domain, same as %{user} if there's no domain +# %{user | domain} - domain part in user@domain, empty if there's no domain # %{name} - name of the Sieve script -sieve_ldap_filter = (&(objectClass=posixAccount)(uid=%u)) +sieve_ldap_filter = (&(objectClass=posixAccount)(uid=%{user})) # Attribute containing the Sieve script sieve_ldap_script_attr = mailSieveRuleSource @@ -564,11 +564,11 @@ For example, to use a Sieve script file named `.sieve` in ``` plugin { ... - sieve = /var/sieve-scripts/%u.sieve + sieve = /var/sieve-scripts/%{user}.sieve } ``` -You may use templates like `%u`, as shown in the example. See [[variable]]. +You may use templates like `%{user}`, as shown in the example. See [[variable]]. A relative path (or just a filename) will be interpreted to point under the user's home directory. @@ -632,12 +632,12 @@ plugin { # User-specific scripts executed before the user's personal script. # E.g. a vacation script managed through a non-ManageSieve GUI. - sieve_before3 = /var/vmail/%d/%n/sieve-before + sieve_before3 = /var/vmail/%{user | domain}/%{user | username}/sieve-before # User-specific scripts executed after the user's personal script. # (if keep is still in effect) # E.g. user-specific default mail filing rules - sieve_after = /var/vmail/%d/%n/sieve-after + sieve_after = /var/vmail/%{user | domain}/%{user | username}/sieve-after # Global scripts executed after the user's personal script # (if keep is still in effect) diff --git a/docs/core/settings/variables.md b/docs/core/settings/variables.md index 2f111a3c7..6375ec41c 100644 --- a/docs/core/settings/variables.md +++ b/docs/core/settings/variables.md @@ -15,6 +15,9 @@ dovecotlinks: settings_variables_modifiers: hash: modifiers text: Variable Modifiers + conditionals: + hash: conditionals + text: Conditionals --- # Settings Variables @@ -27,24 +30,121 @@ You can use special variables in several places: * [[link,auth_ldap]], [[link,auth_sql]], and [[link,userdb]] query strings * Log prefix for imap/pop3 process -## Global Variables - -Global variables that work everywhere are: +## Variable expansion syntax + +[[changed,var_expand]] + +We have introduced an entirely new variable expansion syntax. +See [[link,var_expand]] for in-depth details how it works. + +The basic syntax is `%{variable (| filter | filter ...)}`, which means that most existing +variables work, but there are some changes, so check variable usage carefully when converting old syntax. + +The simple case of just getting a value of variable is `%{variable}`. +These can be in middle of strings. + +Another syntax is `%{provider:variable}`, where the value is provided by +a provider. There are global providers, and context-specific providers. + +A variable can be then filtered with various filters, such as `%{variable | upper}` to get +uppercase representation of variable. You can chain as many filters as you need. + +Filters can accept parameters, both positional and named. E.g. `%{literal('\r\n\')}` will expand +to CR LF. `%{user | substr(0, 1)}` will take first character of username. Example of named parameters +would be `%{user | md5(rounds=1000,salt='pepper')}` + +You can use `%%{variable}` to escape this, and emit `%{variable}`. + +Filters accept strings, numbers and variables as parameters. Parameters can be positional or +named key-value pairs. Key names cannot be variables. +The left side of pipe character (`|`) is provided as input to a filter. Some filters can be used +in place of variables, e.g. lookup, literal and if. + +When value is missing or empty, you can use the `default` filter to provide value. Missing variables +will cause errors and must be negated with default. This does not apply to all providers, some +providers return empty when value is missing. + +If the last filter would output binary data, the data is encoded with `hexlify` filter by default. +To avoid this, you can use `text` filter, which will sanitize the input and mark it as text. + +The new syntax also supports simple maths, you can do one operation. E.g. `%{port + 1000}`. +Addition, substraction, multiplication, division and modulo operations are supported for now. + +A special case for modulo operation is that it can be applied to binary input, e.g. `sha1 % 256`. +The input is treated as 64-bit unsigned number and modulo is taken from that. + +All strings must be encapsulated with `"` or `'`, and you can escape them using `\\` within string. +Numbers when used as parameters must be provided without quotes. + +## List of filters + +All parameters are strings unless stated otherwise. +If parameter is `any`, it accepts both numbers and strings. +Boolean type is `0` for false and `1` for true. + +Filters that have `any` in input and output mean that they will accept bytes or string, and the result +will be bytes or strings, depending on the input. The types indicated as interpreted types, as everything +is stored as strings internally. + +Bytes output type indicates that the output will be tagged as binary output. Subsequent filters can change this. + +| Filter | Input | Output | Description | +| ------ | ----- | ------ | ----------- | +| base64(pad=boolean, url=boolean) | Bytes | String | Base64 encode given input, defaults to pad and not url scheme. | +| benumber | Bytes | Number | Convert big-endian encoded input into a number. | +| concat(any, any...) | Bytes | Bytes | Concatenates input with value(s). Numbers are coerced to strings. Input is optional. | +| default(value) | String | String | Replace empty or missing input with value. Clears missing variable error. If no value is provided, empty string is used. | +| domain | String | String | Provides domain part of user@domain value. | +| hash(method, rounds=number, salt=string) | Bytes | Bytes | Returns raw hash from input using given hash method. Rounds and salt are optional. | +| hexlify(width) | Bytes | String | Convert bytes into hex with optional width, truncates or pads up to width. | +| hex(width) | Number | Number | Convert base-10 number to base-16 number. If width is specified the result is truncated or padded with 0 to width. Negative width is applied after number. | +| if(left,operator,right,true,false) | String | String | Evaluates given comparison and returns true or false value. See [conditionals](#conditionals). | +| if(operator,right,true,false) | String | String | Evaluates given comparison against input value and retuns true or false value. | +| index(separator, nth) | String | String | Returns nth element from separator separated string. Zero based. Negative values are looked relative to end of list. | +| ldap_dn | String | String | Converts `domain.com` to `dc=domain,dc=com`. | +| lenumber | Bytes | Number| Convert little-endian encoded input into a number. | +| lfill(width, filler) | Any | Any | Pads value from left with filler until length is width. Default filler is `0`. | +| list(separator) | String | String | Converts tab-escaped list into separator separated list. Defaults to `,`. | +| literal(string) | None | String | Expands into literally the value. If variable is used, works like lookup. Input is ignored. | +| lookup(name) | None | String | Lookup var from table. If var is variable, the name is taken from variable's contents. Input is ignored. | +| lower | String | String | Lowercases input. | +| md5(rounds=number, salt=string) | Bytes | Bytes | Alias for hash with method md5. | +| regexp(expression, replacement) | String | String |Performs regular expression replacement using [POSIX Extended Regular Expression syntax](https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002dextended-regular-expression-syntax.html). Supports up to 9 capture groups. | +| reverse | Any | Any | Reverse input bytes. | +| rfill(width, filler) | Any | Any | Pads value to right with filler until length is width. Default filler is `0`. | +| sha1(rounds=number, salt=string) | Bytes | Bytes | Alias for hash with method sha1. | +| sha256(rounds=number, salt=string) | Bytes | Bytes | Alias for hash with method sha256. | +| sha384(rounds=number, salt=string) | Bytes | Bytes | Alias for hash with method sha384. | +| sha512(rounds=number, salt=string) | Bytes | Bytes | Alias for hash with method sha512. | +| substr(offset, length) | Any | Any | Extracts a substring out of input and returns it. First character is at offset zero. If offset is negative, starts that far back from the end of the string. If length is omitted, returns everything through the end of the string. If length is negative, leaves that many characters off the end of the string. | +| text | Bytes | String | Sanitize input into text and clear binary tag. | +| truncate(len, bits=number) | Bytes | Bytes | Truncate to len bytes, or number of bits. The parameters are mutually exclusive. | +| unbase64(pad=boolean, url=boolean) | String | Bytes | Base64 decode given input, defaults to pad and not url scheme. | +| unhex | String | Number | Convert base-16 number to base-10 number. | +| unhexlify | String | Bytes | Convert hex encoded input into bytes. | +| upper | String | String | Uppercases input. | +| username | String | String | Provides user part of user@domain value. | + +If [[plugin,var-expand-crypt]] is loaded, these filters are registered as well. + +| Filter | Input | Output | Description | +| ------ | ----- | ----- | ----------- | +| decrypt(algorithm=string,key=string,iv=string,raw=boolean) | Bytes/String | Bytes | Decrypts input with given parameters. If raw is `0`, expects '$' separated value of IV and encrypted data. | +| encrypt(algorithm=string,key=string,iv=string,raw=boolean) | Bytes | Bytes/String | Encrypts input with given parameters. If raw is `0`, outputs `$` separated value of IV and encrypted data. | + +## Global providers + +Global providers that work everywhere are: | Long Name | Description | | --------- | ----------- | -| `%%` | '%' character. [[link,shared_mailboxes_percent,Further information about %% variables]] | -| `env:` | Environment variable \. | -| `system:` | Get a system variable, see [below](#system-variables) for list of supported names. | +| `date:` | Get a date field, available keys are `year`, `month`, `day`. | +| `dovecot:` | Get a distribution variable, see [below](#distribution-variables) for a list of supported names. | +| `env:` | Environment variable \. Returns empty string if unset. | +| `event:` | Get an event field. Returns empty string if no such field is found from event. | | `process:` | Get a process variable, see [below](#process-variables) for list of supported names. | -| `dovecot:` | Get a distribution variable, see [below](#distribution-variables) for a list of supported names. - -If [[plugin,var-expand-crypt]] is loaded, these also work globally: - -| Long Name | Description | -| --------- | ----------- | -| `encrypt; :` | Encrypt field | -| `decrypt; :` | Decrypt field | +| `system:` | Get a system variable, see [below](#system-variables) for list of supported names. | +| `time:` | Get a time field, available keys are `hour`, `min`, `minute`, `sec`, `second` and `usec`. | ## System Variables @@ -118,15 +218,11 @@ See also: Variables that work nearly everywhere where there is a username: -| Variable | Long Name | Description | -| -------- | --------- | ----------- | -| `%u` | `user` | full username (e.g. user@domain) | -| `%n` | `username` | user part in user@domain, same as `%u` if there's no domain | -| `%d` | `domain` | domain part in user@domain, empty if user with no domain | -| | `session` | session ID for this client connection (unique for 9 years) | -| | `auth_user` | SASL authentication ID (e.g. if master user login is done, this contains the master username). If username changes during authentication, this value contains the original username. Otherwise the same as `%{user}`. | -| | `auth_username` | user part in `%{auth_user}` | -| | `auth_domain` | domain part in `%{auth_user}` | +| Variable | Description | +| -------- | ----------- | +| `user` | Full username (e.g. user@domain) | +| `session` | Session ID for this client connection (unique for 9 years) | +| `auth_user` | SASL authentication ID (e.g. if master user login is done, this contains the master username). If username changes during authentication, this value contains the original username. Otherwise the same as `user`. | ## Mail Service User Variables @@ -136,12 +232,12 @@ See also: * [User Variables](#user-variables). ::: -| Variable | Long Name | Description | -| -------- | --------- | ----------- | -| | `service` | imap, pop3, smtp, lda (and doveadm, etc.) | -| `%l` | `local_ip` | local IP address | -| `%r` | `remote_ip` | remote IP address | -| | `userdb:` | Return userdb extra field "name". `%{userdb:name:default}` returns "default" if "name" doesn't exist (not returned if name exists but is empty) | +| Variable | Description | +| -------- | ----------- | +| `service` | imap, pop3, smtp, lda (and doveadm, etc.) | +| `local_ip` | local IP address | +| `remote_ip` | remote IP address | +| `userdb:` | Return userdb extra field "name". | ## Mail User Variables @@ -152,10 +248,10 @@ See also: * [Mail Service User Variables](#mail-service-user-variables). ::: -| Variable | Long Name | Description | -| -------- | --------- | ----------- | -| `%h` | `home` | home directory. Use of `~/` is better whenever possible. | -| | `hostname` | Expands to the hostname setting. Overrides the global `%{hostname}`. | +| Variable | Description | +| -------- | ----------- | +| `home` | home directory. Use of `~/` is better whenever possible. | +| `hostname` | Expands to the hostname setting. Overrides the global `hostname`. | ## Login Variables @@ -165,31 +261,29 @@ See also: * [User Variables](#user-variables). ::: -| Variable | Long Name | Description | -| -------- | --------- | ----------- | -| | `protocol` | imap, pop3, smtp, lda (and doveadm, etc.)
[[added,variables_login_variables_protocol]] Renamed from `%{service}` variable. | -| | `local_name` | TLS SNI hostname, if given | -| `%l` | `local_ip` | local IP address | -| `%r` | `remote_ip` | remote IP address | -| `%a` | `local_port` | local port | -| `%b` | `remote_port` | remote port | -| | `real_remote_ip` | Same as `%{remote_ip}`, except in proxy setups contains the remote proxy's IP instead of the client's IP | -| | `real_local_ip` | Same as `%{local_ip}`, except in proxy setups contains the local proxy's IP instead of the remote proxy's IP | -| | `real_remote_port` | Similar to `%{real_rip}` except for port instead of IP | -| | `real_local_port` | Similar to `%{real_lip}` except for port instead of IP | -| `%m` | `mechanism` | [[link,sasl]], e.g., PLAIN | -| `%c` | `secured` | "TLS" with established SSL/TLS connections, "TLS handshaking", or "TLS [handshaking]: error text" if disconnecting due to TLS error. "secured" with secured connections (see: [[setting,ssl]]). Otherwise empty. | -| `%k` | `ssl_security` | TLS session security string. If HAProxy is configured and it terminated the TLS connection, contains "(proxied)". | -| | `ssl_ja3` | [[link,ssl_ja3]] composed from TLS Client Hello. | -| | `ssl_ja3_hash` | MD5 hash from [[link,ssl_ja3]] composed from TLS Client Hello. | -| `%e` | `mail_pid` | PID for process that handles the mail session post-login | -| | `original_user` | Same as `%{user}`, except using the original username the client sent before any changes by auth process. With master user logins (also with [[setting,auth_master_user_separator]] based logins),this contains only the original master username. | -| | `original_username` | Same as `%{username}`, except using the original username | -| | `original_domain` | Same as `%{domain}`, except using the original username | -| | `listener` | Socket listener name as specified in config file, which accepted the client connection. | -| | `owner_user` | For shared storage this is the `%{user}` variable of the owner, otherwise it is the same as `%{user}`.
[[added,variables_owner_user_added]] | -| | `passdb:` | Return passdb extra field "name". `%{passdb:name:default}` returns "default" if "name" doesn't exist (not returned if name exists but is empty). Note that this doesn't work in passdb/userdb ldap's pass_attrs or user_attrs. | -| | `passdb:forward_` | Used by proxies to pass on extra fields to the next hop, see [[link,auth_forward_fields]]. | +| Variable | Description | +| -------- | ----------- | +| `protocol` | imap, pop3, smtp, lda (and doveadm, etc.)
[[added,variables_login_variables_protocol]] Renamed from `service` variable. | +| `local_name` | TLS SNI hostname, if given. | +| `local_ip` | Local IP address. | +| `remote_ip` | Remote IP address. | +| `local_port` | Local port. | +| `remote_port` | Remote port. | +| `real_remote_ip` | Same as `remote_ip`, except in proxy setups contains the remote proxy's IP instead of the client's IP. | +| `real_local_ip` | Same as `local_ip`, except in proxy setups contains the local proxy's IP instead of the remote proxy's IP. | +| `real_remote_port` | Similar to `real_remote_ip` except for port instead of IP. | +| `real_local_port` | Similar to `real_local_ip` except for port instead of IP. | +| `mechanism` | [[link,sasl]], e.g., PLAIN. | +| `secured` | "TLS" with established SSL/TLS connections, "TLS handshaking", or "TLS [handshaking]: error text" if disconnecting due to TLS error. "secured" with secured connections (see: [[setting,ssl]]). Otherwise empty. | +| `ssl_security` | TLS session security string. If HAProxy is configured and it terminated the TLS connection, contains "(proxied)". | +| `ssl_ja3` | [[link,ssl_ja3]] composed from TLS Client Hello. | +| `ssl_ja3_hash` | MD5 hash from [[link,ssl_ja3]] composed from TLS Client Hello. | +| `mail_pid` | PID for process that handles the mail session post-login. | +| `original_user` | Same as `user`, except using the original username the client sent before any changes by auth process. With master user logins (also with [[setting,auth_master_user_separator]] based logins),this contains only the original master username. | +| `listener` | Socket listener name as specified in config file, which accepted the client connection. | +| `owner_user` | For shared storage this is the `user` variable of the owner, otherwise it is the same as `user`.
[[added,variables_owner_user_added]] | +| `passdb:` | Return passdb extra field "name". | +| `passdb:forward_` | Used by proxies to pass on extra fields to the next hop, see [[link,auth_forward_fields]]. | ## Authentication Variables @@ -199,153 +293,62 @@ See also: * [User Variables](#user-variables). ::: -| Variable | Long Name | Description | -| -------- | --------- | ----------- | -| | `protocol` | imap, pop3, smtp, lda (and doveadm, etc.)
[[added,variables_auth_variables_protocol]] Renamed from `%{service}` variable. | -| | `domain_first` | For "username@domain_first@domain_last" style usernames | -| | `domain_last` | For "username@domain_first@domain_last" style usernames | -| | `local_name` | TLS SNI hostname, if given | -| `%l` | `local_ip` | local IP address | -| `%r` | `remote_ip` | remote IP address | -| `%a` | `local_port` | local port | -| `%b` | `remote_port` | remote port | -| | `real_remote_ip` | Same as `%{remote_ip}`, except in proxy setups contains the remote proxy's IP instead of the client's IP | -| | `real_local_ip` | Same as `%{local_ip}`, except in proxy setups contains the local proxy's IP instead of the remote proxy's IP | -| | `real_remote_port` | Similar to `%{real_rip}` except for port instead of IP | -| | `real_local_port` | Similar to `%{real_lip}` except for port instead of IP | -| `%p` | `client_pid` | process ID of the authentication client | -| | `session_pid` | For user logins: The PID of the IMAP/POP3 process handling the session. | -| `%m` | `mechanism` | [[link,sasl]], e.g., PLAIN | -| `%w` | `password` | cleartext password from cleartext authentication mechanism | -| `%c` | `secured` | "TLS" with established SSL/TLS connections, "secured" with secured connections (see: [[setting,ssl]]). Otherwise empty. | -| | `ssl_ja3_hash` | MD5 hash from JA3 string composed from TLS Client Hello. | -| `%k` | `cert` | "valid" if client had sent a valid client certificate, otherwise empty. | -| | `login_user` | For master user logins: Logged in user@domain | -| | `login_username` | For master user logins: Logged in user | -| | `login_domain` | For master user logins: Logged in domain | -| | `master_user` | For master user logins: The master username | -| | `original_user` | Same as `%{user}`, except using the original username the client sent before any changes by auth process | -| | `original_username` | Same as `%{username}`, except using the original username | -| | `original_domain` | Same as `%{domain}`, except using the original username | -| | `passdb:` | Return passdb extra field "name". `%{passdb:name:default}` returns "default" if "name" doesn't exist (not returned if name exists but is empty). Note that this doesn't work in passdb/userdb ldap's pass_attrs or user_attrs. | -| | `userdb:` | Return userdb extra field "name". Note that this can also be used in passdbs to access any userdb_\* extra fields added by previous passdb lookups. `%{userdb:name:default}` returns "default" if "name" doesn't exist (not returned if name exists but is empty). Note that this doesn't work in passdb/userdb ldap's pass_attrs or user_attrs. | -| | `client_id` | If [[setting,imap_id_retain]] is enabled this variable is populated with the client ID request as IMAP arglist. For directly logging the ID see the [[event,imap_id_received]] event. | -| | `passdb:forward_` | Used by proxies to pass on extra fields to the next hop, see [[link,auth_forward_fields]]. | -| `%!` | | Internal ID number of the current passdb/userdb. | - -## Modifiers - -You can apply a modifies for each variable (e.g. `%Us` or `%U{service}` = POP3): - -* `%L` - lowercase -* `%U` - uppercase -* `%E` - escape '"', "'" and '\\' characters by inserting '\\' before them. - Note that variables in SQL queries are automatically escaped, you don't need - to use this modifier for them. -* `%X` - parse the variable as a base-10 number, and convert it to base-16 - (hexadecimal) -* `%R` - reverse the string -* `%N` - take a 32bit hash of the variable and return it as hex. You can also - limit the hash value. For example `%256Nu` gives values 0..ff. You might want - padding also, so `%2.256Nu` gives 00..ff. - - * This is "New Hash", based on MD5 to give better distribution of values (no - need for any string reversing kludges either). - -* `%H` - Same as `%N`, but use "old hash" (not recommended anymore) - - * `%H` hash function is a bit bad if all the strings end with the same text, - so if you're hashing usernames being in user@domain form, you probably - want to reverse the username to get better hash value variety, e.g. - `%3RHu`. - -* `%{;rounds=,truncate=,salt=s,format=:field}` - - * Generic hash function that outputs a hex (by default) or `base64` value. - Hash algorithm is any of the supported ones, e.g. `md5`, `sha1`, `sha256`. - Also "pkcs5" is supported using `SHA256`. - - Example: - - ``` - %{sha256:user} or %{md5;truncate=32:user}. - ``` - -* `%M` - return the string's MD5 sum as hex. -* `%D` - return "sub.domain.org" as "sub,dc=domain,dc=org" (for LDAP queries) -* `%T` - Trim trailing whitespace - -You can take a substring of the variable by giving optional offset followed by -'.' and width after the '%' character. For example `%2u` gives first two -characters of the username. `%2.1u` gives third character of the username. - -If the offset is negative, it counts from the end, for example `%-2.2i` gives -the UID mod 100 (last two characters of the UID printed in a string). If a -positive offset points outside the value, empty string is returned, if a -negative offset does then the string is taken from the start. - -If the width is prefixed with zero, the string isn't truncated, but only padded -with '0' character if the string is shorter. - -::: warning -`%04i` may return "0001", "1000" and "12345". `%1.04i` for the same string -would return "001", "000" and "2345". -::: - -If the width is negative, it counts from the end. - -::: warning -`%0.-2u` gives all but the last two characters from the username. -::: - -The modifiers are applied from left-to-right order, except the substring is -always taken from the final string. +| Variable | Description | +| -------- | ----------- | +| `protocol` | imap, pop3, smtp, lda (and doveadm, etc.)
[[added,variables_auth_variables_protocol]] Renamed from `service` variable. | +| `domain_first` | For "username@domain_first@domain_last" style usernames.| +| `domain_last` | For "username@domain_first@domain_last" style usernames. | +| `local_name` | TLS SNI hostname, if given. | +| `local_ip` | Local IP address. | +| `remote_ip` | Remote IP address | +| `local_port` | Local port. | +| `remote_port` | Remote port. | +| `real_remote_ip` | Same as `remote_ip`, except in proxy setups contains the remote proxy's IP instead of the client's IP. | +| `real_local_ip` | Same as `local_ip`, except in proxy setups contains the local proxy's IP instead of the remote proxy's IP. | +| `real_remote_port` | Similar to `real_remote_ip` except for port instead of IP. | +| `real_local_port` | Similar to `real_local_ip` except for port instead of IP. | +| `client_pid` | Process ID of the authentication client. | +| `session_pid` | For user logins: The PID of the IMAP/POP3 process handling the session. | +| `mechanism` | [[link,sasl]], e.g., PLAIN. | +| `password` | Cleartext password from cleartext authentication mechanism. | +| `secured` | "TLS" with established SSL/TLS connections, "secured" with secured connections (see: [[setting,ssl]]). Otherwise empty. | +| `ssl_ja3_hash` | MD5 hash from JA3 string composed from TLS Client Hello. | +| `cert` | "valid" if client had sent a valid client certificate, otherwise empty. | +| `login_user` | For master user logins: Logged in user@domain. | +| `master_user` | For master user logins: The master username. | +| `original_user` | Same as `user`, except using the original username the client sent before any changes by auth process. | +| `passdb:` | Return passdb extra field "name". | +| `userdb:` | Return userdb extra field "name". Note that this can also be used in passdbs to access any userdb_\* extra fields added by previous passdb lookups. | +| `client_id` | If [[setting,imap_id_retain]] is enabled this variable is populated with the client ID request as IMAP arglist. For directly logging the ID see the [[event,imap_id_received]] event. | +| `passdb:forward_` | Used by proxies to pass on extra fields to the next hop, see [[link,auth_forward_fields]]. | +| `id` | Internal ID number of the current passdb/userdb. | ## Conditionals -It's possible to use conditionals in variable expansion. The generic syntax is - -``` -%{if;value1;operator;value2;value-if-true;value-if-false} -``` - -Each of the value fields can contain another variable expansion, facilitating -for nested ifs. Both `%f` and `%{field}` syntaxes work. - -Escaping is supported, so it's possible to use values like `\%`, `\:` or `\;` -that expand to the literal `%`, `:` or `;` characters. Values can have spaces -and quotes without any special escaping. - -Note that currently unescaped `:` cuts off the if statement and ignores -everything after it. - The following operators are supported: | Operator | Explanation | | -------- | ----------- | -| `==` | NUMERIC equality | -| `!=` | NUMERIC inequality | -| `<` | NUMERIC less than | -| `<=` | NUMERIC less or equal | -| `>` | NUMERIC greater than | -| `>=` | NUMERIC greater or equal | -| `eq` | String equality | -| `ne` | String inequality | -| `lt` | String inequality | -| `le` | String inequality | -| `gt` | String inequality | -| `ge` | String inequality | -| `*` | Wildcard match (mask on value2) | -| `!*` | Wildcard non-match (mask on value2) | -| `~` | Regular expression match (pattern on value2, extended POSIX) | -| `!~` | String inequality (pattern on value2, extended POSIX) | +| `==` | Numeric equality. | +| `!=` | Numeric inequality. | +| `<` | Numeric less than. | +| `<=` | Numeric less or equal. | +| `>` | Numeric greater than. | +| `>=` | Numeric greater or equal. | +| `eq` | String equality. | +| `ne` | String inequality. | +| `lt` | String less than. | +| `le` | String less or equal. | +| `gt` | String greater than. | +| `ge` | String greater or equal. | +| `*` | Wildcard match (mask on value2). | +| `!*` | Wildcard non-match (mask on value2). | +| `~` | Regular expression match (pattern on value2, [POSIX Extended Regular Expression syntax](https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002dextended-regular-expression-syntax.html)). | +| `!~` | String inequality (pattern on value2, [POSIX Extended Regular Expression syntax](https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002dextended-regular-expression-syntax.html)). | Examples: ``` -# If %u is "testuser", return "INVALID". Otherwise return %u uppercased. -%{if;%u;eq;testuser;INVALID;%Uu} - -# Same as above, but for use nested IF just for showing how they work: -%{if;%{if;%u;eq;testuser;a;b};eq;a;INVALID;%Uu} +# If %{user} is "testuser", return "INVALID". Otherwise return %{user} uppercased. +%{user | if ("=", "testuser, "invalid", user) | upper } ``` diff --git a/docs/howto/convert_password_schemes.md b/docs/howto/convert_password_schemes.md index 06ddc44d8..1bffe4480 100644 --- a/docs/howto/convert_password_schemes.md +++ b/docs/howto/convert_password_schemes.md @@ -40,7 +40,7 @@ https://kaworu.ch/blog/2016/04/20/strong-crypt-scheme-with-dovecot-postfixadmin- # Enviroment ($PLAIN_PASS) query = SELECT id as user, newpassword as password, home as userdb_home, uid as userdb_uid, gid as userdb_gid, '%w' as userdb_plain_pass \ FROM users \ - WHERE id = '%u' + WHERE id = '%{user}' # Alternatively, here is another config that worked for me with # SHA512-CRYPT (note: uncomment the lines relevant for your setup): @@ -50,12 +50,12 @@ https://kaworu.ch/blog/2016/04/20/strong-crypt-scheme-with-dovecot-postfixadmin- # default_pass_scheme = SHA512-CRYPT # query = SELECT username AS user, password, CONCAT('/var/mail/vdomains/', maildir) as userdb_home, 'vmail' as userdb_uid, 'vmail' as userdb_gid, '%w' as userdb_plain_pass \ # FROM mailbox \ - # WHERE username = '%u' + # WHERE username = '%{user}' } userdb sql { # query = SELECT CONCAT('/var/mail/vdomains/', maildir) AS home, 'vmail' AS uid, 'vmail' AS gid, password \ # FROM mailbox \ - # WHERE username = '%u' AND active = 1 + # WHERE username = '%{user}' AND active = 1 ``` ::: @@ -267,11 +267,11 @@ Enable the `plain_pass` variable in the auth-passwdfile configuration. ``` passdb passwd-file { - passwd_file_path = /var/vmail/auth.d/%d/passwd + passwd_file_path = /var/vmail/auth.d/%{user | domain}/passwd } userdb passwd-file { - passwd_file_path = /var/vmail/auth.d/%d/passwd + passwd_file_path = /var/vmail/auth.d/%{user | domain}/passwd fields { plain_pass = %w } diff --git a/docs/howto/imapc_proxy.md b/docs/howto/imapc_proxy.md index afa5f7deb..c1863a194 100644 --- a/docs/howto/imapc_proxy.md +++ b/docs/howto/imapc_proxy.md @@ -55,7 +55,7 @@ This is based on already having Dovecot already compiled and installed. # Change the line below to reflect the IP address of your Exchange Server. args = host=10.1.2.3 fields { - userdb_imapc_user = %u + userdb_imapc_user = %{user} userdb_imapc_password = %w } } @@ -64,9 +64,9 @@ This is based on already having Dovecot already compiled and installed. } # /home/imapproxy is the home directory for the imapproxy user, and - # %u is a subdir that will be automatically created for each IMAP user + # %{user} is a subdir that will be automatically created for each IMAP user # when they connect - mail_home = /home/imapproxy/%u + mail_home = /home/imapproxy/%{user} auth_mechanisms = plain login diff --git a/docs/howto/lmtp/exim.md b/docs/howto/lmtp/exim.md index 49e4df72d..b420d0f36 100644 --- a/docs/howto/lmtp/exim.md +++ b/docs/howto/lmtp/exim.md @@ -73,8 +73,8 @@ to add a setting to `dovecot.conf`: ``` protocol lmtp { ... - # use %n to strip away the domain part - auth_username_format = %n + # use %{user | username} to strip away the domain part + auth_username_format = %{user | username} } ``` @@ -119,7 +119,7 @@ this problem by extending the *protocol lmtp* section: protocol lmtp { ... # use %Ln to strip away the domain part - auth_username_format = %Lu + auth_username_format = %{user|lower} } ``` diff --git a/docs/howto/restrict_access.md b/docs/howto/restrict_access.md index a71f27017..7ed5da210 100644 --- a/docs/howto/restrict_access.md +++ b/docs/howto/restrict_access.md @@ -38,7 +38,7 @@ You can use the `%{protocol}` variable which expands to `imap` or `pop3` in passdb sql { query = SELECT password \ FROM users \ - WHERE userid = '%u' AND NOT (imap_allowed = false and '%{protocol}' = 'imap') + WHERE userid = '%{user}' AND NOT (imap_allowed = false and '%{protocol}' = 'imap') } ``` @@ -47,7 +47,7 @@ passdb sql { Just like with SQL, you can use `%{protocol}` in [[setting,ldap_filter]]: ``` -pass_filter = (&(objectClass=posixAccount)(uid=%u)(protocol=%{protocol})) +pass_filter = (&(objectClass=posixAccount)(uid=%{user})(protocol=%{protocol})) ``` That would require setting both protocol=pop3 and protocol=imap attributes diff --git a/docs/howto/virtual/postfix.md b/docs/howto/virtual/postfix.md index 23c3a9c58..2f00d877c 100644 --- a/docs/howto/virtual/postfix.md +++ b/docs/howto/virtual/postfix.md @@ -85,11 +85,11 @@ quotas or mailbox formats. auth_mechanisms = plain passdb passwd-file { - passwd_file_path = /var/vmail/auth.d/%d/passwd + passwd_file_path = /var/vmail/auth.d/%{user | domain}/passwd } userdb passwd-file { - passwd_file_path = /var/vmail/auth.d/%d/passwd + passwd_file_path = /var/vmail/auth.d/%{user | domain}/passwd } ``` diff --git a/docs/howto/virtual/postgresql.md b/docs/howto/virtual/postgresql.md index 14f3a05a5..3a58f9cd3 100644 --- a/docs/howto/virtual/postgresql.md +++ b/docs/howto/virtual/postgresql.md @@ -291,13 +291,13 @@ passdb sql { default_password_scheme = CRYPT query = SELECT userid as user, password \ FROM users \ - WHERE userid = '%u' + WHERE userid = '%{user}' } userdb sql { query = SELECT '/home/' || home AS home, uid, gid \ FROM users \ - WHERE userid = '%u' + WHERE userid = '%{user}' } ``` diff --git a/docs/howto/virtual/simple_install.md b/docs/howto/virtual/simple_install.md index c8a747395..0d69ce697 100644 --- a/docs/howto/virtual/simple_install.md +++ b/docs/howto/virtual/simple_install.md @@ -69,7 +69,7 @@ userdb static { fields { uid = vmail gid = vmail - home = /home/vmail/%u + home = /home/vmail/%{user} } } ``` diff --git a/docs/installation/upgrade/2.3-to-2.4.md b/docs/installation/upgrade/2.3-to-2.4.md index fb6bd88f1..c4336fefd 100644 --- a/docs/installation/upgrade/2.3-to-2.4.md +++ b/docs/installation/upgrade/2.3-to-2.4.md @@ -1,6 +1,10 @@ --- layout: doc title: 2.3 to 2.4 +dovecotlinks: + upgrading_directory_hashing: + hash: directory-hashing + text: Directory hashing --- # Upgrading Dovecot CE from 2.3 to 2.4 diff --git a/docs/installation/upgrade/include/2.3-to-2.4.inc b/docs/installation/upgrade/include/2.3-to-2.4.inc index 2243c2e13..a555ae413 100644 --- a/docs/installation/upgrade/include/2.3-to-2.4.inc +++ b/docs/installation/upgrade/include/2.3-to-2.4.inc @@ -16,6 +16,23 @@ configuration changes in the future. Another new required setting is [[setting,dovecot_storage_version]]. This helps to avoid unexpected storage file format incompatibilities. +#### Directory hashing + +If you have been using `/home/%2.256N/%u` or similar constructs: + +* How to replace `%N` in new format: + + * `%2.256Nu` becomes `%{ user | md5 | substr(0, 8) % 256 | hex(2)}` to return maximum 256 different hashes in range `00..ff`. + + * `%256Nu` becomes `%{ user | md5 | substr(0, 8) % 256 | hex}` to return maximum 256 different hashes in range `0..ff` (without 0-padding in the front). + +* How to replace '%M' in new format: + + * `%1Mu/%2.1Mu/%u` becomes `%{user | md5 | hexlify(1)}/%{user | md5 | hexlify | substr(2,1)}/%{user}` to returns directories from `0/0/user` to + `f/f/user`. + +* There is no way to use '%H' anymore. + #### passdb/userdb Section Naming [[link,passdb]] and [[link,userdb]] sections now require a name, i.e.: