From bd69b7a4915cb1841adf700003018b07f7a260ad Mon Sep 17 00:00:00 2001 From: kaiyou Date: Tue, 14 Jan 2020 01:18:30 +0100 Subject: [PATCH 01/30] Add support for SRS, related to #328 --- core/admin/mailu/internal/views/postfix.py | 33 ++++++++++++++++++++++ core/admin/requirements-prod.txt | 1 + core/admin/requirements.txt | 1 + core/postfix/conf/main.cf | 7 ++++- core/postfix/start.py | 2 ++ 5 files changed, 43 insertions(+), 1 deletion(-) diff --git a/core/admin/mailu/internal/views/postfix.py b/core/admin/mailu/internal/views/postfix.py index 21a4aa91..76d33fca 100644 --- a/core/admin/mailu/internal/views/postfix.py +++ b/core/admin/mailu/internal/views/postfix.py @@ -3,6 +3,7 @@ from mailu.internal import internal import flask import re +import srslib @internal.route("/postfix/domain/") @@ -39,6 +40,38 @@ def postfix_transport(email): return flask.jsonify("smtp:[{}]".format(relay.smtp)) +@internal.route("/postfix/recipient/map/") +def postfix_recipient_map(recipient): + """ Rewrite the envelope recipient if it is a valid SRS address. + + This is meant for bounces to go back to the original sender. + """ + srs = srslib.SRS(flask.current_app.config["SECRET_KEY"]) + if srslib.SRS.is_srs_address(recipient): + try: + return flask.jsonify(srs.reverse(recipient)) + except srslib.Error as error: + return flask.abort(404) + return flask.abort(404) + + +@internal.route("/postfix/sender/map/") +def postfix_sender_map(sender): + """ Rewrite the envelope sender in case the mail was not emitted by us. + + This is for bounces to come back the reverse path properly. + """ + srs = srslib.SRS(flask.current_app.config["SECRET_KEY"]) + domain = flask.current_app.config["DOMAIN"] + try: + localpart, domain_name = models.Email.resolve_domain(sender) + except Exception as error: + return flask.abort(404) + if models.Domain.query.get(domain_name): + return flask.abort(404) + return flask.jsonify(srs.forward(sender, domain)) + + @internal.route("/postfix/sender/login/") def postfix_sender_login(sender): localpart, domain_name = models.Email.resolve_domain(sender) diff --git a/core/admin/requirements-prod.txt b/core/admin/requirements-prod.txt index e61eebfd..1fcffbcd 100644 --- a/core/admin/requirements-prod.txt +++ b/core/admin/requirements-prod.txt @@ -41,6 +41,7 @@ redis==3.2.1 six==1.12.0 socrate==0.1.1 SQLAlchemy==1.3.3 +srslib==0.1.4 tabulate==0.8.3 tenacity==5.0.4 validators==0.12.5 diff --git a/core/admin/requirements.txt b/core/admin/requirements.txt index c68130db..d0515b52 100644 --- a/core/admin/requirements.txt +++ b/core/admin/requirements.txt @@ -22,3 +22,4 @@ tenacity mysqlclient psycopg2 idna +srslib diff --git a/core/postfix/conf/main.cf b/core/postfix/conf/main.cf index 0790946d..8b4b11ac 100644 --- a/core/postfix/conf/main.cf +++ b/core/postfix/conf/main.cf @@ -75,6 +75,12 @@ relay_domains = ${podop}transport transport_maps = ${podop}transport virtual_transport = lmtp:inet:{{ LMTP_ADDRESS }} +# Sender and recipient canonical maps, mostly for SRS +sender_canonical_maps = ${podop}sendermap +sender_canonical_classes = envelope_sender +recipient_canonical_maps = ${podop}recipientmap +recipient_canonical_classes= envelope_recipient,header_recipient + # In order to prevent Postfix from running DNS query, enforce the use of the # native DNS stack, that will check /etc/hosts properly. lmtp_host_lookup = native @@ -120,4 +126,3 @@ milter_default_action = tempfail ############### # Extra Settings ############### - diff --git a/core/postfix/start.py b/core/postfix/start.py index 7523bb4d..12aaa770 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -21,6 +21,8 @@ def start_podop(): ("alias", "url", url + "alias/§"), ("domain", "url", url + "domain/§"), ("mailbox", "url", url + "mailbox/§"), + ("recipientmap", "url", url + "recipient/map/§"), + ("sendermap", "url", url + "sender/map/§"), ("senderaccess", "url", url + "sender/access/§"), ("senderlogin", "url", url + "sender/login/§") ]) From e499d5a8049772aaf011f1ed41a0c475edfed7dd Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Sat, 4 Jan 2020 18:04:25 +0100 Subject: [PATCH 02/30] Add xapian full-text-search plugin to dovecot Currently we are not able to offer our users a FTS experience after the demise of lucene due to unfixed coredumps with musl/alpine. We now add lucene, the only remaining maintained small/lean FTS plugin for dovecot. It is quite simple to add to our stack: A two-stage docker build is used to compile the fts plugin in the first stage, and copy over only the resulting plugin-artifact to the second stage, which is our usual dovecot container. Configuration is also minimal. --- core/dovecot/Dockerfile | 15 ++++++++++++++- core/dovecot/conf/dovecot.conf | 9 ++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/core/dovecot/Dockerfile b/core/dovecot/Dockerfile index bb67370c..5a715099 100644 --- a/core/dovecot/Dockerfile +++ b/core/dovecot/Dockerfile @@ -1,4 +1,15 @@ ARG DISTRO=alpine:3.10 +FROM $DISTRO +WORKDIR /tmp +RUN apk add git build-base automake autoconf libtool dovecot-dev xapian-core-dev icu-dev +RUN git clone https://github.com/grosjo/fts-xapian.git \ + && cd fts-xapian \ + # && git checkout 1.2.7 \ # TODO: when upstream releases 1.2.7, use that + && autoreconf -vi \ + && PANDOC=false ./configure --with-dovecot=/usr/lib/dovecot \ + && make \ + && make install + FROM $DISTRO # python3 shared with most images RUN apk add --no-cache \ @@ -13,9 +24,11 @@ RUN pip3 install "podop>0.2.5" # Image specific layers under this line RUN apk add --no-cache \ - dovecot dovecot-lmtpd dovecot-pop3d dovecot-submissiond dovecot-pigeonhole-plugin rspamd-client \ + dovecot dovecot-lmtpd dovecot-pop3d dovecot-submissiond dovecot-pigeonhole-plugin rspamd-client xapian-core \ && mkdir /var/lib/dovecot +COPY --from=0 /usr/lib/dovecot/lib21_fts_xapian_plugin.* /usr/lib/dovecot/ + COPY conf /conf COPY start.py /start.py diff --git a/core/dovecot/conf/dovecot.conf b/core/dovecot/conf/dovecot.conf index f4181e0c..aa33a7bb 100644 --- a/core/dovecot/conf/dovecot.conf +++ b/core/dovecot/conf/dovecot.conf @@ -21,7 +21,8 @@ mail_access_groups = mail maildir_stat_dirs = yes mailbox_list_index = yes mail_vsize_bg_after_count = 100 -mail_plugins = $mail_plugins quota quota_clone zlib +mail_plugins = $mail_plugins quota quota_clone zlib fts fts_xapian +default_vsz_limit = 2GB namespace inbox { inbox = yes @@ -38,6 +39,12 @@ plugin { quota_vsizes = yes quota_clone_dict = proxy:/tmp/podop.socket:quota + fts = xapian + fts_xapian = partial=2 full=30 + fts_autoindex = yes + fts_enforced = yes + fts_autoindex_exclude = \Trash + {% if COMPRESSION in [ 'gz', 'bz2' ] %} zlib_save = {{ COMPRESSION }} {% endif %} From 4f973f63e62bec857c8e2b63c4f3ddad6e07ce99 Mon Sep 17 00:00:00 2001 From: Tom Radtke Date: Mon, 20 Jan 2020 10:09:11 +0100 Subject: [PATCH 03/30] Upgrading nginx TLS configuration --- core/nginx/conf/nginx.conf | 5 +++-- core/nginx/conf/tls.conf | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/core/nginx/conf/nginx.conf b/core/nginx/conf/nginx.conf index a8228888..250ceec7 100644 --- a/core/nginx/conf/nginx.conf +++ b/core/nginx/conf/nginx.conf @@ -218,8 +218,9 @@ mail { listen 25; listen [::]:25; {% if TLS and not TLS_ERROR %} - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA; + ssl_prefer_server_ciphers on; starttls on; {% endif %} protocol smtp; diff --git a/core/nginx/conf/tls.conf b/core/nginx/conf/tls.conf index e1639256..5d7ec031 100644 --- a/core/nginx/conf/tls.conf +++ b/core/nginx/conf/tls.conf @@ -1,8 +1,8 @@ -ssl_protocols TLSv1.1 TLSv1.2; -ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384'; -ssl_prefer_server_ciphers on; -ssl_session_timeout 10m; -ssl_session_tickets off; ssl_certificate {{ TLS[0] }}; ssl_certificate_key {{ TLS[1] }}; +ssl_session_timeout 1d; +ssl_session_tickets off; ssl_dhparam /conf/dhparam.pem; +ssl_protocols TLSv1.2 TLSv1.3; +ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; +ssl_prefer_server_ciphers off; From 99ecaee7b97dfa15a0939856d6c9caa7d2776f19 Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Thu, 23 Jan 2020 22:35:30 +0100 Subject: [PATCH 04/30] Use a released git-tag for fts-xapian --- core/dovecot/Dockerfile | 2 +- towncrier/newsfragments/1320.feature | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 towncrier/newsfragments/1320.feature diff --git a/core/dovecot/Dockerfile b/core/dovecot/Dockerfile index 5a715099..60f6ab9b 100644 --- a/core/dovecot/Dockerfile +++ b/core/dovecot/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /tmp RUN apk add git build-base automake autoconf libtool dovecot-dev xapian-core-dev icu-dev RUN git clone https://github.com/grosjo/fts-xapian.git \ && cd fts-xapian \ - # && git checkout 1.2.7 \ # TODO: when upstream releases 1.2.7, use that + && git checkout 1.2.7 \ && autoreconf -vi \ && PANDOC=false ./configure --with-dovecot=/usr/lib/dovecot \ && make \ diff --git a/towncrier/newsfragments/1320.feature b/towncrier/newsfragments/1320.feature new file mode 100644 index 00000000..fc3fd5aa --- /dev/null +++ b/towncrier/newsfragments/1320.feature @@ -0,0 +1 @@ +Allow users to use server-sided full-text-search again by adding the dovecot fts-xapian plugin From 9b10e938cf5fd46dcafe25ed1c16237a3ebe4156 Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Tue, 19 Nov 2019 12:22:09 +0100 Subject: [PATCH 05/30] Make roundcube db configurable --- webmails/roundcube/config.inc.php | 2 +- webmails/roundcube/start.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/webmails/roundcube/config.inc.php b/webmails/roundcube/config.inc.php index eff8ffa2..09a743d7 100644 --- a/webmails/roundcube/config.inc.php +++ b/webmails/roundcube/config.inc.php @@ -3,7 +3,7 @@ $config = array(); // Generals -$config['db_dsnw'] = 'sqlite:////data/roundcube.db'; +$config['db_dsnw'] = getenv('DB_DSNW');; $config['temp_dir'] = '/tmp/'; $config['des_key'] = getenv('SECRET_KEY'); $config['cipher_method'] = 'AES-256-CBC'; diff --git a/webmails/roundcube/start.py b/webmails/roundcube/start.py index 55832dd0..7851921a 100755 --- a/webmails/roundcube/start.py +++ b/webmails/roundcube/start.py @@ -9,6 +9,29 @@ log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", "WARNING")) os.environ["MAX_FILESIZE"] = str(int(int(os.environ.get("MESSAGE_SIZE_LIMIT"))*0.66/1048576)) +db_flavor=os.environ.get("ROUNDCUBE_DB_FLAVOR","sqlite") +if db_flavor=="sqlite": + os.environ["DB_DSNW"]="sqlite:////data/roundcube.db" +elif db_flavor=="mysql": + os.environ["DB_DSNW"]="mysql://%s:%s@%s/%s" % ( + os.environ.get("ROUNDCUBE_DB_USER","roundcube"), + os.environ.get("ROUNDCUBE_DB_PW"), + os.environ.get("ROUNDCUBE_DB_HOST","database"), + os.environ.get("ROUNDCUBE_DB_NAME","roundcube") + ) +elif db_flavor=="postgresql": + os.environ["DB_DSNW"]="pgsql://%s:%s@%s/%s" % ( + os.environ.get("ROUNDCUBE_DB_USER","roundcube"), + os.environ.get("ROUNDCUBE_DB_PW"), + os.environ.get("ROUNDCUBE_DB_HOST","database"), + os.environ.get("ROUNDCUBE_DB_NAME","roundcube") + ) +else: + print("Unknown ROUNDCUBE_DB_FLAVOR: %s",db_flavor) + exit(1) + + + conf.jinja("/php.ini", os.environ, "/usr/local/etc/php/conf.d/roundcube.ini") # Fix some permissions From 06926561e238b4a8d81c106e073b87dfb4563191 Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Thu, 21 Nov 2019 13:32:45 +0100 Subject: [PATCH 06/30] Install php-mysql on roundcube container --- webmails/roundcube/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webmails/roundcube/Dockerfile b/webmails/roundcube/Dockerfile index ad326767..53c043d9 100644 --- a/webmails/roundcube/Dockerfile +++ b/webmails/roundcube/Dockerfile @@ -21,7 +21,7 @@ ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1 RUN apt-get update && apt-get install -y \ zlib1g-dev libzip4 libzip-dev \ python3-jinja2 \ - && docker-php-ext-install zip \ + && docker-php-ext-install zip pdo_mysql \ && echo date.timezone=UTC > /usr/local/etc/php/conf.d/timezone.ini \ && rm -rf /var/www/html/ \ && cd /var/www \ From 982c93e3a2355ba211ee996e1d12cac09668a224 Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Thu, 21 Nov 2019 13:46:38 +0100 Subject: [PATCH 07/30] Print roundcube error log to stdout --- webmails/roundcube/start.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/webmails/roundcube/start.py b/webmails/roundcube/start.py index 7851921a..f2c037cb 100755 --- a/webmails/roundcube/start.py +++ b/webmails/roundcube/start.py @@ -4,6 +4,7 @@ import os import logging as log import sys from socrate import conf +import subprocess log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", "WARNING")) @@ -35,8 +36,12 @@ else: conf.jinja("/php.ini", os.environ, "/usr/local/etc/php/conf.d/roundcube.ini") # Fix some permissions -os.system("mkdir -p /data/gpg") -os.system("chown -R www-data:www-data /data") +os.system("mkdir -p /data/gpg /var/www/html/logs") +os.system("touch /var/www/html/logs/errors") +os.system("chown -R www-data:www-data /data /var/www/html/logs") + +# Tail roundcube logs +subprocess.Popen(["tail","-f","-n","0","/var/www/html/logs/errors"]) # Run apache os.execv("/usr/local/bin/apache2-foreground", ["apache2-foreground"]) From 8242ca150ded72c5c06c9af91e751ed142a4543e Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Thu, 21 Nov 2019 21:05:15 +0100 Subject: [PATCH 08/30] Create/update database on startup --- webmails/roundcube/start.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/webmails/roundcube/start.py b/webmails/roundcube/start.py index f2c037cb..e7dda509 100755 --- a/webmails/roundcube/start.py +++ b/webmails/roundcube/start.py @@ -40,6 +40,23 @@ os.system("mkdir -p /data/gpg /var/www/html/logs") os.system("touch /var/www/html/logs/errors") os.system("chown -R www-data:www-data /data /var/www/html/logs") +try: + print("Initializing database") + result=subprocess.check_output(["/var/www/html/bin/initdb.sh","--dir","/var/www/html/SQL"],stderr=subprocess.STDOUT) + print(result.decode()) +except subprocess.CalledProcessError as e: + if "already exists" in e.stdout.decode(): + print("Already initialzed") + else: + print(e.stdout.decode()) + quit(1) + +try: + print("Upgrading database") + subprocess.check_call(["/var/www/html/bin/update.sh","--version=?","-y"],stderr=subprocess.STDOUT) +except subprocess.CalledProcessError as e: + quit(1) + # Tail roundcube logs subprocess.Popen(["tail","-f","-n","0","/var/www/html/logs/errors"]) From 2cb42c31a6c6f5c4470bce4a034bc4bd50ed24dc Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Thu, 21 Nov 2019 23:32:01 +0100 Subject: [PATCH 09/30] Add changelog --- towncrier/newsfragments/1268.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 towncrier/newsfragments/1268.feature diff --git a/towncrier/newsfragments/1268.feature b/towncrier/newsfragments/1268.feature new file mode 100644 index 00000000..e4cf9a5a --- /dev/null +++ b/towncrier/newsfragments/1268.feature @@ -0,0 +1 @@ +The roundcube container does support mysql now (no setup integration yet) From fe09cf57052ae8a21810897bff7b1c9a183ed9a9 Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Thu, 12 Dec 2019 20:43:56 +0100 Subject: [PATCH 10/30] Default to DB_FLAVOR/DB_HOST if ROUNDCUBE_DB_FLAVOR/ROUNDCUBE_DB_HOST is not set --- webmails/roundcube/start.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webmails/roundcube/start.py b/webmails/roundcube/start.py index e7dda509..ef913a2b 100755 --- a/webmails/roundcube/start.py +++ b/webmails/roundcube/start.py @@ -10,21 +10,21 @@ log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", "WARNING")) os.environ["MAX_FILESIZE"] = str(int(int(os.environ.get("MESSAGE_SIZE_LIMIT"))*0.66/1048576)) -db_flavor=os.environ.get("ROUNDCUBE_DB_FLAVOR","sqlite") +db_flavor=os.environ.get("ROUNDCUBE_DB_FLAVOR",os.environ.get("DB_FLAVOR","sqlite")) if db_flavor=="sqlite": os.environ["DB_DSNW"]="sqlite:////data/roundcube.db" elif db_flavor=="mysql": os.environ["DB_DSNW"]="mysql://%s:%s@%s/%s" % ( os.environ.get("ROUNDCUBE_DB_USER","roundcube"), os.environ.get("ROUNDCUBE_DB_PW"), - os.environ.get("ROUNDCUBE_DB_HOST","database"), + os.environ.get("ROUNDCUBE_DB_HOST",os.environ.get("DB_HOST","database")), os.environ.get("ROUNDCUBE_DB_NAME","roundcube") ) elif db_flavor=="postgresql": os.environ["DB_DSNW"]="pgsql://%s:%s@%s/%s" % ( os.environ.get("ROUNDCUBE_DB_USER","roundcube"), os.environ.get("ROUNDCUBE_DB_PW"), - os.environ.get("ROUNDCUBE_DB_HOST","database"), + os.environ.get("ROUNDCUBE_DB_HOST",os.environ.get("DB_HOST","database")), os.environ.get("ROUNDCUBE_DB_NAME","roundcube") ) else: From 0593ccd9f06935004b9d29e6b98576c4cdf854d0 Mon Sep 17 00:00:00 2001 From: NeroPcStation Date: Thu, 30 Jan 2020 14:34:55 +0100 Subject: [PATCH 11/30] Fix bad file path in documentation --- docs/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index 91bdd20c..0ac41a8f 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -456,7 +456,7 @@ follow these steps: logging: driver: journald -2. Add the /etc/fail2ban/jail.d/bad-auth.conf +2. Add the /etc/fail2ban/filter.d/bad-auth.conf .. code-block:: bash From 27e37577c61067fb9dec77a895238ee6d05d616e Mon Sep 17 00:00:00 2001 From: Philip Rosenberg-Watt Date: Mon, 3 Feb 2020 14:53:04 -0700 Subject: [PATCH 12/30] Add IPv6 to allow_nets Roundcube was not connecting to sieve with IPv6 enabled. Fixes #1336 --- core/admin/mailu/configuration.py | 1 + core/admin/mailu/internal/views/dovecot.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/core/admin/mailu/configuration.py b/core/admin/mailu/configuration.py index 7dcd7c3a..66b0b832 100644 --- a/core/admin/mailu/configuration.py +++ b/core/admin/mailu/configuration.py @@ -67,6 +67,7 @@ DEFAULT_CONFIG = { 'HOST_REDIS': 'redis', 'HOST_FRONT': 'front', 'SUBNET': '192.168.203.0/24', + 'SUBNET6': None, 'POD_ADDRESS_RANGE': None } diff --git a/core/admin/mailu/internal/views/dovecot.py b/core/admin/mailu/internal/views/dovecot.py index f44f59bc..9c665977 100644 --- a/core/admin/mailu/internal/views/dovecot.py +++ b/core/admin/mailu/internal/views/dovecot.py @@ -11,6 +11,8 @@ def dovecot_passdb_dict(user_email): user = models.User.query.get(user_email) or flask.abort(404) allow_nets = [] allow_nets.append(app.config["SUBNET"]) + if app.config["SUBNET6"]: + allow_nets.append(app.config["SUBNET6"]) if app.config["POD_ADDRESS_RANGE"]: allow_nets.append(app.config["POD_ADDRESS_RANGE"]) return flask.jsonify({ From 6ad9b7c2b2498866551399fcc227dd7ebadb6900 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Fri, 7 Feb 2020 15:17:29 +0100 Subject: [PATCH 13/30] Add a newsfragment --- towncrier/newsfragments/328.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 towncrier/newsfragments/328.feature diff --git a/towncrier/newsfragments/328.feature b/towncrier/newsfragments/328.feature new file mode 100644 index 00000000..b73b478e --- /dev/null +++ b/towncrier/newsfragments/328.feature @@ -0,0 +1 @@ +Add support for backward-forwarding using SRS \ No newline at end of file From ff1dfec39aa85df24a6f6e8bf74aec3337b88883 Mon Sep 17 00:00:00 2001 From: Philip Rosenberg-Watt Date: Sun, 9 Feb 2020 07:25:45 -0700 Subject: [PATCH 14/30] Add port to relay if it contains a colon This closes #1357 --- core/admin/mailu/internal/views/postfix.py | 6 +++++- towncrier/newsfragments/1357.feature | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 towncrier/newsfragments/1357.feature diff --git a/core/admin/mailu/internal/views/postfix.py b/core/admin/mailu/internal/views/postfix.py index 21a4aa91..0843b147 100644 --- a/core/admin/mailu/internal/views/postfix.py +++ b/core/admin/mailu/internal/views/postfix.py @@ -36,7 +36,11 @@ def postfix_transport(email): return flask.abort(404) localpart, domain_name = models.Email.resolve_domain(email) relay = models.Relay.query.get(domain_name) or flask.abort(404) - return flask.jsonify("smtp:[{}]".format(relay.smtp)) + ret = "smtp:[{0}]".format(relay.smtp) + if ":" in relay.smtp: + split = relay.smtp.split(':') + ret = "smtp:[{0}]:{1}".format(split[0], split[1]) + return flask.jsonify(ret) @internal.route("/postfix/sender/login/") diff --git a/towncrier/newsfragments/1357.feature b/towncrier/newsfragments/1357.feature new file mode 100644 index 00000000..56fa443f --- /dev/null +++ b/towncrier/newsfragments/1357.feature @@ -0,0 +1 @@ +Relay a domain to a nonstandard SMTP port by adding ":" to the remote hostname or IP address. \ No newline at end of file From 8e88f1b8c36d8cd194a50eea444c12ecb45b64cb Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 9 Feb 2020 17:38:18 +0100 Subject: [PATCH 15/30] Refactor the rate limiting code Rate limiting was already redesigned to use Python limits. This introduced some unexpected behavior, including the fact that only one criteria is supported per limiter. Docs and setup utility are updated with this in mind. Also, the code was made more generic, so limiters can be delivered for something else than authentication. Authentication-specific code was moved directly to the authentication routine. --- core/admin/mailu/internal/__init__.py | 16 --------- core/admin/mailu/internal/views/auth.py | 20 ++++++++--- core/admin/mailu/limiter.py | 48 ++++++++++++------------- core/admin/mailu/utils.py | 2 +- docs/compose/.env | 6 +++- docs/configuration.rst | 5 ++- setup/flavors/compose/mailu.env | 4 +-- setup/templates/steps/config.html | 7 ++-- 8 files changed, 53 insertions(+), 55 deletions(-) diff --git a/core/admin/mailu/internal/__init__.py b/core/admin/mailu/internal/__init__.py index 95f2f782..560f4d97 100644 --- a/core/admin/mailu/internal/__init__.py +++ b/core/admin/mailu/internal/__init__.py @@ -1,23 +1,7 @@ -from mailu.limiter import RateLimitExceeded - -from mailu import utils -from flask import current_app as app - -import socket import flask internal = flask.Blueprint('internal', __name__, template_folder='templates') -@internal.app_errorhandler(RateLimitExceeded) -def rate_limit_handler(e): - response = flask.Response() - response.headers['Auth-Status'] = 'Authentication rate limit from one source exceeded' - response.headers['Auth-Error-Code'] = '451 4.3.2' - if int(flask.request.headers['Auth-Login-Attempt']) < 10: - response.headers['Auth-Wait'] = '3' - return response - - from mailu.internal.views import * diff --git a/core/admin/mailu/internal/views/auth.py b/core/admin/mailu/internal/views/auth.py index b1f37d17..825dba56 100644 --- a/core/admin/mailu/internal/views/auth.py +++ b/core/admin/mailu/internal/views/auth.py @@ -5,21 +5,31 @@ from flask import current_app as app import flask import flask_login import base64 - +import ipaddress @internal.route("/auth/email") def nginx_authentication(): """ Main authentication endpoint for Nginx email server """ - utils.limiter.check(flask.request.headers["Client-Ip"]) + limiter = utils.limiter.get_limiter(app.config["AUTH_RATELIMIT"], "auth-ip") + client_ip = flask.request.headers["Client-Ip"] + if not limiter.test(client_ip): + response = flask.Response() + response.headers['Auth-Status'] = 'Authentication rate limit from one source exceeded' + response.headers['Auth-Error-Code'] = '451 4.3.2' + if int(flask.request.headers['Auth-Login-Attempt']) < 10: + response.headers['Auth-Wait'] = '3' + return response headers = nginx.handle_authentication(flask.request.headers) response = flask.Response() for key, value in headers.items(): response.headers[key] = str(value) - if ("Auth-Status" not in headers) or (headers["Auth-Status"]!="OK"): - utils.limiter.hit(flask.request.headers["Client-Ip"]) - + if ("Auth-Status" not in headers) or (headers["Auth-Status"] != "OK"): + limit_subnet = str(app.config["AUTH_RATELIMIT_SUBNET"]) != 'False' + subnet = ipaddress.ip_network(app.config["SUBNET"]) + if limit_subnet or ipaddress.ip_address(client_ip) not in subnet: + limiter.hit(flask.request.headers["Client-Ip"]) return response diff --git a/core/admin/mailu/limiter.py b/core/admin/mailu/limiter.py index f7819c31..b5f99915 100644 --- a/core/admin/mailu/limiter.py +++ b/core/admin/mailu/limiter.py @@ -1,36 +1,34 @@ import limits import limits.storage import limits.strategies -import ipaddress -class RateLimitExceeded(Exception): - pass -class Limiter: +class LimitWrapper(object): + """ Wraps a limit by providing the storage, item and identifiers + """ - def __init__(self): - self.storage = None - self.limiter = None - self.rate = None - self.subnet = None - self.rate_limit_subnet = True + def __init__(self, limiter, limit, *identifiers): + self.limiter = limiter + self.limit = limit + self.base_identifiers = identifiers + + def test(self, *args): + return self.limiter.test(self.limit, *(self.base_identifiers + args)) + + def hit(self, *args): + return self.limiter.hit(self.limit, *(self.base_identifiers + args)) + + def get_window_stats(self, *args): + return self.limiter.get_window_stats(self.limit, *(self.base_identifiers + args)) + + +class LimitWraperFactory(object): + """ Global limiter, to be used as a factory + """ def init_app(self, app): self.storage = limits.storage.storage_from_string(app.config["RATELIMIT_STORAGE_URL"]) self.limiter = limits.strategies.MovingWindowRateLimiter(self.storage) - self.rate = limits.parse(app.config["AUTH_RATELIMIT"]) - self.rate_limit_subnet = str(app.config["AUTH_RATELIMIT_SUBNET"])!='False' - self.subnet = ipaddress.ip_network(app.config["SUBNET"]) - def check(self,clientip): - # disable limits for internal requests (e.g. from webmail)? - if self.rate_limit_subnet==False and ipaddress.ip_address(clientip) in self.subnet: - return - if not self.limiter.test(self.rate,"client-ip",clientip): - raise RateLimitExceeded() - - def hit(self,clientip): - # disable limits for internal requests (e.g. from webmail)? - if self.rate_limit_subnet==False and ipaddress.ip_address(clientip) in self.subnet: - return - self.limiter.hit(self.rate,"client-ip",clientip) + def get_limiter(self, limit, *args): + return LimitWrapper(self.limiter, limits.parse(limit), *args) \ No newline at end of file diff --git a/core/admin/mailu/utils.py b/core/admin/mailu/utils.py index df23b8e7..39181678 100644 --- a/core/admin/mailu/utils.py +++ b/core/admin/mailu/utils.py @@ -20,7 +20,7 @@ def handle_needs_login(): ) # Rate limiter -limiter = limiter.Limiter() +limiter = limiter.LimitWraperFactory() # Application translation babel = flask_babel.Babel() diff --git a/docs/compose/.env b/docs/compose/.env index 218b94d2..69c91d82 100644 --- a/docs/compose/.env +++ b/docs/compose/.env @@ -38,7 +38,7 @@ POSTMASTER=admin TLS_FLAVOR=cert # Authentication rate limit (per source IP address) -AUTH_RATELIMIT=10/minute;1000/hour +AUTH_RATELIMIT=10/minute # Opt-out of statistics, replace with "True" to opt out DISABLE_STATISTICS=False @@ -68,6 +68,10 @@ ANTIVIRUS=none # Max attachment size will be 33% smaller MESSAGE_SIZE_LIMIT=50000000 +# Message rate limit for outgoing messages +# This limit is per user +MESSAGE_RATELIMIT=100/day + # Networks granted relay permissions # Use this with care, all hosts in this networks will be able to send mail without authentication! RELAYNETS= diff --git a/docs/configuration.rst b/docs/configuration.rst index cda6becb..2b7227ba 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -46,7 +46,6 @@ rules does also apply to auth requests coming from ``SUBNET``, especially for th If you disable this, ensure that the rate limit on the webmail is enforced in a different way (e.g. roundcube plug-in), otherwise an attacker can simply bypass the limit using webmail. - The ``TLS_FLAVOR`` sets how Mailu handles TLS connections. Setting this value to ``notls`` will cause Mailu not to server any web content! More on :ref:`tls_flavor`. @@ -57,6 +56,10 @@ The ``MESSAGE_SIZE_LIMIT`` is the maximum size of a single email. It should not be too low to avoid dropping legitimate emails and should not be too high to avoid filling the disks with large junk emails. +The ``MESSAGE_RATELIMIT`` is the limit of messages a single user can send. This is +meant to fight outbound spam in case of compromised or malicious account on the +server. + The ``RELAYNETS`` are network addresses for which mail is relayed for free with no authentication required. This should be used with great care. If you want other Docker services' outbound mail to be relayed, you can set this to ``172.16.0.0/12`` diff --git a/setup/flavors/compose/mailu.env b/setup/flavors/compose/mailu.env index 180239c3..78ecce72 100644 --- a/setup/flavors/compose/mailu.env +++ b/setup/flavors/compose/mailu.env @@ -30,8 +30,8 @@ POSTMASTER={{ postmaster }} TLS_FLAVOR={{ tls_flavor }} # Authentication rate limit (per source IP address) -{% if auth_ratelimit_pm > '0' and auth_ratelimit_ph > '0' %} -AUTH_RATELIMIT={{ auth_ratelimit_pm }}/minute;{{ auth_ratelimit_ph }}/hour +{% if auth_ratelimit_pm > '0' %} +AUTH_RATELIMIT={{ auth_ratelimit_pm }}/minute {% endif %} # Opt-out of statistics, replace with "True" to opt out diff --git a/setup/templates/steps/config.html b/setup/templates/steps/config.html index 330e008f..b3e57e22 100644 --- a/setup/templates/steps/config.html +++ b/setup/templates/steps/config.html @@ -47,11 +47,10 @@ Or in plain english: if receivers start to classify your mail as spam, this post
- +

