@@ -14,7 +14,7 @@ jobs:
 | 
				
			|||||||
      pull-requests: write
 | 
					      pull-requests: write
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Mark/Close Stale Issues and Pull Requests 🗑️
 | 
					      - name: Mark/Close Stale Issues and Pull Requests 🗑️
 | 
				
			||||||
        uses: actions/stale@v7.0.0
 | 
					        uses: actions/stale@v8.0.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          repo-token: ${{ secrets.STALE_ACTION_PAT }}
 | 
					          repo-token: ${{ secrets.STALE_ACTION_PAT }}
 | 
				
			||||||
          days-before-stale: 60
 | 
					          days-before-stale: 60
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -380,7 +380,12 @@ class DockerUtils:
 | 
				
			|||||||
    if 'maildir' in request_json:
 | 
					    if 'maildir' in request_json:
 | 
				
			||||||
      for container in self.docker_client.containers.list(filters={"id": container_id}):
 | 
					      for container in self.docker_client.containers.list(filters={"id": container_id}):
 | 
				
			||||||
        sane_name = re.sub(r'\W+', '', request_json['maildir'])
 | 
					        sane_name = re.sub(r'\W+', '', request_json['maildir'])
 | 
				
			||||||
        cmd = ["/bin/bash", "-c", "if [[ -d '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' ]]; then /bin/mv '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"]
 | 
					        vmail_name = request_json['maildir'].replace("'", "'\\''")
 | 
				
			||||||
 | 
					        index_name = request_json['maildir'].split("/")
 | 
				
			||||||
 | 
					        index_name = index_name[1].replace("'", "'\\''") + "@" + index_name[0].replace("'", "'\\''")
 | 
				
			||||||
 | 
					        cmd_vmail = "if [[ -d '/var/vmail/" + vmail_name + "' ]]; then /bin/mv '/var/vmail/" + vmail_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"
 | 
				
			||||||
 | 
					        cmd_vmail_index = "if [[ -d '/var/vmail_index/" + index_name + "' ]]; then /bin/mv '/var/vmail_index/" + index_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "_index'; fi"
 | 
				
			||||||
 | 
					        cmd = ["/bin/bash", "-c", cmd_vmail + " && " + cmd_vmail_index]
 | 
				
			||||||
        maildir_cleanup = container.exec_run(cmd, user='vmail')
 | 
					        maildir_cleanup = container.exec_run(cmd, user='vmail')
 | 
				
			||||||
        return exec_run_handler('generic', maildir_cleanup)
 | 
					        return exec_run_handler('generic', maildir_cleanup)
 | 
				
			||||||
  # api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
 | 
					  # api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,28 +64,40 @@ def refreshF2boptions():
 | 
				
			|||||||
  global f2boptions
 | 
					  global f2boptions
 | 
				
			||||||
  global quit_now
 | 
					  global quit_now
 | 
				
			||||||
  global exit_code
 | 
					  global exit_code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  f2boptions = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if not r.get('F2B_OPTIONS'):
 | 
					  if not r.get('F2B_OPTIONS'):
 | 
				
			||||||
    f2boptions = {}
 | 
					    f2boptions['ban_time'] = r.get('F2B_BAN_TIME')
 | 
				
			||||||
    f2boptions['ban_time'] = int
 | 
					    f2boptions['max_ban_time'] = r.get('F2B_MAX_BAN_TIME')
 | 
				
			||||||
    f2boptions['max_attempts'] = int
 | 
					    f2boptions['ban_time_increment'] = r.get('F2B_BAN_TIME_INCREMENT')
 | 
				
			||||||
    f2boptions['retry_window'] = int
 | 
					    f2boptions['max_attempts'] = r.get('F2B_MAX_ATTEMPTS')
 | 
				
			||||||
    f2boptions['netban_ipv4'] = int
 | 
					    f2boptions['retry_window'] = r.get('F2B_RETRY_WINDOW')
 | 
				
			||||||
    f2boptions['netban_ipv6'] = int
 | 
					    f2boptions['netban_ipv4'] = r.get('F2B_NETBAN_IPV4')
 | 
				
			||||||
    f2boptions['ban_time'] = r.get('F2B_BAN_TIME') or 1800
 | 
					    f2boptions['netban_ipv6'] = r.get('F2B_NETBAN_IPV6')
 | 
				
			||||||
    f2boptions['max_attempts'] = r.get('F2B_MAX_ATTEMPTS') or 10
 | 
					 | 
				
			||||||
    f2boptions['retry_window'] = r.get('F2B_RETRY_WINDOW') or 600
 | 
					 | 
				
			||||||
    f2boptions['netban_ipv4'] = r.get('F2B_NETBAN_IPV4') or 32
 | 
					 | 
				
			||||||
    f2boptions['netban_ipv6'] = r.get('F2B_NETBAN_IPV6') or 128
 | 
					 | 
				
			||||||
    r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
 | 
					 | 
				
			||||||
  else:
 | 
					  else:
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
      f2boptions = {}
 | 
					 | 
				
			||||||
      f2boptions = json.loads(r.get('F2B_OPTIONS'))
 | 
					      f2boptions = json.loads(r.get('F2B_OPTIONS'))
 | 
				
			||||||
    except ValueError:
 | 
					    except ValueError:
 | 
				
			||||||
      print('Error loading F2B options: F2B_OPTIONS is not json')
 | 
					      print('Error loading F2B options: F2B_OPTIONS is not json')
 | 
				
			||||||
      quit_now = True
 | 
					      quit_now = True
 | 
				
			||||||
      exit_code = 2
 | 
					      exit_code = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  verifyF2boptions(f2boptions)
 | 
				
			||||||
 | 
					  r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def verifyF2boptions(f2boptions):
 | 
				
			||||||
 | 
					  verifyF2boption(f2boptions,'ban_time', 1800)
 | 
				
			||||||
 | 
					  verifyF2boption(f2boptions,'max_ban_time', 10000)
 | 
				
			||||||
 | 
					  verifyF2boption(f2boptions,'ban_time_increment', True)
 | 
				
			||||||
 | 
					  verifyF2boption(f2boptions,'max_attempts', 10)
 | 
				
			||||||
 | 
					  verifyF2boption(f2boptions,'retry_window', 600)
 | 
				
			||||||
 | 
					  verifyF2boption(f2boptions,'netban_ipv4', 32)
 | 
				
			||||||
 | 
					  verifyF2boption(f2boptions,'netban_ipv6', 128)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def verifyF2boption(f2boptions, f2boption, f2bdefault):
 | 
				
			||||||
 | 
					  f2boptions[f2boption] = f2boptions[f2boption] if f2boption in f2boptions and f2boptions[f2boption] is not None else f2bdefault
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def refreshF2bregex():
 | 
					def refreshF2bregex():
 | 
				
			||||||
  global f2bregex
 | 
					  global f2bregex
 | 
				
			||||||
  global quit_now
 | 
					  global quit_now
 | 
				
			||||||
@@ -147,6 +159,7 @@ def ban(address):
 | 
				
			|||||||
  global lock
 | 
					  global lock
 | 
				
			||||||
  refreshF2boptions()
 | 
					  refreshF2boptions()
 | 
				
			||||||
  BAN_TIME = int(f2boptions['ban_time'])
 | 
					  BAN_TIME = int(f2boptions['ban_time'])
 | 
				
			||||||
 | 
					  BAN_TIME_INCREMENT = bool(f2boptions['ban_time_increment'])
 | 
				
			||||||
  MAX_ATTEMPTS = int(f2boptions['max_attempts'])
 | 
					  MAX_ATTEMPTS = int(f2boptions['max_attempts'])
 | 
				
			||||||
  RETRY_WINDOW = int(f2boptions['retry_window'])
 | 
					  RETRY_WINDOW = int(f2boptions['retry_window'])
 | 
				
			||||||
  NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4'])
 | 
					  NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4'])
 | 
				
			||||||
@@ -174,20 +187,16 @@ def ban(address):
 | 
				
			|||||||
  net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)), strict=False)
 | 
					  net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)), strict=False)
 | 
				
			||||||
  net = str(net)
 | 
					  net = str(net)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if not net in bans or time.time() - bans[net]['last_attempt'] > RETRY_WINDOW:
 | 
					  if not net in bans:
 | 
				
			||||||
    bans[net] = { 'attempts': 0 }
 | 
					    bans[net] = {'attempts': 0, 'last_attempt': 0, 'ban_counter': 0}
 | 
				
			||||||
    active_window = RETRY_WINDOW
 | 
					 | 
				
			||||||
  else:
 | 
					 | 
				
			||||||
    active_window = time.time() - bans[net]['last_attempt']
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bans[net]['attempts'] += 1
 | 
					  bans[net]['attempts'] += 1
 | 
				
			||||||
  bans[net]['last_attempt'] = time.time()
 | 
					  bans[net]['last_attempt'] = time.time()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  active_window = time.time() - bans[net]['last_attempt']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if bans[net]['attempts'] >= MAX_ATTEMPTS:
 | 
					  if bans[net]['attempts'] >= MAX_ATTEMPTS:
 | 
				
			||||||
    cur_time = int(round(time.time()))
 | 
					    cur_time = int(round(time.time()))
 | 
				
			||||||
    logCrit('Banning %s for %d minutes' % (net, BAN_TIME / 60))
 | 
					    NET_BAN_TIME = BAN_TIME if not BAN_TIME_INCREMENT else BAN_TIME * 2 ** bans[net]['ban_counter']
 | 
				
			||||||
 | 
					    logCrit('Banning %s for %d minutes' % (net, NET_BAN_TIME / 60 ))
 | 
				
			||||||
    if type(ip) is ipaddress.IPv4Address:
 | 
					    if type(ip) is ipaddress.IPv4Address:
 | 
				
			||||||
      with lock:
 | 
					      with lock:
 | 
				
			||||||
        chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
 | 
					        chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
 | 
				
			||||||
@@ -206,7 +215,7 @@ def ban(address):
 | 
				
			|||||||
        rule.target = target
 | 
					        rule.target = target
 | 
				
			||||||
        if rule not in chain.rules:
 | 
					        if rule not in chain.rules:
 | 
				
			||||||
          chain.insert_rule(rule)
 | 
					          chain.insert_rule(rule)
 | 
				
			||||||
    r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + BAN_TIME)
 | 
					    r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + NET_BAN_TIME)
 | 
				
			||||||
  else:
 | 
					  else:
 | 
				
			||||||
    logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
 | 
					    logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -238,7 +247,8 @@ def unban(net):
 | 
				
			|||||||
  r.hdel('F2B_ACTIVE_BANS', '%s' % net)
 | 
					  r.hdel('F2B_ACTIVE_BANS', '%s' % net)
 | 
				
			||||||
  r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
 | 
					  r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
 | 
				
			||||||
  if net in bans:
 | 
					  if net in bans:
 | 
				
			||||||
    del bans[net]
 | 
					    bans[net]['attempts'] = 0
 | 
				
			||||||
 | 
					    bans[net]['ban_counter'] += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def permBan(net, unban=False):
 | 
					def permBan(net, unban=False):
 | 
				
			||||||
  global lock
 | 
					  global lock
 | 
				
			||||||
