Commit d6357ff8 authored by Justin's avatar Justin Committed by Joanne Hugé

software/dovecot: WIP instance dovecot

parent 9134c976
# THIS IS NOT A BUILDOUT FILE, despite purposedly using a compatible syntax.
# The only allowed lines here are (regexes):
# - "^#" comments, copied verbatim
# - "^[" section beginings, copied verbatim
# - lines containing an "=" sign which must fit in the following categorie.
# - "^\s*filename\s*=\s*path\s*$" where "path" is relative to this file
# Copied verbatim.
# - "^\s*hashtype\s*=.*" where "hashtype" is one of the values supported
# by the re-generation script.
# Re-generated.
# - other lines are copied verbatim
# Substitution (${...:...}), extension ([buildout] extends = ...) and
# section inheritance (< = ...) are NOT supported (but you should really
# not need these here).
[template]
filename = instance.cfg.in
md5sum = 35079cbfed884eadcef732ad0842bd46
[template-dovecot]
filename = instance-dovecot.cfg.in
md5sum = 76e0f278e232b0e98b22d12244779b35
[template-postfix]
filename = instance-postfix.cfg.jinja2.in
md5sum = cfea5b89b15081da3a836483e930621b
[template-postfix-master-cf]
filename = postfix_master.cf.jinja2.in
md5sum = ef164517e3f7170d03499967d625c3bb
[template-postfix-main-cf]
filename = postfix_main.cf.jinja2.in
md5sum = e9f03c66627beb4054d45123450162d2
[template-postfix-aliases]
filename = postfix_aliases.jinja2.in
md5sum = 0969fbb25b05c02ef3c2d437b2f4e1a0
[template-dovecot-main-conf]
filename = dovecot_main.conf.in
md5sum = 446bfa9103220c74207baad1fb7bd8d2
## Dovecot configuration file
# If you're in a hurry, see http://wiki2.dovecot.org/QuickConfiguration
# Enable installed protocols
!include_try /usr/share/dovecot/protocols.d/*.protocol
# A comma separated list of IPs or hosts where to listen in for connections.
# "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
# If you want to specify non-default ports or anything more complex,
# edit conf.d/master.conf.
listen = *, ::
# Most of the actual configuration gets included below. The filenames are
# first sorted by their ASCII value and parsed in that order. The 00-prefixes
# in filenames are intended to make it easier to understand the ordering.
!include conf.d/*.conf
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "",
"type": "object",
"additionalProperties": false,
"properties": {
"tcpv4-port": {
"allOf": [
{
"$ref": "./schemas-definitions.json#/tcpv4port"
},
{
"description": "Start allocating ports at this value, going upward"
}
]
},
"postmaster": {
"description": "Mail address to send technical mails to. Non-empty value required for smptd relay service to be deployed. Values will be put in alias-dict as 'postmaster' key (alias-dict takes precedence)",
"default": "",
"type": "string"
},
"alias-dict": {
"description": "Mail alias support",
"default": {},
"patternProperties": {
".*": {
"description": "List of addresses alias expands to",
"type": "array"
}
},
"type": "object"
},
"relay": {
"description": "Forward outgoing mails to a specific relay. If enabled, relay must support TLS-encrypted SASL authentication.",
"dependencies": {
"host": [
"sasl-credential"
]
},
"properties": {
"host": {
"description": "Host name or address of relay, with optional port (ex: '[example.com]:submission'). Enclosing hostname with [] prevents MX lookup.",
"type": "string"
},
"sasl-credential": {
"description": "SASL credential, in the login:password form",
"type": "string"
}
},
"default": {},
"type": "object"
},
"divert": {
"description": "Intercept all mails and send them to given addresses instead of original recipient",
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true
}
}
}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "",
"type": "object",
"additionalProperties": false,
"properties": {
"tcpv4-port": {
"allOf": [
{
"$ref": "./schemas-definitions.json#/tcpv4port"
},
{
"description": "Start allocating ports at this value, going upward"
}
]
},
"postmaster": {
"description": "Mail address to send technical mails to. Non-empty value required for smptd relay service to be deployed. Values will be put in alias-dict as 'postmaster' key (alias-dict takes precedence)",
"type": "string"
},
"alias-dict": {
"description": "Mail alias support",
"patternProperties": {
".*": {
"description": "List of addresses alias expands to",
"type": "array"
}
},
"type": "object"
},
"relay": {
"description": "Forward outgoing mails to a specific relay. If enabled, relay must support TLS-encrypted SASL authentication.",
"dependencies": {
"host": [
"sasl-credential"
]
},
"properties": {
"host": {
"description": "Host name or address of relay, with optional port (ex: '[example.com]:submission'). Enclosing hostname with [] prevents MX lookup.",
"type": "string"
},
"sasl-credential": {
"description": "SASL credential, in the login:password form",
"type": "string"
}
},
"default": {},
"type": "object"
},
"divert": {
"description": "Intercept all mails and send them to given addresses instead of original recipient",
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true
}
}
}
[buildout]
parts =
directory
eggs-directory = {{ buildout['eggs-directory'] }}
develop-eggs-directory = {{ buildout['develop-eggs-directory'] }}
offline= true
[directory]
recipe = slapos.cookbook:mkdirectory
etc = $${buildout:directory}/etc
var = $${buildout:directory}/var
srv = $${buildout:directory}/srv
bin = $${buildout:directory}/bin
tmp = $${buildout:directory}/tmp
usr = $${buildout:directory}/usr
run = $${:etc}/run
service = $${:etc}/service
[instance-parameter]
recipe = slapos.cookbook:slapconfiguration
computer = $${slap-connection:computer-id}
partition = $${slap-connection:partition-id}
url = $${slap-connection:server-url}
key = $${slap-connection:key-file}
cert = $${slap-connection:cert-file}
{% set part_list = [] -%}
{% macro section(name) %}{% do part_list.append(name) %}{{ name }}{% endmacro -%}
{% if slapparameter_dict.get('use-ipv6', True) -%}
{% set ip = '[' ~ (ipv6_set | list)[0] ~ ']' -%}
{% else -%}
{% set ip = (ipv4_set | list)[0] -%}
{% endif -%}
{% set tcpv4_port = slapparameter_dict.get('tcpv4-port', 2025) -%}
{% set relay = slapparameter_dict.get('relay', {}) -%}
{% set alias_dict = slapparameter_dict.get('alias-dict', {}) -%}
{# do alias_dict.setdefault('postmaster', [slapparameter_dict['postmaster']]) -#}
{% do alias_dict.setdefault('postmaster', [""]) -%}
{# set smtpd_sasl_user = slapparameter_dict['smtpd-sasl-user'] -#}
{% set smtpd_sasl_user = slapparameter_dict.get('smtpd-sasl-user', "vmail@nowhere") -%}
{# set smtpd_sasl_password = slapparameter_dict['smtpd-sasl-password'] -#}
{% set smtpd_sasl_password = slapparameter_dict.get('smtpd-sasl-password', "1234") -%}
{% set milter_list = [] %}
[jinja2-template-base]
recipe = slapos.recipe.template:jinja2
[smtpd-password]
recipe = slapos.cookbook:generate.password
storage-path =
[{{ section('publish') }}]
recipe = slapos.cookbook:publish.serialised
url = {{ dumps('smtp://' ~ urllib.parse.quote_plus(smtpd_sasl_user) ~ ':' ~ urllib.parse.quote_plus(smtpd_sasl_password) ~ '@' ~ ip ~ ':' ~ tcpv4_port) }}
[directory]
recipe = slapos.cookbook:mkdirectory
etc = $${buildout:directory}/etc
plugin = $${:etc}/plugin
etc-postfix = $${:etc}/postfix
etc-dovecot = $${:etc}/dovecot
etc-cyrus = $${:etc}/cyrus
run = $${:etc}/run
bin = $${buildout:directory}/bin
usr = $${buildout:directory}/usr
srv = $${buildout:directory}/srv
var = $${buildout:directory}/var
var-log = $${:var}/log
var-lib = $${:var}/lib
var-lib-postfix = $${:var-lib}/postfix
var-spool = $${:var}/spool
var-spool-postfix = $${:var-spool}/postfix
# Not used at buildout level, presence needed by postfix.
var-spool-postfix-active = $${:var-spool-postfix}/active
var-spool-postfix-bounce = $${:var-spool-postfix}/bounce
var-spool-postfix-corrupt = $${:var-spool-postfix}/corrupt
var-spool-postfix-defer = $${:var-spool-postfix}/defer
var-spool-postfix-deferred = $${:var-spool-postfix}/deferred
var-spool-postfix-flush = $${:var-spool-postfix}/flush
var-spool-postfix-hold = $${:var-spool-postfix}/hold
var-spool-postfix-incoming = $${:var-spool-postfix}/incoming
var-spool-postfix-maildrop = $${:var-spool-postfix}/maildrop
var-spool-postfix-pid = $${:var-spool-postfix}/pid
var-spool-postfix-private = $${:var-spool-postfix}/private
var-spool-postfix-public = $${:var-spool-postfix}/public
var-spool-postfix-saved = $${:var-spool-postfix}/saved
var-spool-postfix-trace = $${:var-spool-postfix}/trace
# Used for ERP5 resiliency or (more probably)
# webrunner resiliency with erp5 inside.
[{{ section("resiliency-exclude-file") }}]
# Generate rdiff exclude file
recipe = slapos.recipe.template
inline = {{ '{{ "**\\n" }}' }}
output = $${directory:srv}/exporter.exclude
[configuration]
smtp = {{ dumps(tcpv4_port) }}
inet-interfaces = {{ dumps(ip) }}
alias-dict = {{ dumps(alias_dict) }}
relayhost = {{ dumps(relay.get('host')) }}
relay-sasl-credential = {{ dumps(relay.get('sasl-credential')) }}
cyrus-sasldb = $${directory:etc-cyrus}/postfix.gdbm
milter-list = {{ dumps(milter_list) }}
xz-utils-location = {{ dumps(parameter_dict['xz-utils-location']) }}
[userinfo]
recipe = slapos.cookbook:userinfo
[smtp-sasl-passwd]
< = jinja2-template-base
output = $${directory:etc-postfix}/sasl_passwd
{% if relay -%}
inline = {{ "{{ host }} {{ sasl_credential }}" }}
{%- else -%}
inline = " "
{%- endif %}
context =
key host configuration:relayhost
key sasl_credential configuration:relay-sasl-credential
[{{ section('cyrus-smtpd-conf') }}]
< = jinja2-template-base
output = $${directory:etc-cyrus}/smtpd.conf
inline =
pwcheck_method: auxprop
mech_list: PLAIN LOGIN
sasldb_path: {{ '{{ sasldb }}' }}
context =
key sasldb configuration:cyrus-sasldb
[{{ section('cyrus-smtpd-password') }}]
recipe = plone.recipe.command
stop-on-error = true
command =
rm -f '$${configuration:cyrus-sasldb}' &&
echo '{{ smtpd_sasl_password }}' | '$${wrapper-postfix-saslpasswd2:wrapper-path}' -pc '{{ smtpd_sasl_user }}'
update-command = $${:command}
[smtpd-ssl]
recipe = plone.recipe.command
stop-on-error = true
openssl = '{{ parameter_dict['openssl'] }}/bin/openssl'
cert = $${directory:etc-postfix}/smtpd.crt
key = $${directory:etc-postfix}/smtpd.pem
dh-512 = $${directory:etc-postfix}/dh512.pem
dh-2048 = $${directory:etc-postfix}/dh2048.pem
command =
$${:openssl} dhparam -out '$${:dh-512}' 512 &&
$${:openssl} dhparam -out '$${:dh-2048}' 2048 &&
$${:update}
update =
$${:openssl} req -newkey rsa -batch -new -x509 -days 3650 -nodes -keyout '$${:key}' -out '$${:cert}'
[{{ section('postfix-logrotate') }}]
recipe = slapos.cookbook:cron.d
cron-entries = $${cron:cron-entries}
name = postfix-logrotate
frequency = 0 0 * * *
command = $${directory:bin}/postfix logrotate
[postfix-main-cf-parameter]
postfix-location = {{ parameter_dict['postfix-location'] }}
[{{ section('postfix-main-cf') }}]
< = jinja2-template-base
output = $${directory:etc-postfix}/main.cf
url = {{ parameter_dict['template-postfix-main-cf'] }}
context =
key bin_directory directory:bin
key usr_directory directory:usr
key queue_directory directory:var-spool-postfix
key data_directory directory:var-lib-postfix
key spool_directory directory:var-spool
key mail_owner userinfo:pw-name
key setgid_group userinfo:gr-name
key inet_interfaces configuration:inet-interfaces
key relayhost configuration:relayhost
key sasl_passwd typed-paths:smtp-sasl-passwd
key aliases typed-paths:aliases
key milter_list configuration:milter-list
key cyrus_directory directory:etc-cyrus
key cert smtpd-ssl:cert
key key smtpd-ssl:key
key dh_512 smtpd-ssl:dh-512
key dh_2048 smtpd-ssl:dh-2048
key log_directory directory:var-log
key xz_utils_location configuration:xz-utils-location
key postfix_location postfix-main-cf-parameter:postfix-location
key etc_postfix directory:etc-postfix
[{{ section('postfix-master-cf') }}]
< = jinja2-template-base
output = $${directory:etc-postfix}/master.cf
url = {{ parameter_dict['template-postfix-master-cf'] }}
context = key smtp configuration:smtp
[aliases]
< = jinja2-template-base
url = {{ parameter_dict['template-postfix-aliases'] }}
output = $${directory:etc-postfix}/aliases
context =
key alias_dict configuration:alias-dict
[typed-paths]
# Postfix-friendly rendering of file paths, prefixed with database type.
aliases = hash:$${aliases:output}
smtp-sasl-passwd = hash:$${smtp-sasl-passwd:output}
[{{ section('postalias-db') }}]
recipe = plone.recipe.command
stop-on-error = true
command = '$${wrapper-postalias:wrapper-path}' '$${typed-paths:aliases}' '$${typed-paths:smtp-sasl-passwd}'
update-command = $${:command}
[wrapper-postfix-saslpasswd2]
recipe = slapos.cookbook:wrapper
command-line = '{{ parameter_dict['cyrus-sasl-location'] }}/sbin/saslpasswd2' -f '$${configuration:cyrus-sasldb}'
wrapper-path = $${directory:bin}/saslpasswd2
[base-wrapper]
recipe = slapos.cookbook:wrapper
environment =
MAIL_CONFIG=$${directory:etc-postfix}
SASL_CONF_PATH=$${directory:etc-cyrus}
[base-bin-wrapper]
< = base-wrapper
command-line = $${:path}/$${:basename}
wrapper-path = $${directory:bin}/$${:basename}
[base-bin-bin-wrapper]
< = base-bin-wrapper
path = {{ parameter_dict['postfix-location'] }}/usr/bin
[base-sbin-bin-wrapper]
< = base-bin-wrapper
path = {{ parameter_dict['postfix-location'] }}/usr/sbin
{% for extend, basename_list in (
(
'base-bin-bin-wrapper',
(
'mailq',
'newaliases',
),
),
(
'base-sbin-bin-wrapper',
(
'postalias',
'postcat',
'postconf',
'postdrop',
'postfix',
'postkick',
'postlock',
'postlog',
'postmap',
'postmulti',
'postqueue',
'postsuper',
'sendmail',
),
),
) %}
{% for basename in basename_list -%}
[{{ section('wrapper-' ~ basename) }}]
< = {{ extend }}
basename = {{ basename }}
{% endfor %}
{% endfor %}
[{{ section('postfix-symlinks-libexec') }}]
recipe = slapos.cookbook:symbolic.link
target-directory = $${directory:usr}
link-binary =
{{ parameter_dict['postfix-location'] }}/usr/libexec
[{{ section('service-postfix-master') }}]
< = base-wrapper
command-line = $${directory:usr}/libexec/postfix/master
wrapper-path = $${directory:run}/postfix-master
[{{ section('service-dovecot') }}]
recipe = slapos.cookbook:wrapper
environment =
DOVE_CONFIG= $${directory:etc-dovecot}
command-line = $${directory:usr}/dovecot
wrapper-path = $${directory:run}/dovecot
[{{ section('dovecot-symlinks-libexec') }}]
recipe = slapos.cookbook:symbolic.link
target-directory = $${directory:usr}
link-binary =
{{ parameter_dict['dovecot-location'] }}/libexec/dovecot
[monitor-instance-parameter]
monitor-httpd-ipv6 = {{ (ipv6_set | list)[0] }}
monitor-httpd-port = {{ tcpv4_port + 2 }}
monitor-title = {{ slapparameter_dict.get('name', "Dovecot") }}
password = {{ slapparameter_dict.get('monitor-passwd', "pwd") }}
[buildout]
extends =
{{ template_monitor }}
parts =
{{ part_list | join('\n ') }}
eggs-directory = ${buildout:eggs-directory}
develop-eggs-directory = ${buildout:develop-eggs-directory}
[buildout]
parts =
switch-softwaretype
eggs-directory = ${buildout:eggs-directory}
develop-eggs-directory = ${buildout:develop-eggs-directory}
offline = true
[default-dynamic-template-parameters]
bin-directory = ${buildout:bin-directory}
buildout-bin-directory = ${buildout:bin-directory}
[dynamic-template-postfix-parameters]
<= default-dynamic-template-parameters
cyrus-sasl-location = ${cyrus-sasl:location}
openssl = ${openssl:location}
postfix-location = ${postfix:location}
dovecot-location = ${dovecot:location}
template-postfix-aliases = ${template-postfix-aliases:target}
template-postfix-main-cf = ${template-postfix-main-cf:target}
template-postfix-master-cf = ${template-postfix-master-cf:target}
xz-utils-location = ${xz-utils:location}
[dynamic-template-postfix]
recipe = slapos.recipe.template:jinja2
url = ${template-postfix:output}
filename = instance-postfix.cfg
output = $${buildout:directory}/$${:filename}
extensions = jinja2.ext.do
context =
import urllib urllib
section parameter_dict dynamic-template-postfix-parameters
key slapparameter_dict slap-configuration:configuration
key ipv6_set slap-configuration:ipv6
raw template_monitor ${monitor2-template:output}
raw template_dovecot ${template-dovecot:output}
[slap-configuration]
recipe = slapos.cookbook:slapconfiguration.serialised
computer = $${slap-connection:computer-id}
partition = $${slap-connection:partition-id}
url = $${slap-connection:server-url}
key = $${slap-connection:key-file}
cert = $${slap-connection:cert-file}
[switch-softwaretype]
recipe = slapos.cookbook:switch-softwaretype
default = dynamic-template-postfix:output
RootSoftwareInstance = $${:default}
# See http://www.postfix.org/aliases.5.html for format
{% for name, alias_list in alias_dict.items() -%}
{{ name }}: {{ alias_list | join(', ') }}
{% endfor %}
# http://www.postfix.org/STANDARD_CONFIGURATION_README.html
# http://www.postfix.org/postconf.5.html
queue_directory = {{ queue_directory }}
command_directory = {{ bin_directory }}
daemon_directory = {{ usr_directory }}/libexec/postfix
data_directory = {{ data_directory }}
mail_owner = {{ mail_owner }}
alias_maps = {{ aliases }}
alias_database = {{ aliases }}
mail_spool_directory = {{ spool_directory }}
sendmail_path = {{ bin_directory }}/sendmail
newaliases_path = {{ bin_directory }}/newaliases
mailq_path = {{ bin_directory }}/mailq
setgid_group = {{ setgid_group }}
html_directory = no
manpage_directory = {{ postfix_location }}/usr/local/man
sample_directory = {{ postfix_location }}/etc/postfix
readme_directory = no
inet_interfaces = {{ inet_interfaces }}
smtp_bind_address = 0.0.0.0
smtp_bind_address6 = ::
compatibility_level = 3.6
smtputf8_enable = no
# Compared to default:
# - remove X-related variables, irrelevant for slapos, to be concise
# - add SASL_CONF_PATH to have per-partition cyrus-sasl configuration
import_environment =
MAIL_CONFIG MAIL_DEBUG MAIL_LOGTAG TZ LANG=C
SASL_CONF_PATH
# Mandatory sasl auth over TLS
# XXX: no man-in-the-middle protection
smtpd_tls_cert_file = {{ cert }}
smtpd_tls_key_file = {{ key }}
smtpd_tls_dh512_param_file = {{ dh_512 }}
{#
Note: 1024 vs. 2048 is not a typo, but what is actually recommended in
postfix documentation
-#}
smtpd_tls_dh1024_param_file = {{ dh_2048 }}
smtpd_tls_security_level = encrypt
smtpd_sasl_auth_enable = yes
# Reject as many bogus cases as soon as possible, so errors are visible to ERP5
# developper rather than relying on bounces.
smtpd_recipient_restrictions =
reject_non_fqdn_recipient
reject_unknown_recipient_domain
permit_sasl_authenticated
reject
# Do not allow mynetworks to send mails, only authenticated clients.
smtpd_relay_restrictions =
permit_sasl_authenticated
defer_unauth_destination
# We do not pass mail address in command lines, so accept those starting with
# a dash.
allow_min_user = yes
# Disable local delivery
local_transport = error
smtpd_milters ={{ '\n '.join(milter_list) }}
{% if relayhost -%}
relayhost = {{ relayhost }}
smtp_tls_security_level = encrypt
smtp_tls_session_cache_database = btree:{{ data_directory }}/smtp_scache
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = {{ sasl_passwd }}
smtp_sasl_tls_security_options = noanonymous
{%- endif %}
maillog_file = {{ log_directory }}/postfix.log
maillog_file_compressor = {{ xz_utils_location }}/bin/xz
maillog_file_prefixes = {{ log_directory }}
# http://www.postfix.org/master.5.html
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (yes) (never) (100)
# ==========================================================================
{{ smtp }} inet n - n - - smtpd
pickup unix n - n 60 1 pickup
cleanup unix n - n - 0 cleanup
qmgr unix n - n 300 1 qmgr
tlsmgr unix - - n 1000? 1 tlsmgr
rewrite unix - - n - - trivial-rewrite
bounce unix - - n - 0 bounce
defer unix - - n - 0 bounce
trace unix - - n - 0 bounce
verify unix - - n - 1 verify
flush unix n - n 1000? 0 flush
proxymap unix - - n - - proxymap
proxywrite unix - - n - 1 proxymap
smtp unix - - n - - smtp
relay unix - - n - - smtp
showq unix n - n - - showq
error unix - - n - - error
retry unix - - n - - error
discard unix - - n - - discard
local unix - n n - - local
virtual unix - n n - - virtual
lmtp unix - - n - - lmtp
anvil unix - - n - 1 anvil
scache unix - - n - 1 scache
postlog unix-dgram n - n - 1 postlogd
[buildout]
extends =
../../component/xz-utils/buildout.cfg
../../component/postfix/buildout.cfg
../../stack/monitor/buildout.cfg
../../stack/slapos.cfg
buildout.hash.cfg
parts =
slapos-cookbook
template
dovecot
[dovecot]
recipe = slapos.recipe.cmmi
url = https://dovecot.org/releases/2.3/dovecot-2.3.20.tar.gz
location = @@LOCATION@@
configure-command = ./configure
configure-options =
--prefix=${:location}
make-targets = install
post-install = cp -r ${:location}/share/doc/dovecot/example-config/* ${:location}/etc/dovecot/
[template]
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/${:filename}
output = ${buildout:directory}/template.cfg
[template-postfix]
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/${:filename}
output = ${buildout:directory}/instance-postfix.cfg.jinja2
[template-dovecot]
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/${:filename}
output = ${buildout:directory}/instance-dovecot.cfg
[download-base]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/${:filename}
[template-postfix-master-cf]
< = download-base
[template-postfix-main-cf]
< = download-base
[template-postfix-aliases]
< = download-base
[template-dovecot-main-conf]
< = download-base
{
"name": "ERP5",
"description": "ERP5, Open-Source ERP",
"serialisation": "json-in-xml",
"software-type": {
"default": {
"title": "Default",
"software-type": "default",
"request": "instance-dovecot-input-schema.json",
"response": "instance-dovecot-output-schema.json",
"index": 0
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment