# -*- mode: conf -*-
# vim:ft=cfg

# Config file for Radicale - A simple calendar server
#
# Place it into /etc/radicale/config (global)
# or ~/.config/radicale/config (user)
#
# The current values are the default ones


[server]

# CalDAV server hostnames separated by a comma
# IPv4 syntax: address:port
# IPv6 syntax: [address]:port
# Hostname syntax (using "getaddrinfo" to resolve to IPv4/IPv6 adress(es)): hostname:port
# For example: 0.0.0.0:9999, [::]:9999, localhost:9999
#hosts = localhost:5232

# Max parallel connections
#max_connections = 8

# Base delay in case of error 5xx response (seconds)
#delay_on_error = 1

# Max size of request body (bytes), default: 100 Mbyte
# In case of using a reverse proxy in front of check also there related option
#max_content_length = 100000000

# Max resource size (bytes), default: 10 Mbyte
# Limited to 80% of max_content_length to cover plain base64 encoded payload
# Announced to clients requesting "max-resource-size" via PROPFIND
#max_resource_size = 10000000

# Socket timeout (seconds)
#timeout = 30

# SSL flag, enable HTTPS protocol
#ssl = False

# SSL certificate path
#certificate = /etc/ssl/radicale.cert.pem

# SSL private key
#key = /etc/ssl/radicale.key.pem

# CA certificate for validating clients. This can be used to secure
# TCP traffic between Radicale and a reverse proxy
#certificate_authority =

# SSL protocol, secure configuration: ALL -SSLv3 -TLSv1 -TLSv1.1
#protocol = (default)

# SSL ciphersuite, secure configuration: DHE:ECDHE:-NULL:-SHA (see also "man openssl-ciphers")
#ciphersuite = (default)

# script name to strip from URI if called by reverse proxy
#script_name = (default taken from HTTP_X_SCRIPT_NAME or SCRIPT_NAME)

# validate user type
# Value: none|minimal|unicode-letter|no-unicode|strict
#validate_user_value = minimal

# validate path value
# Value: none|minimal|unicode-letter|no-unicode|strict
#validate_path_value = minimal


[encoding]

# Encoding for responding requests
#request = utf-8

# Encoding for storing local collections
#stock = utf-8


[auth]

# Authentication method
# Value: none | htpasswd | remote_user | http_remote_user | http_x_remote_user | dovecot | ldap | oauth2 | pam | denyall
#type = denyall

# Cache logins for until expiration time
#cache_logins = false

# Expiration time for caching successful logins in seconds
#cache_successful_logins_expiry = 15

## Expiration time of caching failed logins in seconds
#cache_failed_logins_expiry = 90

# URI to the LDAP server
#ldap_uri = ldap://localhost

# Base DN of the LDAP server to search for user accounts
#ldap_base = ##BASE_DN##

# Reader DN of the LDAP server;  (needs read access to users and - if defined - groups)
#ldap_reader_dn = CN=ldapreader,CN=Users,##BASE_DN##

# Password of the reader DN (better: use 'ldap_secret_file'!)
#ldap_secret = ldapreader-secret

# Path to the file containing the password of the reader DN
#ldap_secret_file = /run/secrets/ldap_password

# Filter to search for the LDAP entry of the user to authenticate. It must contain '{0}' as placeholder for the login name.
#ldap_filter = (&(objectClass=person)(uid={0}))

# Attribute holding the value to be used as username after authentication
#ldap_user_attribute = cn

# Encryption mode to be used. Default: none; one of: none, tls, starttls
#ldap_security = none

# Certificate verification mode for tls & starttls. Default: REQUIRED; one of NONE, OPTIONAL, REQUIRED
#ldap_ssl_verify_mode = REQUIRED

# Path to the CA file in PEM format to certify the server certificate
#ldap_ssl_ca_file =

# Attribute in the user's LDAP entry to read the group memberships from; default: not set
#ldap_groups_attribute = memberOf

# Attribute in the group entries to read the group's members from, e.g. member; default: not set
#ldap_group_members_attribute = member

# Base DN to search for groups; only if it differs from 'ldap_base' and if 'ldap_group_members_attribute' is set
#ldap_group_base = ##GROUP_BASE_DN##

# Search filter to search for groups having the user DN found as member; only if 'ldap_group_members_attribute' is set
#ldap_group_filter = (objectclass=groupOfNames)

# Quirks for Authentik LDAP server: ignore modifyTimestamp and createTimestamp attributes
#ldap_ignore_attribute_create_modify_timestamp = false

# Connection type for dovecot authentication (AF_UNIX|AF_INET|AF_INET6)
# Note: credentials are transmitted in cleartext
#dovecot_connection_type = AF_UNIX

# The path to the Dovecot client authentication socket (eg. /run/dovecot/auth-client on Fedora). Radicale must have read / write access to the socket.
#dovecot_socket = /var/run/dovecot/auth-client