@@ -332,7 +342,7 @@ def watch():
 | 
				
			|||||||
              logWarn('%s matched rule id %s (%s)' % (addr, rule_id, item['data']))
 | 
					              logWarn('%s matched rule id %s (%s)' % (addr, rule_id, item['data']))
 | 
				
			||||||
              ban(addr)
 | 
					              ban(addr)
 | 
				
			||||||
    except Exception as ex:
 | 
					    except Exception as ex:
 | 
				
			||||||
      logWarn('Error reading log line from pubsub')
 | 
					      logWarn('Error reading log line from pubsub: %s' % ex)
 | 
				
			||||||
      quit_now = True
 | 
					      quit_now = True
 | 
				
			||||||
      exit_code = 2
 | 
					      exit_code = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -366,6 +376,8 @@ def snat4(snat_target):
 | 
				
			|||||||
          chain.insert_rule(new_rule)
 | 
					          chain.insert_rule(new_rule)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
          for position, rule in enumerate(chain.rules):
 | 
					          for position, rule in enumerate(chain.rules):
 | 
				
			||||||
 | 
					            if not hasattr(rule.target, 'parameter'):
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
            match = all((
 | 
					            match = all((
 | 
				
			||||||
              new_rule.get_src() == rule.get_src(),
 | 
					              new_rule.get_src() == rule.get_src(),
 | 
				
			||||||
              new_rule.get_dst() == rule.get_dst(),
 | 
					              new_rule.get_dst() == rule.get_dst(),
 | 
				
			||||||
@@ -425,6 +437,8 @@ def autopurge():
 | 
				
			|||||||
    time.sleep(10)
 | 
					    time.sleep(10)
 | 
				
			||||||
    refreshF2boptions()
 | 
					    refreshF2boptions()
 | 
				
			||||||
    BAN_TIME = int(f2boptions['ban_time'])
 | 
					    BAN_TIME = int(f2boptions['ban_time'])
 | 
				
			||||||
 | 
					    MAX_BAN_TIME = int(f2boptions['max_ban_time'])
 | 
				
			||||||
 | 
					    BAN_TIME_INCREMENT = bool(f2boptions['ban_time_increment'])
 | 
				
			||||||
    MAX_ATTEMPTS = int(f2boptions['max_attempts'])
 | 
					    MAX_ATTEMPTS = int(f2boptions['max_attempts'])
 | 
				
			||||||
    QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN')
 | 
					    QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN')
 | 
				
			||||||
    if QUEUE_UNBAN:
 | 
					    if QUEUE_UNBAN:
 | 
				
			||||||
@@ -432,7 +446,9 @@ def autopurge():
 | 
				
			|||||||
        unban(str(net))
 | 
					        unban(str(net))
 | 
				
			||||||
    for net in bans.copy():
 | 
					    for net in bans.copy():
 | 
				
			||||||
      if bans[net]['attempts'] >= MAX_ATTEMPTS:
 | 
					      if bans[net]['attempts'] >= MAX_ATTEMPTS:
 | 
				
			||||||
        if time.time() - bans[net]['last_attempt'] > BAN_TIME:
 | 
					        NET_BAN_TIME = BAN_TIME if not BAN_TIME_INCREMENT else BAN_TIME * 2 ** bans[net]['ban_counter']
 | 
				
			||||||
 | 
					        TIME_SINCE_LAST_ATTEMPT = time.time() - bans[net]['last_attempt']
 | 
				
			||||||
 | 
					        if TIME_SINCE_LAST_ATTEMPT > NET_BAN_TIME or TIME_SINCE_LAST_ATTEMPT > MAX_BAN_TIME:
 | 
				
			||||||
          unban(net)
 | 
					          unban(net)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def isIpNetwork(address):
 | 
					def isIpNetwork(address):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
FROM php:8.1-fpm-alpine3.17
 | 
					FROM php:8.2-fpm-alpine3.17
 | 
				
			||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
 | 
					LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced
 | 
					# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced
 | 
				
			||||||
@@ -12,7 +12,7 @@ ARG MEMCACHED_PECL_VERSION=3.2.0
 | 
				
			|||||||
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced
 | 
					# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced
 | 
				
			||||||
ARG REDIS_PECL_VERSION=5.3.7
 | 
					ARG REDIS_PECL_VERSION=5.3.7
 | 
				
			||||||
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced
 | 
					# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced
 | 
				
			||||||
ARG COMPOSER_VERSION=2.5.4
 | 
					ARG COMPOSER_VERSION=2.5.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN apk add -U --no-cache autoconf \
 | 
					RUN apk add -U --no-cache autoconf \
 | 
				
			||||||
  aspell-dev \
 | 
					  aspell-dev \
 | 
				
			||||||
@@ -52,6 +52,7 @@ RUN apk add -U --no-cache autoconf \
 | 
				
			|||||||
  libxpm-dev \
 | 
					  libxpm-dev \
 | 
				
			||||||
  libzip \
 | 
					  libzip \
 | 
				
			||||||
  libzip-dev \
 | 
					  libzip-dev \
 | 
				
			||||||
 | 
					  linux-headers \
 | 
				
			||||||
  make \
 | 
					  make \
 | 
				
			||||||
  mysql-client \
 | 
					  mysql-client \
 | 
				
			||||||
  openldap-dev \
 | 
					  openldap-dev \
 | 
				
			||||||
@@ -75,7 +76,7 @@ RUN apk add -U --no-cache autoconf \
 | 
				
			|||||||
    --with-webp \
 | 
					    --with-webp \
 | 
				
			||||||
    --with-xpm \
 | 
					    --with-xpm \
 | 
				
			||||||
    --with-avif \
 | 
					    --with-avif \
 | 
				
			||||||
  && docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets zip bcmath gmp \
 | 
					  && docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets sysvsem zip bcmath gmp \
 | 
				
			||||||
  && docker-php-ext-configure imap --with-imap --with-imap-ssl \
 | 
					  && docker-php-ext-configure imap --with-imap --with-imap-ssl \
 | 
				
			||||||
  && docker-php-ext-install -j 4 imap \
 | 
					  && docker-php-ext-install -j 4 imap \
 | 
				
			||||||
  && curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \
 | 
					  && curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \
 | 
				
			||||||
@@ -99,6 +100,7 @@ RUN apk add -U --no-cache autoconf \
 | 
				
			|||||||
    libxml2-dev \
 | 
					    libxml2-dev \
 | 
				
			||||||
    libxpm-dev \
 | 
					    libxpm-dev \
 | 
				
			||||||
    libzip-dev \
 | 
					    libzip-dev \
 | 
				
			||||||
 | 
					    linux-headers \
 | 
				
			||||||
    make \
 | 
					    make \
 | 
				
			||||||
    openldap-dev \
 | 
					    openldap-dev \
 | 
				
			||||||
    pcre-dev \
 | 
					    pcre-dev \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@ server {
 | 
				
			|||||||
  add_header X-Download-Options "noopen" always;
 | 
					  add_header X-Download-Options "noopen" always;
 | 
				
			||||||
  add_header X-Frame-Options "SAMEORIGIN" always;
 | 
					  add_header X-Frame-Options "SAMEORIGIN" always;
 | 
				
			||||||
  add_header X-Permitted-Cross-Domain-Policies "none" always;
 | 
					  add_header X-Permitted-Cross-Domain-Policies "none" always;
 | 
				
			||||||
  add_header X-Robots-Tag "none" always;
 | 
					  add_header X-Robots-Tag "noindex, nofollow" always;
 | 
				
			||||||
  add_header X-XSS-Protection "1; mode=block" always;
 | 
					  add_header X-XSS-Protection "1; mode=block" always;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fastcgi_hide_header X-Powered-By;
 | 
					  fastcgi_hide_header X-Powered-By;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ VIRUS_FOUND {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
# Bad policy from free mail providers
 | 
					# Bad policy from free mail providers
 | 
				
			||||||
FREEMAIL_POLICY_FAILURE {
 | 
					FREEMAIL_POLICY_FAILURE {
 | 
				
			||||||
  expression = "-g+:policies & !DMARC_POLICY_ALLOW & !MAILLIST & ( FREEMAIL_ENVFROM | FREEMAIL_FROM ) & !WHITELISTED_FWD_HOST";
 | 
					  expression = "FREEMAIL_FROM & !DMARC_POLICY_ALLOW & !MAILLIST& !WHITELISTED_FWD_HOST & -g+:policies";
 | 
				
			||||||
  score = 16.0;
 | 
					  score = 16.0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
# Applies to freemail with undisclosed recipients
 | 
					# Applies to freemail with undisclosed recipients
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,7 +62,7 @@
 | 
				
			|||||||
    SOGoFirstDayOfWeek = "1";
 | 
					    SOGoFirstDayOfWeek = "1";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SOGoSieveFolderEncoding = "UTF-8";
 | 
					    SOGoSieveFolderEncoding = "UTF-8";
 | 
				
			||||||
    SOGoPasswordChangeEnabled = YES;
 | 
					    SOGoPasswordChangeEnabled = NO;
 | 
				
			||||||
    SOGoSentFolderName = "Sent";
 | 
					    SOGoSentFolderName = "Sent";
 | 
				
			||||||
    SOGoMailShowSubscribedFoldersOnly = NO;
 | 
					    SOGoMailShowSubscribedFoldersOnly = NO;
 | 
				
			||||||
    NGImap4ConnectionStringSeparator = "/";
 | 
					    NGImap4ConnectionStringSeparator = "/";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3176,8 +3176,10 @@ paths:
 | 
				
			|||||||
              example:
 | 
					              example:
 | 
				
			||||||
                attr:
 | 
					                attr:
 | 
				
			||||||
                  ban_time: "86400"
 | 
					                  ban_time: "86400"
 | 
				
			||||||
 | 
					                  ban_time_increment: "1"
 | 
				
			||||||
                  blacklist: "10.100.6.5/32,10.100.8.4/32"
 | 
					                  blacklist: "10.100.6.5/32,10.100.8.4/32"
 | 
				
			||||||
                  max_attempts: "5"
 | 
					                  max_attempts: "5"
 | 
				
			||||||
 | 
					                  max_ban_time: "86400"
 | 
				
			||||||
                  netban_ipv4: "24"
 | 
					                  netban_ipv4: "24"
 | 
				
			||||||
                  netban_ipv6: "64"
 | 
					                  netban_ipv6: "64"
 | 
				
			||||||
                  retry_window: "600"
 | 
					                  retry_window: "600"
 | 
				
			||||||
@@ -3191,11 +3193,17 @@ paths:
 | 
				
			|||||||
                      description: the backlisted ips or hostnames separated by comma
 | 
					                      description: the backlisted ips or hostnames separated by comma
 | 
				
			||||||
                      type: string
 | 
					                      type: string
 | 
				
			||||||
                    ban_time:
 | 
					                    ban_time:
 | 
				
			||||||
                      description: the time a ip should be banned
 | 
					                      description: the time an ip should be banned
 | 
				
			||||||
                      type: number
 | 
					                      type: number
 | 
				
			||||||
 | 
					                    ban_time_increment:
 | 
				
			||||||
 | 
					                      description: if the time of the ban should increase each time
 | 
				
			||||||
 | 
					                      type: boolean
 | 
				
			||||||
                    max_attempts:
 | 
					                    max_attempts:
 | 
				
			||||||
                      description: the maximum numbe of wrong logins before a ip is banned
 | 
					                      description: the maximum numbe of wrong logins before a ip is banned
 | 
				
			||||||
                      type: number
 | 
					                      type: number
 | 
				
			||||||
 | 
					                    max_ban_time:
 | 
				
			||||||
 | 
					                      description: the maximum time an ip should be banned
 | 
				
			||||||
 | 
					                      type: number
 | 
				
			||||||
                    netban_ipv4:
 | 
					                    netban_ipv4:
 | 
				
			||||||
                      description: the networks mask to ban for ipv4
 | 
					                      description: the networks mask to ban for ipv4
 | 
				
			||||||
                      type: number
 | 
					                      type: number
 | 
				
			||||||
@@ -4113,10 +4121,12 @@ paths:
 | 
				
			|||||||
                response:
 | 
					                response:
 | 
				
			||||||
                  value:
 | 
					                  value:
 | 
				
			||||||
                    ban_time: 604800
 | 
					                    ban_time: 604800
 | 
				
			||||||
 | 
					                    ban_time_increment: 1
 | 
				
			||||||
                    blacklist: |-
 | 
					                    blacklist: |-
 | 
				
			||||||
                      45.82.153.37/32
 | 
					                      45.82.153.37/32
 | 
				
			||||||
                      92.118.38.52/32
 | 
					                      92.118.38.52/32
 | 
				
			||||||
                    max_attempts: 1
 | 
					                    max_attempts: 1
 | 
				
			||||||
 | 
					                    max_ban_time: 604800
 | 
				
			||||||
                    netban_ipv4: 32
 | 
					                    netban_ipv4: 32
 | 
				
			||||||
                    netban_ipv6: 128
 | 
					                    netban_ipv6: 128
 | 
				
			||||||
                    perm_bans:
 | 
					                    perm_bans:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -239,7 +239,9 @@ function fail2ban($_action, $_data = null) {
 | 
				
			|||||||
      $is_now = fail2ban('get');
 | 
					      $is_now = fail2ban('get');
 | 
				
			||||||
      if (!empty($is_now)) {
 | 
					      if (!empty($is_now)) {
 | 
				
			||||||
        $ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']);
 | 
					        $ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']);
 | 
				
			||||||
 | 
					        $ban_time_increment = (isset($_data['ban_time_increment']) && $_data['ban_time_increment'] == "1") ? 1 : 0;
 | 
				
			||||||
        $max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['max_attempts']);
 | 
					        $max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['max_attempts']);
 | 
				
			||||||
 | 
					        $max_ban_time = intval((isset($_data['max_ban_time'])) ? $_data['max_ban_time'] : $is_now['max_ban_time']);
 | 
				
			||||||
        $retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']);
 | 
					        $retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']);
 | 
				
			||||||
        $netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']);
 | 
					        $netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']);
 | 
				
			||||||
        $netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']);
 | 
					        $netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']);
 | 
				
			||||||
@@ -256,6 +258,8 @@ function fail2ban($_action, $_data = null) {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      $f2b_options = array();
 | 
					      $f2b_options = array();
 | 
				
			||||||
      $f2b_options['ban_time'] = ($ban_time < 60) ? 60 : $ban_time;
 | 
					      $f2b_options['ban_time'] = ($ban_time < 60) ? 60 : $ban_time;
 | 
				
			||||||
 | 
					      $f2b_options['ban_time_increment'] = ($ban_time_increment == 1) ? true : false;
 | 
				
			||||||
 | 
					      $f2b_options['max_ban_time'] = ($max_ban_time < 60) ? 60 : $max_ban_time;
 | 
				
			||||||
      $f2b_options['netban_ipv4'] = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
 | 
					      $f2b_options['netban_ipv4'] = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
 | 
				
			||||||
      $f2b_options['netban_ipv6'] = ($netban_ipv6 < 8) ? 8 : $netban_ipv6;
 | 
					      $f2b_options['netban_ipv6'] = ($netban_ipv6 < 8) ? 8 : $netban_ipv6;
 | 
				
			||||||
      $f2b_options['netban_ipv4'] = ($netban_ipv4 > 32) ? 32 : $netban_ipv4;
 | 
					      $f2b_options['netban_ipv4'] = ($netban_ipv4 > 32) ? 32 : $netban_ipv4;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1181,7 +1181,7 @@ jQuery(function($){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (table = $('#' + log_table).DataTable()) {
 | 
					    if (table = $('#' + log_table).DataTable()) {
 | 
				
			||||||
      var heading = $('#' + log_table).closest('.card').find('.card-header');
 | 
					      var heading = $('#' + log_table).closest('.card').find('.card-header');
 | 
				
			||||||
      var load_rows = (table.page.len() + 1) + '-' + (table.page.len() + new_nrows)
 | 
					      var load_rows = (table.data().length + 1) + '-' + (table.data().length + new_nrows)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      $.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
 | 
					      $.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
 | 
				
			||||||
        if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
 | 
					        if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,7 +83,7 @@
 | 
				
			|||||||
        "private_comment": "Privat kommentar",
 | 
					        "private_comment": "Privat kommentar",
 | 
				
			||||||
        "public_comment": "Offentlig kommentar",
 | 
					        "public_comment": "Offentlig kommentar",
 | 
				
			||||||
        "quota_mb": "Kvota (Mb)",
 | 
					        "quota_mb": "Kvota (Mb)",
 | 
				
			||||||
        "relay_all": "Send alle modtagere videre",
 | 
					        "relay_all": "Besvar alle modtager",
 | 
				
			||||||
        "relay_all_info": "↪ Hvis du vælger <b> ikke </b> at videresende alle modtagere, skal du tilføje et (\"blind\") postkasse til hver enkelt modtager, der skal videresendes.",
 | 
					        "relay_all_info": "↪ Hvis du vælger <b> ikke </b> at videresende alle modtagere, skal du tilføje et (\"blind\") postkasse til hver enkelt modtager, der skal videresendes.",
 | 
				
			||||||
        "relay_domain": "Send dette domæne videre",
 | 
					        "relay_domain": "Send dette domæne videre",
 | 
				
			||||||
        "relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Du kan definere transportkort til en tilpasset destination for dette domæne. Hvis ikke indstillet, foretages der et MX-opslag.",
 | 
					        "relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Du kan definere transportkort til en tilpasset destination for dette domæne. Hvis ikke indstillet, foretages der et MX-opslag.",
 | 
				
			||||||
@@ -104,7 +104,10 @@
 | 
				
			|||||||
        "timeout2": "Timeout for forbindelse til lokal vært",
 | 
					        "timeout2": "Timeout for forbindelse til lokal vært",
 | 
				
			||||||
        "username": "Brugernavn",
 | 
					        "username": "Brugernavn",
 | 
				
			||||||
        "validate": "Bekræft",
 | 
					        "validate": "Bekræft",
 | 
				
			||||||
        "validation_success": "Valideret med succes"
 | 
					        "validation_success": "Valideret med succes",
 | 
				
			||||||
 | 
					        "bcc_dest_format": "BCC-destination skal være en enkelt gyldig e-mail-adresse.<br>Hvis du har brug for at sende en kopi til flere adresser, kan du oprette et alias og bruge det her.",
 | 
				
			||||||
 | 
					        "app_passwd_protocols": "Tilladte protokoller for app adgangskode",
 | 
				
			||||||
 | 
					        "tags": "Tag's"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "admin": {
 | 
					    "admin": {
 | 
				
			||||||
        "access": "Adgang",
 | 
					        "access": "Adgang",
 | 
				
			||||||
@@ -313,7 +316,8 @@
 | 
				
			|||||||
        "verify": "Verificere",
 | 
					        "verify": "Verificere",
 | 
				
			||||||
        "yes": "✓",
 | 
					        "yes": "✓",
 | 
				
			||||||
        "ip_check_opt_in": "Opt-In for brug af tredjepartstjeneste <strong>ipv4.mailcow.email</strong> og <strong>ipv6.mailcow.email</strong> til at finde eksterne IP-adresser.",
 | 
					        "ip_check_opt_in": "Opt-In for brug af tredjepartstjeneste <strong>ipv4.mailcow.email</strong> og <strong>ipv6.mailcow.email</strong> til at finde eksterne IP-adresser.",
 | 
				
			||||||
        "queue_unban": "unban"
 | 
					        "queue_unban": "unban",
 | 
				
			||||||
 | 
					        "admins": "Administratorer"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "danger": {
 | 
					    "danger": {
 | 
				
			||||||
        "access_denied": "Adgang nægtet eller ugyldig formular data",
 | 
					        "access_denied": "Adgang nægtet eller ugyldig formular data",
 | 
				
			||||||
@@ -1044,7 +1048,7 @@
 | 
				
			|||||||
        "spamfilter_table_empty": "Intet data at vise",
 | 
					        "spamfilter_table_empty": "Intet data at vise",
 | 
				
			||||||
        "spamfilter_table_remove": "slet",
 | 
					        "spamfilter_table_remove": "slet",
 | 
				
			||||||
        "spamfilter_table_rule": "Regl",
 | 
					        "spamfilter_table_rule": "Regl",
 | 
				
			||||||
        "spamfilter_wl": "Hvisliste",
 | 
					        "spamfilter_wl": "Hvidliste",
 | 
				
			||||||
        "spamfilter_wl_desc": "Hvidlistede e-mail-adresser til <b>aldrig</b> at klassificeres som spam. Wildcards kan bruges. Et filter anvendes kun på direkte aliaser (aliaser med en enkelt målpostkasse) eksklusive catch-aliaser og selve en postkasse.",
 | 
					        "spamfilter_wl_desc": "Hvidlistede e-mail-adresser til <b>aldrig</b> at klassificeres som spam. Wildcards kan bruges. Et filter anvendes kun på direkte aliaser (aliaser med en enkelt målpostkasse) eksklusive catch-aliaser og selve en postkasse.",
 | 
				
			||||||
        "spamfilter_yellow": "Gul: denne besked kan være spam, vil blive tagget som spam og flyttes til din junk-mappe",
 | 
					        "spamfilter_yellow": "Gul: denne besked kan være spam, vil blive tagget som spam og flyttes til din junk-mappe",
 | 
				
			||||||
        "status": "Status",
 | 
					        "status": "Status",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -175,10 +175,12 @@
 | 
				
			|||||||
        "empty": "Keine Einträge vorhanden",
 | 
					        "empty": "Keine Einträge vorhanden",
 | 
				
			||||||
        "excludes": "Diese Empfänger ausschließen",
 | 
					        "excludes": "Diese Empfänger ausschließen",
 | 
				
			||||||
        "f2b_ban_time": "Bannzeit in Sekunden",
 | 
					        "f2b_ban_time": "Bannzeit in Sekunden",
 | 
				
			||||||
 | 
					        "f2b_ban_time_increment": "Bannzeit erhöht sich mit jedem Bann",
 | 
				
			||||||
        "f2b_blacklist": "Blacklist für Netzwerke und Hosts",
 | 
					        "f2b_blacklist": "Blacklist für Netzwerke und Hosts",
 | 
				
			||||||
        "f2b_filter": "Regex-Filter",
 | 
					        "f2b_filter": "Regex-Filter",
 | 
				
			||||||
        "f2b_list_info": "Ein Host oder Netzwerk auf der Blacklist wird immer eine Whitelist-Einheit überwiegen. <b>Die Aktualisierung der Liste dauert einige Sekunden.</b>",
 | 
					        "f2b_list_info": "Ein Host oder Netzwerk auf der Blacklist wird immer eine Whitelist-Einheit überwiegen. <b>Die Aktualisierung der Liste dauert einige Sekunden.</b>",
 | 
				
			||||||
        "f2b_max_attempts": "Max. Versuche",
 | 
					        "f2b_max_attempts": "Max. Versuche",
 | 
				
			||||||
 | 
					        "f2b_max_ban_time": "Maximale Bannzeit in Sekunden",
 | 
				
			||||||
        "f2b_netban_ipv4": "Netzbereich für IPv4-Banns (8-32)",
 | 
					        "f2b_netban_ipv4": "Netzbereich für IPv4-Banns (8-32)",
 | 
				
			||||||
        "f2b_netban_ipv6": "Netzbereich für IPv6-Banns (8-128)",
 | 
					        "f2b_netban_ipv6": "Netzbereich für IPv6-Banns (8-128)",
 | 
				
			||||||
        "f2b_parameters": "Fail2ban-Parameter",
 | 
					        "f2b_parameters": "Fail2ban-Parameter",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -177,10 +177,12 @@
 | 
				
			|||||||
        "empty": "No results",
 | 
					        "empty": "No results",
 | 
				
			||||||
        "excludes": "Excludes these recipients",
 | 
					        "excludes": "Excludes these recipients",
 | 
				
			||||||
        "f2b_ban_time": "Ban time (s)",
 | 
					        "f2b_ban_time": "Ban time (s)",
 | 
				
			||||||
 | 
					        "f2b_ban_time_increment": "Ban time is incremented with each ban",
 | 
				
			||||||
        "f2b_blacklist": "Blacklisted networks/hosts",
 | 
					        "f2b_blacklist": "Blacklisted networks/hosts",
 | 
				
			||||||
        "f2b_filter": "Regex filters",
 | 
					        "f2b_filter": "Regex filters",
 | 
				
			||||||
        "f2b_list_info": "A blacklisted host or network will always outweigh a whitelist entity. <b>List updates will take a few seconds to be applied.</b>",
 | 
					        "f2b_list_info": "A blacklisted host or network will always outweigh a whitelist entity. <b>List updates will take a few seconds to be applied.</b>",
 | 
				
			||||||
        "f2b_max_attempts": "Max. attempts",
 | 
					        "f2b_max_attempts": "Max. attempts",
 | 
				
			||||||
 | 
					        "f2b_max_ban_time": "Max. ban time (s)",
 | 
				
			||||||
        "f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
 | 
					        "f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
 | 
				
			||||||
        "f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
 | 
					        "f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
 | 
				
			||||||
        "f2b_parameters": "Fail2ban parameters",
 | 
					        "f2b_parameters": "Fail2ban parameters",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -141,9 +141,11 @@
 | 
				
			|||||||
        "empty": "Sin resultados",
 | 
					        "empty": "Sin resultados",
 | 
				
			||||||
        "excludes": "Excluye a estos destinatarios",
 | 
					        "excludes": "Excluye a estos destinatarios",
 | 
				
			||||||
        "f2b_ban_time": "Tiempo de restricción (s)",
 | 
					        "f2b_ban_time": "Tiempo de restricción (s)",
 | 
				
			||||||
 | 
					        "f2b_ban_time_increment": "Tiempo de restricción se incrementa con cada restricción",
 | 
				
			||||||
        "f2b_blacklist": "Redes y hosts en lista negra",
 | 
					        "f2b_blacklist": "Redes y hosts en lista negra",
 | 
				
			||||||
        "f2b_list_info": "Un host o red en lista negra siempre superará a una entidad de la lista blanca. <b>Las actualizaciones de la lista tardarán unos segundos en aplicarse.</b>",
 | 
					        "f2b_list_info": "Un host o red en lista negra siempre superará a una entidad de la lista blanca. <b>Las actualizaciones de la lista tardarán unos segundos en aplicarse.</b>",
 | 
				
			||||||
        "f2b_max_attempts": "Max num. de intentos",
 | 
					        "f2b_max_attempts": "Max num. de intentos",
 | 
				
			||||||
 | 
					        "f2b_max_ban_time": "Max tiempo de restricción (s)",
 | 
				
			||||||
        "f2b_netban_ipv4": "Tamaño de subred IPv4 para aplicar la restricción (8-32)",
 | 
					        "f2b_netban_ipv4": "Tamaño de subred IPv4 para aplicar la restricción (8-32)",
 | 
				
			||||||
        "f2b_netban_ipv6": "Tamaño de subred IPv6 para aplicar la restricción (8-128)",
 | 
					        "f2b_netban_ipv6": "Tamaño de subred IPv6 para aplicar la restricción (8-128)",
 | 
				
			||||||
        "f2b_parameters": "Parametros Fail2ban",
 | 
					        "f2b_parameters": "Parametros Fail2ban",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@
 | 
				
			|||||||
        "spam_policy": "Liste Noire/Liste Blanche",
 | 
					        "spam_policy": "Liste Noire/Liste Blanche",
 | 
				
			||||||
        "spam_score": "Score SPAM",
 | 
					        "spam_score": "Score SPAM",
 | 
				
			||||||
        "syncjobs": "Tâches de synchronisation",
 | 
					        "syncjobs": "Tâches de synchronisation",
 | 
				
			||||||
        "tls_policy": "Police TLS",
 | 
					        "tls_policy": "Politique TLS",
 | 
				
			||||||
        "unlimited_quota": "Quota illimité pour les boites de courriel",
 | 
					        "unlimited_quota": "Quota illimité pour les boites de courriel",
 | 
				
			||||||
        "domain_desc": "Modifier la description du domaine",
 | 
					        "domain_desc": "Modifier la description du domaine",
 | 
				
			||||||
        "domain_relayhost": "Changer le relais pour un domaine",
 | 
					        "domain_relayhost": "Changer le relais pour un domaine",
 | 
				
			||||||
@@ -106,7 +106,8 @@
 | 
				
			|||||||
        "validate": "Valider",
 | 
					        "validate": "Valider",
 | 
				
			||||||
        "validation_success": "Validation réussie",
 | 
					        "validation_success": "Validation réussie",
 | 
				
			||||||
        "bcc_dest_format": "La destination Cci doit être une seule adresse e-mail valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici.",
 | 
					        "bcc_dest_format": "La destination Cci doit être une seule adresse e-mail valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici.",
 | 
				
			||||||
        "tags": "Etiquettes"
 | 
					        "tags": "Etiquettes",
 | 
				
			||||||
 | 
					        "app_passwd_protocols": "Protocoles autorisés pour le mot de passe de l'application"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "admin": {
 | 
					    "admin": {
 | 
				
			||||||
        "access": "Accès",
 | 
					        "access": "Accès",
 | 
				
			||||||
@@ -171,11 +172,13 @@
 | 
				
			|||||||
        "edit": "Editer",
 | 
					        "edit": "Editer",
 | 
				
			||||||
        "empty": "Aucun résultat",
 | 
					        "empty": "Aucun résultat",
 | 
				
			||||||
        "excludes": "Exclure ces destinataires",
 | 
					        "excludes": "Exclure ces destinataires",
 | 
				
			||||||
        "f2b_ban_time": "Durée du bannissement(s)",
 | 
					        "f2b_ban_time": "Durée du bannissement (s)",
 | 
				
			||||||
 | 
					        "f2b_ban_time_increment": "Durée du bannissement est augmentée à chaque bannissement",
 | 
				
			||||||
        "f2b_blacklist": "Réseaux/Domaines sur Liste Noire",
 | 
					        "f2b_blacklist": "Réseaux/Domaines sur Liste Noire",
 | 
				
			||||||
        "f2b_filter": "Filtre(s) Regex",
 | 
					        "f2b_filter": "Filtre(s) Regex",
 | 
				
			||||||
        "f2b_list_info": "Un hôte ou un réseau sur liste noire l'emportera toujours sur une entité de liste blanche. <b>L'application des mises à jour de liste prendra quelques secondes.</b>",
 | 
					        "f2b_list_info": "Un hôte ou un réseau sur liste noire l'emportera toujours sur une entité de liste blanche. <b>L'application des mises à jour de liste prendra quelques secondes.</b>",
 | 
				
			||||||
        "f2b_max_attempts": "Nb max. de tentatives",
 | 
					        "f2b_max_attempts": "Nb max. de tentatives",
 | 
				
			||||||
 | 
					        "f2b_max_ban_time": "Max. durée du bannissement (s)",
 | 
				
			||||||
        "f2b_netban_ipv4": "Taille du sous-réseau IPv4 pour l'application du bannissement (8-32)",
 | 
					        "f2b_netban_ipv4": "Taille du sous-réseau IPv4 pour l'application du bannissement (8-32)",
 | 
				
			||||||
        "f2b_netban_ipv6": "Taille du sous-réseau IPv6 pour l'application du bannissement (8-128)",
 | 
					        "f2b_netban_ipv6": "Taille du sous-réseau IPv6 pour l'application du bannissement (8-128)",
 | 
				
			||||||
        "f2b_parameters": "Paramètres Fail2ban",
 | 
					        "f2b_parameters": "Paramètres Fail2ban",
 | 
				
			||||||
@@ -321,7 +324,9 @@
 | 
				
			|||||||
        "admins": "Administrateurs",
 | 
					        "admins": "Administrateurs",
 | 
				
			||||||
        "api_read_only": "Accès lecture-seule",
 | 
					        "api_read_only": "Accès lecture-seule",
 | 
				
			||||||
        "password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules",
 | 
					        "password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules",
 | 
				
			||||||
        "password_policy_numbers": "Doit contenir au moins un chiffre"
 | 
					        "password_policy_numbers": "Doit contenir au moins un chiffre",
 | 
				
			||||||
 | 
					        "ip_check": "Vérification IP",
 | 
				
			||||||
 | 
					        "ip_check_disabled": "La vérification IP est désactivée. Vous pouvez l'activer sous<br> <strong>Système > Configuration > Options > Personnaliser</strong>"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "danger": {
 | 
					    "danger": {
 | 
				
			||||||
        "access_denied": "Accès refusé ou données de formulaire non valides",
 | 
					        "access_denied": "Accès refusé ou données de formulaire non valides",
 | 
				
			||||||
@@ -440,7 +445,12 @@
 | 
				
			|||||||
        "username_invalid": "Le nom d'utilisateur %s ne peut pas être utilisé",
 | 
					        "username_invalid": "Le nom d'utilisateur %s ne peut pas être utilisé",
 | 
				
			||||||
        "validity_missing": "Veuillez attribuer une période de validité",
 | 
					        "validity_missing": "Veuillez attribuer une période de validité",
 | 
				
			||||||
        "value_missing": "Veuillez fournir toutes les valeurs",
 | 
					        "value_missing": "Veuillez fournir toutes les valeurs",
 | 
				
			||||||
        "yotp_verification_failed": "La vérification Yubico OTP a échoué : %s"
 | 
					        "yotp_verification_failed": "La vérification Yubico OTP a échoué : %s",
 | 
				
			||||||
 | 
					        "webauthn_authenticator_failed": "L'authentificateur selectionné est introuvable",
 | 
				
			||||||
 | 
					        "demo_mode_enabled": "Le mode de démonstration est activé",
 | 
				
			||||||
 | 
					        "template_exists": "La template %s existe déja",
 | 
				
			||||||
 | 
					        "template_id_invalid": "Le numéro de template %s est invalide",
 | 
				
			||||||
 | 
					        "template_name_invalid": "Le nom de la template est invalide"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "debug": {
 | 
					    "debug": {
 | 
				
			||||||
        "chart_this_server": "Graphique (ce serveur)",
 | 
					        "chart_this_server": "Graphique (ce serveur)",
 | 
				
			||||||
@@ -578,7 +588,7 @@
 | 
				
			|||||||
        "unchanged_if_empty": "Si non modifié, laisser en blanc",
 | 
					        "unchanged_if_empty": "Si non modifié, laisser en blanc",
 | 
				
			||||||
        "username": "Nom d'utilisateur",
 | 
					        "username": "Nom d'utilisateur",
 | 
				
			||||||
        "validate_save": "Valider et sauver",
 | 
					        "validate_save": "Valider et sauver",
 | 
				
			||||||
        "lookup_mx": "La destination est une expression régulière qui doit correspondre avec le nom du MX (<code>.*google\\.com</code> pour acheminer tout le courrier destiné à un MX se terminant par google.com via ce saut).",
 | 
					        "lookup_mx": "La destination est une expression régulière qui doit correspondre avec le nom du MX (<code>.*google\\.com</code> pour acheminer tout le courrier destiné à un MX se terminant par google.com via ce saut)",
 | 
				
			||||||
        "mailbox_relayhost_info": "S'applique uniquement à la boîte aux lettres et aux alias directs, remplace le relayhost du domaine."
 | 
					        "mailbox_relayhost_info": "S'applique uniquement à la boîte aux lettres et aux alias directs, remplace le relayhost du domaine."
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "footer": {
 | 
					    "footer": {
 | 
				
			||||||
@@ -1081,9 +1091,12 @@
 | 
				
			|||||||
        "username": "Nom d'utilisateur",
 | 
					        "username": "Nom d'utilisateur",
 | 
				
			||||||
        "verify": "Vérification",
 | 
					        "verify": "Vérification",
 | 
				
			||||||
        "waiting": "En attente",
 | 
					        "waiting": "En attente",
 | 
				
			||||||
        "week": "Semaine",
 | 
					        "week": "semaine",
 | 
				
			||||||
        "weekly": "Hebdomadaire",
 | 
					        "weekly": "Hebdomadaire",
 | 
				
			||||||
        "weeks": "semaines"
 | 
					        "weeks": "semaines",
 | 
				
			||||||
 | 
					        "months": "mois",
 | 
				
			||||||
 | 
					        "year": "année",
 | 
				
			||||||
 | 
					        "years": "années"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "warning": {
 | 
					    "warning": {
 | 
				
			||||||
        "cannot_delete_self": "Impossible de supprimer l’utilisateur connecté",
 | 
					        "cannot_delete_self": "Impossible de supprimer l’utilisateur connecté",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -175,10 +175,12 @@
 | 
				
			|||||||
        "empty": "Nessun risultato",
 | 
					        "empty": "Nessun risultato",
 | 
				
			||||||
        "excludes": "Esclude questi destinatari",
 | 
					        "excludes": "Esclude questi destinatari",
 | 
				
			||||||
        "f2b_ban_time": "Tempo di blocco (s)",
 | 
					        "f2b_ban_time": "Tempo di blocco (s)",
 | 
				
			||||||
 | 
					        "f2b_ban_time_increment": "Tempo di blocco aumenta ad ogni blocco",
 | 
				
			||||||
        "f2b_blacklist": "Host/reti in blacklist",
 | 
					        "f2b_blacklist": "Host/reti in blacklist",
 | 
				
			||||||
        "f2b_filter": "Filtri Regex",
 | 
					        "f2b_filter": "Filtri Regex",
 | 
				
			||||||
        "f2b_list_info": "Un host oppure una rete in blacklist, avrà sempre un peso maggiore rispetto ad una in whitelist. <b>L'aggiornamento della lista richiede alcuni secondi per la sua entrata in azione.</b>",
 | 
					        "f2b_list_info": "Un host oppure una rete in blacklist, avrà sempre un peso maggiore rispetto ad una in whitelist. <b>L'aggiornamento della lista richiede alcuni secondi per la sua entrata in azione.</b>",
 | 
				
			||||||
        "f2b_max_attempts": "Tentativi massimi",
 | 
					        "f2b_max_attempts": "Tentativi massimi",
 | 
				
			||||||
 | 
					        "f2b_max_ban_time": "Tempo massimo di blocco (s)",
 | 
				
			||||||
        "f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
 | 
					        "f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
 | 
				
			||||||
        "f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
 | 
					        "f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
 | 
				
			||||||
        "f2b_parameters": "Parametri Fail2ban",
 | 
					        "f2b_parameters": "Parametri Fail2ban",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -168,10 +168,12 @@
 | 
				
			|||||||
        "empty": "Geen resultaten",
 | 
					        "empty": "Geen resultaten",
 | 
				
			||||||
        "excludes": "Exclusief",
 | 
					        "excludes": "Exclusief",
 | 
				
			||||||
        "f2b_ban_time": "Verbanningstijd (s)",
 | 
					        "f2b_ban_time": "Verbanningstijd (s)",
 | 
				
			||||||
 | 
					        "f2b_ban_time_increment": "Verbanningstijd wordt verhoogd met elk verbanning",
 | 
				
			||||||
        "f2b_blacklist": "Netwerken/hosts op de blacklist",
 | 
					        "f2b_blacklist": "Netwerken/hosts op de blacklist",
 | 
				
			||||||
        "f2b_filter": "Regex-filters",
 | 
					        "f2b_filter": "Regex-filters",
 | 
				
			||||||
        "f2b_list_info": "Een host of netwerk op de blacklist staat altijd boven eenzelfde op de whitelist. <b>Het doorvoeren van wijzigingen kan enkele seconden in beslag nemen.</b>",
 | 
					        "f2b_list_info": "Een host of netwerk op de blacklist staat altijd boven eenzelfde op de whitelist. <b>Het doorvoeren van wijzigingen kan enkele seconden in beslag nemen.</b>",
 | 
				
			||||||
        "f2b_max_attempts": "Maximaal aantal pogingen",
 | 
					        "f2b_max_attempts": "Maximaal aantal pogingen",
 | 
				
			||||||
 | 
					        "f2b_max_ban_time": "Maximaal verbanningstijd (s)",
 | 
				
			||||||
        "f2b_netban_ipv4": "Voer de IPv4-subnetgrootte in waar de verbanning van kracht moet zijn (8-32)",
 | 
					        "f2b_netban_ipv4": "Voer de IPv4-subnetgrootte in waar de verbanning van kracht moet zijn (8-32)",
 | 
				
			||||||
        "f2b_netban_ipv6": "Voer de IPv6-subnetgrootte in waar de verbanning van kracht moet zijn (8-128)",
 | 
					        "f2b_netban_ipv6": "Voer de IPv6-subnetgrootte in waar de verbanning van kracht moet zijn (8-128)",
 | 
				
			||||||
        "f2b_parameters": "Fail2ban",
 | 
					        "f2b_parameters": "Fail2ban",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,14 @@
 | 
				
			|||||||
          <label for="f2b_ban_time">{{ lang.admin.f2b_ban_time }}:</label>
 | 
					          <label for="f2b_ban_time">{{ lang.admin.f2b_ban_time }}:</label>
 | 
				
			||||||
          <input type="number" class="form-control" id="f2b_ban_time" name="ban_time" value="{{ f2b_data.ban_time }}" required>
 | 
					          <input type="number" class="form-control" id="f2b_ban_time" name="ban_time" value="{{ f2b_data.ban_time }}" required>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="mb-4">
 | 
				
			||||||
 | 
					          <label for="f2b_max_ban_time">{{ lang.admin.f2b_max_ban_time }}:</label>
 | 
				
			||||||
 | 
					          <input type="number" class="form-control" id="f2b_max_ban_time" name="max_ban_time" value="{{ f2b_data.max_ban_time }}" required>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="mb-4">
 | 
				
			||||||
 | 
					          <input class="form-check-input" type="checkbox" value="1" name="ban_time_increment" id="f2b_ban_time_increment" {% if f2b_data.ban_time_increment == 1 %}checked{% endif %}>
 | 
				
			||||||
 | 
					          <label class="form-check-label" for="f2b_ban_time_increment">{{ lang.admin.f2b_ban_time_increment }}</label>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
        <div class="mb-4">
 | 
					        <div class="mb-4">
 | 
				
			||||||
          <label for="f2b_max_attempts">{{ lang.admin.f2b_max_attempts }}:</label>
 | 
					          <label for="f2b_max_attempts">{{ lang.admin.f2b_max_attempts }}:</label>
 | 
				
			||||||
          <input type="number" class="form-control" id="f2b_max_attempts" name="max_attempts" value="{{ f2b_data.max_attempts }}" required>
 | 
					          <input type="number" class="form-control" id="f2b_max_attempts" name="max_attempts" value="{{ f2b_data.max_attempts }}" required>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,7 +76,7 @@ services:
 | 
				
			|||||||
            - clamd
 | 
					            - clamd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rspamd-mailcow:
 | 
					    rspamd-mailcow:
 | 
				
			||||||
      image: mailcow/rspamd:1.92
 | 
					      image: mailcow/rspamd:1.93
 | 
				
			||||||
      stop_grace_period: 30s
 | 
					      stop_grace_period: 30s
 | 
				
			||||||
      depends_on:
 | 
					      depends_on:
 | 
				
			||||||
        - dovecot-mailcow
 | 
					        - dovecot-mailcow
 | 
				
			||||||
@@ -106,7 +106,7 @@ services:
 | 
				
			|||||||
            - rspamd
 | 
					            - rspamd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    php-fpm-mailcow:
 | 
					    php-fpm-mailcow:
 | 
				
			||||||
      image: mailcow/phpfpm:1.82
 | 
					      image: mailcow/phpfpm:1.83
 | 
				
			||||||
      command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
 | 
					      command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
 | 
				
			||||||
      depends_on:
 | 
					      depends_on:
 | 
				
			||||||
        - redis-mailcow
 | 
					        - redis-mailcow
 | 
				
			||||||
@@ -169,7 +169,7 @@ services:
 | 
				
			|||||||
            - phpfpm
 | 
					            - phpfpm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sogo-mailcow:
 | 
					    sogo-mailcow:
 | 
				
			||||||
      image: mailcow/sogo:1.115
 | 
					      image: mailcow/sogo:1.116
 | 
				
			||||||
      environment:
 | 
					      environment:
 | 
				
			||||||
        - DBNAME=${DBNAME}
 | 
					        - DBNAME=${DBNAME}
 | 
				
			||||||
        - DBUSER=${DBUSER}
 | 
					        - DBUSER=${DBUSER}
 | 
				
			||||||
@@ -191,7 +191,7 @@ services:
 | 
				
			|||||||
      volumes:
 | 
					      volumes:
 | 
				
			||||||
        - ./data/hooks/sogo:/hooks:Z
 | 
					        - ./data/hooks/sogo:/hooks:Z
 | 
				
			||||||
        - ./data/conf/sogo/:/etc/sogo/:z
 | 
					        - ./data/conf/sogo/:/etc/sogo/:z
 | 
				
			||||||
        - ./data/web/inc/init_db.inc.php:/init_db.inc.php:Z
 | 
					        - ./data/web/inc/init_db.inc.php:/init_db.inc.php:z
 | 
				
			||||||
        - ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z
 | 
					        - ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z
 | 
				
			||||||
        - ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z
 | 
					        - ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z
 | 
				
			||||||
        - ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z
 | 
					        - ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z
 | 
				
			||||||
@@ -425,7 +425,7 @@ services:
 | 
				
			|||||||
            - acme
 | 
					            - acme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    netfilter-mailcow:
 | 
					    netfilter-mailcow:
 | 
				
			||||||
      image: mailcow/netfilter:1.51
 | 
					      image: mailcow/netfilter:1.52
 | 
				
			||||||
      stop_grace_period: 30s
 | 
					      stop_grace_period: 30s
 | 
				
			||||||
      depends_on:
 | 
					      depends_on:
 | 
				
			||||||
        - dovecot-mailcow
 | 
					        - dovecot-mailcow
 | 
				
			||||||
@@ -510,7 +510,7 @@ services:
 | 
				
			|||||||
            - watchdog
 | 
					            - watchdog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dockerapi-mailcow:
 | 
					    dockerapi-mailcow:
 | 
				
			||||||
      image: mailcow/dockerapi:2.01
 | 
					      image: mailcow/dockerapi:2.02
 | 
				
			||||||
      security_opt:
 | 
					      security_opt:
 | 
				
			||||||
        - label=disable
 | 
					        - label=disable
 | 
				
			||||||
      restart: always
 | 
					      restart: always
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -205,8 +205,8 @@ DBUSER=mailcow
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Please use long, random alphanumeric strings (A-Za-z0-9)
 | 
					# Please use long, random alphanumeric strings (A-Za-z0-9)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DBPASS=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 | head -c 28)
 | 
					DBPASS=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
 | 
				
			||||||
DBROOT=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 | head -c 28)
 | 
					DBROOT=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------
 | 
					# ------------------------------
 | 
				
			||||||
# HTTP/S Bindings
 | 
					# HTTP/S Bindings
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ read -r -p "Are you sure you want to reset the mailcow administrator account? [y
 | 
				
			|||||||
response=${response,,}    # tolower
 | 
					response=${response,,}    # tolower
 | 
				
			||||||
if [[ "$response" =~ ^(yes|y)$ ]]; then
 | 
					if [[ "$response" =~ ^(yes|y)$ ]]; then
 | 
				
			||||||
	echo -e "\nWorking, please wait..."
 | 
						echo -e "\nWorking, please wait..."
 | 
				
			||||||
  random=$(</dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-16})
 | 
					  random=$(</dev/urandom tr -dc _A-Z-a-z-0-9 2> /dev/null | head -c${1:-16})
 | 
				
			||||||
  password=$(docker exec -it $(docker ps -qf name=dovecot-mailcow) doveadm pw -s SSHA256 -p ${random} | tr -d '\r')
 | 
					  password=$(docker exec -it $(docker ps -qf name=dovecot-mailcow) doveadm pw -s SSHA256 -p ${random} | tr -d '\r')
 | 
				
			||||||
	docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM admin WHERE username='admin';"
 | 
						docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM admin WHERE username='admin';"
 | 
				
			||||||
  docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM domain_admins WHERE username='admin';"
 | 
					  docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM domain_admins WHERE username='admin';"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
#!/usr/bin/env bash
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$
 | 
					# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$
 | 
				
			||||||
NEXTCLOUD_VERSION=25.0.4
 | 
					NEXTCLOUD_VERSION=26.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo -ne "Checking prerequisites..."
 | 
					echo -ne "Checking prerequisites..."
 | 
				
			||||||
sleep 1
 | 
					sleep 1
 | 
				
			||||||
@@ -122,7 +122,7 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
 | 
				
			|||||||
    && chmod +x ./data/web/nextcloud/occ
 | 
					    && chmod +x ./data/web/nextcloud/occ
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  echo -e "\033[33mCreating 'nextcloud' database...\033[0m"
 | 
					  echo -e "\033[33mCreating 'nextcloud' database...\033[0m"
 | 
				
			||||||
  NC_DBPASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
 | 
					  NC_DBPASS=$(</dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
 | 
				
			||||||
  NC_DBUSER=nextcloud
 | 
					  NC_DBUSER=nextcloud
 | 
				
			||||||
  NC_DBNAME=nextcloud
 | 
					  NC_DBNAME=nextcloud
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -138,7 +138,7 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  echo ""
 | 
					  echo ""
 | 
				
			||||||
  echo -e "\033[33mInstalling Nextcloud...\033[0m"
 | 
					  echo -e "\033[33mInstalling Nextcloud...\033[0m"
 | 
				
			||||||
  ADMIN_NC_PASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
 | 
					  ADMIN_NC_PASS=$(</dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  echo -ne "[1/4] Setting correct permissions for www-data"
 | 
					  echo -ne "[1/4] Setting correct permissions for www-data"
 | 
				
			||||||
  docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud"
 | 
					  docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										77
									
								
								update.sh
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								update.sh
									
									
									
									
									
								
							@@ -176,18 +176,19 @@ remove_obsolete_nginx_ports() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
detect_docker_compose_command(){
 | 
					detect_docker_compose_command(){
 | 
				
			||||||
if ! [ "${DOCKER_COMPOSE_VERSION}" == "native" ] && ! [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then
 | 
					if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
 | 
				
			||||||
  if docker compose > /dev/null 2>&1; then
 | 
					  if docker compose > /dev/null 2>&1; then
 | 
				
			||||||
      if docker compose version --short | grep "2." > /dev/null 2>&1; then
 | 
					      if docker compose version --short | grep "2." > /dev/null 2>&1; then
 | 
				
			||||||
        DOCKER_COMPOSE_VERSION=native
 | 
					        DOCKER_COMPOSE_VERSION=native
 | 
				
			||||||
        COMPOSE_COMMAND="docker compose"
 | 
					        COMPOSE_COMMAND="docker compose"
 | 
				
			||||||
        echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
 | 
					        echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
 | 
				
			||||||
        echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
 | 
					        echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
 | 
				
			||||||
 | 
					        sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' $SCRIPT_DIR/mailcow.conf 
 | 
				
			||||||
        sleep 2
 | 
					        sleep 2
 | 
				
			||||||
        echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
 | 
					        echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
 | 
				
			||||||
      else
 | 
					      else
 | 
				
			||||||
        echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" 
 | 
					        echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" 
 | 
				
			||||||
        echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
 | 
					        echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
 | 
				
			||||||
        exit 1
 | 
					        exit 1
 | 
				
			||||||
      fi
 | 
					      fi
 | 
				
			||||||
  elif docker-compose > /dev/null 2>&1; then
 | 
					  elif docker-compose > /dev/null 2>&1; then
 | 
				
			||||||
@@ -197,26 +198,60 @@ if ! [ "${DOCKER_COMPOSE_VERSION}" == "native" ] && ! [ "${DOCKER_COMPOSE_VERSIO
 | 
				
			|||||||
        COMPOSE_COMMAND="docker-compose"
 | 
					        COMPOSE_COMMAND="docker-compose"
 | 
				
			||||||
        echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
 | 
					        echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
 | 
				
			||||||
        echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
 | 
					        echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
 | 
				
			||||||
 | 
					        sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' $SCRIPT_DIR/mailcow.conf
 | 
				
			||||||
        sleep 2
 | 
					        sleep 2
 | 
				
			||||||
        echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
 | 
					        echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
 | 
				
			||||||
      else
 | 
					      else
 | 
				
			||||||
        echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" 
 | 
					        echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" 
 | 
				
			||||||
        echo -e "\e[31mPlease update/install regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
 | 
					        echo -e "\e[31mPlease update/install regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
 | 
				
			||||||
        exit 1
 | 
					        exit 1
 | 
				
			||||||
      fi
 | 
					      fi
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    echo -e "\e[31mCannot find Docker Compose.\e[0m" 
 | 
					    echo -e "\e[31mCannot find Docker Compose.\e[0m" 
 | 
				
			||||||
    echo -e "\e[31mPlease install it regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
 | 
					    echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
 | 
				
			||||||
    exit 1
 | 
					    exit 1
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
elif [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then
 | 
					elif [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then
 | 
				
			||||||
  COMPOSE_COMMAND="docker compose"
 | 
					  COMPOSE_COMMAND="docker compose"
 | 
				
			||||||
 | 
					  # Check if Native Compose works and has not been deleted  
 | 
				
			||||||
 | 
					  if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
 | 
				
			||||||
 | 
					    # IF it not exists/work anymore try the other command
 | 
				
			||||||
 | 
					    COMPOSE_COMMAND="docker-compose"
 | 
				
			||||||
 | 
					    if ! $COMPOSE_COMMAND > /dev/null 2>&1 || ! $COMPOSE_COMMAND --version | grep "^2." > /dev/null 2>&1; then
 | 
				
			||||||
 | 
					      # IF it cannot find Standalone in > 2.X, then script stops
 | 
				
			||||||
 | 
					      echo -e "\e[31mCannot find Docker Compose or the Version is lower then 2.X.X.\e[0m" 
 | 
				
			||||||
 | 
					      echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
 | 
				
			||||||
 | 
					      exit 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					      # If it finds the standalone Plugin it will use this instead and change the mailcow.conf Variable accordingly
 | 
				
			||||||
 | 
					      echo -e "\e[31mFound different Docker Compose Version then declared in mailcow.conf!\e[0m"
 | 
				
			||||||
 | 
					      echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable from native to standalone\e[0m"
 | 
				
			||||||
 | 
					      sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' $SCRIPT_DIR/mailcow.conf 
 | 
				
			||||||
 | 
					      sleep 2
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then
 | 
					elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then
 | 
				
			||||||
  COMPOSE_COMMAND="docker-compose"
 | 
					  COMPOSE_COMMAND="docker-compose"
 | 
				
			||||||
 | 
					  # Check if Standalone Compose works and has not been deleted  
 | 
				
			||||||
 | 
					  if ! $COMPOSE_COMMAND > /dev/null 2>&1 && ! $COMPOSE_COMMAND --version > /dev/null 2>&1 | grep "^2." > /dev/null 2>&1; then
 | 
				
			||||||
 | 
					    # IF it not exists/work anymore try the other command
 | 
				
			||||||
 | 
					    COMPOSE_COMMAND="docker compose"
 | 
				
			||||||
 | 
					    if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
 | 
				
			||||||
 | 
					      # IF it cannot find Native in > 2.X, then script stops
 | 
				
			||||||
 | 
					      echo -e "\e[31mCannot find Docker Compose.\e[0m" 
 | 
				
			||||||
 | 
					      echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
 | 
				
			||||||
 | 
					      exit 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					      # If it finds the native Plugin it will use this instead and change the mailcow.conf Variable accordingly
 | 
				
			||||||
 | 
					      echo -e "\e[31mFound different Docker Compose Version then declared in mailcow.conf!\e[0m"
 | 
				
			||||||
 | 
					      echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable from standalone to native\e[0m"
 | 
				
			||||||
 | 
					      sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' $SCRIPT_DIR/mailcow.conf 
 | 
				
			||||||
 | 
					      sleep 2
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -326,8 +361,12 @@ while (($#)); do
 | 
				
			|||||||
      echo -e "\e[32mRunning in forced mode...\e[0m"
 | 
					      echo -e "\e[32mRunning in forced mode...\e[0m"
 | 
				
			||||||
      FORCE=y
 | 
					      FORCE=y
 | 
				
			||||||
    ;;
 | 
					    ;;
 | 
				
			||||||
 | 
					    -d|--dev)
 | 
				
			||||||
 | 
					      echo -e "\e[32mRunning in Developer mode...\e[0m"
 | 
				
			||||||
 | 
					      DEV=y
 | 
				
			||||||
 | 
					    ;;
 | 
				
			||||||
    --help|-h)
 | 
					    --help|-h)
 | 
				
			||||||
    echo './update.sh [-c|--check, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, -f|--force, -h|--help]
 | 
					    echo './update.sh [-c|--check, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, -f|--force, -d|--dev, -h|--help]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  -c|--check           -   Check for updates and exit (exit codes => 0: update available, 3: no updates)
 | 
					  -c|--check           -   Check for updates and exit (exit codes => 0: update available, 3: no updates)
 | 
				
			||||||
  --ours               -   Use merge strategy option "ours" to solve conflicts in favor of non-mailcow code (local changes over remote changes), not recommended!
 | 
					  --ours               -   Use merge strategy option "ours" to solve conflicts in favor of non-mailcow code (local changes over remote changes), not recommended!
 | 
				
			||||||
@@ -338,6 +377,7 @@ while (($#)); do
 | 
				
			|||||||
  --skip-ping-check    -   Skip ICMP Check to public DNS resolvers (Use it only if you´ve blocked any ICMP Connections to your mailcow machine)
 | 
					  --skip-ping-check    -   Skip ICMP Check to public DNS resolvers (Use it only if you´ve blocked any ICMP Connections to your mailcow machine)
 | 
				
			||||||
  --stable             -   Switch your mailcow updates to the stable (master) branch. Default unless you changed it with --nightly.
 | 
					  --stable             -   Switch your mailcow updates to the stable (master) branch. Default unless you changed it with --nightly.
 | 
				
			||||||
  -f|--force           -   Force update, do not ask questions
 | 
					  -f|--force           -   Force update, do not ask questions
 | 
				
			||||||
 | 
					  -d|--dev             -   Enables Developer Mode (No Checkout of update.sh for tests)
 | 
				
			||||||
'
 | 
					'
 | 
				
			||||||
    exit 1
 | 
					    exit 1
 | 
				
			||||||
  esac
 | 
					  esac
 | 
				
			||||||
@@ -597,7 +637,7 @@ for option in ${CONFIG_ARRAY[@]}; do
 | 
				
			|||||||
      echo "Adding new option \"${option}\" to mailcow.conf"
 | 
					      echo "Adding new option \"${option}\" to mailcow.conf"
 | 
				
			||||||
      echo '# Password hash algorithm' >> mailcow.conf
 | 
					      echo '# Password hash algorithm' >> mailcow.conf
 | 
				
			||||||
      echo '# Only certain password hash algorithm are supported. For a fully list of supported schemes,' >> mailcow.conf
 | 
					      echo '# Only certain password hash algorithm are supported. For a fully list of supported schemes,' >> mailcow.conf
 | 
				
			||||||
      echo '# see https://mailcow.github.io/mailcow-dockerized-docs/models/model-passwd/' >> mailcow.conf
 | 
					      echo '# see https://docs.mailcow.email/models/model-passwd/' >> mailcow.conf
 | 
				
			||||||
      echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf
 | 
					      echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
  elif [[ ${option} == "ADDITIONAL_SERVER_NAMES" ]]; then
 | 
					  elif [[ ${option} == "ADDITIONAL_SERVER_NAMES" ]]; then
 | 
				
			||||||
@@ -617,7 +657,7 @@ for option in ${CONFIG_ARRAY[@]}; do
 | 
				
			|||||||
      echo '# Optional: Leave empty for none' >> mailcow.conf
 | 
					      echo '# Optional: Leave empty for none' >> mailcow.conf
 | 
				
			||||||
      echo '# This value is only used on first order!' >> mailcow.conf
 | 
					      echo '# This value is only used on first order!' >> mailcow.conf
 | 
				
			||||||
      echo '# Setting it at a later point will require the following steps:' >> mailcow.conf
 | 
					      echo '# Setting it at a later point will require the following steps:' >> mailcow.conf
 | 
				
			||||||
      echo '# https://mailcow.github.io/mailcow-dockerized-docs/troubleshooting/debug-reset_tls/' >> mailcow.conf
 | 
					      echo '# https://docs.mailcow.email/troubleshooting/debug-reset_tls/' >> mailcow.conf
 | 
				
			||||||
      echo 'ACME_CONTACT=' >> mailcow.conf
 | 
					      echo 'ACME_CONTACT=' >> mailcow.conf
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
  elif [[ ${option} == "WEBAUTHN_ONLY_TRUSTED_VENDORS" ]]; then
 | 
					  elif [[ ${option} == "WEBAUTHN_ONLY_TRUSTED_VENDORS" ]]; then
 | 
				
			||||||
@@ -727,15 +767,17 @@ elif [ $NEW_BRANCH == "nightly" ] && [ $CURRENT_BRANCH != "nightly" ]; then
 | 
				
			|||||||
  git checkout -f ${BRANCH}
 | 
					  git checkout -f ${BRANCH}
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo -e "\e[32mChecking for newer update script...\e[0m"
 | 
					if [ ! $DEV ]; then
 | 
				
			||||||
SHA1_1=$(sha1sum update.sh)
 | 
					  echo -e "\e[32mChecking for newer update script...\e[0m"
 | 
				
			||||||
git fetch origin #${BRANCH}
 | 
					  SHA1_1=$(sha1sum update.sh)
 | 
				
			||||||
git checkout origin/${BRANCH} update.sh
 | 
					  git fetch origin #${BRANCH}
 | 
				
			||||||
SHA1_2=$(sha1sum update.sh)
 | 
					  git checkout origin/${BRANCH} update.sh
 | 
				
			||||||
if [[ ${SHA1_1} != ${SHA1_2} ]]; then
 | 
					  SHA1_2=$(sha1sum update.sh)
 | 
				
			||||||
  echo "update.sh changed, please run this script again, exiting."
 | 
					  if [[ ${SHA1_1} != ${SHA1_2} ]]; then
 | 
				
			||||||
  chmod +x update.sh
 | 
					    echo "update.sh changed, please run this script again, exiting."
 | 
				
			||||||
  exit 2
 | 
					    chmod +x update.sh
 | 
				
			||||||
 | 
					    exit 2
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ ! $FORCE ]; then
 | 
					if [ ! $FORCE ]; then
 | 
				
			||||||
@@ -902,9 +944,6 @@ else
 | 
				
			|||||||
  echo -e "\e[33mCannot determine current git repository version...\e[0m"
 | 
					  echo -e "\e[33mCannot determine current git repository version...\e[0m"
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Set DOCKER_COMPOSE_VERSION
 | 
					 | 
				
			||||||
sed -i 's/^DOCKER_COMPOSE_VERSION=$/DOCKER_COMPOSE_VERSION='$DOCKER_COMPOSE_VERSION'/g' mailcow.conf
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if [[ ${SKIP_START} == "y" ]]; then
 | 
					if [[ ${SKIP_START} == "y" ]]; then
 | 
				
			||||||
  echo -e "\e[33mNot starting mailcow, please run \"$COMPOSE_COMMAND up -d --remove-orphans\" to start mailcow.\e[0m"
 | 
					  echo -e "\e[33mNot starting mailcow, please run \"$COMPOSE_COMMAND up -d --remove-orphans\" to start mailcow.\e[0m"
 | 
				
			||||||
else
 | 
					else
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user