/minute; - /hour

+ value="10" required > / minute +

From 365f21007d4769aa7345179b0962f8b8950d730d Mon Sep 17 00:00:00 2001 From: NeroPcStation Date: Sun, 16 Feb 2020 19:59:15 +0000 Subject: [PATCH 16/30] Translated using Weblate (Polish) Currently translated at 90.2% (147 of 163 strings) Translation: Mailu/admin Translate-URL: https://translate.tedomum.net/projects/mailu/admin/pl/ --- .../translations/pl/LC_MESSAGES/messages.po | 84 +++++++++++-------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/core/admin/mailu/translations/pl/LC_MESSAGES/messages.po b/core/admin/mailu/translations/pl/LC_MESSAGES/messages.po index 595b1b43..cec7a4a0 100644 --- a/core/admin/mailu/translations/pl/LC_MESSAGES/messages.po +++ b/core/admin/mailu/translations/pl/LC_MESSAGES/messages.po @@ -1,11 +1,17 @@ msgid "" msgstr "" +"Project-Id-Version: Mailu\n" +"PO-Revision-Date: 2020-02-17 20:23+0000\n" +"Last-Translator: NeroPcStation \n" +"Language-Team: Polish \n" +"Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: POEditor.com\n" -"Project-Id-Version: Mailu\n" -"Language: pl\n" +"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 3.3\n" #: mailu/ui/forms.py:32 msgid "Invalid email address." @@ -65,7 +71,7 @@ msgstr "Potwierdź hasło" #: mailu/ui/forms.py:80 mailu/ui/templates/user/list.html:22 #: mailu/ui/templates/user/signup_domain.html:16 msgid "Quota" -msgstr "" +msgstr "Maksymalna przestrzeń na dysku" #: mailu/ui/forms.py:81 msgid "Allow IMAP access" @@ -101,7 +107,7 @@ msgstr "" #: mailu/ui/forms.py:111 mailu/ui/templates/sidebar.html:16 msgid "Update password" -msgstr "Zmień hasło" +msgstr "Zaktualizuj hasło" #: mailu/ui/forms.py:100 msgid "Enable forwarding" @@ -146,7 +152,7 @@ msgstr "Prześlij" #: mailu/ui/forms.py:150 msgid "Manager email" -msgstr "" +msgstr "E-mail menedżera" #: mailu/ui/forms.py:155 msgid "Protocol" @@ -192,7 +198,7 @@ msgstr "Twoje konto" #: mailu/ui/templates/login.html:8 msgid "to access the administration tools" -msgstr "" +msgstr "aby uzyskać dostęp do narzędzi administracyjnych" #: mailu/ui/templates/services.html:4 mailu/ui/templates/sidebar.html:39 msgid "Services status" @@ -212,7 +218,7 @@ msgstr "PID" #: mailu/ui/templates/services.html:13 msgid "Image" -msgstr "" +msgstr "Obraz" #: mailu/ui/templates/services.html:14 msgid "Started" @@ -265,7 +271,7 @@ msgstr "Pomoc" #: mailu/ui/templates/working.html:4 msgid "We are still working on this feature!" -msgstr "" +msgstr "Nadal pracujemy nad tą funkcją!" #: mailu/ui/templates/admin/create.html:4 msgid "Add a global administrator" @@ -405,7 +411,7 @@ msgstr "Aliasy" #: mailu/ui/templates/domain/list.html:37 msgid "Managers" -msgstr "" +msgstr "Menedżerowie" #: mailu/ui/templates/fetch/create.html:4 msgid "Add a fetched account" @@ -425,19 +431,19 @@ msgstr "" #: mailu/ui/templates/fetch/list.html:22 msgid "Last check" -msgstr "" +msgstr "Ostatnie sprawdzenie" #: mailu/ui/templates/manager/create.html:4 msgid "Add a manager" -msgstr "" +msgstr "Dodaj menedżera" #: mailu/ui/templates/manager/list.html:4 msgid "Manager list" -msgstr "" +msgstr "Lista menedżerów" #: mailu/ui/templates/manager/list.html:12 msgid "Add manager" -msgstr "" +msgstr "Dodaj menedżera" #: mailu/ui/forms.py:168 msgid "Announcement subject" @@ -489,7 +495,7 @@ msgstr "Ustawienia użytkownika" #: mailu/ui/templates/user/list.html:21 msgid "Features" -msgstr "" +msgstr "Funkcje" #: mailu/ui/templates/user/password.html:4 msgid "Password update" @@ -501,7 +507,7 @@ msgstr "Automatyczna odpowiedź" #: mailu/ui/forms.py:49 msgid "Maximum user quota" -msgstr "" +msgstr "Maksymalny przydział użytkownika" #: mailu/ui/forms.py:101 msgid "Keep a copy of the emails" @@ -525,7 +531,7 @@ msgstr "Nie" #: mailu/ui/forms.py:65 msgid "Alternative name" -msgstr "" +msgstr "Alternatywna nazwa" #: mailu/ui/forms.py:70 msgid "Relayed domain name" @@ -533,7 +539,7 @@ msgstr "" #: mailu/ui/forms.py:71 mailu/ui/templates/relay/list.html:18 msgid "Remote host" -msgstr "" +msgstr "Zdalny host" #: mailu/ui/templates/sidebar.html:54 msgid "Relayed domains" @@ -541,19 +547,19 @@ msgstr "" #: mailu/ui/templates/alternative/create.html:4 msgid "Create alternative domain" -msgstr "" +msgstr "Utwórz alternatywną domenę" #: mailu/ui/templates/alternative/list.html:4 msgid "Alternative domain list" -msgstr "" +msgstr "Alternatywna lista domen" #: mailu/ui/templates/alternative/list.html:12 msgid "Add alternative" -msgstr "" +msgstr "Dodaj alternatywę" #: mailu/ui/templates/alternative/list.html:19 msgid "Name" -msgstr "" +msgstr "Nazwa" #: mailu/ui/templates/domain/list.html:39 msgid "Alternatives" @@ -577,19 +583,19 @@ msgstr "" #: mailu/ui/forms.py:125 msgid "Your token (write it down, as it will never be displayed again)" -msgstr "" +msgstr "Twój token (zapisz go, ponieważ nigdy więcej nie będzie wyświetlany)" #: mailu/ui/forms.py:130 mailu/ui/templates/token/list.html:20 msgid "Authorized IP" -msgstr "" +msgstr "Autoryzowany adres IP" #: mailu/ui/templates/sidebar.html:31 mailu/ui/templates/token/list.html:4 msgid "Authentication tokens" -msgstr "" +msgstr "Tokeny uwierzytelnienia" #: mailu/ui/templates/sidebar.html:72 msgid "Go to" -msgstr "" +msgstr "Przejdź do" #: mailu/ui/templates/sidebar.html:76 msgid "Webmail" @@ -601,7 +607,7 @@ msgstr "Strona internetowa" #: mailu/ui/templates/token/create.html:4 msgid "Create an authentication token" -msgstr "" +msgstr "Utwórz token uwierzytelniający" #: mailu/ui/templates/token/list.html:12 msgid "New token" @@ -625,7 +631,7 @@ msgstr "Filtr antyspamowy" #: mailu/ui/forms.py:99 msgid "Spam filter tolerance" -msgstr "" +msgstr "Tolerancja filtra spamu" #: mailu/ui/forms.py:50 msgid "Enable sign-up" @@ -633,7 +639,7 @@ msgstr "Włącz rejestrację" #: mailu/ui/forms.py:57 msgid "Initial admin" -msgstr "" +msgstr "Początkowy administrator" #: mailu/ui/forms.py:58 msgid "Admin password" @@ -641,11 +647,11 @@ msgstr "hasło administratora" #: mailu/ui/forms.py:84 msgid "Enabled" -msgstr "" +msgstr "Włączone" #: mailu/ui/forms.py:89 msgid "Email address" -msgstr "" +msgstr "Adres e-mail" #: mailu/ui/forms.py:93 mailu/ui/templates/sidebar.html:117 #: mailu/ui/templates/user/signup.html:4 @@ -655,11 +661,11 @@ msgstr "" #: mailu/ui/forms.py:119 msgid "End of vacation" -msgstr "" +msgstr "Koniec wakacji" #: mailu/ui/templates/client.html:4 mailu/ui/templates/sidebar.html:82 msgid "Client setup" -msgstr "" +msgstr "Konfiguracja klienta" #: mailu/ui/templates/client.html:16 mailu/ui/templates/client.html:43 msgid "Mail protocol" @@ -681,6 +687,8 @@ msgstr "Wygeneruj klucze" msgid "In order to register a new domain, you must first setup the\n" " domain zone so that the domain MX points to this server" msgstr "" +"Aby zarejestrować nową domenę, musisz najpierw skonfigurować strefę domeny, " +"aby domena MX wskazywała na ten serwer" #: mailu/ui/templates/domain/signup.html:18 msgid "If you do not know how to setup an MX record for your DNS zone,\n" @@ -688,10 +696,15 @@ msgid "If you do not know how to setup an MX record for your DNS zo " couple minutes after the MX is set so the local server cache\n" " expires." msgstr "" +"Jeśli nie wiesz, jak skonfigurować rekord MX dla swojej " +"strefy DNS,\n" +"skontaktuj się z dostawcą DNS lub administratorem. Proszę również poczekać\n" +"kilka minut po ustawieniu MX , żeby pamięć podręczna serwera " +"lokalnego wygasła." #: mailu/ui/templates/user/signup_domain.html:8 msgid "pick a domain for the new account" -msgstr "" +msgstr "wybierz domenę dla nowego konta" #: mailu/ui/templates/user/signup_domain.html:14 msgid "Domain" @@ -699,5 +712,4 @@ msgstr "Domena" #: mailu/ui/templates/user/signup_domain.html:15 msgid "Available slots" -msgstr "" - +msgstr "Dostępne miejsca" From b4d81f833dc2172c7049cfe9f77319c7134b1297 Mon Sep 17 00:00:00 2001 From: micw Date: Fri, 6 Mar 2020 15:55:35 +0100 Subject: [PATCH 17/30] Add pdo_pgsql to Dockerfile --- webmails/roundcube/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webmails/roundcube/Dockerfile b/webmails/roundcube/Dockerfile index 53c043d9..a3ee3d40 100644 --- a/webmails/roundcube/Dockerfile +++ b/webmails/roundcube/Dockerfile @@ -19,9 +19,9 @@ RUN pip3 install socrate ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1.3.10/roundcubemail-1.3.10-complete.tar.gz RUN apt-get update && apt-get install -y \ - zlib1g-dev libzip4 libzip-dev \ + zlib1g-dev libzip4 libzip-dev postgresql-dev \ python3-jinja2 \ - && docker-php-ext-install zip pdo_mysql \ + && docker-php-ext-install zip pdo_mysql pdo_pgsql \ && echo date.timezone=UTC > /usr/local/etc/php/conf.d/timezone.ini \ && rm -rf /var/www/html/ \ && cd /var/www \ From dfe092eb46da1a20f40b65fbc699e9620dba683c Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Fri, 6 Mar 2020 16:11:59 +0100 Subject: [PATCH 18/30] Use names for docker build stages in dovecot Dockerfile --- core/dovecot/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/dovecot/Dockerfile b/core/dovecot/Dockerfile index 60f6ab9b..1e40bddf 100644 --- a/core/dovecot/Dockerfile +++ b/core/dovecot/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.10 +ARG DISTRO=alpine:3.10 as builder FROM $DISTRO WORKDIR /tmp RUN apk add git build-base automake autoconf libtool dovecot-dev xapian-core-dev icu-dev @@ -27,7 +27,7 @@ RUN apk add --no-cache \ dovecot dovecot-lmtpd dovecot-pop3d dovecot-submissiond dovecot-pigeonhole-plugin rspamd-client xapian-core \ && mkdir /var/lib/dovecot -COPY --from=0 /usr/lib/dovecot/lib21_fts_xapian_plugin.* /usr/lib/dovecot/ +COPY --from=builder /usr/lib/dovecot/lib21_fts_xapian_plugin.* /usr/lib/dovecot/ COPY conf /conf COPY start.py /start.py From 94cfc31e04f2ac2f408b6f37b9a394f0b0793de1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2020 15:33:41 +0000 Subject: [PATCH 19/30] Bump validators from 0.12.5 to 0.12.6 in /core/admin Bumps [validators](https://github.com/kvesteri/validators) from 0.12.5 to 0.12.6. - [Release notes](https://github.com/kvesteri/validators/releases) - [Changelog](https://github.com/kvesteri/validators/blob/master/CHANGES.rst) - [Commits](https://github.com/kvesteri/validators/commits) Signed-off-by: dependabot[bot] --- core/admin/requirements-prod.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/admin/requirements-prod.txt b/core/admin/requirements-prod.txt index 1fcffbcd..e04089c4 100644 --- a/core/admin/requirements-prod.txt +++ b/core/admin/requirements-prod.txt @@ -44,7 +44,7 @@ SQLAlchemy==1.3.3 srslib==0.1.4 tabulate==0.8.3 tenacity==5.0.4 -validators==0.12.5 +validators==0.12.6 visitor==0.1.3 Werkzeug==0.15.3 WTForms==2.2.1 From 862632655960d79c4b70dc1e22d938fae191fda0 Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Sat, 7 Mar 2020 10:21:21 +0100 Subject: [PATCH 20/30] Fix dovecot dockerfile (accidentally broken in previous commit) --- core/dovecot/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/dovecot/Dockerfile b/core/dovecot/Dockerfile index 1e40bddf..da75140c 100644 --- a/core/dovecot/Dockerfile +++ b/core/dovecot/Dockerfile @@ -1,5 +1,5 @@ -ARG DISTRO=alpine:3.10 as builder -FROM $DISTRO +ARG DISTRO=alpine:3.10 +FROM $DISTRO as builder WORKDIR /tmp RUN apk add git build-base automake autoconf libtool dovecot-dev xapian-core-dev icu-dev RUN git clone https://github.com/grosjo/fts-xapian.git \ From dbcab06587c394cd44ca6a5059f98c636d4c5428 Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Sat, 7 Mar 2020 18:17:24 +0000 Subject: [PATCH 21/30] Ignore newlines and comment-lines in postfix overrides To make postfix override files understandable and readable, users may want to insert empty newlines and #-commented lines in their postfix override files too. This will now ignore such bogus-lines and not send them to `postconf`, which produced ugly errors in the past. closes #1098 --- core/postfix/start.py | 10 ++++++++-- towncrier/newsfragments/1098.misc | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 towncrier/newsfragments/1098.misc diff --git a/core/postfix/start.py b/core/postfix/start.py index 12aaa770..c32099bf 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -27,6 +27,10 @@ def start_podop(): ("senderlogin", "url", url + "sender/login/§") ]) +def is_valid_postconf_line(line): + return not line.startswith("#") \ + and not line == '' + # Actual startup script os.environ["FRONT_ADDRESS"] = system.get_host_address_from_environment("FRONT", "front") os.environ["ADMIN_ADDRESS"] = system.get_host_address_from_environment("ADMIN", "admin") @@ -38,11 +42,13 @@ for postfix_file in glob.glob("/conf/*.cf"): if os.path.exists("/overrides/postfix.cf"): for line in open("/overrides/postfix.cf").read().strip().split("\n"): - os.system('postconf -e "{}"'.format(line)) + if is_valid_postconf_line(line): + os.system('postconf -e "{}"'.format(line)) if os.path.exists("/overrides/postfix.master"): for line in open("/overrides/postfix.master").read().strip().split("\n"): - os.system('postconf -Me "{}"'.format(line)) + if is_valid_postconf_line(line): + os.system('postconf -Me "{}"'.format(line)) for map_file in glob.glob("/overrides/*.map"): destination = os.path.join("/etc/postfix", os.path.basename(map_file)) diff --git a/towncrier/newsfragments/1098.misc b/towncrier/newsfragments/1098.misc new file mode 100644 index 00000000..d99aa24d --- /dev/null +++ b/towncrier/newsfragments/1098.misc @@ -0,0 +1 @@ +Ignore newlines and comment-lines in postfix overrides - this means you can now make your override configfiles much more readable. From e08f4ab7a92aacc360a9dcf4a435d51f9ca7801e Mon Sep 17 00:00:00 2001 From: micw Date: Sat, 7 Mar 2020 19:38:12 +0100 Subject: [PATCH 22/30] Fix wrong psql lib name --- webmails/roundcube/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webmails/roundcube/Dockerfile b/webmails/roundcube/Dockerfile index a3ee3d40..55de8b93 100644 --- a/webmails/roundcube/Dockerfile +++ b/webmails/roundcube/Dockerfile @@ -19,7 +19,7 @@ RUN pip3 install socrate ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1.3.10/roundcubemail-1.3.10-complete.tar.gz RUN apt-get update && apt-get install -y \ - zlib1g-dev libzip4 libzip-dev postgresql-dev \ + zlib1g-dev libzip4 libzip-dev libpq-dev \ python3-jinja2 \ && docker-php-ext-install zip pdo_mysql pdo_pgsql \ && echo date.timezone=UTC > /usr/local/etc/php/conf.d/timezone.ini \ From afec5f08fb1b7b1902ab9cd2f1134b011b7fedca Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Sat, 7 Mar 2020 18:54:04 +0000 Subject: [PATCH 23/30] Remove duplicate ports line closes #1079 --- docs/kubernetes/mailu/imap.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/kubernetes/mailu/imap.yaml b/docs/kubernetes/mailu/imap.yaml index c3a188c7..64e69930 100644 --- a/docs/kubernetes/mailu/imap.yaml +++ b/docs/kubernetes/mailu/imap.yaml @@ -67,7 +67,6 @@ spec: role: mail tier: backend ports: - ports: - name: imap-auth port: 2102 protocol: TCP From 23f21f8b9cadef63b55b969ad5b1eccc8dfe6141 Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Sat, 7 Mar 2020 18:58:57 +0000 Subject: [PATCH 24/30] Use pyyaml safe_load instead of load Since load in unsafe (ref: https://msg.pyyaml.org/load), switch the only occurrance of `yaml.load` that i could find to safe_load. closes #1085 --- core/admin/mailu/manage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/admin/mailu/manage.py b/core/admin/mailu/manage.py index 819fe410..62f214d3 100644 --- a/core/admin/mailu/manage.py +++ b/core/admin/mailu/manage.py @@ -177,7 +177,7 @@ def config_update(verbose=False, delete_objects=False): """sync configuration with data from YAML-formatted stdin""" import yaml import sys - new_config = yaml.load(sys.stdin) + new_config = yaml.safe_load(sys.stdin) # print new_config domains = new_config.get('domains', []) tracked_domains = set() From 313e98c1a2130f35c42554c8b1bc3bf3433903e5 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sun, 5 Jan 2020 14:00:00 +0100 Subject: [PATCH 25/30] fetchmail: print unhandled exceptions, but don't crash refs #1295 --- optional/fetchmail/fetchmail.py | 84 +++++++++++++++++---------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/optional/fetchmail/fetchmail.py b/optional/fetchmail/fetchmail.py index f3ee6a70..fb0c6f04 100755 --- a/optional/fetchmail/fetchmail.py +++ b/optional/fetchmail/fetchmail.py @@ -8,6 +8,7 @@ import subprocess import re import requests import sys +import traceback FETCHMAIL = """ @@ -45,47 +46,50 @@ def fetchmail(fetchmailrc): def run(debug): - fetches = requests.get("http://admin/internal/fetch").json() - smtphost, smtpport = extract_host_port(os.environ.get("HOST_SMTP", "smtp"), None) - if smtpport is None: - smtphostport = smtphost - else: - smtphostport = "%s/%d" % (smtphost, smtpport) - for fetch in fetches: - fetchmailrc = "" - options = "options antispam 501, 504, 550, 553, 554" - options += " sslmode wrapped" if fetch["tls"] else "" - options += " keep" if fetch["keep"] else " fetchall" - fetchmailrc += RC_LINE.format( - user_email=escape_rc_string(fetch["user_email"]), - protocol=fetch["protocol"], - host=escape_rc_string(fetch["host"]), - port=fetch["port"], - smtphost=smtphostport, - username=escape_rc_string(fetch["username"]), - password=escape_rc_string(fetch["password"]), - options=options - ) - if debug: - print(fetchmailrc) - try: - print(fetchmail(fetchmailrc)) - error_message = "" - except subprocess.CalledProcessError as error: - error_message = error.output.decode("utf8") - # No mail is not an error - if not error_message.startswith("fetchmail: No mail"): - print(error_message) - user_info = "for %s at %s" % (fetch["user_email"], fetch["host"]) - # Number of messages seen is not a error as well - if ("messages" in error_message and - "(seen " in error_message and - user_info in error_message): - print(error_message) - finally: - requests.post("http://admin/internal/fetch/{}".format(fetch["id"]), - json=error_message.split("\n")[0] + try: + fetches = requests.get("http://admin/internal/fetch").json() + smtphost, smtpport = extract_host_port(os.environ.get("HOST_SMTP", "smtp"), None) + if smtpport is None: + smtphostport = smtphost + else: + smtphostport = "%s/%d" % (smtphost, smtpport) + for fetch in fetches: + fetchmailrc = "" + options = "options antispam 501, 504, 550, 553, 554" + options += " sslmode wrapped" if fetch["tls"] else "" + options += " keep" if fetch["keep"] else " fetchall" + fetchmailrc += RC_LINE.format( + user_email=escape_rc_string(fetch["user_email"]), + protocol=fetch["protocol"], + host=escape_rc_string(fetch["host"]), + port=fetch["port"], + smtphost=smtphostport, + username=escape_rc_string(fetch["username"]), + password=escape_rc_string(fetch["password"]), + options=options ) + if debug: + print(fetchmailrc) + try: + print(fetchmail(fetchmailrc)) + error_message = "" + except subprocess.CalledProcessError as error: + error_message = error.output.decode("utf8") + # No mail is not an error + if not error_message.startswith("fetchmail: No mail"): + print(error_message) + user_info = "for %s at %s" % (fetch["user_email"], fetch["host"]) + # Number of messages seen is not a error as well + if ("messages" in error_message and + "(seen " in error_message and + user_info in error_message): + print(error_message) + finally: + requests.post("http://admin/internal/fetch/{}".format(fetch["id"]), + json=error_message.split("\n")[0] + ) + except Exception: + traceback.print_exc() if __name__ == "__main__": From 5e2d0b78b61f9077b2f6591dac1738c454ab66bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20S=C3=A4nger?= Date: Wed, 11 Mar 2020 13:26:32 +0100 Subject: [PATCH 26/30] Update Roundcube to 1.4.3 --- webmails/roundcube/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webmails/roundcube/Dockerfile b/webmails/roundcube/Dockerfile index d04a03b3..19f2933b 100644 --- a/webmails/roundcube/Dockerfile +++ b/webmails/roundcube/Dockerfile @@ -16,7 +16,7 @@ RUN apt-get update && apt-get install -y \ # Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, rainloop, roundcube RUN pip3 install socrate -ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1.4.2/roundcubemail-1.4.2-complete.tar.gz +ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1.4.3/roundcubemail-1.4.3-complete.tar.gz RUN apt-get update && apt-get install -y \ zlib1g-dev libzip4 libzip-dev \ From ca82380bcf62b25f1d2f3583d191619d1f5e97c3 Mon Sep 17 00:00:00 2001 From: Jae Beojkkoch Date: Wed, 11 Mar 2020 12:30:37 +0000 Subject: [PATCH 27/30] Translated using Weblate (English) Currently translated at 7.9% (13 of 163 strings) Translation: Mailu/admin Translate-URL: https://translate.tedomum.net/projects/mailu/admin/en/ --- .../mailu/translations/en/LC_MESSAGES/messages.po | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/admin/mailu/translations/en/LC_MESSAGES/messages.po b/core/admin/mailu/translations/en/LC_MESSAGES/messages.po index 87f411e0..2ada20b1 100644 --- a/core/admin/mailu/translations/en/LC_MESSAGES/messages.po +++ b/core/admin/mailu/translations/en/LC_MESSAGES/messages.po @@ -8,14 +8,16 @@ msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2018-04-22 12:10+0200\n" -"PO-Revision-Date: 2016-10-02 15:02+0200\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2020-03-11 23:03+0000\n" +"Last-Translator: Jae Beojkkoch \n" +"Language-Team: English \n" "Language: en\n" -"Language-Team: en \n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.11.2\n" "Generated-By: Babel 2.5.3\n" #: mailu/ui/forms.py:32 @@ -24,7 +26,7 @@ msgstr "" #: mailu/ui/forms.py:36 msgid "Confirm" -msgstr "" +msgstr "Confirm" #: mailu/ui/forms.py:40 mailu/ui/forms.py:77 msgid "E-mail" @@ -684,4 +686,3 @@ msgstr "" #~ msgid "General settings" #~ msgstr "" - From ecb8e07da2be2512709f16e68ed2fb77cef7c4ea Mon Sep 17 00:00:00 2001 From: Jaume Barber Date: Tue, 10 Mar 2020 09:02:19 +0000 Subject: [PATCH 28/30] Translated using Weblate (Spanish) Currently translated at 98.7% (161 of 163 strings) Translation: Mailu/admin Translate-URL: https://translate.tedomum.net/projects/mailu/admin/es/ --- .../translations/es/LC_MESSAGES/messages.po | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/core/admin/mailu/translations/es/LC_MESSAGES/messages.po b/core/admin/mailu/translations/es/LC_MESSAGES/messages.po index c5b0322c..94b39439 100644 --- a/core/admin/mailu/translations/es/LC_MESSAGES/messages.po +++ b/core/admin/mailu/translations/es/LC_MESSAGES/messages.po @@ -1,11 +1,16 @@ msgid "" msgstr "" +"Project-Id-Version: Mailu\n" +"PO-Revision-Date: 2020-03-11 23:03+0000\n" +"Last-Translator: Jaume Barber \n" +"Language-Team: Spanish \n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: POEditor.com\n" -"Project-Id-Version: Mailu\n" -"Language: es\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.11.2\n" #: mailu/ui/forms.py:32 msgid "Invalid email address." @@ -17,7 +22,7 @@ msgstr "Confirmar" #: mailu/ui/forms.py:40 mailu/ui/forms.py:77 msgid "E-mail" -msgstr "Dirección de Correo" +msgstr "Dirección de correo" #: mailu/ui/forms.py:41 mailu/ui/forms.py:78 mailu/ui/forms.py:90 #: mailu/ui/forms.py:109 mailu/ui/forms.py:162 @@ -34,7 +39,7 @@ msgstr "Entrar" #: mailu/ui/templates/domain/details.html:27 #: mailu/ui/templates/domain/list.html:18 mailu/ui/templates/relay/list.html:17 msgid "Domain name" -msgstr "Nmbre de dominio" +msgstr "Nombre de dominio" #: mailu/ui/forms.py:47 msgid "Maximum user count" @@ -42,7 +47,7 @@ msgstr "Máximo número de usuarios" #: mailu/ui/forms.py:48 msgid "Maximum alias count" -msgstr "Máximo número de aliases" +msgstr "Máximo número de alias" #: mailu/ui/forms.py:51 mailu/ui/forms.py:72 mailu/ui/forms.py:83 #: mailu/ui/forms.py:128 mailu/ui/forms.py:140 @@ -64,7 +69,7 @@ msgstr "Confirmar contraseña" #: mailu/ui/forms.py:80 mailu/ui/templates/user/list.html:22 #: mailu/ui/templates/user/signup_domain.html:16 msgid "Quota" -msgstr "Cuota" +msgstr "Espacio" #: mailu/ui/forms.py:81 msgid "Allow IMAP access" @@ -137,7 +142,7 @@ msgstr "Usar sintaxis SQL (p.ej. para abarcar todos los alias)" #: mailu/ui/forms.py:145 msgid "Admin email" -msgstr "Correo-e del administrador" +msgstr "Correo del administrador" #: mailu/ui/forms.py:146 mailu/ui/forms.py:151 mailu/ui/forms.py:164 msgid "Submit" @@ -145,7 +150,7 @@ msgstr "Enviar" #: mailu/ui/forms.py:150 msgid "Manager email" -msgstr "Gestor de correo" +msgstr "Correo del gestor" #: mailu/ui/forms.py:155 msgid "Protocol" @@ -175,7 +180,7 @@ msgstr "Confirmar acción" #: mailu/ui/templates/confirm.html:13 msgid "You are about to %(action)s. Please confirm your action." -msgstr "Está a punto de %(action)s. Por favor, confirme su acción. " +msgstr "Está a punto de %(action)s. Por favor, confirme su acción." #: mailu/ui/templates/docker-error.html:4 msgid "Docker error" @@ -264,7 +269,7 @@ msgstr "Ayuda" #: mailu/ui/templates/working.html:4 msgid "We are still working on this feature!" -msgstr "Aún trabajamos en esta característica!" +msgstr "Aún estamos trabajando en esta funcionalidad!" #: mailu/ui/templates/admin/create.html:4 msgid "Add a global administrator" @@ -276,7 +281,7 @@ msgstr "Administradores globales" #: mailu/ui/templates/admin/list.html:9 msgid "Add administrator" -msgstr "Añadr administrador" +msgstr "Añadir administrador" #: mailu/ui/templates/admin/list.html:16 mailu/ui/templates/alias/list.html:18 #: mailu/ui/templates/alternative/list.html:18 @@ -424,7 +429,7 @@ msgstr "Punto final" #: mailu/ui/templates/fetch/list.html:22 msgid "Last check" -msgstr "Último checkeo" +msgstr "Última comprobación" #: mailu/ui/templates/manager/create.html:4 msgid "Add a manager" @@ -516,7 +521,7 @@ msgstr "Mantener los correos" #: mailu/ui/templates/fetch/list.html:35 msgid "yes" -msgstr "si" +msgstr "sí" #: mailu/ui/templates/fetch/list.html:35 msgid "no" @@ -529,7 +534,7 @@ msgstr "Nombre alternativo" #. I assume relayed domain means the server is a relay server (hoy receive it from another machine), and relay domain means hoy silla send it todo another machine. Is it right, or opossite? #: mailu/ui/forms.py:70 msgid "Relayed domain name" -msgstr "Nombre de.dominio a recepcionar" +msgstr "Nombre de dominio a recepcionar" #: mailu/ui/forms.py:71 mailu/ui/templates/relay/list.html:18 msgid "Remote host" @@ -577,7 +582,7 @@ msgstr "Nuevo dominio externo (relayed)" #: mailu/ui/forms.py:125 msgid "Your token (write it down, as it will never be displayed again)" -msgstr "Sus llaves (token) (anótelas, ya que no se mostrarán nunca más)." +msgstr "Su token (anótelo, ya que no se mostrará nunca más)" #: mailu/ui/forms.py:130 mailu/ui/templates/token/list.html:20 msgid "Authorized IP" @@ -585,7 +590,7 @@ msgstr "IP autorizada" #: mailu/ui/templates/sidebar.html:31 mailu/ui/templates/token/list.html:4 msgid "Authentication tokens" -msgstr "Tokens (llaves) autorizados" +msgstr "Tokens de autenticación" #: mailu/ui/templates/sidebar.html:72 msgid "Go to" @@ -593,15 +598,15 @@ msgstr "Ir a" #: mailu/ui/templates/sidebar.html:76 msgid "Webmail" -msgstr "Webmail" +msgstr "Correo web" #: mailu/ui/templates/sidebar.html:87 msgid "Website" -msgstr "Web" +msgstr "Correo web" #: mailu/ui/templates/token/create.html:4 msgid "Create an authentication token" -msgstr "Crear un token (llave) de autenticación." +msgstr "Crear un token de autenticación" #: mailu/ui/templates/token/list.html:12 msgid "New token" @@ -700,4 +705,3 @@ msgstr "Dominio" #: mailu/ui/templates/user/signup_domain.html:15 msgid "Available slots" msgstr "Slots disponibles" - From aafcbadb2388f470a6aebcc60745ca7941e27587 Mon Sep 17 00:00:00 2001 From: Jaume Barber Date: Tue, 10 Mar 2020 11:52:59 +0000 Subject: [PATCH 29/30] Translated using Weblate (Italian) Currently translated at 98.7% (161 of 163 strings) Translation: Mailu/admin Translate-URL: https://translate.tedomum.net/projects/mailu/admin/it/ --- .../translations/it/LC_MESSAGES/messages.po | 106 ++++++++++-------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/core/admin/mailu/translations/it/LC_MESSAGES/messages.po b/core/admin/mailu/translations/it/LC_MESSAGES/messages.po index 4e07a794..9ef5ac84 100644 --- a/core/admin/mailu/translations/it/LC_MESSAGES/messages.po +++ b/core/admin/mailu/translations/it/LC_MESSAGES/messages.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: Mailu\n" -"PO-Revision-Date: 2019-07-22 06:23+0000\n" -"Last-Translator: kaiyou \n" +"PO-Revision-Date: 2020-03-11 23:03+0000\n" +"Last-Translator: Jaume Barber \n" "Language-Team: Italian \n" "Language: it\n" @@ -10,7 +10,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.3\n" +"X-Generator: Weblate 3.11.2\n" #: mailu/ui/forms.py:32 msgid "Invalid email address." @@ -43,11 +43,11 @@ msgstr "Nome dominio" #: mailu/ui/forms.py:47 msgid "Maximum user count" -msgstr "" +msgstr "Numero massimo utenti" #: mailu/ui/forms.py:48 msgid "Maximum alias count" -msgstr "" +msgstr "Numero massimo alias" #: mailu/ui/forms.py:51 mailu/ui/forms.py:72 mailu/ui/forms.py:83 #: mailu/ui/forms.py:128 mailu/ui/forms.py:140 @@ -55,7 +55,7 @@ msgstr "" #: mailu/ui/templates/relay/list.html:19 mailu/ui/templates/token/list.html:19 #: mailu/ui/templates/user/list.html:23 msgid "Comment" -msgstr "" +msgstr "Commento" #: mailu/ui/forms.py:52 mailu/ui/forms.py:61 mailu/ui/forms.py:66 #: mailu/ui/forms.py:73 mailu/ui/forms.py:132 mailu/ui/forms.py:141 @@ -101,7 +101,7 @@ msgstr "Salva impostazioni" #: mailu/ui/forms.py:110 msgid "Password check" -msgstr "" +msgstr "Verifica la password" #: mailu/ui/forms.py:111 mailu/ui/templates/sidebar.html:16 msgid "Update password" @@ -172,7 +172,7 @@ msgstr "Abilita TLS" #: mailu/ui/forms.py:161 mailu/ui/templates/client.html:28 #: mailu/ui/templates/client.html:55 mailu/ui/templates/fetch/list.html:20 msgid "Username" -msgstr "Username" +msgstr "Nome utente" #: mailu/ui/templates/confirm.html:4 msgid "Confirm action" @@ -188,7 +188,7 @@ msgstr "Errore Docker" #: mailu/ui/templates/docker-error.html:12 msgid "An error occurred while talking to the Docker server." -msgstr "" +msgstr "C'è stato un errore nel tentativo di connessione con il Docker server." #: mailu/admin/templates/login.html:6 msgid "Your account" @@ -196,15 +196,15 @@ msgstr "Il tuo account" #: mailu/ui/templates/login.html:8 msgid "to access the administration tools" -msgstr "" +msgstr "per accedere le impostazioni d'admin" #: mailu/ui/templates/services.html:4 mailu/ui/templates/sidebar.html:39 msgid "Services status" -msgstr "" +msgstr "Status dei servizi" #: mailu/ui/templates/services.html:10 msgid "Service" -msgstr "" +msgstr "Servizio" #: mailu/ui/templates/fetch/list.html:23 mailu/ui/templates/services.html:11 msgid "Status" @@ -245,7 +245,7 @@ msgstr "Auto-risponditore" #: mailu/ui/templates/fetch/list.html:4 mailu/ui/templates/sidebar.html:26 #: mailu/ui/templates/user/list.html:36 msgid "Fetched accounts" -msgstr "" +msgstr "Conti ricuperati" #: mailu/ui/templates/sidebar.html:105 msgid "Sign out" @@ -357,23 +357,23 @@ msgstr "Rigenera chiavi" #: mailu/ui/templates/domain/details.html:31 msgid "DNS MX entry" -msgstr "" +msgstr "Entrate DNS MX" #: mailu/ui/templates/domain/details.html:35 msgid "DNS SPF entries" -msgstr "" +msgstr "Entrate DNS SPF" #: mailu/ui/templates/domain/details.html:42 msgid "DKIM public key" -msgstr "" +msgstr "Chiave pubblica DKIM" #: mailu/ui/templates/domain/details.html:46 msgid "DNS DKIM entry" -msgstr "" +msgstr "Entrata DNS DKIM" #: mailu/ui/templates/domain/details.html:50 msgid "DNS DMARC entry" -msgstr "" +msgstr "Entrata DNS DMARC" #: mailu/ui/templates/domain/edit.html:4 msgid "Edit domain" @@ -413,11 +413,11 @@ msgstr "Manager" #: mailu/ui/templates/fetch/create.html:4 msgid "Add a fetched account" -msgstr "" +msgstr "Aggiungere un conto ricuperato" #: mailu/ui/templates/fetch/edit.html:4 msgid "Update a fetched account" -msgstr "" +msgstr "Aggiornare un conto ricuperato" #: mailu/ui/templates/fetch/list.html:12 msgid "Add an account" @@ -425,7 +425,7 @@ msgstr "Aggiungi un account" #: mailu/ui/templates/fetch/list.html:19 msgid "Endpoint" -msgstr "" +msgstr "Endpoint" #: mailu/ui/templates/fetch/list.html:22 msgid "Last check" @@ -437,7 +437,7 @@ msgstr "Aggiungi un manager" #: mailu/ui/templates/manager/list.html:4 msgid "Manager list" -msgstr "" +msgstr "Elenco di Manager" #: mailu/ui/templates/manager/list.html:12 msgid "Add manager" @@ -445,7 +445,7 @@ msgstr "Aggiungi manager" #: mailu/ui/forms.py:168 msgid "Announcement subject" -msgstr "" +msgstr "Oggetto dell'avviso" #: mailu/ui/forms.py:170 msgid "Announcement body" @@ -505,7 +505,7 @@ msgstr "Risposta automatica" #: mailu/ui/forms.py:49 msgid "Maximum user quota" -msgstr "" +msgstr "Spazio massimo per utente" #: mailu/ui/forms.py:101 msgid "Keep a copy of the emails" @@ -533,7 +533,7 @@ msgstr "Nome alternativo" #: mailu/ui/forms.py:70 msgid "Relayed domain name" -msgstr "" +msgstr "Nome di dominio affidato" #: mailu/ui/forms.py:71 mailu/ui/templates/relay/list.html:18 msgid "Remote host" @@ -541,15 +541,15 @@ msgstr "Host remoto" #: mailu/ui/templates/sidebar.html:54 msgid "Relayed domains" -msgstr "" +msgstr "Domini affidati" #: mailu/ui/templates/alternative/create.html:4 msgid "Create alternative domain" -msgstr "" +msgstr "Creare un dominio alternativo" #: mailu/ui/templates/alternative/list.html:4 msgid "Alternative domain list" -msgstr "" +msgstr "Elenco di domini alternativi" #: mailu/ui/templates/alternative/list.html:12 msgid "Add alternative" @@ -565,23 +565,23 @@ msgstr "Alternative" #: mailu/ui/templates/relay/create.html:4 msgid "New relay domain" -msgstr "" +msgstr "Nuovo dominio affidato" #: mailu/ui/templates/relay/edit.html:4 msgid "Edit relayd domain" -msgstr "" +msgstr "Editare dominio affidato" #: mailu/ui/templates/relay/list.html:4 msgid "Relayed domain list" -msgstr "" +msgstr "Elenco di domini affidati" #: mailu/ui/templates/relay/list.html:9 msgid "New relayed domain" -msgstr "" +msgstr "Nuovo dominio affidato" #: mailu/ui/forms.py:125 msgid "Your token (write it down, as it will never be displayed again)" -msgstr "" +msgstr "Il suo token (dovrà annotarlo, dato che non verrà più mostrato)" #: mailu/ui/forms.py:130 mailu/ui/templates/token/list.html:20 msgid "Authorized IP" @@ -633,58 +633,61 @@ msgstr "Tolleranza filtro spam" #: mailu/ui/forms.py:50 msgid "Enable sign-up" -msgstr "" +msgstr "Avviare l'iscrizione" #: mailu/ui/forms.py:57 msgid "Initial admin" -msgstr "" +msgstr "Admin iniziale" #: mailu/ui/forms.py:58 msgid "Admin password" -msgstr "" +msgstr "Admin password" #: mailu/ui/forms.py:84 msgid "Enabled" -msgstr "" +msgstr "Attivo" #: mailu/ui/forms.py:89 msgid "Email address" -msgstr "" +msgstr "Indirizzo email" #: mailu/ui/forms.py:93 mailu/ui/templates/sidebar.html:117 #: mailu/ui/templates/user/signup.html:4 #: mailu/ui/templates/user/signup_domain.html:4 msgid "Sign up" -msgstr "" +msgstr "Iscriversi" #: mailu/ui/forms.py:119 msgid "End of vacation" -msgstr "" +msgstr "Rientro delle vacanze" #: mailu/ui/templates/client.html:4 mailu/ui/templates/sidebar.html:82 msgid "Client setup" -msgstr "" +msgstr "Impostazioni del cliente" #: mailu/ui/templates/client.html:16 mailu/ui/templates/client.html:43 msgid "Mail protocol" -msgstr "" +msgstr "Protocolo mail" #: mailu/ui/templates/client.html:24 mailu/ui/templates/client.html:51 msgid "Server name" -msgstr "" +msgstr "Nome del server" #: mailu/ui/templates/domain/signup.html:4 mailu/ui/templates/sidebar.html:98 msgid "Register a domain" -msgstr "" +msgstr "Registrare un dominio" #: mailu/ui/templates/domain/details.html:17 msgid "Generate keys" -msgstr "" +msgstr "Generare chiavi" #: mailu/ui/templates/domain/signup.html:13 msgid "In order to register a new domain, you must first setup the\n" " domain zone so that the domain MX points to this server" msgstr "" +"Per registrare un nuovo dominio, dovrà aggiustare prima la \n" +"domain zone affinché il vostro dominio MX punti contro il " +"server" #: mailu/ui/templates/domain/signup.html:18 msgid "If you do not know how to setup an MX record for your DNS zone,\n" @@ -692,15 +695,22 @@ msgid "If you do not know how to setup an MX record for your DNS zo " couple minutes after the MX is set so the local server cache\n" " expires." msgstr "" +"Se non sa come aggiungere un registro MX alla vostra zona DNS, " +"\n" +"dovrà contattare il suo provider o amministratore DNS. Prego, attenda " +"qualche\n" +"minuto tra l'impostazione del registro MX finché la cache " +"locale \n" +"del server scaderà." #: mailu/ui/templates/user/signup_domain.html:8 msgid "pick a domain for the new account" -msgstr "" +msgstr "scelga un dominio per il nuovo conto" #: mailu/ui/templates/user/signup_domain.html:14 msgid "Domain" -msgstr "" +msgstr "Dominio" #: mailu/ui/templates/user/signup_domain.html:15 msgid "Available slots" -msgstr "" +msgstr "Slot disponibili" From a2fa52170c09e970a29b89de9ca066c91af21ab8 Mon Sep 17 00:00:00 2001 From: Jaume Barber Date: Mon, 9 Mar 2020 19:44:08 +0000 Subject: [PATCH 30/30] Translated using Weblate (Catalan) Currently translated at 98.6% (149 of 151 strings) Translation: Mailu/admin Translate-URL: https://translate.tedomum.net/projects/mailu/admin/ca/ --- .../translations/ca/LC_MESSAGES/messages.po | 314 +++++++++--------- 1 file changed, 163 insertions(+), 151 deletions(-) diff --git a/core/admin/mailu/translations/ca/LC_MESSAGES/messages.po b/core/admin/mailu/translations/ca/LC_MESSAGES/messages.po index 01ba188a..a14d7faf 100644 --- a/core/admin/mailu/translations/ca/LC_MESSAGES/messages.po +++ b/core/admin/mailu/translations/ca/LC_MESSAGES/messages.po @@ -8,13 +8,16 @@ msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2018-04-22 12:10+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" +"PO-Revision-Date: 2020-03-11 23:03+0000\n" +"Last-Translator: Jaume Barber \n" +"Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.11.2\n" "Generated-By: Babel 2.5.3\n" #: mailu/ui/forms.py:32 @@ -22,45 +25,46 @@ msgid "Invalid email address." msgstr "" #: mailu/ui/forms.py:36 +#, fuzzy msgid "Confirm" -msgstr "" +msgstr "Confirmeu" #: mailu/ui/forms.py:40 mailu/ui/forms.py:77 msgid "E-mail" -msgstr "" +msgstr "Correu" #: mailu/ui/forms.py:41 mailu/ui/forms.py:78 mailu/ui/forms.py:90 #: mailu/ui/forms.py:109 mailu/ui/forms.py:162 #: mailu/ui/templates/client.html:32 mailu/ui/templates/client.html:59 msgid "Password" -msgstr "" +msgstr "Contrasenya" #: mailu/ui/forms.py:42 mailu/ui/templates/login.html:4 #: mailu/ui/templates/sidebar.html:111 msgid "Sign in" -msgstr "" +msgstr "Entreu" #: mailu/ui/forms.py:46 mailu/ui/forms.py:56 #: mailu/ui/templates/domain/details.html:27 #: mailu/ui/templates/domain/list.html:18 mailu/ui/templates/relay/list.html:17 msgid "Domain name" -msgstr "" +msgstr "Nom de domini" #: mailu/ui/forms.py:47 msgid "Maximum user count" -msgstr "" +msgstr "Nombre màxim d'usuaris" #: mailu/ui/forms.py:48 msgid "Maximum alias count" -msgstr "" +msgstr "Nombre màxim d'àlies" #: mailu/ui/forms.py:49 msgid "Maximum user quota" -msgstr "" +msgstr "Espai màxim per usuari" #: mailu/ui/forms.py:50 msgid "Enable sign-up" -msgstr "" +msgstr "Activeu el registre" #: mailu/ui/forms.py:51 mailu/ui/forms.py:72 mailu/ui/forms.py:83 #: mailu/ui/forms.py:128 mailu/ui/forms.py:140 @@ -68,308 +72,308 @@ msgstr "" #: mailu/ui/templates/relay/list.html:19 mailu/ui/templates/token/list.html:19 #: mailu/ui/templates/user/list.html:23 msgid "Comment" -msgstr "" +msgstr "Comentari" #: mailu/ui/forms.py:52 mailu/ui/forms.py:61 mailu/ui/forms.py:66 #: mailu/ui/forms.py:73 mailu/ui/forms.py:132 mailu/ui/forms.py:141 msgid "Create" -msgstr "" +msgstr "Creeu" #: mailu/ui/forms.py:57 msgid "Initial admin" -msgstr "" +msgstr "Admin inicial" #: mailu/ui/forms.py:58 msgid "Admin password" -msgstr "" +msgstr "Contrasenya d'admin" #: mailu/ui/forms.py:59 mailu/ui/forms.py:79 mailu/ui/forms.py:91 msgid "Confirm password" -msgstr "" +msgstr "Confirmeu la contrasenya" #: mailu/ui/forms.py:65 msgid "Alternative name" -msgstr "" +msgstr "Nom alternatiu" #: mailu/ui/forms.py:70 msgid "Relayed domain name" -msgstr "" +msgstr "Nom de domini traspassat (relayed)" #: mailu/ui/forms.py:71 mailu/ui/templates/relay/list.html:18 msgid "Remote host" -msgstr "" +msgstr "Amfitrió remot" #: mailu/ui/forms.py:80 mailu/ui/templates/user/list.html:22 #: mailu/ui/templates/user/signup_domain.html:16 msgid "Quota" -msgstr "" +msgstr "Espai" #: mailu/ui/forms.py:81 msgid "Allow IMAP access" -msgstr "" +msgstr "Permeteu accés IMAP" #: mailu/ui/forms.py:82 msgid "Allow POP3 access" -msgstr "" +msgstr "Permeteu accés POP3" #: mailu/ui/forms.py:84 msgid "Enabled" -msgstr "" +msgstr "Activat" #: mailu/ui/forms.py:85 msgid "Save" -msgstr "" +msgstr "Desa" #: mailu/ui/forms.py:89 msgid "Email address" -msgstr "" +msgstr "Adreça email" #: mailu/ui/forms.py:93 mailu/ui/templates/sidebar.html:117 #: mailu/ui/templates/user/signup.html:4 #: mailu/ui/templates/user/signup_domain.html:4 msgid "Sign up" -msgstr "" +msgstr "Registreu-vos" #: mailu/ui/forms.py:97 msgid "Displayed name" -msgstr "" +msgstr "Nom per mostrar" #: mailu/ui/forms.py:98 msgid "Enable spam filter" -msgstr "" +msgstr "Activeu filtre d'spam" #: mailu/ui/forms.py:99 msgid "Spam filter tolerance" -msgstr "" +msgstr "Tolerància del filtre spam" #: mailu/ui/forms.py:100 msgid "Enable forwarding" -msgstr "" +msgstr "Activeu el reenviament" #: mailu/ui/forms.py:101 msgid "Keep a copy of the emails" -msgstr "" +msgstr "Mantigueu una còpia dels correus" #: mailu/ui/forms.py:103 mailu/ui/forms.py:139 #: mailu/ui/templates/alias/list.html:20 msgid "Destination" -msgstr "" +msgstr "Destinació" #: mailu/ui/forms.py:105 msgid "Save settings" -msgstr "" +msgstr "Desa ajustos" #: mailu/ui/forms.py:110 msgid "Password check" -msgstr "" +msgstr "Comproveu la contrasenya" #: mailu/ui/forms.py:111 mailu/ui/templates/sidebar.html:16 msgid "Update password" -msgstr "" +msgstr "Canvieu la contrasenya" #: mailu/ui/forms.py:115 msgid "Enable automatic reply" -msgstr "" +msgstr "Activeu la resposta automàtica" #: mailu/ui/forms.py:116 msgid "Reply subject" -msgstr "" +msgstr "Tema de la resposta" #: mailu/ui/forms.py:117 msgid "Reply body" -msgstr "" +msgstr "Missatge de resposta" #: mailu/ui/forms.py:119 msgid "End of vacation" -msgstr "" +msgstr "Tornada de vacances" #: mailu/ui/forms.py:120 msgid "Update" -msgstr "" +msgstr "Actualitzeu" #: mailu/ui/forms.py:125 msgid "Your token (write it down, as it will never be displayed again)" -msgstr "" +msgstr "Token personal (apunteu-lo perquè no es mostrarà de nou)" #: mailu/ui/forms.py:130 mailu/ui/templates/token/list.html:20 msgid "Authorized IP" -msgstr "" +msgstr "IP autoritzada" #: mailu/ui/forms.py:136 msgid "Alias" -msgstr "" +msgstr "Àlies" #: mailu/ui/forms.py:138 msgid "Use SQL LIKE Syntax (e.g. for catch-all aliases)" -msgstr "" +msgstr "Feu servir sintaxi tipus SQL (ex. per a agafar tots els àlies)" #: mailu/ui/forms.py:145 msgid "Admin email" -msgstr "" +msgstr "Adreça d'admin" #: mailu/ui/forms.py:146 mailu/ui/forms.py:151 mailu/ui/forms.py:164 msgid "Submit" -msgstr "" +msgstr "Envia" #: mailu/ui/forms.py:150 msgid "Manager email" -msgstr "" +msgstr "Adreça de gestor" #: mailu/ui/forms.py:155 msgid "Protocol" -msgstr "" +msgstr "Protocol" #: mailu/ui/forms.py:158 msgid "Hostname or IP" -msgstr "" +msgstr "Nom d'amfitrio o IP" #: mailu/ui/forms.py:159 mailu/ui/templates/client.html:20 #: mailu/ui/templates/client.html:47 msgid "TCP port" -msgstr "" +msgstr "Port TCP" #: mailu/ui/forms.py:160 msgid "Enable TLS" -msgstr "" +msgstr "Activeu TLS" #: mailu/ui/forms.py:161 mailu/ui/templates/client.html:28 #: mailu/ui/templates/client.html:55 mailu/ui/templates/fetch/list.html:20 msgid "Username" -msgstr "" +msgstr "Nom d'usuari" #: mailu/ui/forms.py:163 msgid "Keep emails on the server" -msgstr "" +msgstr "Mantén els correus al servidor" #: mailu/ui/forms.py:168 msgid "Announcement subject" -msgstr "" +msgstr "Tema de l'avís" #: mailu/ui/forms.py:170 msgid "Announcement body" -msgstr "" +msgstr "Missatge de l'avís" #: mailu/ui/forms.py:172 msgid "Send" -msgstr "" +msgstr "Envia" #: mailu/ui/templates/announcement.html:4 msgid "Public announcement" -msgstr "" +msgstr "Avís públic" #: mailu/ui/templates/client.html:4 mailu/ui/templates/sidebar.html:82 msgid "Client setup" -msgstr "" +msgstr "Ajustos del client" #: mailu/ui/templates/client.html:16 mailu/ui/templates/client.html:43 msgid "Mail protocol" -msgstr "" +msgstr "Protocol de correu" #: mailu/ui/templates/client.html:24 mailu/ui/templates/client.html:51 msgid "Server name" -msgstr "" +msgstr "Nom de servidor" #: mailu/ui/templates/confirm.html:4 msgid "Confirm action" -msgstr "" +msgstr "Confirmeu acció" #: mailu/ui/templates/confirm.html:13 #, python-format msgid "You are about to %(action)s. Please confirm your action." -msgstr "" +msgstr "Esteu a punt de %(action)s. Per favor, confirmeu l'acció." #: mailu/ui/templates/docker-error.html:4 msgid "Docker error" -msgstr "" +msgstr "Error de Docker" #: mailu/ui/templates/docker-error.html:12 msgid "An error occurred while talking to the Docker server." -msgstr "" +msgstr "Hi ha hagut un error de comunicació amb el servidor Docker." #: mailu/ui/templates/login.html:8 msgid "to access the administration tools" -msgstr "" +msgstr "per accedir a les eines d'administració" #: mailu/ui/templates/sidebar.html:11 mailu/ui/templates/user/list.html:34 msgid "Settings" -msgstr "" +msgstr "Ajustos" #: mailu/ui/templates/sidebar.html:21 mailu/ui/templates/user/list.html:35 msgid "Auto-reply" -msgstr "" +msgstr "Resposta automàtica" #: mailu/ui/templates/fetch/list.html:4 mailu/ui/templates/sidebar.html:26 #: mailu/ui/templates/user/list.html:36 msgid "Fetched accounts" -msgstr "" +msgstr "Comptes trobats" #: mailu/ui/templates/sidebar.html:31 mailu/ui/templates/token/list.html:4 msgid "Authentication tokens" -msgstr "" +msgstr "Tokens d'autenticació" #: mailu/ui/templates/sidebar.html:35 msgid "Administration" -msgstr "" +msgstr "Administració" #: mailu/ui/templates/sidebar.html:44 msgid "Announcement" -msgstr "" +msgstr "Avís" #: mailu/ui/templates/sidebar.html:49 msgid "Administrators" -msgstr "" +msgstr "Administradors" #: mailu/ui/templates/sidebar.html:54 msgid "Relayed domains" -msgstr "" +msgstr "Dominis tramesos" #: mailu/ui/templates/sidebar.html:59 mailu/ui/templates/user/settings.html:15 msgid "Antispam" -msgstr "" +msgstr "Antispam" #: mailu/ui/templates/sidebar.html:66 msgid "Mail domains" -msgstr "" +msgstr "Dominis de correu" #: mailu/ui/templates/sidebar.html:72 msgid "Go to" -msgstr "" +msgstr "Aneu a" #: mailu/ui/templates/sidebar.html:76 msgid "Webmail" -msgstr "" +msgstr "Correu web" #: mailu/ui/templates/sidebar.html:87 msgid "Website" -msgstr "" +msgstr "Lloc web" #: mailu/ui/templates/sidebar.html:92 msgid "Help" -msgstr "" +msgstr "Ajuda" #: mailu/ui/templates/domain/signup.html:4 mailu/ui/templates/sidebar.html:98 msgid "Register a domain" -msgstr "" +msgstr "Registreu un domini" #: mailu/ui/templates/sidebar.html:105 msgid "Sign out" -msgstr "" +msgstr "Eixiu" #: mailu/ui/templates/working.html:4 msgid "We are still working on this feature!" -msgstr "" +msgstr "Encara estem treballant en aquesta funcionalitat!" #: mailu/ui/templates/admin/create.html:4 msgid "Add a global administrator" -msgstr "" +msgstr "Afegiu un admin global" #: mailu/ui/templates/admin/list.html:4 msgid "Global administrators" -msgstr "" +msgstr "Administradors globals" #: mailu/ui/templates/admin/list.html:9 msgid "Add administrator" -msgstr "" +msgstr "Afegiu un administrador" #: mailu/ui/templates/admin/list.html:16 mailu/ui/templates/alias/list.html:18 #: mailu/ui/templates/alternative/list.html:18 @@ -378,12 +382,12 @@ msgstr "" #: mailu/ui/templates/relay/list.html:16 mailu/ui/templates/token/list.html:18 #: mailu/ui/templates/user/list.html:18 msgid "Actions" -msgstr "" +msgstr "Accions" #: mailu/ui/templates/admin/list.html:17 mailu/ui/templates/alias/list.html:19 #: mailu/ui/templates/manager/list.html:19 mailu/ui/templates/user/list.html:20 msgid "Email" -msgstr "" +msgstr "Correu" #: mailu/ui/templates/admin/list.html:22 mailu/ui/templates/alias/list.html:29 #: mailu/ui/templates/alternative/list.html:25 @@ -392,23 +396,23 @@ msgstr "" #: mailu/ui/templates/relay/list.html:27 mailu/ui/templates/token/list.html:26 #: mailu/ui/templates/user/list.html:31 msgid "Delete" -msgstr "" +msgstr "Esborra" #: mailu/ui/templates/alias/create.html:4 msgid "Create alias" -msgstr "" +msgstr "Creeu àlies" #: mailu/ui/templates/alias/edit.html:4 msgid "Edit alias" -msgstr "" +msgstr "Editeu àlies" #: mailu/ui/templates/alias/list.html:4 msgid "Alias list" -msgstr "" +msgstr "Llista d'àlies" #: mailu/ui/templates/alias/list.html:12 msgid "Add alias" -msgstr "" +msgstr "Afegiu àlies" #: mailu/ui/templates/alias/list.html:22 #: mailu/ui/templates/alternative/list.html:20 @@ -416,118 +420,121 @@ msgstr "" #: mailu/ui/templates/relay/list.html:20 mailu/ui/templates/token/list.html:21 #: mailu/ui/templates/user/list.html:24 msgid "Created" -msgstr "" +msgstr "Creat" #: mailu/ui/templates/alias/list.html:23 mailu/ui/templates/domain/list.html:23 #: mailu/ui/templates/fetch/list.html:25 mailu/ui/templates/relay/list.html:21 #: mailu/ui/templates/user/list.html:25 msgid "Last edit" -msgstr "" +msgstr "Última edició" #: mailu/ui/templates/alias/list.html:28 mailu/ui/templates/domain/list.html:30 #: mailu/ui/templates/fetch/list.html:30 mailu/ui/templates/relay/list.html:26 #: mailu/ui/templates/user/list.html:30 msgid "Edit" -msgstr "" +msgstr "Edita" #: mailu/ui/templates/alternative/create.html:4 msgid "Create alternative domain" -msgstr "" +msgstr "Creeu domini alternatiu" #: mailu/ui/templates/alternative/list.html:4 msgid "Alternative domain list" -msgstr "" +msgstr "Llista de dominis alternatius" #: mailu/ui/templates/alternative/list.html:12 msgid "Add alternative" -msgstr "" +msgstr "Afegiu alternativa" #: mailu/ui/templates/alternative/list.html:19 msgid "Name" -msgstr "" +msgstr "Nom" #: mailu/ui/templates/domain/create.html:4 #: mailu/ui/templates/domain/list.html:9 msgid "New domain" -msgstr "" +msgstr "Nou domini" #: mailu/ui/templates/domain/details.html:4 msgid "Domain details" -msgstr "" +msgstr "Detalls del domini" #: mailu/ui/templates/domain/details.html:15 msgid "Regenerate keys" -msgstr "" +msgstr "Regenereu les claus" #: mailu/ui/templates/domain/details.html:17 msgid "Generate keys" -msgstr "" +msgstr "Genereu claus" #: mailu/ui/templates/domain/details.html:31 msgid "DNS MX entry" -msgstr "" +msgstr "Entrada DNS MX" #: mailu/ui/templates/domain/details.html:35 msgid "DNS SPF entries" -msgstr "" +msgstr "Entrada DNS SPF" #: mailu/ui/templates/domain/details.html:42 msgid "DKIM public key" -msgstr "" +msgstr "Clau pública DKIM" #: mailu/ui/templates/domain/details.html:46 msgid "DNS DKIM entry" -msgstr "" +msgstr "Entrada DNS DKIM" #: mailu/ui/templates/domain/details.html:50 msgid "DNS DMARC entry" -msgstr "" +msgstr "Entrada DNS DMARC" #: mailu/ui/templates/domain/edit.html:4 msgid "Edit domain" -msgstr "" +msgstr "Edita domini" #: mailu/ui/templates/domain/list.html:4 msgid "Domain list" -msgstr "" +msgstr "Llista de dominis" #: mailu/ui/templates/domain/list.html:17 msgid "Manage" -msgstr "" +msgstr "Gestioneu" #: mailu/ui/templates/domain/list.html:19 msgid "Mailbox count" -msgstr "" +msgstr "Nombre de bústies" #: mailu/ui/templates/domain/list.html:20 msgid "Alias count" -msgstr "" +msgstr "Nombre d'àlies" #: mailu/ui/templates/domain/list.html:28 msgid "Details" -msgstr "" +msgstr "Detalls" #: mailu/ui/templates/domain/list.html:35 msgid "Users" -msgstr "" +msgstr "Usuaris" #: mailu/ui/templates/domain/list.html:36 msgid "Aliases" -msgstr "" +msgstr "Àlies" #: mailu/ui/templates/domain/list.html:37 msgid "Managers" -msgstr "" +msgstr "Gestors" #: mailu/ui/templates/domain/list.html:39 msgid "Alternatives" -msgstr "" +msgstr "Alternatives" #: mailu/ui/templates/domain/signup.html:13 msgid "" "In order to register a new domain, you must first setup the\n" " domain zone so that the domain MX points to this server" msgstr "" +"Per a registrar un nou domini, heu de configurar la \n" +" zona de dominis per tal que el domini MX apunte a aquest " +"servidor" #: mailu/ui/templates/domain/signup.html:18 msgid "" @@ -539,131 +546,136 @@ msgid "" "cache\n" " expires." msgstr "" +"Si no sabeu configurar un registre MX a la zona DNS,\n" +"contacteu el vostre proveïdor o administrador de DNS. Per favor, espereu \n" +"uns quants minuts despres d'ajustar el registre MX perquè la " +"caixet \n" +"del servidor local expire." #: mailu/ui/templates/fetch/create.html:4 msgid "Add a fetched account" -msgstr "" +msgstr "Afegiu un compte (fetched)" #: mailu/ui/templates/fetch/edit.html:4 msgid "Update a fetched account" -msgstr "" +msgstr "Actualitzeu un compte (fetched)" #: mailu/ui/templates/fetch/list.html:12 msgid "Add an account" -msgstr "" +msgstr "Afegiu un compte" #: mailu/ui/templates/fetch/list.html:19 msgid "Endpoint" -msgstr "" +msgstr "Endpoint" #: mailu/ui/templates/fetch/list.html:21 msgid "Keep emails" -msgstr "" +msgstr "Mantingueu els correus" #: mailu/ui/templates/fetch/list.html:22 msgid "Last check" -msgstr "" +msgstr "Última comprovació" #: mailu/ui/templates/fetch/list.html:35 msgid "yes" -msgstr "" +msgstr "sí" #: mailu/ui/templates/fetch/list.html:35 msgid "no" -msgstr "" +msgstr "no" #: mailu/ui/templates/manager/create.html:4 msgid "Add a manager" -msgstr "" +msgstr "Afegiu un gestor" #: mailu/ui/templates/manager/list.html:4 msgid "Manager list" -msgstr "" +msgstr "Llista de gestors" #: mailu/ui/templates/manager/list.html:12 msgid "Add manager" -msgstr "" +msgstr "Afegiu un gestor" #: mailu/ui/templates/relay/create.html:4 msgid "New relay domain" -msgstr "" +msgstr "Nou domini traspassat (relayed)" #: mailu/ui/templates/relay/edit.html:4 msgid "Edit relayd domain" -msgstr "" +msgstr "Editeu domini traspassat (relayed)" #: mailu/ui/templates/relay/list.html:4 msgid "Relayed domain list" -msgstr "" +msgstr "Llista de dominis traspassats (relayed)" #: mailu/ui/templates/relay/list.html:9 msgid "New relayed domain" -msgstr "" +msgstr "Nou domini traspassat (relayed)" #: mailu/ui/templates/token/create.html:4 msgid "Create an authentication token" -msgstr "" +msgstr "Creeu un token d'autenticació" #: mailu/ui/templates/token/list.html:12 msgid "New token" -msgstr "" +msgstr "Nou token" #: mailu/ui/templates/user/create.html:4 msgid "New user" -msgstr "" +msgstr "Nou usuari" #: mailu/ui/templates/user/create.html:15 msgid "General" -msgstr "" +msgstr "General" #: mailu/ui/templates/user/create.html:22 msgid "Features and quotas" -msgstr "" +msgstr "Funcions i espai" #: mailu/ui/templates/user/edit.html:4 msgid "Edit user" -msgstr "" +msgstr "Edita usuari" #: mailu/ui/templates/user/forward.html:4 msgid "Forward emails" -msgstr "" +msgstr "Reenvia correus" #: mailu/ui/templates/user/list.html:4 msgid "User list" -msgstr "" +msgstr "Llista d'usuaris" #: mailu/ui/templates/user/list.html:12 msgid "Add user" -msgstr "" +msgstr "Afegiu usuari" #: mailu/ui/templates/user/list.html:19 mailu/ui/templates/user/settings.html:4 msgid "User settings" -msgstr "" +msgstr "Ajustos d'usuari" #: mailu/ui/templates/user/list.html:21 msgid "Features" -msgstr "" +msgstr "Funcions" #: mailu/ui/templates/user/password.html:4 msgid "Password update" -msgstr "" +msgstr "Canvieu la contrasenya" #: mailu/ui/templates/user/reply.html:4 msgid "Automatic reply" -msgstr "" +msgstr "Resposta automàtica" #: mailu/ui/templates/user/settings.html:22 msgid "Auto-forward" -msgstr "" +msgstr "Auto-reenviament" #: mailu/ui/templates/user/signup_domain.html:8 msgid "pick a domain for the new account" -msgstr "" +msgstr "tria un domini per al compte nou" #: mailu/ui/templates/user/signup_domain.html:14 msgid "Domain" -msgstr "" +msgstr "Domini" #: mailu/ui/templates/user/signup_domain.html:15 msgid "Available slots" -msgstr "" +msgstr "Ranures lliures"