# Host of via network exposed dovecot socket
#dovecot_host = localhost

# Port of via network exposed dovecot socket
#dovecot_port = 12345

# Remote address source for authentication mechanisms (such as dovecot)
# that are passed this information.
#remote_ip_source = REMOTE_ADDR

# IMAP server hostname
# Syntax: address | address:port | [address]:port | imap.server.tld
#imap_host = localhost

# Secure the IMAP connection
# Value: tls | starttls | none
#imap_security = tls

# OAuth2 token endpoint URL
#oauth2_token_endpoint = <URL>

# OAuth2 client id
#oauth2_client_id = radicale

# OAuth2 client secret (optional)
#oauth2_client_secret =

# PAM service
#pam_serivce = radicale

# PAM group user should be member of
#pam_group_membership =

# Htpasswd filename
#htpasswd_filename = /etc/radicale/users

# Htpasswd encryption method
# Value: plain | bcrypt | md5 | sha256 | sha512 | argon2 | autodetect
# bcrypt requires the installation of 'bcrypt' module.
# argon2 requires the installation of 'argon2-cffi' module.
#htpasswd_encryption = autodetect

# Enable caching of htpasswd file based on size and mtime_ns
#htpasswd_cache = False

# Incorrect or missing authentication or denied access average delay (seconds)
#delay = 1

# Message displayed in the client when a password is needed
#realm = Radicale - Password Required

# Convert username to lowercase, must be true for case-insensitive auth providers
#lc_username = False

# Strip domain name from username
#strip_domain = False

# URL Decode the given username (when URL-encoded by the client - useful for iOS devices when using email address)
#urldecode_username = False


[rights]

# Rights backend
# Value: authenticated | owner_only | owner_write | from_file
#type = owner_only

# File for rights management from_file
#file = /etc/radicale/rights

# Permit delete of a collection (global)
#permit_delete_collection = True

# Permit overwrite of a collection (global)
#permit_overwrite_collection = True


[storage]

# Storage backend
# Value: multifilesystem | multifilesystem_nolock
#type = multifilesystem

# Folder for storing local collections, created if not present
#filesystem_folder = /var/lib/radicale/collections

# Folder for storing cache of local collections, created if not present
# Note: only used in case of use_cache_subfolder_* options are active
# Note: can be used on multi-instance setup to cache files on local node (see below)
#filesystem_cache_folder = (filesystem_folder)

# Use subfolder 'collection-cache' for 'item' cache file structure instead of inside collection folder
# Note: can be used on multi-instance setup to cache 'item' on local node
#use_cache_subfolder_for_item = False

# Use subfolder 'collection-cache' for 'history' cache file structure instead of inside collection folder
# Note: use only on single-instance setup, will break consistency with client in multi-instance setup
#use_cache_subfolder_for_history = False

# Use subfolder 'collection-cache' for 'sync-token' cache file structure instead of inside collection folder
# Note: use only on single-instance setup, will break consistency with client in multi-instance setup
#use_cache_subfolder_for_synctoken = False

# Use last modifiction time (nanoseconds) and size (bytes) for 'item' cache instead of SHA256 (improves speed)
# Note: check used filesystem mtime precision before enabling
# Note: conversion is done on access, bulk conversion can be done offline using storage verification option: radicale --verify-storage
#use_mtime_and_size_for_item_cache = False

# Use configured umask for folder creation (not applicable for OS Windows)
# Useful value: 0077 | 0027 | 0007 | 0022
#folder_umask = (system default, usual 0022)

# Delete sync token that are older (seconds)
#max_sync_token_age = 2592000

# Skip broken item instead of triggering an exception
#skip_broken_item = True

# Strict preconditions check on PUT
#strict_preconditions = False

# Command that is run after changes to storage, default is emtpy
#  Supported placeholders:
#   %(user)s: logged-in user
#   %(cwd)s : current working directory
#   %(path)s: full path of item
#   %(to_path)s: full path of destination item (only set on MOVE request)
#   %(request)s: request method
#  Command will be executed with base directory defined in filesystem_folder
#  For "git" check DOCUMENTATION.md for bootstrap instructions
# Example(test): echo \"user=%(user)s path=%(path)s cwd=%(cwd)s\"
# Example(test/json): echo \"hook-json {'user':'%(user)s', 'cwd':'%(cwd)s', 'path':'%(path)s', 'request':'%(request)s', 'to_path':'%(to_path)s'}\"
# Example(git): git add -A && (git diff --cached --quiet || git commit -m "Changes by \"%(user)s\"")
#hook =

# Create predefined user collections
#
# json format:
#
# predefined_collections = {
#    "def-personal-addressbook": {
#       "D:displayname": "Personal Address Book",
#       "tag": "VADDRESSBOOK"
#    },
#    "def-work-addressbook": {
#       "D:displayname": "Work Address Book",
#       "tag": "VADDRESSBOOK"
#    },
#    "def-personal-calendar": {
#       "C:supported-calendar-component-set": "VEVENT,VJOURNAL,VTODO",
#       "D:displayname": "Personal Calendar",
#       "tag": "VCALENDAR"
#    },
#    "def-birthday-calendar": {
#       "C:supported-calendar-component-set": "VEVENT",
#       "D:displayname": "Birthday Calendar",
#       "tag": "VCALENDAR"
#    },
#    "def-work-calendar": {
#       "C:supported-calendar-component-set": "VEVENT",
#       "D:displayname": "Work Calendar",
#       "tag": "VCALENDAR"
#    }
#  }
#predefined_collections =


[sharing]

# Sharing database type
# Value: none | csv | files
#type = none

# Sharing database path for type 'csv'
#database_path = (filesystem_folder)/collection-db/sharing.csv

# Sharing database path for type 'files'
#database_path = (filesystem_folder)/collection-db/files

# Share collection by token
#collection_by_token = false

# Share collection by map
#collection_by_map = false

# Permit create of token-based sharing
#  If False it can be explicitly granted by permissions: t
#  If True it can be explicitly forbidden by permissions: T
#permit_create_token = false

# Permit create of map-based sharing
#  If False it can be explicitly granted by permissions: m
#  If True it can be explicitly forbidden by permissions: M
#permit_create_map = false

# Permit properties overlay
#  If False it can be explicitly granted by share permissions: P
#  If True it can be explicitly forbidden by share permissions: p
#permit_properties_overlay = false

# Enforce properties overlay on write access
#  If False it can be explicitly enforced by share permissions: E
#  If True it can be explicitly forbidden by share permissions: e
#enforce_properties_overlay = true

# Default permissions for token-based sharing
#   Supported: rwEePp
#default_permissions_create_token = rp

# Default permissions for map-based sharing
#   Supported: rwEePp
#default_permissions_create_map = r


[web]

# Web interface backend
# Value: none | internal
#type = internal

# Prefer browser login
#prefer_browser_login = False


[logging]

# Threshold for the logger
# Value: trace | debug | info | notice | warning | error | critical | alert
#level = info

# Limit content of wrapped text (chars)
#limit_content = 3000

# filter debug messages starting with '<TOKEN>'
#trace_filter = ""

# Don't include passwords in logs
#mask_passwords = True

# Log bad PUT request content
#bad_put_request_content = False

# Log backtrace on level=debug
#backtrace_on_debug = False

# Log request header on level=debug
#request_header_on_debug = False

# Log request content on level=debug
#request_content_on_debug = False

# Log response header on level=debug
#response_header_on_debug = False

# Log response content on level=debug
#response_content_on_debug = False

# Log request header on level=notice with condition
#request_header_on_notice_condition = {}

# Log request content on level=notice with condition
#request_content_on_notice_condition = {}

# Log response header on level=notice with condition
#response_header_on_notice_condition = {}

# Log response content on level=notice with condition
#response_content_on_notice_condition = {}

# Log rights rule which doesn't match on level=debug
#rights_rule_doesnt_match_on_debug = False

# Log storage cache actions on level=debug
#storage_cache_actions_on_debug = False

# Log profiling data on level=info
# Value: per_request | per_request_method | none
#profiling = none

# Log profiling data per request minimum duration (seconds)
#profiling_per_request_min_duration = 3

# Log profiling request header (if passing minimum duration)
#profiling_per_request_header = False

# Log profiling request XML (if passing minimum duration)
#profiling_per_request_xml = False

# Log profiling data per request method interval (seconds)
#profiling_per_request_method_interval = 600

# Log profiling top X functions (limit)
#profiling_top_x_functions = 10


[headers]

# Additional HTTP headers
#Access-Control-Allow-Origin = *

# Set CSP to disallow execution of unknown javascript
# This may become the default in future versions, override if you need a different CSP.
Content-Security-Policy = default-src 'self'; object-src 'none'


[hook]

# Hook types
# Value: none | rabbitmq | email
#type = none

# dry-run (do not really trigger hook action)
#dryrun = False

# hook: rabbitmq
#rabbitmq_endpoint =
#rabbitmq_topic =
#rabbitmq_queue_type = classic

# hook: email
#smtp_server = localhost
#smtp_port = 25
#smtp_security = starttls
#smtp_ssl_verify_mode = REQUIRED
#smtp_username =
#smtp_password =
#from_email =
#mass_email = False
#new_or_added_to_event_template =
#deleted_or_removed_from_event_template =
#updated_event_template =


[reporting]

# When returning a free-busy report, limit the number of returned
# occurences per event to prevent DoS attacks.
#max_freebusy_occurrence = 10000
